feat(select): add shapes for ionic theme (#30016)
@ -2064,7 +2064,7 @@ ion-select,prop,name,string,this.inputId,false,false
|
|||||||
ion-select,prop,okText,string,'OK',false,false
|
ion-select,prop,okText,string,'OK',false,false
|
||||||
ion-select,prop,placeholder,string | undefined,undefined,false,false
|
ion-select,prop,placeholder,string | undefined,undefined,false,false
|
||||||
ion-select,prop,selectedText,null | string | undefined,undefined,false,false
|
ion-select,prop,selectedText,null | string | undefined,undefined,false,false
|
||||||
ion-select,prop,shape,"round" | undefined,undefined,false,false
|
ion-select,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
|
||||||
ion-select,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
ion-select,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||||
ion-select,prop,toggleIcon,string | undefined,undefined,false,false
|
ion-select,prop,toggleIcon,string | undefined,undefined,false,false
|
||||||
ion-select,prop,value,any,undefined,false,false
|
ion-select,prop,value,any,undefined,false,false
|
||||||
|
8
core/src/components.d.ts
vendored
@ -3280,9 +3280,9 @@ export namespace Components {
|
|||||||
*/
|
*/
|
||||||
"selectedText"?: string | null;
|
"selectedText"?: string | null;
|
||||||
/**
|
/**
|
||||||
* The shape of the select. If "round" it will have an increased border radius.
|
* Set to `"soft"` for a select with slightly rounded corners, `"round"` for a select with fully rounded corners, or `"rectangular"` for a select without rounded corners. Defaults to `"round"` for the `"ionic"` theme, undefined for all other themes.
|
||||||
*/
|
*/
|
||||||
"shape"?: 'round';
|
"shape"?: 'soft' | 'round' | 'rectangular';
|
||||||
/**
|
/**
|
||||||
* The theme determines the visual appearance of the component.
|
* The theme determines the visual appearance of the component.
|
||||||
*/
|
*/
|
||||||
@ -8704,9 +8704,9 @@ declare namespace LocalJSX {
|
|||||||
*/
|
*/
|
||||||
"selectedText"?: string | null;
|
"selectedText"?: string | null;
|
||||||
/**
|
/**
|
||||||
* The shape of the select. If "round" it will have an increased border radius.
|
* Set to `"soft"` for a select with slightly rounded corners, `"round"` for a select with fully rounded corners, or `"rectangular"` for a select without rounded corners. Defaults to `"round"` for the `"ionic"` theme, undefined for all other themes.
|
||||||
*/
|
*/
|
||||||
"shape"?: 'round';
|
"shape"?: 'soft' | 'round' | 'rectangular';
|
||||||
/**
|
/**
|
||||||
* The theme determines the visual appearance of the component.
|
* The theme determines the visual appearance of the component.
|
||||||
*/
|
*/
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
// TODO(ROU-10778, ROU-10875): Sync the color names to the design system of
|
// TODO(ROU-10778, ROU-10875): Sync the color names to the design system of
|
||||||
// ios and md. This will allow us to have a single color map.
|
// ios and md. This will allow us to have a single color map.
|
||||||
--border-color: #{globals.current-color(neutral)};
|
--border-color: #{globals.current-color(neutral)};
|
||||||
--border-radius: #{globals.$ion-border-radius-400};
|
|
||||||
--border-width: #{globals.$ion-border-size-025};
|
--border-width: #{globals.$ion-border-size-025};
|
||||||
--padding-start: #{globals.$ion-space-400};
|
--padding-start: #{globals.$ion-space-400};
|
||||||
--padding-end: #{globals.$ion-space-400};
|
--padding-end: #{globals.$ion-space-400};
|
||||||
@ -170,7 +169,7 @@
|
|||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
// Disabled
|
// Disabled
|
||||||
// ----------------------------------------------------------------
|
// ---------------------------------------------
|
||||||
|
|
||||||
:host(.select-disabled) {
|
:host(.select-disabled) {
|
||||||
--background: #{globals.$ion-primitives-neutral-100};
|
--background: #{globals.$ion-primitives-neutral-100};
|
||||||
@ -183,3 +182,18 @@
|
|||||||
:host(.select-disabled) .select-icon {
|
:host(.select-disabled) .select-icon {
|
||||||
color: globals.$ion-primitives-neutral-500;
|
color: globals.$ion-primitives-neutral-500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shapes
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
|
:host(.select-shape-soft) {
|
||||||
|
--border-radius: #{globals.$ion-border-radius-200};
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.select-shape-round) {
|
||||||
|
--border-radius: #{globals.$ion-border-radius-400};
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(.select-shape-rectangular) {
|
||||||
|
--border-radius: #{globals.$ion-border-radius-0};
|
||||||
|
}
|
||||||
|
@ -191,9 +191,13 @@ export class Select implements ComponentInterface {
|
|||||||
@Prop() expandedIcon?: string;
|
@Prop() expandedIcon?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The shape of the select. If "round" it will have an increased border radius.
|
* Set to `"soft"` for a select with slightly rounded corners,
|
||||||
|
* `"round"` for a select with fully rounded corners,
|
||||||
|
* or `"rectangular"` for a select without rounded corners.
|
||||||
|
*
|
||||||
|
* Defaults to `"round"` for the `"ionic"` theme, undefined for all other themes.
|
||||||
*/
|
*/
|
||||||
@Prop() shape?: 'round';
|
@Prop() shape?: 'soft' | 'round' | 'rectangular';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The value of the select.
|
* The value of the select.
|
||||||
@ -990,6 +994,18 @@ export class Select implements ComponentInterface {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getShape(): string | undefined {
|
||||||
|
const theme = getIonTheme(this);
|
||||||
|
const { shape } = this;
|
||||||
|
|
||||||
|
// TODO(ROU-11366): Remove theme check when shapes are defined for all themes.
|
||||||
|
if (theme === 'ionic' && shape === undefined) {
|
||||||
|
return 'round';
|
||||||
|
}
|
||||||
|
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the icon to use for the expand icon.
|
* Get the icon to use for the expand icon.
|
||||||
* If an icon is set on the component, use that.
|
* If an icon is set on the component, use that.
|
||||||
@ -1046,9 +1062,9 @@ export class Select implements ComponentInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { disabled, el, isExpanded, expandedIcon, labelPlacement, justify, placeholder, fill, shape, name, value } =
|
const { disabled, el, isExpanded, expandedIcon, labelPlacement, justify, placeholder, fill, name, value } = this;
|
||||||
this;
|
|
||||||
const theme = getIonTheme(this);
|
const theme = getIonTheme(this);
|
||||||
|
const shape = this.getShape();
|
||||||
const hasFloatingOrStackedLabel = labelPlacement === 'floating' || labelPlacement === 'stacked';
|
const hasFloatingOrStackedLabel = labelPlacement === 'floating' || labelPlacement === 'stacked';
|
||||||
const shouldRenderOuterIcon = theme !== 'ionic' && hasFloatingOrStackedLabel;
|
const shouldRenderOuterIcon = theme !== 'ionic' && hasFloatingOrStackedLabel;
|
||||||
const shouldRenderInnerIcon = theme === 'ionic' || !hasFloatingOrStackedLabel;
|
const shouldRenderInnerIcon = theme === 'ionic' || !hasFloatingOrStackedLabel;
|
||||||
|
@ -89,24 +89,6 @@ configs({ modes: ['md'] }).forEach(({ title, screenshot, config }) => {
|
|||||||
const select = page.locator('ion-select');
|
const select = page.locator('ion-select');
|
||||||
await expect(select).toHaveScreenshot(screenshot(`select-fill-solid-label-floating`));
|
await expect(select).toHaveScreenshot(screenshot(`select-fill-solid-label-floating`));
|
||||||
});
|
});
|
||||||
test('should not have visual regressions with shaped solid', async ({ page }) => {
|
|
||||||
await page.setContent(
|
|
||||||
`
|
|
||||||
<ion-select
|
|
||||||
shape="round"
|
|
||||||
fill="solid"
|
|
||||||
label="Fruit"
|
|
||||||
value="apple"
|
|
||||||
>
|
|
||||||
<ion-select-option value="apple">Apple</ion-select-option>
|
|
||||||
</ion-select>
|
|
||||||
`,
|
|
||||||
config
|
|
||||||
);
|
|
||||||
|
|
||||||
const select = page.locator('ion-select');
|
|
||||||
await expect(select).toHaveScreenshot(screenshot(`select-fill-shaped-solid`));
|
|
||||||
});
|
|
||||||
test('padding and border radius should be customizable', async ({ page }) => {
|
test('padding and border radius should be customizable', async ({ page }) => {
|
||||||
await page.setContent(
|
await page.setContent(
|
||||||
`
|
`
|
||||||
@ -153,25 +135,6 @@ configs({ modes: ['md'] }).forEach(({ title, screenshot, config }) => {
|
|||||||
const select = page.locator('ion-select');
|
const select = page.locator('ion-select');
|
||||||
await expect(select).toHaveScreenshot(screenshot(`select-fill-outline-label-floating`));
|
await expect(select).toHaveScreenshot(screenshot(`select-fill-outline-label-floating`));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not have visual regressions with shaped outline', async ({ page }) => {
|
|
||||||
await page.setContent(
|
|
||||||
`
|
|
||||||
<ion-select
|
|
||||||
shape="round"
|
|
||||||
fill="outline"
|
|
||||||
label="Fruit"
|
|
||||||
value="apple"
|
|
||||||
>
|
|
||||||
<ion-select-option value="apple">Apple</ion-select-option>
|
|
||||||
</ion-select>
|
|
||||||
`,
|
|
||||||
config
|
|
||||||
);
|
|
||||||
|
|
||||||
const select = page.locator('ion-select');
|
|
||||||
await expect(select).toHaveScreenshot(screenshot(`select-fill-shaped-outline`));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.7 KiB |
69
core/src/components/select/test/shape/index.html
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Select - Shape</title>
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||||
|
/>
|
||||||
|
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||||
|
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||||
|
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||||
|
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||||
|
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h2 {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
color: #6f7378;
|
||||||
|
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<ion-app>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Select - Shape</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content class="ion-padding">
|
||||||
|
<div class="grid">
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Default</h2>
|
||||||
|
<ion-select shape="round" fill="outline" label="Label" label-placement="stacked" value="filledText">
|
||||||
|
<ion-select-option value="filledText">Filled text</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Soft</h2>
|
||||||
|
<ion-select shape="soft" fill="outline" label="Label" label-placement="stacked" value="filledText">
|
||||||
|
<ion-select-option value="filledText">Filled text</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Round</h2>
|
||||||
|
<ion-select shape="round" fill="outline" label="Label" label-placement="stacked" value="filledText">
|
||||||
|
<ion-select-option value="filledText">Filled text</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-item">
|
||||||
|
<h2>Rectangular</h2>
|
||||||
|
<ion-select shape="rectangular" fill="outline" label="Label" label-placement="stacked" value="filledText">
|
||||||
|
<ion-select-option value="filledText">Filled text</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
76
core/src/components/select/test/shape/select.e2e.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { expect } from '@playwright/test';
|
||||||
|
import { configs, test } from '@utils/test/playwright';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This behavior does not vary across directions.
|
||||||
|
*/
|
||||||
|
configs({ modes: ['ionic-md'], directions: ['ltr'] }).forEach(({ config, screenshot, title }) => {
|
||||||
|
test.describe(title('select: shape'), () => {
|
||||||
|
['soft', 'round', 'rectangular'].forEach((shape) => {
|
||||||
|
test(`${shape} - should not have visual regressions with outline fill`, async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-select
|
||||||
|
shape="${shape}"
|
||||||
|
fill="outline"
|
||||||
|
label="Label"
|
||||||
|
label-placement="stacked"
|
||||||
|
value="filledText"
|
||||||
|
>
|
||||||
|
<ion-select-option value="filledText">Filled text</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const select = page.locator('ion-select');
|
||||||
|
|
||||||
|
await expect(select).toHaveScreenshot(screenshot(`select-shape-${shape}-fill-outline`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
configs({ modes: ['md'] }).forEach(({ config, screenshot, title }) => {
|
||||||
|
test.describe(title('select: shape'), () => {
|
||||||
|
test('round - should not have visual regressions with outline fill', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-select
|
||||||
|
shape="round"
|
||||||
|
fill="outline"
|
||||||
|
label="Label"
|
||||||
|
label-placement="stacked"
|
||||||
|
value="filledText"
|
||||||
|
>
|
||||||
|
<ion-select-option value="filledText">Filled text</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const select = page.locator('ion-select');
|
||||||
|
await expect(select).toHaveScreenshot(screenshot(`select-shape-round-fill-outline`));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('round - should not have visual regressions with solid fill', async ({ page }) => {
|
||||||
|
await page.setContent(
|
||||||
|
`
|
||||||
|
<ion-select
|
||||||
|
shape="round"
|
||||||
|
fill="solid"
|
||||||
|
label="Label"
|
||||||
|
label-placement="stacked"
|
||||||
|
value="filledText"
|
||||||
|
>
|
||||||
|
<ion-select-option value="filledText">Filled text</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
`,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
const select = page.locator('ion-select');
|
||||||
|
await expect(select).toHaveScreenshot(screenshot(`select-shape-round-fill-solid`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.1 KiB |