11import React from 'react'
22import '@testing-library/jest-dom/extend-expect'
3- import { fireEvent , render } from '@testing-library/react'
3+ import MatchMediaMock from 'jest-matchmedia-mock'
4+ import { render , fireEvent , waitFor } from '@testing-library/react'
45import { EyeIcon , FileCodeIcon , PeopleIcon } from '@primer/octicons-react'
56import userEvent from '@testing-library/user-event'
67import { behavesAsComponent , checkExports , checkStoriesForAxeViolations } from '../utils/testing'
78import { SegmentedControl } from '.' // TODO: update import when we move this to the global index
9+ import theme from '../theme'
10+ import { BaseStyles , SSRProvider , ThemeProvider } from '..'
11+ import { act } from 'react-test-renderer'
12+ import { viewportRanges } from '../hooks/useMatchMedia'
813
914const segmentData = [
1015 { label : 'Preview' , id : 'preview' , iconLabel : 'EyeIcon' , icon : ( ) => < EyeIcon aria-label = "EyeIcon" /> } ,
1116 { label : 'Raw' , id : 'raw' , iconLabel : 'FileCodeIcon' , icon : ( ) => < FileCodeIcon aria-label = "FileCodeIcon" /> } ,
1217 { label : 'Blame' , id : 'blame' , iconLabel : 'PeopleIcon' , icon : ( ) => < PeopleIcon aria-label = "PeopleIcon" /> }
1318]
1419
15- // TODO: improve test coverage
20+ let matchMedia : MatchMediaMock
21+
1622describe ( 'SegmentedControl' , ( ) => {
1723 const mockWarningFn = jest . fn ( )
1824
1925 beforeAll ( ( ) => {
2026 jest . spyOn ( global . console , 'warn' ) . mockImplementation ( mockWarningFn )
27+ matchMedia = new MatchMediaMock ( )
28+ } )
29+
30+ afterAll ( ( ) => {
31+ jest . clearAllMocks ( )
32+ matchMedia . clear ( )
2133 } )
2234
2335 behavesAsComponent ( {
@@ -54,6 +66,47 @@ describe('SegmentedControl', () => {
5466 expect ( selectedButton ?. getAttribute ( 'aria-current' ) ) . toBe ( 'true' )
5567 } )
5668
69+ it ( 'renders the dropdown variant' , ( ) => {
70+ act ( ( ) => {
71+ matchMedia . useMediaQuery ( viewportRanges . narrow )
72+ } )
73+
74+ const { getByText} = render (
75+ < SegmentedControl aria-label = "File view" variant = { { narrow : 'dropdown' } } >
76+ { segmentData . map ( ( { label} , index ) => (
77+ < SegmentedControl . Button selected = { index === 1 } key = { label } >
78+ { label }
79+ </ SegmentedControl . Button >
80+ ) ) }
81+ </ SegmentedControl >
82+ )
83+ const button = getByText ( segmentData [ 1 ] . label )
84+
85+ expect ( button ) . toBeInTheDocument ( )
86+ expect ( button . closest ( 'button' ) ?. getAttribute ( 'aria-haspopup' ) ) . toBe ( 'true' )
87+ } )
88+
89+ it ( 'renders the hideLabels variant' , ( ) => {
90+ act ( ( ) => {
91+ matchMedia . useMediaQuery ( viewportRanges . narrow )
92+ } )
93+
94+ const { getByLabelText} = render (
95+ < SegmentedControl aria-label = "File view" variant = { { narrow : 'hideLabels' } } >
96+ { segmentData . map ( ( { label, icon} , index ) => (
97+ < SegmentedControl . Button leadingIcon = { icon } selected = { index === 1 } key = { label } >
98+ { label }
99+ </ SegmentedControl . Button >
100+ ) ) }
101+ </ SegmentedControl >
102+ )
103+
104+ for ( const datum of segmentData ) {
105+ const labelledButton = getByLabelText ( datum . label )
106+ expect ( labelledButton ) . toBeDefined ( )
107+ }
108+ } )
109+
57110 it ( 'renders the first segment as selected if no child has the `selected` prop passed' , ( ) => {
58111 const { getByText} = render (
59112 < SegmentedControl aria-label = "File view" >
@@ -190,6 +243,83 @@ describe('SegmentedControl', () => {
190243 expect ( document . activeElement ?. id ) . toEqual ( initialFocusButtonNode . id )
191244 } )
192245
246+ it ( 'calls onChange with index of clicked segment button when using the dropdown variant' , async ( ) => {
247+ act ( ( ) => {
248+ matchMedia . useMediaQuery ( viewportRanges . narrow )
249+ } )
250+ const handleChange = jest . fn ( )
251+ const component = render (
252+ < ThemeProvider theme = { theme } >
253+ < SSRProvider >
254+ < BaseStyles >
255+ < SegmentedControl aria-label = "File view" onChange = { handleChange } variant = { { narrow : 'dropdown' } } >
256+ { segmentData . map ( ( { label} , index ) => (
257+ < SegmentedControl . Button selected = { index === 0 } key = { label } >
258+ { label }
259+ </ SegmentedControl . Button >
260+ ) ) }
261+ </ SegmentedControl >
262+ </ BaseStyles >
263+ </ SSRProvider >
264+ </ ThemeProvider >
265+ )
266+ const button = component . getByText ( segmentData [ 0 ] . label )
267+
268+ fireEvent . click ( button )
269+ expect ( handleChange ) . not . toHaveBeenCalled ( )
270+ const menuItems = await waitFor ( ( ) => component . getAllByRole ( 'menuitemradio' ) )
271+ fireEvent . click ( menuItems [ 1 ] )
272+
273+ expect ( handleChange ) . toHaveBeenCalledWith ( 1 )
274+ } )
275+
276+ it ( 'calls segment button onClick if it is passed when using the dropdown variant' , async ( ) => {
277+ act ( ( ) => {
278+ matchMedia . useMediaQuery ( viewportRanges . narrow )
279+ } )
280+ const handleClick = jest . fn ( )
281+ const component = render (
282+ < ThemeProvider theme = { theme } >
283+ < SSRProvider >
284+ < BaseStyles >
285+ < SegmentedControl aria-label = "File view" variant = { { narrow : 'dropdown' } } >
286+ { segmentData . map ( ( { label} , index ) => (
287+ < SegmentedControl . Button selected = { index === 0 } key = { label } onClick = { handleClick } >
288+ { label }
289+ </ SegmentedControl . Button >
290+ ) ) }
291+ </ SegmentedControl >
292+ </ BaseStyles >
293+ </ SSRProvider >
294+ </ ThemeProvider >
295+ )
296+ const button = component . getByText ( segmentData [ 0 ] . label )
297+
298+ fireEvent . click ( button )
299+ expect ( handleClick ) . not . toHaveBeenCalled ( )
300+ const menuItems = await waitFor ( ( ) => component . getAllByRole ( 'menuitemradio' ) )
301+ fireEvent . click ( menuItems [ 1 ] )
302+
303+ expect ( handleClick ) . toHaveBeenCalled ( )
304+ } )
305+
306+ it ( 'warns users if they try to use the hideLabels variant without a leadingIcon' , ( ) => {
307+ act ( ( ) => {
308+ matchMedia . useMediaQuery ( viewportRanges . narrow )
309+ } )
310+ const consoleSpy = jest . spyOn ( global . console , 'warn' )
311+ render (
312+ < SegmentedControl aria-label = "File view" variant = { { narrow : 'hideLabels' } } >
313+ { segmentData . map ( ( { label} , index ) => (
314+ < SegmentedControl . Button selected = { index === 1 } key = { label } >
315+ { label }
316+ </ SegmentedControl . Button >
317+ ) ) }
318+ </ SegmentedControl >
319+ )
320+ expect ( consoleSpy ) . toHaveBeenCalled ( )
321+ } )
322+
193323 it ( 'should warn the user if they neglect to specify a label for the segmented control' , ( ) => {
194324 render (
195325 < SegmentedControl >
@@ -205,5 +335,6 @@ describe('SegmentedControl', () => {
205335 } )
206336} )
207337
208- checkStoriesForAxeViolations ( 'examples' , '../SegmentedControl/' )
338+ // TODO: uncomment these tests after we fix a11y for the Tooltip component
339+ // checkStoriesForAxeViolations('examples', '../SegmentedControl/')
209340checkStoriesForAxeViolations ( 'fixtures' , '../SegmentedControl/' )
0 commit comments