-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(inline-time-picker): support focus and focus traps (VIV-2296) #2146
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
import { elementUpdated, fixture } from '@vivid-nx/shared'; | ||
import { InlineTimePicker } from './inline-time-picker.ts'; | ||
import { beforeEach } from 'vitest'; | ||
import { TrappedFocus } from '../../../shared/patterns'; | ||
import { InlineTimePicker } from './inline-time-picker'; | ||
import '.'; | ||
|
||
const COMPONENT_TAG = 'vwc-inline-time-picker'; | ||
|
@@ -24,17 +26,15 @@ describe('vwc-inline-time-picker', () => { | |
const getLabels = (type: 'hours' | 'minutes' | 'seconds' | 'meridies') => | ||
getAllPickerItems(type).map((item) => item.innerHTML.trim()); | ||
|
||
const pressKey = ( | ||
key: string, | ||
options: KeyboardEventInit = {}, | ||
triggerElement = false | ||
) => { | ||
const triggeredElement = triggerElement | ||
? element | ||
: element.shadowRoot!.activeElement; | ||
triggeredElement!.dispatchEvent( | ||
new KeyboardEvent('keydown', { key, bubbles: true, ...options }) | ||
); | ||
const pressKey = (key: string, options: KeyboardEventInit = {}) => { | ||
const event = new KeyboardEvent('keydown', { | ||
key, | ||
bubbles: true, | ||
composed: true, | ||
...options, | ||
}); | ||
element.shadowRoot!.activeElement!.dispatchEvent(event); | ||
return event; | ||
}; | ||
|
||
const isScrolledToTop = (element: HTMLElement) => | ||
|
@@ -596,5 +596,48 @@ describe('vwc-inline-time-picker', () => { | |
expect(isScrolledIntoView(getPickerItem('hours', '00'))).toBe(true); | ||
}); | ||
}); | ||
|
||
describe('focus trap support', () => { | ||
const originalIgnoreEvent = TrappedFocus.ignoreEvent; | ||
beforeEach(() => { | ||
TrappedFocus.ignoreEvent = vi.fn(); | ||
}); | ||
afterEach(() => { | ||
TrappedFocus.ignoreEvent = originalIgnoreEvent; | ||
}); | ||
|
||
it('should submit Tab keydown events that move focus internally to be ignored by focus traps', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I see in this test is: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And how will you understand the purpose without this description? I think it will not become clearer by phrasing it like that |
||
(element.shadowRoot!.querySelector('#hours') as HTMLElement).focus(); | ||
|
||
const event = pressKey('Tab'); | ||
|
||
expect(TrappedFocus.ignoreEvent).toHaveBeenCalledTimes(1); | ||
expect(TrappedFocus.ignoreEvent).toHaveBeenCalledWith(event); | ||
}); | ||
|
||
it('should not submit Tab keydown events that move focus out', () => { | ||
(element.shadowRoot!.querySelector('#hours') as HTMLElement).focus(); | ||
pressKey('Tab', { shiftKey: true }); | ||
(element.shadowRoot!.querySelector('#minutes') as HTMLElement).focus(); | ||
pressKey('Tab'); | ||
|
||
expect(TrappedFocus.ignoreEvent).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('focus method', () => { | ||
it('should focus the first picker programmatically to avoid visual focus', async () => { | ||
const firstPicker = element.shadowRoot!.querySelector( | ||
'#hours' | ||
) as HTMLElement; | ||
const focusSpy = vi.spyOn(firstPicker, 'focus'); | ||
const options = {}; | ||
|
||
element.focus(options); | ||
|
||
expect(element.shadowRoot!.activeElement).toBe(firstPicker); | ||
expect(focusSpy).toHaveBeenCalledWith(options); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO describes should describe API's.
I know there's more like this in our codebase. It should be refactored.
Anyway, it describes nothing but a single use case so should be removed and its it remain under keyboard interactions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I think we should use describe to group related behaviour. This is similar to how we have
describe("form association")
in the codebase.I don't want to move this under "time picker > keyboard navigation" as it has nothing to do with keyboard navigation for picking times
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What scenario does it test? What user action?