mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
refactor: convert controlUtils to TypeScript (2 of 2) (#13520)
This commit is contained in:
174
superset-frontend/src/explore/controlUtils/getControlState.ts
Normal file
174
superset-frontend/src/explore/controlUtils/getControlState.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* 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 { ReactNode } from 'react';
|
||||
import {
|
||||
DatasourceType,
|
||||
ensureIsArray,
|
||||
JsonValue,
|
||||
QueryFormData,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
ControlConfig,
|
||||
ControlPanelState,
|
||||
ControlState,
|
||||
ControlType,
|
||||
ControlValueValidator,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { getSectionsToRender } from './getSectionsToRender';
|
||||
import { getControlConfig } from './getControlConfig';
|
||||
|
||||
type ValidationError = JsonValue;
|
||||
|
||||
function execControlValidator<T = ControlType>(
|
||||
control: ControlState<T>,
|
||||
processedState: ControlState<T>,
|
||||
) {
|
||||
const validators = control.validators as ControlValueValidator[] | undefined;
|
||||
const validationErrors: ValidationError[] = [];
|
||||
if (validators && validators.length > 0) {
|
||||
validators.forEach(validator => {
|
||||
const error = validator.call(control, control.value, processedState);
|
||||
if (error) {
|
||||
validationErrors.push(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
// always reset validation errors even when there is no validator
|
||||
return { ...control, validationErrors };
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear control values that are no longer in the `choices` list.
|
||||
*/
|
||||
function handleMissingChoice<T = ControlType>(control: ControlState<T>) {
|
||||
// If the value is not valid anymore based on choices, clear it
|
||||
if (
|
||||
control.type === 'SelectControl' &&
|
||||
!control.freeForm &&
|
||||
control.choices &&
|
||||
control.value
|
||||
) {
|
||||
const alteredControl = { ...control };
|
||||
const choices = control.choices as [JsonValue, ReactNode][];
|
||||
const value = ensureIsArray(control.value);
|
||||
const choiceValues = choices.map(c => c[0]);
|
||||
if (control.multi && value.length > 0) {
|
||||
alteredControl.value = value.filter(el => choiceValues.includes(el));
|
||||
return alteredControl;
|
||||
}
|
||||
if (!control.multi && !choiceValues.includes(value[0])) {
|
||||
alteredControl.value = null;
|
||||
return alteredControl;
|
||||
}
|
||||
}
|
||||
return control;
|
||||
}
|
||||
|
||||
export function applyMapStateToPropsToControl<T = ControlType>(
|
||||
controlState: ControlState<T>,
|
||||
controlPanelState: Partial<ControlPanelState>,
|
||||
) {
|
||||
const { mapStateToProps } = controlState;
|
||||
let state = { ...controlState };
|
||||
let { value } = state; // value is current user-input value
|
||||
if (mapStateToProps && controlPanelState) {
|
||||
state = {
|
||||
...controlState,
|
||||
...mapStateToProps.call(controlState, controlPanelState, controlState),
|
||||
};
|
||||
// `mapStateToProps` may also provide a value
|
||||
value = value || state.value;
|
||||
}
|
||||
// If default is a function, evaluate it
|
||||
if (typeof state.default === 'function') {
|
||||
state.default = state.default(state, controlPanelState);
|
||||
// if default is still a function, discard
|
||||
if (typeof state.default === 'function') {
|
||||
delete state.default;
|
||||
}
|
||||
}
|
||||
// If no current value, set it as default
|
||||
if (state.default && value === undefined) {
|
||||
value = state.default;
|
||||
}
|
||||
// If a choice control went from multi=false to true, wrap value in array
|
||||
if (value && state.multi && !Array.isArray(value)) {
|
||||
value = [value];
|
||||
}
|
||||
state.value = value;
|
||||
return execControlValidator(handleMissingChoice(state), state);
|
||||
}
|
||||
|
||||
export function getControlStateFromControlConfig<T = ControlType>(
|
||||
controlConfig: ControlConfig<T> | null,
|
||||
controlPanelState: Partial<ControlPanelState>,
|
||||
value?: JsonValue,
|
||||
) {
|
||||
// skip invalid config values
|
||||
if (!controlConfig) {
|
||||
return null;
|
||||
}
|
||||
const controlState = { ...controlConfig, value } as ControlState<T>;
|
||||
// only apply mapStateToProps when control states have been initialized
|
||||
// or when explicitly didn't provide control panel state (mostly for testing)
|
||||
if (
|
||||
(controlPanelState && controlPanelState.controls) ||
|
||||
controlPanelState === null
|
||||
) {
|
||||
return applyMapStateToPropsToControl(controlState, controlPanelState);
|
||||
}
|
||||
return controlState;
|
||||
}
|
||||
|
||||
export function getControlState(
|
||||
controlKey: string,
|
||||
vizType: string,
|
||||
state: Partial<ControlPanelState>,
|
||||
value?: JsonValue,
|
||||
) {
|
||||
return getControlStateFromControlConfig(
|
||||
getControlConfig(controlKey, vizType),
|
||||
state,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
export function getAllControlsState(
|
||||
vizType: string,
|
||||
datasourceType: DatasourceType,
|
||||
state: ControlPanelState,
|
||||
formData: QueryFormData,
|
||||
) {
|
||||
const controlsState = {};
|
||||
getSectionsToRender(vizType, datasourceType).forEach(section =>
|
||||
section.controlSetRows.forEach(fieldsetRow =>
|
||||
fieldsetRow.forEach(field => {
|
||||
if (field && 'config' in field && field.config && field.name) {
|
||||
const { config, name } = field;
|
||||
controlsState[name] = getControlStateFromControlConfig(
|
||||
config,
|
||||
state,
|
||||
formData[name],
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
return controlsState;
|
||||
}
|
||||
Reference in New Issue
Block a user