Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Commit 0f7ebba

Browse files
Ben McKernanMatt Goo
Ben McKernan
authored and
Matt Goo
committed
feat(tab): implement setFocusOnActivate (#722)
1 parent c77a681 commit 0f7ebba

File tree

3 files changed

+64
-4
lines changed

3 files changed

+64
-4
lines changed

packages/tab/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class MyApp extends React.Component {
7878
Prop Name | Type | Description
7979
--- | --- | ---
8080
active | boolean | If true will activate the tab and indicator.
81+
focusOnActivate | boolean | If true will focus itself when activated. Defaults to `true`.
8182
className | string | Classes to appear on className attribute of root element.
8283
isFadingIndicator | boolean | Enables a fading indicator, instead of sliding (default).
8384
indicatorContent | element | Element that will appear within the `<TabIndicator />` element.

packages/tab/index.tsx

+13-4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import TabRipple, {TabRippleProps} from './TabRipple';
3131

3232
export interface TabProps extends React.HTMLProps<HTMLButtonElement> {
3333
active?: boolean;
34+
focusOnActivate?: boolean;
3435
isFadingIndicator?: boolean;
3536
indicatorContent?: React.ReactNode;
3637
minWidth?: boolean;
@@ -59,6 +60,7 @@ export default class Tab extends React.Component<TabProps, TabState> {
5960

6061
static defaultProps: Partial<TabProps> = {
6162
active: false,
63+
focusOnActivate: true,
6264
className: '',
6365
isFadingIndicator: false,
6466
indicatorContent: null,
@@ -76,9 +78,11 @@ export default class Tab extends React.Component<TabProps, TabState> {
7678
};
7779

7880
componentDidMount() {
81+
const {active, focusOnActivate} = this.props;
7982
this.foundation = new MDCTabFoundation(this.adapter);
8083
this.foundation.init();
81-
if (this.props.active) {
84+
this.foundation.setFocusOnActivate(focusOnActivate);
85+
if (active) {
8286
this.foundation.activate();
8387
}
8488
}
@@ -88,10 +92,14 @@ export default class Tab extends React.Component<TabProps, TabState> {
8892
}
8993

9094
componentDidUpdate(prevProps: TabProps) {
91-
if (this.props.active !== prevProps.active) {
92-
if (this.props.active) {
95+
const {active, focusOnActivate, previousIndicatorClientRect} = this.props;
96+
if (focusOnActivate !== prevProps.focusOnActivate) {
97+
this.foundation.setFocusOnActivate(focusOnActivate);
98+
}
99+
if (active !== prevProps.active) {
100+
if (active) {
93101
// If active state is updated through props, previousIndicatorClientRect must also be passed through props
94-
this.activate(this.props.previousIndicatorClientRect);
102+
this.activate(previousIndicatorClientRect);
95103
} else {
96104
this.deactivate();
97105
}
@@ -176,6 +184,7 @@ export default class Tab extends React.Component<TabProps, TabState> {
176184
const {
177185
/* eslint-disable */
178186
active,
187+
focusOnActivate,
179188
previousIndicatorClientRect,
180189
className,
181190
isFadingIndicator,

test/unit/tab/index.test.tsx

+50
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,56 @@ test('if props.active updates to false, foundation.deactivate is called', () =>
6565
td.verify(wrapper.instance().deactivate(), {times: 1});
6666
});
6767

68+
test('calls foundation.setFocusOnActivate when props.focusOnActivate changes from false to true', () => {
69+
const wrapper = shallow<Tab>(<Tab focusOnActivate={false} />);
70+
wrapper.instance().foundation.setFocusOnActivate = td.func();
71+
wrapper.setProps({focusOnActivate: true});
72+
td.verify(wrapper.instance().foundation.setFocusOnActivate(true), {times: 1});
73+
});
74+
75+
test('calls foundation.setFocusOnActivate when props.focusOnActivate changes from true to false', () => {
76+
const wrapper = shallow<Tab>(<Tab focusOnActivate />);
77+
wrapper.instance().foundation.setFocusOnActivate = td.func();
78+
wrapper.setProps({focusOnActivate: false});
79+
td.verify(wrapper.instance().foundation.setFocusOnActivate(false), {times: 1});
80+
});
81+
82+
test('when props.focusOnActivate is true, an active tab should be focused on mount', () => {
83+
const div = document.createElement('div');
84+
document.body.append(div);
85+
const wrapper = mount<Tab>(<Tab active focusOnActivate />, {attachTo: div});
86+
assert.equal(document.activeElement, wrapper.getDOMNode());
87+
div.remove();
88+
});
89+
90+
test('when props.focusOnActivate is true and active is changed to true, the tab should be focused', () => {
91+
const div = document.createElement('div');
92+
document.body.append(div);
93+
const wrapper = mount<Tab>(<Tab focusOnActivate />, {attachTo: div});
94+
assert.notEqual(document.activeElement, wrapper.getDOMNode());
95+
wrapper.setProps({active: true});
96+
assert.equal(document.activeElement, wrapper.getDOMNode());
97+
div.remove();
98+
});
99+
100+
test('when props.focusOnActivate is false, an active tab should not be focused on mount', () => {
101+
const div = document.createElement('div');
102+
document.body.append(div);
103+
const wrapper = mount<Tab>(<Tab active focusOnActivate={false} />, {attachTo: div});
104+
assert.notEqual(document.activeElement, wrapper.getDOMNode());
105+
div.remove();
106+
});
107+
108+
test('when props.focusOnActivate is false and active is changed to true, the tab should not be focused', () => {
109+
const div = document.createElement('div');
110+
document.body.append(div);
111+
const wrapper = mount<Tab>(<Tab focusOnActivate={false} />, {attachTo: div});
112+
assert.notEqual(document.activeElement, wrapper.getDOMNode());
113+
wrapper.setProps({active: true});
114+
assert.notEqual(document.activeElement, wrapper.getDOMNode());
115+
div.remove();
116+
});
117+
68118
test('#adapter.addClass adds class to state.classList', () => {
69119
const wrapper = shallow<Tab>(<Tab />);
70120
wrapper.instance().adapter.addClass('test-class');

0 commit comments

Comments
 (0)