Skip to content

feat: disabledWhenInvalid in ButtonGroupWidget #38656

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading