Skip to content

Commit

Permalink
remove Select key value selects, fix default
Browse files Browse the repository at this point in the history
  • Loading branch information
znamenskii-ilia committed Sep 6, 2024
1 parent 08ec688 commit ad0b1ba
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 304 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,22 @@ export function optionsCustomValidation(
});
}

const isValidKeys = options.every((option) => {
return (
_.isPlainObject(option) &&
_.has(option, "label") &&
_.has(option, "value")
);
});

if (!isValidKeys) {
return createErrorValidationResponse(options, {
name: "ValidationError",
message:
'This value does not evaluate to type Array<{ "label": "string", "value": "string" | number }>',
});
}

for (let i = 0; i < options.length; i++) {
const option = options[i];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,11 @@ class WDSComboBoxWidget extends BaseWidget<
id: option["value"] as string,
}));

return items;
const isValidItems = items.every(
(item) => item.label !== undefined && item.id !== undefined,
);

return isValidItems ? items : [];
}

return [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,237 +1,16 @@
import {
ValidationTypes,
type ValidationResponse,
} from "constants/WidgetValidation";
import { get, isPlainObject, uniq, type LoDashStatic } from "lodash";
import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType";

import { ValidationTypes } from "constants/WidgetValidation";
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
import { EVAL_VALUE_PATH } from "../../../../../utils/DynamicBindingUtils";
import type { PropertyUpdates } from "../../../../../WidgetProvider/constants";
import type { WidgetProps } from "../../../../BaseWidget";
import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType";
import type { WDSSelectWidgetProps } from "../../widget/types";
import {
defaultOptionValidation,
optionsCustomValidation,
} from "./validations";
import type { WidgetProps } from "../../../../BaseWidget";
import type { PropertyUpdates } from "../../../../../WidgetProvider/constants";

type WidgetTypeValue = "SELECT" | "COMBOBOX";

interface ValidationErrorMessage {
name: string;
message: string;
}

export const getOptionLabelValueExpressionPrefix = (widget: WidgetProps) =>
`{{${widget.widgetName}.sourceData.map((item) => (`;

export const optionLabelValueExpressionSuffix = `))}}`;

export function getLabelValueKeyOptions(
widget: WidgetProps,
): Record<string, unknown>[] {
// UTILS
const isTrueObject = (item: unknown): item is Record<string, unknown> => {
return Object.prototype.toString.call(item) === "[object Object]";
};

const sourceData = get(widget, `${EVAL_VALUE_PATH}.options`);
const widgetOptions = get(widget, "options");
const options = sourceData || widgetOptions;

// Is Form mode, otherwise it is JS mode
if (Array.isArray(widgetOptions)) {
return options.map((option: Record<string, unknown> | string) => {
if (isTrueObject(option)) {
return {
label: option[widget.optionLabel],
value: option[widget.optionValue],
};
}

return [];
});
}

if (Array.isArray(options)) {
const x = uniq(
options.reduce((keys, obj) => {
if (isPlainObject(obj)) {
Object.keys(obj).forEach((d) => keys.push(d));
}

return keys;
}, []),
).map((d: unknown) => ({
label: d,
value: d,
}));

return x;
} else {
return [];
}
}

export function labelKeyValidation(
value: unknown,
widgetProps: WDSSelectWidgetProps,
_: LoDashStatic,
) {
// UTILS
const hasDuplicates = (array: unknown[]): boolean => {
const set = new Set(array);

return set.size !== array.length;
};

const createErrorValidationResponse = (
value: unknown,
message: ValidationErrorMessage,
): ValidationResponse => ({
isValid: false,
parsed: value,
messages: [message],
});

const createSuccessValidationResponse = (
value: unknown,
): ValidationResponse => ({
isValid: true,
parsed: value,
});

if (value === "" || _.isNil(value)) {
return createErrorValidationResponse(value, {
name: "ValidationError",
message: `value does not evaluate to type: string | Array<string>`,
});
}

if (Array.isArray(widgetProps.options)) {
const values = _.map(widgetProps.options, (option) => {
return option[widgetProps.optionLabel];
}).filter((d) => d);

if (values.length && hasDuplicates(values)) {
return createErrorValidationResponse(value, {
name: "ValidationError",
message: "Duplicate values found, value must be unique",
});
}
}

if (_.isString(value)) {
const keys = _.map(widgetProps.options, _.keys).flat();

if (!keys.includes(value)) {
return createErrorValidationResponse(value, {
name: "ValidationError",
message: "value key should be present in the options",
});
}

return createSuccessValidationResponse(value);
} else if (_.isArray(value)) {
const errorIndex = value.findIndex((d) => !_.isString(d));

if (errorIndex === -1) {
return createSuccessValidationResponse(value);
}

return createErrorValidationResponse(value, {
name: "ValidationError",
message: `Invalid entry at index: ${errorIndex}. This value does not evaluate to type: string`,
});
} else {
return createErrorValidationResponse(value, {
name: "ValidationError",
message: `value does not evaluate to type: string | Array<string>`,
});
}
}

export function getLabelValueAdditionalAutocompleteData(props: WidgetProps) {
const keys = getLabelValueKeyOptions(props);

return {
item: keys
.map((d) => d.label)
.reduce((prev: Record<string, string>, curr: unknown) => {
prev[curr as string] = "";

return prev;
}, {}),
};
}

export function valueKeyValidation(
value: unknown,
widgetProps: WDSSelectWidgetProps,
_: LoDashStatic,
) {
// UTILS
const isTrueObject = (item: unknown): item is Record<string, unknown> => {
return Object.prototype.toString.call(item) === "[object Object]";
};

const hasDuplicates = (array: unknown[]): boolean => {
const set = new Set(array);

return set.size !== array.length;
};

const createErrorValidationResponse = (
value: unknown,
message: ValidationErrorMessage,
): ValidationResponse => ({
isValid: false,
parsed: value,
messages: [message],
});

const createSuccessValidationResponse = (
value: unknown,
): ValidationResponse => ({
isValid: true,
parsed: value,
});

if (value === "" || _.isNil(value) || !_.isString(value)) {
return createErrorValidationResponse(value, {
name: "ValidationError",
message:
"value does not evaluate to type: string | Array<string| number | boolean>",
});
}

if (!_.flatMap(widgetProps.options, _.keys).includes(value)) {
return createErrorValidationResponse(value, {
name: "ValidationError",
message: "value key should be present in the options",
});
}

if (!isTrueObject(widgetProps.options)) {
return createSuccessValidationResponse(value);
}

const values = _.map(widgetProps.options, (option) => {
if (isTrueObject(option)) {
return option[widgetProps.optionValue];
}
}).filter((d) => d);

if (values.length && hasDuplicates(values)) {
return createErrorValidationResponse(value, {
name: "ValidationError",
message: "Duplicate values found, value must be unique",
});
}

return createSuccessValidationResponse(value);
}

export const propertyPaneContentConfig = [
{
sectionName: "Data",
Expand Down Expand Up @@ -306,82 +85,6 @@ export const propertyPaneContentConfig = [
},
evaluationSubstitutionType: EvaluationSubstitutionType.SMART_SUBSTITUTE,
},
{
helpText: "Choose or set a field from source data as the display label",
propertyName: "optionLabel",
label: "Label key",
controlType: "DROP_DOWN",
customJSControl: "WRAPPED_CODE_EDITOR",
controlConfig: {
wrapperCode: {
prefix: getOptionLabelValueExpressionPrefix,
suffix: optionLabelValueExpressionSuffix,
},
},
placeholderText: "",
isBindProperty: true,
isTriggerProperty: false,
isJSConvertible: true,
evaluatedDependencies: ["options"],
options: getLabelValueKeyOptions,
alwaysShowSelected: true,
validation: {
type: ValidationTypes.FUNCTION,
params: {
fn: labelKeyValidation,
expected: {
type: "String or Array<string>",
example: `color | ["blue", "green"]`,
autocompleteDataType: AutocompleteDataType.STRING,
},
},
},
dependencies: ["options", "dynamicPropertyPathList"],
additionalAutoComplete: getLabelValueAdditionalAutocompleteData,
hidden: (props: WDSSelectWidgetProps) => {
return !(props.dynamicPropertyPathList || []).some(
({ key }) => key === "options",
);
},
},
{
helpText: "Choose or set a field from source data as the value",
propertyName: "optionValue",
label: "Value key",
controlType: "DROP_DOWN",
customJSControl: "WRAPPED_CODE_EDITOR",
controlConfig: {
wrapperCode: {
prefix: getOptionLabelValueExpressionPrefix,
suffix: optionLabelValueExpressionSuffix,
},
},
placeholderText: "",
isBindProperty: true,
isTriggerProperty: false,
isJSConvertible: true,
evaluatedDependencies: ["options"],
options: getLabelValueKeyOptions,
alwaysShowSelected: true,
validation: {
type: ValidationTypes.FUNCTION,
params: {
fn: valueKeyValidation,
expected: {
type: "String or Array<string | number | boolean>",
example: `color | [1, "orange"]`,
autocompleteDataType: AutocompleteDataType.STRING,
},
},
},
dependencies: ["options", "dynamicPropertyPathList"],
additionalAutoComplete: getLabelValueAdditionalAutocompleteData,
hidden: (props: WDSSelectWidgetProps) => {
return !(props.dynamicPropertyPathList || []).some(
({ key }) => key === "options",
);
},
},
{
helpText: "Sets a default selected option",
propertyName: "defaultOptionValue",
Expand Down
Loading

0 comments on commit ad0b1ba

Please sign in to comment.