Skip to content

Commit e5374cc

Browse files
committed
fix: remove hidden input from text read-only mode and add tests
1 parent 0364be2 commit e5374cc

2 files changed

Lines changed: 108 additions & 12 deletions

File tree

packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { render } from "@testing-library/react";
22
import { CheckboxRadioSelectionContainerProps } from "../../typings/CheckboxRadioSelectionProps";
33
import CheckboxRadioSelection from "../CheckboxRadioSelection";
4+
import { CheckboxSelection } from "../components/CheckboxSelection/CheckboxSelection";
5+
import { MultiSelector } from "../helpers/types";
46

57
// Mock the selector to avoid implementation dependencies for basic tests
68
jest.mock("../helpers/getSelector", () => ({
@@ -89,3 +91,94 @@ describe("CheckboxRadioSelection", () => {
8991
expect(widget?.className).toContain("widget-checkbox-radio-selection");
9092
});
9193
});
94+
95+
function makeMultiSelector(overrides: Partial<MultiSelector> = {}): MultiSelector {
96+
return {
97+
type: "multi",
98+
status: "available",
99+
readOnly: false,
100+
currentId: [],
101+
clearable: false,
102+
customContentType: "no",
103+
validation: undefined,
104+
updateProps: jest.fn(),
105+
setValue: jest.fn(),
106+
getOptions: jest.fn(() => ["option1", "option2", "option3"]),
107+
options: {
108+
status: "available",
109+
searchTerm: "",
110+
isLoading: false,
111+
getAll: jest.fn(() => ["option1", "option2", "option3"]),
112+
setSearchTerm: jest.fn(),
113+
onAfterSearchTermChange: jest.fn(),
114+
_updateProps: jest.fn(),
115+
_optionToValue: jest.fn(),
116+
_valueToOption: jest.fn()
117+
},
118+
caption: {
119+
get: jest.fn((v: string) => `Caption ${v}`),
120+
render: jest.fn((v: string | null | number | null) => `Caption ${v}`),
121+
emptyCaption: "Select an option",
122+
formatter: undefined
123+
},
124+
...overrides
125+
};
126+
}
127+
128+
const baseCheckboxProps = {
129+
inputId: "test-checkbox",
130+
tabIndex: 0,
131+
ariaRequired: { status: "available" as const, value: false } as any,
132+
ariaLabel: undefined,
133+
groupName: undefined,
134+
noOptionsText: "No options"
135+
};
136+
137+
describe("CheckboxSelection – read-only text mode", () => {
138+
it("hides unselected options and their inputs", () => {
139+
const selector = makeMultiSelector({ readOnly: true, currentId: ["option1"] });
140+
const { queryByDisplayValue } = render(
141+
<CheckboxSelection {...baseCheckboxProps} selector={selector} readOnlyStyle="text" />
142+
);
143+
144+
// unselected options should not appear at all
145+
expect(queryByDisplayValue("option2")).toBeNull();
146+
expect(queryByDisplayValue("option3")).toBeNull();
147+
});
148+
149+
it("renders no <input> for the selected option in text mode", () => {
150+
const selector = makeMultiSelector({ readOnly: true, currentId: ["option1"] });
151+
const { queryAllByRole } = render(
152+
<CheckboxSelection {...baseCheckboxProps} selector={selector} readOnlyStyle="text" />
153+
);
154+
155+
expect(queryAllByRole("checkbox")).toHaveLength(0);
156+
});
157+
158+
it("renders selected option caption text", () => {
159+
const selector = makeMultiSelector({ readOnly: true, currentId: ["option1"] });
160+
const { getByText } = render(
161+
<CheckboxSelection {...baseCheckboxProps} selector={selector} readOnlyStyle="text" />
162+
);
163+
164+
expect(getByText("Caption option1")).toBeTruthy();
165+
});
166+
167+
it("renders all inputs when not read-only", () => {
168+
const selector = makeMultiSelector({ readOnly: false, currentId: ["option1"] });
169+
const { getAllByRole } = render(
170+
<CheckboxSelection {...baseCheckboxProps} selector={selector} readOnlyStyle="text" />
171+
);
172+
173+
expect(getAllByRole("checkbox")).toHaveLength(3);
174+
});
175+
176+
it("renders inputs in bordered read-only mode", () => {
177+
const selector = makeMultiSelector({ readOnly: true, currentId: ["option1"] });
178+
const { getAllByRole } = render(
179+
<CheckboxSelection {...baseCheckboxProps} selector={selector} readOnlyStyle="bordered" />
180+
);
181+
182+
expect(getAllByRole("checkbox")).toHaveLength(3);
183+
});
184+
});

packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { If } from "@mendix/widget-plugin-component-kit/If";
12
import classNames from "classnames";
23
import { MouseEvent, ReactElement } from "react";
34
import { MultiSelector, SelectionBaseProps } from "../../helpers/types";
@@ -59,18 +60,20 @@ export function CheckboxSelection({
5960
"widget-checkbox-radio-selection-item-selected": isSelected
6061
})}
6162
>
62-
<input
63-
type="checkbox"
64-
id={checkboxId}
65-
name={name}
66-
value={optionId}
67-
checked={isSelected}
68-
disabled={isReadOnly}
69-
tabIndex={tabIndex}
70-
onChange={e => handleChange(optionId, e.target.checked)}
71-
aria-describedby={isSingleCheckbox && selector.validation ? errorId : undefined}
72-
aria-invalid={isSingleCheckbox && selector.validation ? true : undefined}
73-
/>
63+
<If condition={!isReadOnly || readOnlyStyle !== "text"}>
64+
<input
65+
type="checkbox"
66+
id={checkboxId}
67+
name={name}
68+
value={optionId}
69+
checked={isSelected}
70+
disabled={isReadOnly}
71+
tabIndex={tabIndex}
72+
onChange={e => handleChange(optionId, e.target.checked)}
73+
aria-describedby={isSingleCheckbox && selector.validation ? errorId : undefined}
74+
aria-invalid={isSingleCheckbox && selector.validation ? true : undefined}
75+
/>
76+
</If>
7477
<CaptionContent
7578
onClick={(e: MouseEvent<HTMLDivElement>) => {
7679
e.preventDefault();

0 commit comments

Comments
 (0)