|
1 | 1 | import React from 'react' |
2 | 2 | import '@testing-library/jest-dom/extend-expect' |
3 | | -import {render} from '@testing-library/react' |
| 3 | +import {fireEvent, render} from '@testing-library/react' |
4 | 4 | import {EyeIcon, FileCodeIcon, PeopleIcon} from '@primer/octicons-react' |
5 | 5 | import userEvent from '@testing-library/user-event' |
6 | 6 | import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing' |
7 | 7 | import {SegmentedControl} from '.' // TODO: update import when we move this to the global index |
8 | 8 |
|
9 | 9 | const segmentData = [ |
10 | | - {label: 'Preview', iconLabel: 'EyeIcon', icon: () => <EyeIcon aria-label="EyeIcon" />}, |
11 | | - {label: 'Raw', iconLabel: 'FileCodeIcon', icon: () => <FileCodeIcon aria-label="FileCodeIcon" />}, |
12 | | - {label: 'Blame', iconLabel: 'PeopleIcon', icon: () => <PeopleIcon aria-label="PeopleIcon" />} |
| 10 | + {label: 'Preview', id: 'preview', iconLabel: 'EyeIcon', icon: () => <EyeIcon aria-label="EyeIcon" />}, |
| 11 | + {label: 'Raw', id: 'raw', iconLabel: 'FileCodeIcon', icon: () => <FileCodeIcon aria-label="FileCodeIcon" />}, |
| 12 | + {label: 'Blame', id: 'blame', iconLabel: 'PeopleIcon', icon: () => <PeopleIcon aria-label="PeopleIcon" />} |
13 | 13 | ] |
14 | 14 |
|
15 | 15 | // TODO: improve test coverage |
16 | 16 | describe('SegmentedControl', () => { |
| 17 | + const mockWarningFn = jest.fn() |
| 18 | + |
| 19 | + beforeAll(() => { |
| 20 | + jest.spyOn(global.console, 'warn').mockImplementation(mockWarningFn) |
| 21 | + }) |
| 22 | + |
17 | 23 | behavesAsComponent({ |
18 | 24 | Component: SegmentedControl, |
19 | 25 | toRender: () => ( |
20 | 26 | <SegmentedControl aria-label="File view"> |
21 | | - <SegmentedControl.Button selected>Preview</SegmentedControl.Button> |
22 | | - <SegmentedControl.Button>Raw</SegmentedControl.Button> |
23 | | - <SegmentedControl.Button>Blame</SegmentedControl.Button> |
| 27 | + {segmentData.map(({label}, index) => ( |
| 28 | + <SegmentedControl.Button selected={index === 0} key={label}> |
| 29 | + {label} |
| 30 | + </SegmentedControl.Button> |
| 31 | + ))} |
24 | 32 | </SegmentedControl> |
25 | 33 | ) |
26 | 34 | }) |
@@ -133,6 +141,68 @@ describe('SegmentedControl', () => { |
133 | 141 | } |
134 | 142 | expect(handleClick).toHaveBeenCalled() |
135 | 143 | }) |
| 144 | + |
| 145 | + it('focuses the selected button first', () => { |
| 146 | + const {getByRole} = render( |
| 147 | + <> |
| 148 | + <button>Before</button> |
| 149 | + <SegmentedControl aria-label="File view"> |
| 150 | + {segmentData.map(({label, id}, index) => ( |
| 151 | + <SegmentedControl.Button selected={index === 1} key={label} id={id}> |
| 152 | + {label} |
| 153 | + </SegmentedControl.Button> |
| 154 | + ))} |
| 155 | + </SegmentedControl> |
| 156 | + </> |
| 157 | + ) |
| 158 | + const initialFocusButtonNode = getByRole('button', {name: segmentData[1].label}) |
| 159 | + |
| 160 | + expect(document.activeElement?.id).not.toEqual(initialFocusButtonNode.id) |
| 161 | + |
| 162 | + userEvent.tab() // focus the button before the segmented control |
| 163 | + userEvent.tab() // move focus into the segmented control |
| 164 | + |
| 165 | + expect(document.activeElement?.id).toEqual(initialFocusButtonNode.id) |
| 166 | + }) |
| 167 | + |
| 168 | + it('focuses the previous button when keying ArrowLeft, and the next button when keying ArrowRight', () => { |
| 169 | + const {getByRole} = render( |
| 170 | + <SegmentedControl aria-label="File view"> |
| 171 | + {segmentData.map(({label, id}, index) => ( |
| 172 | + <SegmentedControl.Button selected={index === 1} key={label} id={id}> |
| 173 | + {label} |
| 174 | + </SegmentedControl.Button> |
| 175 | + ))} |
| 176 | + </SegmentedControl> |
| 177 | + ) |
| 178 | + const initialFocusButtonNode = getByRole('button', {name: segmentData[1].label}) |
| 179 | + const nextFocusButtonNode = getByRole('button', {name: segmentData[0].label}) |
| 180 | + |
| 181 | + expect(document.activeElement?.id).not.toEqual(nextFocusButtonNode.id) |
| 182 | + |
| 183 | + fireEvent.focus(initialFocusButtonNode) |
| 184 | + fireEvent.keyDown(initialFocusButtonNode, {key: 'ArrowLeft'}) |
| 185 | + |
| 186 | + expect(document.activeElement?.id).toEqual(nextFocusButtonNode.id) |
| 187 | + |
| 188 | + fireEvent.keyDown(initialFocusButtonNode, {key: 'ArrowRight'}) |
| 189 | + |
| 190 | + expect(document.activeElement?.id).toEqual(initialFocusButtonNode.id) |
| 191 | + }) |
| 192 | + |
| 193 | + it('should warn the user if they neglect to specify a label for the segmented control', () => { |
| 194 | + render( |
| 195 | + <SegmentedControl> |
| 196 | + {segmentData.map(({label, id}) => ( |
| 197 | + <SegmentedControl.Button id={id} key={label}> |
| 198 | + {label} |
| 199 | + </SegmentedControl.Button> |
| 200 | + ))} |
| 201 | + </SegmentedControl> |
| 202 | + ) |
| 203 | + |
| 204 | + expect(mockWarningFn).toHaveBeenCalled() |
| 205 | + }) |
136 | 206 | }) |
137 | 207 |
|
138 | 208 | checkStoriesForAxeViolations('examples', '../SegmentedControl/') |
|
0 commit comments