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

fix(tab): mdc-web v1.0.0 upgrade #748

Merged
merged 8 commits into from
Mar 18, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 92 additions & 27 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"@material/select": "^0.40.1",
"@material/snackbar": "^0.43.0",
"@material/switch": "^0.41.0",
"@material/tab": "^0.41.0",
"@material/tab": "^1.0.0",
"@material/tab-bar": "^0.41.0",
"@material/tab-indicator": "^1.0.0",
"@material/tab-scroller": "^1.0.0",
Expand Down
13 changes: 12 additions & 1 deletion packages/tab-bar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,18 @@ class TabBar extends React.Component<
this.foundation.init();
const {activeIndex, indexInView} = this.props;
if (this.tabList[activeIndex]) {
this.tabList[activeIndex].activate({} /* previousIndicatorClientRect */);
// new DOMRect is not IE11 compatible
const defaultDOMRect = {
bottom: 0,
height: 0,
left: 0,
right: 0,
top: 0,
width: 0,
x: 0,
y: 0,
};
this.tabList[activeIndex].activate(defaultDOMRect /* previousIndicatorClientRect */);
}
this.foundation.scrollIntoView(indexInView);
}
Expand Down
1 change: 1 addition & 0 deletions packages/tab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ minWidth | boolean | If true will display the `<Tab />` as narrow as possible.
isMinWidthIndicator | boolean | If true will display the `<TabIndicator />` to the size of the longest content element.
isIconIndicator | boolean | If true will display the indicator content in the center of the tab.
previousIndicatorClientRect | ClientRect | The indicator's clientRect that was previously activated.
onInteraction | Function | The function is called if the tab receives any interaction
stacked | boolean | If true will display the tab icon and label to flow vertically instead of horizontally.
onTransitionEnd | function | transitionend event callback handler.

Expand Down
32 changes: 19 additions & 13 deletions packages/tab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import * as React from 'react';
import classnames from 'classnames';

import TabIndicator from '@material/react-tab-indicator';
// @ts-ignore No mdc .d.ts files
import {MDCTabFoundation} from '@material/tab/dist/mdc.tab';
import {MDCTabFoundation} from '@material/tab/foundation';
import {MDCTabAdapter} from '@material/tab/adapter';

import TabRipple, {TabRippleProps} from './TabRipple';

Expand All @@ -38,10 +38,11 @@ export interface TabProps extends React.HTMLProps<HTMLButtonElement> {
isMinWidthIndicator?: boolean;
stacked?: boolean;
previousIndicatorClientRect?: ClientRect;
onInteraction?: () => void;
}

interface MDCTabElementAttributes {
'aria-selected': boolean;
'aria-selected'?: 'false' | 'true';
tabIndex?: number;
}

Expand All @@ -52,7 +53,7 @@ interface TabState extends MDCTabElementAttributes {
}

export default class Tab extends React.Component<TabProps, TabState> {
foundation?: MDCTabFoundation;
foundation!: MDCTabFoundation;
tabRef: React.RefObject<HTMLButtonElement> = React.createRef();
tabContentRef: React.RefObject<HTMLSpanElement> = React.createRef();
tabIndicatorRef: React.RefObject<TabIndicator> = React.createRef();
Expand All @@ -67,11 +68,12 @@ export default class Tab extends React.Component<TabProps, TabState> {
minWidth: false,
isMinWidthIndicator: false,
stacked: false,
onInteraction: () => null,
};

state: TabState = {
'classList': new Set(),
'aria-selected': false,
'aria-selected': 'false',
'activateIndicator': false,
'previousIndicatorClientRect': this.props.previousIndicatorClientRect,
'tabIndex': -1,
Expand All @@ -81,7 +83,7 @@ export default class Tab extends React.Component<TabProps, TabState> {
const {active, focusOnActivate} = this.props;
this.foundation = new MDCTabFoundation(this.adapter);
this.foundation.init();
this.foundation.setFocusOnActivate(focusOnActivate);
this.foundation.setFocusOnActivate(focusOnActivate!);
if (active) {
this.foundation.activate();
}
Expand All @@ -94,7 +96,7 @@ export default class Tab extends React.Component<TabProps, TabState> {
componentDidUpdate(prevProps: TabProps) {
const {active, focusOnActivate, previousIndicatorClientRect} = this.props;
if (focusOnActivate !== prevProps.focusOnActivate) {
this.foundation.setFocusOnActivate(focusOnActivate);
this.foundation.setFocusOnActivate(focusOnActivate!);
}
if (active !== prevProps.active) {
if (active) {
Expand All @@ -115,7 +117,7 @@ export default class Tab extends React.Component<TabProps, TabState> {
});
}

get adapter() {
get adapter(): MDCTabAdapter {
return {
addClass: (className: string) => {
const classList = new Set(this.state.classList);
Expand All @@ -136,12 +138,15 @@ export default class Tab extends React.Component<TabProps, TabState> {
getOffsetWidth: () =>
Number(this.tabRef.current && this.tabRef.current.offsetWidth),
getContentOffsetLeft: () =>
this.tabContentRef.current &&
this.tabContentRef.current.offsetLeft,
this.tabContentRef.current ?
this.tabContentRef.current.offsetLeft :
0,
getContentOffsetWidth: () =>
this.tabContentRef.current &&
this.tabContentRef.current.offsetWidth,
this.tabContentRef.current ?
this.tabContentRef.current.offsetWidth :
0,
focus: () => this.tabRef.current && this.tabRef.current.focus(),
notifyInteracted: this.props.onInteraction!,
activateIndicator: (previousIndicatorClientRect: ClientRect) =>
this.setState({
activateIndicator: true,
Expand All @@ -151,7 +156,7 @@ export default class Tab extends React.Component<TabProps, TabState> {
};
}

activate(computeIndicatorClientRect?: ClientRect | {}) {
activate(computeIndicatorClientRect?: ClientRect) {
this.foundation.activate(computeIndicatorClientRect);
}

Expand Down Expand Up @@ -190,6 +195,7 @@ export default class Tab extends React.Component<TabProps, TabState> {
isFadingIndicator,
indicatorContent,
minWidth,
onInteraction,
stacked,
/* eslint-enable */
children,
Expand Down
2 changes: 1 addition & 1 deletion packages/tab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"dependencies": {
"@material/react-ripple": "^0.10.0",
"@material/react-tab-indicator": "^0.8.0",
"@material/tab": "^0.41.0",
"@material/tab": "^1.0.0",
"classnames": "^2.2.6",
"react": "^16.3.2"
},
Expand Down
17 changes: 9 additions & 8 deletions test/unit/tab/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as td from 'testdouble';
import {mount, shallow} from 'enzyme';
import Tab from '../../../packages/tab/index';
import TabIndicatorRef from '../../../packages/tab-indicator/index';
import {MDCTabDimensions} from '@material/tab/types';
import {coerceForTesting} from '../helpers/types';

suite('Tab');
Expand Down Expand Up @@ -67,14 +68,14 @@ test('if props.active updates to false, foundation.deactivate is called', () =>

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.instance().foundation.setFocusOnActivate = td.func<(focusOnActivate: boolean) => null>();
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.instance().foundation.setFocusOnActivate = td.func<(focusOnActivate: boolean) => null>();
wrapper.setProps({focusOnActivate: false});
td.verify(wrapper.instance().foundation.setFocusOnActivate(false), {times: 1});
});
Expand Down Expand Up @@ -149,8 +150,8 @@ test('#adapter.setAttr sets tabIndex on state', () => {

test('#adapter.setAttr sets aria-selected on state', () => {
const wrapper = shallow<Tab>(<Tab />);
wrapper.instance().adapter.setAttr('aria-selected', true);
assert.isTrue(wrapper.state()['aria-selected']);
wrapper.instance().adapter.setAttr('aria-selected', 'true');
assert.equal(wrapper.state()['aria-selected'], 'true');
});

test('#adapter.getOffsetLeft returns tabRef.offsetLeft', () => {
Expand Down Expand Up @@ -199,14 +200,14 @@ test('#adapter.deactivateIndicator sets state.activateIndicator', () => {
test('#activate calls foundation.activate', () => {
const clientRect = {test: 1} as unknown as ClientRect; ;
const wrapper = shallow<Tab>(<Tab />);
wrapper.instance().foundation.activate = td.func();
wrapper.instance().foundation.activate = td.func<(previousIndicatorClientRect?: ClientRect) => null>();
wrapper.instance().activate(clientRect);
td.verify(wrapper.instance().foundation.activate(clientRect), {times: 1});
});

test('#deactivate calls foundation.deactivate', () => {
const wrapper = shallow<Tab>(<Tab />);
wrapper.instance().foundation.deactivate = td.func();
wrapper.instance().foundation.deactivate = td.func<() => null>();
wrapper.instance().deactivate();
td.verify(wrapper.instance().foundation.deactivate(), {times: 1});
});
Expand All @@ -220,7 +221,7 @@ test('#computeIndicatorClientRect returns the tabIndicatorRef clientRect', () =>

test('#computeDimensions calls foundation.computeDimensions', () => {
const wrapper = shallow<Tab>(<Tab />);
wrapper.instance().foundation.computeDimensions = td.func();
wrapper.instance().foundation.computeDimensions = td.func<() => MDCTabDimensions>();
wrapper.instance().computeDimensions();
td.verify(wrapper.instance().foundation.computeDimensions(), {times: 1});
});
Expand Down Expand Up @@ -362,7 +363,7 @@ test('props.isMinWidthIndicator renders indicator within the content element', (
test('#componentWillUnmount destroys foundation', () => {
const wrapper = shallow<Tab>(<Tab />);
const foundation = wrapper.instance().foundation;
foundation.destroy = td.func();
foundation.destroy = td.func<() => null>();
wrapper.unmount();
td.verify(foundation.destroy(), {times: 1});
});
Expand Down