diff --git a/package-lock.json b/package-lock.json index cae11489b..10e7c1100 100644 --- a/package-lock.json +++ b/package-lock.json @@ -391,34 +391,66 @@ } }, "@material/checkbox": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-0.41.0.tgz", - "integrity": "sha512-Zz6e5WRpziO7Z+4rbEs8GHNNBf1UuttniLp6/RvwPSQRaD8G04sdg4HcP/aDCY1KGMwivkuDPc2Bsgs6j+rD7Q==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-1.0.1.tgz", + "integrity": "sha512-/FMQEM5aT3S63Rp2gWgq0/WnMl9tNpSBs222r/3YT9Aqesbdp56mcDG9rk9rq2Ph74sWxlI5+9MVwL4CZ3nCyg==", "dev": true, "requires": { - "@material/animation": "^0.41.0", - "@material/base": "^0.41.0", - "@material/ripple": "^0.41.0", - "@material/rtl": "^0.40.1", - "@material/selection-control": "^0.41.0", - "@material/theme": "^0.41.0" + "@material/animation": "^1.0.0", + "@material/base": "^1.0.0", + "@material/dom": "^1.0.1", + "@material/feature-targeting": "^0.44.1", + "@material/ripple": "^1.0.1", + "@material/rtl": "^0.42.0", + "@material/theme": "^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==", - "dev": true + "@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", + "integrity": "sha512-7gb9Tk8YBn2fLEa5fJfvDexG0QxvRGDb8c6uZEhvK4bTd2ZHCfHg9KrO+smC6Trbn5jC+FsBvdRZBbMjtS/E4g==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } }, "@material/ripple": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-0.41.0.tgz", - "integrity": "sha512-rxEUVWM4AByDlTCH0kkthZQmUuY6eeN0X6cOHBoioFN2vUDk0D0Nfzz/N9FF2AlAf8C2lDDLrTuqnJPVIn+NHA==", + "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": "^0.41.0", - "@material/base": "^0.41.0", - "@material/theme": "^0.41.0" + "@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", + "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" } } } diff --git a/package.json b/package.json index 72376643d..9e9516377 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@material/base": "^1.0.0", "@material/button": "^0.43.0", "@material/card": "^0.41.0", - "@material/checkbox": "^0.41.0", + "@material/checkbox": "^1.0.0", "@material/chips": "^1.0.0", "@material/dialog": "^0.43.0", "@material/dom": "^0.41.0", diff --git a/packages/checkbox/index.tsx b/packages/checkbox/index.tsx index 7304a29c5..203187a7e 100644 --- a/packages/checkbox/index.tsx +++ b/packages/checkbox/index.tsx @@ -22,8 +22,8 @@ import * as React from 'react'; import classnames from 'classnames'; -// @ts-ignore no mdc .d.ts file -import {MDCCheckboxFoundation, MDCCheckboxAdapter} from '@material/checkbox/dist/mdc.checkbox'; +import {MDCCheckboxFoundation} from '@material/checkbox/foundation'; +import {MDCCheckboxAdapter} from '@material/checkbox/adapter'; import * as Ripple from '@material/react-ripple'; import NativeControl from './NativeControl'; @@ -45,12 +45,13 @@ interface CheckboxState { checked?: boolean; indeterminate?: boolean; classList: Set; - 'aria-checked': boolean; + 'aria-checked': string; + disabled: boolean; }; export class Checkbox extends React.Component { inputElement: React.RefObject = React.createRef(); - foundation = MDCCheckboxFoundation; + foundation!: MDCCheckboxFoundation; constructor(props: CheckboxProps) { super(props); @@ -58,7 +59,8 @@ export class Checkbox extends React.Component { 'checked': props.checked, 'indeterminate': props.indeterminate, 'classList': new Set(), - 'aria-checked': false, + 'aria-checked': 'false', + 'disabled': props.disabled!, }; } @@ -74,7 +76,7 @@ export class Checkbox extends React.Component { componentDidMount() { this.foundation = new MDCCheckboxFoundation(this.adapter); this.foundation.init(); - this.foundation.setDisabled(this.props.disabled); + this.foundation.setDisabled(this.props.disabled!); // indeterminate property on checkboxes is not supported: // https://github.com/facebook/react/issues/1798#issuecomment-333414857 if (this.inputElement.current) { @@ -91,7 +93,10 @@ export class Checkbox extends React.Component { this.handleChange(checked!, indeterminate!); } if (disabled !== prevProps.disabled) { - this.foundation.setDisabled(disabled); + this.setState({ + disabled: disabled!, + }); + this.foundation.setDisabled(disabled!); } } @@ -146,10 +151,12 @@ export class Checkbox extends React.Component { // isAttachedToDOM will likely be removed // https://github.com/material-components/material-components-web/issues/3691 isAttachedToDOM: () => true, - isChecked: () => this.state.checked, - isIndeterminate: () => this.state.indeterminate, + isChecked: () => this.state.checked!, + isIndeterminate: () => this.state.indeterminate!, setNativeControlAttr: this.updateState, + setNativeControlDisabled: (disabled) => this.updateState('disabled', disabled), removeNativeControlAttr: this.removeState, + forceLayout: () => null, }; } @@ -187,7 +194,7 @@ export class Checkbox extends React.Component { id={nativeControlId} checked={this.state.checked} disabled={disabled} - aria-checked={this.state['aria-checked'] || this.state.checked} + aria-checked={(this.state['aria-checked'] || this.state.checked!.toString()) as ('true' | 'false')} name={name} onChange={this.onChange} rippleActivatorRef={this.inputElement} diff --git a/packages/checkbox/package.json b/packages/checkbox/package.json index 7fa29357e..66b4f7b61 100644 --- a/packages/checkbox/package.json +++ b/packages/checkbox/package.json @@ -16,7 +16,7 @@ "url": "https://github.com/material-components/material-components-web-react.git" }, "dependencies": { - "@material/checkbox": "^0.41.0", + "@material/checkbox": "^1.0.0", "@material/react-ripple": "^0.11.0", "classnames": "^2.2.6", "react": "^16.3.2" diff --git a/test/unit/checkbox/index.test.tsx b/test/unit/checkbox/index.test.tsx index c08c0d362..97e15beee 100644 --- a/test/unit/checkbox/index.test.tsx +++ b/test/unit/checkbox/index.test.tsx @@ -3,10 +3,16 @@ import {assert} from 'chai'; import {shallow} from 'enzyme'; import * as td from 'testdouble'; import {Checkbox} from '../../../packages/checkbox/index'; +import {MDCCheckboxAdapter} from '@material/checkbox/adapter'; import {coerceForTesting} from '../helpers/types'; suite('Checkbox'); +const getAdapter = (instance: Checkbox): MDCCheckboxAdapter => { + // @ts-ignore adapter_ is a protected property, we need to override it + return instance.foundation.adapter_; +}; + test('creates foundation', () => { const wrapper = shallow(); assert.exists(wrapper.instance().foundation); @@ -48,21 +54,21 @@ test('native control props.checked is true when props.checked is true', () => { test('#foundation.handleChange gets called when prop.checked updates', () => { const wrapper = shallow(); - wrapper.instance().foundation.handleChange = td.func(); + wrapper.instance().foundation.handleChange = td.func<() => null>(); wrapper.setProps({checked: true}); td.verify(wrapper.instance().foundation.handleChange(), {times: 1}); }); test('#foundation.handleChange gets called when prop.indeterminate updates', () => { const wrapper = shallow(); - wrapper.instance().foundation.handleChange = td.func(); + wrapper.instance().foundation.handleChange = td.func<() => null>(); wrapper.setProps({indeterminate: true}); td.verify(wrapper.instance().foundation.handleChange(), {times: 1}); }); test('#foundation.setDisabled gets called when prop.disabled updates', () => { const wrapper = shallow(); - wrapper.instance().foundation.setDisabled = td.func(); + wrapper.instance().foundation.setDisabled = td.func<(disabled: boolean) => null>(); wrapper.setProps({disabled: true}); td.verify(wrapper.instance().foundation.setDisabled(true), {times: 1}); }); @@ -70,62 +76,58 @@ test('#foundation.setDisabled gets called when prop.disabled updates', () => { 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}); }); test('#adapter.addClass adds class to state.classList', () => { const wrapper = shallow(); - wrapper.instance().foundation.adapter_.addClass('test-class-name'); + getAdapter(wrapper.instance()).addClass('test-class-name'); assert.isTrue(wrapper.state().classList.has('test-class-name')); }); test('#adapter.removeClass removes class from state.classList', () => { const wrapper = shallow(); wrapper.setState({classList: new Set(['test-class-name'])}); - wrapper.instance().foundation.adapter_.removeClass('test-class-name'); + getAdapter(wrapper.instance()).removeClass('test-class-name'); assert.isFalse(wrapper.state().classList.has('test-class-name')); }); test('#adapter.isChecked returns state.checked if true', () => { const wrapper = shallow(); wrapper.setState({checked: true}); - assert.isTrue(wrapper.instance().foundation.adapter_.isChecked()); + assert.isTrue(getAdapter(wrapper.instance()).isChecked()); }); test('#adapter.isChecked returns state.checked if false', () => { const wrapper = shallow(); wrapper.setState({checked: false}); - assert.isFalse(wrapper.instance().foundation.adapter_.isChecked()); + assert.isFalse(getAdapter(wrapper.instance()).isChecked()); }); test('#adapter.isIndeterminate returns state.indeterminate if true', () => { const wrapper = shallow(); wrapper.setState({indeterminate: true}); - assert.isTrue(wrapper.instance().foundation.adapter_.isIndeterminate()); + assert.isTrue(getAdapter(wrapper.instance()).isIndeterminate()); }); test('#adapter.isIndeterminate returns state.indeterminate if false', () => { const wrapper = shallow(); wrapper.setState({indeterminate: false}); - assert.isFalse(wrapper.instance().foundation.adapter_.isIndeterminate()); + assert.isFalse(getAdapter(wrapper.instance()).isIndeterminate()); }); test('#adapter.setNativeControlAttr sets aria-checked state', () => { const wrapper = shallow(); - wrapper - .instance() - .foundation.adapter_.setNativeControlAttr('aria-checked', true); - assert.isTrue(wrapper.state()['aria-checked']); + getAdapter(wrapper.instance()).setNativeControlAttr('aria-checked', 'true'); + assert.equal(wrapper.state()['aria-checked'], 'true'); }); test('#adapter.removeNativeControlAttr sets aria-checked state as false', () => { const wrapper = shallow(); - wrapper.setState({'aria-checked': true}); - wrapper - .instance() - .foundation.adapter_.removeNativeControlAttr('aria-checked'); + wrapper.setState({'aria-checked': 'true'}); + getAdapter(wrapper.instance()).removeNativeControlAttr('aria-checked'); assert.isFalse(wrapper.state()['aria-checked']); }); @@ -148,7 +150,7 @@ test('calls foundation.handleChange in native control props.onChange', () => { indeterminate: false, }, }; - wrapper.instance().foundation.handleChange = td.func(); + wrapper.instance().foundation.handleChange = td.func<() => void>(); nativeControl.simulate('change', mockEvt); td.verify(wrapper.instance().foundation.handleChange(), {times: 1}); });