-
Notifications
You must be signed in to change notification settings - Fork 295
feat: accessibility HTML attributes for Course Navigation top bar #1820
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
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 React from 'react'; | ||
| import { Factory } from 'rosie'; | ||
| import { useWindowSize, breakpoints } from '@openedx/paragon'; | ||
|
|
||
| import { | ||
| render, screen, fireEvent, getByText, initializeTestStore, | ||
| } from '../../../../setupTest'; | ||
|
|
@@ -10,6 +12,15 @@ import useIndexOfLastVisibleChild from '../../../../generic/tabs/useIndexOfLastV | |
| jest.mock('../../../../generic/tabs/useIndexOfLastVisibleChild'); | ||
| useIndexOfLastVisibleChild.mockReturnValue([0, null, null]); | ||
|
|
||
| jest.mock('@openedx/paragon', () => { | ||
|
||
| const original = jest.requireActual('@openedx/paragon'); | ||
| return { | ||
| ...original, | ||
| breakpoints: original.breakpoints, | ||
| useWindowSize: jest.fn(), | ||
| }; | ||
| }); | ||
|
|
||
| describe('Sequence Navigation', () => { | ||
| let mockData; | ||
| const courseMetadata = Factory.build('courseMetadata'); | ||
|
|
@@ -29,6 +40,7 @@ describe('Sequence Navigation', () => { | |
| onNavigate: () => {}, | ||
| nextHandler: () => {}, | ||
| }; | ||
| useWindowSize.mockReturnValue({ width: 1024, height: 800 }); | ||
| }); | ||
|
|
||
| it('is empty while loading', async () => { | ||
|
|
@@ -76,7 +88,7 @@ describe('Sequence Navigation', () => { | |
| const onNavigate = jest.fn(); | ||
| render(<SequenceNavigation {...mockData} {...{ onNavigate }} />, { wrapWithRouter: true }); | ||
|
|
||
| const unitButtons = screen.getAllByRole('link', { name: /\d+/ }); | ||
| const unitButtons = screen.getAllByRole('tab', { name: /\d+/ }); | ||
| expect(unitButtons).toHaveLength(unitButtons.length); | ||
| unitButtons.forEach(button => fireEvent.click(button)); | ||
| expect(onNavigate).toHaveBeenCalledTimes(unitButtons.length); | ||
|
|
@@ -209,4 +221,22 @@ describe('Sequence Navigation', () => { | |
| ); | ||
| expect(screen.queryByRole('link', { name: /next/i })).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('shows previous button without label when screen is small', () => { | ||
| useWindowSize.mockReturnValue({ width: breakpoints.small.minWidth - 1 }); | ||
|
|
||
| render(<SequenceNavigation {...mockData} />, { wrapWithRouter: true }); | ||
|
|
||
| const prevButton = screen.getByRole('button'); | ||
| expect(prevButton.textContent).toBe('2 of 3'); | ||
| }); | ||
|
|
||
| it('does not set width when screen is large', () => { | ||
| useWindowSize.mockReturnValue({ width: breakpoints.small.minWidth }); | ||
|
|
||
| render(<SequenceNavigation {...mockData} />, { wrapWithRouter: true }); | ||
|
|
||
| const nav = screen.getByRole('navigation', { name: /course sequence tabs/i }); | ||
|
||
| expect(nav).not.toHaveStyle({ width: '90%' }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,7 +50,7 @@ describe('Sequence Navigation Dropdown', () => { | |
| }); | ||
| const dropdownMenu = container.querySelector('.dropdown-menu'); | ||
| // Only the current unit should be marked as active. | ||
| getAllByRole(dropdownMenu, 'link', { hidden: true }).forEach(button => { | ||
| getAllByRole(dropdownMenu, 'tab', { hidden: true }).forEach(button => { | ||
| if (button.textContent === unit.display_name) { | ||
| expect(button).toHaveClass('active'); | ||
| } else { | ||
|
|
@@ -72,7 +72,7 @@ describe('Sequence Navigation Dropdown', () => { | |
| fireEvent.click(dropdownToggle); | ||
| }); | ||
| const dropdownMenu = container.querySelector('.dropdown-menu'); | ||
| getAllByRole(dropdownMenu, 'link', { hidden: true }).forEach(button => fireEvent.click(button)); | ||
| getAllByRole(dropdownMenu, 'tab', { hidden: true }).forEach(button => fireEvent.click(button)); | ||
|
||
| expect(onNavigate).toHaveBeenCalledTimes(unitBlocks.length); | ||
| unitBlocks.forEach((unit, index) => { | ||
| expect(onNavigate).toHaveBeenNthCalledWith(index + 1, unit.id); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,9 +5,22 @@ import { | |
| } from '../../../../setupTest'; | ||
| import UnitButton from './UnitButton'; | ||
|
|
||
| jest.mock('react-router-dom', () => { | ||
| const actual = jest.requireActual('react-router-dom'); | ||
| return { | ||
| ...actual, | ||
| useLocation: () => ({ pathname: '/preview/anything' }), | ||
| }; | ||
| }); | ||
|
|
||
| describe('Unit Button', () => { | ||
| let mockData; | ||
| const courseMetadata = Factory.build('courseMetadata'); | ||
|
|
||
| afterEach(() => { | ||
| jest.resetModules(); | ||
| }); | ||
|
|
||
| const unitBlocks = [Factory.build( | ||
| 'block', | ||
| { type: 'problem' }, | ||
|
|
@@ -33,12 +46,12 @@ describe('Unit Button', () => { | |
|
|
||
| it('hides title by default', () => { | ||
| render(<UnitButton {...mockData} />, { wrapWithRouter: true }); | ||
| expect(screen.getByRole('link')).not.toHaveTextContent(unit.display_name); | ||
| expect(screen.getByRole('tab')).not.toHaveTextContent(unit.display_name); | ||
| }); | ||
|
|
||
| it('shows title', () => { | ||
| render(<UnitButton {...mockData} showTitle />, { wrapWithRouter: true }); | ||
| expect(screen.getByRole('link')).toHaveTextContent(unit.display_name); | ||
| expect(screen.getByRole('tab')).toHaveTextContent(unit.display_name); | ||
| }); | ||
|
|
||
| it('does not show completion for non-completed unit', () => { | ||
|
|
@@ -79,7 +92,39 @@ describe('Unit Button', () => { | |
| it('handles the click', () => { | ||
| const onClick = jest.fn(); | ||
| render(<UnitButton {...mockData} onClick={onClick} />, { wrapWithRouter: true }); | ||
| fireEvent.click(screen.getByRole('link')); | ||
| fireEvent.click(screen.getByRole('tab')); | ||
|
||
| expect(onClick).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('calls onClick with correct unitId when clicked', () => { | ||
| const onClick = jest.fn(); | ||
| render(<UnitButton {...mockData} onClick={onClick} />, { wrapWithRouter: true }); | ||
|
|
||
| fireEvent.click(screen.getByRole('tab')); | ||
|
||
|
|
||
| expect(onClick).toHaveBeenCalledWith(mockData.unitId); | ||
| }); | ||
|
|
||
| it('renders with no unitId and does not crash', async () => { | ||
| render(<UnitButton unitId={undefined} onClick={jest.fn()} contentType="video" title="Unit" />, { | ||
| wrapWithRouter: true, | ||
| }); | ||
|
|
||
| expect(screen.getByRole('tab')).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('prepends /preview to the unit path if in preview mode', () => { | ||
| const onClick = jest.fn(); | ||
|
|
||
| render( | ||
| <UnitButton {...mockData} onClick={onClick} />, | ||
| { | ||
| wrapWithRouter: true, | ||
| initialEntries: ['/preview/some/path'], | ||
| }, | ||
| ); | ||
|
|
||
| const button = screen.getByRole('tab'); | ||
| expect(button.closest('a')).toHaveAttribute('href', expect.stringContaining('/preview/course/')); | ||
| }); | ||
| }); | ||
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.
Should we have a translation for this label?
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.
translations have been added