From 3ea74ec78ebf666b114b2f85e5a826e46585fe56 Mon Sep 17 00:00:00 2001 From: Dhruvik Neharia Date: Mon, 23 Oct 2023 21:51:20 +0530 Subject: [PATCH] feat: WDS icon button widget integration (#27928) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Adds WDSIconButton Widget. #### PR fixes following issue(s) Fixes #27538 > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: Pawan Kumar --- app/client/src/components/wds/constants.ts | 1 + app/client/src/widgets/index.ts | 2 + .../WDSIconButtonWidget/component/index.tsx | 19 +++ .../WDSIconButtonWidget/component/types.ts | 16 +++ .../config/autocompleteConfig.ts | 8 ++ .../config/defaultsConfig.ts | 19 +++ .../wds/WDSIconButtonWidget/config/index.ts | 5 + .../WDSIconButtonWidget/config/metaConfig.ts | 9 ++ .../propertyPaneConfig/contentConfig.ts | 87 ++++++++++++++ .../config/propertyPaneConfig/index.ts | 2 + .../config/propertyPaneConfig/styleConfig.ts | 53 ++++++++ .../config/settersConfig.ts | 12 ++ .../widgets/wds/WDSIconButtonWidget/icon.svg | 3 + .../widgets/wds/WDSIconButtonWidget/index.tsx | 3 + .../wds/WDSIconButtonWidget/widget/index.tsx | 113 ++++++++++++++++++ .../wds/WDSIconButtonWidget/widget/types.ts | 16 +++ 16 files changed, 368 insertions(+) create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/component/index.tsx create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/component/types.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/autocompleteConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/defaultsConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/index.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/metaConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/contentConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/index.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/styleConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/config/settersConfig.ts create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/icon.svg create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/index.tsx create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx create mode 100644 app/client/src/widgets/wds/WDSIconButtonWidget/widget/types.ts diff --git a/app/client/src/components/wds/constants.ts b/app/client/src/components/wds/constants.ts index c89ff8b026..ec7a5a8afb 100644 --- a/app/client/src/components/wds/constants.ts +++ b/app/client/src/components/wds/constants.ts @@ -2,6 +2,7 @@ export const WDS_V2_WIDGET_MAP = { BUTTON_WIDGET: "WDS_BUTTON_WIDGET", INPUT_WIDGET_V2: "WDS_INPUT_WIDGET", CHECKBOX_WIDGET: "WDS_CHECKBOX_WIDGET", + ICON_BUTTON_WIDGET: "WDS_ICON_BUTTON_WIDGET", TEXT_WIDGET: "WDS_TEXT_WIDGET", TABLE_WIDGET_V2: "WDS_TABLE_WIDGET", }; diff --git a/app/client/src/widgets/index.ts b/app/client/src/widgets/index.ts index d022c05e2d..caf4172148 100644 --- a/app/client/src/widgets/index.ts +++ b/app/client/src/widgets/index.ts @@ -60,6 +60,7 @@ import ListWidgetV2 from "./ListWidgetV2"; import { WDSButtonWidget } from "./wds/WDSButtonWidget"; import { WDSInputWidget } from "./wds/WDSInputWidget"; import { WDSCheckboxWidget } from "./wds/WDSCheckboxWidget"; +import { WDSIconButtonWidget } from "./wds/WDSIconButtonWidget"; import { WDSTextWidget } from "./wds/WDSTextWidget"; import type BaseWidget from "./BaseWidget"; import { WDSTableWidget } from "./wds/WDSTableWidget"; @@ -117,6 +118,7 @@ const Widgets = [ WDSButtonWidget, WDSInputWidget, WDSCheckboxWidget, + WDSIconButtonWidget, WDSTextWidget, WDSTableWidget, diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/component/index.tsx b/app/client/src/widgets/wds/WDSIconButtonWidget/component/index.tsx new file mode 100644 index 0000000000..e0def84118 --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/component/index.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Icon as BIcon } from "@blueprintjs/core"; +import { IconButton, Tooltip } from "@design-system/widgets"; +import type { IconButtonComponentProps } from "./types"; + +export const IconButtonComponent = (props: IconButtonComponentProps) => { + const { iconName, tooltip, ...rest } = props; + const icon = + iconName && + (() => { + return ; + }); + + return ( + + + + ); +}; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/component/types.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/component/types.ts new file mode 100644 index 0000000000..3af971afb3 --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/component/types.ts @@ -0,0 +1,16 @@ +import type { IconName } from "@blueprintjs/icons"; +import type { IconButtonProps } from "@design-system/widgets"; + +export interface IconButtonComponentProps { + tooltip?: string; + visuallyDisabled?: boolean; + isLoading: boolean; + iconName?: IconName; + isDisabled?: boolean; + variant?: IconButtonProps["variant"]; + color?: IconButtonProps["color"]; + onPress?: IconButtonProps["onPress"]; + minWidth?: number; + maxWidth?: number; + minHeight?: number; +} diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/config/autocompleteConfig.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/config/autocompleteConfig.ts new file mode 100644 index 0000000000..c003ee9072 --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/config/autocompleteConfig.ts @@ -0,0 +1,8 @@ +import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils"; + +export const autocompleteConfig = { + "!doc": + "Icon button widget is just an icon, along with all other button properties.", + "!url": "https://docs.appsmith.com/widget-reference/icon-button", + isVisible: DefaultAutocompleteDefinitions.isVisible, +}; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/config/defaultsConfig.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/config/defaultsConfig.ts new file mode 100644 index 0000000000..1dee7795aa --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/config/defaultsConfig.ts @@ -0,0 +1,19 @@ +import { BUTTON_VARIANTS, COLORS } from "@design-system/widgets"; +import { IconNames } from "@blueprintjs/icons"; +import { ResponsiveBehavior } from "layoutSystems/common/utils/constants"; +import { ICON_BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; + +export const defaultsConfig = { + iconName: IconNames.PLUS, + buttonVariant: BUTTON_VARIANTS.filled, + buttonColor: COLORS.accent, + isDisabled: false, + isVisible: true, + rows: 4, + columns: 4, + widgetName: "IconButton", + version: 1, + animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Hug, + minWidth: ICON_BUTTON_MIN_WIDTH, +}; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/config/index.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/config/index.ts new file mode 100644 index 0000000000..3233e112d9 --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/config/index.ts @@ -0,0 +1,5 @@ +export * from "./propertyPaneConfig"; +export { autocompleteConfig } from "./autocompleteConfig"; +export { defaultsConfig } from "./defaultsConfig"; +export { metaConfig } from "./metaConfig"; +export { settersConfig } from "./settersConfig"; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/config/metaConfig.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/config/metaConfig.ts new file mode 100644 index 0000000000..fb74fc53cd --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/config/metaConfig.ts @@ -0,0 +1,9 @@ +import IconSVG from "../icon.svg"; +import { WIDGET_TAGS } from "constants/WidgetConstants"; + +export const metaConfig = { + name: "Icon button", + iconSVG: IconSVG, + tags: [WIDGET_TAGS.BUTTONS], + searchTags: ["click", "submit"], +}; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/contentConfig.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/contentConfig.ts new file mode 100644 index 0000000000..32b563110a --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/contentConfig.ts @@ -0,0 +1,87 @@ +import { ValidationTypes } from "constants/WidgetValidation"; +import { IconNames } from "@blueprintjs/icons"; + +const ICON_NAMES = Object.keys(IconNames).map( + (name: string) => IconNames[name as keyof typeof IconNames], +); + +export const propertyPaneContentConfig = [ + { + sectionName: "Basic", + children: [ + { + propertyName: "iconName", + label: "Icon", + helpText: "Sets the icon to be used for the icon button", + controlType: "ICON_SELECT", + defaultIconName: "plus", + hideNoneIcon: true, + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + params: { + allowedValues: ICON_NAMES, + default: IconNames.PLUS, + }, + }, + }, + { + helpText: "when the button is clicked", + propertyName: "onClick", + label: "onClick", + controlType: "ACTION_SELECTOR", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: true, + }, + ], + }, + { + sectionName: "General", + children: [ + { + helpText: "Show helper text with button on hover", + propertyName: "tooltip", + label: "Tooltip", + controlType: "INPUT_TEXT", + placeholderText: "Add Input Field", + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + { + propertyName: "isVisible", + helpText: "Controls the visibility of the widget", + label: "Visible", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + propertyName: "isDisabled", + helpText: "Disables input to the widget", + label: "Disabled", + controlType: "SWITCH", + 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 }, + }, + ], + }, +]; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/index.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/index.ts new file mode 100644 index 0000000000..4273f74125 --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/index.ts @@ -0,0 +1,2 @@ +export { propertyPaneContentConfig } from "./contentConfig"; +export { propertyPaneStyleConfig } from "./styleConfig"; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/styleConfig.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/styleConfig.ts new file mode 100644 index 0000000000..34c2dd186f --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/config/propertyPaneConfig/styleConfig.ts @@ -0,0 +1,53 @@ +import { capitalize } from "lodash"; +import { BUTTON_VARIANTS, COLORS } from "@design-system/widgets"; +import { ValidationTypes } from "constants/WidgetValidation"; + +export const propertyPaneStyleConfig = [ + { + sectionName: "General", + children: [ + { + propertyName: "buttonVariant", + label: "Button variant", + controlType: "ICON_TABS", + fullWidth: true, + helpText: "Sets the variant of the button", + options: Object.values(BUTTON_VARIANTS).map((variant) => ({ + label: capitalize(variant), + value: variant, + })), + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + params: { + allowedValues: Object.values(BUTTON_VARIANTS), + default: BUTTON_VARIANTS.filled, + }, + }, + }, + { + propertyName: "buttonColor", + label: "Button color", + controlType: "DROP_DOWN", + fullWidth: true, + helpText: "Sets the semantic color of the button", + options: Object.values(COLORS).map((semantic) => ({ + label: capitalize(semantic), + value: semantic, + })), + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + params: { + allowedValues: Object.values(COLORS), + default: COLORS.accent, + }, + }, + }, + ], + }, +]; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/config/settersConfig.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/config/settersConfig.ts new file mode 100644 index 0000000000..888f2f9bcb --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/config/settersConfig.ts @@ -0,0 +1,12 @@ +export const settersConfig = { + __setters: { + setVisibility: { + path: "isVisible", + type: "boolean", + }, + setDisabled: { + path: "isDisabled", + type: "boolean", + }, + }, +}; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/icon.svg b/app/client/src/widgets/wds/WDSIconButtonWidget/icon.svg new file mode 100644 index 0000000000..5a7a953e1a --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/index.tsx b/app/client/src/widgets/wds/WDSIconButtonWidget/index.tsx new file mode 100644 index 0000000000..58186d0659 --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/index.tsx @@ -0,0 +1,3 @@ +import { WDSIconButtonWidget } from "./widget"; + +export { WDSIconButtonWidget }; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx b/app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx new file mode 100644 index 0000000000..cb14bfee36 --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/widget/index.tsx @@ -0,0 +1,113 @@ +import React from "react"; +import type { SetterConfig } from "entities/AppTheming"; +import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; +import BaseWidget from "widgets/BaseWidget"; +import { + metaConfig, + defaultsConfig, + autocompleteConfig, + propertyPaneContentConfig, + propertyPaneStyleConfig, + settersConfig, +} from "./../config"; +import type { IconButtonWidgetProps, IconButtonWidgetState } from "./types"; +import { IconButtonComponent } from "../component"; + +class WDSIconButtonWidget extends BaseWidget< + IconButtonWidgetProps, + IconButtonWidgetState +> { + constructor(props: IconButtonWidgetProps) { + super(props); + + this.state = { + isLoading: false, + }; + } + + static type = "WDS_ICON_BUTTON_WIDGET"; + + static getConfig() { + return metaConfig; + } + + static getDefaults() { + return defaultsConfig; + } + + static getAutoLayoutConfig() { + return {}; + } + + static getAutocompleteDefinitions() { + return autocompleteConfig; + } + + static getPropertyPaneContentConfig() { + return propertyPaneContentConfig; + } + + static getPropertyPaneStyleConfig() { + return propertyPaneStyleConfig; + } + + static getSetterConfig(): SetterConfig { + return settersConfig; + } + + hasOnClickAction = () => { + const { isDisabled, onClick } = this.props; + + return Boolean(onClick && !isDisabled); + }; + + handleActionComplete = () => { + this.setState({ + isLoading: false, + }); + }; + + onButtonClick = () => { + const { onClick } = this.props; + + if (onClick) { + this.setState({ isLoading: true }); + + super.executeAction({ + triggerPropertyName: "onClick", + dynamicString: onClick, + event: { + type: EventType.ON_CLICK, + callback: this.handleActionComplete, + }, + }); + + return; + } + }; + + getWidgetView() { + const onPress = (() => { + if (this.hasOnClickAction()) { + return this.onButtonClick; + } + + return undefined; + })(); + + return ( + + ); + } +} + +export { WDSIconButtonWidget }; diff --git a/app/client/src/widgets/wds/WDSIconButtonWidget/widget/types.ts b/app/client/src/widgets/wds/WDSIconButtonWidget/widget/types.ts new file mode 100644 index 0000000000..1bdd35bef7 --- /dev/null +++ b/app/client/src/widgets/wds/WDSIconButtonWidget/widget/types.ts @@ -0,0 +1,16 @@ +import type { IconButtonProps } from "@design-system/widgets"; +import type { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import type { IconName } from "@blueprintjs/icons"; + +export interface IconButtonWidgetProps extends WidgetProps { + iconName: IconName; + buttonColor: IconButtonProps["color"]; + buttonVariant: IconButtonProps["variant"]; + isDisabled: boolean; + isVisible: boolean; + onClick?: string; +} + +export interface IconButtonWidgetState extends WidgetState { + isLoading: boolean; +}