diff --git a/core/src/components/action-sheet/action-sheet.tsx b/core/src/components/action-sheet/action-sheet.tsx index 7012980439f..1a4922f5746 100644 --- a/core/src/components/action-sheet/action-sheet.tsx +++ b/core/src/components/action-sheet/action-sheet.tsx @@ -642,6 +642,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface { class="action-sheet-group" ref={(el) => (this.groupEl = el)} role={hasRadioButtons ? 'radiogroup' : undefined} + data-roving-focus={hasRadioButtons ? true : undefined} > {header !== undefined && (
(this.dragHandleEl = el)} > )} diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts b/core/src/components/modal/test/sheet/modal.e2e.ts index db3f9d0632c..42ab591f7e0 100644 --- a/core/src/components/modal/test/sheet/modal.e2e.ts +++ b/core/src/components/modal/test/sheet/modal.e2e.ts @@ -391,6 +391,75 @@ configs({ modes: ['ios', 'ionic-ios'], directions: ['ltr'] }).forEach(({ title, await expect(dragHandle).toBeFocused(); }); + + test('it should preserve the last arrow-focused radio when tabbing', async ({ page, pageUtils }) => { + await page.goto('/src/components/modal/test/sheet', config); + + await page.setContent( + ` + + Open + + + + Options + + Cancel + + + + + + + + One + + + Two + + + Three + + + + + + + + `, + config + ); + + const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent'); + + await page.click('#open-modal'); + await ionModalDidPresent.next(); + + const modal = page.locator('ion-modal'); + const firstRadio = modal.locator('ion-radio').nth(0); + const secondRadio = modal.locator('ion-radio').nth(1); + const handle = modal.locator('.modal-handle'); + + await firstRadio.focus(); + await expect(firstRadio).toBeFocused(); + + await pageUtils.pressKeys('ArrowDown'); + await expect(secondRadio).toBeFocused(); + + await pageUtils.pressKeys('Tab'); + await expect(handle).toBeFocused(); + }); }); test.describe(title('sheet modal: drag events'), () => { diff --git a/core/src/components/radio-group/radio-group.tsx b/core/src/components/radio-group/radio-group.tsx index f1ff225faa0..4d597db617c 100644 --- a/core/src/components/radio-group/radio-group.tsx +++ b/core/src/components/radio-group/radio-group.tsx @@ -386,6 +386,7 @@ export class RadioGroup implements ComponentInterface { aria-labelledby={label ? labelId : null} aria-describedby={this.hintTextId} aria-invalid={this.isInvalid ? 'true' : undefined} + data-roving-focus onClick={this.onClick} > {this.renderHintText()} diff --git a/core/src/components/radio/radio.common.scss b/core/src/components/radio/radio.common.scss index 75677ccbfdf..ebb7b8194b7 100644 --- a/core/src/components/radio/radio.common.scss +++ b/core/src/components/radio/radio.common.scss @@ -51,6 +51,7 @@ input { box-sizing: border-box; } +// Hide the native focus outline. :host(:focus) { outline: none; } diff --git a/core/src/components/select-modal/select-modal.ionic.scss b/core/src/components/select-modal/select-modal.ionic.scss index 51b215ae88a..9e257f8b35c 100644 --- a/core/src/components/select-modal/select-modal.ionic.scss +++ b/core/src/components/select-modal/select-modal.ionic.scss @@ -23,18 +23,6 @@ ion-item { --border-radius: #{globals.$ion-border-radius-400}; } -// TODO(): Remove this when the focus styles are added back to the interface -ion-item.ion-focused::part(native)::after { - // Your styles for the ::after pseudo element when ion-item is focused - outline: none; -} - -ion-item.ion-focused.item-checkbox-checked, -ion-item.ion-focused.item-radio-checked { - --background-focused: #{globals.$ion-bg-primary-subtle-default}; - --background-focused-opacity: 1; -} - // Toolbar // ---------------------------------------------------------------- diff --git a/core/src/components/select-modal/select-modal.tsx b/core/src/components/select-modal/select-modal.tsx index 561ba79c6cc..baafc792e83 100644 --- a/core/src/components/select-modal/select-modal.tsx +++ b/core/src/components/select-modal/select-modal.tsx @@ -143,6 +143,7 @@ export class SelectModal implements ComponentInterface { return ( {this.header}} - this.closeModal()}> + this.closeModal()} + > {this.cancelIcon ? ( ) : ( diff --git a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Chrome-linux.png index 2db82f13b83..3787c9e7b68 100644 Binary files a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Firefox-linux.png index bc16961c5ff..1e6b6d973e7 100644 Binary files a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Safari-linux.png index a8089b419fa..701a847907a 100644 Binary files a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-checkbox-states-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Chrome-linux.png index 23d9800a4ec..26762bd525f 100644 Binary files a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Firefox-linux.png index 38c83ed8738..c30b69efea4 100644 Binary files a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Safari-linux.png index 72a251813f5..a766e706cfa 100644 Binary files a/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/select-modal/test/states/select-modal.e2e.ts-snapshots/select-modal-radio-states-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/select.tsx b/core/src/components/select/select.tsx index 61f037bb10a..24b6024f051 100644 --- a/core/src/components/select/select.tsx +++ b/core/src/components/select/select.tsx @@ -4,7 +4,7 @@ import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, h, import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config'; import type { NotchController } from '@utils/forms'; import { compareOptions, createNotchController, isOptionSelected, checkInvalidState } from '@utils/forms'; -import { focusVisibleElement, renderHiddenInput, inheritAttributes } from '@utils/helpers'; +import { suppressFocusVisible, renderHiddenInput, inheritAttributes } from '@utils/helpers'; import type { Attributes } from '@utils/helpers'; import { printIonWarning } from '@utils/logging'; import { actionSheetController, alertController, popoverController, modalController } from '@utils/overlays'; @@ -436,54 +436,72 @@ export class Select implements ComponentInterface { // Add logic to scroll selected item into view before presenting const scrollSelectedIntoView = () => { const indexOfSelected = this.childOpts.findIndex((o) => o.value === this.value); + + /** + * Determine which option to focus when the overlay opens: the selected + * option if the select has a value, otherwise the first enabled option. + */ + let optionToFocus: HTMLElement | null = null; + if (indexOfSelected > -1) { const selectedItem = overlay.querySelector( `.select-interface-option:nth-of-type(${indexOfSelected + 1})` ); + /** + * If the option contains an `ion-radio` or `ion-checkbox`, focus + * that instead of the option element itself. This ensures that + * screen readers will announce the role and state of the element + * (e.g. "radio button, checked") rather than only the option text. + * Alert and action sheet options are plain buttons, so fall back to + * focusing the option element itself in those cases. + */ if (selectedItem) { - /** - * Browsers such as Firefox do not - * correctly delegate focus when manually - * focusing an element with delegatesFocus. - * We work around this by manually focusing - * the interactive element. - * ion-radio and ion-checkbox are the only - * elements that ion-select-popover uses, so - * we only need to worry about those two components - * when focusing. - */ - const interactiveEl = selectedItem.querySelector('ion-radio, ion-checkbox') as - | HTMLIonRadioElement - | HTMLIonCheckboxElement - | null; + const interactiveEl = selectedItem.querySelector('ion-radio, ion-checkbox'); if (interactiveEl) { selectedItem.scrollIntoView({ block: 'nearest' }); - // Needs to be called before `focusVisibleElement` to prevent issue with focus event bubbling - // and removing `ion-focused` style - interactiveEl.setFocus(); } - - focusVisibleElement(selectedItem); + optionToFocus = interactiveEl ?? selectedItem; } } else { /** * If no value is set then focus the first enabled option. */ - const firstEnabledOption = overlay.querySelector( + optionToFocus = overlay.querySelector( 'ion-radio:not(.radio-disabled), ion-checkbox:not(.checkbox-disabled)' - ) as HTMLIonRadioElement | HTMLIonCheckboxElement | null; + ); + } - if (firstEnabledOption) { - /** - * Focus the option for the same reason as we do above. - * - * Needs to be called before `focusVisibleElement` to prevent issue with focus event bubbling - * and removing `ion-focused` style - */ - firstEnabledOption.setFocus(); + if (optionToFocus) { + /** + * Focus the option directly (`setFocus()`/`focus()`) rather than + * with `focusVisibleElement()`. `focusVisibleElement()` forces the + * `ion-focused` focus ring on regardless of how the overlay was + * opened, whereas a plain focus lets the focus-visible utility + * decide: it shows the ring only when the overlay was opened with + * the keyboard and hides it for pointer opens. + * + * `ion-radio` and `ion-checkbox` expose `setFocus`, which correctly + * delegates focus to their inner focusable element. This is needed + * because browsers such as Firefox do not delegate focus correctly + * when focusing an element with delegatesFocus. When the option is + * a plain button it can be focused directly. + */ + if (optionToFocus.matches('ion-radio, ion-checkbox')) { + (optionToFocus as HTMLIonRadioElement | HTMLIonCheckboxElement).setFocus(); + } else { + optionToFocus.focus(); + } - focusVisibleElement(firstEnabledOption.closest('ion-item')!); + /** + * In the alert interface, when tabbing and pressing enter to open + * the select, the value that is currently selected flashes the + * focused state briefly before moving to its wrapper. This suppresses + * the focus visible state, so that the option will only show as + * focused when navigating with the keyboard. + */ + if (this.interface === 'alert') { + suppressFocusVisible(); } } }; diff --git a/core/src/components/select/test/basic/index.html b/core/src/components/select/test/basic/index.html index e8bd88d3736..d2cb7da9a4d 100644 --- a/core/src/components/select/test/basic/index.html +++ b/core/src/components/select/test/basic/index.html @@ -340,6 +340,7 @@ header: 'Pizza Toppings are really long', breakpoints: [0.5], initialBreakpoint: 0.5, + handleBehavior: 'cycle', }; customModalSelect.interfaceOptions = customModalSheetOptions; diff --git a/core/src/components/select/test/basic/select.e2e.ts b/core/src/components/select/test/basic/select.e2e.ts index 6b797740b96..c9d5f9072c3 100644 --- a/core/src/components/select/test/basic/select.e2e.ts +++ b/core/src/components/select/test/basic/select.e2e.ts @@ -8,7 +8,7 @@ import { configs, test } from '@utils/test/playwright'; * does not. The overlay rendering is already tested in the respective * test files. */ -configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { +configs({ modes: ['ios', 'md', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { test.describe(title('select: basic'), () => { test.beforeEach(async ({ page }) => { await page.goto('/src/components/select/test/basic', config); @@ -35,7 +35,11 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { await expect(alert).toHaveScreenshot(screenshot(`select-basic-alert-scroll-to-selected`)); }); - test('it should not focus any option when opened with no value', async ({ page }) => { + // On open, the alert focuses its wrapper (mirroring the native alert + // behavior) rather than an option, so no option is focused or shows the + // focus ring. Tabbing then moves focus into the radio group, focusing + // the first option and showing the focus ring. + test('it should focus the wrapper on open, then the first option on Tab', async ({ page, pageUtils }) => { // ion-app is required to apply the focused styles await page.setContent( ` @@ -60,12 +64,26 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const alert = page.locator('ion-alert'); - // Verify that no option has the ion-focused class - const focusedOptions = alert.locator('.alert-radio-button.ion-focused'); - await expect(focusedOptions).toHaveCount(0); + // On open the wrapper is focused and no option has the focus ring. + await expect(alert.locator('.alert-wrapper')).toBeFocused(); + await expect(alert.locator('.alert-radio-button.ion-focused')).toHaveCount(0); + + // Tabbing moves focus into the radio group, focusing the first option + // and showing the focus ring. + await pageUtils.pressKeys('Tab'); + await page.waitForChanges(); + + const firstOption = alert.locator('.alert-radio-button').nth(0); + await expect(firstOption).toBeFocused(); + await expect(firstOption).toHaveClass(/ion-focused/); + + await expect(alert).toHaveScreenshot(screenshot(`select-basic-alert-opened-focused`)); }); - test('it should not focus any option when opened with a value', async ({ page }) => { + // Same as above, but with a selected value: the wrapper is focused on + // open and tabbing focuses the selected option (the roving tabindex + // points at the checked radio) rather than the first option. + test('it should focus the wrapper on open, then the selected option on Tab', async ({ page, pageUtils }) => { // ion-app is required to apply the focused styles await page.setContent( ` @@ -90,9 +108,101 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const alert = page.locator('ion-alert'); - // Alert interface doesn't apply ion-focused class to selected options - const focusedOptions = alert.locator('.alert-radio-button.ion-focused'); - await expect(focusedOptions).toHaveCount(0); + // On open the wrapper is focused and no option has the focus ring. + await expect(alert.locator('.alert-wrapper')).toBeFocused(); + await expect(alert.locator('.alert-radio-button.ion-focused')).toHaveCount(0); + + // Tabbing moves focus to the selected option and shows the focus ring. + await pageUtils.pressKeys('Tab'); + await page.waitForChanges(); + + const selectedOption = alert.locator('.alert-radio-button').nth(1); + await expect(selectedOption).toBeFocused(); + await expect(selectedOption).toHaveClass(/ion-focused/); + + await expect(alert).toHaveScreenshot(screenshot(`select-basic-alert-opened-with-value-focused`)); + }); + + // When opening with the keyboard (Tab to the select, then Enter), the + // focus indicator should display on the alert wrapper. + test('it should show the focus indicator when opened with the keyboard', async ({ page, pageUtils, skip }) => { + // TODO (ROU-5437) + skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent'); + + // Tab to the select and open it with the keyboard. + await pageUtils.pressKeys('Tab'); + await pageUtils.pressKeys('Enter'); + await ionAlertDidPresent.next(); + + await page.waitForChanges(); + + const alert = page.locator('ion-alert'); + + // The wrapper should have :focus-visible, and no option should + // have the .ion-focused class. + const wrapper = alert.locator('.alert-wrapper'); + await expect(wrapper).toBeFocused(); + expect(await wrapper.evaluate((el) => el.matches(':focus-visible'))).toBe(true); + await expect(alert.locator('.alert-radio-button.ion-focused')).toHaveCount(0); + }); + + // When opening with the keyboard and a value, the focus indicator + // still displays on the wrapper, not the selected option. + test('it should focus the wrapper when opened with the keyboard and a value', async ({ + page, + pageUtils, + skip, + }) => { + // TODO (ROU-5437) + skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent'); + + // Tab to the select and open it with the keyboard. + await pageUtils.pressKeys('Tab'); + await pageUtils.pressKeys('Enter'); + await ionAlertDidPresent.next(); + + await page.waitForChanges(); + + const alert = page.locator('ion-alert'); + + // The wrapper (not the selected option) has :focus-visible, and no + // option has the .ion-focused class. + const wrapper = alert.locator('.alert-wrapper'); + await expect(wrapper).toBeFocused(); + expect(await wrapper.evaluate((el) => el.matches(':focus-visible'))).toBe(true); + await expect(alert.locator('.alert-radio-button.ion-focused')).toHaveCount(0); }); }); @@ -117,7 +227,10 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { await expect(actionSheet).toHaveScreenshot(screenshot(`select-basic-action-sheet-scroll-to-selected`)); }); - test('it should not focus any option when opened with no value', async ({ page }) => { + // On open, the action sheet focuses its dialog rather than an option, + // so no option is focused or shows the focus ring. Tabbing then moves + // focus to the first option and shows the focus ring. + test('it should focus the dialog on open, then the first option on Tab', async ({ page, pageUtils }) => { // ion-app is required to apply the focused styles await page.setContent( ` @@ -142,12 +255,27 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const actionSheet = page.locator('ion-action-sheet'); - // Verify that none of the options have the ion-focused class - const focusedOptions = actionSheet.locator('.action-sheet-button.ion-focused'); - await expect(focusedOptions).toHaveCount(0); + // On open the dialog is focused and no option has the focus ring. + await expect(actionSheet).toBeFocused(); + await expect(actionSheet.locator('.action-sheet-button.ion-focused')).toHaveCount(0); + + // Tabbing moves focus to the first option and shows the focus ring. + await pageUtils.pressKeys('Tab'); + await page.waitForChanges(); + + const firstOption = actionSheet.locator('.action-sheet-button').nth(0); + await expect(firstOption).toBeFocused(); + await expect(firstOption).toHaveClass(/ion-focused/); + + await expect(actionSheet).toHaveScreenshot(screenshot(`select-basic-action-sheet-opened-focused`)); }); - test('it should focus the second option when opened with a value', async ({ page }) => { + // Same as above, but with a selected value: the selected option is + // focused on open and tabbing moves focus to the Cancel button. + test('it should focus the selected option on open, then the Cancel button on Tab', async ({ + page, + pageUtils, + }) => { // ion-app is required to apply the focused styles await page.setContent( ` @@ -172,9 +300,19 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const actionSheet = page.locator('ion-action-sheet'); - // Find the button containing "Bananas" and verify it has the ion-focused class + // On open the selected option is focused and no option has the focus ring. const bananasOption = actionSheet.locator('.action-sheet-button:has-text("Bananas")'); - await expect(bananasOption).toHaveClass(/ion-focused/); + await expect(bananasOption).toBeFocused(); + await expect(actionSheet.locator('.action-sheet-button.ion-focused')).toHaveCount(0); + + // Tabbing moves focus to the Cancel button and shows the focus ring. + await pageUtils.pressKeys('Tab'); + await page.waitForChanges(); + const cancelButton = actionSheet.getByRole('button', { name: 'Cancel' }); + await expect(cancelButton).toBeFocused(); + await expect(cancelButton).toHaveClass(/ion-focused/); + + await expect(actionSheet).toHaveScreenshot(screenshot(`select-basic-action-sheet-opened-with-value-focused`)); }); test('it should focus the second option when opened with a value and a header', async ({ page }) => { @@ -213,36 +351,160 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const actionSheet = page.locator('ion-action-sheet'); - // Find the option containing "Bananas" and verify it has the ion-focused class + // Find the option containing "Bananas" and verify it has focus + // so screen readers announce it const bananasOption = actionSheet.locator('.action-sheet-button:has-text("Bananas")'); - await expect(bananasOption).toHaveClass(/ion-focused/); + await expect(bananasOption).toBeFocused(); + + // Verify the focus indicator is not shown when the action sheet + // first opens. + await expect(actionSheet.locator('.action-sheet-button.ion-focused')).toHaveCount(0); }); - }); - test.describe('select: popover', () => { - test('it should open a popover select', async ({ page, skip }) => { + // When opening with the keyboard (Tab to the select, then Enter), no + // focus indicator should display on the action sheet, but we still + // need to ensure the dialog is focused. + test('it should add the focused class when opened with the keyboard', async ({ page, pageUtils, skip }) => { // TODO (ROU-5437) skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent'); + + // Tab to the select and open it with the keyboard. + await pageUtils.pressKeys('Tab'); + await pageUtils.pressKeys('Enter'); + await ionActionSheetDidPresent.next(); + + await page.waitForChanges(); + + const actionSheet = page.locator('ion-action-sheet'); + + // The action sheet should have :focus-visible, and no option should + // have the .ion-focused class. + await expect(actionSheet).toBeFocused(); + expect(await actionSheet.evaluate((el) => el.matches(':focus-visible'))).toBe(true); + await expect(actionSheet.locator('.action-sheet-button.ion-focused')).toHaveCount(0); + }); + + // When opening with the keyboard and a value, the focus indicator + // should display on the selected option. + test('it should focus the selected option when opened with the keyboard and a value', async ({ + page, + pageUtils, + skip, + }) => { + // TODO (ROU-5437) + skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent'); + + // Tab to the select and open it with the keyboard. + await pageUtils.pressKeys('Tab'); + await pageUtils.pressKeys('Enter'); + await ionActionSheetDidPresent.next(); + + await page.waitForChanges(); + + const actionSheet = page.locator('ion-action-sheet'); + + // The selected option (Bananas) is focused and shows the focus ring. + const bananasOption = actionSheet.locator('.action-sheet-button:has-text("Bananas")'); + await expect(bananasOption).toBeFocused(); + await expect(bananasOption).toHaveClass(/ion-focused/); + }); + }); + + test.describe('select: popover', () => { + test('it should open a popover select', async ({ page }) => { const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent'); await page.click('#customPopoverSelect'); await ionPopoverDidPresent.next(); + await expect(page.locator('ion-popover')).toBeVisible(); + }); + + // On open, the popover focuses the first option (so screen readers + // announce it) rather than the dialog, but no option shows the focus + // ring. Arrowing then moves focus to the next option and shows the focus + // ring. + test('it should focus the first option on open, then the next option on Arrow', async ({ page, pageUtils }) => { + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const select = page.locator('ion-select'); + const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent'); + + await select.click(); + await ionPopoverDidPresent.next(); + + await page.waitForChanges(); + const popover = page.locator('ion-popover'); - // select has no value, so first option should be focused by default - const popoverOption1 = popover.locator('.select-interface-option:first-of-type ion-radio'); - await expect(popoverOption1).toBeFocused(); + // On open the first option is focused and no option has the focus ring. + await expect(popover.locator('.select-interface-option').nth(0).locator('ion-radio')).toBeFocused(); + await expect(popover.locator('.select-interface-option.ion-focused')).toHaveCount(0); - await expect(popover).toBeVisible(); - }); + // Arrowing moves focus to the next option and shows the focus ring. + await pageUtils.pressKeys('ArrowDown'); + await page.waitForChanges(); - test('it should focus the second option when opened with a value', async ({ page, skip }) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + const secondOption = popover.locator('.select-interface-option').nth(1); + await expect(secondOption.locator('ion-radio')).toBeFocused(); + await expect(secondOption).toHaveClass(/ion-focused/); + await expect(popover).toHaveScreenshot(screenshot(`select-basic-popover-opened-focused`)); + }); + + // Same as above, but with a selected value: the popover focuses the + // selected option (so screen readers announce it), but no option + // shows the focus ring. Arrowing then moves focus to the next option + // and shows the focus ring. + test('it should focus the selected option on open, then the next option on Arrow', async ({ + page, + pageUtils, + }) => { // ion-app is required to apply the focused styles await page.setContent( ` @@ -267,25 +529,112 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const popover = page.locator('ion-popover'); - // Find the option containing "Bananas" and verify it has the ion-focused class + // On open the selected option is focused and no option has the focus ring. const bananasOption = popover.locator('.select-interface-option:has-text("Bananas")'); - await expect(bananasOption).toHaveClass(/ion-focused/); + await expect(bananasOption.locator('ion-radio')).toBeFocused(); + await expect(popover.locator('.select-interface-option.ion-focused')).toHaveCount(0); + + // Arrowing moves focus to the next option and shows the focus ring. + await pageUtils.pressKeys('ArrowDown'); + await page.waitForChanges(); + + const orangesOption = popover.locator('.select-interface-option:has-text("Oranges")'); + await expect(orangesOption.locator('ion-radio')).toBeFocused(); + await expect(orangesOption).toHaveClass(/ion-focused/); + + await expect(popover).toHaveScreenshot(screenshot(`select-basic-popover-opened-with-value-focused`)); }); - test('it should scroll to selected option when opened', async ({ page }) => { + // When opening with the keyboard (Tab to the select, then Enter), the + // focus indicator should display on the focused option. + test('it should show the focus indicator when opened with the keyboard', async ({ page, pageUtils, skip }) => { + // TODO (ROU-5437) + skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent'); - await page.click('#popover-select-scroll-to-selected'); + // Tab to the select and open it with the keyboard. + await pageUtils.pressKeys('Tab'); + await pageUtils.pressKeys('Enter'); await ionPopoverDidPresent.next(); + await page.waitForChanges(); + const popover = page.locator('ion-popover'); - await expect(popover).toHaveScreenshot(screenshot(`select-basic-popover-scroll-to-selected`)); + + // The focus indicator should be shown on the focused option. + await expect(popover.locator('.select-interface-option').nth(0)).toHaveClass(/ion-focused/); }); - test('opening a popover with Enter should not immediately dismiss it', async ({ page, skip }, testInfo) => { + // When opening with the keyboard and a value, the focus indicator + // should display on the selected option. + test('it should focus the selected option when opened with the keyboard and a value', async ({ + page, + pageUtils, + skip, + }) => { // TODO (ROU-5437) skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent'); + + // Tab to the select and open it with the keyboard. + await pageUtils.pressKeys('Tab'); + await pageUtils.pressKeys('Enter'); + await ionPopoverDidPresent.next(); + + await page.waitForChanges(); + + const popover = page.locator('ion-popover'); + + // The selected option (Bananas, the 2nd), not the first, is focused and + // shows the focus ring. + const firstOption = popover.locator('.select-interface-option').nth(0); + const selectedOption = popover.locator('.select-interface-option').nth(1); + await expect(selectedOption.locator('ion-radio')).toBeFocused(); + await expect(selectedOption).toHaveClass(/ion-focused/); + await expect(firstOption).not.toHaveClass(/ion-focused/); + }); + + test('it should scroll to selected option when opened', async ({ page }) => { + const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent'); + + await page.click('#popover-select-scroll-to-selected'); + await ionPopoverDidPresent.next(); + + const popover = page.locator('ion-popover'); + await expect(popover).toHaveScreenshot(screenshot(`select-basic-popover-scroll-to-selected`)); + }); + + test('opening a popover with Enter should not immediately dismiss it', async ({ page, pageUtils }, testInfo) => { testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/30561', @@ -307,7 +656,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const ionPopoverDidDismiss = await page.spyOnEvent('ionPopoverDidDismiss'); await page.locator('ion-select button').focus(); - await page.keyboard.press('Enter'); + await pageUtils.pressKeys('Enter'); await ionPopoverDidPresent.next(); const popover = page.locator('ion-popover'); @@ -318,10 +667,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { await expect(popover).toBeVisible(); }); - test('holding Enter to open a popover should not immediately dismiss it', async ({ page, skip }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - + test('holding Enter to open a popover should not immediately dismiss it', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/30561', @@ -371,16 +717,61 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { await ionModalDidPresent.next(); + await expect(page.locator('ion-modal')).toBeVisible(); + }); + + // On open, the modal focuses the first option (so screen readers + // announce it) rather than the dialog, but no option shows the focus + // ring. Arrowing then moves focus to the next option and shows the + // focus ring. + test('it should focus the first option on open, then the next option on Arrow', async ({ page, pageUtils }) => { + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const select = page.locator('ion-select'); + const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent'); + + await select.click(); + await ionModalDidPresent.next(); + + await page.waitForChanges(); + const modal = page.locator('ion-modal'); - // select has no value, so first option should be focused by default - const modalOption1 = modal.locator('.select-interface-option:first-of-type ion-radio'); - await expect(modalOption1).toBeFocused(); + // On open the first option is focused and no option has the focus ring. + await expect(modal.locator('.select-interface-option').nth(0).locator('ion-radio')).toBeFocused(); + await expect(modal.locator('.select-interface-option.ion-focused')).toHaveCount(0); - await expect(modal).toBeVisible(); + // Arrowing moves focus to the next option and shows the focus ring. + await pageUtils.pressKeys('ArrowDown'); + await page.waitForChanges(); + + const secondOption = modal.locator('.select-interface-option').nth(1); + await expect(secondOption.locator('ion-radio')).toBeFocused(); + await expect(secondOption).toHaveClass(/ion-focused/); + + await expect(modal).toHaveScreenshot(screenshot(`select-basic-modal-opened-focused`)); }); - test('it should focus the second option when opened with a value', async ({ page }) => { + // Same as above, but with a selected value: the modal focuses the + // selected option (so screen readers announce it), but no option shows + // the focus ring. Arrowing then moves focus to the next option and + // shows the focus ring. + test('it should focus the selected option on open, then the next option on Arrow', async ({ + page, + pageUtils, + }) => { // ion-app is required to apply the focused styles await page.setContent( ` @@ -405,9 +796,99 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const modal = page.locator('ion-modal'); - // Find the option containing "Bananas" and verify it has the ion-focused class + // On open the selected option is focused and no option has the focus ring. const bananasOption = modal.locator('.select-interface-option:has-text("Bananas")'); - await expect(bananasOption).toHaveClass(/ion-focused/); + await expect(bananasOption.locator('ion-radio')).toBeFocused(); + await expect(modal.locator('.select-interface-option.ion-focused')).toHaveCount(0); + + // Arrowing moves focus to the next option and shows the focus ring. + await pageUtils.pressKeys('ArrowDown'); + await page.waitForChanges(); + + const orangesOption = modal.locator('.select-interface-option:has-text("Oranges")'); + await expect(orangesOption.locator('ion-radio')).toBeFocused(); + await expect(orangesOption).toHaveClass(/ion-focused/); + + await expect(modal).toHaveScreenshot(screenshot(`select-basic-modal-opened-with-value-focused`)); + }); + + // When opening with the keyboard (Tab to the select, then Enter), the + // focus indicator should display on the focused option. + test('it should show the focus indicator when opened with the keyboard', async ({ page, pageUtils, skip }) => { + // TODO (ROU-5437) + skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent'); + + // Tab to the select and open it with the keyboard. + await pageUtils.pressKeys('Tab'); + await pageUtils.pressKeys('Enter'); + await ionModalDidPresent.next(); + + await page.waitForChanges(); + + const modal = page.locator('ion-modal'); + + // The focus indicator should be shown on the focused option. + await expect(modal.locator('.select-interface-option').nth(0)).toHaveClass(/ion-focused/); + }); + + // When opening with the keyboard and a value, the focus indicator + // should display on the selected option. + test('it should focus the selected option when opened with the keyboard and a value', async ({ + page, + pageUtils, + skip, + }) => { + // TODO (ROU-5437) + skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); + + // ion-app is required to apply the focused styles + await page.setContent( + ` + + + Apples + Bananas + Oranges + + + `, + config + ); + + const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent'); + + // Tab to the select and open it with the keyboard. + await pageUtils.pressKeys('Tab'); + await pageUtils.pressKeys('Enter'); + await ionModalDidPresent.next(); + + await page.waitForChanges(); + + const modal = page.locator('ion-modal'); + + // The selected option (Bananas, the 2nd), not the first, is focused and + // shows the focus ring. + const firstOption = modal.locator('.select-interface-option').nth(0); + const selectedOption = modal.locator('.select-interface-option').nth(1); + await expect(selectedOption.locator('ion-radio')).toBeFocused(); + await expect(selectedOption).toHaveClass(/ion-focused/); + await expect(firstOption).not.toHaveClass(/ion-focused/); }); test('it should scroll to selected option when opened', async ({ page }) => { @@ -419,6 +900,89 @@ configs({ directions: ['ltr'] }).forEach(({ title, config, screenshot }) => { const modal = page.locator('ion-modal'); await expect(modal).toHaveScreenshot(screenshot(`select-basic-modal-scroll-to-selected`)); }); + + test('it should support keyboard focus cycling between list, handle, and cancel', async ({ page, pageUtils }) => { + const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent'); + + await page.click('#customModalSelect'); + await ionModalDidPresent.next(); + + const modal = page.locator('ion-modal'); + const firstOption = modal.locator('.select-interface-option:first-of-type ion-radio'); + const secondOption = modal.locator('.select-interface-option:nth-of-type(2) ion-radio'); + const handle = modal.locator('.modal-handle'); + const cancelButton = modal.getByRole('button', { name: 'Cancel' }); + + await expect(firstOption).toBeFocused(); + + // After moving focus with arrow keys, Tab should still visit the handle + // before the cancel button + await pageUtils.pressKeys('ArrowDown'); + await expect(secondOption).toBeFocused(); + await page.waitForChanges(); + await pageUtils.pressKeys('Tab'); + await expect(handle).toBeFocused(); + await pageUtils.pressKeys('Shift+Tab'); + await expect(secondOption).toBeFocused(); + + await pageUtils.pressKeys('ArrowUp'); + await expect(firstOption).toBeFocused(); + + // Forward cycle: list option -> handle -> cancel -> list option + await pageUtils.pressKeys('Tab'); + await expect(handle).toBeFocused(); + + await pageUtils.pressKeys('Tab'); + await expect(cancelButton).toBeFocused(); + + await pageUtils.pressKeys('Tab'); + await expect(firstOption).toBeFocused(); + + // Reverse cycle: list option -> cancel -> handle -> list option + await pageUtils.pressKeys('Shift+Tab'); + await expect(cancelButton).toBeFocused(); + + await pageUtils.pressKeys('Shift+Tab'); + await expect(handle).toBeFocused(); + + await pageUtils.pressKeys('Shift+Tab'); + await expect(firstOption).toBeFocused(); + }); + + test('it should tab through cancel using the last arrow-highlighted option', async ({ page, pageUtils }) => { + const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent'); + + await page.click('#customModalSelect'); + await ionModalDidPresent.next(); + + const modal = page.locator('ion-modal'); + const thirdOption = modal.locator('.select-interface-option:nth-of-type(3) ion-radio'); + const handle = modal.locator('.modal-handle'); + const cancelButton = modal.getByRole('button', { name: 'Cancel' }); + + await pageUtils.pressKeys('ArrowDown'); + await pageUtils.pressKeys('ArrowDown'); + await expect(thirdOption).toBeFocused(); + await page.waitForChanges(); + + await pageUtils.pressKeys('Tab'); + await expect(handle).toBeFocused(); + + await pageUtils.pressKeys('Tab'); + await expect(cancelButton).toBeFocused(); + + await pageUtils.pressKeys('Tab'); + await expect(thirdOption).toBeFocused(); + + await pageUtils.pressKeys('Shift+Tab'); + await expect(cancelButton).toBeFocused(); + + await pageUtils.pressKeys('Shift+Tab'); + await expect(handle).toBeFocused(); + + await pageUtils.pressKeys('Shift+Tab'); + await expect(thirdOption).toBeFocused(); + }); }); }); }); @@ -530,10 +1094,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { expect(ionChange).toHaveReceivedEventTimes(1); }); - test('should fire ionChange when confirming a popover value with Enter', async ({ page, skip }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - + test('should fire ionChange when confirming a popover value with Enter', async ({ page, pageUtils }, testInfo) => { testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/30561', @@ -563,7 +1124,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { const secondRadio = popover.locator('ion-radio').nth(1); await secondRadio.focus(); - await page.keyboard.press('Enter'); + await pageUtils.pressKeys('Enter'); await ionChange.next(); await ionPopoverDidDismiss.next(); @@ -603,11 +1164,8 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should fire ionChange exactly once when confirming a popover value with Space', async ({ page, - skip, + pageUtils, }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/30561', @@ -637,7 +1195,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { const secondRadio = popover.locator('ion-radio').nth(1); await secondRadio.focus(); - await page.keyboard.press('Space'); + await pageUtils.pressKeys('Space'); await ionChange.next(); await ionPopoverDidDismiss.next(); @@ -649,11 +1207,8 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should not fire ionChange when confirming the already-selected popover option with Enter', async ({ page, - skip, + pageUtils, }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/26789', @@ -683,7 +1238,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { const selectedRadio = popover.locator('ion-radio').nth(0); await selectedRadio.focus(); - await page.keyboard.press('Enter'); + await pageUtils.pressKeys('Enter'); await ionPopoverDidDismiss.next(); @@ -694,11 +1249,8 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should not fire ionChange when confirming the already-selected popover option with Space', async ({ page, - skip, + pageUtils, }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/26789', @@ -728,7 +1280,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { const selectedRadio = popover.locator('ion-radio').nth(0); await selectedRadio.focus(); - await page.keyboard.press('Space'); + await pageUtils.pressKeys('Space'); await ionPopoverDidDismiss.next(); @@ -739,11 +1291,8 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should fire ionChange exactly once when confirming a modal value with Enter', async ({ page, - skip, + pageUtils, }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/30561', @@ -773,7 +1322,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { const secondRadio = modal.locator('ion-radio').nth(1); await secondRadio.focus(); - await page.keyboard.press('Enter'); + await pageUtils.pressKeys('Enter'); await ionChange.next(); await ionModalDidDismiss.next(); @@ -785,11 +1334,8 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should fire ionChange exactly once when confirming a modal value with Space', async ({ page, - skip, + pageUtils, }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/30561', @@ -819,7 +1365,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { const secondRadio = modal.locator('ion-radio').nth(1); await secondRadio.focus(); - await page.keyboard.press('Space'); + await pageUtils.pressKeys('Space'); await ionChange.next(); await ionModalDidDismiss.next(); @@ -831,11 +1377,8 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should not fire ionChange when confirming the already-selected modal option with Enter', async ({ page, - skip, + pageUtils, }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/26789', @@ -865,7 +1408,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { const selectedRadio = modal.locator('ion-radio').nth(0); await selectedRadio.focus(); - await page.keyboard.press('Enter'); + await pageUtils.pressKeys('Enter'); await ionModalDidDismiss.next(); @@ -876,11 +1419,8 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should not fire ionChange when confirming the already-selected modal option with Space', async ({ page, - skip, + pageUtils, }, testInfo) => { - // TODO (ROU-5437) - skip.browser('webkit', 'Safari 16 only allows text fields and pop-up menus to be focused.'); - testInfo.annotations.push({ type: 'issue', description: 'https://github.com/ionic-team/ionic-framework/issues/26789', @@ -910,7 +1450,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { const selectedRadio = modal.locator('ion-radio').nth(0); await selectedRadio.focus(); - await page.keyboard.press('Space'); + await pageUtils.pressKeys('Space'); await ionModalDidDismiss.next(); diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d1d9e3e8e00 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..3c6f46f5319 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..06956eb026d Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..566247ab96f Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..53857acb532 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..192b61406fd Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..34f52dd7469 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..cd590b32199 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..61ac7e42e34 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-focused-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d6a03539947 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..ec82326f552 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..e0054c8a53c Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4dcc803f168 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..527a22887fe Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..d20e77721f2 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..580d3b04fa5 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..c2688b9442f Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..e63a5138f12 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..c9b8ff901ff Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..6a610ed52df Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..9f0d3c8b3c2 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png index 1b15e0e4c10..ad0020a7f32 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png index 26f83cbaf0f..6c89ef1b952 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png index 7884f70bed7..6fe7e332b5c 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png index b67c3ff807a..3498eb9ddfc 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png index b2753efbf69..09a35219e1b 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Safari-linux.png index 59581a60761..57602452646 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-action-sheet-scroll-to-selected-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..cf19ba94541 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..c917213a875 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..e9006bf9c38 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d2951bc7e22 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..3813ac535b4 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..e19e913fd4e Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..ef4f14aa464 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..386e5103ba6 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..da5446b5a66 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-focused-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..7f9c0176d17 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..e6cb3c3d770 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..0078acf4043 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..59e89015129 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..05edf457b02 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..70969260ecc Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..39cefb5f777 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..e6af47a87fd Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..f8fead92f20 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..9c157400b39 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..252126e97f7 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..1bb7d4b89c7 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-alert-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f5a8516fbc3 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..3b28c6f7af9 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..93a27f6f3e0 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d762c8817c6 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..b06cc8bce65 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..a6807430952 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..4ed3c30b3c2 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..60e26df3ce4 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..9bbd458910f Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-focused-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..88da194c2e7 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..53c5aa7f605 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..2fa3512f6e7 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f37332b7124 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..55feb85ad16 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..3bab54e2469 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..ad82d42d224 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..2e5d8745810 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..e2661893d91 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..e51a1956b4f Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..4469ae5fdbe Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..c82ff67b404 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png index 43fcd9ef709..c34076f7793 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png index b25f9c98d2f..ee7886439c3 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png index 85abf17ca89..86ddf038b4a 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png index a75ada58efb..b4af5cf9108 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png index 81dd78d1d86..8e269f81bdb 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Safari-linux.png index 5010cc499ea..5fecf379ead 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-modal-scroll-to-selected-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..a2856a581d1 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..4dfa40ee34d Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..ff2ee5dc3f7 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d8abe36f6c0 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..6729de1d87c Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..fcb0748fd8d Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..ac0a9317494 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..7f688f43b8d Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..7685bc5b07c Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-focused-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..cf8e2ca0cec Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..ce96ab7b311 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..cc0cbc7453c Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..ff5bd7eeb75 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..a1b2e47cc38 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..b5570d7de7f Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..d1fbfaa9483 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..93ff7eeacc8 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..b47239e31a6 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-opened-with-value-focused-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..f48320a721f Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..eba655575b6 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png new file mode 100644 index 00000000000..23aefebc5e8 Binary files /dev/null and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png index 1e7861eda5d..d0c359b271c 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png index e4274e14f01..9d6096270a3 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png index 426f785d18b..07af1b2f5e1 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png index c27db8f1d50..d08e3f38d77 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png index f30c52a9d56..75ec154c2fc 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Safari-linux.png index 7717021c56a..a1cc24cbe96 100644 Binary files a/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/basic/select.e2e.ts-snapshots/select-basic-popover-scroll-to-selected-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Chrome-linux.png index 886d1dd9f7d..8c4dc160ecd 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Firefox-linux.png index d2d728a7c1c..4e42987c679 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Safari-linux.png index 063821f6cd7..66d06584d2f 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Chrome-linux.png index 7bd6d9b5eb2..94a1adb90f5 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Firefox-linux.png index 6b94e4b327f..b6d1957e643 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Safari-linux.png index 0500953c143..4a2f8629671 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-modal-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Chrome-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Chrome-linux.png index b3f4cddd661..c4cbc725bca 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Chrome-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Firefox-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Firefox-linux.png index 7693ed60d0c..50469bb8ae4 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Firefox-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Safari-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Safari-linux.png index 0486fbe4fd0..d3bb47a4dc9 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Safari-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ionic-md-ltr-light-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Chrome-linux.png index 815c9a3e0da..e9dc982e01f 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Firefox-linux.png index a3567e18458..b2828859184 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Safari-linux.png index 528b3b23802..3b7c5a19170 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-ios-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Chrome-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Chrome-linux.png index c95a90b27dd..047bc166e15 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Chrome-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Firefox-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Firefox-linux.png index dfa6943cc37..ec5de4bfa5b 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Firefox-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Safari-linux.png b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Safari-linux.png index ef1b48f49f0..8b8d1e5b68c 100644 Binary files a/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Safari-linux.png and b/core/src/components/select/test/rich-content-option/select.e2e.ts-snapshots/select-rich-content-popover-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/utils/helpers.ts b/core/src/utils/helpers.ts index ca7d1f406c7..068281bbb1d 100644 --- a/core/src/utils/helpers.ts +++ b/core/src/utils/helpers.ts @@ -303,6 +303,20 @@ export const focusVisibleElement = (el: HTMLElement) => { } }; +/** + * Clears the keyboard focus ring (`ion-focused`) that the focus-visible + * utility may have applied to elements during a programmatic focus. + * + * Use this after moving DOM focus to an element for assistive technologies + * (e.g. when an overlay opens) where the option should be focused so screen + * readers announce it, but the focus ring should not be shown until the user + * navigates with the keyboard (Tab/Arrow). The focus-visible utility will add + * `ion-focused` again on the next keyboard-driven focus change. + */ +export const suppressFocusVisible = () => { + focusElements([]); +}; + /** * This method is used to add a hidden input to a host element that contains * a Shadow DOM. It does not add the input inside of the Shadow root which diff --git a/core/src/utils/overlays.ts b/core/src/utils/overlays.ts index 4f24ef4efaf..f4b6fd83f02 100644 --- a/core/src/utils/overlays.ts +++ b/core/src/utils/overlays.ts @@ -44,8 +44,133 @@ type OverlayWithFocusTrapProps = HTMLIonOverlayElement & { backdropBreakpoint?: number; }; +const OVERLAY_FOCUS_TRAP_SELECTOR = 'ion-alert,ion-action-sheet,ion-loading,ion-modal,ion-picker-legacy,ion-popover'; + +/** + * The focus trap is generic and knows nothing about which components are + * inside an overlay. Components mark their elements with these `data-*` + * attributes, which the trap reads, to control how Tab moves through them. + * This keeps private component class names (e.g. `.modal-handle`, + * `.action-sheet-button`) out of the trap, so changing a component does + * not silently break Tab order. + * + * - `data-focus-ignore`: skip this element as a tab stop. + * - `data-focus-order`: explicit numeric placement in the tab sequence. + * - `data-roving-focus`: a container whose focusable descendants share a + * single tab stop via a roving tabindex. + */ +const FOCUS_IGNORE_SELECTOR = '[data-focus-ignore]'; +const FOCUS_ORDER_SELECTOR = '[data-focus-order]'; +const ROVING_FOCUS_SELECTOR = '[data-roving-focus]'; + +/** + * Used to restore focus to the correct member when Tab would land on + * a roving group's tabbable slot (e.g. Shift+Tab from the sheet handle + * back into the option list). + */ +type OverlayWithRovingFocusState = HTMLIonOverlayElement & { + trapLastRovingFocus?: HTMLElement; +}; + +/** + * Returns the roving-focus group an element belongs to, or `null`. + * A member is a descendant of a `[data-roving-focus]` container. The + * container itself is not a member. Members share a single tab stop. + */ +const getRovingFocusGroup = (el: HTMLElement): HTMLElement | null => { + const group = el.closest(ROVING_FOCUS_SELECTOR); + return group !== null && group !== el ? group : null; +}; + +/** + * Whether an element participates in a roving-focus group. + */ +const isRovingFocusItem = (el: HTMLElement) => getRovingFocusGroup(el) !== null; + +/** + * Returns the currently focused element for keyboard focus checks. + * + * Starts from `document.activeElement` (non-shadow / light DOM focus). + * If focus is inside one or more open shadow roots + * (e.g. native control inside `ion-radio`), walks through nested + * `shadowRoot.activeElement` values until the innermost focused node is reached. + */ +const getActiveElement = (ownerDoc: Document): HTMLElement | null => { + let active = ownerDoc.activeElement as HTMLElement | null; + if (!active) { + return null; + } + while (active.shadowRoot?.activeElement) { + active = active.shadowRoot.activeElement as HTMLElement; + } + return active; +}; + +/** + * Walks from a focused node (possibly deep inside shadow roots) up to + * the nearest roving-focus host (e.g. `ion-radio` inside an + * `ion-radio-group`, or an action sheet `