mirror of https://github.com/apache/superset.git
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:
parent
1fbdabd2cf
commit
fa8c81e1b1
|
@ -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;
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
};
|
|
@ -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>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue