diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts index c7bc83527c..5636f510b2 100644 --- a/angular/src/directives/proxies.ts +++ b/angular/src/directives/proxies.ts @@ -1954,7 +1954,7 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {} @ProxyCmp({ - inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'legacy', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'value'], + inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'legacy', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'toggleIcon', 'value'], methods: ['open'] }) @Component({ @@ -1962,7 +1962,7 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {} changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'legacy', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'value'], + inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'legacy', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'toggleIcon', 'value'], }) export class IonSelect { protected el: HTMLElement; diff --git a/core/api.txt b/core/api.txt index 04df06ad12..eecee0c71f 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1238,6 +1238,7 @@ ion-select,prop,cancelText,string,'Cancel',false,false ion-select,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record | undefined,undefined,false,true ion-select,prop,compareWith,((currentValue: any, compareValue: any) => boolean) | null | string | undefined,undefined,false,false ion-select,prop,disabled,boolean,false,false,false +ion-select,prop,expandedIcon,string | undefined,undefined,false,false ion-select,prop,fill,"outline" | "solid" | undefined,undefined,false,false ion-select,prop,interface,"action-sheet" | "alert" | "popover",'alert',false,false ion-select,prop,interfaceOptions,any,{},false,false @@ -1252,6 +1253,7 @@ ion-select,prop,okText,string,'OK',false,false ion-select,prop,placeholder,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,toggleIcon,string | undefined,undefined,false,false ion-select,prop,value,any,undefined,false,false ion-select,method,open,open(event?: UIEvent) => Promise ion-select,event,ionBlur,void,true diff --git a/core/src/components.d.ts b/core/src/components.d.ts index b65f10b179..a85b7cfcfb 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2681,6 +2681,10 @@ export namespace Components { * If `true`, the user cannot interact with the select. */ "disabled": boolean; + /** + * The toggle icon to show when the select is open. If defined, the icon rotation behavior in `md` mode will be disabled. If undefined, `toggleIcon` will be used for when the select is both open and closed. + */ + "expandedIcon"?: string; /** * The fill for the item. If `"solid"` the item will have a background. If `"outline"` the item will be transparent with a border. Only available in `md` mode. */ @@ -2742,6 +2746,10 @@ export namespace Components { * The shape of the select. If "round" it will have an increased border radius. */ "shape"?: 'round'; + /** + * The toggle icon to use. Defaults to `chevronExpand` for `ios` mode, or `caretDownSharp` for `md` mode. + */ + "toggleIcon"?: string; /** * The value of the select. */ @@ -6755,6 +6763,10 @@ declare namespace LocalJSX { * If `true`, the user cannot interact with the select. */ "disabled"?: boolean; + /** + * The toggle icon to show when the select is open. If defined, the icon rotation behavior in `md` mode will be disabled. If undefined, `toggleIcon` will be used for when the select is both open and closed. + */ + "expandedIcon"?: string; /** * The fill for the item. If `"solid"` the item will have a background. If `"outline"` the item will be transparent with a border. Only available in `md` mode. */ @@ -6835,6 +6847,10 @@ declare namespace LocalJSX { * The shape of the select. If "round" it will have an increased border radius. */ "shape"?: 'round'; + /** + * The toggle icon to use. Defaults to `chevronExpand` for `ios` mode, or `caretDownSharp` for `md` mode. + */ + "toggleIcon"?: string; /** * The value of the select. */ diff --git a/core/src/components/select/select.md.scss b/core/src/components/select/select.md.scss index ffbd004520..fa7c621c40 100644 --- a/core/src/components/select/select.md.scss +++ b/core/src/components/select/select.md.scss @@ -94,7 +94,7 @@ * when the select is activated. * This should only happen on MD. */ -:host(.select-expanded:not(.legacy-select)) .select-icon { +:host(.select-expanded:not(.legacy-select):not(.has-expanded-icon)) .select-icon { @include transform(rotate(180deg)); } @@ -123,7 +123,7 @@ @include transform(translate3d(0, -9px, 0)); } -:host-context(.item-has-focus) .select-icon { +:host-context(.item-has-focus):host(:not(.has-expanded-icon)) .select-icon { @include transform(rotate(180deg)); } @@ -131,8 +131,8 @@ * Ensure that the translation we did * above is preserved when we rotate the select icon. */ -:host-context(.item-has-focus.item-label-stacked) .select-icon, -:host-context(.item-has-focus.item-label-floating:not(.item-fill-outline)) .select-icon { +:host-context(.item-has-focus.item-label-stacked):host(:not(.has-expanded-icon)) .select-icon, +:host-context(.item-has-focus.item-label-floating:not(.item-fill-outline)):host(:not(.has-expanded-icon)) .select-icon { @include transform(rotate(180deg), translate3d(0, -9px, 0)); } diff --git a/core/src/components/select/select.tsx b/core/src/components/select/select.tsx index 49999a24f2..639d8e22a3 100644 --- a/core/src/components/select/select.tsx +++ b/core/src/components/select/select.tsx @@ -183,6 +183,19 @@ export class Select implements ComponentInterface { */ @Prop() selectedText?: string | null; + /** + * The toggle icon to use. Defaults to `chevronExpand` for `ios` mode, + * or `caretDownSharp` for `md` mode. + */ + @Prop() toggleIcon?: string; + + /** + * The toggle icon to show when the select is open. If defined, the icon + * rotation behavior in `md` mode will be disabled. If undefined, `toggleIcon` + * will be used for when the select is both open and closed. + */ + @Prop() expandedIcon?: string; + /** * The shape of the select. If "round" it will have an increased border radius. */ @@ -820,7 +833,8 @@ export class Select implements ComponentInterface { } private renderSelect() { - const { disabled, el, isExpanded, labelPlacement, justify, placeholder, fill, shape, name, value } = this; + const { disabled, el, isExpanded, expandedIcon, labelPlacement, justify, placeholder, fill, shape, name, value } = + this; const mode = getIonMode(this); const hasFloatingOrStackedLabel = labelPlacement === 'floating' || labelPlacement === 'stacked'; const justifyEnabled = !hasFloatingOrStackedLabel; @@ -839,6 +853,7 @@ export class Select implements ComponentInterface { 'in-item-color': hostContext('ion-item.ion-color', el), 'select-disabled': disabled, 'select-expanded': isExpanded, + 'has-expanded-icon': expandedIcon !== undefined, 'has-value': this.hasValue(), 'has-placeholder': placeholder !== undefined, 'ion-focusable': true, @@ -893,7 +908,7 @@ Developers can use the "legacy" property to continue using the legacy form marku this.hasLoggedDeprecationWarning = true; } - const { disabled, el, inputId, isExpanded, name, placeholder, value } = this; + const { disabled, el, inputId, isExpanded, expandedIcon, name, placeholder, value } = this; const mode = getIonMode(this); const { labelText, labelId } = getAriaLabel(el, inputId); @@ -926,6 +941,7 @@ Developers can use the "legacy" property to continue using the legacy form marku 'in-item-color': hostContext('ion-item.ion-color', el), 'select-disabled': disabled, 'select-expanded': isExpanded, + 'has-expanded-icon': expandedIcon !== undefined, 'legacy-select': true, }} > @@ -974,7 +990,16 @@ Developers can use the "legacy" property to continue using the legacy form marku */ private renderSelectIcon() { const mode = getIonMode(this); - const icon = mode === 'ios' ? chevronExpand : caretDownSharp; + const { isExpanded, toggleIcon, expandedIcon } = this; + let icon: string; + + if (isExpanded && expandedIcon !== undefined) { + icon = expandedIcon; + } else { + const defaultIcon = mode === 'ios' ? chevronExpand : caretDownSharp; + icon = toggleIcon ?? defaultIcon; + } + return ; } diff --git a/core/src/components/select/test/toggle-icon/index.html b/core/src/components/select/test/toggle-icon/index.html new file mode 100644 index 0000000000..1c144416bb --- /dev/null +++ b/core/src/components/select/test/toggle-icon/index.html @@ -0,0 +1,58 @@ + + + + + Select - toggleIcon + + + + + + + + + + + + + Select - toggleIcon + + + + + + + + Apples + Oranges + Pears + + + + + Apples + Oranges + Pears + + + + + Apples + Oranges + Pears + + + + + + + diff --git a/core/src/components/select/test/toggle-icon/select.e2e.ts b/core/src/components/select/test/toggle-icon/select.e2e.ts new file mode 100644 index 0000000000..c956429358 --- /dev/null +++ b/core/src/components/select/test/toggle-icon/select.e2e.ts @@ -0,0 +1,39 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('select: toggleIcon'), () => { + test('should render a custom toggleIcon', async ({ page }) => { + await page.setContent( + ` + + Apple + + `, + config + ); + + const select = page.locator('ion-select'); + await expect(select).toHaveScreenshot(screenshot(`select-toggle-icon`)); + }); + + test('should render a custom expandedIcon', async ({ page }) => { + await page.setContent( + ` + + Apple + + `, + config + ); + + const select = page.locator('ion-select'); + const popoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent'); + + await select.click(); + await popoverDidPresent.next(); + + await expect(select).toHaveScreenshot(screenshot(`select-expanded-icon`)); + }); + }); +}); diff --git a/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000..beb10c2593 Binary files /dev/null and b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000..fb9550dbb3 Binary files /dev/null and b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000..e3592b3ddf Binary files /dev/null and b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-expanded-icon-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000..78512cad71 Binary files /dev/null and b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000..95155e5a6d Binary files /dev/null and b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000..f80cd24d98 Binary files /dev/null and b/core/src/components/select/test/toggle-icon/select.e2e.ts-snapshots/select-toggle-icon-md-ltr-Mobile-Safari-linux.png differ diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index 88a6bc0010..23131fb7bc 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -739,6 +739,8 @@ export const IonSelect = /*@__PURE__*/ defineContainer