From f6e53b9a71e5132f17e7265f4302aae0e649d7c8 Mon Sep 17 00:00:00 2001 From: Adam Dobrawy Date: Thu, 27 Jan 2022 14:32:33 +0100 Subject: [PATCH 1/2] chore: upgrade BoundsControl to TS,FC, add storybook --- .../components/controls/BoundsControl.jsx | 129 ------------------ .../controls/BoundsControl.stories.tsx | 54 ++++++++ .../components/controls/BoundsControl.tsx | 105 ++++++++++++++ 3 files changed, 159 insertions(+), 129 deletions(-) delete mode 100644 superset-frontend/src/explore/components/controls/BoundsControl.jsx create mode 100644 superset-frontend/src/explore/components/controls/BoundsControl.stories.tsx create mode 100644 superset-frontend/src/explore/components/controls/BoundsControl.tsx diff --git a/superset-frontend/src/explore/components/controls/BoundsControl.jsx b/superset-frontend/src/explore/components/controls/BoundsControl.jsx deleted file mode 100644 index 39a0d560bc333..0000000000000 --- a/superset-frontend/src/explore/components/controls/BoundsControl.jsx +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import { InputNumber } from 'src/common/components'; -import { t, styled } from '@superset-ui/core'; -import { isEqual, debounce } from 'lodash'; -import ControlHeader from 'src/explore/components/ControlHeader'; - -const propTypes = { - onChange: PropTypes.func, - value: PropTypes.array, -}; - -const defaultProps = { - onChange: () => {}, - value: [null, null], -}; - -const StyledDiv = styled.div` - display: flex; -`; - -const MinInput = styled(InputNumber)` - flex: 1; - margin-right: ${({ theme }) => theme.gridUnit}px; -`; - -const MaxInput = styled(InputNumber)` - flex: 1; - margin-left: ${({ theme }) => theme.gridUnit}px; -`; - -export default class BoundsControl extends React.Component { - constructor(props) { - super(props); - this.state = { - minMax: [ - Number.isNaN(this.props.value[0]) ? '' : props.value[0], - Number.isNaN(this.props.value[1]) ? '' : props.value[1], - ], - }; - this.onChange = debounce(this.onChange.bind(this), 300); - this.onMinChange = this.onMinChange.bind(this); - this.onMaxChange = this.onMaxChange.bind(this); - this.update = this.update.bind(this); - } - - componentDidUpdate(prevProps) { - if (!isEqual(prevProps.value, this.props.value)) { - this.update(); - } - } - - update() { - this.setState({ - minMax: [ - Number.isNaN(this.props.value[0]) ? '' : this.props.value[0], - Number.isNaN(this.props.value[1]) ? '' : this.props.value[1], - ], - }); - } - - onMinChange(value) { - this.setState( - prevState => ({ - minMax: [value, prevState.minMax[1]], - }), - this.onChange, - ); - } - - onMaxChange(value) { - this.setState( - prevState => ({ - minMax: [prevState.minMax[0], value], - }), - this.onChange, - ); - } - - onChange() { - const mm = this.state.minMax; - const min = Number.isNaN(parseFloat(mm[0])) ? null : parseFloat(mm[0]); - const max = Number.isNaN(parseFloat(mm[1])) ? null : parseFloat(mm[1]); - this.props.onChange([min, max]); - } - - render() { - return ( -
- - - - - -
- ); - } -} - -BoundsControl.propTypes = propTypes; -BoundsControl.defaultProps = defaultProps; diff --git a/superset-frontend/src/explore/components/controls/BoundsControl.stories.tsx b/superset-frontend/src/explore/components/controls/BoundsControl.stories.tsx new file mode 100644 index 0000000000000..04c197682f3c7 --- /dev/null +++ b/superset-frontend/src/explore/components/controls/BoundsControl.stories.tsx @@ -0,0 +1,54 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import BoundsControl, { BoundsControlProps } from './BoundsControl'; + +export default { + title: 'BoundsControl', + component: BoundsControl, +}; + +export const InteractiveBoundsControl = ( + args: BoundsControlProps & { initialMin: number; initialMax: number }, +) => { + const { initialMin, initialMax, ...props } = args; + + return ( + <> + + + ); +}; + +InteractiveBoundsControl.args = { + initialMin: 0, + initialMax: 50, +}; + +InteractiveBoundsControl.argTypes = { + onChange: { action: 'onChange' }, +}; + +InteractiveBoundsControl.story = { + parameters: { + knobs: { + disable: true, + }, + }, +}; diff --git a/superset-frontend/src/explore/components/controls/BoundsControl.tsx b/superset-frontend/src/explore/components/controls/BoundsControl.tsx new file mode 100644 index 0000000000000..39901b56b77cd --- /dev/null +++ b/superset-frontend/src/explore/components/controls/BoundsControl.tsx @@ -0,0 +1,105 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { InputNumber } from 'src/common/components'; +import { t, styled } from '@superset-ui/core'; +import { debounce } from 'lodash'; +import ControlHeader from 'src/explore/components/ControlHeader'; + +type ValueType = (number | null)[]; + +export type BoundsControlProps = { + onChange?: (value: ValueType) => void; + value?: ValueType; +}; + +const StyledDiv = styled.div` + display: flex; +`; + +const MinInput = styled(InputNumber)` + flex: 1; + margin-right: ${({ theme }) => theme.gridUnit}px; +`; + +const MaxInput = styled(InputNumber)` + flex: 1; + margin-left: ${({ theme }) => theme.gridUnit}px; +`; + +const parseNumber = (value: undefined | number | string | null) => + value === null || Number.isNaN(Number(value)) ? null : Number(value); + +export default function BoundsControl({ + onChange = () => {}, + value = [null, null], + ...props +}: BoundsControlProps) { + const [minMax, setMinMax] = React.useState([ + parseNumber(value[0]), + parseNumber(value[1]), + ]); + const min = value[0]; + const max = value[1]; + const debouncedOnChange = React.useRef(debounce(onChange, 300)).current; + + const update = (mm: ValueType) => { + setMinMax(mm); + debouncedOnChange([ + mm[0] === undefined ? null : mm[0], + mm[1] === undefined ? null : mm[1], + ]); + }; + + React.useEffect(() => { + setMinMax([parseNumber(min), parseNumber(max)]); + }, [min, max]); + + const onMinChange = (value: number | string | undefined) => { + update([parseNumber(value), minMax[1]]); + }; + + const onMaxChange = (value: number | string | undefined) => { + update([minMax[0], parseNumber(value)]); + }; + + return ( +
+ + + + + +
+ ); +} From 32a7872ca7a31afa28fab37d642de9c8207e4ec7 Mon Sep 17 00:00:00 2001 From: Adam Dobrawy Date: Tue, 8 Feb 2022 03:09:03 +0100 Subject: [PATCH 2/2] chore: improve React import reference consistency --- .../src/explore/components/controls/BoundsControl.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/superset-frontend/src/explore/components/controls/BoundsControl.tsx b/superset-frontend/src/explore/components/controls/BoundsControl.tsx index 39901b56b77cd..1565ce7d31c2c 100644 --- a/superset-frontend/src/explore/components/controls/BoundsControl.tsx +++ b/superset-frontend/src/explore/components/controls/BoundsControl.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { InputNumber } from 'src/common/components'; import { t, styled } from '@superset-ui/core'; import { debounce } from 'lodash'; @@ -51,13 +51,13 @@ export default function BoundsControl({ value = [null, null], ...props }: BoundsControlProps) { - const [minMax, setMinMax] = React.useState([ + const [minMax, setMinMax] = useState([ parseNumber(value[0]), parseNumber(value[1]), ]); const min = value[0]; const max = value[1]; - const debouncedOnChange = React.useRef(debounce(onChange, 300)).current; + const debouncedOnChange = useRef(debounce(onChange, 300)).current; const update = (mm: ValueType) => { setMinMax(mm); @@ -67,7 +67,7 @@ export default function BoundsControl({ ]); }; - React.useEffect(() => { + useEffect(() => { setMinMax([parseNumber(min), parseNumber(max)]); }, [min, max]);