UNPKG

box-ui-elements-mlh

Version:
646 lines (569 loc) 24.9 kB
import React from 'react'; import { shallow, mount } from 'enzyme'; import sinon from 'sinon'; import { Tab, TabViewPrimitive } from '..'; const sandbox = sinon.sandbox.create(); describe('components/tab-view/TabViewPrimitive', () => { afterEach(() => { sandbox.verifyAndRestore(); }); const simulateKeyDown = (comp, key, shouldStopEvent = false, isShiftKey = false) => { // conveniently ensuring that the keydown event is attached to the tablist element, // not the entire container comp.find('[role="tablist"]') .at(0) .simulate('keyDown', { key, shiftKey: isShiftKey, preventDefault: shouldStopEvent ? sandbox.mock() : sandbox.mock().never(), stopPropagation: shouldStopEvent ? sandbox.mock() : sandbox.mock().never(), }); }; test('should render tabview ui with tabs', () => { const onTabFocus = () => {}; const onTabSelect = () => {}; const component = shallow( <TabViewPrimitive focusedIndex={0} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); expect(component.hasClass('tab-view')).toBe(true); expect(component.instance().handleKeyDown).toEqual(component.instance().handleKeyDown); expect(component.find('.tabs').find('button').length).toEqual(2); expect(component.find({ tabIndex: 0 }).toBeFalsy); }); test('should render tabview ui with link tabs', () => { const onTabFocus = () => {}; const onTabSelect = () => {}; const component = shallow( <TabViewPrimitive focusedIndex={0} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab href="https://www.box.com/" title="tab2" /> </TabViewPrimitive>, ); expect(component.find('.tabs').find('button').length).toEqual(1); expect(component.find('.tabs').find('LinkButton').length).toEqual(1); }); test('should select appropriate tab when passed selectedIndex', () => { const onTabFocus = () => {}; const onTabSelect = () => {}; const component = shallow( <TabViewPrimitive focusedIndex={0} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={1} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); const tabs = component.find('.tabs').find('button'); expect(tabs.at(0).hasClass('is-selected')).toBe(false); expect(tabs.at(1).hasClass('is-selected')).toBe(true); }); test('should call onTabSelect when tab selected', () => { const onTabFocus = sinon.spy(); const onTabSelect = sinon.spy(); const event = { type: 'click' }; const component = shallow( <TabViewPrimitive focusedIndex={0} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={1} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); const tabTwoButton = component .find('.tabs') .find('button') .at(1) .find('button'); tabTwoButton.simulate('click', event); expect(onTabSelect.calledWith(1)).toBe(true); expect(onTabFocus.calledWith(1)).toBe(true); }); test('should select the right tab on ArrowRight', () => { const onTabFocus = sinon.mock().withArgs(1); const onTabSelect = sinon.mock().never(); const component = mount( <TabViewPrimitive focusedIndex={0} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); simulateKeyDown(component, 'ArrowRight', true); }); test('should focus first tab on ArrowRight from the last tab', () => { const onTabFocus = sinon.mock().withArgs(1); const onTabSelect = sinon.mock().never(); const component = mount( <TabViewPrimitive focusedIndex={0} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={1} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); simulateKeyDown(component, 'ArrowRight', true); }); test('should focus the left tab on ArrowLeft', () => { const onTabFocus = sinon.mock().withArgs(0); const onTabSelect = sinon.mock().never(); const component = mount( <TabViewPrimitive focusedIndex={1} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={1} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); simulateKeyDown(component, 'ArrowLeft', true); }); test('should focus last tab on ArrowLeft from the first tab', () => { const onTabFocus = sinon.mock().withArgs(2); const onTabSelect = sinon.mock().never(); const component = mount( <TabViewPrimitive focusedIndex={0} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> <Tab title="tab2">Tab 3</Tab> </TabViewPrimitive>, ); simulateKeyDown(component, 'ArrowLeft', true); }); test('should reset selected tab on Escape', () => { const onTabFocus = sinon.mock().never(); const onTabSelect = sinon.mock().never(); const resetActiveTab = sinon.mock(); const component = mount( <TabViewPrimitive focusedIndex={0} onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={resetActiveTab} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); simulateKeyDown(component, 'Escape', false); }); describe('render()', () => { [ { tabsContainerOffsetLeft: 1, isDynamic: true, style: { left: '1px' }, }, { tabsContainerOffsetLeft: 1, isDynamic: false, style: {}, }, ].forEach(({ tabsContainerOffsetLeft, isDynamic, style }) => { test('should render tabs with correct style', () => { const component = shallow( <TabViewPrimitive focusedIndex={0} isDynamic={isDynamic} onTabFocus={sandbox.stub()} onTabSelect={sandbox.stub()} resetActiveTab={sandbox.stub()} resetFocusedTab={sandbox.stub()} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); component.setState({ tabsContainerOffsetLeft }); const tab = component.find('nav'); expect(tab.prop('style')).toEqual(style); }); }); }); describe('Dynamic Tabs', () => { describe('scrollToTab', () => { let component; beforeEach(() => { const onTabFocus = sinon.mock(); const onTabSelect = sinon.mock(); component = mount( <TabViewPrimitive focusedIndex={0} isDynamic onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); }); test('should not throw an error if scroll to index is out of bound', () => { component.instance().scrollToTab(-1); component.instance().scrollToTab(3); // 3 is more than the number of tabs }); test('should not do anything if component is not dynamic', () => { component = mount( <TabViewPrimitive focusedIndex={0} isDynamic onTabFocus={() => {}} onTabSelect={() => {}} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); const instance = component.instance(); instance.setState = sinon.mock(); component.instance().scrollToTab(0); expect(instance.setState.never()).toBeTruthy(); }); test("should set tabsContainerOffsetLeft to 0 if last element's anchor point does not go beyond the view port width", () => { const tabsElements = [ { offsetLeft: 0, offsetWidth: 1, }, { offsetLeft: 1, offsetWidth: 1, }, ]; const instance = component.instance(); instance.tabsElements = tabsElements; instance.tabsContainer = { offsetWidth: 100, }; instance.setState = sinon.mock(); component.instance().scrollToTab(1); expect( instance.setState.calledWith({ tabsContainerOffsetLeft: 0, }), ).toBeTruthy(); }); test("should set tabsContainerOffsetLeft to -100 if last element's anchor point goes beyond the view port width", () => { const tabsElements = [ { offsetLeft: 100, offsetWidth: 100, }, ]; const instance = component.instance(); instance.tabsElements = tabsElements; instance.tabsContainer = { offsetWidth: 100, }; instance.setState = sinon.mock(); component.instance().scrollToTab(0); expect( instance.setState.calledWith({ tabsContainerOffsetLeft: -100, }), ).toBeTruthy(); }); }); describe('life cycle methods', () => { let component; beforeEach(() => { const onTabFocus = sinon.mock(); const onTabSelect = sinon.mock(); component = mount( <TabViewPrimitive focusedIndex={1} isDynamic onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); }); describe('componentDidMount', () => { test('should call scrollToTab when mounted to make sure the scroll position is correct and the button visiblity is adjusted', () => { const instance = component.instance(); instance.scrollToTab = sinon.mock(); instance.componentDidMount(); expect(instance.scrollToTab.calledWith(1)).toBeTruthy(); }); test('should not call scrollToTab when mounted to make sure the scroll position is correct and the button visiblity is adjusted', () => { const instance = component.instance(); component.setProps({ isDynamic: false }); instance.scrollToTab = sinon.mock(); instance.componentDidMount(); expect(instance.scrollToTab.never).toBeTruthy(); }); }); describe('componentDidUpdate', () => { test('should not call focusOnTabElment if component is not dynamic', () => { const instance = component.instance(); instance.focusOnTabElement = sinon.mock(); component.setProps({ focusedIndex: 0, isDynamic: false, }); expect(instance.focusOnTabElement.never).toBeTruthy(); }); test('should call focusOnTabElment if it was changed', () => { const instance = component.instance(); instance.focusOnTabElement = sinon.mock(); const prevFocusedIndex = 2; const newFocusedIndex = component.props().focusedIndex; instance.componentDidUpdate({ focusedIndex: prevFocusedIndex, isDynamic: true, }); expect(instance.focusOnTabElement.calledWith(newFocusedIndex)).toBeTruthy(); }); test('should do nothing if it is not dynamic', () => { const instance = component.instance(); instance.scrollToTab = sinon.mock(); instance.componentDidUpdate({ focusedIndex: 0, isDynamic: false, selectedIndex: 0, }); expect(instance.scrollToTab.never).toBeTruthy(); }); test('should call scrollToTab with focusedIndex if it was changed', () => { const instance = component.instance(); instance.scrollToTab = sinon.mock(); component.setProps({ focusedIndex: 2, isDynamic: true, selectedIndex: component.props().selectedIndex, }); expect(instance.scrollToTab.calledWith(2)).toBeTruthy(); }); test('should call scrollToTab with selectedIndex if it was changed', () => { const instance = component.instance(); instance.scrollToTab = sinon.mock(); const selectedIndex = 2; component.setProps({ focusedIndex: component.props().focusedIndex, isDynamic: true, selectedIndex, }); expect(instance.scrollToTab.calledWith(selectedIndex)).toBeTruthy(); }); }); }); describe('arrows', () => { let component; let onTabFocus; beforeEach(() => { onTabFocus = sinon.mock(); const onTabSelect = sinon.mock(); component = mount( <TabViewPrimitive focusedIndex={0} isDynamic onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> <Tab title="tab3">Tab 2</Tab> </TabViewPrimitive>, ); }); describe('right arrow', () => { test('should make hidden if there is no tabsContainer meaning it has not been rendered', () => { const result = component.instance().isRightArrowVisible(); expect(result).toBeFalsy(); }); test('should make hidden if last element is inside the viewport', () => { const instance = component.instance(); instance.setState({ tabsContainerOffsetLeft: 100, }); const lastElementIsInsideOfTabsContainer = { offsetLeft: 0, offsetWidth: 50, }; instance.tabsElements = [lastElementIsInsideOfTabsContainer]; instance.tabsContainer = { offsetWidth: 200, }; expect(instance.isRightArrowVisible()).toBeFalsy(); }); test('should make visible if the last element is outside the viewport', () => { const instance = component.instance(); instance.setState({ tabsContainerOffsetLeft: 100, }); const lastElementIsOutsideOfTabsContainer = { offsetLeft: 100, offsetWidth: 100, }; instance.tabsElements = [lastElementIsOutsideOfTabsContainer]; instance.tabsContainer = { offsetWidth: 100, }; expect(instance.isRightArrowVisible()).toBeTruthy(); }); test('should call onTabFocus when clicked', () => { component.find('button.right-arrow').simulate('click'); expect(onTabFocus.calledWith(1)).toBeTruthy(); }); }); describe('left arrow', () => { test('should make hidden when tabsContainer offset left is 0', () => { const instance = component.instance(); instance.tabsContainer = {}; component.setProps({ focusedIndex: 0 }); component.setProps({ selectedIndex: 0 }); component.setState({ tabsContainerOffsetLeft: 0 }); expect(instance.isLeftArrowVisible()).toBeFalsy(); }); test('should make hidden when tabsContainer offset left is 0 when focus index is not 0', () => { const instance = component.instance(); instance.tabsContainer = {}; component.setProps({ focusedIndex: 1 }); component.setState({ tabsContainerOffsetLeft: 0 }); expect(instance.isLeftArrowVisible()).toBeFalsy(); }); test('should make hidden when tabsContainer offset left is 0 when select index is not 0', () => { const instance = component.instance(); instance.tabsContainer = {}; component.setProps({ selectedIndex: 1 }); component.setState({ tabsContainerOffsetLeft: 0 }); expect(instance.isLeftArrowVisible()).toBeFalsy(); }); test('should make visible when tabsContainer offset left is not 0 and the focused is not 0', () => { const instance = component.instance(); instance.tabsContainer = {}; component.setProps({ focusedIndex: 1 }); component.setState({ tabsContainerOffsetLeft: 1 }); expect(instance.isLeftArrowVisible()).toBeTruthy(); }); test('should make visible when tabsContainer offset left is not 0 and the selected index is not 0', () => { const instance = component.instance(); instance.tabsContainer = {}; component.setProps({ selectedIndex: 1 }); component.setState({ tabsContainerOffsetLeft: 1 }); expect(instance.isLeftArrowVisible()).toBeTruthy(); }); test('should call onTabFocus when clicked', () => { component.find('button.left-arrow').simulate('click'); expect(onTabFocus.calledWith(-1)).toBeTruthy(); }); }); }); describe('accessibility with Tab key', () => { test('should refocus on the selected tab', () => { const resetFocusedTab = sinon.mock(); const component = mount( <TabViewPrimitive focusedIndex={0} isDynamic onTabFocus={() => {}} onTabSelect={() => {}} resetActiveTab={() => {}} resetFocusedTab={resetFocusedTab} selectedIndex={1} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); simulateKeyDown(component, 'Tab', false); expect(resetFocusedTab.calledOnce).toBeTruthy(); }); }); describe('focusOnTabElement', () => { let onTabFocus; let onTabSelect; let component; beforeEach(() => { onTabFocus = sinon.mock().withArgs(1); onTabSelect = sinon.mock().never(); component = mount( <TabViewPrimitive focusedIndex={0} isDynamic onTabFocus={onTabFocus} onTabSelect={onTabSelect} resetActiveTab={() => {}} resetFocusedTab={() => {}} selectedIndex={0} > <Tab title="tab1">Tab 1</Tab> <Tab title="tab2">Tab 2</Tab> </TabViewPrimitive>, ); }); test('should not throw when index is less than 0', () => { component.instance().focusOnTabElement(-1); }); test('should not throw when index is larger than then number of tabs', () => { component.instance().focusOnTabElement(3); }); }); }); });