chore: upgrade BoundsControl to TS (#18200)

* chore: upgrade BoundsControl to TS,FC, add storybook

* chore: improve React import reference consistency
This commit is contained in:
Adam Dobrawy 2022-02-08 13:45:10 +01:00 committed by GitHub
parent 1fbdabd2cf
commit fa8c81e1b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 129 deletions

View File

@ -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 (
<div>
<ControlHeader {...this.props} />
<StyledDiv>
<MinInput
data-test="min-bound"
placeholder={t('Min')}
onChange={this.onMinChange}
value={this.state.minMax[0]}
/>
<MaxInput
data-test="max-bound"
placeholder={t('Max')}
onChange={this.onMaxChange}
value={this.state.minMax[1]}
/>
</StyledDiv>
</div>
);
}
}
BoundsControl.propTypes = propTypes;
BoundsControl.defaultProps = defaultProps;

View File

@ -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 (
<>
<BoundsControl {...props} value={[initialMin, initialMax]} />
</>
);
};
InteractiveBoundsControl.args = {
initialMin: 0,
initialMax: 50,
};
InteractiveBoundsControl.argTypes = {
onChange: { action: 'onChange' },
};
InteractiveBoundsControl.story = {
parameters: {
knobs: {
disable: true,
},
},
};

View File

@ -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, { useEffect, useRef, useState } 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] = useState<ValueType>([
parseNumber(value[0]),
parseNumber(value[1]),
]);
const min = value[0];
const max = value[1];
const debouncedOnChange = useRef(debounce(onChange, 300)).current;
const update = (mm: ValueType) => {
setMinMax(mm);
debouncedOnChange([
mm[0] === undefined ? null : mm[0],
mm[1] === undefined ? null : mm[1],
]);
};
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 (
<div>
<ControlHeader {...props} />
<StyledDiv>
<MinInput
data-test="min-bound"
placeholder={t('Min')}
// emit (string | number | undefined)
onChange={onMinChange}
// accept (number | undefined)
value={minMax[0] === null ? undefined : minMax[0]}
/>
<MaxInput
data-test="max-bound"
placeholder={t('Max')}
// emit (number | string | undefined)
onChange={onMaxChange}
// accept (number | undefined)
value={minMax[1] === null ? undefined : minMax[1]}
/>
</StyledDiv>
</div>
);
}