feat(explore): Remove default for time range filter and Metrics (#14661)

* feat(explore): Remove default for time range filter and Metrics

* Merge errors with same messages

* Fix e2e test

* Rename a variable

* Bump packages

* Fix unit tests
This commit is contained in:
Kamil Gabryjelski
2021-05-25 15:55:41 +02:00
committed by GitHub
parent add35f9fe4
commit 63dc035d6a
9 changed files with 398 additions and 381 deletions

View File

@@ -370,18 +370,34 @@ function ExploreViewContainer(props) {
function renderErrorMessage() {
// Returns an error message as a node if any errors are in the store
const errors = Object.entries(props.controls)
.filter(
([, control]) =>
control.validationErrors && control.validationErrors.length > 0,
)
.map(([key, control]) => (
<div key={key}>
{t('Control labeled ')}
<strong>{` "${control.label}" `}</strong>
{control.validationErrors.join('. ')}
const controlsWithErrors = Object.values(props.controls).filter(
control =>
control.validationErrors && control.validationErrors.length > 0,
);
if (controlsWithErrors.length === 0) {
return null;
}
const errorMessages = controlsWithErrors.map(
control => control.validationErrors,
);
const uniqueErrorMessages = [...new Set(errorMessages.flat())];
const errors = uniqueErrorMessages
.map(message => {
const matchingLabels = controlsWithErrors
.filter(control => control.validationErrors?.includes(message))
.map(control => control.label);
return [matchingLabels, message];
})
.map(([labels, message]) => (
<div key={message}>
{labels.length > 1 ? t('Controls labeled ') : t('Control labeled ')}
<strong>{` ${labels.join(', ')}`}</strong>
<span>: {message}</span>
</div>
));
let errorMessage;
if (errors.length > 0) {
errorMessage = <div style={{ textAlign: 'left' }}>{errors}</div>;

View File

@@ -106,4 +106,4 @@ export const TIME_FILTER_MAP = {
};
// TODO: make this configurable per Superset installation
export const DEFAULT_TIME_RANGE = 'Last week';
export const DEFAULT_TIME_RANGE = 'No filter';

View File

@@ -40,15 +40,17 @@ function execControlValidator<T = ControlType>(
processedState: ControlState<T>,
) {
const validators = control.validators as ControlValueValidator[] | undefined;
const validationErrors: ValidationError[] = [];
const { externalValidationErrors = [] } = control;
const errors: ValidationError[] = [];
if (validators && validators.length > 0) {
validators.forEach(validator => {
const error = validator.call(control, control.value, processedState);
if (error) {
validationErrors.push(error);
errors.push(error);
}
});
}
const validationErrors = [...errors, ...externalValidationErrors];
// always reset validation errors even when there is no validator
return { ...control, validationErrors };
}

View File

@@ -64,7 +64,7 @@ import {
legacyValidateInteger,
validateNonEmpty,
} from '@superset-ui/core';
import { formatSelectOptions, mainMetric } from 'src/modules/utils';
import { formatSelectOptions } from 'src/modules/utils';
import { TIME_FILTER_LABELS } from './constants';
import { StyledColumnOption } from './components/optionRenderers';
@@ -152,10 +152,6 @@ const metrics = {
multi: true,
label: t('Metrics'),
validators: [validateNonEmpty],
default: c => {
const metric = mainMetric(c.savedMetrics);
return metric ? [metric] : null;
},
mapStateToProps: state => {
const { datasource } = state;
return {
@@ -171,7 +167,6 @@ const metric = {
multi: false,
label: t('Metric'),
description: t('Metric'),
default: props => mainMetric(props.savedMetrics),
};
export function columnChoices(datasource) {
@@ -346,7 +341,7 @@ export const controls = {
type: 'DateFilterControl',
freeForm: true,
label: TIME_FILTER_LABELS.time_range,
default: t('Last week'), // this value is translated, but the backend wouldn't understand a translated value?
default: t('No filter'), // this value is translated, but the backend wouldn't understand a translated value?
description: t(
'The time range for the visualization. All relative times, e.g. "Last month", ' +
'"Last 7 days", "now", etc. are evaluated on the server using the server\'s ' +

View File

@@ -135,6 +135,24 @@ export default function exploreReducer(state = {}, action) {
...getControlStateFromControlConfig(controlConfig, state, action.value),
};
const newState = {
...state,
controls: { ...state.controls, [action.controlName]: control },
};
const rerenderedControls = {};
if (Array.isArray(control.rerender)) {
control.rerender.forEach(controlName => {
rerenderedControls[controlName] = {
...getControlStateFromControlConfig(
newState.controls[controlName],
newState,
newState.controls[controlName].value,
),
};
});
}
// combine newly detected errors with errors from `onChange` event of
// each control component (passed via reducer action).
const errors = control.validationErrors || [];
@@ -169,6 +187,7 @@ export default function exploreReducer(state = {}, action) {
...control,
validationErrors: errors,
},
...rerenderedControls,
},
};
},