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..9a0666e64 100644 --- a/packages/checkbox/index.tsx +++ b/packages/checkbox/index.tsx @@ -22,8 +22,9 @@ 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 {cssClasses} from '@material/checkbox/constants'; import * as Ripple from '@material/react-ripple'; import NativeControl from './NativeControl'; @@ -45,12 +46,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 +60,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 +77,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 +94,7 @@ export class Checkbox extends React.Component { this.handleChange(checked!, indeterminate!); } if (disabled !== prevProps.disabled) { - this.foundation.setDisabled(disabled); + this.foundation.setDisabled(disabled!); } } @@ -118,7 +121,9 @@ export class Checkbox extends React.Component { get classes(): string { const {classList} = this.state; const {className} = this.props; - return classnames('mdc-checkbox', Array.from(classList), className); + return classnames( + 'mdc-checkbox', Array.from(classList), + this.state.disabled ? cssClasses.DISABLED : null, className); } updateState = (key: keyof CheckboxState, value: string | boolean) => { @@ -146,10 +151,14 @@ 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, }; } @@ -169,8 +178,8 @@ export class Checkbox extends React.Component { initRipple, onChange, unbounded, - /* eslint-enable no-unused-vars */ disabled, + /* eslint-enable no-unused-vars */ nativeControlId, name, ...otherProps @@ -186,8 +195,8 @@ export class Checkbox extends React.Component { { + // @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); @@ -34,12 +40,29 @@ test('has disabled class when props.disabled is true', () => { ); }); +test('has disabled class when foundation calls setDisabled is true', () => { + const wrapper = shallow(); + getAdapter(wrapper.instance()).setNativeControlDisabled(true); + wrapper.update(); + assert.isTrue( + wrapper.find('.mdc-checkbox').hasClass('mdc-checkbox--disabled') + ); +}); + test('native control props.disabled is true when props.disabled is true', () => { const wrapper = shallow(); const nativeControl = wrapper.childAt(0); assert.isTrue(nativeControl.props().disabled); }); +test('native control props.disabled when foundation calls setDisabled is true', () => { + const wrapper = shallow(); + getAdapter(wrapper.instance()).setNativeControlDisabled(true); + wrapper.update(); + const nativeControl = wrapper.childAt(0); + assert.isTrue(nativeControl.props().disabled); +}); + test('native control props.checked is true when props.checked is true', () => { const wrapper = shallow(); const nativeControl = wrapper.childAt(0); @@ -48,21 +71,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 +93,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 +167,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}); });