in src/plugins/vis_default_editor/public/components/agg_params.tsx [80:279]
function DefaultEditorAggParams({
agg,
aggError,
aggIndex = 0,
aggIsTooLow = false,
className,
disabledParams,
groupName,
formIsTouched,
indexPattern,
metricAggs,
state,
setAggParamValue,
onAggTypeChange,
setTouched,
setValidity,
schemas,
allowedAggs = [],
hideCustomLabel = false,
}: DefaultEditorAggParamsProps) {
const schema = useMemo(() => getSchemaByName(schemas, agg.schema), [agg.schema, schemas]);
const aggFilter = useMemo(() => [...allowedAggs, ...(schema.aggFilter || [])], [
allowedAggs,
schema.aggFilter,
]);
const { services } = useOpenSearchDashboards<VisDefaultEditorOpenSearchDashboardsServices>();
const aggTypes = useMemo(() => services.data.search.aggs.types.getAll(), [
services.data.search.aggs.types,
]);
const groupedAggTypeOptions = useMemo(
() => getAggTypeOptions(aggTypes, agg, indexPattern, groupName, aggFilter),
[aggTypes, agg, indexPattern, groupName, aggFilter]
);
const error = aggIsTooLow
? i18n.translate('visDefaultEditor.aggParams.errors.aggWrongRunOrderErrorMessage', {
defaultMessage: '"{schema}" aggs must run before all other buckets!',
values: { schema: schema.title },
})
: '';
const aggTypeName = agg.type?.name;
const fieldName = agg.params?.field?.name;
const editorConfig = useMemo(() => getEditorConfig(indexPattern, aggTypeName, fieldName), [
indexPattern,
aggTypeName,
fieldName,
]);
const params = useMemo(
() => getAggParamsToRender({ agg, editorConfig, metricAggs, state, schemas, hideCustomLabel }),
[agg, editorConfig, metricAggs, state, schemas, hideCustomLabel]
);
const allParams = [...params.basic, ...params.advanced];
const [paramsState, onChangeParamsState] = useReducer(
aggParamsReducer,
allParams,
initAggParamsState
);
const [aggType, onChangeAggType] = useReducer(aggTypeReducer, { touched: false, valid: true });
const isFormValid =
!error &&
aggType.valid &&
Object.entries(paramsState).every(([, paramState]) => paramState.valid);
const isAllInvalidParamsTouched =
!!error || isInvalidParamsTouched(agg.type, aggType, paramsState);
const onAggSelect = useCallback(
(value) => {
if (agg.type !== value) {
onAggTypeChange(agg.id, value);
// reset touched and valid of params
onChangeParamsState({ type: AGG_PARAMS_ACTION_KEYS.RESET });
}
},
[onAggTypeChange, agg]
);
// reset validity before component destroyed
useUnmount(() => setValidity(true));
useEffect(() => {
Object.entries(editorConfig).forEach(([param, paramConfig]) => {
const paramOptions = agg.type.params.find((paramOption) => paramOption.name === param);
const hasFixedValue = paramConfig.hasOwnProperty(FIXED_VALUE_PROP);
const hasDefault = paramConfig.hasOwnProperty(DEFAULT_PROP);
// If the parameter has a fixed value in the config, set this value.
// Also for all supported configs we should freeze the editor for this param.
if (hasFixedValue || hasDefault) {
let newValue;
let property = FIXED_VALUE_PROP;
let typedParamConfig: EditorParamConfigType = paramConfig as FixedParam;
if (hasDefault) {
property = DEFAULT_PROP;
typedParamConfig = paramConfig as TimeIntervalParam;
}
if (paramOptions && paramOptions.deserialize) {
newValue = paramOptions.deserialize(typedParamConfig[property]);
} else {
newValue = typedParamConfig[property];
}
// this check is obligatory to avoid infinite render, because setAggParamValue creates a brand new agg object
if (agg.params[param] !== newValue) {
setAggParamValue(agg.id, param, newValue);
}
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editorConfig]);
useEffect(() => {
setTouched(false);
}, [agg.type, setTouched]);
useEffect(() => {
setValidity(isFormValid);
}, [isFormValid, agg.type, setValidity]);
useEffect(() => {
// when all invalid controls were touched or they are untouched
setTouched(isAllInvalidParamsTouched);
}, [isAllInvalidParamsTouched, setTouched]);
return (
<EuiForm
className={className}
isInvalid={!!error}
error={error}
data-test-subj="visAggEditorParams"
>
<DefaultEditorAggSelect
aggError={aggError}
id={agg.id}
indexPattern={indexPattern}
value={agg.type}
aggTypeOptions={groupedAggTypeOptions}
isSubAggregation={aggIndex >= 1 && groupName === AggGroupNames.Buckets}
showValidation={formIsTouched || aggType.touched}
setValue={onAggSelect}
onChangeAggType={onChangeAggType}
/>
{params.basic.map((param) => {
const model = paramsState[param.aggParam.name] || {
touched: false,
valid: true,
};
return (
<DefaultEditorAggParam
key={`${param.aggParam.name}${agg.type ? agg.type.name : ''}`}
disabled={disabledParams && disabledParams.includes(param.aggParam.name)}
formIsTouched={formIsTouched}
showValidation={formIsTouched || model.touched}
setAggParamValue={setAggParamValue}
onChangeParamsState={onChangeParamsState}
{...param}
/>
);
})}
{params.advanced.length ? (
<>
<EuiSpacer size="m" />
<EuiAccordion
id="advancedAccordion"
data-test-subj={`advancedParams-${agg.id}`}
buttonContent={i18n.translate('visDefaultEditor.advancedToggle.advancedLinkLabel', {
defaultMessage: 'Advanced',
})}
>
<EuiSpacer size="s" />
{params.advanced.map((param) => {
const model = paramsState[param.aggParam.name] || {
touched: false,
valid: true,
};
return (
<DefaultEditorAggParam
key={`${param.aggParam.name}${agg.type ? agg.type.name : ''}`}
disabled={disabledParams && disabledParams.includes(param.aggParam.name)}
formIsTouched={formIsTouched}
showValidation={formIsTouched || model.touched}
setAggParamValue={setAggParamValue}
onChangeParamsState={onChangeParamsState}
{...param}
/>
);
})}
</EuiAccordion>
</>
) : null}
</EuiForm>
);
}