diff --git a/app/client/src/components/wds/constants.ts b/app/client/src/components/wds/constants.ts index c2b66497b0..9093278fee 100644 --- a/app/client/src/components/wds/constants.ts +++ b/app/client/src/components/wds/constants.ts @@ -6,4 +6,5 @@ export const WDS_V2_WIDGET_MAP = { TEXT_WIDGET: "WDS_TEXT_WIDGET", TABLE_WIDGET_V2: "WDS_TABLE_WIDGET", BUTTON_GROUP_WIDGET: "WDS_BUTTON_GROUP_WIDGET", + CHECKBOX_GROUP_WIDGET: "WDS_CHECKBOX_GROUP_WIDGET", }; diff --git a/app/client/src/widgets/index.ts b/app/client/src/widgets/index.ts index e5c98808d7..71aa13d2de 100644 --- a/app/client/src/widgets/index.ts +++ b/app/client/src/widgets/index.ts @@ -65,6 +65,7 @@ import { WDSTextWidget } from "./wds/WDSTextWidget"; import type BaseWidget from "./BaseWidget"; import { WDSTableWidget } from "./wds/WDSTableWidget"; import { WDSButtonGroupWidget } from "./wds/WDSButtonGroupWidget"; +import { WDSCheckboxGroupWidget } from "./wds/WDSCheckboxGroupWidget"; const Widgets = [ CanvasWidget, @@ -123,6 +124,7 @@ const Widgets = [ WDSTextWidget, WDSTableWidget, WDSButtonGroupWidget, + WDSCheckboxGroupWidget, //Deprecated Widgets InputWidget, diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/component/index.tsx b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/component/index.tsx new file mode 100644 index 0000000000..300a2973d0 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/component/index.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { CheckboxGroup, Checkbox } from "@design-system/widgets"; +import type { CheckboxGroupComponentProps } from "./types"; +import type { OptionProps } from "../widget/types"; + +export const CheckboxGroupComponent = (props: CheckboxGroupComponentProps) => { + return ( + + {props.options.map((option: OptionProps, index: number) => { + return ( + + {option.label} + + ); + })} + + ); +}; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/component/types.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/component/types.ts new file mode 100644 index 0000000000..f38db10c6d --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/component/types.ts @@ -0,0 +1,19 @@ +import type { CheckboxGroupWidgetProps, OptionProps } from "../widget/types"; + +export interface CheckboxGroupComponentProps + extends Pick< + CheckboxGroupWidgetProps, + | "defaultSelectedValues" + | "isDisabled" + | "isRequired" + | "isValid" + | "labelPosition" + | "labelText" + | "onCheckChange" + | "options" + | "orientation" + | "widgetId" + > { + onChange: (value: string[]) => void; + selectedValues: OptionProps["value"][]; +} diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/anvilConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/anvilConfig.ts new file mode 100644 index 0000000000..3e259eae1a --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/anvilConfig.ts @@ -0,0 +1,10 @@ +import type { AnvilConfig } from "WidgetProvider/constants"; + +export const anvilConfig: AnvilConfig = { + widgetSize: { + maxHeight: {}, + maxWidth: {}, + minHeight: { base: "42px" }, + minWidth: { base: "130px" }, + }, +}; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/autocompleteConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/autocompleteConfig.ts new file mode 100644 index 0000000000..5836c4c3d4 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/autocompleteConfig.ts @@ -0,0 +1,12 @@ +import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils"; + +export const autocompleteConfig = { + "!doc": + "Checkbox group widget allows users to easily configure multiple checkboxes together.", + "!url": "https://docs.appsmith.com/widget-reference/checkbox-group", + isVisible: DefaultAutocompleteDefinitions.isVisible, + isDisabled: "bool", + isValid: "bool", + options: "[$__dropdownOption__$]", + selectedValues: "[string]", +}; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/defaultsConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/defaultsConfig.ts new file mode 100644 index 0000000000..f6b52854c3 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/defaultsConfig.ts @@ -0,0 +1,19 @@ +export const defaultsConfig = { + rows: 10, + columns: 20, + animateLoading: true, + options: [ + { label: "Blue", value: "BLUE" }, + { label: "Green", value: "GREEN" }, + { label: "Red", value: "RED" }, + ], + defaultSelectedValues: ["BLUE"], + isDisabled: false, + isRequired: false, + isVisible: true, + labelPosition: "left", + labelText: "Label", + orientation: "vertical", + version: 1, + widgetName: "CheckboxGroup", +}; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/featuresConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/featuresConfig.ts new file mode 100644 index 0000000000..60a677c66c --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/featuresConfig.ts @@ -0,0 +1,6 @@ +export const featuresConfig = { + dynamicHeight: { + sectionIndex: 3, + active: true, + }, +}; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/index.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/index.ts new file mode 100644 index 0000000000..47a4551c1e --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/index.ts @@ -0,0 +1,7 @@ +export * from "./propertyPaneConfig"; +export { metaConfig } from "./metaConfig"; +export { settersConfig } from "./settersConfig"; +export { defaultsConfig } from "./defaultsConfig"; +export { featuresConfig } from "./featuresConfig"; +export { autocompleteConfig } from "./autocompleteConfig"; +export { anvilConfig } from "./anvilConfig"; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/metaConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/metaConfig.ts new file mode 100644 index 0000000000..6bbcaf024e --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/metaConfig.ts @@ -0,0 +1,9 @@ +import IconSVG from "../icon.svg"; +import { WIDGET_TAGS } from "constants/WidgetConstants"; + +export const metaConfig = { + name: "Checkbox Group", + iconSVG: IconSVG, + tags: [WIDGET_TAGS.TOGGLES], + needsMeta: true, +}; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/contentConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/contentConfig.ts new file mode 100644 index 0000000000..34cdb7bc3e --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/contentConfig.ts @@ -0,0 +1,172 @@ +import { ValidationTypes } from "constants/WidgetValidation"; +import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; +import { defaultSelectedValuesValidation } from "./validations"; +import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; + +export const propertyPaneContentConfig = [ + { + sectionName: "Data", + 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: "", + }, + }, + ], + }, + }, + }, + }, + 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", + example: `apple | ["apple", "orange"]`, + autocompleteDataType: AutocompleteDataType.STRING, + }, + }, + }, + }, + ], + }, + { + sectionName: "Label", + children: [ + { + helpText: "Sets the label text of the widget", + propertyName: "labelText", + label: "Text", + controlType: "INPUT_TEXT", + placeholderText: "Enter label text", + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + { + helpText: "Sets the label position of the widget", + propertyName: "labelPosition", + label: "Position", + controlType: "ICON_TABS", + fullWidth: true, + options: [ + { label: "Left", value: "left" }, + { label: "Right", value: "right" }, + ], + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + ], + }, + { + sectionName: "Validations", + children: [ + { + propertyName: "isRequired", + label: "Required", + helpText: "Makes input to the widget mandatory", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.BOOLEAN, + }, + }, + ], + }, + { + sectionName: "General", + children: [ + { + 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: "Events", + children: [ + { + helpText: "When the check state is changed", + propertyName: "onCheckChange", + label: "onCheckChange", + controlType: "ACTION_SELECTOR", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: true, + }, + ], + }, +]; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/index.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/index.ts new file mode 100644 index 0000000000..4273f74125 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/index.ts @@ -0,0 +1,2 @@ +export { propertyPaneContentConfig } from "./contentConfig"; +export { propertyPaneStyleConfig } from "./styleConfig"; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/styleConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/styleConfig.ts new file mode 100644 index 0000000000..37a70b3aa2 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/styleConfig.ts @@ -0,0 +1,30 @@ +import { ValidationTypes } from "constants/WidgetValidation"; + +export const propertyPaneStyleConfig = [ + { + sectionName: "General", + children: [ + { + helpText: "Controls widget orientation", + propertyName: "orientation", + label: "Orientation", + controlType: "ICON_TABS", + fullWidth: true, + options: [ + { + label: "Horizontal", + value: "horizontal", + }, + { + label: "Vertical", + value: "vertical", + }, + ], + defaultValue: "vertical", + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + ], + }, +]; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/validations/defaultSelectedValuesValidation.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/validations/defaultSelectedValuesValidation.ts new file mode 100644 index 0000000000..d10dc9dda0 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/validations/defaultSelectedValuesValidation.ts @@ -0,0 +1,30 @@ +import type { ValidationResponse } from "constants/WidgetValidation"; + +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, + }; +} diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/validations/index.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/validations/index.ts new file mode 100644 index 0000000000..f1b59d04b6 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/propertyPaneConfig/validations/index.ts @@ -0,0 +1 @@ +export * from "./defaultSelectedValuesValidation"; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/settersConfig.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/settersConfig.ts new file mode 100644 index 0000000000..2a036bc6b0 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/config/settersConfig.ts @@ -0,0 +1,21 @@ +export const settersConfig = { + __setters: { + setVisibility: { + path: "isVisible", + type: "boolean", + }, + setDisabled: { + path: "isDisabled", + type: "boolean", + }, + setRequired: { + path: "isRequired", + type: "boolean", + }, + setSelectedOptions: { + path: "defaultSelectedValues", + type: "array", + accessor: "selectedValues", + }, + }, +}; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/icon.svg b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/icon.svg new file mode 100644 index 0000000000..a0a3a45d85 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/index.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/index.ts new file mode 100644 index 0000000000..8a6bcdb377 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/index.ts @@ -0,0 +1,3 @@ +import { WDSCheckboxGroupWidget } from "./widget"; + +export { WDSCheckboxGroupWidget }; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/widget/index.tsx new file mode 100644 index 0000000000..ac4b064a3c --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/widget/index.tsx @@ -0,0 +1,127 @@ +import React from "react"; +import type { SetterConfig } from "entities/AppTheming"; +import type { DerivedPropertiesMap } from "WidgetProvider/factory"; +import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; +import { xor } from "lodash"; +import BaseWidget from "widgets/BaseWidget"; +import type { CheckboxGroupWidgetProps, OptionProps } from "./types"; +import type { WidgetState } from "widgets/BaseWidget"; +import type { AnvilConfig } from "WidgetProvider/constants"; +import { CheckboxGroupComponent } from "../component"; +import { + anvilConfig, + autocompleteConfig, + defaultsConfig, + featuresConfig, + metaConfig, + propertyPaneContentConfig, + propertyPaneStyleConfig, + settersConfig, +} from "./../config"; + +class WDSCheckboxGroupWidget extends BaseWidget< + CheckboxGroupWidgetProps, + WidgetState +> { + static type = "WDS_CHECKBOX_GROUP_WIDGET"; + + static getConfig() { + return metaConfig; + } + + static getFeatures() { + return featuresConfig; + } + + static getDefaults() { + return defaultsConfig; + } + + static getAutoLayoutConfig() { + return {}; + } + + static getAnvilConfig(): AnvilConfig | null { + return anvilConfig; + } + + static getAutocompleteDefinitions() { + return autocompleteConfig; + } + + static getSetterConfig(): SetterConfig { + return settersConfig; + } + + static getPropertyPaneContentConfig() { + return propertyPaneContentConfig; + } + + static getPropertyPaneStyleConfig() { + return propertyPaneStyleConfig; + } + + static getDefaultPropertiesMap(): Record { + return { + selectedValues: "defaultSelectedValues", + }; + } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return { + value: `{{this.selectedValues}}`, + isValid: `{{ this.isRequired ? !!this.selectedValues.length : true }}`, + }; + } + + static getMetaPropertiesMap(): Record { + return { + selectedValues: undefined, + isDirty: false, + }; + } + + componentDidUpdate(prevProps: CheckboxGroupWidgetProps) { + if ( + xor(this.props.defaultSelectedValues, prevProps.defaultSelectedValues) + .length > 0 && + this.props.isDirty + ) { + this.props.updateWidgetMetaProperty("isDirty", false); + } + } + + handleChange = (selectedValues: OptionProps["value"][]) => { + if (!this.props.isDirty) { + this.props.updateWidgetMetaProperty("isDirty", true); + } + + this.props.updateWidgetMetaProperty("selectedValues", selectedValues, { + triggerPropertyName: "onCheckChange", + dynamicString: this.props.onCheckChange, + event: { + type: EventType.ON_CHECKBOX_GROUP_SELECTION_CHANGE, + }, + }); + }; + + getWidgetView() { + return ( + + ); + } +} + +export { WDSCheckboxGroupWidget }; diff --git a/app/client/src/widgets/wds/WDSCheckboxGroupWidget/widget/types.ts b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/widget/types.ts new file mode 100644 index 0000000000..7731d91772 --- /dev/null +++ b/app/client/src/widgets/wds/WDSCheckboxGroupWidget/widget/types.ts @@ -0,0 +1,19 @@ +import type { WidgetProps } from "widgets/BaseWidget"; + +export interface OptionProps { + label?: string; + value: string; +} + +export interface CheckboxGroupWidgetProps extends WidgetProps { + defaultSelectedValues?: OptionProps["value"][]; + isDisabled: boolean; + isRequired?: boolean; + isValid?: boolean; + isVisible: boolean; + labelPosition: "left" | "right"; + labelText?: string; + onCheckChange?: string; + options: OptionProps[]; + orientation: "vertical" | "horizontal"; +}