From 5a7cedbd14cac0ed3fd60522c943b5ea057d81be Mon Sep 17 00:00:00 2001 From: Andrii Kostenko Date: Mon, 25 Mar 2019 00:34:41 +0300 Subject: [PATCH] fix(tab-bar): upgrade mdc-web to v1 --- package-lock.json | 176 ++++++++++++++++++------------- package.json | 2 +- packages/tab-bar/index.tsx | 24 +++-- packages/tab-bar/package.json | 2 +- packages/tab/index.tsx | 2 +- test/unit/tab-bar/index.test.tsx | 44 +++++--- 6 files changed, 147 insertions(+), 103 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a0a1000a..94d8f5a16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -692,21 +692,6 @@ "tslib": "^1.9.3" } }, - "@material/list": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/list/-/list-0.41.0.tgz", - "integrity": "sha512-HhYN0I02CTT8j91c1eeeI+L2KXVKdfzj0Zuapp2SdeCmQZLJO2tu2NYj0W6REBDTVBWBccr12Sn8o71CodEScQ==", - "dev": true, - "requires": { - "@material/base": "^0.41.0", - "@material/dom": "^0.41.0", - "@material/ripple": "^0.41.0", - "@material/rtl": "^0.40.1", - "@material/shape": "^0.41.0", - "@material/theme": "^0.41.0", - "@material/typography": "^0.41.0" - } - }, "@material/ripple": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-1.0.1.tgz", @@ -809,23 +794,51 @@ "dev": true }, "@material/floating-label": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-0.41.0.tgz", - "integrity": "sha512-qI6f1nZU3crXxWAI9fw3U5fHw2qOzEor49EvskbcaV5KSRW5qO+jtfUQ3ib/Vhki7lqhgwNHB/0n7KYhvhjRHQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-1.0.0.tgz", + "integrity": "sha512-yESFi8HEUO0PmPWvaU3VW4X8+xaoFHhj2xGxLPu2hGye4ZBjTpmjOX6y1vwYqZLD5KWXj91k695UAT6J2wtWFQ==", "dev": true, "requires": { - "@material/animation": "^0.41.0", - "@material/base": "^0.41.0", - "@material/rtl": "^0.40.1", - "@material/theme": "^0.41.0", - "@material/typography": "^0.41.0" + "@material/animation": "^1.0.0", + "@material/base": "^1.0.0", + "@material/rtl": "^0.42.0", + "@material/theme": "^1.0.0", + "@material/typography": "^1.0.0", + "tslib": "^1.9.3" }, "dependencies": { - "@material/base": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/base/-/base-0.41.0.tgz", - "integrity": "sha512-tEyzwBRu3d1H120SfKsDVYZHcqT5lKohh/7cWKR93aAaPDkSvjpKJIjyu2yuSkjpDduVZGzVocYbOvhUKhhzXQ==", + "@material/animation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-1.0.0.tgz", + "integrity": "sha512-Ed5/vggn6ZhSJ87yn3ZS1d826VJNFz73jHF2bSsgRtHDoB8KCuOwQMfdgAgDa4lKDF6CDIPCKBZPKrs2ubehdw==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } + }, + "@material/rtl": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-0.42.0.tgz", + "integrity": "sha512-VrnrKJzhmspsN8WXHuxxBZ69yM5IwhCUqWr1t1eNfw3ZEvEj7i1g3P31HGowKThIN1dc1Wh4LE14rCISWCtv5w==", "dev": true + }, + "@material/theme": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-1.0.0.tgz", + "integrity": "sha512-Bg/BQLU5MmCwtQ3DHcSs9DodZB8PTvuItv1wXrP54S/wBVwryIB5uMDmERhnItbNnAFbkKhlAuhn1asMmMzfkQ==", + "dev": true, + "requires": { + "@material/feature-targeting": "^0.44.1" + } + }, + "@material/typography": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-1.0.0.tgz", + "integrity": "sha512-Oeqbjci1cC7jTE8/n3dwnkqKe9ZeWiaE+rgMtRYtRFw1HvAw14SpGA5EEAS/Li2Hu2KZ50FYCe3HYqShfxtChA==", + "dev": true, + "requires": { + "@material/feature-targeting": "^0.44.1" + } } } }, @@ -942,6 +955,15 @@ "tslib": "^1.9.3" }, "dependencies": { + "@material/animation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-1.0.0.tgz", + "integrity": "sha512-Ed5/vggn6ZhSJ87yn3ZS1d826VJNFz73jHF2bSsgRtHDoB8KCuOwQMfdgAgDa4lKDF6CDIPCKBZPKrs2ubehdw==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } + }, "@material/dom": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@material/dom/-/dom-1.0.1.tgz", @@ -951,6 +973,20 @@ "tslib": "^1.9.3" } }, + "@material/ripple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-1.0.1.tgz", + "integrity": "sha512-aBigRoVMjIU2lLDq7TMocI2H2YFbO1hICs5FTdSRp4Yis/QFTrgaW32q8yuHdZI56j+b2BWIWapqA2xpSmCMXQ==", + "dev": true, + "requires": { + "@material/animation": "^1.0.0", + "@material/base": "^1.0.0", + "@material/dom": "^1.0.1", + "@material/feature-targeting": "^0.44.1", + "@material/theme": "^1.0.0", + "tslib": "^1.9.3" + } + }, "@material/rtl": { "version": "0.42.0", "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-0.42.0.tgz", @@ -1476,68 +1512,45 @@ } }, "@material/tab-bar": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-0.41.0.tgz", - "integrity": "sha512-RL+0CA4ZeZAmhz3vlyFsm8h9sLim8JHTLkosfZSYRnx2o9iQHQHpV58jz76ZSWG+0iuDoHFnwZ2oNKNmImn0KQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-1.0.1.tgz", + "integrity": "sha512-zou4iUmTPW96uEepD3whGbkPeo4SvuaQCuR0FsRiBQA2fCdnGX2Eyn03GY5Kv41lDw9RpoEoMflkKU/njHK8gw==", "dev": true, "requires": { - "@material/base": "^0.41.0", - "@material/elevation": "^0.41.0", - "@material/tab": "^0.41.0", - "@material/tab-scroller": "^0.41.0" + "@material/base": "^1.0.0", + "@material/elevation": "^1.0.0", + "@material/tab": "^1.0.1", + "@material/tab-scroller": "^1.0.1", + "tslib": "^1.9.3" }, "dependencies": { - "@material/base": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/base/-/base-0.41.0.tgz", - "integrity": "sha512-tEyzwBRu3d1H120SfKsDVYZHcqT5lKohh/7cWKR93aAaPDkSvjpKJIjyu2yuSkjpDduVZGzVocYbOvhUKhhzXQ==", - "dev": true - }, - "@material/ripple": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-0.41.0.tgz", - "integrity": "sha512-rxEUVWM4AByDlTCH0kkthZQmUuY6eeN0X6cOHBoioFN2vUDk0D0Nfzz/N9FF2AlAf8C2lDDLrTuqnJPVIn+NHA==", - "dev": true, - "requires": { - "@material/animation": "^0.41.0", - "@material/base": "^0.41.0", - "@material/theme": "^0.41.0" - } - }, - "@material/tab": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/tab/-/tab-0.41.0.tgz", - "integrity": "sha512-yM6eYD8Kgrk2cHa+zN3GYIK4Mt6EsSxDIpaArE6JopqRpalULjiOk83hWVPR1V95xphnzYAWM1YF6I6JexE9kw==", + "@material/animation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-1.0.0.tgz", + "integrity": "sha512-Ed5/vggn6ZhSJ87yn3ZS1d826VJNFz73jHF2bSsgRtHDoB8KCuOwQMfdgAgDa4lKDF6CDIPCKBZPKrs2ubehdw==", "dev": true, "requires": { - "@material/base": "^0.41.0", - "@material/ripple": "^0.41.0", - "@material/rtl": "^0.40.1", - "@material/tab-indicator": "^0.41.0", - "@material/theme": "^0.41.0", - "@material/typography": "^0.41.0" + "tslib": "^1.9.3" } }, - "@material/tab-indicator": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-0.41.0.tgz", - "integrity": "sha512-IBJEO+O8OnFVgRAn4CCGccpyNPF1bvTp5+1foD46S2u7XZLD7ejfxTQhqE5HYWtVLQ3zk1aYo3+N9+oSUkpM2w==", + "@material/elevation": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-1.0.0.tgz", + "integrity": "sha512-TqmvEXmZDYLm2X5lEnjKCsZMDkCXpxFFxL22AfCAQB5L4d0gAS7vqDEE797y4Rp+BBKEcOP71mum1l56RI3NBQ==", "dev": true, "requires": { - "@material/animation": "^0.41.0", - "@material/base": "^0.41.0", - "@material/theme": "^0.41.0" + "@material/animation": "^1.0.0", + "@material/feature-targeting": "^0.44.1", + "@material/theme": "^1.0.0" } }, - "@material/tab-scroller": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-0.41.0.tgz", - "integrity": "sha512-dyxaxLLSiDigIUVJ0BwqnKBtBseALrOhmPgvk6BQVDbynnRQ2bOvaNZ7cbpe3A0i8zOQGOoTZF4i9D38/iubcg==", + "@material/theme": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-1.0.0.tgz", + "integrity": "sha512-Bg/BQLU5MmCwtQ3DHcSs9DodZB8PTvuItv1wXrP54S/wBVwryIB5uMDmERhnItbNnAFbkKhlAuhn1asMmMzfkQ==", "dev": true, "requires": { - "@material/animation": "^0.41.0", - "@material/base": "^0.41.0", - "@material/tab": "^0.41.0" + "@material/feature-targeting": "^0.44.1" } } } @@ -1684,6 +1697,19 @@ "integrity": "sha512-tEyzwBRu3d1H120SfKsDVYZHcqT5lKohh/7cWKR93aAaPDkSvjpKJIjyu2yuSkjpDduVZGzVocYbOvhUKhhzXQ==", "dev": true }, + "@material/floating-label": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-0.41.0.tgz", + "integrity": "sha512-qI6f1nZU3crXxWAI9fw3U5fHw2qOzEor49EvskbcaV5KSRW5qO+jtfUQ3ib/Vhki7lqhgwNHB/0n7KYhvhjRHQ==", + "dev": true, + "requires": { + "@material/animation": "^0.41.0", + "@material/base": "^0.41.0", + "@material/rtl": "^0.40.1", + "@material/theme": "^0.41.0", + "@material/typography": "^0.41.0" + } + }, "@material/line-ripple": { "version": "0.41.0", "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-0.41.0.tgz", diff --git a/package.json b/package.json index d4c85d40e..5df31c790 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "@material/snackbar": "^1.0.0", "@material/switch": "^0.41.0", "@material/tab": "^1.0.0", - "@material/tab-bar": "^0.41.0", + "@material/tab-bar": "^1.0.0", "@material/tab-indicator": "^1.0.0", "@material/tab-scroller": "^1.0.0", "@material/textfield": "^0.41.0", diff --git a/packages/tab-bar/index.tsx b/packages/tab-bar/index.tsx index fa95ec124..232fd9167 100644 --- a/packages/tab-bar/index.tsx +++ b/packages/tab-bar/index.tsx @@ -2,13 +2,14 @@ import * as React from 'react'; import classnames from 'classnames'; import TabScroller from '@material/react-tab-scroller'; import Tab, {TabProps} from '@material/react-tab'; // eslint-disable-line no-unused-vars -// @ts-ignore No mdc .d.ts files -import {MDCTabBarFoundation} from '@material/tab-bar/dist/mdc.tabBar'; +import {MDCTabBarFoundation} from '@material/tab-bar/foundation'; +import {MDCTabBarAdapter} from '@material/tab-bar/adapter'; export interface TabBarProps extends React.HTMLAttributes { indexInView?: number; activeIndex: number; handleActiveIndexUpdate?: (index: number) => void; + onActivated?: (index: number) => void; className?: string; isRtl?: boolean; children: React.ReactElement | React.ReactElement[]; @@ -24,7 +25,7 @@ class TabBar extends React.Component< tabBarRef: React.RefObject = React.createRef(); tabScrollerRef: React.RefObject = React.createRef(); tabList: Tab[] = []; - foundation?: MDCTabBarFoundation; + foundation!: MDCTabBarFoundation; constructor(props: TabBarProps) { super(props); @@ -60,7 +61,7 @@ class TabBar extends React.Component< }; this.tabList[activeIndex].activate(defaultDOMRect /* previousIndicatorClientRect */); } - this.foundation.scrollIntoView(indexInView); + this.foundation.scrollIntoView(indexInView!); } componentDidUpdate(prevProps: TabBarProps) { @@ -70,7 +71,7 @@ class TabBar extends React.Component< ); } if (this.props.indexInView !== prevProps.indexInView) { - this.foundation.scrollIntoView(this.props.indexInView); + this.foundation.scrollIntoView(this.props.indexInView!); } } @@ -82,7 +83,7 @@ class TabBar extends React.Component< return classnames('mdc-tab-bar', this.props.className); } - get adapter() { + get adapter(): MDCTabBarAdapter { return { scrollTo: (scrollX: number) => { this.tabScrollerRef.current && this.tabScrollerRef.current.scrollTo(scrollX); @@ -91,15 +92,15 @@ class TabBar extends React.Component< this.tabScrollerRef.current && this.tabScrollerRef.current.incrementScroll(scrollXIncrement); }, getScrollPosition: () => { - if (!this.tabScrollerRef.current) return; + if (!this.tabScrollerRef.current) return 0; return this.tabScrollerRef.current.getScrollPosition(); }, getScrollContentWidth: () => { - if (!this.tabScrollerRef.current) return; + if (!this.tabScrollerRef.current) return 0; return this.tabScrollerRef.current.getScrollContentWidth(); }, getOffsetWidth: () => { - if (this.tabBarRef.current === null) return; + if (this.tabBarRef.current === null) return 0; return this.tabBarRef.current.offsetWidth; }, isRTL: () => !!this.props.isRtl, @@ -127,8 +128,9 @@ class TabBar extends React.Component< } return -1; }, - getIndexOfTab: (tabToFind: Tab) => this.tabList.indexOf(tabToFind), + getIndexOfTabById: (id: string) => this.tabList.map((tab) => tab.props.id).indexOf(id), getTabListLength: () => this.tabList.length, + notifyTabActivated: (index: number) => this.props.onActivated && this.props.onActivated(index), }; } @@ -140,7 +142,7 @@ class TabBar extends React.Component< // Persist the synthetic event to access its `key`. e.persist(); this.setState({previousActiveIndex: this.props.activeIndex}, () => - this.foundation.handleKeyDown(e) + this.foundation.handleKeyDown(e.nativeEvent) ); if (this.props.onKeyDown) { this.props.onKeyDown(e); diff --git a/packages/tab-bar/package.json b/packages/tab-bar/package.json index c2ffee10d..6351292d6 100644 --- a/packages/tab-bar/package.json +++ b/packages/tab-bar/package.json @@ -20,7 +20,7 @@ "dependencies": { "@material/react-tab": "^0.11.0", "@material/react-tab-scroller": "^0.11.0", - "@material/tab-bar": "^0.41.0", + "@material/tab-bar": "^1.0.0", "classnames": "^2.2.6", "react": "^16.3.2" }, diff --git a/packages/tab/index.tsx b/packages/tab/index.tsx index 76e9ab96d..72bb08f5b 100644 --- a/packages/tab/index.tsx +++ b/packages/tab/index.tsx @@ -165,7 +165,7 @@ export default class Tab extends React.Component { } computeIndicatorClientRect = () => { - if (!this.tabIndicatorRef.current) return; + if (!this.tabIndicatorRef.current) return {} as ClientRect; return this.tabIndicatorRef.current.computeContentClientRect(); }; diff --git a/test/unit/tab-bar/index.test.tsx b/test/unit/tab-bar/index.test.tsx index ac379b06d..3f5e1c07f 100644 --- a/test/unit/tab-bar/index.test.tsx +++ b/test/unit/tab-bar/index.test.tsx @@ -4,10 +4,16 @@ import * as td from 'testdouble'; import {mount, shallow} from 'enzyme'; import TabBar from '../../../packages/tab-bar/index'; import Tab from '../../../packages/tab/index'; +import {MDCTabBarAdapter} from '@material/tab-bar/adapter'; import {coerceForTesting} from '../helpers/types'; suite('TabBar'); +const getAdapter = (instance: TabBar): MDCTabBarAdapter => { + // @ts-ignore adapter_ is a private property, we need to override this for testing + return instance.foundation.adapter_; +}; + test('classNames adds classes', () => { const wrapper = shallow(); assert.isTrue(wrapper.hasClass('test-class-name')); @@ -22,7 +28,7 @@ test('has a foundation after mount', () => { test('#componentWillUnmount destroys foundation', () => { const wrapper = shallow(); const foundation = wrapper.instance().foundation; - foundation.destroy = td.func(); + foundation.destroy = td.func<() => void>(); wrapper.unmount(); td.verify(foundation.destroy(), {times: 1}); }); @@ -36,12 +42,13 @@ test('initially sets state.previousActiveIndex to props.activeIndex', () => { test('key down event calls foundation.handleKeyDown', () => { const wrapper = shallow(); const foundation = wrapper.instance().foundation; - foundation.handleKeyDown = td.func(); - const evt = { + foundation.handleKeyDown = td.func<(evt: KeyboardEvent) => void>(); + const evt = coerceForTesting({ persist: () => {}, - }; + nativeEvent: {}, + }); wrapper.simulate('keyDown', evt); - td.verify(foundation.handleKeyDown(evt), {times: 1}); + td.verify(foundation.handleKeyDown(evt.nativeEvent), {times: 1}); }); test('key down event calls props.onKeyDown', () => { @@ -49,6 +56,7 @@ test('key down event calls props.onKeyDown', () => { const wrapper = shallow(); const evt = coerceForTesting>({ persist: () => {}, + nativeEvent: {}, }); wrapper.simulate('keyDown', evt); td.verify(onKeyDown(evt), {times: 1}); @@ -116,12 +124,12 @@ test('#adapter.getOffsetWidth returns tab bar element offsetWidth', () => { test('#adapter.isRTL returns true if props.isRtl is true', () => { const wrapper = shallow(); - assert.isTrue(wrapper.instance().foundation.adapter_.isRTL()); + assert.isTrue(getAdapter(wrapper.instance()).isRTL()); }); test('#adapter.isRTL returns false is props.isRtl is false', () => { const wrapper = shallow(); - assert.isFalse(wrapper.instance().foundation.adapter_.isRTL()); + assert.isFalse(getAdapter(wrapper.instance()).isRTL()); }); test('#adapter.setActiveTab calls props.handleActiveIndexUpdate', () => { @@ -183,11 +191,19 @@ test('#adapter.getTabDimensionsAtIndex calls computeDimensions on tab at index', }); test('#adapter.getIndexOfTab returns index of given tab', () => { - const tab0 = coerceForTesting({}); - const tab1 = coerceForTesting({}); + const tab0 = coerceForTesting({ + props: { + id: 'tab0', + }, + }); + const tab1 = coerceForTesting({ + props: { + id: 'tab1', + }, + }); const wrapper = shallow(); wrapper.instance().tabList = [tab0, tab1]; - assert.equal(wrapper.instance().adapter.getIndexOfTab(tab1), 1); + assert.equal(wrapper.instance().adapter.getIndexOfTabById('tab1'), 1); }); test('#adapter.getTabListLength returns length of tab list', () => { @@ -204,7 +220,7 @@ test('props.activeIndex updates to different value when not initially set calls const tab0 = coerceForTesting({}); const tab1 = coerceForTesting({deactivate: td.func()}); const wrapper = shallow(); - wrapper.instance().foundation.activateTab = td.func(); + wrapper.instance().foundation.activateTab = td.func<(index: number) => void>(); wrapper.instance().tabList = [tab0, tab1]; wrapper.setProps({activeIndex: 1}); td.verify(wrapper.instance().foundation.activateTab(1), {times: 1}); @@ -214,7 +230,7 @@ test('props.indexInView updates to different value when not initially set calls const tab0 = coerceForTesting({}); const tab1 = coerceForTesting({deactivate: td.func()}); const wrapper = shallow(); - wrapper.instance().foundation.scrollIntoView = td.func(); + wrapper.instance().foundation.scrollIntoView = td.func<(index: number) => void>(); wrapper.instance().tabList = [tab0, tab1]; wrapper.setProps({indexInView: 1}); td.verify(wrapper.instance().foundation.scrollIntoView(1), {times: 1}); @@ -224,7 +240,7 @@ test('props.activeIndex updates to different value with a set value calls founda const tab0 = coerceForTesting({}); const tab1 = coerceForTesting({deactivate: td.func()}); const wrapper = shallow(); - wrapper.instance().foundation.activateTab = td.func(); + wrapper.instance().foundation.activateTab = td.func<(index: number) => void>(); wrapper.instance().tabList = [tab0, tab1]; wrapper.setProps({activeIndex: 0}); td.verify(wrapper.instance().foundation.activateTab(0), {times: 1}); @@ -234,7 +250,7 @@ test('props.indexInView updates to different value with a set value calls founda const tab0 = coerceForTesting({}); const tab1 = coerceForTesting({deactivate: td.func()}); const wrapper = shallow(); - wrapper.instance().foundation.scrollIntoView = td.func(); + wrapper.instance().foundation.scrollIntoView = td.func<(index: number) => void>(); wrapper.instance().tabList = [tab0, tab1]; wrapper.setProps({indexInView: 0}); td.verify(wrapper.instance().foundation.scrollIntoView(0), {times: 1});