fix(explore): DndColumnSelect sometimes not working with multi: false (#15731)

* fix(explore): DndColumnSelect not working with multi: false

* fix values not synchronized when dataset changes
This commit is contained in:
Kamil Gabryjelski 2021-07-16 14:20:13 +02:00 committed by GitHub
parent 0721f54580
commit 66c28d653f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 25 deletions

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import React, { useState } from 'react'; import React, { useEffect } from 'react';
import { tn } from '@superset-ui/core'; import { tn } from '@superset-ui/core';
import { ColumnMeta } from '@superset-ui/chart-controls'; import { ColumnMeta } from '@superset-ui/chart-controls';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
@ -29,19 +29,41 @@ import { DndItemType } from 'src/explore/components/DndItemType';
import { StyledColumnOption } from 'src/explore/components/optionRenderers'; import { StyledColumnOption } from 'src/explore/components/optionRenderers';
export const DndColumnSelect = (props: LabelProps) => { export const DndColumnSelect = (props: LabelProps) => {
const { value, options, multi = true } = props; const { value, options, multi = true, onChange } = props;
const optionSelector = new OptionSelector(options, value); const optionSelector = new OptionSelector(options, multi, value);
const [values, setValues] = useState<ColumnMeta[]>(optionSelector.values);
// synchronize values in case of dataset changes
useEffect(() => {
const optionSelectorValues = optionSelector.getValues();
if (typeof value !== typeof optionSelectorValues) {
onChange(optionSelectorValues);
}
if (
typeof value === 'string' &&
typeof optionSelectorValues === 'string' &&
value !== optionSelectorValues
) {
onChange(optionSelectorValues);
}
if (
Array.isArray(optionSelectorValues) &&
Array.isArray(value) &&
(optionSelectorValues.length !== value.length ||
optionSelectorValues.every((val, index) => val === value[index]))
) {
onChange(optionSelectorValues);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(value), JSON.stringify(optionSelector.getValues())]);
const onDrop = (item: DatasourcePanelDndItem) => { const onDrop = (item: DatasourcePanelDndItem) => {
const column = item.value as ColumnMeta; const column = item.value as ColumnMeta;
if (!optionSelector.isArray && !isEmpty(optionSelector.values)) { if (!optionSelector.multi && !isEmpty(optionSelector.values)) {
optionSelector.replace(0, column.column_name); optionSelector.replace(0, column.column_name);
} else { } else {
optionSelector.add(column.column_name); optionSelector.add(column.column_name);
} }
setValues(optionSelector.values); onChange(optionSelector.getValues());
props.onChange(optionSelector.getValues());
}; };
const canDrop = (item: DatasourcePanelDndItem) => const canDrop = (item: DatasourcePanelDndItem) =>
@ -50,18 +72,16 @@ export const DndColumnSelect = (props: LabelProps) => {
const onClickClose = (index: number) => { const onClickClose = (index: number) => {
optionSelector.del(index); optionSelector.del(index);
setValues(optionSelector.values); onChange(optionSelector.getValues());
props.onChange(optionSelector.getValues());
}; };
const onShiftOptions = (dragIndex: number, hoverIndex: number) => { const onShiftOptions = (dragIndex: number, hoverIndex: number) => {
optionSelector.swap(dragIndex, hoverIndex); optionSelector.swap(dragIndex, hoverIndex);
setValues(optionSelector.values); onChange(optionSelector.getValues());
props.onChange(optionSelector.getValues());
}; };
const valuesRenderer = () => const valuesRenderer = () =>
values.map((column, idx) => ( optionSelector.values.map((column, idx) => (
<OptionWrapper <OptionWrapper
key={idx} key={idx}
index={idx} index={idx}

View File

@ -17,28 +17,23 @@
* under the License. * under the License.
*/ */
import { ColumnMeta } from '@superset-ui/chart-controls'; import { ColumnMeta } from '@superset-ui/chart-controls';
import { ensureIsArray } from '@superset-ui/core';
export class OptionSelector { export class OptionSelector {
values: ColumnMeta[]; values: ColumnMeta[];
options: { string: ColumnMeta }; options: { string: ColumnMeta };
isArray: boolean; multi: boolean;
constructor( constructor(
options: { string: ColumnMeta }, options: { string: ColumnMeta },
multi: boolean,
initialValues?: string[] | string, initialValues?: string[] | string,
) { ) {
this.options = options; this.options = options;
let values: string[]; this.multi = multi;
if (Array.isArray(initialValues)) { this.values = ensureIsArray(initialValues)
values = initialValues;
this.isArray = true;
} else {
values = initialValues ? [initialValues] : [];
this.isArray = false;
}
this.values = values
.map(value => { .map(value => {
if (value in options) { if (value in options) {
return options[value]; return options[value];
@ -68,12 +63,12 @@ export class OptionSelector {
[this.values[a], this.values[b]] = [this.values[b], this.values[a]]; [this.values[a], this.values[b]] = [this.values[b], this.values[a]];
} }
has(groupBy: string): boolean { has(value: string): boolean {
return !!this.getValues()?.includes(groupBy); return !!this.getValues()?.includes(value);
} }
getValues(): string[] | string | undefined { getValues(): string[] | string | undefined {
if (!this.isArray) { if (!this.multi) {
return this.values.length > 0 ? this.values[0].column_name : undefined; return this.values.length > 0 ? this.values[0].column_name : undefined;
} }
return this.values.map(option => option.column_name); return this.values.map(option => option.column_name);