import * as React from 'react' import { render } from '@testing-library/react' import userEvent from '@testing-library/user-event' import * as UtilsModule from '../utils/index' import { breakpoints } from '@planview/pv-utilities' import { Toolbar, ToolbarSectionLeft, ToolbarSectionRight, ToolbarSeparator, ToolbarButtonGroup, } from '../index' import { ToolbarButtonEmpty, ToolbarButtonGhost } from '../button' import { Activity, Card, Filter, PlusCircle } from '@planview/pv-icons' import { ToolbarDropdownMenu } from '../dropdown' import { ToolbarMoreMenu } from '../more-menu' import { ListItem } from '@planview/pv-uikit' const originalGetBreakPoint = UtilsModule.getBreakPoint describe('pv-toolbar', () => { describe('Test adaptive layout', () => { const AdaptiveComponent = () => ( }> Group button 1 }> Group button 2 Button 1 } displayOn="desktop"> Button 2 ) describe('Desktop breakpoint', () => { it('All items should be present', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.DESKTOP) ) const { queryByTestId, getByText, getByTestId, queryByLabelText, } = render() expect(queryByLabelText('More')).not.toBeInTheDocument() expect(getByText('Button 1')).toBeInTheDocument() expect(getByText('Button 2')).toBeInTheDocument() expect(queryByTestId('trigger')).not.toBeInTheDocument() expect(getByTestId('separator')).toBeInTheDocument() }) it('Group button labels should be visible', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.DESKTOP) ) const { getByText } = render() expect(getByText('Group button 1')).toBeInTheDocument() expect(getByText('Group button 2')).toBeInTheDocument() }) it('Nested desktop item should be there', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.DESKTOP) ) const { getByLabelText, getByText, queryByLabelText } = render( } tooltip="Filter" /> } displayOn="desktop" tooltip="Activity" /> }> Add Stuff ) expect(queryByLabelText('More')).not.toBeInTheDocument() expect(getByLabelText('Filter')).toBeInTheDocument() expect(getByLabelText('Activity')).toBeInTheDocument() expect(getByText('Add Stuff')).toBeInTheDocument() }) }) describe('Tablet landscape breakpoint', () => { it('Desktop items should not be present. More menu should be present', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_LANDSCAPE) ) const { getByTestId, queryByText, getByText, getByRole } = render() expect(getByText('Button 1')).toBeInTheDocument() expect(queryByText('Button 2')).not.toBeInTheDocument() expect(getByTestId('separator')).toBeInTheDocument() expect( getByRole('button', { name: 'More' }) ).toBeInTheDocument() }) it('Group button labels should be visible', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_LANDSCAPE) ) const { getByText } = render() expect(getByText('Group button 1')).toBeInTheDocument() expect(getByText('Group button 2')).toBeInTheDocument() }) it('Nested desktop items in middle should be moved to more menu', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_LANDSCAPE) ) const { getByLabelText, getByText, queryByLabelText } = render( } tooltip="Filter" /> } displayOn="desktop" tooltip="Activity" /> }> Add Stuff ) expect(getByLabelText('More')).toBeInTheDocument() expect(getByLabelText('Filter')).toBeInTheDocument() expect(queryByLabelText('Activity')).not.toBeInTheDocument() expect(getByText('Add Stuff')).toBeInTheDocument() }) it('No more menu if there are no desktop items to show in more menu', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_LANDSCAPE) ) const { getByLabelText, getByText, queryByLabelText } = render( } tooltip="Filter" /> } tooltip="Activity" /> }> Add Stuff ) expect(queryByLabelText('More')).not.toBeInTheDocument() expect(getByLabelText('Filter')).toBeInTheDocument() expect(getByLabelText('Activity')).toBeInTheDocument() expect(getByText('Add Stuff')).toBeInTheDocument() }) }) describe('Tablet portrait breakpoint', () => { it('Desktop items should not be present. More menu should be present', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_PORTRAIT) ) const { getByRole, queryByText, getByText, queryByTestId } = render( Button 1 } displayOn="desktop" > Button 2 ) expect(getByText('Button 1')).toBeInTheDocument() expect(queryByText('Button 2')).not.toBeInTheDocument() expect(queryByTestId('separator')).not.toBeInTheDocument() expect( getByRole('button', { name: 'More' }) ).toBeInTheDocument() }) it('Group button labels should be not visible', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_PORTRAIT) ) const { queryByText, getByLabelText } = render( ) //Visible labels should be gone expect(queryByText('Group button 1')).not.toBeInTheDocument() expect(queryByText('Group button 2')).not.toBeInTheDocument() //Replaced with aria-labels expect(getByLabelText('Group button 1')).toBeInTheDocument() expect(getByLabelText('Group button 2')).toBeInTheDocument() }) it('No more menu if there are no desktop items to show in more menu', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_PORTRAIT) ) const { getByLabelText, queryByLabelText } = render( } tooltip="Filter" /> } tooltip="Activity" /> }> Add Stuff ) expect(queryByLabelText('More')).not.toBeInTheDocument() expect(getByLabelText('Filter')).toBeInTheDocument() expect(getByLabelText('Activity')).toBeInTheDocument() expect(getByLabelText('Add Stuff')).toBeInTheDocument() }) it('Visible label should be visible only if preventLabelCollapse = true', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_PORTRAIT) ) const { getByLabelText, queryByLabelText, getByText, queryByText, } = render( } > Filter }> Activity } > Add Stuff ) //No aria labels on the buttons w preventLabelCollapse expect(queryByLabelText('Filter')).not.toBeInTheDocument() expect(queryByLabelText('Add Stuff')).not.toBeInTheDocument() //But they should have label expect(getByText('Filter')).toBeInTheDocument() expect(getByText('Add Stuff')).toBeInTheDocument() //This one should have aria-label, but no visible label expect(getByLabelText('Activity')).toBeInTheDocument() expect(queryByText('Activity')).not.toBeInTheDocument() }) it('Phone items should still be there', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_PORTRAIT) ) const { getByLabelText, queryByLabelText, queryByText } = render( } > Activity } > Add Stuff ) //This one should have aria-label, but no visible label expect(getByLabelText('Activity')).toBeInTheDocument() expect(queryByText('Activity')).not.toBeInTheDocument() //Should be gone into more menu expect(queryByLabelText('Add Stuff')).not.toBeInTheDocument() expect(queryByText('Add Stuff')).not.toBeInTheDocument() }) it('ToolbarButtonGroup displayOn Desktop should be in more menu', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { queryByText, getByLabelText } = render( Hello World ) expect(queryByText('Hello')).not.toBeInTheDocument() expect(queryByText('World')).not.toBeInTheDocument() expect(getByLabelText('More')).toBeInTheDocument() }) }) describe('Phone breakpoint', () => { it('Only more menu should be present, no separator', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { getByRole, queryByText, queryByTestId } = render( ) expect(queryByText('Group button 1')).not.toBeInTheDocument() expect(queryByText('Group button 2')).not.toBeInTheDocument() expect(queryByText('Button 1')).not.toBeInTheDocument() expect(queryByText('Button 2')).not.toBeInTheDocument() expect( getByRole('button', { name: 'More' }) ).toBeInTheDocument() expect(queryByTestId('separator')).not.toBeInTheDocument() }) it('Visible label should be visible only if preventLabelCollapse = true', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { getByRole, queryByLabelText, getByText, queryByText } = render( } > Filter }> Activity } > Add Stuff ) //No aria labels on the buttons w preventLabelCollapse in normal section expect(queryByLabelText('Filter')).not.toBeInTheDocument() //Should have a visible label expect(getByText('Filter')).toBeInTheDocument() //ToolbarSectionRight should be in more menu expect( getByRole('button', { name: 'More' }) ).toBeInTheDocument() expect(queryByLabelText('Add Stuff')).not.toBeInTheDocument() expect(queryByLabelText('Activity')).not.toBeInTheDocument() expect(queryByText('Activity')).not.toBeInTheDocument() }) it('Phone items should still be there', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { getByText, getByLabelText, queryByLabelText, queryByText, } = render( ( Open dropdown )} > } > Activity } > Add Stuff ) //This one should have aria-label, but no visible label expect(getByLabelText('Activity')).toBeInTheDocument() expect(queryByText('Activity')).not.toBeInTheDocument() //Should be gone into more menu expect(queryByLabelText('Add Stuff')).not.toBeInTheDocument() expect(queryByText('Add Stuff')).not.toBeInTheDocument() //Dropdown should still be there expect(getByText('Open dropdown')).toBeInTheDocument() }) it('ToolbarButtonGroup displayOn always: Buttons in group should ignore individual displayOn', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { getByText, queryByLabelText } = render( Hello World ) expect(getByText('Hello')).toBeInTheDocument() expect(getByText('World')).toBeInTheDocument() expect(queryByLabelText('More')).not.toBeInTheDocument() }) }) }) describe('Test transformed items', () => { describe('Button -> DropdownListItem', () => { it('Tablet landscape breakpoint: Desktop Buttons should transformed to DropdownListItems', async () => { const spy1 = jest.fn() const spy2 = jest.fn() jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_LANDSCAPE) ) const { queryByText, getByRole } = render( } tooltip="Button with only tooltip" displayOn="desktop" onClick={spy1} /> }> This button should not be in dropdown } displayOn="desktop" onClick={spy2} > Button with visible label ) expect( queryByText('Button with visible label') ).not.toBeInTheDocument() expect( queryByText('This button should not be in dropdown') ).toBeInTheDocument() await userEvent.click(getByRole('button', { name: 'More' })) expect( queryByText('Button with only tooltip') ).toBeInTheDocument() expect( queryByText('Button with visible label') ).toBeInTheDocument() //Navigate to first item in dropdown 'Button with only tooltip' await userEvent.keyboard('{ArrowDown}{ArrowDown}{Enter}') expect(spy1).toHaveBeenCalledTimes(0) expect(spy2).toHaveBeenCalledTimes(1) }) it('Phone breakpoint: All Buttons should transformed to DropdownListItems', async () => { const spy1 = jest.fn() const spy2 = jest.fn() const spy3 = jest.fn() jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { queryByText, getByRole } = render( } tooltip="Button with only tooltip" displayOn="desktop" onClick={spy1} /> } > This button should now be in dropdown } displayOn="desktop" onClick={spy3} > Button with visible label ) expect( queryByText('This button should now be in dropdown') ).not.toBeInTheDocument() await userEvent.click(getByRole('button', { name: 'More' })) expect( queryByText('Button with only tooltip') ).toBeInTheDocument() expect( queryByText('Button with visible label') ).toBeInTheDocument() expect( queryByText('This button should now be in dropdown') ).toBeInTheDocument() await userEvent.keyboard('{ArrowDown}{ArrowDown}{Enter}') expect(spy1).toHaveBeenCalledTimes(0) expect(spy2).toHaveBeenCalledTimes(1) expect(spy3).toHaveBeenCalledTimes(0) }) }) describe('ButtonGroup -> DropdownGroup', () => { it('Phone breakpoint: ButtonGroup should be transformed to DropdownGroup with items', async () => { const spy1 = jest.fn() const spy2 = jest.fn() jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { queryByText, getByRole } = render( } tooltip="Activity button" onClick={spy1} /> } tooltip="Card button" onClick={spy2} /> ) expect(queryByText('Activity')).not.toBeInTheDocument() await userEvent.click(getByRole('button', { name: 'More' })) expect(queryByText('test label')).toBeInTheDocument() expect(queryByText('Activity button')).toBeInTheDocument() expect(queryByText('Card button')).toBeInTheDocument() await userEvent.keyboard('{ArrowDown}{ArrowDown}{Enter}') expect(spy1).toHaveBeenCalledTimes(0) expect(spy2).toHaveBeenCalledTimes(1) }) }) describe('Dropdown -> DropdownAccordion', () => { it('Phone breakpoint: Dropdown should be transformed to DropdownAccordion with items', async () => { const spy1 = jest.fn() const spy2 = jest.fn() jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { queryByText, getByRole, baseElement } = render( ( Open dropdown )} > ) expect(queryByText('Open dropdown')).not.toBeInTheDocument() await userEvent.click(getByRole('button', { name: 'More' })) await userEvent.click( getByRole('menuitem', { name: 'test dropdown' }) ) await userEvent.click( /* For some reason, react testing library cannot find this with normal getByRole selection */ baseElement.querySelectorAll('[role="menuitemradio"]')[1] ) expect(spy1).toHaveBeenCalledTimes(0) expect(spy2).toHaveBeenCalledTimes(1) }) }) }) describe('Test keyboard navigation arrows', () => { beforeEach(() => { userEvent.setup() }) it('Navigate to third item in first section', async () => { const spy1 = jest.fn() const { getByLabelText } = render( } /> } /> } /> } /> } /> ) await userEvent.keyboard('{Tab}') expect(getByLabelText('first button')).toHaveFocus() await userEvent.keyboard('{ArrowRight}') await userEvent.keyboard('{ArrowRight}') expect(getByLabelText('third button')).toHaveFocus() await userEvent.keyboard('{enter}') expect(spy1).toHaveBeenCalledTimes(1) }) it('Navigate to last item in second section', async () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.DESKTOP) ) const spy1 = jest.fn() const { getByLabelText } = render( } /> } /> } /> ) await userEvent.keyboard('{Tab}') expect(getByLabelText('first button')).toHaveFocus() await userEvent.keyboard('{ArrowRight}') await userEvent.keyboard('{ArrowRight}') expect(getByLabelText('last button')).toHaveFocus() await userEvent.keyboard('{enter}') expect(spy1).toHaveBeenCalledTimes(1) }) it('should not skip disabled buttons (but not call onClick)', async () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.DESKTOP) ) const spy1 = jest.fn() const { getByLabelText } = render( } /> } /> } /> ) await userEvent.keyboard('{Tab}') expect(getByLabelText('first button')).toHaveFocus() await userEvent.keyboard('{ArrowRight}') expect(getByLabelText('disabled button 1')).toHaveFocus() await userEvent.keyboard('{enter}') expect(spy1).toHaveBeenCalledTimes(0) }) }) describe('ToolbarMoreMenu', () => { describe('Breakpoint desktop', () => { it('Custom menu should be there, all items present', () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.DESKTOP) ) const { getByLabelText, getByRole } = render( } /> } /> ) expect( getByRole('button', { name: 'custom more' }) ).toBeInTheDocument() expect(getByLabelText('disabled button 2')).toBeInTheDocument() expect(getByLabelText('custom more')).toBeInTheDocument() expect(getByLabelText('last button')).toBeInTheDocument() }) }) describe('Breakpoint tablet landscape', () => { it('Custom menu should be there, desktop items in menu', async () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_LANDSCAPE) ) const { getByRole, queryByText } = render( } /> } /> ) await userEvent.click( getByRole('button', { name: 'custom more' }) ) expect(queryByText('disabled button 2')).toBeInTheDocument() expect(queryByText('last button')).toBeInTheDocument() expect(queryByText('hello')).toBeInTheDocument() }) }) describe('Breakpoint tablet portrait', () => { it('Custom menu should be there, desktop items in menu', async () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_PORTRAIT) ) const { getByRole, queryByText } = render( } /> } /> ) await userEvent.click( getByRole('button', { name: 'custom more' }) ) expect(queryByText('disabled button 2')).toBeInTheDocument() expect(queryByText('last button')).toBeInTheDocument() expect(queryByText('hello')).toBeInTheDocument() }) it('More menu should be there, only non-desktop items but custom content', async () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.TABLET_PORTRAIT) ) const { getByRole, container } = render( } /> } /> ) await userEvent.click( getByRole('button', { name: 'custom more' }) ) //Make sure there is no divider line if no collapsed content expect(container.querySelector('hr')).not.toBeInTheDocument() }) }) describe('Breakpoint phone', () => { it('Custom menu should be there, desktop items in menu', async () => { jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.PHONE) ) const { getByRole, queryByText } = render( } /> } /> ) await userEvent.click( getByRole('button', { name: 'custom more' }) ) expect(queryByText('disabled button 2')).toBeInTheDocument() expect(queryByText('last button')).toBeInTheDocument() expect(queryByText('hello')).toBeInTheDocument() }) }) }) describe('Test conditional children patterns', () => { function renderComponent(showChildren: boolean) { return render( {showChildren && } {showChildren ? ( ) : null} {showChildren ? ( ) : undefined} {showChildren ? ( <> > ) : undefined} {showChildren ? ( Returns undefined ) : undefined} {showChildren ? ( Returns null ) : null} {showChildren && ( Returns false )} {showChildren && ( <> Returns fragment > )} ) } beforeEach(() => { userEvent.setup() jest.spyOn(UtilsModule, 'getBreakPoint').mockReturnValue( originalGetBreakPoint(breakpoints.DESKTOP) ) }) it('should render without additional buttons or errors', () => { const { queryAllByRole } = renderComponent(false) expect(queryAllByRole('button')).toHaveLength(1) }) it('should render buttons', () => { const { queryAllByRole } = renderComponent(true) expect(queryAllByRole('button')).toHaveLength(5) }) it('should render dropdown without additional items or errors', async () => { const { getByRole, queryAllByRole } = renderComponent(false) await userEvent.click(getByRole('button', { name: 'Trigger' })) expect(queryAllByRole('menuitemradio')).toHaveLength(1) }) it('should render dropdown with additional items', async () => { const { getByRole, queryAllByRole } = renderComponent(true) await userEvent.click(getByRole('button', { name: 'Trigger' })) expect(queryAllByRole('menuitemradio')).toHaveLength(5) }) }) })