Skip to content

[WC-3335] Checkbox Radio Selection: add new options on readonly style#2153

Open
gjulivan wants to merge 2 commits into
mainfrom
checkboxradio-readonly
Open

[WC-3335] Checkbox Radio Selection: add new options on readonly style#2153
gjulivan wants to merge 2 commits into
mainfrom
checkboxradio-readonly

Conversation

@gjulivan
Copy link
Copy Markdown
Collaborator

Pull request type


Description

@gjulivan gjulivan requested a review from a team as a code owner March 27, 2026 13:13
yordan-st
yordan-st previously approved these changes Apr 2, 2026
@yordan-st yordan-st force-pushed the checkboxradio-readonly branch 2 times, most recently from da19bba to 6a72077 Compare April 30, 2026 12:49
@yordan-st yordan-st force-pushed the checkboxradio-readonly branch from 6a72077 to 96da742 Compare May 26, 2026 09:10
Copy link
Copy Markdown
Collaborator

@iobuhov iobuhov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please extract agent skills to separate PR?

@yordan-st yordan-st force-pushed the checkboxradio-readonly branch from 18f932d to 7f65552 Compare May 26, 2026 12:48
@github-actions
Copy link
Copy Markdown

AI Code Review

🔶 Changes requested — one or more medium-severity items must be addressed


What was reviewed

File Change
packages/pluggableWidgets/checkbox-radio-selection-web/src/components/CheckboxSelection/CheckboxSelection.tsx Read-only "text" mode: filter out unselected checkboxes at render time instead of relying solely on CSS
packages/pluggableWidgets/checkbox-radio-selection-web/src/ui/CheckboxRadioSelection.scss SCSS updated: when readOnlyStyle === "text", hide unselected items and their inputs via CSS class
packages/pluggableWidgets/checkbox-radio-selection-web/CHANGELOG.md Added [Unreleased] entry describing the read-only "Content only" fix

Skipped (out of scope): dist/, pnpm-lock.yaml

CI checks could not be retrieved (gh pr checks blocked by API auth) — status unknown.


Findings

🔶 Medium — Read-only text mode renders hidden <input> elements inside DOM

File: packages/pluggableWidgets/checkbox-radio-selection-web/src/ui/CheckboxRadioSelection.scss lines 9–19
Problem: When readOnlyStyle === "text", unselected .checkbox-item elements are hidden with display: none via CSS, but the CheckboxSelection.tsx component already returns null for unselected items at the JS layer (line 51–53). The SCSS block that hides items and their inputs (display: none / selected display: block with input { display: none }) now applies a double-suppression for unselected items. More importantly, the CSS block still hides the <input> inside selected items via input { display: none } — but CheckboxSelection.tsx doesn't use the <If> guard that RadioSelection.tsx uses (line 71–84 of RadioSelection.tsx). This means selected items in text mode still render a disabled checkbox input in the DOM that is visually hidden by CSS, making it invisible to sighted users but potentially picked up by screen readers as a checked disabled control, creating a confusing accessible experience.
Fix: Mirror the approach used in RadioSelection.tsx — wrap the <input> in <If condition={!isReadOnly || readOnlyStyle !== "text"}> so the input is not rendered at all in "text" read-only mode:

import { If } from "@mendix/widget-plugin-component-kit/If";

// Inside the options.map():
<If condition={!isReadOnly || readOnlyStyle !== "text"}>
    <input
        type="checkbox"
        id={checkboxId}
        name={name}
        value={optionId}
        checked={isSelected}
        disabled={isReadOnly}
        tabIndex={tabIndex}
        onChange={e => handleChange(optionId, e.target.checked)}
        aria-describedby={isSingleCheckbox && selector.validation ? errorId : undefined}
        aria-invalid={isSingleCheckbox && selector.validation ? true : undefined}
    />
</If>

This removes the hidden-but-present input from the accessibility tree, consistent with how RadioSelection handles the same case.


🔶 Medium — No unit tests for the new read-only "text" mode behaviour

File: packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx
Problem: The PR fixes a bug where checkboxes remained visible in read-only "Content only" mode, but there are no tests covering this path. The existing test file has a weak mock (manual object instead of builders) and the three tests it contains don't exercise readOnlyStyle: "text" with a read-only selector at all. A regression could reappear silently.
Fix: Add a test directly for CheckboxSelection that verifies unselected options are not rendered and selected options appear as text (no <input>) when readOnlyStyle === "text" and isReadOnly === true. Example sketch:

it("hides unselected checkboxes in read-only text mode", () => {
    const { queryByRole } = render(
        <CheckboxSelection
            selector={mockMultiSelector({ readOnly: true, currentId: ["option1"] })}
            readOnlyStyle="text"
            inputId="test"
            tabIndex={0}
            ariaRequired={...}
            ariaLabel={undefined}
            groupName={undefined}
            noOptionsText="No options"
        />
    );
    // option2 (unselected) should not be in the DOM
    expect(queryByRole("checkbox", { name: /option2/i })).toBeNull();
    // option1 (selected) should show text but no interactive input
    expect(queryByRole("checkbox", { name: /option1/i })).toBeNull();
});

⚠️ Low — Existing tests use manual mock objects instead of builders

File: packages/pluggableWidgets/checkbox-radio-selection-web/src/__tests__/CheckboxRadioSelection.spec.tsx lines 6–39
Note: The mock inside jest.mock(...) builds a plain object for the selector rather than using EditableValueBuilder / ListValueBuilder from @mendix/widget-plugin-test-utils. Manual mocks miss status edge cases (Loading, Unavailable, ReadOnly) and tend to drift from the actual interface over time. Consider refactoring to use the builders so future tests can easily cover all EditableValue status states.


Positives

  • The JS-layer return null guard in CheckboxSelection.tsx (line 51–53) correctly mirrors what RadioSelection.tsx does — early-returning null is cleaner than relying solely on CSS.
  • CHANGELOG entry is present and follows Keep a Changelog format with a clear, user-facing description of the fix.
  • The fix is narrow and surgical — only the affected render path is changed, with no unrelated refactoring.

Copy link
Copy Markdown
Collaborator

@iobuhov iobuhov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants