feat(chip): add dynamic type support (#27868)
Issue number: N/A --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> The chip component does not support dynamic type. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> The chip component supports dynamic type, has a dynamic height, has min and max font sizes in `ios` mode, and has a screenshot test. ### md mode Small md mode: <img width="1368" alt="md-small" src="https://github.com/ionic-team/ionic-framework/assets/14926794/4466c1b6-6d01-4ec8-8de9-0eb71fa45223"> Medium md mode: <img width="1371" alt="md-medium" src="https://github.com/ionic-team/ionic-framework/assets/14926794/c51c324e-a3b7-486b-9f68-84b0e4146570"> Large md mode: <img width="1369" alt="md-large" src="https://github.com/ionic-team/ionic-framework/assets/14926794/166e1229-96b7-4270-9ef2-4c708776691e"> ### ios mode Small ios mode: <img width="1371" alt="ios-small" src="https://github.com/ionic-team/ionic-framework/assets/14926794/de240058-5fe5-4387-8481-d695950853db"> Medium ios mode: <img width="1371" alt="ios-medium" src="https://github.com/ionic-team/ionic-framework/assets/14926794/c63db4be-e813-4917-a525-02ec456849ea"> Large ios mode: <img width="1370" alt="ios-large" src="https://github.com/ionic-team/ionic-framework/assets/14926794/3479f619-82c8-4645-85f7-2725ea43de00"> ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> This also fixes an existing bug that the contents of a chip could get smooshed if the chip doesn't have enough horizontal space for its contents. ### Before <img width="253" alt="rStll" src="https://github.com/ionic-team/ionic-framework/assets/14926794/9a8f4c30-ceef-4fe1-a600-38b2f01c4e68"> ### After <img width="253" alt="Screenshot 2023-08-02 at 5 00 39 PM" src="https://github.com/ionic-team/ionic-framework/assets/14926794/e82e17c3-91e3-4089-a57b-16c7d7b386fb"> --------- Co-authored-by: ionitron <hi@ionicframework.com> Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
11
core/src/components/chip/chip.ios.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
@import "./chip";
|
||||
@import "./chip.vars";
|
||||
|
||||
:host {
|
||||
/**
|
||||
* Main content should be prioritized on iOS,
|
||||
* so we set max font size for chips.
|
||||
* We add a min font size to keep the text legible.
|
||||
*/
|
||||
font-size: clamp(13px, $chip-base-font-size-rem, 22px);
|
||||
}
|
||||
6
core/src/components/chip/chip.md.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
@import "./chip";
|
||||
@import "./chip.vars";
|
||||
|
||||
:host {
|
||||
font-size: $chip-base-font-size-rem;
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
@import "./chip.vars";
|
||||
|
||||
:host {
|
||||
/**
|
||||
* @prop --background: Background of the chip
|
||||
* @prop --color: Color of the chip
|
||||
*/
|
||||
--background: #{rgba($text-color-rgb, .12)};
|
||||
--color: #{rgba($text-color-rgb, .87)};
|
||||
--background: #{rgba($text-color-rgb, 0.12)};
|
||||
--color: #{rgba($text-color-rgb, 0.87)};
|
||||
|
||||
@include border-radius(16px);
|
||||
@include font-smoothing();
|
||||
@include margin(4px);
|
||||
@include padding(7px, 12px);
|
||||
@include padding(6px, 12px);
|
||||
|
||||
display: inline-flex;
|
||||
|
||||
@@ -19,13 +20,12 @@
|
||||
|
||||
align-items: center;
|
||||
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
|
||||
background: var(--background);
|
||||
color: var(--color);
|
||||
|
||||
font-family: $font-family-base;
|
||||
font-size: 14px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
:host(.chip-disabled) {
|
||||
cursor: default;
|
||||
opacity: .4;
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -45,19 +45,18 @@
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(.ion-color) {
|
||||
background: current-color(base, .08);
|
||||
background: current-color(base, 0.08);
|
||||
color: current-color(shade);
|
||||
}
|
||||
|
||||
:host(.ion-color:focus) {
|
||||
background: current-color(base, .12);
|
||||
background: current-color(base, 0.12);
|
||||
}
|
||||
|
||||
:host(.ion-color.ion-activated) {
|
||||
background: current-color(base, .16);
|
||||
background: current-color(base, 0.16);
|
||||
}
|
||||
|
||||
|
||||
// Outline Chip
|
||||
// ---------------------------------------------
|
||||
|
||||
@@ -67,33 +66,32 @@
|
||||
}
|
||||
|
||||
:host(.chip-outline) {
|
||||
border-color: rgba($text-color-rgb, .32);
|
||||
border-color: rgba($text-color-rgb, 0.32);
|
||||
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:host(.chip-outline.ion-color) {
|
||||
border-color: current-color(base, .32);
|
||||
border-color: current-color(base, 0.32);
|
||||
}
|
||||
|
||||
:host(.chip-outline:not(.ion-color):focus) {
|
||||
background: rgba($text-color-rgb, .04);
|
||||
background: rgba($text-color-rgb, 0.04);
|
||||
}
|
||||
|
||||
:host(.chip-outline.ion-activated:not(.ion-color)) {
|
||||
background: rgba($text-color-rgb, .08);
|
||||
background: rgba($text-color-rgb, 0.08);
|
||||
}
|
||||
|
||||
|
||||
// Chip Icon
|
||||
// ---------------------------------------------
|
||||
|
||||
::slotted(ion-icon) {
|
||||
font-size: 20px;
|
||||
font-size: $chip-icon-size;
|
||||
}
|
||||
|
||||
:host(:not(.ion-color)) ::slotted(ion-icon) {
|
||||
color: rgba($text-color-rgb, .54);
|
||||
color: rgba($text-color-rgb, 0.54);
|
||||
}
|
||||
|
||||
::slotted(ion-icon:first-child) {
|
||||
@@ -104,13 +102,14 @@
|
||||
@include margin(-4px, -4px, -4px, 8px);
|
||||
}
|
||||
|
||||
|
||||
// Chip Avatar
|
||||
// ---------------------------------------------
|
||||
|
||||
::slotted(ion-avatar) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
|
||||
width: $chip-avatar-size;
|
||||
height: $chip-avatar-size;
|
||||
}
|
||||
|
||||
::slotted(ion-avatar:first-child) {
|
||||
@@ -121,7 +120,6 @@
|
||||
@include margin(-4px, -8px, -4px, 8px);
|
||||
}
|
||||
|
||||
|
||||
// Chip: Focus
|
||||
// ---------------------------------------------
|
||||
|
||||
@@ -130,31 +128,29 @@
|
||||
}
|
||||
|
||||
:host(:focus) {
|
||||
--background: #{rgba($text-color-rgb, .16)};
|
||||
--background: #{rgba($text-color-rgb, 0.16)};
|
||||
}
|
||||
|
||||
|
||||
// Chip: Activated
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(.ion-activated) {
|
||||
--background: #{rgba($text-color-rgb, .20)};
|
||||
--background: #{rgba($text-color-rgb, 0.2)};
|
||||
}
|
||||
|
||||
|
||||
// Chip: Hover
|
||||
// ---------------------------------------------
|
||||
|
||||
@media (any-hover: hover) {
|
||||
:host(:hover) {
|
||||
--background: #{rgba($text-color-rgb, .16)};
|
||||
--background: #{rgba($text-color-rgb, 0.16)};
|
||||
}
|
||||
|
||||
:host(.ion-color:hover) {
|
||||
background: current-color(base, .12);
|
||||
background: current-color(base, 0.12);
|
||||
}
|
||||
|
||||
:host(.chip-outline:not(.ion-color):hover) {
|
||||
background: rgba($text-color-rgb, .04);
|
||||
background: rgba($text-color-rgb, 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ import { createColorClasses } from '../../utils/theme';
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-chip',
|
||||
styleUrl: 'chip.scss',
|
||||
styleUrls: {
|
||||
ios: 'chip.ios.scss',
|
||||
md: 'chip.md.scss',
|
||||
},
|
||||
shadow: true,
|
||||
})
|
||||
export class Chip implements ComponentInterface {
|
||||
|
||||
13
core/src/components/chip/chip.vars.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
@use "sass:math";
|
||||
|
||||
/// @prop - Unitless font size of the chip before scaling
|
||||
$chip-base-font-size: 14;
|
||||
|
||||
/// @prop - Font size of the chip in rem before scaling
|
||||
$chip-base-font-size-rem: #{math.div($chip-base-font-size, 16)}rem;
|
||||
|
||||
/// @prop - Size of an icon within a chip (in em to scale as the font size of the chip scales)
|
||||
$chip-icon-size: math.div(20em, $chip-base-font-size);
|
||||
|
||||
/// @prop - Size of an avatar within a chip (in em to scale as the font size of the chip scales)
|
||||
$chip-avatar-size: math.div(24em, $chip-base-font-size);
|
||||
67
core/src/components/chip/test/a11y/chip.e2e.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
/**
|
||||
* Font scaling does not vary across directions.
|
||||
*/
|
||||
test.describe(title('chip: font scaling'), () => {
|
||||
test('should scale text', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
html {
|
||||
font-size: 30px;
|
||||
}
|
||||
</style>
|
||||
<ion-chip>
|
||||
<ion-avatar>
|
||||
<img
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSIjYzVkYmZmIiBkPSJNMCAwaDUxMnY1MTJIMHoiLz48cGF0aCBkPSJNMjU2IDMwNGM2MS42IDAgMTEyLTUwLjQgMTEyLTExMlMzMTcuNiA4MCAyNTYgODBzLTExMiA1MC40LTExMiAxMTIgNTAuNCAxMTIgMTEyIDExMnptMCA0MGMtNzQuMiAwLTIyNCAzNy44LTIyNCAxMTJ2NTZoNDQ4di01NmMwLTc0LjItMTQ5LjgtMTEyLTIyNC0xMTJ6IiBmaWxsPSIjODJhZWZmIi8+PC9zdmc+"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label>With Icon and Avatar</ion-label>
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</ion-chip>`,
|
||||
config
|
||||
);
|
||||
|
||||
const chip = page.locator('ion-chip');
|
||||
|
||||
await expect(chip).toHaveScreenshot(screenshot('chip-scale'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, config }) => {
|
||||
/**
|
||||
* Font scaling does not vary across directions
|
||||
* ios mode has a min font size
|
||||
*/
|
||||
test.describe(title('chip: contents at large scale'), () => {
|
||||
test('should handle contents wider than chip', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<style>
|
||||
html {
|
||||
font-size: 48px;
|
||||
}
|
||||
</style>
|
||||
<ion-chip>
|
||||
<ion-avatar>
|
||||
<img
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSIjYzVkYmZmIiBkPSJNMCAwaDUxMnY1MTJIMHoiLz48cGF0aCBkPSJNMjU2IDMwNGM2MS42IDAgMTEyLTUwLjQgMTEyLTExMlMzMTcuNiA4MCAyNTYgODBzLTExMiA1MC40LTExMiAxMTIgNTAuNCAxMTIgMTEyIDExMnptMCA0MGMtNzQuMiAwLTIyNCAzNy44LTIyNCAxMTJ2NTZoNDQ4di01NmMwLTc0LjItMTQ5LjgtMTEyLTIyNC0xMTJ6IiBmaWxsPSIjODJhZWZmIi8+PC9zdmc+"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label>With Icon and Avatar</ion-label>
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
</ion-chip>`,
|
||||
config
|
||||
);
|
||||
|
||||
const chip = page.locator('ion-chip');
|
||||
|
||||
await expect(chip).toHaveScreenshot(screenshot('chip-large-contents'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 6.0 KiB |