Skip to content

Commit

Permalink
feat(tab): implement setFocusOnActivate (#722)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben McKernan authored and Matt Goo committed Mar 18, 2019
1 parent 7c15c54 commit fcd480d
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/tab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<TabIndicator />` element.
Expand Down
17 changes: 13 additions & 4 deletions packages/tab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import TabRipple, {TabRippleProps} from './TabRipple';

export interface TabProps extends React.HTMLProps<HTMLButtonElement> {
active?: boolean;
focusOnActivate?: boolean;
isFadingIndicator?: boolean;
indicatorContent?: React.ReactNode;
minWidth?: boolean;
Expand Down Expand Up @@ -59,6 +60,7 @@ export default class Tab extends React.Component<TabProps, TabState> {

static defaultProps: Partial<TabProps> = {
active: false,
focusOnActivate: true,
className: '',
isFadingIndicator: false,
indicatorContent: null,
Expand All @@ -76,9 +78,11 @@ export default class Tab extends React.Component<TabProps, TabState> {
};

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();
}
}
Expand All @@ -88,10 +92,14 @@ export default class Tab extends React.Component<TabProps, TabState> {
}

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();
}
Expand Down Expand Up @@ -176,6 +184,7 @@ export default class Tab extends React.Component<TabProps, TabState> {
const {
/* eslint-disable */
active,
focusOnActivate,
previousIndicatorClientRect,
className,
isFadingIndicator,
Expand Down
50 changes: 50 additions & 0 deletions test/unit/tab/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Tab>(<Tab focusOnActivate={false} />);
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<Tab>(<Tab focusOnActivate />);
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<Tab>(<Tab active focusOnActivate />, {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<Tab>(<Tab focusOnActivate />, {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<Tab>(<Tab active focusOnActivate={false} />, {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<Tab>(<Tab focusOnActivate={false} />, {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<Tab>(<Tab />);
wrapper.instance().adapter.addClass('test-class');
Expand Down

0 comments on commit fcd480d

Please sign in to comment.