| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Thanks for the pull request, @filippovskii09! This repository is currently maintained by @openedx/committers-frontend-app-learning. Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information? If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:
Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
Sorry, something went wrong.
Codecov Report❌ Patch coverage is 98.66667% with 1 line in your changes missing coverage. Please review.
@@ Coverage Diff @@
## master #1817 +/- ##
==========================================
+ Coverage 90.83% 91.26% +0.43%
==========================================
Files 345 350 +5
Lines 5791 5872 +81
Branches 1380 1393 +13
==========================================
+ Hits 5260 5359 +99
+ Misses 512 494 -18
Partials 19 19 ☔ View full report in Codecov by Sentry.
|
Sorry, something went wrong.
| } | ||
|
|
||
| // Tab | ||
| const courseOutlineTrigger = document.querySelector('#courseOutlineTrigger'); |
There was a problem hiding this comment.
[optional]: Maybe it would be better to clarify what exactly this is a sidebar trigger.
| const courseOutlineTrigger = document.querySelector('#courseOutlineTrigger'); | |
| const courseOutlineSidebarTrigger = document.querySelector('#courseOutlineTrigger'); |
Sorry, something went wrong.
There was a problem hiding this comment.
yes, this naming better, fixed
Sorry, something went wrong.
| const leftArrow = document.querySelector('.previous-button'); | ||
| if (leftArrow && !leftArrow.disabled) { | ||
| event.preventDefault(); | ||
| leftArrow.focus(); | ||
| return; | ||
| } | ||
|
|
||
| const rightArrow = document.querySelector('.next-button'); | ||
| if (rightArrow && !rightArrow.disabled) { | ||
| event.preventDefault(); | ||
| rightArrow.focus(); | ||
| } |
There was a problem hiding this comment.
[question]: These are two almost identical constructs. Can we create a function for this that would be convenient to reuse here?
Sorry, something went wrong.
There was a problem hiding this comment.
thx, create helper function for this, fixed
Sorry, something went wrong.
| focusSidebarTriggerBtn(); | ||
| }; | ||
|
|
||
| const handleKeyDown = useCallback((event) => { |
There was a problem hiding this comment.
[nit]: I think it's worth adding a meaningful comment here that would describe how the logic of moving focus around the page works and for which elements.
Sorry, something went wrong.
There was a problem hiding this comment.
i added describe text for function, please check it
Sorry, something went wrong.
| <div className="d-flex align-items-center mb-2"> | ||
| <strong className="p-2.5 d-inline-block course-sidebar-title">{title}</strong> | ||
| {/* TODO: view this title in UI and decide */} | ||
| {/* <strong className="p-2.5 d-inline-block course-sidebar-title">{title}</strong> */} |
There was a problem hiding this comment.
Do we need these comments?
Sorry, something went wrong.
There was a problem hiding this comment.
removed
Sorry, something went wrong.
| const newFocusStatus = !isOpenNotificationStatusBar; | ||
| setSessionStorage(`notificationTrayFocus.${courseId}`, String(newFocusStatus)); | ||
|
|
||
| const isNotificationTrayOpen = getSessionStorage(`notificationTrayStatus.${courseId}`) === 'open'; |
There was a problem hiding this comment.
[nit]: I suggest splitting the logic into two PRs:
Sorry, something went wrong.
There was a problem hiding this comment.
ok, i removed Fixes for storing sidebar state
Sorry, something went wrong.
There was a problem hiding this comment.
PR for storing sidebar state: #1823
Sorry, something went wrong.
There was a problem hiding this comment.
i added this custom hook useSidebarFocusAndKeyboard.js to separate the logic which we added in this PR
because a lot of logic interfered with orientation in the component
Sorry, something went wrong.
| import { renderHook, act } from '@testing-library/react'; | ||
| import SidebarContext from '../../SidebarContext'; |
There was a problem hiding this comment.
| import { renderHook, act } from '@testing-library/react'; | |
| import SidebarContext from '../../SidebarContext'; | |
| import { renderHook, act } from '@testing-library/react'; | |
| import SidebarContext from '../../SidebarContext'; |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
There was a problem hiding this comment.
Resolved
Sorry, something went wrong.
| @@ -0,0 +1,152 @@ | |||
| import React from 'react'; | |||
There was a problem hiding this comment.
Do we need this React import?
Sorry, something went wrong.
There was a problem hiding this comment.
removed
Sorry, something went wrong.
| import { Factory } from 'rosie'; | ||
| import { |
There was a problem hiding this comment.
[code style]: Let’s separate external library imports from local imports with a blank line for better readability.
| import { Factory } from 'rosie'; | |
| import { | |
| import { Factory } from 'rosie'; | |
| import { |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
There was a problem hiding this comment.
Should the line import { createRef } from 'react'; come before import { Factory } from 'rosie';? The idea is to separate library imports from local imports.
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| import { Factory } from 'rosie'; | ||
| import messages from '../../../messages'; |
There was a problem hiding this comment.
[question]: Does it make sense to use aliases here?
| import { Factory } from 'rosie'; | |
| import messages from '../../../messages'; | |
| import { Factory } from 'rosie'; | |
| import messages from '../../../messages'; |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| import PropTypes from 'prop-types'; | ||
| import { Factory } from 'rosie'; | ||
|
|
||
| import messages from '../../../messages'; |
There was a problem hiding this comment.
[question]: Does it make sense to use aliases here?
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| }); | ||
|
|
||
| expect(mockToggleSidebar).toHaveBeenCalledWith(null); | ||
| act(() => { jest.runAllTimers(); }); |
There was a problem hiding this comment.
| act(() => { jest.runAllTimers(); }); | |
| act(() => jest.runAllTimers()); |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
|
|
||
| const mockUseSidebarFocusAndKeyboard = useSidebarFocusAndKeyboard; | ||
|
|
||
| describe('SidebarBase (Refactored)', () => { |
There was a problem hiding this comment.
| describe('SidebarBase (Refactored)', () => { | |
| describe('SidebarBase', () => { |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| } | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [sidebarId, toggleSidebar]); | ||
| }, [toggleSidebar]); |
There was a problem hiding this comment.
[clarify]: Is there any reason why sidebarId was removed from deps? Also, do I need to remove // eslint-disable-next-line react-hooks/exhaustive-deps?
Sorry, something went wrong.
There was a problem hiding this comment.
no reason, removed
Sorry, something went wrong.
There was a problem hiding this comment.
My question is related to the fact that sidebarId was previously included in the dependency array. Why was it removed?
Sorry, something went wrong.
There was a problem hiding this comment.
because sidebarId it was an unnecessary dependency
Sorry, something went wrong.
| const triggerButton = screen.getByRole('button', { | ||
| name: messages.openNotificationTrigger.defaultMessage, | ||
| }); | ||
| fireEvent.click(triggerButton); |
There was a problem hiding this comment.
Lets use userEvent
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| sectionId: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations', | ||
| sequenceId: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions', | ||
|
|
||
| currentSequence: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions', |
There was a problem hiding this comment.
Just curious. Why was this change only added now?
Sorry, something went wrong.
There was a problem hiding this comment.
my mistake, removed
Sorry, something went wrong.
| import { tryFocusAndPreventDefault } from '../../utils'; | ||
| import SidebarContext from '../../SidebarContext'; | ||
|
|
||
| export const useSidebarFocusAndKeyboard = (sidebarId, triggerButtonSelector = '.sidebar-trigger-btn') => { |
There was a problem hiding this comment.
Resolved
Sorry, something went wrong.
| import { renderHook, act } from '@testing-library/react'; | ||
| import SidebarContext from '../../SidebarContext'; |
There was a problem hiding this comment.
Resolved
Sorry, something went wrong.
| import { Factory } from 'rosie'; | ||
| import { |
There was a problem hiding this comment.
Should the line import { createRef } from 'react'; come before import { Factory } from 'rosie';? The idea is to separate library imports from local imports.
Sorry, something went wrong.
|
|
||
| it('should hide title bar when showTitleBar prop is false', () => { | ||
| renderSidebar({ currentSidebar: SIDEBAR_ID }, { showTitleBar: false }); | ||
| expect(screen.queryByText('Test Sidebar Title')).not.toBeInTheDocument(); |
There was a problem hiding this comment.
Can we replace the static text in these tests with values from defaultProps?
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| } | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [sidebarId, toggleSidebar]); | ||
| }, [toggleSidebar]); |
There was a problem hiding this comment.
My question is related to the fact that sidebarId was previously included in the dependency array. Why was it removed?
Sorry, something went wrong.
| import { Factory } from 'rosie'; | ||
| import messages from '@src/courseware/course/messages'; |
There was a problem hiding this comment.
| import { Factory } from 'rosie'; | |
| import messages from '@src/courseware/course/messages'; | |
| import { Factory } from 'rosie'; | |
| import messages from '@src/courseware/course/messages'; |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| @@ -0,0 +1,9 @@ | |||
| export const tryFocusAndPreventDefault = (event, selector) => { | |||
There was a problem hiding this comment.
Resolved
Sorry, something went wrong.
|
Sandbox deployment successful 🚀 |
Sorry, something went wrong.
|
Sandbox deployment successful 🚀 |
Sorry, something went wrong.
|
Hey @PKulkoRaccoonGang, am I right in assuming that this is another PR you'll add when you create your product proposal for a11y improvements? |
Sorry, something went wrong.
|
|
||
| mockQuerySelector = jest.spyOn(document, 'querySelector'); | ||
| mockQuerySelector.mockImplementation((selector) => { | ||
| if (selector === TRIGGER_SELECTOR) { return triggerButtonMock; } |
There was a problem hiding this comment.
| if (selector === TRIGGER_SELECTOR) { return triggerButtonMock; } | |
| if (selector === TRIGGER_SELECTOR) { | |
| return triggerButtonMock; | |
| } |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| if (selector === '#courseOutlineSidebarTrigger') { return mockOutlineTrigger; } | ||
| if (selector === '.previous-button') { return mockPrevButton; } | ||
| if (selector === '.next-button') { return mockNextButton; } |
There was a problem hiding this comment.
| if (selector === '#courseOutlineSidebarTrigger') { return mockOutlineTrigger; } | |
| if (selector === '.previous-button') { return mockPrevButton; } | |
| if (selector === '.next-button') { return mockNextButton; } | |
| if (selector === '#courseOutlineSidebarTrigger') { | |
| return mockOutlineTrigger; | |
| } | |
| if (selector === '.previous-button') { | |
| return mockPrevButton; | |
| } | |
| if (selector === '.next-button') { | |
| return mockNextButton; | |
| } |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| const { result } = renderHookWithContext(mockContextValue); | ||
|
|
||
| mockQuerySelector.mockImplementation((selector) => { | ||
| if (selector === TRIGGER_SELECTOR) { return triggerButtonMock; } |
There was a problem hiding this comment.
| if (selector === TRIGGER_SELECTOR) { return triggerButtonMock; } | |
| if (selector === TRIGGER_SELECTOR) { | |
| return triggerButtonMock; | |
| } |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| if (selector === '.call-to-action-btn') { return mockCtaButton; } | ||
| if (selector === TRIGGER_SELECTOR) { return triggerButtonMock; } |
There was a problem hiding this comment.
| if (selector === '.call-to-action-btn') { return mockCtaButton; } | |
| if (selector === TRIGGER_SELECTOR) { return triggerButtonMock; } | |
| if (selector === '.call-to-action-btn') { | |
| return mockCtaButton; | |
| } | |
| if (selector === TRIGGER_SELECTOR) { | |
| return triggerButtonMock; | |
| } |
Sorry, something went wrong.
There was a problem hiding this comment.
fixed
Sorry, something went wrong.
| sidebarTriggerBtnRef.current?.blur(); | ||
| sidebarTriggerBtnRef.current?.blur(); | ||
|
|
||
| sidebarTriggerBtnRef.current?.blur(); |
There was a problem hiding this comment.
[clarify]: Do we need to add blur to sidebarTriggerBtnRef three times here?
Sorry, something went wrong.
There was a problem hiding this comment.
removed extra lines
Sorry, something went wrong.
|
Sandbox deployment failed 💥 |
Sorry, something went wrong.
|
Sandbox deployment failed 💥 |
Sorry, something went wrong.
|
Sandbox deployment successful 🚀 |
Sorry, something went wrong.
|
Sandbox deployment failed 💥 |
Sorry, something went wrong.
|
Sandbox deployment failed 💥 |
Sorry, something went wrong.
|
Sandbox deployment successful 🚀 |
Sorry, something went wrong.
|
Hi @openedx/openedx-product-managers could you please take a look at this PR? Thanks in advance! |
Sorry, something went wrong.
|
Sandbox deployment failed 💥 |
Sorry, something went wrong.
|
Hi @PKulkoRaccoonGang - the Product team doesn't have any a11y experts, so I'm not sure if any of us can be called upon. I think an a11y expert should be the one to approve these types of PRs, but I'm not sure who that would be - @brian-smith-tcril any advice? |
Sorry, something went wrong.
|
@ali-hugo @cassiezamparini Given what @sarina said above, would you have any ideas/suggestions on how to deal with a11y PRs like this one in the context of product review? We'll need to find a way to get them unblocked for engineering review. CC @filippovskii09 @PKulkoRaccoonGang |
Sorry, something went wrong.
|
@sarina A while back Mary Ziegler joined some UX and UI chats. She's an accessibility expert. However I'm not sure she's still part of the community. @itsjeyd My only other thought is using some sort of tooling to assist us here. I know there are things available but don't have much time to look into it. However, from what I understand we'd still need to manually test certain things. We'd need some dedicated time to resolve this. |
Sorry, something went wrong.
|
@filippovskii09 Do you mind rebasing or pushing a new commit to this so that the sandbox gets re-created? |
Sorry, something went wrong.
@kdmccormick yes, I will take care of it |
Sorry, something went wrong.
|
@cassiezamparini Thank you for sharing your thoughts re: a11y reviews. In terms of coming up with a general process, @sarina put a draft together here (thanks!). It doesn't yet answer the question of who would be able to take the a11y expert role, but the workflows and decision criteria that it captures make sense to me from an OSPR management perspective. Curious to see what you and @ali-hugo think.
For manual testing, leveraging PR sandboxes might be an option?
Would it be possible to discuss this topic with the other product managers/CCs in an upcoming product working group meeting? |
Sorry, something went wrong.
|
Hi all, we're actually starting with #1811 for the first PR to test the a11y process. Peter is working on reaching out to some people he knows might be able to ask for help. Can we move conversation about the process to the process wiki page, rather than buried in a random PR? |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Note
This PR has been added to the product proposal to improve accessibility.
Product proposal
Issue
#1880
Description
This PR introduces significant accessibility (a11y) improvements to the Notification Tray (Sidebar) and its Trigger Button, making the feature fully operable for keyboard-only users.
Key Changes
Trigger State
Focus Management
Managed Keyboard Navigation
Steps to Test
Note: These steps should be performed using the keyboard to verify accessibility.
Before:
2025-10-31.16.00.26.movAfter:
2025-10-30.13.47.42.mov