From 89d43e5d774fd3d6ec397f0a12264bad7e39b309 Mon Sep 17 00:00:00 2001 From: emyarod Date: Wed, 27 Nov 2019 00:27:22 -0800 Subject: [PATCH 1/5] fix(Tab): remove broken setTabFocus method --- packages/react/src/components/Tab/Tab-test.js | 5 +---- packages/react/src/components/Tab/Tab.js | 20 ------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/packages/react/src/components/Tab/Tab-test.js b/packages/react/src/components/Tab/Tab-test.js index fdd59ac083c4..b7d526f8b9c1 100644 --- a/packages/react/src/components/Tab/Tab-test.js +++ b/packages/react/src/components/Tab/Tab-test.js @@ -101,21 +101,18 @@ describe('Tab', () => { describe('keydown', () => { const onKeyDown = jest.fn(); - const handleTabAnchorFocus = jest.fn(); const handleTabKeyDown = jest.fn(); const wrapper = shallow(); - wrapper.setProps({ onKeyDown, handleTabAnchorFocus, handleTabKeyDown }); + wrapper.setProps({ onKeyDown, handleTabKeyDown }); it('invokes onKeyDown when a function is passed to onKeyDown prop', () => { wrapper.simulate('keyDown', { which: 38 }); expect(onKeyDown).toBeCalled(); - expect(handleTabAnchorFocus).not.toBeCalled(); }); it('invokes handleTabAnchorFocus when onKeyDown occurs for appropriate events', () => { wrapper.simulate('keyDown', { which: 37 }); expect(onKeyDown).toBeCalled(); - expect(handleTabAnchorFocus).toBeCalled(); }); }); }); diff --git a/packages/react/src/components/Tab/Tab.js b/packages/react/src/components/Tab/Tab.js index 5c35b034cfff..dde7ce38914a 100644 --- a/packages/react/src/components/Tab/Tab.js +++ b/packages/react/src/components/Tab/Tab.js @@ -26,12 +26,6 @@ export default class Tab extends React.Component { */ handleTabClick: PropTypes.func, - /** - * A handler that is invoked when a user presses left/right key. - * Reserved for usage in Tabs - */ - handleTabAnchorFocus: PropTypes.func, - /** * A handler that is invoked on the key down event for the control. * Reserved for usage in Tabs @@ -108,23 +102,10 @@ export default class Tab extends React.Component { onKeyDown: () => {}, }; - setTabFocus(evt) { - const leftKey = 37; - const rightKey = 39; - if (evt.which === leftKey) { - this.props.handleTabAnchorFocus(this.props.index - 1); - } else if (evt.which === rightKey) { - this.props.handleTabAnchorFocus(this.props.index + 1); - } else { - return; - } - } - render() { const { className, handleTabClick, - handleTabAnchorFocus, // eslint-disable-line handleTabKeyDown, disabled, href, @@ -172,7 +153,6 @@ export default class Tab extends React.Component { if (disabled) { return; } - this.setTabFocus(evt); handleTabKeyDown(index, evt); onKeyDown(evt); }} From b139cee2fa0bc8b195e18ec1ff9352b15fab6513 Mon Sep 17 00:00:00 2001 From: emyarod Date: Wed, 27 Nov 2019 00:29:47 -0800 Subject: [PATCH 2/5] docs(Tabs): add disabled tab --- packages/react/src/components/Tabs/Tabs-story.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/react/src/components/Tabs/Tabs-story.js b/packages/react/src/components/Tabs/Tabs-story.js index 1adea0976a6c..e49bdfbc7fbf 100644 --- a/packages/react/src/components/Tabs/Tabs-story.js +++ b/packages/react/src/components/Tabs/Tabs-story.js @@ -78,14 +78,17 @@ storiesOf('Tabs', module)
Content for second tab goes here.
+ +
Content for third tab goes here.
+
-
Content for third tab goes here.
+
Content for fourth tab goes here.
}> -
Content for fourth tab goes here.
+
Content for fifth tab goes here.
), From 2ce31ea899abdbff3830103165d7c17d3d9ecd62 Mon Sep 17 00:00:00 2001 From: emyarod Date: Wed, 27 Nov 2019 00:44:31 -0800 Subject: [PATCH 3/5] fix(Tabs): ignore disabled tabs on keyboard navigation --- .../react/src/components/Tabs/Tabs-test.js | 57 ++++++++++++++++- packages/react/src/components/Tabs/Tabs.js | 62 ++++++++++++------- 2 files changed, 95 insertions(+), 24 deletions(-) diff --git a/packages/react/src/components/Tabs/Tabs-test.js b/packages/react/src/components/Tabs/Tabs-test.js index 55f5034c7032..793821e153d6 100644 --- a/packages/react/src/components/Tabs/Tabs-test.js +++ b/packages/react/src/components/Tabs/Tabs-test.js @@ -7,14 +7,25 @@ import React from 'react'; import { ChevronDownGlyph } from '@carbon/icons-react'; +import { settings } from 'carbon-components'; +import { shallow, mount } from 'enzyme'; import Tabs from '../Tabs'; import Tab from '../Tab'; import TabsSkeleton from '../Tabs/Tabs.Skeleton'; -import { shallow, mount } from 'enzyme'; -import { settings } from 'carbon-components'; const { prefix } = settings; +window.matchMedia = jest.fn().mockImplementation(query => ({ + matches: true, + media: query, + onchange: null, + addListener: jest.fn(), // deprecated + removeListener: jest.fn(), // deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), +})); + describe('Tabs', () => { describe('renders as expected', () => { describe('navigation (
)', () => { @@ -241,6 +252,48 @@ describe('Tabs', () => { expect(wrapper.state().selected).toEqual(1); }); }); + + describe('ignore disabled child tab', () => { + const wrapper = mount( + + + content1 + + + content2 + + + content3 + + + ); + const firstTab = wrapper.find('.firstTab').last(); + const lastTab = wrapper.find('.lastTab').last(); + it('updates selected state when pressing arrow keys', () => { + firstTab.simulate('keydown', { which: rightKey }); + expect(wrapper.state().selected).toEqual(2); + lastTab.simulate('keydown', { which: leftKey }); + expect(wrapper.state().selected).toEqual(0); + }); + + it('loops focus and selected state from lastTab to firstTab', () => { + wrapper.setState({ selected: 2 }); + lastTab.simulate('keydown', { which: rightKey }); + expect(wrapper.state().selected).toEqual(0); + }); + + it('loops focus and selected state from firstTab to lastTab', () => { + firstTab.simulate('keydown', { which: leftKey }); + expect(wrapper.state().selected).toEqual(2); + }); + + it('updates selected state when pressing space or enter key', () => { + firstTab.simulate('keydown', { which: spaceKey }); + expect(wrapper.state().selected).toEqual(0); + lastTab.simulate('keydown', { which: enterKey }); + expect(wrapper.state().selected).toEqual(2); + }); + }); }); }); diff --git a/packages/react/src/components/Tabs/Tabs.js b/packages/react/src/components/Tabs/Tabs.js index 00979a33b700..5b2372a8e5af 100644 --- a/packages/react/src/components/Tabs/Tabs.js +++ b/packages/react/src/components/Tabs/Tabs.js @@ -10,6 +10,7 @@ import React from 'react'; import classNames from 'classnames'; import { ChevronDownGlyph } from '@carbon/icons-react'; import { settings } from 'carbon-components'; +import { keys, match, matches } from '../../internal/keyboard'; const { prefix } = settings; @@ -116,6 +117,12 @@ export default class Tabs extends React.Component { return React.Children.map(this.props.children, tab => tab); } + getEnabledTabs = () => + React.Children.toArray(this.props.children).reduce( + (acc, tab, index) => (!tab.props.disabled ? acc.concat(index) : acc), + [] + ); + getTabAt = (index, useFresh) => { return ( (!useFresh && this[`tab${index}`]) || @@ -139,35 +146,47 @@ export default class Tabs extends React.Component { }; }; + getDirection = evt => { + if (match(evt, keys.ArrowLeft)) { + return -1; + } + if (match(evt, keys.ArrowRight)) { + return 1; + } + return 0; + }; + + getNextIndex = (index, direction) => { + const enabledTabs = this.getEnabledTabs(); + const nextIndex = Math.max( + enabledTabs.indexOf(index) + direction, + -1 /* For `tab` not found in `enabledTabs` */ + ); + const nextIndexLooped = + nextIndex >= 0 && nextIndex < enabledTabs.length + ? nextIndex + : nextIndex - Math.sign(nextIndex) * enabledTabs.length; + return enabledTabs[nextIndexLooped]; + }; + handleTabKeyDown = onSelectionChange => { return (index, evt) => { - const key = evt.key || evt.which; - - if (key === 'Enter' || key === 13 || key === ' ' || key === 32) { + if (matches(evt, [keys.Enter, keys.Space])) { this.selectTabAt(index, onSelectionChange); this.setState({ dropdownHidden: true, }); } - }; - }; - - handleTabAnchorFocus = onSelectionChange => { - return index => { - const tabCount = React.Children.count(this.props.children) - 1; - let tabIndex = index; - if (index < 0) { - tabIndex = tabCount; - } else if (index > tabCount) { - tabIndex = 0; - } - - const tab = this.getTabAt(tabIndex); - if (tab) { - this.selectTabAt(tabIndex, onSelectionChange); - if (tab.tabAnchor) { - tab.tabAnchor.focus(); + if (window.matchMedia('(min-width: 42rem)').matches) { + evt.preventDefault(); + const nextIndex = this.getNextIndex(index, this.getDirection(evt)); + const tab = this.getTabAt(nextIndex); + if (tab) { + this.selectTabAt(nextIndex, onSelectionChange); + if (tab.tabAnchor) { + tab.tabAnchor.focus(); + } } } }; @@ -222,7 +241,6 @@ export default class Tabs extends React.Component { index, selected: index === this.state.selected, handleTabClick: this.handleTabClick(onSelectionChange), - handleTabAnchorFocus: this.handleTabAnchorFocus(onSelectionChange), tabIndex, ref: e => { this.setTabAt(index, e); From a384253f89d287bc72ba2fe7608aca4dfe44f2d2 Mon Sep 17 00:00:00 2001 From: emyarod Date: Tue, 7 Jan 2020 18:33:22 -0600 Subject: [PATCH 4/5] test(Tabs): add `beforeEach` block --- .../react/src/components/Tabs/Tabs-test.js | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/react/src/components/Tabs/Tabs-test.js b/packages/react/src/components/Tabs/Tabs-test.js index 793821e153d6..365ae339c2a7 100644 --- a/packages/react/src/components/Tabs/Tabs-test.js +++ b/packages/react/src/components/Tabs/Tabs-test.js @@ -254,21 +254,26 @@ describe('Tabs', () => { }); describe('ignore disabled child tab', () => { - const wrapper = mount( - - - content1 - - - content2 - - - content3 - - - ); - const firstTab = wrapper.find('.firstTab').last(); - const lastTab = wrapper.find('.lastTab').last(); + let wrapper; + let firstTab; + let lastTab; + beforeEach(() => { + wrapper = mount( + + + content1 + + + content2 + + + content3 + + + ); + firstTab = wrapper.find('.firstTab').last(); + lastTab = wrapper.find('.lastTab').last(); + }); it('updates selected state when pressing arrow keys', () => { firstTab.simulate('keydown', { which: rightKey }); expect(wrapper.state().selected).toEqual(2); From 695fe4850c418c122546eb70547421c55b004f88 Mon Sep 17 00:00:00 2001 From: Akira Sudoh Date: Thu, 9 Jan 2020 09:01:06 +0900 Subject: [PATCH 5/5] docs(contributing): format fix --- .github/ISSUE_TEMPLATE/bug-report.md | 4 ++-- .github/ISSUE_TEMPLATE/feature-request-or-enhancement.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 7c004b247455..090d1405c587 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,8 +1,8 @@ --- -name: "Bug Report 🐛" +name: 'Bug Report 🐛' about: Something isn't working as expected? Here is the right place to report. title: '' -labels: "type: bug 🐛, squad: system" +labels: 'type: bug 🐛, squad: system' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature-request-or-enhancement.md b/.github/ISSUE_TEMPLATE/feature-request-or-enhancement.md index b051831f095b..a36dcf77aca7 100644 --- a/.github/ISSUE_TEMPLATE/feature-request-or-enhancement.md +++ b/.github/ISSUE_TEMPLATE/feature-request-or-enhancement.md @@ -1,8 +1,8 @@ --- -name: "Feature request or enhancement 💡" +name: 'Feature request or enhancement 💡' about: Suggest an idea for this project title: '' -labels: "type: enhancement 💡, squad: system" +labels: 'type: enhancement 💡, squad: system' assignees: '' ---