From c08b2a76974db4cb9dbf7a71a23f36c2571bd79a Mon Sep 17 00:00:00 2001 From: Christopher Boyd Date: Fri, 10 May 2019 12:38:42 -0400 Subject: [PATCH 01/43] fix(chips): ClientRect/DOMRect is not IE11 compatible (#855) --- packages/chips/Chip.tsx | 30 ++++++++++++++++++++++++++++-- packages/tab-indicator/index.tsx | 15 ++++++++++++++- packages/tab-scroller/index.tsx | 32 ++++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/packages/chips/Chip.tsx b/packages/chips/Chip.tsx index 1d6bf4378..e1efffb7f 100644 --- a/packages/chips/Chip.tsx +++ b/packages/chips/Chip.tsx @@ -134,12 +134,38 @@ export class Chip extends React.Component { .getPropertyValue(propertyName); }, getRootBoundingClientRect: () => { - if (!this.chipElement) return new ClientRect(); + if (!this.chipElement) { + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } return this.chipElement.getBoundingClientRect(); }, getCheckmarkBoundingClientRect: () => { const {chipCheckmark} = this.props; - if (!chipCheckmark) return new ClientRect(); + if (!(chipCheckmark && chipCheckmark.props && chipCheckmark.props.getBoundingClientRect)) { + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } return chipCheckmark.props.getBoundingClientRect(); }, setStyleProperty: (propertyName: keyof React.CSSProperties, value: string | null) => { diff --git a/packages/tab-indicator/index.tsx b/packages/tab-indicator/index.tsx index 6fe0b2031..164e8efbd 100644 --- a/packages/tab-indicator/index.tsx +++ b/packages/tab-indicator/index.tsx @@ -132,7 +132,20 @@ export default class TabIndicator extends React.Component computeContentClientRect = () => { const contentElement = this.getNativeContentElement(); - if (!(contentElement && contentElement.getBoundingClientRect)) return new ClientRect(); + if (!(contentElement && contentElement.getBoundingClientRect)) { + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } return contentElement.getBoundingClientRect(); }; diff --git a/packages/tab-scroller/index.tsx b/packages/tab-scroller/index.tsx index c623bc15b..603748878 100644 --- a/packages/tab-scroller/index.tsx +++ b/packages/tab-scroller/index.tsx @@ -151,19 +151,35 @@ export default class TabScroller extends React.Component< getScrollContentOffsetWidth: this.getScrollContentWidth, getScrollAreaOffsetWidth: () => this.areaElement.current ? this.areaElement.current.offsetWidth : 0, - computeScrollAreaClientRect: () => - this.areaElement.current ? - this.areaElement.current.getBoundingClientRect() : - new ClientRect(), - computeScrollContentClientRect: () => - this.contentElement.current ? - this.contentElement.current.getBoundingClientRect() : - new ClientRect(), + computeScrollAreaClientRect: () => { + return this.getBoundingClientRectOf(this.contentElement.current); + }, + computeScrollContentClientRect: () => { + return this.getBoundingClientRectOf(this.contentElement.current); + }, computeHorizontalScrollbarHeight: () => computeHorizontalScrollbarHeight(document), }; } + getBoundingClientRectOf = (element: HTMLElement | null) => { + if (!element) { + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } + return element.getBoundingClientRect(); + } + getScrollPosition = () => { return this.foundation.getScrollPosition(); }; From 1d74bdce0bac7ce0a8da42433965eaa4cf82a2be Mon Sep 17 00:00:00 2001 From: TroyTae Date: Sun, 12 May 2019 16:38:41 +0900 Subject: [PATCH 02/43] feat(text-field): create character-counter --- .../text-field/character-counter/index.scss | 23 ++++ .../text-field/character-counter/index.tsx | 108 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 packages/text-field/character-counter/index.scss create mode 100644 packages/text-field/character-counter/index.tsx diff --git a/packages/text-field/character-counter/index.scss b/packages/text-field/character-counter/index.scss new file mode 100644 index 000000000..8b3766187 --- /dev/null +++ b/packages/text-field/character-counter/index.scss @@ -0,0 +1,23 @@ +// The MIT License +// +// Copyright (c) 2019 Google, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@import "@material/textfield/character-counter/mdc-text-field-character-counter"; diff --git a/packages/text-field/character-counter/index.tsx b/packages/text-field/character-counter/index.tsx new file mode 100644 index 000000000..b7c38fb7a --- /dev/null +++ b/packages/text-field/character-counter/index.tsx @@ -0,0 +1,108 @@ +// The MIT License +// +// Copyright (c) 2019 Google, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +import React from 'react'; +import classnames from 'classnames'; +import {MDCTextFieldCharacterCounterAdapter} from '@material/textfield/character-counter/adapter'; +import {MDCTextFieldCharacterCounterFoundation} from '@material/textfield/character-counter/foundation'; + +export interface CharacterCounterProps extends React.HTMLProps { + count?: number; + maxLength?: number; + template?: string; +} + +interface CharacterCounterState { + count: number; + maxLength: number; +} + +export default class CharacterCounter extends React.Component { + foundation = new MDCTextFieldCharacterCounterFoundation(this.adapter); + + constructor(props: CharacterCounterProps) { + super(props); + this.state = { + count: props.count || 0, + maxLength: props.maxLength || 0, + }; + } + + componentWillUnmount() { + this.foundation.destroy(); + } + + get adapter(): MDCTextFieldCharacterCounterAdapter { + return { + // Please manage content through JSX + setContent: () => undefined, + }; + } + + renderTemplate(template: string, data: { + count: number, + maxLength: number, + }) { + const { + maxLength, + } = data; + + return template + .replace('${maxLength}', maxLength.toString()) + .replace('${count}', '0') + ; + } + + get classes() { + return classnames('mdc-text-field-character-counter', this.props.className); + } + + get otherProps() { + const { + /* eslint-disable no-unused-vars */ + className, + maxLength, + template, + /* eslint-disable no-unused-vars */ + ...otherProps + } = this.props; + + return otherProps; + } + + render() { + const { + count, + maxLength, + } = this.state; + const { + template, + } = this.props; + + return
+ {this.renderTemplate(template ? template : '${count} / ${maxLength}', { + count: count, + maxLength: maxLength!, + })} +
; + } +} + From cf24b80907bc4d55d28bb117fdc7a674bc97e828 Mon Sep 17 00:00:00 2001 From: TroyTae Date: Sun, 12 May 2019 16:46:18 +0900 Subject: [PATCH 03/43] feat(text-field): delete state --- .../text-field/character-counter/index.tsx | 36 ++++--------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/packages/text-field/character-counter/index.tsx b/packages/text-field/character-counter/index.tsx index b7c38fb7a..b595c803b 100644 --- a/packages/text-field/character-counter/index.tsx +++ b/packages/text-field/character-counter/index.tsx @@ -30,22 +30,9 @@ export interface CharacterCounterProps extends React.HTMLProps { template?: string; } -interface CharacterCounterState { - count: number; - maxLength: number; -} - -export default class CharacterCounter extends React.Component { +export default class CharacterCounter extends React.Component { foundation = new MDCTextFieldCharacterCounterFoundation(this.adapter); - constructor(props: CharacterCounterProps) { - super(props); - this.state = { - count: props.count || 0, - maxLength: props.maxLength || 0, - }; - } - componentWillUnmount() { this.foundation.destroy(); } @@ -57,17 +44,15 @@ export default class CharacterCounter extends React.Component - {this.renderTemplate(template ? template : '${count} / ${maxLength}', { - count: count, - maxLength: maxLength!, - })} + {this.renderTemplate(template ? template : '${count} / ${maxLength}')} ; } } From 536e233c3ae771f103c2a60793b0109e02d5a1fb Mon Sep 17 00:00:00 2001 From: TroyTae Date: Sun, 12 May 2019 16:49:48 +0900 Subject: [PATCH 04/43] feat(text-field): use constant --- packages/text-field/character-counter/index.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/text-field/character-counter/index.tsx b/packages/text-field/character-counter/index.tsx index b595c803b..9ba28b7be 100644 --- a/packages/text-field/character-counter/index.tsx +++ b/packages/text-field/character-counter/index.tsx @@ -24,6 +24,13 @@ import classnames from 'classnames'; import {MDCTextFieldCharacterCounterAdapter} from '@material/textfield/character-counter/adapter'; import {MDCTextFieldCharacterCounterFoundation} from '@material/textfield/character-counter/foundation'; +const cssClasses = MDCTextFieldCharacterCounterFoundation.cssClasses; + +const TEMPLATE = { + COUNT: '${count}', + MAX_LENGTH: '${maxLength}', +}; + export interface CharacterCounterProps extends React.HTMLProps { count?: number; maxLength?: number; @@ -51,13 +58,13 @@ export default class CharacterCounter extends React.Component - {this.renderTemplate(template ? template : '${count} / ${maxLength}')} + {this.renderTemplate(template ? template : `${TEMPLATE.COUNT} / ${TEMPLATE.MAX_LENGTH}`)} ; } } From 706e30db289a706311c36e174d86b198dca157a7 Mon Sep 17 00:00:00 2001 From: TroyTae Date: Sun, 12 May 2019 17:02:09 +0900 Subject: [PATCH 05/43] feat(text-field): render character counter --- packages/text-field/index.tsx | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/text-field/index.tsx b/packages/text-field/index.tsx index 9f2bbd452..bc049d61f 100644 --- a/packages/text-field/index.tsx +++ b/packages/text-field/index.tsx @@ -31,8 +31,9 @@ import { } from '@material/textfield/adapter'; import {MDCTextFieldFoundation} from '@material/textfield/foundation'; import Input, {InputProps} from './Input'; -import Icon, {IconProps} from './icon/index'; -import HelperText, {HelperTextProps} from './helper-text/index'; +import Icon, {IconProps} from './icon'; +import HelperText, {HelperTextProps} from './helper-text'; +import CharacterCounter, {CharacterCounterProps} from './character-counter'; import FloatingLabel from '@material/react-floating-label'; import LineRipple from '@material/react-line-ripple'; import NotchedOutline from '@material/react-notched-outline'; @@ -48,7 +49,7 @@ export interface Props { floatingLabelClassName?: string; fullWidth?: boolean; helperText?: React.ReactElement; - characterCounter?: React.ReactElement; + characterCounter?: React.ReactElement; label?: React.ReactNode; leadingIcon?: React.ReactElement>; lineRippleClassName?: string; @@ -171,6 +172,7 @@ class TextField extends React.Componen floatingLabelClassName, fullWidth, helperText, + characterCounter, label, leadingIcon, lineRippleClassName, @@ -374,10 +376,13 @@ class TextField extends React.Componen ); } - renderHelperLine(helperText?: React.ReactElement, characterCounter?: React.ReactElement) { + renderHelperLine( + helperText?: React.ReactElement, + characterCounter?: React.ReactElement + ) { return
{helperText && this.renderHelperText(helperText)} - {characterCounter} + {characterCounter && this.renderCharacterCounter(characterCounter)}
; } @@ -399,7 +404,17 @@ class TextField extends React.Componen // Toggling disabled will trigger icon.foundation.setDisabled() return {icon}; } + + renderCharacterCounter(characterCounter: React.ReactElement) { + const input = this.inputComponent_!; + const props = Object.assign(characterCounter.props, { + key: 'text-field-character-counter', + count: input.getValue().length, + maxLength: input.getMaxLength(), + }); + return React.cloneElement(characterCounter, props); + } } -export {Icon, HelperText, Input, IconProps, HelperTextProps, InputProps}; +export {Icon, HelperText, CharacterCounter, Input, IconProps, HelperTextProps, CharacterCounterProps, InputProps}; export default TextField; From e2d0f4c4c36a99224025a17b8d5a994a33504139 Mon Sep 17 00:00:00 2001 From: TroyTae Date: Sun, 12 May 2019 18:32:21 +0900 Subject: [PATCH 06/43] feat(text-field): use inputProps in characterCounterProps --- .../text-field/character-counter/index.tsx | 1 + packages/text-field/index.tsx | 32 ++++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/text-field/character-counter/index.tsx b/packages/text-field/character-counter/index.tsx index 9ba28b7be..e9e5fdb4a 100644 --- a/packages/text-field/character-counter/index.tsx +++ b/packages/text-field/character-counter/index.tsx @@ -71,6 +71,7 @@ export default class CharacterCounter extends React.Component extends React.Componen }; } - inputProps(child: React.ReactElement>) { + get inputProps() { // ref does exist on React.ReactElement> // @ts-ignore - const {props} = child; + const {props} = React.Children.only(this.props.children); + return Object.assign({}, props, { foundation: this.state.foundation, handleFocusChange: (isFocused: boolean) => this.setState({isFocused}), @@ -288,6 +289,19 @@ class TextField extends React.Componen }); } + get characterCounterProps() { + const { + value, + maxLength, + } = this.inputProps; + console.warn(value, maxLength); + + return { + count: value ? value.length : 0, + maxLength: maxLength ? parseInt(maxLength) : 0, + }; + } + /** * render methods */ @@ -328,8 +342,7 @@ class TextField extends React.Componen renderInput() { const child: React.ReactElement> = React.Children.only(this.props.children); - const props = this.inputProps(child); - return React.cloneElement(child, props); + return React.cloneElement(child, this.inputProps); } renderLabel() { @@ -406,13 +419,10 @@ class TextField extends React.Componen } renderCharacterCounter(characterCounter: React.ReactElement) { - const input = this.inputComponent_!; - const props = Object.assign(characterCounter.props, { - key: 'text-field-character-counter', - count: input.getValue().length, - maxLength: input.getMaxLength(), - }); - return React.cloneElement(characterCounter, props); + return React.cloneElement(characterCounter, Object.assign( + this.characterCounterProps, + characterCounter.props, + )); } } From d2e47d002ab9f5ee59b96c516449b2290bec70b5 Mon Sep 17 00:00:00 2001 From: TroyTae Date: Sun, 12 May 2019 18:45:45 +0900 Subject: [PATCH 07/43] feat(text-field): textarea render case --- packages/text-field/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/text-field/index.tsx b/packages/text-field/index.tsx index c7a0990f4..14f0489bc 100644 --- a/packages/text-field/index.tsx +++ b/packages/text-field/index.tsx @@ -294,7 +294,6 @@ class TextField extends React.Componen value, maxLength, } = this.inputProps; - console.warn(value, maxLength); return { count: value ? value.length : 0, @@ -328,6 +327,7 @@ class TextField extends React.Componen key='text-field-container' > {leadingIcon ? this.renderIcon(leadingIcon, onLeadingIconSelect) : null} + {textarea && characterCounter && this.renderCharacterCounter(characterCounter)} {this.renderInput()} {this.notchedOutlineAdapter.hasOutline() ? this.renderNotchedOutline() : {this.labelAdapter.hasLabel() ? this.renderLabel() : null} @@ -395,7 +395,7 @@ class TextField extends React.Componen ) { return
{helperText && this.renderHelperText(helperText)} - {characterCounter && this.renderCharacterCounter(characterCounter)} + {characterCounter && !this.props.textarea && this.renderCharacterCounter(characterCounter)}
; } From 573311e288496996ba0a5b6d74d4bec9d3f6cb3f Mon Sep 17 00:00:00 2001 From: TroyTae Date: Sun, 12 May 2019 18:46:17 +0900 Subject: [PATCH 08/43] test(text-field): create unit test --- .../character-counter/index.test.tsx | 59 +++++++++++++++++++ test/unit/text-field/index.test.tsx | 40 ------------- 2 files changed, 59 insertions(+), 40 deletions(-) create mode 100644 test/unit/text-field/character-counter/index.test.tsx diff --git a/test/unit/text-field/character-counter/index.test.tsx b/test/unit/text-field/character-counter/index.test.tsx new file mode 100644 index 000000000..501d4f464 --- /dev/null +++ b/test/unit/text-field/character-counter/index.test.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import {shallow, mount} from 'enzyme'; +import {assert} from 'chai'; +import TextField, { + Input, + CharacterCounter, +} from '../../../../packages/text-field'; + + +suite('Text Field Charater Counter'); + +test('classNames adds classes', () => { + const wrapper = shallow(); + assert.isTrue(wrapper.hasClass('test-class-name')); + assert.isTrue(wrapper.hasClass('mdc-text-field-character-counter')); +}); + +test('default props test', () => { + const wrapper = shallow(); + assert.equal('0 / 0', wrapper.text()); +}); + +test('maxLength test', () => { + const maxLength = 100; + const wrapper = mount( + }> + + + ); + assert.equal(`0 / ${maxLength}`, wrapper.find('.mdc-text-field-character-counter').text()); +}); + + +test('maxLength test', () => { + const value = 'test value'; + const maxLength = 1000; + const wrapper = mount( + }> + + + ); + assert.equal(`${value.length} / ${maxLength}`, wrapper.find('.mdc-text-field-character-counter').text()); +}); + +test('dynamic count test', () => { + class TestComponent extends React.Component { + state = {value: ''}; + render() { + return }> + + ; + } + } + const wrapper = mount(); + const counter = wrapper.find('.mdc-text-field-character-counter'); + assert.equal('0 / 250', counter.text()); + wrapper.instance().setState({value: 'Test Value'}); + assert.equal('10 / 250', counter.text()); +}); diff --git a/test/unit/text-field/index.test.tsx b/test/unit/text-field/index.test.tsx index fd9770bda..690971821 100644 --- a/test/unit/text-field/index.test.tsx +++ b/test/unit/text-field/index.test.tsx @@ -7,7 +7,6 @@ import TextField, { Input, } from '../../../packages/text-field'; import {coerceForTesting} from '../helpers/types'; -import {InputProps} from '../../../packages/text-field/Input'; // eslint-disable-line no-unused-vars /* eslint-disable */ import FloatingLabel from '../../../packages/floating-label'; /* eslint-enable */ @@ -599,45 +598,6 @@ test('renders input after foundation is created', () => { assert.equal(wrapper.find(Input).length, 1); }); -test('#inputProps.handleFocusChange updates state.isFocused', () => { - const wrapper = mount>( - - - - ); - wrapper - .instance() - .inputProps(coerceForTesting>>({})) - .handleFocusChange(true); - assert.isTrue(wrapper.state().isFocused); -}); - -test('#inputProps.setDisabled updates state.disabled', () => { - const wrapper = mount>( - - - - ); - wrapper - .instance() - .inputProps(coerceForTesting>>({})) - .setDisabled(true); - assert.isTrue(wrapper.state().disabled); -}); - -test('#inputProps.setInputId updates state.disabled', () => { - const wrapper = mount>( - - - - ); - wrapper - .instance() - .inputProps(coerceForTesting>>({})) - .setInputId('my-id'); - assert.equal(wrapper.state().inputId, 'my-id'); -}); - test('passing a ref to the should return the instance of the Input', () => { let inputInstance; const inputRef = (input: any) => inputInstance = input; From 8c4c38351f1ef024490c3eaae64e5d9909e96302 Mon Sep 17 00:00:00 2001 From: Christopher Boyd Date: Fri, 10 May 2019 12:38:42 -0400 Subject: [PATCH 09/43] fix(chips): ClientRect/DOMRect is not IE11 compatible (#855) --- packages/chips/Chip.tsx | 30 ++++++++++++++++++++++++++++-- packages/tab-indicator/index.tsx | 15 ++++++++++++++- packages/tab-scroller/index.tsx | 32 ++++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/packages/chips/Chip.tsx b/packages/chips/Chip.tsx index 1d6bf4378..e1efffb7f 100644 --- a/packages/chips/Chip.tsx +++ b/packages/chips/Chip.tsx @@ -134,12 +134,38 @@ export class Chip extends React.Component { .getPropertyValue(propertyName); }, getRootBoundingClientRect: () => { - if (!this.chipElement) return new ClientRect(); + if (!this.chipElement) { + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } return this.chipElement.getBoundingClientRect(); }, getCheckmarkBoundingClientRect: () => { const {chipCheckmark} = this.props; - if (!chipCheckmark) return new ClientRect(); + if (!(chipCheckmark && chipCheckmark.props && chipCheckmark.props.getBoundingClientRect)) { + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } return chipCheckmark.props.getBoundingClientRect(); }, setStyleProperty: (propertyName: keyof React.CSSProperties, value: string | null) => { diff --git a/packages/tab-indicator/index.tsx b/packages/tab-indicator/index.tsx index 6fe0b2031..164e8efbd 100644 --- a/packages/tab-indicator/index.tsx +++ b/packages/tab-indicator/index.tsx @@ -132,7 +132,20 @@ export default class TabIndicator extends React.Component computeContentClientRect = () => { const contentElement = this.getNativeContentElement(); - if (!(contentElement && contentElement.getBoundingClientRect)) return new ClientRect(); + if (!(contentElement && contentElement.getBoundingClientRect)) { + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } return contentElement.getBoundingClientRect(); }; diff --git a/packages/tab-scroller/index.tsx b/packages/tab-scroller/index.tsx index c623bc15b..603748878 100644 --- a/packages/tab-scroller/index.tsx +++ b/packages/tab-scroller/index.tsx @@ -151,19 +151,35 @@ export default class TabScroller extends React.Component< getScrollContentOffsetWidth: this.getScrollContentWidth, getScrollAreaOffsetWidth: () => this.areaElement.current ? this.areaElement.current.offsetWidth : 0, - computeScrollAreaClientRect: () => - this.areaElement.current ? - this.areaElement.current.getBoundingClientRect() : - new ClientRect(), - computeScrollContentClientRect: () => - this.contentElement.current ? - this.contentElement.current.getBoundingClientRect() : - new ClientRect(), + computeScrollAreaClientRect: () => { + return this.getBoundingClientRectOf(this.contentElement.current); + }, + computeScrollContentClientRect: () => { + return this.getBoundingClientRectOf(this.contentElement.current); + }, computeHorizontalScrollbarHeight: () => computeHorizontalScrollbarHeight(document), }; } + getBoundingClientRectOf = (element: HTMLElement | null) => { + if (!element) { + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } + return element.getBoundingClientRect(); + } + getScrollPosition = () => { return this.foundation.getScrollPosition(); }; From 317c1b3d5b8d6f9fab1fbde7bd773c74d1421ab9 Mon Sep 17 00:00:00 2001 From: Lucas Cordeiro Date: Wed, 15 May 2019 19:42:38 -0300 Subject: [PATCH 10/43] chore: add prettier (#863) --- .eslintrc.json | 45 +- .prettierrc | 7 + package-lock.json | 442 +++++++++++++-- package.json | 15 +- packages/button/index.tsx | 86 ++- packages/card/ActionButtons.tsx | 16 +- packages/card/ActionIcons.tsx | 16 +- packages/card/Actions.tsx | 7 +- packages/card/Media.tsx | 29 +- packages/card/PrimaryContent.tsx | 18 +- packages/card/index.tsx | 9 +- packages/checkbox/NativeControl.tsx | 4 +- packages/checkbox/index.tsx | 40 +- packages/chips/Chip.tsx | 46 +- packages/chips/ChipSet.tsx | 90 +-- packages/dialog/DialogButton.tsx | 54 +- packages/dialog/DialogContent.tsx | 36 +- packages/dialog/DialogFooter.tsx | 35 +- packages/dialog/DialogTitle.tsx | 36 +- packages/dialog/index.tsx | 215 +++---- packages/drawer/AppContent.tsx | 11 +- packages/drawer/Content.tsx | 11 +- packages/drawer/Header.tsx | 11 +- packages/drawer/Subtitle.tsx | 7 +- packages/drawer/Title.tsx | 7 +- packages/drawer/index.tsx | 37 +- packages/fab/index.tsx | 33 +- packages/floating-label/index.tsx | 15 +- packages/icon-button/IconToggle.tsx | 4 +- packages/icon-button/index.tsx | 30 +- packages/layout-grid/Cell.tsx | 36 +- packages/layout-grid/Grid.tsx | 16 +- packages/layout-grid/Row.tsx | 12 +- packages/line-ripple/index.tsx | 20 +- packages/linear-progress/index.tsx | 15 +- packages/list/ListDivider.tsx | 15 +- packages/list/ListGroup.tsx | 15 +- packages/list/ListGroupSubheader.tsx | 13 +- packages/list/ListItem.tsx | 48 +- packages/list/ListItemGraphic.tsx | 6 +- packages/list/ListItemMeta.tsx | 12 +- packages/list/ListItemText.tsx | 4 +- packages/list/index.tsx | 122 ++-- packages/material-icon/index.tsx | 47 +- packages/menu-surface/index.tsx | 55 +- packages/menu/MenuList.tsx | 13 +- packages/menu/MenuListItem.tsx | 19 +- packages/menu/index.tsx | 42 +- packages/notched-outline/index.tsx | 46 +- packages/radio/NativeControl.tsx | 10 +- packages/radio/index.tsx | 23 +- packages/ripple/index.tsx | 525 +++++++++--------- packages/select/BaseSelect.tsx | 55 +- packages/select/EnhancedSelect.tsx | 49 +- packages/select/NativeSelect.tsx | 22 +- packages/select/Option.tsx | 30 +- packages/select/helper-text/index.tsx | 45 +- packages/select/icon/index.tsx | 28 +- packages/select/index.tsx | 91 +-- packages/snackbar/index.tsx | 49 +- packages/switch/NativeControl.tsx | 3 +- packages/switch/ThumbUnderlay.tsx | 11 +- packages/tab-bar/index.tsx | 50 +- packages/tab-indicator/index.tsx | 27 +- packages/tab-scroller/index.tsx | 37 +- packages/tab/TabRipple.tsx | 26 +- packages/tab/index.tsx | 41 +- packages/text-field/Input.tsx | 137 +++-- packages/text-field/helper-text/index.tsx | 37 +- packages/text-field/icon/index.tsx | 22 +- packages/text-field/index.tsx | 83 ++- packages/top-app-bar/FixedAdjust.tsx | 4 +- packages/top-app-bar/Icon.tsx | 35 +- packages/top-app-bar/Row.tsx | 29 +- packages/top-app-bar/Section.tsx | 43 +- packages/top-app-bar/Title.tsx | 29 +- packages/top-app-bar/index.tsx | 74 ++- packages/typography/index.tsx | 20 +- packages/typography/typography.tsx | 3 - packages/webpack.config.js | 104 ++-- packages/webpack.util.js | 10 +- test/screenshot/card/index.tsx | 11 +- test/screenshot/chips/index.tsx | 49 +- test/screenshot/dialog/alert.tsx | 19 +- test/screenshot/dialog/confirmation.tsx | 33 +- test/screenshot/dialog/scrollable.tsx | 81 +-- test/screenshot/dialog/simple.tsx | 35 +- test/screenshot/dialog/variants.ts | 6 +- .../drawer/DrawerAboveTopAppBar.tsx | 9 +- .../drawer/DrawerBelowTopAppBar.tsx | 16 +- test/screenshot/drawer/DrawerTest.tsx | 14 +- test/screenshot/drawer/List.js | 74 ++- test/screenshot/drawer/permanentToModal.tsx | 9 +- test/screenshot/fab/index.tsx | 9 +- test/screenshot/linear-progress/index.tsx | 2 +- test/screenshot/list/index.tsx | 88 +-- test/screenshot/menu-surface/index.tsx | 21 +- test/screenshot/menu/index.tsx | 26 +- test/screenshot/radio/index.tsx | 14 +- test/screenshot/ripple/index.tsx | 8 +- test/screenshot/screenshot.tsx | 21 +- test/screenshot/select/index.tsx | 100 ++-- test/screenshot/select/variants.tsx | 5 +- test/screenshot/snackbar/index.tsx | 5 +- test/screenshot/tab-bar/index.tsx | 18 +- test/screenshot/tab-indicator/index.tsx | 16 +- test/screenshot/tab-scroller/index.tsx | 4 +- test/screenshot/tab/index.tsx | 34 +- test/screenshot/text-field/TestTextField.tsx | 26 +- .../text-field/TextFieldPermutations.tsx | 93 ++-- test/screenshot/text-field/attributesMap.tsx | 9 +- test/screenshot/text-field/refTest.tsx | 14 +- test/screenshot/text-field/variants.tsx | 8 +- test/screenshot/top-app-bar/dense.tsx | 5 +- test/screenshot/top-app-bar/fixed.tsx | 4 +- test/screenshot/top-app-bar/mainContent.tsx | 8 +- test/screenshot/top-app-bar/prominent.tsx | 5 +- .../screenshot/top-app-bar/prominentDense.tsx | 4 +- .../top-app-bar/prominentToShortCollapsed.tsx | 25 +- test/screenshot/top-app-bar/short.tsx | 6 +- .../screenshot/top-app-bar/shortCollapsed.tsx | 6 +- test/screenshot/top-app-bar/standard.tsx | 2 +- .../top-app-bar/standardNoActionItems.tsx | 2 +- .../standardWithNavigationIconElement.tsx | 10 +- test/screenshot/top-app-bar/twoRows.tsx | 7 +- test/screenshot/top-app-bar/variants.tsx | 1 - test/screenshot/webpack.config.js | 58 +- test/unit/button/index.test.tsx | 4 +- test/unit/card/ActionButton.test.tsx | 8 +- test/unit/card/ActionIcons.test.tsx | 8 +- test/unit/checkbox/index.test.tsx | 8 +- test/unit/chips/Chip.test.tsx | 188 +++++-- test/unit/chips/ChipSet.test.tsx | 170 ++++-- test/unit/dialog/DialogButton.test.tsx | 8 +- test/unit/dialog/DialogContent.test.tsx | 6 +- test/unit/dialog/DialogFooter.test.tsx | 7 +- test/unit/dialog/DialogTitle.test.tsx | 6 +- test/unit/dialog/index.test.tsx | 347 ++++++++---- test/unit/drawer/index.test.tsx | 59 +- test/unit/fab/index.test.tsx | 5 +- test/unit/floating-label/index.test.tsx | 36 +- test/unit/helpers/raf.tsx | 22 +- test/unit/icon-button/index.test.tsx | 12 +- test/unit/line-ripple/index.test.tsx | 21 +- test/unit/linear-progress/index.test.tsx | 24 +- test/unit/list/ListItem.test.tsx | 136 ++++- test/unit/list/index.test.tsx | 411 +++++++++----- test/unit/menu-surface/index.test.tsx | 119 ++-- test/unit/menu/MenuList.test.tsx | 23 +- test/unit/menu/index.test.tsx | 201 ++++--- test/unit/notched-outline/index.test.tsx | 33 +- test/unit/radio/index.test.tsx | 49 +- test/unit/ripple/index.test.tsx | 165 ++++-- test/unit/select/BaseSelect.test.tsx | 65 ++- test/unit/select/EnhancedSelect.test.tsx | 100 +++- test/unit/select/Option.test.tsx | 17 +- test/unit/select/helper-text/index.test.tsx | 18 +- test/unit/select/icon/index.test.tsx | 12 +- test/unit/select/index.test.tsx | 223 ++++++-- test/unit/snackbar/index.test.tsx | 76 ++- test/unit/switch/ThumbUnderlay.test.tsx | 5 +- test/unit/switch/index.test.tsx | 12 +- test/unit/tab-bar/index.test.tsx | 28 +- test/unit/tab-indicator/index.test.tsx | 92 ++- test/unit/tab-scroller/index.test.tsx | 124 +++-- test/unit/tab/index.test.tsx | 83 ++- test/unit/text-field/Input.test.tsx | 32 +- .../text-field/helper-text/index.test.tsx | 59 +- test/unit/text-field/icon/index.test.tsx | 36 +- test/unit/text-field/index.test.tsx | 128 +++-- test/unit/top-app-bar/Icon.test.tsx | 60 +- test/unit/top-app-bar/Row.test.tsx | 5 +- test/unit/top-app-bar/Section.test.tsx | 5 +- test/unit/top-app-bar/Title.test.tsx | 5 +- test/unit/top-app-bar/index.test.tsx | 104 +++- 175 files changed, 5166 insertions(+), 2939 deletions(-) create mode 100644 .prettierrc diff --git a/.eslintrc.json b/.eslintrc.json index 83382d906..b3fdf92c3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,39 +1,36 @@ { "extends": [ "google", - "plugin:react/recommended" + "plugin:@typescript-eslint/eslint-recommended", + "plugin:react/recommended", + "prettier/@typescript-eslint", + "prettier", + "prettier/react" ], "plugins": [ - "react" + "@typescript-eslint", + "react", + "prettier" ], "rules": { + "prettier/prettier": "warn", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "warn", "strict": 0, "require-jsdoc": "off", "valid-jsdoc": "off", "switch-colon-spacing": 0, - "max-len": ["error", 120], - "indent": ["error", 2, {"SwitchCase":1}], + "max-len": "off", + "indent": "off", "no-invalid-this": "off", - "object-curly-spacing": ["error", "never"] + "object-curly-spacing": "off", + "react/prop-types": ["error", { "skipUndeclared": true }] }, - "overrides": [ - { - "parser": "typescript-eslint-parser", - "parserOptions": { - "jsx": true - }, - "plugins": [ - "typescript" - ], - "rules": { - "typescript/no-unused-vars": "warn", - "quotes": ["error", "single"], - "jsx-quotes": ["error", "prefer-single"] - }, - "files": ["**/*.+(tsx|ts)"] - }, { - "parser": "babel-eslint", - "files": ["**/*.jsx", "**/*.js"] + "parser": "@typescript-eslint/parser", + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "jsx": true } - ] + } } diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..7fd09b4eb --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "singleQuote": true, + "trailingComma": "es5", + "bracketSpacing": false, + "jsxSingleQuote": true, + "arrowParens": "always" +} diff --git a/package-lock.json b/package-lock.json index 7522aa735..32e2f99dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2013,6 +2013,12 @@ "integrity": "sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA==", "dev": true }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, "@types/prop-types": { "version": "15.5.6", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.6.tgz", @@ -2101,6 +2107,69 @@ "integrity": "sha512-5Th3OsZ4gTRdr9Mho83BQ23cex4sRhOR4XTG+m+cJc0FhtUBK9Vn62hBJ+pnQYnSxoPOsKoAPOx6FcphxBC8ng==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.9.0.tgz", + "integrity": "sha512-FOgfBorxjlBGpDIw+0LaZIXRX6GEEUfzj8LXwaQIUCp+gDOvkI+1WgugJ7SmWiISqK9Vj5r8S7NDKO/LB+6X9A==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "1.9.0", + "@typescript-eslint/parser": "1.9.0", + "eslint-utils": "^1.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^2.0.1", + "requireindex": "^1.2.0", + "tsutils": "^3.7.0" + }, + "dependencies": { + "requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true + }, + "tsutils": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", + "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.9.0.tgz", + "integrity": "sha512-1s2dY9XxBwtS9IlSnRIlzqILPyeMly5tz1bfAmQ84Ul687xBBve5YsH5A5EKeIcGurYYqY2w6RkHETXIwnwV0A==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "1.9.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.9.0.tgz", + "integrity": "sha512-CWgC1XrQ34H/+LwAU7vY5xteZDkNqeAkeidEpJnJgkKu0yqQ3ZhQ7S+dI6MX4vmmM1TKRbOrKuXc6W0fIHhdbA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "1.9.0", + "@typescript-eslint/typescript-estree": "1.9.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.9.0.tgz", + "integrity": "sha512-7Eg0TEQpCkTsEwsl1lIzd6i7L3pJLQFWesV08dS87bNz0NeSjbL78gNAP1xCKaCejkds4PhpLnZkaAjx9SU8OA==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -4460,6 +4529,23 @@ } } }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + } + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -7959,6 +8045,32 @@ "integrity": "sha512-5A83D+lH0PA81QMESKbLJd/a3ic8tPZtwUmqNrxMRo54nfFaUvtt89q/+icQ+fd66c2xQHn0KyFkzJDoAUfpZA==", "dev": true }, + "eslint-config-prettier": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.2.0.tgz", + "integrity": "sha512-y0uWc/FRfrHhpPZCYflWC8aE0KRJRY04rdZVfl8cL3sEZmOYyaBdhdlQPjKZBnuRMyLVK+JUZr7HaZFClQiH4w==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, + "eslint-plugin-prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz", + "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz", @@ -8365,6 +8477,12 @@ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -10199,14 +10317,226 @@ } }, "husky": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-0.14.3.tgz", - "integrity": "sha512-e21wivqHpstpoiWA/Yi8eFti8E+sQDSS53cpJsPptPs295QTOQR0ZwnHo2TXy1XOpZFD9rPOd3NpmqTK6uMLJA==", - "dev": true, - "requires": { - "is-ci": "^1.0.10", - "normalize-path": "^1.0.0", - "strip-indent": "^2.0.0" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-2.3.0.tgz", + "integrity": "sha512-A/ZQSEILoq+mQM3yC3RIBSaw1bYXdkKnyyKVSUiJl+iBjVZc5LQEXdGY1ZjrDxC4IzfRPiJ0IqzEQGCN5TQa/A==", + "dev": true, + "requires": { + "cosmiconfig": "^5.2.0", + "execa": "^1.0.0", + "find-up": "^3.0.0", + "get-stdin": "^7.0.0", + "is-ci": "^2.0.0", + "pkg-dir": "^4.1.0", + "please-upgrade-node": "^3.1.1", + "read-pkg": "^5.1.1", + "run-node": "^1.0.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pkg-dir": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.1.0.tgz", + "integrity": "sha512-55k9QN4saZ8q518lE6EFgYiu95u3BWkSajCifhdQjvLvmr8IpnRbhI+UGpWJQfa0KzDguHeeWT1ccO1PmkOi3A==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "read-pkg": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.1.1.tgz", + "integrity": "sha512-dFcTLQi6BZ+aFUaICg7er+/usEoqFdQxiEBsEMNGoipenihtxxtdrQuBXvyANCEI8VuUIVYFgeHGx9sLLvim4w==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^4.0.0", + "type-fest": "^0.4.1" + } + }, + "resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } } }, "iconv-lite": { @@ -10305,6 +10635,33 @@ "minimatch": "^3.0.4" } }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "dependencies": { + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -13699,12 +14056,6 @@ "validate-npm-package-license": "^3.0.1" } }, - "normalize-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz", - "integrity": "sha1-MtDkcvkf80VwHBWoMRAY07CpA3k=", - "dev": true - }, "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", @@ -14647,6 +14998,15 @@ "find-up": "^2.1.0" } }, + "please-upgrade-node": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz", + "integrity": "sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, "plugin-error": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", @@ -15436,6 +15796,21 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, + "prettier": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.1.tgz", + "integrity": "sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -16532,6 +16907,12 @@ "is-promise": "^2.1.0" } }, + "run-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", + "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", + "dev": true + }, "rx-lite": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", @@ -16723,6 +17104,12 @@ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, "semver-diff": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", @@ -18746,6 +19133,12 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, + "type-fest": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", + "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "dev": true + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -18768,27 +19161,6 @@ "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==", "dev": true }, - "typescript-eslint-parser": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-21.0.2.tgz", - "integrity": "sha512-u+pj4RVJBr4eTzj0n5npoXD/oRthvfUCjSKndhNI714MG0mQq2DJw5WP7qmonRNIFgmZuvdDOH3BHm9iOjIAfg==", - "dev": true, - "requires": { - "eslint-scope": "^4.0.0", - "eslint-visitor-keys": "^1.0.0", - "typescript-estree": "5.3.0" - } - }, - "typescript-estree": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/typescript-estree/-/typescript-estree-5.3.0.tgz", - "integrity": "sha512-Vu0KmYdSCkpae+J48wsFC1ti19Hq3Wi/lODUaE+uesc3gzqhWbZ5itWbsjylLVbjNW4K41RqDzSfnaYNbmEiMQ==", - "dev": true, - "requires": { - "lodash.unescape": "4.0.1", - "semver": "5.5.0" - } - }, "ua-parser-js": { "version": "0.7.17", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", diff --git a/package.json b/package.json index 9f27aef9c..aa8fc5cbe 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "build": "npm run clean && mkdirp build && node --max_old_space_size=8192 node_modules/.bin/webpack --config packages/webpack.config.js --progress --colors", "capture": "MDC_COMMIT_HASH=$(git rev-parse --short HEAD) MDC_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) mocha --require ts-node/register --require babel-core/register --ui tdd --timeout 30000 test/screenshot/capture-suite.tsx", "clean": "rimraf build/** build packages/*/dist/", - "commitmsg": "validate-commit-msg", "fix": "eslint --fix --ext .jsx,.js,.tsx,.ts packages test", "lint": "eslint --ext .jsx,.js,.tsx,.ts packages test", "pretest": "npm stop", @@ -60,6 +59,12 @@ } } }, + "husky": { + "hooks": { + "pre-push": "npm run lint", + "commit-msg": "validate-commit-msg" + } + }, "devDependencies": { "@google-cloud/storage": "^1.6.0", "@material/base": "^1.0.0", @@ -97,6 +102,8 @@ "@types/react-router-dom": "^4.3.1", "@types/uuid": "^3.4.4", "@types/webpack-env": "^1.13.6", + "@typescript-eslint/eslint-plugin": "^1.9.0", + "@typescript-eslint/parser": "^1.9.0", "autoprefixer": "^8.5.1", "axios": "^0.18.0", "babel-core": "^6.26.3", @@ -119,11 +126,13 @@ "enzyme-adapter-utils": "^1.10.1", "eslint": "^5.9.0", "eslint-config-google": "^0.9.1", + "eslint-config-prettier": "^4.2.0", + "eslint-plugin-prettier": "^3.1.0", "eslint-plugin-react": "^7.7.0", "eslint-plugin-typescript": "^0.14.0", "extract-text-webpack-plugin": "^3.0.2", "gts": "^0.9.0", - "husky": "^0.14.3", + "husky": "^2.3.0", "istanbul": "^0.4.5", "istanbul-instrumenter-loader": "^3.0.1", "karma": "^2.0.4", @@ -137,6 +146,7 @@ "node-sass": "^4.9.2", "optimize-css-assets-webpack-plugin": "^3.2.0", "postcss-loader": "^2.1.5", + "prettier": "^1.17.1", "puppeteer": "^1.1.1", "react": "^16.4.2", "react-dom": "^16.4.2", @@ -149,7 +159,6 @@ "ts-loader": "^3.5.0", "ts-node": "^7.0.1", "typescript": "^3.3.3", - "typescript-eslint-parser": "^21.0.1", "utility-types": "^2.1.0", "uuid": "^3.3.2", "validate-commit-msg": "^2.14.0", diff --git a/packages/button/index.tsx b/packages/button/index.tsx index 3d78f1cae..a4d982b9f 100644 --- a/packages/button/index.tsx +++ b/packages/button/index.tsx @@ -29,39 +29,39 @@ import {CSS_CLASSES} from './constant'; type ButtonTypes = HTMLAnchorElement | HTMLButtonElement; export interface ButtonProps - extends InjectedProps, React.AnchorHTMLAttributes, React.ButtonHTMLAttributes { - raised?: boolean; - unelevated?: boolean; - outlined?: boolean; - dense?: boolean; - disabled?: boolean; - className?: string; - icon?: React.ReactElement>; - href?: string; - trailingIcon?: React.ReactElement>; + extends InjectedProps, + React.AnchorHTMLAttributes, + React.ButtonHTMLAttributes { + raised?: boolean; + unelevated?: boolean; + outlined?: boolean; + dense?: boolean; + disabled?: boolean; + className?: string; + icon?: React.ReactElement>; + href?: string; + trailingIcon?: React.ReactElement>; } -export const Button = ( - { - className = '', - raised = false, - unelevated = false, - outlined = false, - dense = false, - disabled = false, - icon, - href, - children, - initRipple, - trailingIcon, - // eslint disabled since we do not want to include - // this in ...otherProps. - // if unbounded is passed to the ); }; -const renderIcon = (icon?: React.ReactElement>) => ( - icon ? - React.cloneElement(icon, { - className: classnames(CSS_CLASSES.ICON, icon.props.className), - }) : - null -); +const renderIcon = ( + icon?: React.ReactElement> +) => + icon + ? React.cloneElement(icon, { + className: classnames(CSS_CLASSES.ICON, icon.props.className), + }) + : null; Button.defaultProps = { initRipple: () => {}, }; export default withRipple, ButtonTypes>(Button); - diff --git a/packages/card/ActionButtons.tsx b/packages/card/ActionButtons.tsx index 6c1033c6a..1f125ae24 100644 --- a/packages/card/ActionButtons.tsx +++ b/packages/card/ActionButtons.tsx @@ -25,27 +25,31 @@ import classnames from 'classnames'; import {CSS_CLASSES} from './constant'; -type ChildType = React.ReactElement>; +type ChildType = React.ReactElement< + React.HTMLProps +>; export interface ActionButtonsProps extends React.HTMLProps { className?: string; children?: ChildType | ChildType[]; -}; +} const addButtonClassToChildren = (children: ChildType | ChildType[]) => { - return React.Children.map((children as ChildType | ChildType[]), (item) => { + return React.Children.map(children as ChildType | ChildType[], (item) => { const className = classnames( (item as ChildType).props.className, CSS_CLASSES.ACTION, - CSS_CLASSES.ACTION_BUTTON, + CSS_CLASSES.ACTION_BUTTON ); const props = Object.assign({}, (item as ChildType).props, {className}); - return React.cloneElement((item as ChildType), props); + return React.cloneElement(item as ChildType, props); }); }; const ActionButtons: React.FunctionComponent = ({ - className = '', children, ...otherProps // eslint-disable-line react/prop-types + className = '', + children, + ...otherProps }) => { const classes = classnames(CSS_CLASSES.ACTION_BUTTONS, className); if (!children) return null; diff --git a/packages/card/ActionIcons.tsx b/packages/card/ActionIcons.tsx index 889239704..210e80b33 100644 --- a/packages/card/ActionIcons.tsx +++ b/packages/card/ActionIcons.tsx @@ -25,27 +25,31 @@ import classnames from 'classnames'; import {CSS_CLASSES} from './constant'; -type ChildType = React.ReactElement>; +type ChildType = React.ReactElement< + React.HTMLProps +>; export interface ActionIconsProps extends React.HTMLProps { className?: string; children?: ChildType | ChildType[]; -}; +} const addIconClassToChildren = (children: ChildType | ChildType[]) => { - return React.Children.map((children as ChildType | ChildType[]), (item) => { + return React.Children.map(children as ChildType | ChildType[], (item) => { const className = classnames( (item as ChildType).props.className, CSS_CLASSES.ACTION, - CSS_CLASSES.ACTION_ICON, + CSS_CLASSES.ACTION_ICON ); const props = Object.assign({}, (item as ChildType).props, {className}); - return React.cloneElement((item as ChildType), props); + return React.cloneElement(item as ChildType, props); }); }; const ActionIcons: React.FunctionComponent = ({ - className = '', children, ...otherProps // eslint-disable-line react/prop-types + className = '', + children, + ...otherProps }) => { const classes = classnames(CSS_CLASSES.ACTION_ICONS, className); if (!children) return null; diff --git a/packages/card/Actions.tsx b/packages/card/Actions.tsx index a0a86a69e..ee6e2fcff 100644 --- a/packages/card/Actions.tsx +++ b/packages/card/Actions.tsx @@ -28,10 +28,13 @@ import {CSS_CLASSES} from './constant'; export interface ActionsProps extends React.HTMLProps { className?: string; fullBleed?: boolean; -}; +} const Actions: React.FunctionComponent = ({ - className = '', children, fullBleed = false, ...otherProps // eslint-disable-line react/prop-types + className = '', + children, + fullBleed = false, + ...otherProps }) => { const classes = classnames(CSS_CLASSES.ACTIONS, className, { [CSS_CLASSES.ACTIONS_FULL_BLEED]: fullBleed, diff --git a/packages/card/Media.tsx b/packages/card/Media.tsx index 94d752b10..63ab6a935 100644 --- a/packages/card/Media.tsx +++ b/packages/card/Media.tsx @@ -37,15 +37,18 @@ export interface MediaProps extends React.HTMLProps { interface MediaChildren { children?: React.ReactNode; contentClassName?: string; -}; +} interface StyleValues { imageUrl: string; style: React.CSSProperties; -}; +} -const renderChildren: (mediaChildren: MediaChildren) => React.ReactElement | undefined = ({ - children, contentClassName, // eslint-disable-line react/prop-types +const renderChildren: ( + mediaChildren: MediaChildren +) => React.ReactElement | undefined = ({ + children, + contentClassName, }) => { if (!children) { return; @@ -54,15 +57,14 @@ const renderChildren: (mediaChildren: MediaChildren) => React.ReactElement{children}; }; -const getStyles: (styleValues: StyleValues) => React.CSSProperties = ({imageUrl, style}) => { - return Object.assign({}, - {backgroundImage: `url(${imageUrl})`}, - style - ); +const getStyles: (styleValues: StyleValues) => React.CSSProperties = ({ + imageUrl, + style, +}) => { + return Object.assign({}, {backgroundImage: `url(${imageUrl})`}, style); }; const Media: React.FunctionComponent = ({ - /* eslint-disable react/prop-types */ className = '', contentClassName = '', children, @@ -70,7 +72,6 @@ const Media: React.FunctionComponent = ({ wide = false, imageUrl = '', style = {}, - /* eslint-enable react/prop-types */ ...otherProps }) => { const classes = classnames(CSS_CLASSES.MEDIA, className, { @@ -79,7 +80,11 @@ const Media: React.FunctionComponent = ({ }); return ( -
+
{renderChildren({children, contentClassName})}
); diff --git a/packages/card/PrimaryContent.tsx b/packages/card/PrimaryContent.tsx index ab6f4e1bd..53f1d5e8c 100644 --- a/packages/card/PrimaryContent.tsx +++ b/packages/card/PrimaryContent.tsx @@ -26,18 +26,20 @@ import {withRipple, InjectedProps} from '@material/react-ripple'; import {CSS_CLASSES} from './constant'; -export interface PrimaryContentBaseProps extends React.HTMLProps, InjectedProps{ +export interface PrimaryContentBaseProps + extends React.HTMLProps, + InjectedProps { className: string; unbounded?: boolean; -}; +} -export const PrimaryContentBase: React.FunctionComponent = ({ - /* eslint-disable react/prop-types */ +export const PrimaryContentBase: React.FunctionComponent< + PrimaryContentBaseProps +> = ({ className = '', initRipple, children, - unbounded, // eslint-disable-line no-unused-vars - /* eslint-enable react/prop-types */ + unbounded, // eslint-disable-line @typescript-eslint/no-unused-vars ...otherProps }) => { const classes = classnames(CSS_CLASSES.PRIMARY_ACTION, className); @@ -49,4 +51,6 @@ export const PrimaryContentBase: React.FunctionComponent(PrimaryContentBase); +export default withRipple( + PrimaryContentBase +); diff --git a/packages/card/index.tsx b/packages/card/index.tsx index fd1a40dbd..0da807b65 100644 --- a/packages/card/index.tsx +++ b/packages/card/index.tsx @@ -32,12 +32,15 @@ import Media from './Media'; import {CSS_CLASSES} from './constant'; export interface CardProps extends React.HTMLProps { - className?: string, - outlined?: boolean + className?: string; + outlined?: boolean; } const Card: React.FunctionComponent = ({ - children, className = '', outlined = false, ...otherProps // eslint-disable-line react/prop-types + children, + className = '', + outlined = false, + ...otherProps }) => { const classes = classnames(CSS_CLASSES.ROOT, className, { [CSS_CLASSES.OUTLINED]: outlined, diff --git a/packages/checkbox/NativeControl.tsx b/packages/checkbox/NativeControl.tsx index 9a22351a6..2c07bd4e9 100644 --- a/packages/checkbox/NativeControl.tsx +++ b/packages/checkbox/NativeControl.tsx @@ -21,13 +21,13 @@ // THE SOFTWARE. import React from 'react'; -export interface NativeControlProps extends React.HTMLProps{ +export interface NativeControlProps extends React.HTMLProps { checked: boolean; disabled: boolean; id?: string; rippleActivatorRef: React.RefObject; onChange: (evt: React.ChangeEvent) => void; -}; +} export class NativeControl extends React.Component { static defaultProps: Partial = { diff --git a/packages/checkbox/index.tsx b/packages/checkbox/index.tsx index c6da27f8b..02043e783 100644 --- a/packages/checkbox/index.tsx +++ b/packages/checkbox/index.tsx @@ -29,7 +29,8 @@ import {withRipple, InjectedProps} from '@material/react-ripple'; import NativeControl from './NativeControl'; -export interface CheckboxProps extends InjectedProps { +export interface CheckboxProps + extends InjectedProps { checked?: boolean; className?: string; disabled?: boolean; @@ -40,7 +41,7 @@ export interface CheckboxProps extends InjectedProps void; children?: React.ReactNode; unbounded: boolean; -}; +} interface CheckboxState { checked?: boolean; @@ -48,7 +49,7 @@ interface CheckboxState { classList: Set; 'aria-checked': string; disabled: boolean; -}; +} export class Checkbox extends React.Component { inputElement: React.RefObject = React.createRef(); @@ -57,11 +58,11 @@ export class Checkbox extends React.Component { constructor(props: CheckboxProps) { super(props); this.state = { - 'checked': props.checked, - 'indeterminate': props.indeterminate, - 'classList': new Set(), + checked: props.checked, + indeterminate: props.indeterminate, + classList: new Set(), 'aria-checked': 'false', - 'disabled': props.disabled!, + disabled: props.disabled!, }; } @@ -122,8 +123,11 @@ export class Checkbox extends React.Component { const {classList} = this.state; const {className} = this.props; return classnames( - 'mdc-checkbox', Array.from(classList), - this.state.disabled ? cssClasses.DISABLED : null, className); + 'mdc-checkbox', + Array.from(classList), + this.state.disabled ? cssClasses.DISABLED : null, + className + ); } updateState = (key: keyof CheckboxState, value: string | boolean) => { @@ -131,7 +135,7 @@ export class Checkbox extends React.Component { ...prevState, [key]: value, })); - } + }; removeState = (key: keyof CheckboxState) => this.updateState(key, false); @@ -167,11 +171,11 @@ export class Checkbox extends React.Component { const {checked, indeterminate} = evt.target; this.handleChange(checked, indeterminate); onChange!(evt); - } + }; render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ className, checked, indeterminate, @@ -179,7 +183,7 @@ export class Checkbox extends React.Component { onChange, unbounded, disabled, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ nativeControlId, name, ...otherProps @@ -196,7 +200,11 @@ export class Checkbox extends React.Component { id={nativeControlId} checked={this.state.checked} disabled={this.state.disabled} - aria-checked={(this.state['aria-checked'] || this.state.checked!.toString()) as ('true' | 'false')} + aria-checked={ + (this.state['aria-checked'] || this.state.checked!.toString()) as ( + | 'true' + | 'false') + } name={name} onChange={this.onChange} rippleActivatorRef={this.inputElement} @@ -220,4 +228,6 @@ export class Checkbox extends React.Component { } } -export default withRipple(Checkbox); +export default withRipple( + Checkbox +); diff --git a/packages/chips/Chip.tsx b/packages/chips/Chip.tsx index e1efffb7f..6013ae485 100644 --- a/packages/chips/Chip.tsx +++ b/packages/chips/Chip.tsx @@ -42,7 +42,7 @@ export interface ChipProps extends InjectedProps { shouldRemoveOnTrailingIconClick?: boolean; trailingIcon?: React.ReactElement; initRipple: (surface: HTMLElement | null) => void; -}; +} type ChipState = { classList: Set; @@ -79,8 +79,13 @@ export class Chip extends React.Component { this.foundation = new MDCChipFoundation(this.adapter); this.foundation.init(); this.foundation.setSelected(selected!); - if (shouldRemoveOnTrailingIconClick !== this.foundation.getShouldRemoveOnTrailingIconClick()) { - this.foundation.setShouldRemoveOnTrailingIconClick(shouldRemoveOnTrailingIconClick!); + if ( + shouldRemoveOnTrailingIconClick !== + this.foundation.getShouldRemoveOnTrailingIconClick() + ) { + this.foundation.setShouldRemoveOnTrailingIconClick( + shouldRemoveOnTrailingIconClick! + ); } } @@ -91,8 +96,13 @@ export class Chip extends React.Component { this.foundation.setSelected(selected!); } - if (shouldRemoveOnTrailingIconClick !== prevProps.shouldRemoveOnTrailingIconClick) { - this.foundation.setShouldRemoveOnTrailingIconClick(shouldRemoveOnTrailingIconClick!); + if ( + shouldRemoveOnTrailingIconClick !== + prevProps.shouldRemoveOnTrailingIconClick + ) { + this.foundation.setShouldRemoveOnTrailingIconClick( + shouldRemoveOnTrailingIconClick! + ); } } @@ -123,7 +133,8 @@ export class Chip extends React.Component { classList.delete(className); this.setState({classList}); }, - hasClass: (className: string) => this.classes.split(' ').includes(className), + hasClass: (className: string) => + this.classes.split(' ').includes(className), hasLeadingIcon: () => Boolean(this.props.leadingIcon), eventTargetHasClass: (target: HTMLElement, className: string) => target.classList.contains(className), @@ -152,7 +163,13 @@ export class Chip extends React.Component { }, getCheckmarkBoundingClientRect: () => { const {chipCheckmark} = this.props; - if (!(chipCheckmark && chipCheckmark.props && chipCheckmark.props.getBoundingClientRect)) { + if ( + !( + chipCheckmark && + chipCheckmark.props && + chipCheckmark.props.getBoundingClientRect + ) + ) { // new DOMRect is not IE11 compatible const defaultDOMRect = { bottom: 0, @@ -168,7 +185,10 @@ export class Chip extends React.Component { } return chipCheckmark.props.getBoundingClientRect(); }, - setStyleProperty: (propertyName: keyof React.CSSProperties, value: string | null) => { + setStyleProperty: ( + propertyName: keyof React.CSSProperties, + value: string | null + ) => { if (!this.chipElement) return; this.chipElement.style.setProperty(propertyName, value); }, @@ -176,7 +196,8 @@ export class Chip extends React.Component { notifyInteraction: () => this.props.handleInteraction!(this.props.id!), notifySelection: (selected: boolean) => this.props.handleSelect!(this.props.id!, selected), - notifyTrailingIconInteraction: () => this.props.handleTrailingIconInteraction!(this.props.id!), + notifyTrailingIconInteraction: () => + this.props.handleTrailingIconInteraction!(this.props.id!), addClassToLeadingIcon: (className: string) => { const leadingIconClassList = new Set(this.state.leadingIconClassList); leadingIconClassList.add(className); @@ -200,7 +221,8 @@ export class Chip extends React.Component { this.foundation.handleInteraction(e.nativeEvent); }; - handleTrailingIconClick = (e: React.MouseEvent) => this.foundation.handleTrailingIconInteraction(e.nativeEvent); + handleTrailingIconClick = (e: React.MouseEvent) => + this.foundation.handleTrailingIconInteraction(e.nativeEvent); handleTransitionEnd = (e: React.TransitionEvent) => { this.props.onTransitionEnd!(e); @@ -241,7 +263,7 @@ export class Chip extends React.Component { render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ id, className, selected, @@ -256,7 +278,7 @@ export class Chip extends React.Component { initRipple, unbounded, shouldRemoveOnTrailingIconClick, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ chipCheckmark, leadingIcon, trailingIcon, diff --git a/packages/chips/ChipSet.tsx b/packages/chips/ChipSet.tsx index d15fdd883..742909bb6 100644 --- a/packages/chips/ChipSet.tsx +++ b/packages/chips/ChipSet.tsx @@ -23,7 +23,7 @@ import React from 'react'; import classnames from 'classnames'; import {MDCChipSetFoundation} from '@material/chips/chip-set/foundation'; import ChipCheckmark from './ChipCheckmark'; -import {ChipProps} from './Chip'; // eslint-disable-line no-unused-vars +import {ChipProps} from './Chip'; // eslint-disable-line @typescript-eslint/no-unused-vars type ChipType = React.ReactElement; @@ -36,15 +36,18 @@ export interface ChipSetProps { filter?: boolean; input?: boolean; children: ChipType | ChipType[]; -}; +} interface ChipSetState { foundation: MDCChipSetFoundation | null; selectedChipIds: string[]; hasInitialized: boolean; -}; +} -export default class ChipSet extends React.Component { +export default class ChipSet extends React.Component< + ChipSetProps, + ChipSetState +> { checkmarkWidth = 0; constructor(props: ChipSetProps) { super(props); @@ -96,9 +99,12 @@ export default class ChipSet extends React.Component get adapter() { return { - hasClass: (className: string) => this.classes.split(' ').includes(className), + hasClass: (className: string) => + this.classes.split(' ').includes(className), setSelected: () => { - const selectedChipIds = this.state.foundation!.getSelectedChipIds().slice(); + const selectedChipIds = this.state + .foundation!.getSelectedChipIds() + .slice(); this.setState({selectedChipIds}, () => { this.props.handleSelect!(selectedChipIds); }); @@ -108,13 +114,16 @@ export default class ChipSet extends React.Component } initChipSelection() { - React.Children.forEach((this.props.children as ChipType | ChipType[]), (child) => { - const {id} = (child as ChipType).props; - const selected = this.state.selectedChipIds.indexOf(id!) > -1; - if (selected) { - this.state.foundation!.select(id!); + React.Children.forEach( + this.props.children as ChipType | ChipType[], + (child) => { + const {id} = (child as ChipType).props; + const selected = this.state.selectedChipIds.indexOf(id!) > -1; + if (selected) { + this.state.foundation!.select(id!); + } } - }); + ); this.setState({hasInitialized: true}); } @@ -133,7 +142,9 @@ export default class ChipSet extends React.Component removeChip = (chipId: string) => { const {updateChips, children} = this.props; if (!children) return; - const chips = React.Children.toArray(children).slice() as React.ReactElement[]; + const chips = React.Children.toArray( + children + ).slice() as React.ReactElement[]; for (let i = 0; i < chips.length; i++) { const chip = chips[i]; if (chip.props.id === chipId) { @@ -166,30 +177,31 @@ export default class ChipSet extends React.Component const {selectedChipIds} = this.state; const selected = selectedChipIds.indexOf(chip.props.id) > -1; - const {handleInteraction, handleSelect, handleRemove, ...chipProps} = chip.props; - const props = Object.assign( - {}, - ...chipProps, - { - selected, - handleSelect: (id: string, selected: boolean): void => { - handleSelect!(id, selected); - this.handleSelect(id, selected); - }, - handleInteraction: (id: string): void => { - handleInteraction!(id); - this.handleInteraction(id); - }, - handleRemove: (id: string): void => { - handleRemove!(id); - this.handleRemove(id); - }, - chipCheckmark: filter ? ( - - ) : null, - computeBoundingRect: filter ? this.computeBoundingRect : null, + const { + handleInteraction, + handleSelect, + handleRemove, + ...chipProps + } = chip.props; + const props = Object.assign({}, ...chipProps, { + selected, + handleSelect: (id: string, selected: boolean): void => { + handleSelect!(id, selected); + this.handleSelect(id, selected); }, - ); + handleInteraction: (id: string): void => { + handleInteraction!(id); + this.handleInteraction(id); + }, + handleRemove: (id: string): void => { + handleRemove!(id); + this.handleRemove(id); + }, + chipCheckmark: filter ? ( + + ) : null, + computeBoundingRect: filter ? this.computeBoundingRect : null, + }); return React.cloneElement(chip, props); }; @@ -199,9 +211,11 @@ export default class ChipSet extends React.Component if (!this.state.hasInitialized) return null; return (
- {React.Children.map((this.props.children as ChipType | ChipType[]), this.renderChip)} + {React.Children.map( + this.props.children as ChipType | ChipType[], + this.renderChip + )}
); } } - diff --git a/packages/dialog/DialogButton.tsx b/packages/dialog/DialogButton.tsx index 65e930aeb..8b6c5cf12 100644 --- a/packages/dialog/DialogButton.tsx +++ b/packages/dialog/DialogButton.tsx @@ -26,31 +26,35 @@ import Button, {ButtonProps} from '@material/react-button'; type Omit = Pick>; type ButtonTypes = HTMLAnchorElement | HTMLButtonElement; -export interface DialogButtonProps extends Omit, 'initRipple'> { - action: string, - className?: string, - isDefault?: boolean, -}; +export interface DialogButtonProps + extends Omit, 'initRipple'> { + action: string; + className?: string; + isDefault?: boolean; +} +const DialogButton: ( + props: DialogButtonProps +) => React.ReactElement = ({ + action, + className = '', + children, + isDefault = false, + ...otherProps +}) => ( + // @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892 + +); -const DialogButton: (props: DialogButtonProps) => - React.ReactElement = ({ - /* eslint-disable react/prop-types */ - action, - className = '', - children, - isDefault = false, - ...otherProps - /* eslint-enable react/prop-types */ - }) => ( - // @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892 - - ); - -type DialogButton = React.ReactElement>; +type DialogButton = React.ReactElement< + DialogButtonProps +>; export default DialogButton; diff --git a/packages/dialog/DialogContent.tsx b/packages/dialog/DialogContent.tsx index b229c9d32..02c91706f 100644 --- a/packages/dialog/DialogContent.tsx +++ b/packages/dialog/DialogContent.tsx @@ -23,27 +23,25 @@ import React from 'react'; import classnames from 'classnames'; import {cssClasses} from './constants'; - export interface DialogContentProps extends React.HTMLProps { - className?: string, - tag?: string, - id?: string, -}; + className?: string; + tag?: string; + id?: string; +} -const DialogContent: (props: DialogContentProps) => - React.ReactElement = ({ - /* eslint-disable react/prop-types */ - className = '', - children, - tag: Tag = 'div', - ...otherProps - /* eslint-enable react/prop-types */ - }) => ( - // @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892 - - {children} - - ); +const DialogContent: ( + props: DialogContentProps +) => React.ReactElement = ({ + className = '', + children, + tag: Tag = 'div', + ...otherProps +}) => ( + // @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892 + + {children} + +); type DialogContent = React.ReactElement; export default DialogContent; diff --git a/packages/dialog/DialogFooter.tsx b/packages/dialog/DialogFooter.tsx index 9561e7244..081c8c846 100644 --- a/packages/dialog/DialogFooter.tsx +++ b/packages/dialog/DialogFooter.tsx @@ -23,27 +23,24 @@ import React from 'react'; import classnames from 'classnames'; import {cssClasses} from './constants'; - export interface DialogFooterProps extends React.HTMLProps { - className?: string, - tag?: string, -}; - + className?: string; + tag?: string; +} -const DialogFooter: (props: DialogFooterProps) => - React.ReactElement = ({ - /* eslint-disable react/prop-types */ - className = '', - children, - tag: Tag = 'footer', - ...otherProps - /* eslint-enable react/prop-types */ - }) => ( - // @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892 - - {children} - - ); +const DialogFooter: ( + props: DialogFooterProps +) => React.ReactElement = ({ + className = '', + children, + tag: Tag = 'footer', + ...otherProps +}) => ( + // @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892 + + {children} + +); type DialogFooter = React.ReactElement; export default DialogFooter; diff --git a/packages/dialog/DialogTitle.tsx b/packages/dialog/DialogTitle.tsx index 507fffae5..40d4b682e 100644 --- a/packages/dialog/DialogTitle.tsx +++ b/packages/dialog/DialogTitle.tsx @@ -23,27 +23,25 @@ import React from 'react'; import classnames from 'classnames'; import {cssClasses} from './constants'; - export interface DialogTitleProps extends React.HTMLProps { - className?: string, - tag?: string, - id?: string, -}; + className?: string; + tag?: string; + id?: string; +} -const DialogTitle: (props: DialogTitleProps) => - React.ReactElement = ({ - /* eslint-disable react/prop-types */ - className = '', - children, - tag: Tag = 'h2', - ...otherProps - /* eslint-enable react/prop-types */ - }) => ( - // @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892 - - {children} - - ); +const DialogTitle: ( + props: DialogTitleProps +) => React.ReactElement = ({ + className = '', + children, + tag: Tag = 'h2', + ...otherProps +}) => ( + // @ts-ignore https://github.com/Microsoft/TypeScript/issues/28892 + + {children} + +); type DialogTitle = React.ReactElement; export default DialogTitle; diff --git a/packages/dialog/index.tsx b/packages/dialog/index.tsx index 5be86db2e..dceaff017 100644 --- a/packages/dialog/index.tsx +++ b/packages/dialog/index.tsx @@ -25,41 +25,42 @@ import classnames from 'classnames'; import {MDCDialogFoundation} from '@material/dialog/foundation'; import {MDCDialogAdapter} from '@material/dialog/adapter'; -import {createFocusTrapInstance, isScrollable, areTopsMisaligned} from '@material/dialog/util'; +import { + createFocusTrapInstance, + isScrollable, + areTopsMisaligned, +} from '@material/dialog/util'; import {strings} from '@material/dialog/constants'; import {ponyfill} from '@material/dom'; -/* eslint-disable no-unused-vars */ +/* eslint-disable @typescript-eslint/no-unused-vars */ import DialogContent, {DialogContentProps} from './DialogContent'; import DialogFooter, {DialogFooterProps} from './DialogFooter'; import DialogTitle, {DialogTitleProps} from './DialogTitle'; -/* eslint-enable no-unused-vars */ +/* eslint-enable @typescript-eslint/no-unused-vars */ import DialogButton from './DialogButton'; import {cssClasses, LAYOUT_EVENTS} from './constants'; import {FocusTrap} from 'focus-trap'; -export type ChildTypes = ( - DialogTitle> - | DialogContent> - | DialogFooter> - ); - - -export interface DialogProps, - ContentProps extends {} = DialogContentProps, - FooterProps extends {} = DialogFooterProps - > extends React.HTMLProps { +export type ChildTypes = + | DialogTitle> + | DialogContent> + | DialogFooter>; + +export interface DialogProps< + T, + TitleProps extends {} = DialogTitleProps, + ContentProps extends {} = DialogContentProps, + FooterProps extends {} = DialogFooterProps +> extends React.HTMLProps { autoStackButtons?: boolean; - children?:( - ( - React.ReactElement - | React.ReactElement - | React.ReactElement - )[] + children?: + | ( + | React.ReactElement + | React.ReactElement + | React.ReactElement)[] | React.ReactElement | React.ReactElement - | React.ReactElement - ); + | React.ReactElement; className?: string; escapeKeyAction?: string; id?: string; @@ -71,7 +72,7 @@ export interface DialogProps; @@ -88,7 +89,7 @@ function isDialogContent(element: any): element is DialogContent { class Dialog extends React.Component< DialogProps, DialogState - > { +> { focusTrap?: FocusTrap; foundation!: MDCDialogFoundation; dialogElement: React.RefObject = React.createRef(); @@ -111,7 +112,12 @@ class Dialog extends React.Component< state: DialogState = {classList: new Set()}; componentDidMount() { - const {open, autoStackButtons, escapeKeyAction, scrimClickAction} = this.props; + const { + open, + autoStackButtons, + escapeKeyAction, + scrimClickAction, + } = this.props; this.foundation = new MDCDialogFoundation(this.adapter); this.foundation.init(); @@ -122,11 +128,13 @@ class Dialog extends React.Component< this.foundation.setAutoStackButtons(autoStackButtons!); } - if (typeof escapeKeyAction === 'string') { // set even if empty string + if (typeof escapeKeyAction === 'string') { + // set even if empty string this.foundation.setEscapeKeyAction(escapeKeyAction); } - if (typeof scrimClickAction === 'string') { // set even if empty string + if (typeof scrimClickAction === 'string') { + // set even if empty string this.foundation.setScrimClickAction(scrimClickAction); } } @@ -136,7 +144,12 @@ class Dialog extends React.Component< } componentDidUpdate(prevProps: DialogProps) { - const {open, autoStackButtons, escapeKeyAction, scrimClickAction} = this.props; + const { + open, + autoStackButtons, + escapeKeyAction, + scrimClickAction, + } = this.props; if (prevProps.autoStackButtons !== autoStackButtons) { this.foundation.setAutoStackButtons(autoStackButtons!); @@ -162,19 +175,26 @@ class Dialog extends React.Component< } get buttons(): HTMLButtonElement[] { - const buttons = this.dialogElement.current && - [].slice.call(this.dialogElement.current.getElementsByClassName(cssClasses.BUTTON)); + const buttons = + this.dialogElement.current && + [].slice.call( + this.dialogElement.current.getElementsByClassName(cssClasses.BUTTON) + ); return buttons ? buttons : []; } get content(): HTMLElement | null { - return this.dialogElement.current && - this.dialogElement.current.querySelector(`.${cssClasses.CONTENT}`); + return ( + this.dialogElement.current && + this.dialogElement.current.querySelector(`.${cssClasses.CONTENT}`) + ); } get defaultButton(): HTMLButtonElement | null { - return this.dialogElement.current && - this.dialogElement.current.querySelector(`.${cssClasses.DEFAULT_BUTTON}`); + return ( + this.dialogElement.current && + this.dialogElement.current.querySelector(`.${cssClasses.DEFAULT_BUTTON}`) + ); } private open = (): void => { @@ -184,7 +204,9 @@ class Dialog extends React.Component< }; private initializeFocusTrap = (): void => { - this.focusTrap = this.props.children && createFocusTrapInstance(this.dialogElement.current!); + this.focusTrap = + this.props.children && + createFocusTrapInstance(this.dialogElement.current!); }; get adapter(): MDCDialogAdapter { @@ -201,19 +223,23 @@ class Dialog extends React.Component< classList.delete(className); this.setState({classList}); }, - hasClass: (className: string) => this.classes.split(' ').includes(className), - addBodyClass: (className: string) => document.body.classList.add(className), - removeBodyClass: (className: string) => document.body.classList.remove(className), - eventTargetMatches: (target: HTMLElement, selector: string) => matches(target, selector), + hasClass: (className: string) => + this.classes.split(' ').includes(className), + addBodyClass: (className: string) => + document.body.classList.add(className), + removeBodyClass: (className: string) => + document.body.classList.remove(className), + eventTargetMatches: (target: HTMLElement, selector: string) => + matches(target, selector), trapFocus: () => this.focusTrap && this.focusTrap.activate(), releaseFocus: () => this.focusTrap && this.focusTrap.deactivate(), isContentScrollable: () => { const content = this.content; - return (!!content) ? isScrollable(content) : false; + return !!content ? isScrollable(content) : false; }, areButtonsStacked: () => { const buttons = this.buttons; - return (!!buttons) ? areTopsMisaligned(this.buttons) : false; + return !!buttons ? areTopsMisaligned(this.buttons) : false; }, getActionFromEvent: (evt: any) => { const elem = closest(evt.target, `[${strings.ACTION_ATTRIBUTE}]`); @@ -227,15 +253,21 @@ class Dialog extends React.Component< }, reverseButtons: () => { const buttons = this.buttons; - return buttons && - buttons.reverse().forEach((button: HTMLButtonElement) => - button.parentElement && button.parentElement.appendChild(button) - ); + return ( + buttons && + buttons + .reverse() + .forEach( + (button: HTMLButtonElement) => + button.parentElement && button.parentElement.appendChild(button) + ) + ); }, notifyOpening: () => this.handleOpening(), notifyOpened: () => this.props.onOpen && this.props.onOpen(), notifyClosing: (action: string) => this.handleClosing(action), - notifyClosed: (action: string) => this.props.onClose && this.props.onClose(action), + notifyClosed: (action: string) => + this.props.onClose && this.props.onClose(action), }; } @@ -244,9 +276,7 @@ class Dialog extends React.Component< LAYOUT_EVENTS.forEach((evt: string) => window.addEventListener(evt, this.handleLayout) ); - document.addEventListener( - 'keydown', this.handleDocumentKeyDown - ); + document.addEventListener('keydown', this.handleDocumentKeyDown); }; handleClosing = (action: string): void => { @@ -254,21 +284,19 @@ class Dialog extends React.Component< LAYOUT_EVENTS.forEach((evt: string) => window.removeEventListener(evt, this.handleLayout) ); - document.removeEventListener( - 'keydown', this.handleDocumentKeyDown - ); + document.removeEventListener('keydown', this.handleDocumentKeyDown); }; - handleInteraction = (e: React.MouseEvent | React.KeyboardEvent): void => - this.foundation.handleInteraction(e.nativeEvent); + handleInteraction = ( + e: React.MouseEvent | React.KeyboardEvent + ): void => this.foundation.handleInteraction(e.nativeEvent); handleDocumentKeyDown = (e: KeyboardEvent): void => this.foundation.handleDocumentKeydown(e); handleLayout = (): void => this.foundation.layout(); - render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ autoStackButtons, className, children, @@ -284,11 +312,12 @@ class Dialog extends React.Component< scrimClickAction, tag: Tag, ...otherProps - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ } = this.props; - const container: React.ReactElement | undefined = - this.renderContainer(children as ChildTypes[]); + const container: + | React.ReactElement + | undefined = this.renderContainer(children as ChildTypes[]); return ( // @ts-ignore Tag does not have any construct https://github.com/Microsoft/TypeScript/issues/28892 extends React.Component< ref={this.dialogElement} > {container} -
+
); } - - renderContainer = (children?: ChildTypes[]): - React.ReactElement | undefined => !children ? undefined : ( -
-
- {React.Children.map( - (children as ChildTypes[]), - this.renderChild - )} + renderContainer = ( + children?: ChildTypes[] + ): React.ReactElement | undefined => + !children ? ( + undefined + ) : ( +
+
+ {React.Children.map(children as ChildTypes[], this.renderChild)} +
-
- ); + ); - renderChild = (child: ChildTypes, i: number ): - ChildTypes => - (isDialogTitle(child) || isDialogContent(child)) + renderChild = (child: ChildTypes, i: number): ChildTypes => + isDialogTitle(child) || isDialogContent(child) ? React.cloneElement(child, { - key: `child-${i}`, - ...child.props, - id: this.setId(child, child.props.id)}) + key: `child-${i}`, + ...child.props, + id: this.setId(child, child.props.id), + }) : child; - - setId = (child: ChildTypes, componentId?: string): string => { - const {id} = this.props; - if (isDialogTitle(child)) { - const labelledBy = componentId || id + '-title'; - this.labelledBy = labelledBy; - return labelledBy; - } - - const describedBy = componentId || id + '-content'; - this.describedBy = describedBy; - return describedBy; + setId = (child: ChildTypes, componentId?: string): string => { + const {id} = this.props; + if (isDialogTitle(child)) { + const labelledBy = componentId || id + '-title'; + this.labelledBy = labelledBy; + return labelledBy; } + + const describedBy = componentId || id + '-content'; + this.describedBy = describedBy; + return describedBy; + }; } export default Dialog; -export { - DialogTitle, - DialogContent, - DialogFooter, - DialogButton, -}; +export {DialogTitle, DialogContent, DialogFooter, DialogButton}; diff --git a/packages/drawer/AppContent.tsx b/packages/drawer/AppContent.tsx index a404a712e..64f9b23c7 100644 --- a/packages/drawer/AppContent.tsx +++ b/packages/drawer/AppContent.tsx @@ -24,12 +24,15 @@ import React from 'react'; import classnames from 'classnames'; export interface DrawerAppContentProps { - tag?: string, - className?: string, -}; + tag?: string; + className?: string; +} const DrawerAppContent: React.FunctionComponent = ({ - tag: Tag = 'div', children, className = '', ...otherProps // eslint-disable-line react/prop-types + tag: Tag = 'div', + children, + className = '', + ...otherProps }) => { return ( // https://github.com/Microsoft/TypeScript/issues/28892 diff --git a/packages/drawer/Content.tsx b/packages/drawer/Content.tsx index 4f0550c82..57775fa88 100644 --- a/packages/drawer/Content.tsx +++ b/packages/drawer/Content.tsx @@ -24,12 +24,15 @@ import React from 'react'; import classnames from 'classnames'; export interface DrawerContentProps { - tag?: string, - className?: string -}; + tag?: string; + className?: string; +} const DrawerContent: React.FunctionComponent = ({ - tag: Tag = 'div', children, className = '', ...otherProps // eslint-disable-line react/prop-types + tag: Tag = 'div', + children, + className = '', + ...otherProps }) => { return ( // https://github.com/Microsoft/TypeScript/issues/28892 diff --git a/packages/drawer/Header.tsx b/packages/drawer/Header.tsx index 371579433..ce1b6d6ac 100644 --- a/packages/drawer/Header.tsx +++ b/packages/drawer/Header.tsx @@ -24,12 +24,15 @@ import React from 'react'; import classnames from 'classnames'; export interface DrawerHeaderProps { - tag?: string, - className?: string -}; + tag?: string; + className?: string; +} const DrawerHeader: React.FunctionComponent = ({ - tag: Tag = 'div', children, className = '', ...otherProps // eslint-disable-line react/prop-types + tag: Tag = 'div', + children, + className = '', + ...otherProps }) => { return ( // https://github.com/Microsoft/TypeScript/issues/28892 diff --git a/packages/drawer/Subtitle.tsx b/packages/drawer/Subtitle.tsx index 4847bb3e2..a2052fde1 100644 --- a/packages/drawer/Subtitle.tsx +++ b/packages/drawer/Subtitle.tsx @@ -26,10 +26,13 @@ import classnames from 'classnames'; export interface DrawerSubTitleProps { tag?: string; className?: string; -}; +} const DrawerSubTitle: React.FunctionComponent = ({ - tag: Tag = 'h6', children, className = '', ...otherProps // eslint-disable-line react/prop-types + tag: Tag = 'h6', + children, + className = '', + ...otherProps }) => { return ( // https://github.com/Microsoft/TypeScript/issues/28892 diff --git a/packages/drawer/Title.tsx b/packages/drawer/Title.tsx index 01235962c..97d501daf 100644 --- a/packages/drawer/Title.tsx +++ b/packages/drawer/Title.tsx @@ -26,10 +26,13 @@ import classnames from 'classnames'; export interface DrawerTitleProps { tag?: string; className?: string; -}; +} const DrawerTitle: React.FunctionComponent = ({ - tag: Tag = 'h3', children, className = '', ...otherProps // eslint-disable-line react/prop-types + tag: Tag = 'h3', + children, + className = '', + ...otherProps }) => { return ( // https://github.com/Microsoft/TypeScript/issues/28892 diff --git a/packages/drawer/index.tsx b/packages/drawer/index.tsx index 876349153..a9b7acc52 100644 --- a/packages/drawer/index.tsx +++ b/packages/drawer/index.tsx @@ -39,7 +39,7 @@ const {cssClasses: listCssClasses} = MDCListFoundation; type RefCallback = (node: T) => void; -export interface DrawerProps extends React.HTMLProps{ +export interface DrawerProps extends React.HTMLProps { className?: string; open?: boolean; onOpen?: () => void; @@ -48,13 +48,15 @@ export interface DrawerProps extends React.HTMLProps{ dismissible?: boolean; modal?: boolean; innerRef?: RefCallback | React.RefObject; -}; +} interface DrawerState { classList: Set; -}; +} -const isRefObject = function(ref: DrawerProps['innerRef']): ref is React.RefObject { +const isRefObject = function( + ref: DrawerProps['innerRef'] +): ref is React.RefObject { return typeof ref !== 'function'; }; @@ -146,7 +148,8 @@ class Drawer extends React.Component { classList.delete(className); this.setState({classList}); }, - hasClass: (className: string) => this.classes.split(' ').includes(className), + hasClass: (className: string) => + this.classes.split(' ').includes(className), elementHasClass: (element: HTMLElement, className: string) => element.classList.contains(className), saveFocus: () => { @@ -154,19 +157,21 @@ class Drawer extends React.Component { }, restoreFocus: () => { const drawerElement = this.drawerElement && this.drawerElement.current; - if (drawerElement && + if ( + drawerElement && this.previousFocus && this.previousFocus.focus && - drawerElement.contains(document.activeElement)) { + drawerElement.contains(document.activeElement) + ) { this.previousFocus.focus(); } }, focusActiveNavigationItem: () => { - const drawerElement = - this.drawerElement && this.drawerElement.current; + const drawerElement = this.drawerElement && this.drawerElement.current; if (!drawerElement) return; - const activeNavItemEl - = drawerElement.querySelector(`.${listCssClasses.LIST_ITEM_ACTIVATED_CLASS}`) as HTMLElement | null; + const activeNavItemEl = drawerElement.querySelector( + `.${listCssClasses.LIST_ITEM_ACTIVATED_CLASS}` + ) as HTMLElement | null; if (activeNavItemEl) { activeNavItemEl.focus(); } @@ -215,11 +220,11 @@ class Drawer extends React.Component { } else { innerRef(node); } - } + }; render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ onClose, onOpen, onKeyDown, @@ -229,7 +234,7 @@ class Drawer extends React.Component { className, innerRef, modal, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ tag: Tag, ...otherProps } = this.props; @@ -256,7 +261,9 @@ class Drawer extends React.Component { return (
(this.foundation as MDCModalDrawerFoundation).handleScrimClick()} + onClick={() => + (this.foundation as MDCModalDrawerFoundation).handleScrimClick() + } /> ); } diff --git a/packages/fab/index.tsx b/packages/fab/index.tsx index e3e389978..c05b8eddf 100644 --- a/packages/fab/index.tsx +++ b/packages/fab/index.tsx @@ -26,18 +26,21 @@ import {withRipple, InjectedProps} from '@material/react-ripple'; import {CSS_CLASSES} from './constant'; -export interface FabProps extends InjectedProps, - React.ButtonHTMLAttributes { - exited?: boolean; - mini?: boolean; - icon?: React.ReactElement; - textLabel?: string; - className?: string; - initRipple?: React.Ref; - unbounded?: boolean; +export interface FabProps + extends InjectedProps, + React.ButtonHTMLAttributes { + exited?: boolean; + mini?: boolean; + icon?: React.ReactElement; + textLabel?: string; + className?: string; + initRipple?: React.Ref; + unbounded?: boolean; } -const Icon: React.FunctionComponent<{icon?: React.ReactElement}> = ({icon}) => { +const Icon: React.FunctionComponent<{ + icon?: React.ReactElement; +}> = ({icon}) => { if (!icon) { return null; } @@ -48,7 +51,7 @@ const Icon: React.FunctionComponent<{icon?: React.ReactElement}> = }; const TextLabel: React.FunctionComponent<{textLabel: string}> = ({ - textLabel, // eslint-disable-line react/prop-types + textLabel, }) => { if (textLabel.length === 0) { return null; @@ -56,16 +59,16 @@ const TextLabel: React.FunctionComponent<{textLabel: string}> = ({ return {textLabel}; }; -export const Fab: React.FunctionComponent> = ({ - /* eslint-disable react/prop-types */ +export const Fab: React.FunctionComponent< + FabProps & React.HTMLProps +> = ({ exited = false, mini = false, icon, textLabel = '', className = '', initRipple = () => {}, - unbounded, - /* eslint-enable react/prop-types */ + unbounded, // eslint-disable-line @typescript-eslint/no-unused-vars ...otherProps }) => { const extended = textLabel.length > 0; diff --git a/packages/floating-label/index.tsx b/packages/floating-label/index.tsx index 9e3ac1a29..2c04f0958 100644 --- a/packages/floating-label/index.tsx +++ b/packages/floating-label/index.tsx @@ -26,20 +26,21 @@ import {MDCFloatingLabelFoundation} from '@material/floating-label/foundation'; import {MDCFloatingLabelAdapter} from '@material/floating-label/adapter'; import {cssClasses} from '@material/floating-label/constants'; -export interface FloatingLabelProps extends React.LabelHTMLAttributes { +export interface FloatingLabelProps + extends React.LabelHTMLAttributes { className?: string; handleWidthChange?: (width: number) => void; float?: boolean; -}; +} interface FloatingLabelState { classList: Set; -}; +} export default class FloatingLabel extends React.Component< FloatingLabelProps, FloatingLabelState - > { +> { foundation!: MDCFloatingLabelFoundation; labelElement: React.RefObject = React.createRef(); @@ -124,10 +125,10 @@ export default class FloatingLabel extends React.Component< render() { const { - className, // eslint-disable-line no-unused-vars + className, // eslint-disable-line @typescript-eslint/no-unused-vars children, - handleWidthChange, // eslint-disable-line no-unused-vars - float, // eslint-disable-line no-unused-vars + handleWidthChange, // eslint-disable-line @typescript-eslint/no-unused-vars + float, // eslint-disable-line @typescript-eslint/no-unused-vars ...otherProps } = this.props; diff --git a/packages/icon-button/IconToggle.tsx b/packages/icon-button/IconToggle.tsx index 26a9dabaa..57b219cfc 100644 --- a/packages/icon-button/IconToggle.tsx +++ b/packages/icon-button/IconToggle.tsx @@ -29,7 +29,9 @@ export interface IconToggleProps { } const IconToggle: React.FunctionComponent = ({ - isOn = false, className = '', children = '', // eslint-disable-line react/prop-types + isOn = false, + className = '', + children = '', }) => { const classes = classnames( 'mdc-icon-button__icon', diff --git a/packages/icon-button/index.tsx b/packages/icon-button/index.tsx index d53cbbcb5..da2172c97 100644 --- a/packages/icon-button/index.tsx +++ b/packages/icon-button/index.tsx @@ -38,19 +38,21 @@ type IconButtonTypes = HTMLButtonElement | HTMLAnchorElement; export interface IconButtonBaseProps extends ElementAttributes { handleChange?: (evt: MDCIconButtonToggleEventDetail) => void; isLink?: boolean; -}; +} interface IconButtonBaseState extends ElementAttributes { classList: Set; -}; +} export interface IconButtonProps - extends InjectedProps, IconButtonBaseProps, React.HTMLProps {}; + extends InjectedProps, + IconButtonBaseProps, + React.HTMLProps {} class IconButtonBase extends React.Component< IconButtonProps, IconButtonBaseState - > { +> { foundation!: MDCIconButtonToggleFoundation; constructor(props: IconButtonProps) { @@ -90,7 +92,8 @@ class IconButtonBase extends React.Component< classList.delete(className); this.setState({classList}); }, - hasClass: (className: string) => this.classes.split(' ').includes(className), + hasClass: (className: string) => + this.classes.split(' ').includes(className), setAttr: this.updateState, notifyChange: this.props.handleChange!, }; @@ -101,7 +104,7 @@ class IconButtonBase extends React.Component< ...prevState, [key]: value, })); - } + }; handleClick_ = (e: React.MouseEvent) => { this.props.onClick!(e); @@ -113,13 +116,13 @@ class IconButtonBase extends React.Component< children, initRipple, isLink, - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ className, handleChange, onClick, unbounded, [ARIA_PRESSED]: ariaPressed, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ ...otherProps } = this.props; @@ -133,11 +136,18 @@ class IconButtonBase extends React.Component< if (isLink) { return }>{children}; } - return ; + return ( + + ); } } -const IconButton = withRipple, IconButtonTypes>(IconButtonBase); +const IconButton = withRipple< + IconButtonProps, + IconButtonTypes +>(IconButtonBase); export default IconButton; export {IconToggle, IconButtonBase}; diff --git a/packages/layout-grid/Cell.tsx b/packages/layout-grid/Cell.tsx index 3d6ac9ab5..f82ad362e 100644 --- a/packages/layout-grid/Cell.tsx +++ b/packages/layout-grid/Cell.tsx @@ -31,18 +31,19 @@ export type EightColumn = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; export type Alignment = 'bottom' | 'middle' | 'top'; export interface CellProps extends React.HTMLProps { - align?: Alignment, - className?: string, - columns?: TwelveColumn, - desktopColumns?: TwelveColumn, - order?: TwelveColumn, - phoneColumns?: FourColumn, - tabletColumns?: EightColumn, - tag?: string -}; + align?: Alignment; + className?: string; + columns?: TwelveColumn; + desktopColumns?: TwelveColumn; + order?: TwelveColumn; + phoneColumns?: FourColumn; + tabletColumns?: EightColumn; + tag?: string; +} -const Cell: (props: CellProps) => React.ReactElement = ({ - /* eslint-disable react/prop-types */ +const Cell: ( + props: CellProps +) => React.ReactElement = ({ align, children, className = '', @@ -52,16 +53,21 @@ const Cell: (props: CellProps) => React.ReactE phoneColumns, tabletColumns, tag: Tag = 'div', - /* eslint-enable react/prop-types */ ...otherProps }) => { const classes = classnames(CSS_CLASSES.CELL, className, { [`${CSS_CLASSES.CELL_ALIGN}-${align}`]: !!align, [`${CSS_CLASSES.CELL_ORDER}-${order}`]: !!order, [`${CSS_CLASSES.CELL_SPAN}-${columns}`]: !!columns, - [`${CSS_CLASSES.CELL_SPAN}-${desktopColumns}-${DEVICE_TYPE.DESKTOP}`]: !!desktopColumns, - [`${CSS_CLASSES.CELL_SPAN}-${phoneColumns}-${DEVICE_TYPE.PHONE}`]: !!phoneColumns, - [`${CSS_CLASSES.CELL_SPAN}-${tabletColumns}-${DEVICE_TYPE.TABLET}`]: !!tabletColumns, + [`${CSS_CLASSES.CELL_SPAN}-${desktopColumns}-${ + DEVICE_TYPE.DESKTOP + }`]: !!desktopColumns, + [`${CSS_CLASSES.CELL_SPAN}-${phoneColumns}-${ + DEVICE_TYPE.PHONE + }`]: !!phoneColumns, + [`${CSS_CLASSES.CELL_SPAN}-${tabletColumns}-${ + DEVICE_TYPE.TABLET + }`]: !!tabletColumns, }); return ( diff --git a/packages/layout-grid/Grid.tsx b/packages/layout-grid/Grid.tsx index 6198eb13d..850ead8e1 100644 --- a/packages/layout-grid/Grid.tsx +++ b/packages/layout-grid/Grid.tsx @@ -27,20 +27,20 @@ import {CSS_CLASSES} from './constant'; export type Alignment = 'left' | 'right'; export interface GridProps extends React.HTMLProps { - align?: Alignment, - className?: string, - fixedColumnWidth?: boolean, - tag?: string -}; + align?: Alignment; + className?: string; + fixedColumnWidth?: boolean; + tag?: string; +} -const Grid: (props: GridProps) => React.ReactElement = ({ - /* eslint-disable react/prop-types */ +const Grid: ( + props: GridProps +) => React.ReactElement = ({ align, children, className = '', fixedColumnWidth = false, tag: Tag = 'div', - /* eslint-enable react/prop-types */ ...otherProps }) => { const classes = classnames(CSS_CLASSES.ROOT, className, { diff --git a/packages/layout-grid/Row.tsx b/packages/layout-grid/Row.tsx index 144cd15ed..e32aca0e6 100644 --- a/packages/layout-grid/Row.tsx +++ b/packages/layout-grid/Row.tsx @@ -26,17 +26,17 @@ import classnames from 'classnames'; import {CSS_CLASSES} from './constant'; export interface RowProps extends React.HTMLProps { - className?: string, - tag?: string -}; + className?: string; + tag?: string; +} -const Row: (props: RowProps) => React.ReactElement = ({ - /* eslint-disable react/prop-types */ +const Row: ( + props: RowProps +) => React.ReactElement = ({ children = '', className, tag: Tag = 'div', ...otherProps - /* eslint-enable react/prop-types */ }) => { const classes = classnames(CSS_CLASSES.INNER, className); diff --git a/packages/line-ripple/index.tsx b/packages/line-ripple/index.tsx index 26c10292c..347c281f8 100644 --- a/packages/line-ripple/index.tsx +++ b/packages/line-ripple/index.tsx @@ -29,17 +29,17 @@ export interface LineRippleProps extends React.HTMLProps { className?: string; active?: boolean; rippleCenter?: number; -}; +} interface LineRippleState { classList: Set; style: React.CSSProperties; -}; +} export default class LineRipple extends React.Component< LineRippleProps, LineRippleState - > { +> { static defaultProps = { className: '', style: {}, @@ -75,7 +75,8 @@ export default class LineRipple extends React.Component< // https://github.com/material-components/material-components-web/issues/3643 const {rippleCenter} = this.props; if ( - rippleCenter && rippleCenter !== prevProps.rippleCenter && + rippleCenter && + rippleCenter !== prevProps.rippleCenter && !isNaN(rippleCenter) ) { this.foundation_.setRippleCenter(rippleCenter); @@ -122,14 +123,15 @@ export default class LineRipple extends React.Component< return Object.assign({}, style, wrappedStyle); }; - onTransitionEnd = (evt: React.TransitionEvent) => this.foundation_.handleTransitionEnd(evt.nativeEvent); + onTransitionEnd = (evt: React.TransitionEvent) => + this.foundation_.handleTransitionEnd(evt.nativeEvent); render() { const { - style, // eslint-disable-line no-unused-vars - className, // eslint-disable-line no-unused-vars - active, // eslint-disable-line no-unused-vars - rippleCenter, // eslint-disable-line no-unused-vars + style, // eslint-disable-line @typescript-eslint/no-unused-vars + className, // eslint-disable-line @typescript-eslint/no-unused-vars + active, // eslint-disable-line @typescript-eslint/no-unused-vars + rippleCenter, // eslint-disable-line @typescript-eslint/no-unused-vars ...otherProps } = this.props; diff --git a/packages/linear-progress/index.tsx b/packages/linear-progress/index.tsx index 9724fc810..ca87d416c 100644 --- a/packages/linear-progress/index.tsx +++ b/packages/linear-progress/index.tsx @@ -34,16 +34,16 @@ export interface LinearProgressProps extends React.HTMLProps { progress?: number; reversed?: boolean; tag?: string; -}; +} interface LinearProgressState { classList: Set; -}; +} class LinearProgress extends React.Component< LinearProgressProps, LinearProgressState - > { +> { isComponentMounted: boolean = false; bufferElement: React.RefObject = React.createRef(); primaryBarElement: React.RefObject = React.createRef(); @@ -159,7 +159,7 @@ class LinearProgress extends React.Component< render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ buffer, bufferingDots, className, @@ -168,7 +168,7 @@ class LinearProgress extends React.Component< progress, reversed, tag: Tag, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ ...otherProps } = this.props; return ( @@ -178,10 +178,7 @@ class LinearProgress extends React.Component< {bufferingDots && (
)} -
+
{ - className?: string, - tag?: string, - role?: string -}; +export interface ListDividerProps extends React.HTMLProps { + className?: string; + tag?: string; + role?: string; +} const ListDivider: React.FunctionComponent = ({ - tag: Tag = 'li', className = '', role = 'separator', ...otherProps // eslint-disable-line react/prop-types + tag: Tag = 'li', + className = '', + role = 'separator', + ...otherProps }) => { return ( // https://github.com/Microsoft/TypeScript/issues/28892 diff --git a/packages/list/ListGroup.tsx b/packages/list/ListGroup.tsx index bf4198c24..7765bd17d 100644 --- a/packages/list/ListGroup.tsx +++ b/packages/list/ListGroup.tsx @@ -23,13 +23,16 @@ import React from 'react'; import classnames from 'classnames'; -export interface ListGroupProps extends React.HTMLProps{ - className?: string, - tag?: string -}; +export interface ListGroupProps extends React.HTMLProps { + className?: string; + tag?: string; +} -const ListGroup:React.FunctionComponent = ({ - tag: Tag = 'div', className = '', children, ...otherProps // eslint-disable-line react/prop-types +const ListGroup: React.FunctionComponent = ({ + tag: Tag = 'div', + className = '', + children, + ...otherProps }) => { return ( // https://github.com/Microsoft/TypeScript/issues/28892 diff --git a/packages/list/ListGroupSubheader.tsx b/packages/list/ListGroupSubheader.tsx index 79766d6f9..fadd3ed6d 100644 --- a/packages/list/ListGroupSubheader.tsx +++ b/packages/list/ListGroupSubheader.tsx @@ -23,12 +23,15 @@ import React from 'react'; import classnames from 'classnames'; export interface ListGroupSubheaderProps extends React.HTMLProps { - className?: string, - tag?: string -}; + className?: string; + tag?: string; +} -const ListGroupSubheader:React.FunctionComponent = ({ - tag: Tag = 'h3', className = '', children, ...otherProps // eslint-disable-line react/prop-types +const ListGroupSubheader: React.FunctionComponent = ({ + tag: Tag = 'h3', + className = '', + children, + ...otherProps }) => { return ( // https://github.com/Microsoft/TypeScript/issues/28892 diff --git a/packages/list/ListItem.tsx b/packages/list/ListItem.tsx index 380a89224..28f6d949b 100644 --- a/packages/list/ListItem.tsx +++ b/packages/list/ListItem.tsx @@ -26,21 +26,24 @@ import {MDCListFoundation} from '@material/list/foundation'; import {ListItemContext, ListItemContextShape} from './index'; import {closest} from '@material/dom/ponyfill'; -export interface ListItemProps extends React.HTMLProps, ListItemContextShape { +export interface ListItemProps + extends React.HTMLProps, + ListItemContextShape { checkboxList?: boolean; radioList?: boolean; tag?: string; activated?: boolean; selected?: boolean; ref?: React.Ref; -}; +} export interface ListItemState { tabIndex?: number; } -export class ListItemBase extends React.Component< - ListItemProps, ListItemState> { +export class ListItemBase< + T extends HTMLElement = HTMLElement +> extends React.Component, ListItemState> { private listItemElement = React.createRef(); static defaultProps: Partial> = { @@ -68,10 +71,15 @@ export class ListItemBase extends React.Com get listElements(): Element[] { if (this.listItemElement.current) { - const listElement = closest(this.listItemElement.current, `.${MDCListFoundation.cssClasses.ROOT}`); + const listElement = closest( + this.listItemElement.current, + `.${MDCListFoundation.cssClasses.ROOT}` + ); if (!listElement) return []; return [].slice.call( - listElement.querySelectorAll(MDCListFoundation.strings.ENABLED_ITEMS_SELECTOR) + listElement.querySelectorAll( + MDCListFoundation.strings.ENABLED_ITEMS_SELECTOR + ) ); } return []; @@ -95,7 +103,13 @@ export class ListItemBase extends React.Com } get classes() { - const {className, activated, disabled, selected, getClassNamesFromList} = this.props; + const { + className, + activated, + disabled, + selected, + getClassNamesFromList, + } = this.props; let classesFromList = ['']; if (this.listItemElement.current) { const index = this.getIndex(this.listItemElement.current); @@ -126,39 +140,39 @@ export class ListItemBase extends React.Com const tabIndex = this.props.getListItemInitialTabIndex!(index); this.setState({tabIndex}); } - } + }; getIndex = (listElement: Element) => { return this.listElements.indexOf(listElement); - } + }; handleClick = (e: React.MouseEvent) => { const {onClick} = this.props; onClick!(e); this.props.handleClick!(e, this.getIndex(e.currentTarget)); - } + }; handleKeyDown = (e: React.KeyboardEvent) => { const {onKeyDown} = this.props; onKeyDown!(e); this.props.handleKeyDown!(e, this.getIndex(e.currentTarget)); - } + }; handleFocus = (e: React.FocusEvent) => { const {onFocus} = this.props; onFocus!(e); this.props.handleFocus!(e, this.getIndex(e.currentTarget)); - } + }; handleBlur = (e: React.FocusEvent) => { const {onBlur} = this.props; onBlur!(e); this.props.handleBlur!(e, this.getIndex(e.currentTarget)); - } + }; render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ className, children, role, @@ -176,7 +190,7 @@ export class ListItemBase extends React.Com getListItemInitialTabIndex, getClassNamesFromList, tabIndex, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ tag: Tag, ...otherProps } = this.props; @@ -205,9 +219,7 @@ export class ListItemBase extends React.Com const ListItem: React.FunctionComponent = (props) => { return ( - {(context) => ( - - )} + {(context) => } ); }; diff --git a/packages/list/ListItemGraphic.tsx b/packages/list/ListItemGraphic.tsx index 90a5c041e..74f80ae47 100644 --- a/packages/list/ListItemGraphic.tsx +++ b/packages/list/ListItemGraphic.tsx @@ -28,10 +28,10 @@ export interface ListItemGraphicProps { tabIndex?: number; graphic: React.ReactElement; childrenTabIndex?: number; -}; +} -const ListItemGraphic:React.FunctionComponent = ({ - tabIndex = -1, // eslint-disable-line no-unused-vars +const ListItemGraphic: React.FunctionComponent = ({ + tabIndex = -1, // eslint-disable-line @typescript-eslint/no-unused-vars graphic, className = '', ...otherProps diff --git a/packages/list/ListItemMeta.tsx b/packages/list/ListItemMeta.tsx index 8fc065bdc..4617b8fc2 100644 --- a/packages/list/ListItemMeta.tsx +++ b/packages/list/ListItemMeta.tsx @@ -28,10 +28,10 @@ export interface ListItemMetaProps { tabIndex?: number; meta: string | React.ReactElement; childrenTabIndex?: number; -}; +} -const ListItemMeta:React.FunctionComponent = ({ - tabIndex, // eslint-disable-line no-unused-vars +const ListItemMeta: React.FunctionComponent = ({ + tabIndex, // eslint-disable-line @typescript-eslint/no-unused-vars meta, className = '', ...otherProps @@ -43,7 +43,11 @@ const ListItemMeta:React.FunctionComponent = ({ metaElement = meta; } const metaProps = { - className: classnames('mdc-list-item__meta', className, metaElement.props.className), + className: classnames( + 'mdc-list-item__meta', + className, + metaElement.props.className + ), tabIndex: tabIndex !== undefined ? tabIndex : -1, ...otherProps, }; diff --git a/packages/list/ListItemText.tsx b/packages/list/ListItemText.tsx index fc5ab8345..5e781330d 100644 --- a/packages/list/ListItemText.tsx +++ b/packages/list/ListItemText.tsx @@ -29,7 +29,7 @@ export interface ListItemTextProps { primaryText?: React.ReactNode; secondaryText?: React.ReactNode; childrenTabIndex?: number; -}; +} // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/14064 function isReactElement(element: any): element is React.ReactElement { @@ -37,12 +37,10 @@ function isReactElement(element: any): element is React.ReactElement { } const ListItemText: React.FunctionComponent = ({ - /* eslint-disable react/prop-types */ primaryText = '', secondaryText = '', tabIndex = -1, className = '', - /* eslint-enable react/prop-types */ ...otherProps }) => { const renderText = (text: React.ReactNode, className: string) => { diff --git a/packages/list/index.tsx b/packages/list/index.tsx index ff5b7ac9c..06adaf530 100644 --- a/packages/list/index.tsx +++ b/packages/list/index.tsx @@ -28,7 +28,7 @@ import {MDCListAdapter} from '@material/list/adapter'; // @ts-ignore @types cannot be used on dist files import memoizeOne from 'memoize-one/dist/memoize-one.cjs.js'; -import ListItem, {ListItemProps} from './ListItem'; // eslint-disable-line no-unused-vars +import ListItem, {ListItemProps} from './ListItem'; // eslint-disable-line @typescript-eslint/no-unused-vars import ListItemGraphic from './ListItemGraphic'; import ListItemText from './ListItemText'; import ListItemMeta from './ListItemMeta'; @@ -52,7 +52,7 @@ export interface ListProps extends React.HTMLProps { wrapFocus?: boolean; tag?: string; ref?: React.Ref; -}; +} interface ListState { listItemClassNames: {[listItemIndex: number]: string[]}; @@ -68,11 +68,16 @@ export interface ListItemContextShape { onDestroy?: (index: number) => void; getListItemInitialTabIndex?: (index: number) => number; getClassNamesFromList?: () => ListState['listItemClassNames']; - tabIndex?: number + tabIndex?: number; } -function isSelectedIndexType(selectedIndex: unknown): selectedIndex is MDCListIndex { - return typeof selectedIndex === 'number' && !isNaN(selectedIndex) || Array.isArray(selectedIndex); +function isSelectedIndexType( + selectedIndex: unknown +): selectedIndex is MDCListIndex { + return ( + (typeof selectedIndex === 'number' && !isNaN(selectedIndex)) || + Array.isArray(selectedIndex) + ); } export const defaultListItemContext: ListItemContextShape = { @@ -98,19 +103,19 @@ export default class List extends React.Component { }; static defaultProps: Partial = { - 'className': '', - 'checkboxList': false, - 'radioList': false, - 'nonInteractive': false, - 'dense': false, - 'avatarList': false, - 'twoLine': false, - 'singleSelection': false, - 'selectedIndex': -1, - 'handleSelect': () => {}, - 'wrapFocus': true, + className: '', + checkboxList: false, + radioList: false, + nonInteractive: false, + dense: false, + avatarList: false, + twoLine: false, + singleSelection: false, + selectedIndex: -1, + handleSelect: () => {}, + wrapFocus: true, 'aria-orientation': VERTICAL, - 'tag': 'ul', + tag: 'ul', }; componentDidMount() { @@ -157,23 +162,34 @@ export default class List extends React.Component { const {cssClasses, strings} = MDCListFoundation; if (!this.listElement.current) return; - const checkboxListItems = this.listElement.current.querySelectorAll(strings.ARIA_ROLE_CHECKBOX_SELECTOR); - const radioSelectedListItem = this.listElement.current.querySelector(strings.ARIA_CHECKED_RADIO_SELECTOR); + const checkboxListItems = this.listElement.current.querySelectorAll( + strings.ARIA_ROLE_CHECKBOX_SELECTOR + ); + const radioSelectedListItem = this.listElement.current.querySelector( + strings.ARIA_CHECKED_RADIO_SELECTOR + ); if (checkboxListItems.length) { - const preselectedItems = this.listElement.current.querySelectorAll(strings.ARIA_CHECKED_CHECKBOX_SELECTOR); - const selectedIndex = - [].map.call(preselectedItems, (listItem: Element) => this.listElements.indexOf(listItem)) as number[]; + const preselectedItems = this.listElement.current.querySelectorAll( + strings.ARIA_CHECKED_CHECKBOX_SELECTOR + ); + const selectedIndex = [].map.call(preselectedItems, (listItem: Element) => + this.listElements.indexOf(listItem) + ) as number[]; this.foundation.setSelectedIndex(selectedIndex); } else if (singleSelection) { - const isActivated = this.listElement.current.querySelector(cssClasses.LIST_ITEM_ACTIVATED_CLASS); + const isActivated = this.listElement.current.querySelector( + cssClasses.LIST_ITEM_ACTIVATED_CLASS + ); if (isActivated) { this.foundation.setUseActivatedClass(true); } } else if (radioSelectedListItem) { - this.foundation.setSelectedIndex(this.listElements.indexOf(radioSelectedListItem)); + this.foundation.setSelectedIndex( + this.listElements.indexOf(radioSelectedListItem) + ); } - } + }; get listElements(): Element[] { if (this.listElement.current) { @@ -187,13 +203,7 @@ export default class List extends React.Component { } get classes() { - const { - className, - nonInteractive, - dense, - avatarList, - twoLine, - } = this.props; + const {className, nonInteractive, dense, avatarList, twoLine} = this.props; return classnames('mdc-list', className, { 'mdc-list--non-interactive': nonInteractive, 'mdc-list--dense': dense, @@ -205,7 +215,8 @@ export default class List extends React.Component { get adapter(): MDCListAdapter { return { getListItemCount: () => this.listElements.length, - getFocusedElementIndex: () => this.listElements.indexOf(document.activeElement as HTMLLIElement), + getFocusedElementIndex: () => + this.listElements.indexOf(document.activeElement as HTMLLIElement), setAttributeForElementIndex: (index, attr, value) => { const listItem = this.listElements[index]; if (listItem) { @@ -220,7 +231,10 @@ export default class List extends React.Component { */ addClassForElementIndex: (index, className) => { const {listItemClassNames} = this.state; - if (listItemClassNames[index] && listItemClassNames[index].indexOf(className) === -1) { + if ( + listItemClassNames[index] && + listItemClassNames[index].indexOf(className) === -1 + ) { listItemClassNames[index].push(className); } else { listItemClassNames[index] = [className]; @@ -243,10 +257,14 @@ export default class List extends React.Component { }, setTabIndexForListItemChildren: (listItemIndex, tabIndexValue) => { const listItem = this.listElements[listItemIndex]; - const selector = MDCListFoundation.strings.CHILD_ELEMENTS_TO_TOGGLE_TABINDEX; - const listItemChildren: Element[] = - [].slice.call(listItem.querySelectorAll(selector)); - listItemChildren.forEach((el) => el.setAttribute('tabindex', tabIndexValue)); + const selector = + MDCListFoundation.strings.CHILD_ELEMENTS_TO_TOGGLE_TABINDEX; + const listItemChildren: Element[] = [].slice.call( + listItem.querySelectorAll(selector) + ); + listItemChildren.forEach((el) => + el.setAttribute('tabindex', tabIndexValue) + ); }, focusItemAtIndex: (index) => { const element = this.listElements[index] as HTMLElement | undefined; @@ -262,11 +280,15 @@ export default class List extends React.Component { }, hasCheckboxAtIndex: (index) => { const listItem = this.listElements[index]; - return !!listItem.querySelector(MDCListFoundation.strings.CHECKBOX_SELECTOR); + return !!listItem.querySelector( + MDCListFoundation.strings.CHECKBOX_SELECTOR + ); }, hasRadioAtIndex: (index) => { const listItem = this.listElements[index]; - return !!listItem.querySelector(MDCListFoundation.strings.RADIO_SELECTOR); + return !!listItem.querySelector( + MDCListFoundation.strings.RADIO_SELECTOR + ); }, isCheckboxCheckedAtIndex: (index) => { const listItem = this.listElements[index]; @@ -306,8 +328,10 @@ export default class List extends React.Component { const {selectedIndex} = this.props; let tabIndex = -1; if (!this.hasInitializedListItemTabIndex) { - const isSelectedIndexArray - = Array.isArray(selectedIndex) && selectedIndex.length > 0 && index === selectedIndex[0]; + const isSelectedIndexArray = + Array.isArray(selectedIndex) && + selectedIndex.length > 0 && + index === selectedIndex[0]; const isSelected = selectedIndex === index; if (isSelectedIndexArray || isSelected || selectedIndex === -1) { tabIndex = 0; @@ -316,7 +340,7 @@ export default class List extends React.Component { } return tabIndex; - } + }; /** * Method checks if the list item at `index` contains classes. If true, @@ -326,7 +350,7 @@ export default class List extends React.Component { private getListItemClassNames = () => { const {listItemClassNames} = this.state; return listItemClassNames; - } + }; handleKeyDown = (e: React.KeyboardEvent, index: number) => { e.persist(); // Persist the synthetic event to access its `key` @@ -363,7 +387,6 @@ export default class List extends React.Component { this.setState({listItemClassNames}); }; - private getListProps = (checkboxList?: boolean, radioList?: boolean) => ({ checkboxList: Boolean(checkboxList), radioList: Boolean(radioList), @@ -376,14 +399,13 @@ export default class List extends React.Component { getListItemInitialTabIndex: this.getListItemInitialTabIndex, }); - // decreases rerenders // https://overreacted.io/writing-resilient-components/#dont-stop-the-data-flow-in-rendering getListPropsMemoized = memoizeOne(this.getListProps); render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ className, checkboxList, radioList, @@ -396,7 +418,7 @@ export default class List extends React.Component { selectedIndex, handleSelect, wrapFocus, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ children, tag: Tag, ...otherProps @@ -411,7 +433,9 @@ export default class List extends React.Component { role={this.role} {...otherProps} > - + {children} diff --git a/packages/material-icon/index.tsx b/packages/material-icon/index.tsx index cfab0806a..1c79df8f9 100644 --- a/packages/material-icon/index.tsx +++ b/packages/material-icon/index.tsx @@ -24,37 +24,44 @@ import React from 'react'; import classnames from 'classnames'; import {withRipple, InjectedProps} from '@material/react-ripple'; -export interface MaterialIconDefaultProps extends React.HTMLAttributes { +export interface MaterialIconDefaultProps + extends React.HTMLAttributes { icon?: string; className?: string; hasRipple?: boolean; unbounded?: boolean; -}; +} -export interface MaterialIconProps extends MaterialIconDefaultProps, InjectedProps {}; +export interface MaterialIconProps + extends MaterialIconDefaultProps, + InjectedProps {} const MaterialIconDefault: React.FunctionComponent = ({ - className, icon, initRipple, hasRipple, unbounded, ...otherProps // eslint-disable-line react/prop-types + className, + icon, + initRipple, + hasRipple, + unbounded, // eslint-disable-line @typescript-eslint/no-unused-vars + ...otherProps }) => { const classes = classnames('material-icons', className, { 'material-icons--ripple-surface': hasRipple, }); return ( - + {icon} ); }; -export const RippleMaterialIcon = withRipple(MaterialIconDefault); +export const RippleMaterialIcon = withRipple( + MaterialIconDefault +); export default class MaterialIcon extends React.Component< - MaterialIconProps, {} - > { + MaterialIconProps, + {} +> { static defaultProps: Partial = { icon: '', className: '', @@ -63,22 +70,12 @@ export default class MaterialIcon extends React.Component< }; render() { - const {icon, hasRipple, unbounded, ...otherProps} = this.props; // eslint-disable-line no-unused-vars + const {icon, hasRipple, unbounded, ...otherProps} = this.props; // eslint-disable-line @typescript-eslint/no-unused-vars if (hasRipple) { return ( - + ); } - return ( - - ); + return ; } } diff --git a/packages/menu-surface/index.tsx b/packages/menu-surface/index.tsx index fea27f00d..a0a703855 100644 --- a/packages/menu-surface/index.tsx +++ b/packages/menu-surface/index.tsx @@ -48,7 +48,7 @@ export interface MenuSurfaceProps extends React.HTMLProps { quickOpen?: boolean; open?: boolean; fixed?: boolean; -}; +} export interface MenuSurfaceState { transformOrigin: string; @@ -59,7 +59,7 @@ export interface MenuSurfaceState { styleBottom?: number; classList: Set; mounted: boolean; -}; +} class MenuSurface extends React.Component { menuSurfaceElement: React.RefObject = React.createRef(); @@ -103,7 +103,8 @@ class MenuSurface extends React.Component { fixed, quickOpen, } = this.props; - this.handleWindowClick = (evt: MouseEvent) => this.foundation.handleBodyClick(evt); + this.handleWindowClick = (evt: MouseEvent) => + this.foundation.handleBodyClick(evt); this.registerWindowClickListener = () => window.addEventListener('click', this.handleWindowClick!); this.deregisterWindowClickListener = () => @@ -134,7 +135,10 @@ class MenuSurface extends React.Component { // if this.props.open was true for the initial render // then it will not be changed but the mounted state will be, // so this.open_() should also be called in that case. - if (this.props.open !== prevProps.open || (this.props.open && this.state.mounted !== prevState.mounted)) { + if ( + this.props.open !== prevProps.open || + (this.props.open && this.state.mounted !== prevState.mounted) + ) { this.open_(); } if (this.props.coordinates !== prevProps.coordinates) { @@ -219,8 +223,9 @@ class MenuSurface extends React.Component { this.lastFocusableElement === document.activeElement, focusFirstElement: () => { if (!this.firstFocusableElement) return false; - return this.firstFocusableElement.focus && - this.firstFocusableElement.focus(); + return ( + this.firstFocusableElement.focus && this.firstFocusableElement.focus() + ); }, focusLastElement: () => this.lastFocusableElement && @@ -251,12 +256,14 @@ class MenuSurface extends React.Component { return {x: window.pageXOffset, y: window.pageYOffset}; }, setPosition: (position: Partial) => { - this.setState((prevState) => Object.assign(prevState, { - styleLeft: 'left' in position ? position.left : null, - styleRight: 'right' in position ? position.right : null, - styleTop: 'top' in position ? position.top : null, - styleBottom: 'bottom' in position ? position.bottom : null, - })); + this.setState((prevState) => + Object.assign(prevState, { + styleLeft: 'left' in position ? position.left : null, + styleRight: 'right' in position ? position.right : null, + styleTop: 'top' in position ? position.top : null, + styleBottom: 'bottom' in position ? position.bottom : null, + }) + ); }, setMaxHeight: (maxHeight: string) => this.setState({maxHeight}), }; @@ -272,7 +279,8 @@ class MenuSurface extends React.Component { classList.delete(className); this.setState({classList}); }, - hasClass: (className: string) => this.classes.split(' ').includes(className), + hasClass: (className: string) => + this.classes.split(' ').includes(className), hasAnchor: () => !!this.props.anchorElement, notifyOpen: () => { this.handleOpen(); @@ -289,11 +297,14 @@ class MenuSurface extends React.Component { isRtl: () => { if (!this.menuSurfaceElement) return false; if (!this.menuSurfaceElement.current) return false; - return window - .getComputedStyle(this.menuSurfaceElement.current) - .getPropertyValue('direction') === 'rtl'; + return ( + window + .getComputedStyle(this.menuSurfaceElement.current) + .getPropertyValue('direction') === 'rtl' + ); }, - setTransformOrigin: (transformOrigin: string) => this.setState({transformOrigin}), + setTransformOrigin: (transformOrigin: string) => + this.setState({transformOrigin}), ...focusAdapterMethods, ...dimensionAdapterMethods, }; @@ -306,10 +317,12 @@ class MenuSurface extends React.Component { MDCMenuSurfaceFoundation.strings.FOCUSABLE_ELEMENTS ); this.firstFocusableElement = - focusableElements.length > 0 ? focusableElements[0] as HTMLElement : null; + focusableElements.length > 0 + ? (focusableElements[0] as HTMLElement) + : null; this.lastFocusableElement = focusableElements.length > 0 - ? focusableElements[focusableElements.length - 1] as HTMLElement + ? (focusableElements[focusableElements.length - 1] as HTMLElement) : null; this.foundation.open(); } else { @@ -326,13 +339,13 @@ class MenuSurface extends React.Component { if (this.registerWindowClickListener) { this.registerWindowClickListener(); } - } + }; private handleClose = () => { if (this.deregisterWindowClickListener) { this.deregisterWindowClickListener(); } - } + }; render() { const { diff --git a/packages/menu/MenuList.tsx b/packages/menu/MenuList.tsx index 7a999d40e..cedd9a801 100644 --- a/packages/menu/MenuList.tsx +++ b/packages/menu/MenuList.tsx @@ -22,7 +22,7 @@ import * as React from 'react'; import List, {ListProps} from '@material/react-list'; -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line @typescript-eslint/no-unused-vars import {MDCMenuFoundation} from '@material/menu/foundation'; type RefCallback = (node: T | null) => void; @@ -30,7 +30,7 @@ type RefCallback = (node: T | null) => void; export interface MenuListProps extends Exclude { innerRef?: RefCallback | React.RefObject; handleItemAction?: MDCMenuFoundation['handleItemAction']; -}; +} class MenuList extends React.Component { private listInstance = React.createRef(); @@ -51,8 +51,7 @@ class MenuList extends React.Component { handleSelect: ListProps['handleSelect'] = (activatedItemIndex, selected) => { this.props.handleSelect!(activatedItemIndex, selected); this.props.handleItemAction!(this.listElements[activatedItemIndex]); - } - + }; attachRef = (node: List | null) => { const {innerRef} = this.props; @@ -71,19 +70,19 @@ class MenuList extends React.Component { } else { innerRef(node); } - } + }; render() { const { 'aria-hidden': ariaHidden, - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ handleSelect, handleItemAction, role, innerRef, children, ref, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ ...otherProps } = this.props; diff --git a/packages/menu/MenuListItem.tsx b/packages/menu/MenuListItem.tsx index 66ce8d577..62ee93e78 100644 --- a/packages/menu/MenuListItem.tsx +++ b/packages/menu/MenuListItem.tsx @@ -23,23 +23,20 @@ import * as React from 'react'; import {ListItem, ListItemProps} from '@material/react-list'; -export interface MenuListItemProps extends ListItemProps { +export interface MenuListItemProps + extends ListItemProps { children?: React.ReactNode; } -class MenuListItem extends React.Component, {}> { +class MenuListItem extends React.Component< + MenuListItemProps, + {} +> { render() { - const { - role = 'menuitem', - children, - ...otherProps - } = this.props; + const {role = 'menuitem', children, ...otherProps} = this.props; return ( - + {children} ); diff --git a/packages/menu/index.tsx b/packages/menu/index.tsx index 096e24b52..6500abccf 100644 --- a/packages/menu/index.tsx +++ b/packages/menu/index.tsx @@ -34,12 +34,12 @@ export interface MenuProps extends MenuSurfaceProps { children: React.ReactElement; onSelected?: (index: number, item: Element) => void; ref?: React.Ref; -}; +} export interface MenuState { open: boolean; foundation?: MDCMenuFoundation; -}; +} class Menu extends React.Component { menuListElement = React.createRef(); @@ -77,9 +77,13 @@ class Menu extends React.Component { } get listElements(): Element[] { - if (!(this.menuListElement.current - && this.menuListElement.current.listElements - && this.menuListElement.current.listElements.length >= 0 )) { + if ( + !( + this.menuListElement.current && + this.menuListElement.current.listElements && + this.menuListElement.current.listElements.length >= 0 + ) + ) { return []; } return this.menuListElement.current.listElements; @@ -106,18 +110,21 @@ class Menu extends React.Component { const list = this.listElements; list[index].removeAttribute(attr); }, - elementContainsClass: (element, className) => element.classList.contains(className), + elementContainsClass: (element, className) => + element.classList.contains(className), closeSurface: () => this.setState({open: false}), getElementIndex: (element) => this.listElements.indexOf(element), getParentElement: (element) => element.parentElement, getSelectedElementIndex: (selectionGroup) => { - const selectedListItem = selectionGroup.querySelector(`.${cssClasses.MENU_SELECTED_LIST_ITEM}`); - return selectedListItem ? this.listElements.indexOf(selectedListItem) : -1; + const selectedListItem = selectionGroup.querySelector( + `.${cssClasses.MENU_SELECTED_LIST_ITEM}` + ); + return selectedListItem + ? this.listElements.indexOf(selectedListItem) + : -1; }, - notifySelected: (evtData) => this.props.onSelected!( - evtData.index, - this.listElements[evtData.index], - ), + notifySelected: (evtData) => + this.props.onSelected!(evtData.index, this.listElements[evtData.index]), }; } @@ -126,24 +133,25 @@ class Menu extends React.Component { if (onKeyDown) { onKeyDown(evt); } - this.state.foundation && this.state.foundation.handleKeydown(evt.nativeEvent); - } + this.state.foundation && + this.state.foundation.handleKeydown(evt.nativeEvent); + }; handleOpen: MenuSurfaceProps['onOpen'] = () => { const {onOpen} = this.props; onOpen && onOpen(); - } + }; render() { const { className, - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ open, onKeyDown, onOpen, children, onSelected, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ ...otherProps } = this.props; return ( diff --git a/packages/notched-outline/index.tsx b/packages/notched-outline/index.tsx index 155240746..0bbbf1bf5 100644 --- a/packages/notched-outline/index.tsx +++ b/packages/notched-outline/index.tsx @@ -31,17 +31,17 @@ export interface NotchedOutlineProps extends React.HTMLProps { className?: string; notchWidth?: number; notch?: boolean; -}; +} interface NotchedOutlineState { classList: Set; foundationNotchWidth?: number; -}; +} export default class NotchedOutline extends React.Component< NotchedOutlineProps, NotchedOutlineState - > { +> { foundation?: MDCNotchedOutlineFoundation; notchedEl = React.createRef(); @@ -79,8 +79,10 @@ export default class NotchedOutline extends React.Component< } componentDidUpdate(prevProps: NotchedOutlineProps) { - if (this.props.notchWidth !== prevProps.notchWidth - || this.props.notch !== prevProps.notch) { + if ( + this.props.notchWidth !== prevProps.notchWidth || + this.props.notch !== prevProps.notch + ) { this.notch(); } } @@ -97,7 +99,9 @@ export default class NotchedOutline extends React.Component< if (!this.notchedEl.current) { return null; } - return this.notchedEl.current.querySelector(`.${MDCFloatingLabelFoundation.cssClasses.ROOT}`); + return this.notchedEl.current.querySelector( + `.${MDCFloatingLabelFoundation.cssClasses.ROOT}` + ); } get adapter(): MDCNotchedOutlineAdapter { @@ -110,8 +114,10 @@ export default class NotchedOutline extends React.Component< return {classList}; }); }, - setNotchWidthProperty: (foundationNotchWidth) => this.setState({foundationNotchWidth}), - removeNotchWidthProperty: () => this.setState({foundationNotchWidth: undefined}), + setNotchWidthProperty: (foundationNotchWidth) => + this.setState({foundationNotchWidth}), + removeNotchWidthProperty: () => + this.setState({foundationNotchWidth: undefined}), }; } @@ -121,7 +127,7 @@ export default class NotchedOutline extends React.Component< classList.add(className); return {classList}; }); - } + }; notch = () => { const {notchWidth, notch} = this.props; @@ -131,37 +137,33 @@ export default class NotchedOutline extends React.Component< } else { this.foundation.closeNotch(); } - } + }; render() { const { children, - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ className, notchWidth, notch, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ ...otherProps } = this.props; const {foundationNotchWidth} = this.state; const notchStyle = { - width: foundationNotchWidth ? `${foundationNotchWidth}px`: undefined, + width: foundationNotchWidth ? `${foundationNotchWidth}px` : undefined, }; return ( -
-
- {children ? +
+
+ {children ? (
{React.Children.only(children)}
- : null} -
+ ) : null} +
); } diff --git a/packages/radio/NativeControl.tsx b/packages/radio/NativeControl.tsx index 4b323763a..da048220e 100644 --- a/packages/radio/NativeControl.tsx +++ b/packages/radio/NativeControl.tsx @@ -24,12 +24,14 @@ import React from 'react'; import classnames from 'classnames'; export interface NativeControlProps extends React.HTMLProps { - className?: string, - rippleActivatorRef?: React.RefObject -}; + className?: string; + rippleActivatorRef?: React.RefObject; +} const NativeControl: React.FunctionComponent = ({ - rippleActivatorRef, className = '', ...otherProps // eslint-disable-line react/prop-types + rippleActivatorRef, + className = '', + ...otherProps }) => { return ( , React.HTMLProps { - label?: string; - initRipple: (surface: HTMLDivElement, rippleActivatorRef?: HTMLInputElement) => void; - wrapperClasses?: string; - children: React.ReactElement; + extends InjectedProps, + React.HTMLProps { + label?: string; + initRipple: ( + surface: HTMLDivElement, + rippleActivatorRef?: HTMLInputElement + ) => void; + wrapperClasses?: string; + children: React.ReactElement; } interface RadioState { @@ -121,19 +125,20 @@ class Radio extends React.Component { classList.delete(className); this.setState({classList}); }, - setNativeControlDisabled: (disabled: boolean) => this.setState({disabled}), + setNativeControlDisabled: (disabled: boolean) => + this.setState({disabled}), }; } render() { const {nativeControlId} = this.state; const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ label, initRipple, unbounded, className, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ wrapperClasses, ...otherProps } = this.props; diff --git a/packages/ripple/index.tsx b/packages/ripple/index.tsx index fa7df7bec..0c094f37b 100644 --- a/packages/ripple/index.tsx +++ b/packages/ripple/index.tsx @@ -21,7 +21,7 @@ // THE SOFTWARE. import React from 'react'; import classnames from 'classnames'; -import {Subtract} from 'utility-types'; // eslint-disable-line no-unused-vars +import {Subtract} from 'utility-types'; // eslint-disable-line @typescript-eslint/no-unused-vars import {MDCRippleFoundation} from '@material/ripple/foundation'; import {MDCRippleAdapter} from '@material/ripple/adapter'; @@ -51,12 +51,18 @@ export interface RippledComponentState { } // props to be injected by this HOC. -export interface InjectedProps extends RippledComponentProps { - initRipple?: React.Ref | ((surface: S | null, activator?: A | null) => void); +export interface InjectedProps + extends RippledComponentProps { + initRipple?: + | React.Ref + | ((surface: S | null, activator?: A | null) => void); } -type ActivateEventTypes - = React.MouseEvent | React.TouchEvent | React.KeyboardEvent | React.FocusEvent; +type ActivateEventTypes = + | React.MouseEvent + | React.TouchEvent + | React.KeyboardEvent + | React.FocusEvent; export interface RippledComponentInterface { foundation?: MDCRippleFoundation; @@ -84,236 +90,255 @@ export interface RippledComponentInterface { } // This is an HOC that adds Ripple to the component passed as an argument -export function withRipple < +export function withRipple< P extends InjectedProps, Surface extends Element = Element, Activator extends Element = Element ->(WrappedComponent: React.ComponentType

): - React.ComponentType> - & RippledComponentProps> { - return class RippledComponent extends React.Component< - // Subtract removes any props "InjectedProps" if they are on "P" - // This allows the developer to override any props - // https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb - Subtract> & RippledComponentProps, - RippledComponentState - > implements RippledComponentInterface { - foundation!: MDCRippleFoundation; - isComponentMounted: boolean = true; - isTouched: boolean = false; - - displayName = `WithRipple(${getDisplayName

(WrappedComponent)})`; - - state: RippledComponentState = { - classList: new Set(), - style: {}, - }; - - static defaultProps: Partial> = { - unbounded: false, - disabled: false, - style: {}, - className: '', - onMouseDown: () => {}, - onMouseUp: () => {}, - onTouchStart: () => {}, - onTouchEnd: () => {}, - onKeyDown: () => {}, - onKeyUp: () => {}, - onFocus: () => {}, - onBlur: () => {}, - ...WrappedComponent.defaultProps, - }; - - componentDidMount() { - if (!this.foundation) { - throw new Error( - 'You must call initRipple from the element\'s ' + - 'ref prop to initialize the adapter for withRipple' - ); +>( + WrappedComponent: React.ComponentType

+): React.ComponentType< + Subtract> & + RippledComponentProps +> { + return class RippledComponent + extends React.Component< + // Subtract removes any props "InjectedProps" if they are on "P" + // This allows the developer to override any props + // https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb + Subtract> & + RippledComponentProps, + RippledComponentState + > + implements RippledComponentInterface { + foundation!: MDCRippleFoundation; + isComponentMounted: boolean = true; + isTouched: boolean = false; + + displayName = `WithRipple(${getDisplayName

(WrappedComponent)})`; + + state: RippledComponentState = { + classList: new Set(), + style: {}, + }; + + static defaultProps: Partial> = { + unbounded: false, + disabled: false, + style: {}, + className: '', + onMouseDown: () => {}, + onMouseUp: () => {}, + onTouchStart: () => {}, + onTouchEnd: () => {}, + onKeyDown: () => {}, + onKeyUp: () => {}, + onFocus: () => {}, + onBlur: () => {}, + ...WrappedComponent.defaultProps, + }; + + componentDidMount() { + if (!this.foundation) { + throw new Error( + "You must call initRipple from the element's " + + 'ref prop to initialize the adapter for withRipple' + ); + } } - } - componentWillUnmount() { - if (this.foundation) { - this.isComponentMounted = false; - this.foundation.destroy(); + componentWillUnmount() { + if (this.foundation) { + this.isComponentMounted = false; + this.foundation.destroy(); + } } - } - - // surface: This element receives the visual treatment (classes and style) of the ripple. - // activator: This element is used to detect whether to activate the ripple. If this is not - // provided, the ripple surface will be used to detect activation. - initializeFoundation = (surface: Surface, activator?: Activator) => { - const adapter = this.createAdapter(surface, activator); - this.foundation = new MDCRippleFoundation(adapter); - this.foundation.init(); - }; - - createAdapter = (surface: Surface, activator?: Activator): MDCRippleAdapter => { - return { - browserSupportsCssVars: () => supportsCssVariables(window), - isUnbounded: () => this.props.unbounded! as boolean, - isSurfaceActive: () => { - if (activator) { - return matches(activator, ':active'); - } - return matches(surface, ':active'); - }, - isSurfaceDisabled: () => this.props.disabled as boolean, - addClass: (className: string) => { - if (!this.isComponentMounted) { - return; - } - this.setState({classList: this.state.classList.add(className)}); - }, - removeClass: (className: string) => { - if (!this.isComponentMounted) { - return; - } - const {classList} = this.state; - classList.delete(className); - this.setState({classList}); - }, - registerDocumentInteractionHandler: (evtType: string, handler: EventListener) => - document.documentElement.addEventListener( - evtType, - handler, - applyPassive() - ), - deregisterDocumentInteractionHandler: (evtType: string, handler: EventListener) => - document.documentElement.removeEventListener( - evtType, - handler, - applyPassive() - ), - registerResizeHandler: (handler: SpecificEventListener<'resize'>) => - window.addEventListener('resize', handler), - deregisterResizeHandler: (handler: SpecificEventListener<'resize'>) => - window.removeEventListener('resize', handler), - updateCssVariable: this.updateCssVariable, - computeBoundingRect: () => { - if (!this.isComponentMounted) { - // need to return object since foundation expects it - // new DOMRect is not IE11 compatible - const defaultDOMRect = { - bottom: 0, - height: 0, - left: 0, - right: 0, - top: 0, - width: 0, - x: 0, - y: 0, - }; - return defaultDOMRect; - } - if (this.props.computeBoundingRect) { - return this.props.computeBoundingRect(surface); - } - return surface.getBoundingClientRect(); - }, - containsEventTarget: (target: EventTarget | null) => { - if (activator && target !== null) { - return activator.contains(target as Node); - } - return false; - }, - registerInteractionHandler: () => null, - deregisterInteractionHandler: () => null, - getWindowPageOffset: () => ({ - x: window.pageXOffset, - y: window.pageYOffset, - }), + // surface: This element receives the visual treatment (classes and style) of the ripple. + // activator: This element is used to detect whether to activate the ripple. If this is not + // provided, the ripple surface will be used to detect activation. + initializeFoundation = (surface: Surface, activator?: Activator) => { + const adapter = this.createAdapter(surface, activator); + this.foundation = new MDCRippleFoundation(adapter); + this.foundation.init(); + }; + + createAdapter = ( + surface: Surface, + activator?: Activator + ): MDCRippleAdapter => { + return { + browserSupportsCssVars: () => supportsCssVariables(window), + isUnbounded: () => this.props.unbounded! as boolean, + isSurfaceActive: () => { + if (activator) { + return matches(activator, ':active'); + } + + return matches(surface, ':active'); + }, + isSurfaceDisabled: () => this.props.disabled as boolean, + addClass: (className: string) => { + if (!this.isComponentMounted) { + return; + } + this.setState({classList: this.state.classList.add(className)}); + }, + removeClass: (className: string) => { + if (!this.isComponentMounted) { + return; + } + const {classList} = this.state; + classList.delete(className); + this.setState({classList}); + }, + registerDocumentInteractionHandler: ( + evtType: string, + handler: EventListener + ) => + document.documentElement.addEventListener( + evtType, + handler, + applyPassive() + ), + deregisterDocumentInteractionHandler: ( + evtType: string, + handler: EventListener + ) => + document.documentElement.removeEventListener( + evtType, + handler, + applyPassive() + ), + registerResizeHandler: (handler: SpecificEventListener<'resize'>) => + window.addEventListener('resize', handler), + deregisterResizeHandler: (handler: SpecificEventListener<'resize'>) => + window.removeEventListener('resize', handler), + updateCssVariable: this.updateCssVariable, + computeBoundingRect: () => { + if (!this.isComponentMounted) { + // need to return object since foundation expects it + // new DOMRect is not IE11 compatible + const defaultDOMRect = { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + x: 0, + y: 0, + }; + return defaultDOMRect; + } + if (this.props.computeBoundingRect) { + return this.props.computeBoundingRect(surface); + } + return surface.getBoundingClientRect(); + }, + containsEventTarget: (target: EventTarget | null) => { + if (activator && target !== null) { + return activator.contains(target as Node); + } + return false; + }, + registerInteractionHandler: () => null, + deregisterInteractionHandler: () => null, + getWindowPageOffset: () => ({ + x: window.pageXOffset, + y: window.pageYOffset, + }), + }; + }; + + handleFocus = (e: React.FocusEvent) => { + this.props.onFocus && this.props.onFocus(e); + this.foundation.handleFocus(); + }; + + handleBlur = (e: React.FocusEvent) => { + this.props.onBlur && this.props.onBlur(e); + this.foundation.handleBlur(); }; - }; - handleFocus = (e: React.FocusEvent) => { - this.props.onFocus && this.props.onFocus(e); - this.foundation.handleFocus(); - }; + handleMouseDown = (e: React.MouseEvent) => { + this.props.onMouseDown && this.props.onMouseDown(e); + if (!this.isTouched) { + this.activateRipple(e); + } + }; - handleBlur = (e: React.FocusEvent) => { - this.props.onBlur && this.props.onBlur(e); - this.foundation.handleBlur(); - }; + handleMouseUp = (e: React.MouseEvent) => { + this.props.onMouseUp && this.props.onMouseUp(e); + this.deactivateRipple(); + }; - handleMouseDown = (e: React.MouseEvent) => { - this.props.onMouseDown && this.props.onMouseDown(e); - if (!this.isTouched) { + handleTouchStart = (e: React.TouchEvent) => { + this.isTouched = true; + this.props.onTouchStart && this.props.onTouchStart(e); this.activateRipple(e); - } - }; - - handleMouseUp = (e: React.MouseEvent) => { - this.props.onMouseUp && this.props.onMouseUp(e); - this.deactivateRipple(); - }; - - handleTouchStart = (e: React.TouchEvent) => { - this.isTouched = true; - this.props.onTouchStart && this.props.onTouchStart(e); - this.activateRipple(e); - }; - - handleTouchEnd = (e: React.TouchEvent) => { - this.props.onTouchEnd && this.props.onTouchEnd(e); - this.deactivateRipple(); - }; - - handleKeyDown = (e: React.KeyboardEvent) => { - this.props.onKeyDown && this.props.onKeyDown(e); - this.activateRipple(e); - }; - - handleKeyUp = (e: React.KeyboardEvent) => { - this.props.onKeyUp && this.props.onKeyUp(e); - this.deactivateRipple(); - }; - - activateRipple = (e: ActivateEventTypes) => { - // https://reactjs.org/docs/events.html#event-pooling - e.persist(); - this.foundation.activate(e.nativeEvent); - }; - - deactivateRipple = () => { - this.foundation.deactivate(); - }; - - updateCssVariable = (varName: string, value: string | null) => { - if (!this.isComponentMounted) { - return; - } - this.setState((prevState) => { - const updatedStyle = Object.assign({}, this.state.style, prevState.style) as React.CSSProperties; - if (value === null) { - delete updatedStyle[varName as keyof React.CSSProperties]; - } else { - updatedStyle[varName as keyof React.CSSProperties] = value; + }; + + handleTouchEnd = (e: React.TouchEvent) => { + this.props.onTouchEnd && this.props.onTouchEnd(e); + this.deactivateRipple(); + }; + + handleKeyDown = (e: React.KeyboardEvent) => { + this.props.onKeyDown && this.props.onKeyDown(e); + this.activateRipple(e); + }; + + handleKeyUp = (e: React.KeyboardEvent) => { + this.props.onKeyUp && this.props.onKeyUp(e); + this.deactivateRipple(); + }; + + activateRipple = (e: ActivateEventTypes) => { + // https://reactjs.org/docs/events.html#event-pooling + e.persist(); + this.foundation.activate(e.nativeEvent); + }; + + deactivateRipple = () => { + this.foundation.deactivate(); + }; + + updateCssVariable = (varName: string, value: string | null) => { + if (!this.isComponentMounted) { + return; } + this.setState((prevState) => { + const updatedStyle = Object.assign( + {}, + this.state.style, + prevState.style + ) as React.CSSProperties; + if (value === null) { + delete updatedStyle[varName as keyof React.CSSProperties]; + } else { + updatedStyle[varName as keyof React.CSSProperties] = value; + } - return Object.assign(prevState, { - style: updatedStyle, + return Object.assign(prevState, { + style: updatedStyle, + }); }); - }); - }; - - get classes() { - const {className: wrappedComponentClasses} = this.props; - const {classList} = this.state; - return classnames(Array.from(classList), wrappedComponentClasses); - } - - get style() { - const {style: wrappedStyle} = this.props; - const {style} = this.state; - return Object.assign({}, style, wrappedStyle); - } - - render() { - const { + }; + + get classes() { + const {className: wrappedComponentClasses} = this.props; + const {classList} = this.state; + return classnames(Array.from(classList), wrappedComponentClasses); + } + + get style() { + const {style: wrappedStyle} = this.props; + const {style} = this.state; + return Object.assign({}, style, wrappedStyle); + } + + render() { + const { /* eslint-disable */ unbounded, style, @@ -327,38 +352,38 @@ export function withRipple < onFocus, onBlur, /* eslint-enable */ - ...otherProps - } = this.props as P; - - const updatedProps = { - ...otherProps, - onMouseDown: this.handleMouseDown, - onMouseUp: this.handleMouseUp, - onTouchStart: this.handleTouchStart, - onTouchEnd: this.handleTouchEnd, - onKeyDown: this.handleKeyDown, - onKeyUp: this.handleKeyUp, - onFocus: this.handleFocus, - onBlur: this.handleBlur, - initRipple: this.initializeFoundation, - className: this.classes, - style: this.style, - }; - - return ( - // this issue is only appearing in TS v3.2.x. I am not seeing this issue appear in v2.9.1 - // @ts-ignore - - ); - } - // there is explicit type provided for the return value of withRipple function - // without type "any" typescript raises an error - // The inferred type of ... cannot be named without a reference ... + ...otherProps + } = this.props as P; + + const updatedProps = { + ...otherProps, + onMouseDown: this.handleMouseDown, + onMouseUp: this.handleMouseUp, + onTouchStart: this.handleTouchStart, + onTouchEnd: this.handleTouchEnd, + onKeyDown: this.handleKeyDown, + onKeyUp: this.handleKeyUp, + onFocus: this.handleFocus, + onBlur: this.handleBlur, + initRipple: this.initializeFoundation, + className: this.classes, + style: this.style, + }; + + return ( + // this issue is only appearing in TS v3.2.x. I am not seeing this issue appear in v2.9.1 + // @ts-ignore + + ); + } + // there is explicit type provided for the return value of withRipple function + // without type "any" typescript raises an error + // The inferred type of ... cannot be named without a reference ... } as any; } -function getDisplayName

(WrappedComponent: React.ComponentType

): string { +function getDisplayName

( + WrappedComponent: React.ComponentType

+): string { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; } diff --git a/packages/select/BaseSelect.tsx b/packages/select/BaseSelect.tsx index f622deefe..c3e1e6d27 100644 --- a/packages/select/BaseSelect.tsx +++ b/packages/select/BaseSelect.tsx @@ -22,15 +22,16 @@ import * as React from 'react'; import NativeSelect, { - NativeSelectProps, // eslint-disable-line no-unused-vars + NativeSelectProps, // eslint-disable-line @typescript-eslint/no-unused-vars } from './NativeSelect'; import EnhancedSelect, { - EnhancedSelectProps, // eslint-disable-line no-unused-vars + EnhancedSelectProps, // eslint-disable-line @typescript-eslint/no-unused-vars } from './EnhancedSelect'; import {MDCSelectFoundation} from '@material/select/foundation'; -export type BaseSelectProps - = (T extends HTMLSelectElement ? NativeSelectProps : EnhancedSelectProps); +export type BaseSelectProps = T extends HTMLSelectElement + ? NativeSelectProps + : EnhancedSelectProps; export interface CommonSelectProps { enhanced: boolean; @@ -41,8 +42,9 @@ export interface CommonSelectProps { selectClassName?: string; } -export class BaseSelect - extends React.Component> { +export class BaseSelect< + T extends HTMLElement = HTMLSelectElement +> extends React.Component> { static defaultProps = { enhanced: false, selectClassName: '', @@ -70,7 +72,7 @@ export class BaseSelect foundation.handleClick(this.getNormalizedXCoordinate(evt)); } onTouchStart && onTouchStart(evt); - } + }; handleMouseDown = (evt: React.MouseEvent) => { const {foundation, onMouseDown} = this.props; @@ -78,7 +80,7 @@ export class BaseSelect foundation.handleClick(this.getNormalizedXCoordinate(evt)); } onMouseDown && onMouseDown(evt); - } + }; handleClick = (evt: React.MouseEvent) => { const {foundation, onClick} = this.props; @@ -86,7 +88,7 @@ export class BaseSelect foundation.handleClick(this.getNormalizedXCoordinate(evt)); } onClick && onClick(evt); - } + }; handleKeyDown = (evt: React.KeyboardEvent) => { const {foundation, onKeyDown} = this.props; @@ -94,31 +96,32 @@ export class BaseSelect foundation.handleKeydown(evt.nativeEvent); } onKeyDown && onKeyDown(evt); - } + }; private isTouchEvent = (evt: MouseEvent | TouchEvent): evt is TouchEvent => { return Boolean((evt as TouchEvent).touches); - } - - private getNormalizedXCoordinate - = (evt: React.MouseEvent | React.TouchEvent) => { - const targetClientRect = (evt.currentTarget as Element).getBoundingClientRect(); - const xCoordinate - = this.isTouchEvent(evt.nativeEvent) ? evt.nativeEvent.touches[0].clientX : evt.nativeEvent.clientX; - return xCoordinate - targetClientRect.left; - } + }; + private getNormalizedXCoordinate = ( + evt: React.MouseEvent | React.TouchEvent + ) => { + const targetClientRect = (evt.currentTarget as Element).getBoundingClientRect(); + const xCoordinate = this.isTouchEvent(evt.nativeEvent) + ? evt.nativeEvent.touches[0].clientX + : evt.nativeEvent.clientX; + return xCoordinate - targetClientRect.left; + }; render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ onFocus, onBlur, onClick, onMouseDown, onTouchStart, ref, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ enhanced, children, onKeyDown, @@ -138,19 +141,13 @@ export class BaseSelect if (enhanced) { return ( - + {children} ); } return ( - + {children} ); diff --git a/packages/select/EnhancedSelect.tsx b/packages/select/EnhancedSelect.tsx index eca0c0794..cb6130be2 100644 --- a/packages/select/EnhancedSelect.tsx +++ b/packages/select/EnhancedSelect.tsx @@ -23,7 +23,7 @@ import * as React from 'react'; import {MDCMenuSurfaceFoundation} from '@material/menu-surface/foundation'; import Menu, {MenuList} from '@material/react-menu'; -import {OptionProps} from './Option'; // eslint-disable-line no-unused-vars +import {OptionProps} from './Option'; // eslint-disable-line @typescript-eslint/no-unused-vars import {CommonSelectProps} from './BaseSelect'; import MDCSelectFoundation from '@material/select/foundation'; @@ -31,9 +31,13 @@ const {Corner} = MDCMenuSurfaceFoundation; const TRUE = 'true'; const FALSE = 'false'; -export type EnhancedChild = React.ReactElement>; +export type EnhancedChild = React.ReactElement< + OptionProps +>; -export interface EnhancedSelectProps extends CommonSelectProps, React.HTMLProps { +export interface EnhancedSelectProps + extends CommonSelectProps, + React.HTMLProps { closeMenu?: () => void; onEnhancedChange?: (index: number, item: Element) => void; anchorElement: HTMLElement | null; @@ -51,7 +55,7 @@ interface EnhancedSelectState { export default class EnhancedSelect extends React.Component< EnhancedSelectProps, EnhancedSelectState - > { +> { nativeControl: React.RefObject = React.createRef(); private selectedTextEl = React.createRef(); menuEl = React.createRef

(); @@ -67,9 +71,9 @@ export default class EnhancedSelect extends React.Component< state: EnhancedSelectState = { 'aria-expanded': undefined, - 'selectedItem': null, - 'selectedValue': '', - } + selectedItem: null, + selectedValue: '', + }; componentDidUpdate(prevProps: EnhancedSelectProps) { if (this.props.value !== prevProps.value) { @@ -82,22 +86,31 @@ export default class EnhancedSelect extends React.Component< } setSelected = () => { - const listElements = this.menuEl.current !== null && this.menuEl.current!.listElements; + const listElements = + this.menuEl.current !== null && this.menuEl.current!.listElements; if (!listElements || !listElements.length) return; const index = this.getIndexByValue(listElements); const selectedItem = listElements[index]; - const selectedValue - = selectedItem && selectedItem.getAttribute(MDCSelectFoundation.strings.ENHANCED_VALUE_ATTR) || ''; + const selectedValue = + (selectedItem && + selectedItem.getAttribute( + MDCSelectFoundation.strings.ENHANCED_VALUE_ATTR + )) || + ''; this.setState({selectedItem, selectedValue}); - } + }; private getIndexByValue = (listElements: Element[]) => { const {value} = this.props; let index = -1; if (index < 0 && value) { listElements.some((element: Element, elementIndex: number) => { - if (element.getAttribute(MDCSelectFoundation.strings.ENHANCED_VALUE_ATTR) === value) { + if ( + element.getAttribute( + MDCSelectFoundation.strings.ENHANCED_VALUE_ATTR + ) === value + ) { index = elementIndex; return true; } @@ -105,7 +118,7 @@ export default class EnhancedSelect extends React.Component< }); } return index; - } + }; private handleMenuClose = () => { const {closeMenu, foundation} = this.props; @@ -114,7 +127,7 @@ export default class EnhancedSelect extends React.Component< if (foundation && document.activeElement !== this.selectedTextEl.current) { foundation.handleBlur(); } - } + }; private handleMenuOpen = () => { this.setState({'aria-expanded': true}); @@ -124,7 +137,7 @@ export default class EnhancedSelect extends React.Component< const listItem = this.listElements[index]; (listItem as HTMLElement).focus(); } - } + }; render() { const { @@ -143,7 +156,11 @@ export default class EnhancedSelect extends React.Component< isInvalid, } = this.props; - const {'aria-expanded': ariaExpanded, selectedValue, selectedItem} = this.state; + const { + 'aria-expanded': ariaExpanded, + selectedValue, + selectedItem, + } = this.state; const selectedTextAttrs: {[key: string]: string} = {}; if (required) { diff --git a/packages/select/NativeSelect.tsx b/packages/select/NativeSelect.tsx index cf5ddc9f6..18ed8e2be 100644 --- a/packages/select/NativeSelect.tsx +++ b/packages/select/NativeSelect.tsx @@ -26,8 +26,12 @@ import {CommonSelectProps} from './BaseSelect'; type RefCallback = (node: T | null) => void; -export interface NativeSelectProps extends CommonSelectProps, React.HTMLProps { - innerRef?: RefCallback | React.RefObject; +export interface NativeSelectProps + extends CommonSelectProps, + React.HTMLProps { + innerRef?: + | RefCallback + | React.RefObject; value?: string; ref?: React.Ref; } @@ -35,7 +39,7 @@ export interface NativeSelectProps extends CommonSelectProps, React.HTMLProps { +> { NativeSelect: React.RefObject = React.createRef(); static defaultProps: Partial = { @@ -66,25 +70,21 @@ export default class NativeSelect extends React.Component< } else { innerRef(node); } - } + }; render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ className, children, foundation, innerRef, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ ...otherProps } = this.props; return ( - {children} ); diff --git a/packages/select/Option.tsx b/packages/select/Option.tsx index 5871274ef..565aec817 100644 --- a/packages/select/Option.tsx +++ b/packages/select/Option.tsx @@ -21,43 +21,37 @@ // THE SOFTWARE. import * as React from 'react'; -import {MenuListItem, MenuListItemProps} from '@material/react-menu'; // eslint-disable-line no-unused-vars +import {MenuListItem, MenuListItemProps} from '@material/react-menu'; // eslint-disable-line @typescript-eslint/no-unused-vars -export type OptionProps - = BaseOptionProps & (T extends HTMLOptionElement ? React.HTMLProps : MenuListItemProps); +export type OptionProps = BaseOptionProps & + (T extends HTMLOptionElement + ? React.HTMLProps + : MenuListItemProps); interface BaseOptionProps { enhanced?: boolean; } -class Option extends React.Component, {}> { +class Option extends React.Component< + OptionProps, + {} +> { static defaultProps = { enhanced: false, }; render() { - const { - value, - enhanced, - children, - ...otherProps - } = this.props; + const {value, enhanced, children, ...otherProps} = this.props; if (enhanced) { return ( - + {children} ); } return ( - ); diff --git a/packages/select/helper-text/index.tsx b/packages/select/helper-text/index.tsx index 90dda5a3e..ff99c6abb 100644 --- a/packages/select/helper-text/index.tsx +++ b/packages/select/helper-text/index.tsx @@ -25,27 +25,33 @@ import classnames from 'classnames'; import {MDCSelectHelperTextAdapter} from '@material/select/helper-text/adapter'; import {MDCSelectHelperTextFoundation} from '@material/select/helper-text/foundation'; -export interface SelectHelperTextProps extends React.HTMLProps { +export interface SelectHelperTextProps + extends React.HTMLProps { persistent?: boolean; - setHelperTextFoundation?: (foundation?: MDCSelectHelperTextFoundation) => void; + setHelperTextFoundation?: ( + foundation?: MDCSelectHelperTextFoundation + ) => void; } interface ElementAttributes { 'aria-hidden'?: boolean | 'false' | 'true'; role?: string; -}; +} interface SelectHelperTextState extends ElementAttributes { classList: Set; -}; +} -export class SelectHelperText extends React.Component { +export class SelectHelperText extends React.Component< + SelectHelperTextProps, + SelectHelperTextState +> { foundation?: MDCSelectHelperTextFoundation; state: SelectHelperTextState = { 'aria-hidden': undefined, - 'role': undefined, - 'classList': new Set(), + role: undefined, + classList: new Set(), }; componentDidMount() { @@ -66,9 +72,14 @@ export class SelectHelperText extends React.Component { return this.classes.split(' ').includes(className); }, - setAttr: (attr: keyof ElementAttributes, value: ElementAttributes[keyof ElementAttributes]) => { + setAttr: ( + attr: keyof ElementAttributes, + value: ElementAttributes[keyof ElementAttributes] + ) => { this.setState((prevState) => ({ ...prevState, [attr]: value, @@ -104,17 +118,14 @@ export class SelectHelperText extends React.Component { } interface ElementAttributes { - 'tabindex'?: number; + tabindex?: number; role?: string; -}; +} -interface SelectIconState extends ElementAttributes {}; +interface SelectIconState extends ElementAttributes {} -export class SelectIcon extends React.Component { +export class SelectIcon extends React.Component< + SelectIconProps, + SelectIconState +> { foundation?: MDCSelectIconFoundation; state: SelectIconState = { - 'tabindex': undefined, - 'role': undefined, + tabindex: undefined, + role: undefined, }; static defaultProps = { @@ -68,15 +71,20 @@ export class SelectIcon extends React.Component { if (this.state[attr] !== undefined) { - return (this.state[attr] as ElementAttributes[keyof ElementAttributes])!.toString(); + return (this.state[ + attr + ] as ElementAttributes[keyof ElementAttributes])!.toString(); } const reactAttr = attr === 'tabindex' ? 'tabIndex' : attr; if (this.props[reactAttr] !== undefined) { - return (this.props[reactAttr])!.toString(); + return this.props[reactAttr]!.toString(); } return null; }, - setAttr: (attr: keyof ElementAttributes, value: ElementAttributes[keyof ElementAttributes]) => { + setAttr: ( + attr: keyof ElementAttributes, + value: ElementAttributes[keyof ElementAttributes] + ) => { this.setState((prevState) => ({ ...prevState, [attr]: value, @@ -99,7 +107,7 @@ export class SelectIcon extends React.Component)[]; type NativeChild = React.ReactElement>; -type SelectChildren = EnhancedChild | EnhancedChild[] | NativeChild | NativeChild[]; +type SelectChildren = + | EnhancedChild + | EnhancedChild[] + | NativeChild + | NativeChild[]; export interface SelectProps extends React.HTMLProps { @@ -77,10 +81,11 @@ interface SelectState { helperTextFoundation?: MDCSelectHelperTextFoundation; iconFoundation?: MDCSelectIconFoundation; foundation?: MDCSelectFoundation; -}; +} -export default class Select - extends React.Component, SelectState> { +export default class Select< + T extends HTMLElement = HTMLSelectElement +> extends React.Component, SelectState> { nativeControl = React.createRef(); // These Sets are needed because setState({classList}) is asynchronous. @@ -143,8 +148,10 @@ export default class Select if (this.state.foundation && this.state.value !== prevState.value) { this.state.foundation.handleChange(true); } - if (this.state.helperTextFoundation !== prevState.helperTextFoundation - || this.state.iconFoundation !== prevState.iconFoundation) { + if ( + this.state.helperTextFoundation !== prevState.helperTextFoundation || + this.state.iconFoundation !== prevState.iconFoundation + ) { this.destroyFoundation(); this.createFoundation(); } @@ -181,7 +188,8 @@ export default class Select const isBeingRemoved = this.classesBeingRemoved.has(className); return (hasClass || isBeingAdded) && !isBeingRemoved; }, - setRippleCenter: (lineRippleCenter: number) => this.setState({lineRippleCenter}), + setRippleCenter: (lineRippleCenter: number) => + this.setState({lineRippleCenter}), getValue: () => this.state.value, setValue: (value: string) => this.setState({value}), setDisabled: (disabled: boolean) => this.setState({disabled}), @@ -249,38 +257,47 @@ export default class Select } createFoundation = (callback?: () => void) => { - const foundation = new MDCSelectFoundation(this.adapter, this.foundationMap); + const foundation = new MDCSelectFoundation( + this.adapter, + this.foundationMap + ); foundation.init(); this.setState({foundation}, callback); - } + }; destroyFoundation = () => { if (this.state.foundation) { this.state.foundation.destroy(); } - } + }; addClass = (className: string) => { // See comment above about classesBeingAdded/classesBeingRemoved this.classesBeingAdded.add(className); - this.setState((prevState) => { - const classList = new Set(prevState.classList); - classList.add(className); - return {classList}; - }, () => { - this.classesBeingAdded.delete(className); - }); + this.setState( + (prevState) => { + const classList = new Set(prevState.classList); + classList.add(className); + return {classList}; + }, + () => { + this.classesBeingAdded.delete(className); + } + ); }; removeClass = (className: string) => { // See comment above about classesBeingAdded/classesBeingRemoved this.classesBeingRemoved.add(className); - this.setState((prevState) => { - const classList = new Set(prevState.classList); - classList.delete(className); - return {classList}; - }, () => { - this.classesBeingRemoved.delete(className); - }); + this.setState( + (prevState) => { + const classList = new Set(prevState.classList); + classList.delete(className); + return {classList}; + }, + () => { + this.classesBeingRemoved.delete(className); + } + ); }; closeMenu = () => this.setState({open: false}); @@ -290,15 +307,17 @@ export default class Select } else { this.addClass(cssClasses.INVALID); } - } + }; - setHelperTextFoundation = (helperTextFoundation: MDCSelectHelperTextFoundation) => { + setHelperTextFoundation = ( + helperTextFoundation: MDCSelectHelperTextFoundation + ) => { this.setState({helperTextFoundation}); - } + }; setIconFoundation = (iconFoundation: MDCSelectIconFoundation) => { this.setState({iconFoundation}); - } + }; /** * render methods @@ -308,7 +327,7 @@ export default class Select
{this.renderIcon()} - + {this.renderSelect()} {this.props.outlined ? null : this.renderLabel()} {this.props.outlined @@ -453,14 +472,8 @@ export default class Select } } -export { - SelectHelperText, - SelectHelperTextProps, -} from './helper-text'; -export { - SelectIcon, - SelectIconProps, -} from './icon'; +export {SelectHelperText, SelectHelperTextProps} from './helper-text'; +export {SelectIcon, SelectIconProps} from './icon'; export {Option}; export { diff --git a/packages/snackbar/index.tsx b/packages/snackbar/index.tsx index 666817256..d97c3af5d 100644 --- a/packages/snackbar/index.tsx +++ b/packages/snackbar/index.tsx @@ -41,20 +41,20 @@ export interface Props { onClosing?: (reason: string) => void; onClose?: (reason: string) => void; onAnnounce?: () => void; -}; +} type State = { - classes: Set, + classes: Set; }; export class Snackbar extends React.Component { - foundation: MDCSnackbarFoundation + foundation: MDCSnackbarFoundation; static defaultProps: Partial = { open: true, stacked: false, leading: false, - } + }; constructor(props: Props) { super(props); @@ -141,10 +141,10 @@ export class Snackbar extends React.Component { } handleKeyDown = (e: React.KeyboardEvent) => { this.foundation.handleKeyDown(e.nativeEvent); - } + }; handleActionClick = (e: React.MouseEvent) => { this.foundation.handleActionButtonClick(e.nativeEvent); - } + }; componentDidMount() { this.foundation.init(); if (this.props.open) { @@ -165,23 +165,32 @@ export class Snackbar extends React.Component { this.foundation.destroy(); } get classes() { - return classnames(this.props.className, 'mdc-snackbar', ...Array.from(this.state.classes)); + return classnames( + this.props.className, + 'mdc-snackbar', + ...Array.from(this.state.classes) + ); } render() { - return
-
-
- {this.props.message} + return ( +
+
+
+ {this.props.message} +
+ {this.props.actionText ? ( +
+ +
+ ) : null}
- {this.props.actionText ? -
- -
: null}
-
; + ); } } diff --git a/packages/switch/NativeControl.tsx b/packages/switch/NativeControl.tsx index a4e6a77b9..5f6cd10d7 100644 --- a/packages/switch/NativeControl.tsx +++ b/packages/switch/NativeControl.tsx @@ -26,7 +26,8 @@ export interface NativeControlProps extends React.HTMLProps { } const NativeControl: React.FunctionComponent = ({ - rippleActivatorRef, ...otherProps // eslint-disable-line react/prop-types + rippleActivatorRef, + ...otherProps }) => { return ( , React.HTMLProps { - rippleActivator: React.RefObject; - initRipple: (surface: HTMLDivElement, activator?: HTMLInputElement) => void; + extends InjectedProps, + React.HTMLProps { + rippleActivator: React.RefObject; + initRipple: (surface: HTMLDivElement, activator?: HTMLInputElement) => void; } export class ThumbUnderlay extends React.Component { @@ -66,4 +67,6 @@ export class ThumbUnderlay extends React.Component { } } -export default withRipple(ThumbUnderlay); +export default withRipple( + ThumbUnderlay +); diff --git a/packages/tab-bar/index.tsx b/packages/tab-bar/index.tsx index 6d714e417..852b8469f 100644 --- a/packages/tab-bar/index.tsx +++ b/packages/tab-bar/index.tsx @@ -1,7 +1,7 @@ import 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 +import Tab, {TabProps} from '@material/react-tab'; // eslint-disable-line @typescript-eslint/no-unused-vars import {MDCTabBarFoundation} from '@material/tab-bar/foundation'; import {MDCTabBarAdapter} from '@material/tab-bar/adapter'; @@ -13,15 +13,13 @@ export interface TabBarProps extends React.HTMLAttributes { className?: string; isRtl?: boolean; children: React.ReactElement | React.ReactElement[]; -}; +} interface TabBarState { previousActiveIndex: number; -}; +} -class TabBar extends React.Component< - TabBarProps, TabBarState - > { +class TabBar extends React.Component { tabBarRef: React.RefObject = React.createRef(); tabScrollerRef: React.RefObject = React.createRef(); tabList: Tab[] = []; @@ -59,7 +57,9 @@ class TabBar extends React.Component< x: 0, y: 0, }; - this.tabList[activeIndex].activate(defaultDOMRect /* previousIndicatorClientRect */); + this.tabList[activeIndex].activate( + defaultDOMRect /* previousIndicatorClientRect */ + ); } this.foundation.scrollIntoView(indexInView!); } @@ -86,10 +86,12 @@ class TabBar extends React.Component< get adapter(): MDCTabBarAdapter { return { scrollTo: (scrollX: number) => { - this.tabScrollerRef.current && this.tabScrollerRef.current.scrollTo(scrollX); + this.tabScrollerRef.current && + this.tabScrollerRef.current.scrollTo(scrollX); }, incrementScroll: (scrollXIncrement: number) => { - this.tabScrollerRef.current && this.tabScrollerRef.current.incrementScroll(scrollXIncrement); + this.tabScrollerRef.current && + this.tabScrollerRef.current.incrementScroll(scrollXIncrement); }, getScrollPosition: () => { if (!this.tabScrollerRef.current) return 0; @@ -128,15 +130,17 @@ class TabBar extends React.Component< } return -1; }, - getIndexOfTabById: (id: string) => this.tabList.map((tab) => tab.props.id).indexOf(id), + 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), + notifyTabActivated: (index: number) => + this.props.onActivated && this.props.onActivated(index), }; } pushToTabList = (el: Tab) => { this.tabList.push(el); - } + }; onKeyDown = (e: React.KeyboardEvent) => { // Persist the synthetic event to access its `key`. @@ -147,7 +151,7 @@ class TabBar extends React.Component< if (this.props.onKeyDown) { this.props.onKeyDown(e); } - } + }; onClickTab = ( e: React.MouseEvent, @@ -160,17 +164,17 @@ class TabBar extends React.Component< if (onClick) { onClick(e); } - } + }; render() { const { - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ className, indexInView, activeIndex, handleActiveIndexUpdate, onKeyDown, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ isRtl, children, ...otherProps @@ -186,20 +190,20 @@ class TabBar extends React.Component< {...otherProps} > - {React.Children.map(children as React.ReactElement[], this.renderTab)} + {React.Children.map( + children as React.ReactElement[], + this.renderTab + )}
); } renderTab = (tab: React.ReactElement, index: number) => { - const { - children, - onClick, - ...otherProps - } = tab.props; + const {children, onClick, ...otherProps} = tab.props; const props = { - onClick: (e: React.MouseEvent) => this.onClickTab(e, index, onClick), + onClick: (e: React.MouseEvent) => + this.onClickTab(e, index, onClick), ref: this.pushToTabList, ...otherProps, } as TabProps; diff --git a/packages/tab-indicator/index.tsx b/packages/tab-indicator/index.tsx index 164e8efbd..558a51ea5 100644 --- a/packages/tab-indicator/index.tsx +++ b/packages/tab-indicator/index.tsx @@ -26,7 +26,8 @@ import {MDCSlidingTabIndicatorFoundation} from '@material/tab-indicator/sliding- import {MDCFadingTabIndicatorFoundation} from '@material/tab-indicator/fading-foundation'; import {MDCTabIndicatorAdapter} from '@material/tab-indicator/adapter'; -export interface TabIndicatorProps extends React.HTMLAttributes { +export interface TabIndicatorProps + extends React.HTMLAttributes { active?: boolean; className?: string; fade?: boolean; @@ -34,9 +35,16 @@ export interface TabIndicatorProps extends React.HTMLAttributes previousIndicatorClientRect?: ClientRect; } -export default class TabIndicator extends React.Component { - private tabIndicatorElement: React.RefObject = React.createRef(); - foundation!: MDCFadingTabIndicatorFoundation | MDCSlidingTabIndicatorFoundation; +export default class TabIndicator extends React.Component< + TabIndicatorProps, + {} +> { + private tabIndicatorElement: React.RefObject< + HTMLSpanElement + > = React.createRef(); + foundation!: + | MDCFadingTabIndicatorFoundation + | MDCSlidingTabIndicatorFoundation; static defaultProps: Partial = { active: false, @@ -113,7 +121,11 @@ export default class TabIndicator extends React.Component const typedProp = prop as keyof CSSStyleDeclaration; // length and parentRule are readonly properties of CSSStyleDeclaration that // cannot be set - if (!contentElement || typedProp === 'length' || typedProp === 'parentRule') { + if ( + !contentElement || + typedProp === 'length' || + typedProp === 'parentRule' + ) { return; } // https://github.com/Microsoft/TypeScript/issues/11914 @@ -127,7 +139,9 @@ export default class TabIndicator extends React.Component // need to use getElementsByClassName since tabIndicator could be // a non-semantic element (span, i, etc.). This is a problem since refs to a non semantic elements // return the instance of the component. - return this.tabIndicatorElement.current.getElementsByClassName('mdc-tab-indicator__content')[0]; + return this.tabIndicatorElement.current.getElementsByClassName( + 'mdc-tab-indicator__content' + )[0]; }; computeContentClientRect = () => { @@ -186,4 +200,3 @@ export default class TabIndicator extends React.Component return ; } } - diff --git a/packages/tab-scroller/index.tsx b/packages/tab-scroller/index.tsx index 603748878..fa158dbef 100644 --- a/packages/tab-scroller/index.tsx +++ b/packages/tab-scroller/index.tsx @@ -35,21 +35,23 @@ export interface TabScrollerProps extends React.HTMLAttributes { alignEnd?: boolean; alignCenter?: boolean; className?: string; -}; +} interface TabScrollerState { classList: Set; areaClassList: Set; scrollAreaStyleProperty: React.CSSProperties; scrollContentStyleProperty: React.CSSProperties; -}; +} -type ScrollerElementNames = 'scrollAreaStyleProperty' | 'scrollContentStyleProperty'; +type ScrollerElementNames = + | 'scrollAreaStyleProperty' + | 'scrollContentStyleProperty'; export default class TabScroller extends React.Component< TabScrollerProps, TabScrollerState - > { +> { areaElement: React.RefObject = React.createRef(); contentElement: React.RefObject = React.createRef(); foundation!: MDCTabScrollerFoundation; @@ -95,7 +97,9 @@ export default class TabScroller extends React.Component< } setStyleToElement = ( - prop: string, value: string | boolean, elementStyleProperty: ScrollerElementNames + prop: string, + value: string | boolean, + elementStyleProperty: ScrollerElementNames ) => { const styleName = convertDashToCamelCase(prop); const updateElementStyleProperty = Object.assign( @@ -112,7 +116,10 @@ export default class TabScroller extends React.Component< get adapter(): MDCTabScrollerAdapter { return { - eventTargetMatchesSelector: (evtTarget: HTMLDivElement, selector: string) => { + eventTargetMatchesSelector: ( + evtTarget: HTMLDivElement, + selector: string + ) => { if (selector) { return matches(evtTarget, selector); } @@ -138,10 +145,11 @@ export default class TabScroller extends React.Component< setScrollContentStyleProperty: (prop: string, value: string) => this.setStyleToElement(prop, value, 'scrollContentStyleProperty'), getScrollContentStyleValue: (propName: string) => - this.contentElement.current ? - window - .getComputedStyle(this.contentElement.current) - .getPropertyValue(propName) : '', + this.contentElement.current + ? window + .getComputedStyle(this.contentElement.current) + .getPropertyValue(propName) + : '', setScrollAreaScrollLeft: (scrollX: number) => { if (!this.areaElement.current) return; this.areaElement.current.scrollLeft = scrollX; @@ -178,7 +186,7 @@ export default class TabScroller extends React.Component< return defaultDOMRect; } return element.getBoundingClientRect(); - } + }; getScrollPosition = () => { return this.foundation.getScrollPosition(); @@ -186,9 +194,9 @@ export default class TabScroller extends React.Component< // needs to be public class method for react tab-bar getScrollContentWidth = () => { - return ( - this.contentElement.current ? this.contentElement.current.offsetWidth : 0 - ); + return this.contentElement.current + ? this.contentElement.current.offsetWidth + : 0; }; incrementScroll = (scrollXIncrement: number) => { @@ -284,4 +292,3 @@ export default class TabScroller extends React.Component< ); } } - diff --git a/packages/tab/TabRipple.tsx b/packages/tab/TabRipple.tsx index de513dd20..b4a640543 100644 --- a/packages/tab/TabRipple.tsx +++ b/packages/tab/TabRipple.tsx @@ -24,12 +24,15 @@ import React from 'react'; import classnames from 'classnames'; import { - withRipple, InjectedProps, - RippledComponentInterface, RippledComponentState, // eslint-disable-line no-unused-vars + withRipple, + InjectedProps, + RippledComponentInterface, + RippledComponentState, // eslint-disable-line @typescript-eslint/no-unused-vars } from '@material/react-ripple'; -export interface TabRippleProps extends React.HTMLAttributes, - InjectedProps { +export interface TabRippleProps + extends React.HTMLAttributes, + InjectedProps { className: string; ref?: React.RefObject; } @@ -42,23 +45,18 @@ class TabRippleBase extends React.Component { render() { const { // keeping out of ...otherProps - /* eslint-disable no-unused-vars */ + /* eslint-disable @typescript-eslint/no-unused-vars */ className, unbounded, - /* eslint-enable no-unused-vars */ + /* eslint-enable @typescript-eslint/no-unused-vars */ initRipple, ...otherProps } = this.props; - return ( -
- ); + return
; } } const TabRipple = withRipple(TabRippleBase); -type TabRipple = React.Component & RippledComponentInterface; +type TabRipple = React.Component & + RippledComponentInterface; export default TabRipple; diff --git a/packages/tab/index.tsx b/packages/tab/index.tsx index 6159cef3c..e487986e3 100644 --- a/packages/tab/index.tsx +++ b/packages/tab/index.tsx @@ -72,11 +72,11 @@ export default class Tab extends React.Component { }; state: TabState = { - 'classList': new Set(), + classList: new Set(), 'aria-selected': 'false', - 'activateIndicator': false, - 'previousIndicatorClientRect': this.props.previousIndicatorClientRect, - 'tabIndex': -1, + activateIndicator: false, + previousIndicatorClientRect: this.props.previousIndicatorClientRect, + tabIndex: -1, }; componentDidMount() { @@ -129,22 +129,20 @@ export default class Tab extends React.Component { classList.delete(className); this.setState({classList}); }, - hasClass: (className: string) => this.classes.split(' ').includes(className), - setAttr: (attr: keyof MDCTabElementAttributes, value?: string | boolean) => ( - this.setState((prevState) => ({...prevState, [attr]: value})) - ), + hasClass: (className: string) => + this.classes.split(' ').includes(className), + setAttr: ( + attr: keyof MDCTabElementAttributes, + value?: string | boolean + ) => this.setState((prevState) => ({...prevState, [attr]: value})), getOffsetLeft: () => Number(this.tabRef.current && this.tabRef.current.offsetLeft), getOffsetWidth: () => Number(this.tabRef.current && this.tabRef.current.offsetWidth), getContentOffsetLeft: () => - this.tabContentRef.current ? - this.tabContentRef.current.offsetLeft : - 0, + this.tabContentRef.current ? this.tabContentRef.current.offsetLeft : 0, getContentOffsetWidth: () => - this.tabContentRef.current ? - this.tabContentRef.current.offsetWidth : - 0, + this.tabContentRef.current ? this.tabContentRef.current.offsetWidth : 0, focus: () => this.tabRef.current && this.tabRef.current.focus(), notifyInteracted: this.props.onInteraction!, activateIndicator: (previousIndicatorClientRect: ClientRect) => @@ -179,11 +177,11 @@ export default class Tab extends React.Component { onFocus = (e: React.FocusEvent) => { this.tabRippleRef.current && this.tabRippleRef.current.handleFocus(e); - } + }; onBlur = (e: React.FocusEvent) => { this.tabRippleRef.current && this.tabRippleRef.current.handleBlur(e); - } + }; render() { const { @@ -202,10 +200,7 @@ export default class Tab extends React.Component { isMinWidthIndicator, ...otherProps } = this.props; - const { - tabIndex, - ['aria-selected']: ariaSelected, - } = this.state; + const {tabIndex, ['aria-selected']: ariaSelected} = this.state; return (