-
Notifications
You must be signed in to change notification settings - Fork 235
feat(calendar): keyboard navigation #4667
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
feat(calendar): keyboard navigation #4667
Conversation
Lighthouse scores
What is this?Lighthouse scores comparing the documentation site built from the PR ("Branch") to that of the production documentation site ("Latest") and the build currently on Transfer Size
Request Count
|
25416fd to
774ac04
Compare
08b7a46 to
a05159f
Compare
| import { testForLitDevWarnings } from '../../../test/testing-helpers.js'; | ||
| import { Calendar } from '@spectrum-web-components/calendar'; | ||
|
|
||
| import { spy } from 'sinon'; |
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.
I found this necessary for checking the component's change event. In SWC it seems like sinon is used a lot, even in those testing-helpers, but here I need just the spy functionality.
Related to: #3856 (comment)
| }); | ||
|
|
||
| after(() => { | ||
| Date.now = originalDateNow; |
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.
If sinon is used in these tests, should we also use a sandbox to stub the Date.now function?
| if (container) { | ||
| render(story, container as HTMLElement); | ||
| } | ||
| if (container) render(story, container as HTMLElement); |
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.
Can this be done in a different manner or should we stick to requestAnimationFrame?
It seems like without this wrapper approach the component renders only the header (month navigation buttons and the current month indicator) and not the grid of days. Does this hide a problem in the component's lifecycle? 🤔
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.
It seems like the issue comes from the interaction of sp-theme and LanguageResolutionController. Explicitly that the controller's hostConnected is called before the sp-theme's constructor, causing the sp-language-context event to be dispatched before the event listener for said event is added in Theme.ts.
This seems to be a workaround but the StoryDecorator locale picker would work only for the stories that use this approach.
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.
Two possible solutions that I've found are:
- Set
bundledtofalsein theswcThemeDecoratorWithConfig(here) - I'm not aware of all the implications and where is this variable set other than the default value. - Add one more call of
resolveLanguagein theLanguageResolutionControllerusing thehostUpdate(making sure to call this only for the first update). For environments other than the storybook this one extra call should not be a problem becauseThemechecks if thecontextConsumerwas already subscribed (here).
Personally I'm leaning more towards the second solution. What do you think about this @Rajdeepc ?
PS: Worth mentioning that both approaches enable the StoryDecorator locale picker for all existing stories. All components that use the LanguageResolutionController will have real-time updates for locale updates from the storybook decorator picker (i.e. sp-number-field). If this behavior is desired I can create a separate PR and land this quicker in SWC.
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.
Do you need more discussion on this @mizgaionutalexandru . I think adding a @state() decorator will reconcile the changes as mentioned by Ruben on the channel
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.
Yes, I would love to clarify this because there are 2 different topics. The state() decorator works for 'normal app environments' let's say, and that's how the storybook decorator is implemented but due to the fact that in storybook the story function gets called before the sp-theme's constructor from the wrapper decorator gets the chance to add the event listener that is needed for the language resolver.
I found that those 2 approaches would fix the issue in the storybook environment.
ee8f9f1 to
25e48a4
Compare
|
I found the following edge case while manual testing this feature:
|
Thank you for checking this out! I think the issue is fixed now, could you double check please? @Rocss |
|
I would also like to add the below functionality.
Example: https://atlassian.design/components/calendar/examples |
Thank you for your input on this one! If you're saying that unavailable/disabled days should be navigable, I think the current approach that aligns with React Spectrum is better, this way the constraints the user has makes him pick an available day, as per the RFC. You can check out the React Spectrum approach here. What are your thoughts on this? |
| } | ||
|
|
||
| if (this.currentDate.month !== initialMonth) | ||
| this.setWeeksInCurrentMonth(); |
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.
This call, as well the ones from handlePreviousMonth and handleNextMonth will be removed from here and added in a special check inside willUpdate, I think this makes more sense and it's less error prone.
I was looking for this rovingtabindex functionality on the date items. Please check how we can navigate through months from date datepicker.mov |
* feat(calendar): keyboard navigation * fix: focus on disabled dates * refactor: storybook absolute Dates for VRTs * chore: update golden img hash * fix(calendar): focusable day management when changing months


Description
Mainly this PR adds the keyboard navigation functionality to the Calendar's grid of days. As per WAI keyboard support for date picker dialogs, the selected day is the only one tabbable from the grid. If there is no day selected the fallback will be the current day and the first day of the displayed month. After the said day is in focus, the following keys and functions apply.
If the user changes the focus out from the grid and back in, his previous location should be the one tabbable.
Also, this PR addresses some code suggestions/improvements such as:
While creating tests for this new functionality I also overwritten old tests. Some tests have a TODO comment and will be addressed in the next PR to account for the new value API of the components.
Related issue(s)
Motivation and context
Not having keyboard navigation the Calendar wouldn't be keyboard accessible.
How has this been tested?
There can be many use-cases and hopefully most of them are covered in unit tests. As per the behaviour explained above, you can apply said key functions by using the component's storybook.
Test case
Did it pass in Desktop?
Did it pass in Mobile?
Did it pass in iPad?
Screenshots (if appropriate)
keyboard.navigation.mov
Types of changes
Checklist
Best practices
This repository uses conventional commit syntax for each commit message; note that the GitHub UI does not use this by default so be cautious when accepting suggested changes. Avoid the "Update branch" button on the pull request and opt instead for rebasing your branch against
main.