358 lines
11 KiB
TypeScript
358 lines
11 KiB
TypeScript
import React from "react";
|
|
import { compact } from "lodash";
|
|
|
|
import {
|
|
ValidationResponse,
|
|
ValidationTypes,
|
|
} from "constants/WidgetValidation";
|
|
import { WidgetType } from "constants/WidgetConstants";
|
|
import { DerivedPropertiesMap } from "utils/WidgetFactory";
|
|
import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget";
|
|
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
|
|
|
import { AutocompleteDataType } from "utils/autocomplete/TernServer";
|
|
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
|
|
|
import CheckboxGroupComponent, { OptionProps } from "../component";
|
|
import { CheckboxGroupAlignmentTypes } from "components/constants";
|
|
|
|
export function defaultSelectedValuesValidation(
|
|
value: unknown,
|
|
): ValidationResponse {
|
|
let values: string[] = [];
|
|
|
|
if (typeof value === "string") {
|
|
try {
|
|
values = JSON.parse(value);
|
|
if (!Array.isArray(values)) {
|
|
throw new Error();
|
|
}
|
|
} catch {
|
|
values = value.length ? value.split(",") : [];
|
|
if (values.length > 0) {
|
|
values = values.map((_v: string) => _v.trim());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
values = Array.from(new Set(value));
|
|
}
|
|
|
|
return {
|
|
isValid: true,
|
|
parsed: values,
|
|
};
|
|
}
|
|
|
|
class CheckboxGroupWidget extends BaseWidget<
|
|
CheckboxGroupWidgetProps,
|
|
WidgetState
|
|
> {
|
|
static getPropertyPaneConfig() {
|
|
return [
|
|
{
|
|
sectionName: "General",
|
|
children: [
|
|
{
|
|
helpText: "Displays a list of unique checkbox options",
|
|
propertyName: "options",
|
|
label: "Options",
|
|
controlType: "OPTION_INPUT",
|
|
isJSConvertible: true,
|
|
isBindProperty: true,
|
|
isTriggerProperty: false,
|
|
validation: {
|
|
type: ValidationTypes.ARRAY,
|
|
params: {
|
|
default: [],
|
|
unique: ["value"],
|
|
children: {
|
|
type: ValidationTypes.OBJECT,
|
|
params: {
|
|
required: true,
|
|
allowedKeys: [
|
|
{
|
|
name: "label",
|
|
type: ValidationTypes.TEXT,
|
|
params: {
|
|
default: "",
|
|
required: true,
|
|
},
|
|
},
|
|
{
|
|
name: "value",
|
|
type: ValidationTypes.TEXT,
|
|
params: {
|
|
default: "",
|
|
required: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
evaluationSubstitutionType:
|
|
EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
|
},
|
|
{
|
|
helpText: "Sets the values of the options checked by default",
|
|
propertyName: "defaultSelectedValues",
|
|
label: "Default Selected Values",
|
|
placeholderText: '["apple", "orange"]',
|
|
controlType: "INPUT_TEXT",
|
|
isBindProperty: true,
|
|
isTriggerProperty: false,
|
|
validation: {
|
|
type: ValidationTypes.FUNCTION,
|
|
params: {
|
|
fn: defaultSelectedValuesValidation,
|
|
expected: {
|
|
type: "String or Array<string>",
|
|
example: `apple | ["apple", "orange"]`,
|
|
autocompleteDataType: AutocompleteDataType.STRING,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
propertyName: "isInline",
|
|
label: "Inline",
|
|
controlType: "SWITCH",
|
|
helpText: "Displays the checkboxes horizontally",
|
|
isJSConvertible: true,
|
|
isBindProperty: true,
|
|
isTriggerProperty: false,
|
|
validation: {
|
|
type: ValidationTypes.BOOLEAN,
|
|
},
|
|
},
|
|
{
|
|
propertyName: "isRequired",
|
|
label: "Required",
|
|
helpText: "Makes input to the widget mandatory",
|
|
controlType: "SWITCH",
|
|
isJSConvertible: true,
|
|
isBindProperty: true,
|
|
isTriggerProperty: false,
|
|
validation: {
|
|
type: ValidationTypes.BOOLEAN,
|
|
},
|
|
},
|
|
{
|
|
propertyName: "isVisible",
|
|
label: "Visible",
|
|
helpText: "Controls the visibility of the widget",
|
|
controlType: "SWITCH",
|
|
isJSConvertible: true,
|
|
isBindProperty: true,
|
|
isTriggerProperty: false,
|
|
validation: {
|
|
type: ValidationTypes.BOOLEAN,
|
|
},
|
|
},
|
|
{
|
|
propertyName: "isDisabled",
|
|
label: "Disabled",
|
|
controlType: "SWITCH",
|
|
helpText: "Disables input to this widget",
|
|
isJSConvertible: true,
|
|
isBindProperty: true,
|
|
isTriggerProperty: false,
|
|
validation: {
|
|
type: ValidationTypes.BOOLEAN,
|
|
},
|
|
},
|
|
{
|
|
propertyName: "animateLoading",
|
|
label: "Animate Loading",
|
|
controlType: "SWITCH",
|
|
helpText: "Controls the loading of the widget",
|
|
defaultValue: true,
|
|
isJSConvertible: true,
|
|
isBindProperty: true,
|
|
isTriggerProperty: false,
|
|
validation: { type: ValidationTypes.BOOLEAN },
|
|
},
|
|
],
|
|
},
|
|
{
|
|
sectionName: "Styles",
|
|
children: [
|
|
{
|
|
propertyName: "optionAlignment",
|
|
label: "Alignment",
|
|
controlType: "DROP_DOWN",
|
|
helpText: "Sets alignment between options.",
|
|
options: [
|
|
{
|
|
label: "None",
|
|
value: CheckboxGroupAlignmentTypes.NONE,
|
|
},
|
|
{
|
|
label: "Start",
|
|
value: CheckboxGroupAlignmentTypes.START,
|
|
},
|
|
{
|
|
label: "End",
|
|
value: CheckboxGroupAlignmentTypes.END,
|
|
},
|
|
{
|
|
label: "Center",
|
|
value: CheckboxGroupAlignmentTypes.CENTER,
|
|
},
|
|
{
|
|
label: "Between",
|
|
value: CheckboxGroupAlignmentTypes.SPACE_BETWEEN,
|
|
},
|
|
{
|
|
label: "Around",
|
|
value: CheckboxGroupAlignmentTypes.SPACE_AROUND,
|
|
},
|
|
],
|
|
isJSConvertible: true,
|
|
isBindProperty: true,
|
|
isTriggerProperty: false,
|
|
validation: {
|
|
type: ValidationTypes.TEXT,
|
|
params: {
|
|
allowedValues: [
|
|
CheckboxGroupAlignmentTypes.NONE,
|
|
CheckboxGroupAlignmentTypes.START,
|
|
CheckboxGroupAlignmentTypes.END,
|
|
CheckboxGroupAlignmentTypes.CENTER,
|
|
CheckboxGroupAlignmentTypes.SPACE_BETWEEN,
|
|
CheckboxGroupAlignmentTypes.SPACE_AROUND,
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
sectionName: "Events",
|
|
children: [
|
|
{
|
|
helpText: "Triggers an action when the check state is changed",
|
|
propertyName: "onSelectionChange",
|
|
label: "onSelectionChange",
|
|
controlType: "ACTION_SELECTOR",
|
|
isJSConvertible: true,
|
|
isBindProperty: true,
|
|
isTriggerProperty: true,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
}
|
|
|
|
static getDefaultPropertiesMap(): Record<string, string> {
|
|
return {
|
|
selectedValues: "defaultSelectedValues",
|
|
};
|
|
}
|
|
|
|
static getMetaPropertiesMap(): Record<string, any> {
|
|
return {
|
|
selectedValues: undefined,
|
|
};
|
|
}
|
|
|
|
static getDerivedPropertiesMap(): DerivedPropertiesMap {
|
|
return {
|
|
isValid: `{{ this.isRequired ? !!this.selectedValues.length : true }}`,
|
|
};
|
|
}
|
|
|
|
componentDidUpdate(prevProps: CheckboxGroupWidgetProps) {
|
|
if (
|
|
Array.isArray(prevProps.options) &&
|
|
Array.isArray(this.props.options) &&
|
|
this.props.options.length !== prevProps.options.length
|
|
) {
|
|
const prevOptions = compact(prevProps.options).map(
|
|
(prevOption) => prevOption.value,
|
|
);
|
|
const options = compact(this.props.options).map((option) => option.value);
|
|
|
|
// Get an array containing all the options of prevOptions that are not in options and vice-versa
|
|
const diffOptions = prevOptions
|
|
.filter((option) => !options.includes(option))
|
|
.concat(options.filter((option) => !prevOptions.includes(option)));
|
|
|
|
let selectedValues = this.props.selectedValues.filter(
|
|
(selectedValue: string) => !diffOptions.includes(selectedValue),
|
|
);
|
|
// if selectedValues empty, and options have changed, set defaultSelectedValues
|
|
if (!selectedValues.length && this.props.defaultSelectedValues.length) {
|
|
selectedValues = this.props.defaultSelectedValues;
|
|
}
|
|
|
|
this.props.updateWidgetMetaProperty("selectedValues", selectedValues, {
|
|
triggerPropertyName: "onSelectionChange",
|
|
dynamicString: this.props.onSelectionChange,
|
|
event: {
|
|
type: EventType.ON_CHECK_CHANGE,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
getPageView() {
|
|
return (
|
|
<CheckboxGroupComponent
|
|
isDisabled={this.props.isDisabled}
|
|
isInline={this.props.isInline}
|
|
isRequired={this.props.isRequired}
|
|
isValid={this.props.isValid}
|
|
key={this.props.widgetId}
|
|
onChange={this.handleCheckboxChange}
|
|
optionAlignment={this.props.optionAlignment}
|
|
options={compact(this.props.options)}
|
|
rowSpace={this.props.parentRowSpace}
|
|
selectedValues={this.props.selectedValues}
|
|
widgetId={this.props.widgetId}
|
|
/>
|
|
);
|
|
}
|
|
|
|
static getWidgetType(): WidgetType {
|
|
return "CHECKBOX_GROUP_WIDGET";
|
|
}
|
|
|
|
private handleCheckboxChange = (value: string) => {
|
|
return (event: React.FormEvent<HTMLElement>) => {
|
|
let { selectedValues } = this.props;
|
|
const isChecked = (event.target as HTMLInputElement).checked;
|
|
if (isChecked) {
|
|
selectedValues = [...selectedValues, value];
|
|
} else {
|
|
selectedValues = selectedValues.filter(
|
|
(item: string) => item !== value,
|
|
);
|
|
}
|
|
|
|
this.props.updateWidgetMetaProperty("selectedValues", selectedValues, {
|
|
triggerPropertyName: "onSelectionChange",
|
|
dynamicString: this.props.onSelectionChange,
|
|
event: {
|
|
type: EventType.ON_CHECKBOX_GROUP_SELECTION_CHANGE,
|
|
},
|
|
});
|
|
};
|
|
};
|
|
}
|
|
|
|
export interface CheckboxGroupWidgetProps extends WidgetProps {
|
|
options: OptionProps[];
|
|
isInline: boolean;
|
|
isRequired?: boolean;
|
|
isDisabled?: boolean;
|
|
isValid?: boolean;
|
|
onCheckChanged?: string;
|
|
optionAlignment?: string;
|
|
}
|
|
|
|
export default CheckboxGroupWidget;
|