fix(overlays): show focus indicators only during keyboard navigation and improve focus management#31165
fix(overlays): show focus indicators only during keyboard navigation and improve focus management#31165brandyscarney wants to merge 24 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| @@ -114,10 +114,6 @@ slot[name="end"]::slotted(*) { | |||
|
|
|||
| // Item in Select Modal | |||
| // -------------------------------------------------- | |||
| :host(.in-select-modal) { | |||
There was a problem hiding this comment.
This was added to hide the focused state.
| @@ -15,11 +15,6 @@ ion-item { | |||
| --border-width: 0; | |||
| } | |||
|
|
|||
| ion-item.ion-focused::part(native)::after { | |||
There was a problem hiding this comment.
This was added to hide the focused state.
There was a problem hiding this comment.
The ionic theme wasn't added to this test so now it is generating screenshots.
There was a problem hiding this comment.
The focus indicator is no longer displayed when opening with a mouse click.
There was a problem hiding this comment.
The focus indicator is no longer displayed when opening with a mouse click.
There was a problem hiding this comment.
The focus indicator is no longer displayed when opening with a mouse click.
There was a problem hiding this comment.
The focus indicator is no longer displayed when opening with a mouse click.
| @@ -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 }) => { | |||
There was a problem hiding this comment.
This now checks for the ionic theme and captures screenshots to avoid future focus behavior regressions.
There was a problem hiding this comment.
This was removed intentionally because we don't want to show the native focus when we are adding our own. We need to add some ion-focused styles to checkbox for ios and md to fix this. Should I make a follow-up?
There was a problem hiding this comment.
Probably? Just so we don't forget it in the future
There was a problem hiding this comment.
Yes, let's create a ticket and then can you link it to the checkbox modular migration ticket? If it's not done by then, we can just add it during the migration.
There was a problem hiding this comment.
I actually created FW-7585 already for this #31165 (comment) which requests the same thing. I linked it to the modular migration ticket.
| 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.'); | ||
|
|
There was a problem hiding this comment.
I updated the keyboard presses to this:
-await page.keyboard.press('Enter');
+await pageUtils.pressKeys('Enter');This handles Safari tabbing so we are able to check Safari again in most of these tests.
ShaneK
left a comment
There was a problem hiding this comment.
This is looking really good! I tested the functionality and the changes here seem to work fine, but I noticed some things and just wanted a bit of clarification. Good work so far, though!
| }); | ||
|
|
||
| test('it should preserve the last arrow-focused radio when tabbing', async ({ page, pageUtils }) => { | ||
| await page.goto('/src/components/modal/test/sheet', config); |
There was a problem hiding this comment.
Is this useful since you call page.setContent right after it? I thought setContent fully replaced the page's content, but I may be mistaken and am just curious
There was a problem hiding this comment.
I'm under the same assumption. I'm assuming it was from a copy and paste.
thetaPC
left a comment
There was a problem hiding this comment.
Questions:
- Multiple-select interfaces: if a checked option is not the first one, should that checked option receive focus when the overlay is opened with the keyboard, rather than the first option?
Issues:
- Multiple-select on popover: I can only Tab to the first two options.
- Multiple-select on popover: when I Tab into the second option and press Space to select it, it selects the first option instead.
- Action sheet: when I Tab to open it, I do not see the focus indicator on the wrapper the way I do with the alert. This happens both with no value and with a value selected.
Comments:
- The action sheet and alert behave differently from each other, and I find the wrapper ring surprising in general. I would have expected the focus indicator to be on the selected option when an overlay opens with a value, not on the wrapper. You mentioned the wrapper ring is the intended behavior: is that intended based on web standards, or is it inherited from the existing overlay focus-trap?
| private isFocusable(): boolean { | ||
| const focusableChild = this.el.querySelector('.ion-focusable'); | ||
| return this.canActivate() || focusableChild !== null; | ||
| // An item is focusable when it can receive keyboard focus: when it is |
There was a problem hiding this comment.
Is the colon supposed to be a comma?
| // An item is focusable when it can receive keyboard focus: when it is | |
| // An item is focusable when it can receive keyboard focus, when it is |
| }); | ||
|
|
||
| test('it should preserve the last arrow-focused radio when tabbing', async ({ page, pageUtils }) => { | ||
| await page.goto('/src/components/modal/test/sheet', config); |
There was a problem hiding this comment.
I'm under the same assumption. I'm assuming it was from a copy and paste.
There was a problem hiding this comment.
Do we need a snapshot of the entire screen? It seems that a snapshot of the overlay would work. It would be great if we can get in the habit of only capturing the overlay if that's all we need. It limits the need to update snapshots if the main page changes.
| await expect(firstOption).toBeFocused(); | ||
| await expect(firstOption).toHaveClass(/ion-focused/); | ||
|
|
||
| await expect(alert).toHaveScreenshot(screenshot(`select-basic-alert-opened-focused`)); |
There was a problem hiding this comment.
Do we need a snapshot when you've already tested for focus? The snapshots seem redundant in my opinion especially since we have Alert state tests.
| await expect(selectedOption).toBeFocused(); | ||
| await expect(selectedOption).toHaveClass(/ion-focused/); | ||
|
|
||
| await expect(alert).toHaveScreenshot(screenshot(`select-basic-alert-opened-with-value-focused`)); |
There was a problem hiding this comment.
Do we need a snapshot when you've already tested for focus? The snapshots seem redundant in my opinion especially since we have Alert state tests.
| await expect(secondOption.locator('ion-radio')).toBeFocused(); | ||
| await expect(secondOption).toHaveClass(/ion-focused/); | ||
|
|
||
| await expect(popover).toHaveScreenshot(screenshot(`select-basic-popover-opened-focused`)); |
There was a problem hiding this comment.
Do we need a snapshot when you've already tested for focus? The snapshots seem redundant in my opinion especially since we have Select Popover state tests.
| 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`)); |
There was a problem hiding this comment.
Do we need a snapshot when you've already tested for focus? The snapshots seem redundant in my opinion especially since we have Select Popover state tests.
| await expect(firstOption).not.toHaveClass(/ion-focused/); | ||
| }); | ||
|
|
||
| test('it should scroll to selected option when opened', async ({ page }) => { |
There was a problem hiding this comment.
This test feels like it should live under select-popover instead.
| await expect(secondOption.locator('ion-radio')).toBeFocused(); | ||
| await expect(secondOption).toHaveClass(/ion-focused/); | ||
|
|
||
| await expect(modal).toHaveScreenshot(screenshot(`select-basic-modal-opened-focused`)); |
There was a problem hiding this comment.
Do we need a snapshot when you've already tested for focus? The snapshots seem redundant in my opinion especially since we have Select Modal state tests.
| 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`)); |
There was a problem hiding this comment.
Do we need a snapshot when you've already tested for focus? The snapshots seem redundant in my opinion especially since we have Select Modal state tests.
Issue number: internal
What is the current behavior?
The focus indicator is displayed on the option when opening certain interfaces by clicking on the Select component:
What is the new behavior?
Shift + Tabto go backwards in orderDoes this introduce a breaking change?
Other information