Skip to content

Commit

Permalink
feat: disabledWhenInvalid in ButtonGroupWidget (#38656)
Browse files Browse the repository at this point in the history
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: rahul.barwal@appsmith.com <rahul.barwal@appsmith.com>
  • Loading branch information
devin-ai-integration[bot] and rahulbarwal authored Jan 16, 2025
1 parent 4e7a2a0 commit b3e5e43
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 6 deletions.
21 changes: 15 additions & 6 deletions app/client/src/widgets/ButtonGroupWidget/component/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { RefObject } from "react";
import React, { createRef } from "react";
import { sortBy } from "lodash";
import { objectKeys } from "@appsmith/utils";
import {
Alignment,
Icon,
Expand Down Expand Up @@ -45,7 +46,7 @@ interface ButtonData {
const getButtonData = (
groupButtons: Record<string, GroupButtonProps>,
): ButtonData[] => {
const buttonData = Object.keys(groupButtons).reduce(
const buttonData = objectKeys(groupButtons).reduce(
(acc: ButtonData[], id) => {
return [
...acc,
Expand Down Expand Up @@ -344,7 +345,7 @@ interface PopoverContentProps {
function PopoverContent(props: PopoverContentProps) {
const { buttonId, menuItems, onItemClicked } = props;

let items = Object.keys(menuItems)
let items = objectKeys(menuItems)
.map((itemKey) => menuItems[itemKey])
.filter((item) => item.isVisible === true);

Expand Down Expand Up @@ -490,7 +491,7 @@ class ButtonGroupComponent extends React.Component<

// Get widths of menu buttons
getMenuButtonWidths = () =>
Object.keys(this.props.groupButtons).reduce((acc, id) => {
objectKeys(this.props.groupButtons).reduce((acc, id) => {
if (this.props.groupButtons[id].buttonType === "MENU") {
return {
...acc,
Expand All @@ -503,7 +504,7 @@ class ButtonGroupComponent extends React.Component<

// Create refs of menu buttons
createMenuButtonRefs = () =>
Object.keys(this.props.groupButtons).reduce((acc, id) => {
objectKeys(this.props.groupButtons).reduce((acc, id) => {
if (this.props.groupButtons[id].buttonType === "MENU") {
return {
...acc,
Expand Down Expand Up @@ -540,14 +541,15 @@ class ButtonGroupComponent extends React.Component<
buttonVariant,
groupButtons,
isDisabled,
isFormValid,
minPopoverWidth,
orientation,
widgetId,
} = this.props;
const { loadedBtnId } = this.state;
const isHorizontal = orientation === "horizontal";

let items = Object.keys(groupButtons)
let items = objectKeys(groupButtons)
.map((itemKey) => groupButtons[itemKey])
.filter((item) => item.isVisible === true);

Expand All @@ -574,7 +576,11 @@ class ButtonGroupComponent extends React.Component<
{items.map((button) => {
const isLoading = button.id === loadedBtnId;
const isButtonDisabled =
button.isDisabled || isDisabled || !!loadedBtnId || isLoading;
button.isDisabled ||
isDisabled ||
!!loadedBtnId ||
isLoading ||
(button.disabledWhenInvalid && isFormValid === false);

if (button.buttonType === "MENU" && !isButtonDisabled) {
const { menuItems } = button;
Expand Down Expand Up @@ -703,6 +709,7 @@ interface GroupButtonProps {
index: number;
isVisible?: boolean;
isDisabled?: boolean;
disabledWhenInvalid?: boolean;
label?: string;
buttonType?: string;
buttonColor?: string;
Expand All @@ -718,6 +725,7 @@ interface GroupButtonProps {
index: number;
isVisible?: boolean;
isDisabled?: boolean;
disabledWhenInvalid?: boolean;
label?: string;
backgroundColor?: string;
textColor?: string;
Expand Down Expand Up @@ -746,6 +754,7 @@ export interface ButtonGroupComponentProps {
widgetId: string;
buttonMinWidth?: number;
minHeight?: number;
isFormValid?: boolean;
}

export interface ButtonGroupComponentState {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { render } from "@testing-library/react";
import React from "react";
import ButtonGroupWidget from "../index";
import { RenderModes } from "constants/WidgetConstants";
import type { ButtonGroupWidgetProps } from "../index";
import { klona } from "klona";

describe("ButtonGroupWidget disabledWhenInvalid", () => {
const defaultProps: ButtonGroupWidgetProps = {
widgetId: "test-button-group",
renderMode: RenderModes.CANVAS,
version: 1,
parentColumnSpace: 1,
parentRowSpace: 1,
leftColumn: 0,
rightColumn: 0,
topRow: 0,
bottomRow: 0,
isLoading: false,
orientation: "horizontal",
isDisabled: false,
buttonVariant: "PRIMARY",
type: "BUTTON_GROUP_WIDGET",
widgetName: "ButtonGroup1",
groupButtons: {
groupButton1: {
label: "Test Button 1",
id: "groupButton1",
widgetId: "",
buttonType: "SIMPLE",
placement: "CENTER",
isVisible: true,
isDisabled: false,
disabledWhenInvalid: true,
index: 0,
menuItems: {},
},
groupButton2: {
label: "Test Button 2",
id: "groupButton2",
widgetId: "",
buttonType: "SIMPLE",
placement: "CENTER",
isVisible: true,
isDisabled: false,
disabledWhenInvalid: true,
index: 1,
menuItems: {},
},
},
};

it("disables buttons when disabledWhenInvalid is true and form is invalid", () => {
const props = klona(defaultProps);

props.isFormValid = false;

const { container } = render(<ButtonGroupWidget {...props} />);
const buttons = container.querySelectorAll("button");

buttons.forEach((button) => {
expect(button.hasAttribute("disabled")).toBe(true);
});
});

it("enables buttons when disabledWhenInvalid is true but form is valid", () => {
const props = klona(defaultProps);

props.isFormValid = true;

const { container } = render(<ButtonGroupWidget {...props} />);
const buttons = container.querySelectorAll("button");

buttons.forEach((button) => {
expect(button.hasAttribute("disabled")).toBe(false);
});
});

it("enables buttons when disabledWhenInvalid is false regardless of form validity", () => {
const props = klona(defaultProps);

props.groupButtons = {
...defaultProps.groupButtons,
groupButton1: {
...defaultProps.groupButtons.groupButton1,
disabledWhenInvalid: false,
},
groupButton2: {
...defaultProps.groupButtons.groupButton2,
disabledWhenInvalid: false,
},
};

const { container } = render(<ButtonGroupWidget {...props} />);
const buttons = container.querySelectorAll("button");

buttons.forEach((button) => {
expect(button.hasAttribute("disabled")).toBe(false);
});
});
});
26 changes: 26 additions & 0 deletions app/client/src/widgets/ButtonGroupWidget/widget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class ButtonGroupWidget extends BaseWidget<
placement: "CENTER",
isVisible: true,
isDisabled: false,
disabledWhenInvalid: false,
index: 0,
menuItems: {},
},
Expand All @@ -77,6 +78,7 @@ class ButtonGroupWidget extends BaseWidget<
widgetId: "",
isVisible: true,
isDisabled: false,
disabledWhenInvalid: false,
index: 1,
menuItems: {},
},
Expand All @@ -89,6 +91,7 @@ class ButtonGroupWidget extends BaseWidget<
widgetId: "",
isVisible: true,
isDisabled: false,
disabledWhenInvalid: false,
index: 2,
menuItems: {
menuItem1: {
Expand All @@ -99,6 +102,7 @@ class ButtonGroupWidget extends BaseWidget<
onClick: "",
isVisible: true,
isDisabled: false,
disabledWhenInvalid: false,
index: 0,
},
menuItem2: {
Expand All @@ -109,6 +113,7 @@ class ButtonGroupWidget extends BaseWidget<
onClick: "",
isVisible: true,
isDisabled: false,
disabledWhenInvalid: false,
index: 1,
},
menuItem3: {
Expand All @@ -123,6 +128,7 @@ class ButtonGroupWidget extends BaseWidget<
onClick: "",
isVisible: true,
isDisabled: false,
disabledWhenInvalid: false,
index: 2,
},
},
Expand Down Expand Up @@ -517,6 +523,22 @@ class ButtonGroupWidget extends BaseWidget<
},
],
},
{
sectionName: "Form settings",
children: [
{
propertyName: "disabledWhenInvalid",
label: "Disabled invalid forms",
helpText:
"Disables this button if the form is invalid, if this button exists directly within a Form widget",
controlType: "SWITCH",
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.BOOLEAN },
},
],
},
{
sectionName: "Events",
hidden: (
Expand Down Expand Up @@ -825,6 +847,7 @@ class ButtonGroupWidget extends BaseWidget<
buttonVariant={this.props.buttonVariant}
groupButtons={this.props.groupButtons}
isDisabled={this.props.isDisabled}
isFormValid={this.props.isFormValid}
minHeight={this.isAutoLayoutMode ? this.props.minHeight : undefined}
minPopoverWidth={minPopoverWidth}
orientation={this.props.orientation}
Expand All @@ -839,6 +862,7 @@ class ButtonGroupWidget extends BaseWidget<
export interface ButtonGroupWidgetProps extends WidgetProps {
orientation: string;
isDisabled: boolean;
isFormValid?: boolean;
borderRadius?: string;
boxShadow?: string;
buttonVariant: ButtonVariant;
Expand All @@ -850,6 +874,7 @@ export interface ButtonGroupWidgetProps extends WidgetProps {
index: number;
isVisible?: boolean;
isDisabled?: boolean;
disabledWhenInvalid?: boolean;
label?: string;
buttonType?: string;
buttonColor?: string;
Expand All @@ -865,6 +890,7 @@ export interface ButtonGroupWidgetProps extends WidgetProps {
index: number;
isVisible?: boolean;
isDisabled?: boolean;
disabledWhenInvalid?: boolean;
label?: string;
backgroundColor?: string;
textColor?: string;
Expand Down

0 comments on commit b3e5e43

Please sign in to comment.