Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tab): implement setFocusOnActivate #722

Merged
merged 5 commits into from Mar 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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', () => {
This conversation was marked as resolved.
Show resolved Hide resolved
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