From fcd480d7273bda79e8f8cd14cd977a1a0d204d6f Mon Sep 17 00:00:00 2001 From: Ben McKernan Date: Tue, 12 Mar 2019 17:54:05 +0100 Subject: [PATCH] feat(tab): implement setFocusOnActivate (#722) --- packages/tab/README.md | 1 + packages/tab/index.tsx | 17 +++++++++--- test/unit/tab/index.test.tsx | 50 ++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/packages/tab/README.md b/packages/tab/README.md index 107292b85..c0a65e7e5 100644 --- a/packages/tab/README.md +++ b/packages/tab/README.md @@ -78,6 +78,7 @@ class MyApp extends React.Component { Prop Name | Type | Description --- | --- | --- active | boolean | If true will activate the tab and indicator. +focusOnActivate | boolean | If true will focus itself when activated. Defaults to `true`. className | string | Classes to appear on className attribute of root element. isFadingIndicator | boolean | Enables a fading indicator, instead of sliding (default). indicatorContent | element | Element that will appear within the `` element. diff --git a/packages/tab/index.tsx b/packages/tab/index.tsx index cef0fa5af..f9c9104db 100644 --- a/packages/tab/index.tsx +++ b/packages/tab/index.tsx @@ -31,6 +31,7 @@ import TabRipple, {TabRippleProps} from './TabRipple'; export interface TabProps extends React.HTMLProps { active?: boolean; + focusOnActivate?: boolean; isFadingIndicator?: boolean; indicatorContent?: React.ReactNode; minWidth?: boolean; @@ -59,6 +60,7 @@ export default class Tab extends React.Component { static defaultProps: Partial = { active: false, + focusOnActivate: true, className: '', isFadingIndicator: false, indicatorContent: null, @@ -76,9 +78,11 @@ export default class Tab extends React.Component { }; componentDidMount() { + const {active, focusOnActivate} = this.props; this.foundation = new MDCTabFoundation(this.adapter); this.foundation.init(); - if (this.props.active) { + this.foundation.setFocusOnActivate(focusOnActivate); + if (active) { this.foundation.activate(); } } @@ -88,10 +92,14 @@ export default class Tab extends React.Component { } componentDidUpdate(prevProps: TabProps) { - if (this.props.active !== prevProps.active) { - if (this.props.active) { + const {active, focusOnActivate, previousIndicatorClientRect} = this.props; + if (focusOnActivate !== prevProps.focusOnActivate) { + this.foundation.setFocusOnActivate(focusOnActivate); + } + if (active !== prevProps.active) { + if (active) { // If active state is updated through props, previousIndicatorClientRect must also be passed through props - this.activate(this.props.previousIndicatorClientRect); + this.activate(previousIndicatorClientRect); } else { this.deactivate(); } @@ -176,6 +184,7 @@ export default class Tab extends React.Component { const { /* eslint-disable */ active, + focusOnActivate, previousIndicatorClientRect, className, isFadingIndicator, diff --git a/test/unit/tab/index.test.tsx b/test/unit/tab/index.test.tsx index 5787a66d5..92797aed7 100644 --- a/test/unit/tab/index.test.tsx +++ b/test/unit/tab/index.test.tsx @@ -65,6 +65,56 @@ test('if props.active updates to false, foundation.deactivate is called', () => td.verify(wrapper.instance().deactivate(), {times: 1}); }); +test('calls foundation.setFocusOnActivate when props.focusOnActivate changes from false to true', () => { + const wrapper = shallow(); + wrapper.instance().foundation.setFocusOnActivate = td.func(); + wrapper.setProps({focusOnActivate: true}); + td.verify(wrapper.instance().foundation.setFocusOnActivate(true), {times: 1}); +}); + +test('calls foundation.setFocusOnActivate when props.focusOnActivate changes from true to false', () => { + const wrapper = shallow(); + wrapper.instance().foundation.setFocusOnActivate = td.func(); + wrapper.setProps({focusOnActivate: false}); + td.verify(wrapper.instance().foundation.setFocusOnActivate(false), {times: 1}); +}); + +test('when props.focusOnActivate is true, an active tab should be focused on mount', () => { + const div = document.createElement('div'); + document.body.append(div); + const wrapper = mount(, {attachTo: div}); + assert.equal(document.activeElement, wrapper.getDOMNode()); + div.remove(); +}); + +test('when props.focusOnActivate is true and active is changed to true, the tab should be focused', () => { + const div = document.createElement('div'); + document.body.append(div); + const wrapper = mount(, {attachTo: div}); + assert.notEqual(document.activeElement, wrapper.getDOMNode()); + wrapper.setProps({active: true}); + assert.equal(document.activeElement, wrapper.getDOMNode()); + div.remove(); +}); + +test('when props.focusOnActivate is false, an active tab should not be focused on mount', () => { + const div = document.createElement('div'); + document.body.append(div); + const wrapper = mount(, {attachTo: div}); + assert.notEqual(document.activeElement, wrapper.getDOMNode()); + div.remove(); +}); + +test('when props.focusOnActivate is false and active is changed to true, the tab should not be focused', () => { + const div = document.createElement('div'); + document.body.append(div); + const wrapper = mount(, {attachTo: div}); + assert.notEqual(document.activeElement, wrapper.getDOMNode()); + wrapper.setProps({active: true}); + assert.notEqual(document.activeElement, wrapper.getDOMNode()); + div.remove(); +}); + test('#adapter.addClass adds class to state.classList', () => { const wrapper = shallow(); wrapper.instance().adapter.addClass('test-class');