refactor: Migration of AnnotationLayerControl to TypeScript (#28346)

Co-authored-by: JUST.in DO IT <justin.park@airbnb.com>
Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
This commit is contained in:
Enzo Martellucci 2024-09-25 17:45:11 +02:00 committed by GitHub
parent 0baac62099
commit 39f1b714a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 95 additions and 44 deletions

View File

@ -249,7 +249,7 @@ export async function getChartDataRequest({
export function runAnnotationQuery({ export function runAnnotationQuery({
annotation, annotation,
timeout, timeout,
formData = null, formData,
key, key,
isDashboardRequest = false, isDashboardRequest = false,
force = false, force = false,

View File

@ -16,16 +16,26 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { List } from 'src/components'; import { List } from 'src/components';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { t, withTheme } from '@superset-ui/core'; import { PureComponent } from 'react';
import {
HandlerFunction,
JsonObject,
Payload,
QueryFormData,
SupersetTheme,
t,
withTheme,
} from '@superset-ui/core';
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
import AsyncEsmComponent from 'src/components/AsyncEsmComponent'; import AsyncEsmComponent from 'src/components/AsyncEsmComponent';
import { getChartKey } from 'src/explore/exploreUtils'; import { getChartKey } from 'src/explore/exploreUtils';
import { runAnnotationQuery } from 'src/components/Chart/chartAction'; import { runAnnotationQuery } from 'src/components/Chart/chartAction';
import CustomListItem from 'src/explore/components/controls/CustomListItem'; import CustomListItem from 'src/explore/components/controls/CustomListItem';
import { ChartState, ExplorePageState } from 'src/explore/types';
import { AnyAction } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import ControlPopover, { import ControlPopover, {
getSectionContainerElement, getSectionContainerElement,
} from '../ControlPopover/ControlPopover'; } from '../ControlPopover/ControlPopover';
@ -36,19 +46,37 @@ const AnnotationLayer = AsyncEsmComponent(
() => <div style={{ width: 450, height: 368 }} />, () => <div style={{ width: 450, height: 368 }} />,
); );
const propTypes = { export interface Annotation {
colorScheme: PropTypes.string.isRequired, name: string;
annotationError: PropTypes.object, show?: boolean;
annotationQuery: PropTypes.object, annotation: string;
vizType: PropTypes.string, timeout: Date;
key: string;
formData: QueryFormData | null;
isDashboardRequest?: boolean;
force?: boolean;
}
validationErrors: PropTypes.array, export interface Props {
name: PropTypes.string.isRequired, colorScheme: string;
actions: PropTypes.object, annotationError: Record<string, string>;
value: PropTypes.arrayOf(PropTypes.object), annotationQuery: Record<string, AbortController>;
onChange: PropTypes.func, vizType: string;
refreshAnnotationData: PropTypes.func, validationErrors: JsonObject[];
name: string;
actions: {
setControlValue: HandlerFunction;
}; };
value: Annotation[];
onChange: (annotations: Annotation[]) => void;
refreshAnnotationData: (payload: Payload) => void;
theme: SupersetTheme;
}
export interface PopoverState {
popoverVisible: Record<number | string, boolean>;
addedAnnotationIndex: number | null;
}
const defaultProps = { const defaultProps = {
vizType: '', vizType: '',
@ -57,9 +85,10 @@ const defaultProps = {
annotationQuery: {}, annotationQuery: {},
onChange: () => {}, onChange: () => {},
}; };
class AnnotationLayerControl extends PureComponent<Props, PopoverState> {
static defaultProps = defaultProps;
class AnnotationLayerControl extends PureComponent { constructor(props: Props) {
constructor(props) {
super(props); super(props);
this.state = { this.state = {
popoverVisible: {}, popoverVisible: {},
@ -75,7 +104,7 @@ class AnnotationLayerControl extends PureComponent {
AnnotationLayer.preload(); AnnotationLayer.preload();
} }
UNSAFE_componentWillReceiveProps(nextProps) { UNSAFE_componentWillReceiveProps(nextProps: Props) {
const { name, annotationError, validationErrors, value } = nextProps; const { name, annotationError, validationErrors, value } = nextProps;
if (Object.keys(annotationError).length && !validationErrors.length) { if (Object.keys(annotationError).length && !validationErrors.length) {
this.props.actions.setControlValue( this.props.actions.setControlValue(
@ -89,9 +118,12 @@ class AnnotationLayerControl extends PureComponent {
} }
} }
addAnnotationLayer(originalAnnotation, newAnnotation) { addAnnotationLayer = (
originalAnnotation: Annotation | null,
newAnnotation: Annotation,
) => {
let annotations = this.props.value; let annotations = this.props.value;
if (annotations.includes(originalAnnotation)) { if (originalAnnotation && annotations.includes(originalAnnotation)) {
annotations = annotations.map(anno => annotations = annotations.map(anno =>
anno === originalAnnotation ? newAnnotation : anno, anno === originalAnnotation ? newAnnotation : anno,
); );
@ -106,15 +138,15 @@ class AnnotationLayerControl extends PureComponent {
}); });
this.props.onChange(annotations); this.props.onChange(annotations);
} };
handleVisibleChange(visible, popoverKey) { handleVisibleChange = (visible: boolean, popoverKey: number | string) => {
this.setState(prevState => ({ this.setState(prevState => ({
popoverVisible: { ...prevState.popoverVisible, [popoverKey]: visible }, popoverVisible: { ...prevState.popoverVisible, [popoverKey]: visible },
})); }));
} };
removeAnnotationLayer(annotation) { removeAnnotationLayer(annotation: Annotation | null) {
const annotations = this.props.value.filter(anno => anno !== annotation); const annotations = this.props.value.filter(anno => anno !== annotation);
// So scrollbar doesnt get stuck on hidden // So scrollbar doesnt get stuck on hidden
const element = getSectionContainerElement(); const element = getSectionContainerElement();
@ -124,17 +156,21 @@ class AnnotationLayerControl extends PureComponent {
this.props.onChange(annotations); this.props.onChange(annotations);
} }
renderPopover(popoverKey, annotation, error) { renderPopover = (
popoverKey: number | string,
annotation: Annotation | null,
error: string,
) => {
const id = annotation?.name || '_new'; const id = annotation?.name || '_new';
return ( return (
<div id={`annotation-pop-${id}`} data-test="popover-content"> <div id={`annotation-pop-${id}`} data-test="popover-content">
<AnnotationLayer <AnnotationLayer
{...annotation} {...(annotation || {})}
error={error} error={error}
colorScheme={this.props.colorScheme} colorScheme={this.props.colorScheme}
vizType={this.props.vizType} vizType={this.props.vizType}
addAnnotationLayer={newAnnotation => addAnnotationLayer={(newAnnotation: Annotation) =>
this.addAnnotationLayer(annotation, newAnnotation) this.addAnnotationLayer(annotation, newAnnotation)
} }
removeAnnotationLayer={() => this.removeAnnotationLayer(annotation)} removeAnnotationLayer={() => this.removeAnnotationLayer(annotation)}
@ -145,9 +181,9 @@ class AnnotationLayerControl extends PureComponent {
/> />
</div> </div>
); );
} };
renderInfo(anno) { renderInfo(anno: Annotation) {
const { annotationError, annotationQuery, theme } = this.props; const { annotationError, annotationQuery, theme } = this.props;
if (annotationQuery[anno.name]) { if (annotationQuery[anno.name]) {
return ( return (
@ -175,8 +211,10 @@ class AnnotationLayerControl extends PureComponent {
render() { render() {
const { addedAnnotationIndex } = this.state; const { addedAnnotationIndex } = this.state;
const addedAnnotation = this.props.value[addedAnnotationIndex]; const addedAnnotation =
addedAnnotationIndex !== null
? this.props.value[addedAnnotationIndex]
: null;
const annotations = this.props.value.map((anno, i) => ( const annotations = this.props.value.map((anno, i) => (
<ControlPopover <ControlPopover
key={i} key={i}
@ -202,15 +240,19 @@ class AnnotationLayerControl extends PureComponent {
</CustomListItem> </CustomListItem>
</ControlPopover> </ControlPopover>
)); ));
const addLayerPopoverKey = 'add'; const addLayerPopoverKey = 'add';
return ( return (
<div> <div>
<List bordered css={theme => ({ borderRadius: theme.gridUnit })}> <List bordered css={theme => ({ borderRadius: theme.gridUnit })}>
{annotations} {annotations}
<ControlPopover <ControlPopover
trigger="click" trigger="click"
content={this.renderPopover(addLayerPopoverKey, addedAnnotation)} content={this.renderPopover(
addLayerPopoverKey,
addedAnnotation,
'',
)}
title={t('Add annotation layer')} title={t('Add annotation layer')}
visible={this.state.popoverVisible[addLayerPopoverKey]} visible={this.state.popoverVisible[addLayerPopoverKey]}
destroyTooltipOnHide destroyTooltipOnHide
@ -232,27 +274,36 @@ class AnnotationLayerControl extends PureComponent {
} }
} }
AnnotationLayerControl.propTypes = propTypes;
AnnotationLayerControl.defaultProps = defaultProps;
// Tried to hook this up through stores/control.jsx instead of using redux // Tried to hook this up through stores/control.jsx instead of using redux
// directly, could not figure out how to get access to the color_scheme // directly, could not figure out how to get access to the color_scheme
function mapStateToProps({ charts, explore }) { function mapStateToProps({
charts,
explore,
}: Pick<ExplorePageState, 'charts' | 'explore'>) {
const chartKey = getChartKey(explore); const chartKey = getChartKey(explore);
const chart = charts[chartKey] || charts[0] || {};
const defaultChartState: Partial<ChartState> = {
annotationError: {},
annotationQuery: {},
};
const chart =
chartKey && charts[chartKey] ? charts[chartKey] : defaultChartState;
return { return {
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
colorScheme: explore.controls?.color_scheme?.value, colorScheme: explore.controls?.color_scheme?.value,
annotationError: chart.annotationError, annotationError: chart.annotationError ?? {},
annotationQuery: chart.annotationQuery, annotationQuery: chart.annotationQuery ?? {},
vizType: explore.controls.viz_type.value, vizType: explore.controls?.viz_type.value,
}; };
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(
dispatch: ThunkDispatch<any, undefined, AnyAction>,
) {
return { return {
refreshAnnotationData: annotationObj => refreshAnnotationData: (annotationObj: Annotation) =>
dispatch(runAnnotationQuery(annotationObj)), dispatch(runAnnotationQuery(annotationObj)),
}; };
} }