Skip to content

NO-1520: Select All / Deselect All toggle for multi-select fields & table/collection cells - #398

Open
vivekjyani wants to merge 5 commits into
mainfrom
NO-1520
Open

vivekjyani wants to merge 5 commits into
mainfrom
NO-1520

Conversation

@vivekjyani

@vivekjyani vivekjyani commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Context

Multi-select fields (and multi-select table/collection cells) only allowed selecting options one at a time. Spec asks for a bulk Select All / Deselect All control, and the toggle must NOT be one of the selectable options itself.

What changed

  • SelectAllToggleButton<T> (new, in MultiSelectionView.swift) — shared, generic toggle rendered above the options box. Selects every option id / clears all; label flips between "Select All" and "Deselect All".
  • MultiSelectionView.swift — renders the toggle for the simple form field when multi == true.
  • TableMultiSelectView.swift — renders the same toggle, pinned above the sheet's ScrollView, for multi-select table/collection cells; edits go through the existing temp-array → Apply path.
  • MultiSelectionDataModel (+ DocumentEditor) — added a mode field (fed from fieldEditMode) so the field view can tell when it's readonly.
  • Toggle is hidden in readonly mode on both surfaces (mode != .readonly / cellModel.editMode != .readonly).

Decisions

  • One shared generic component (SelectAllToggleButton<T: Identifiable>) instead of two copies — the simple field passes Option, the cell passes OptionLocal; both satisfy getId: (T) -> String?.
  • Table/collection toggle writes to the temp array, committed only on Apply, to match the existing cell-sheet behavior (nothing persists until Apply).
  • isSelected computed, not @State in TableMultiSelection — a @State copy ignored external array mutations, so Select All didn't update the checkmarks. Computed value stays reactive.
  • Readonly hides rather than disables the toggle — a greyed bulk control is noise in a readonly field; options themselves still render.

Screenshot / video

image image

Public API / docs

No public API change — SelectAllToggleButton, MultiSelectionDataModel, and the views are internal; the new mode has a .fill default (non-breaking). No docs update needed.

Test coverage

Covered via XCUITest (UI bridge can't run in CI sandbox; verified on simulator):

  • Simple field: Select All, Deselect All, Select-All-then-deselect-one, and editable-only toggle visibility (single-select + readonly excluded).
  • Table cell + collection cell: Select All / Deselect All commit on Apply; single-select cell shows no toggle.

Reviewer notes

  • No readonly multi: true column exists in the table/collection fixtures, so the readonly-hide path is UI-tested only on the simple field; table/collection share the identical editMode != .readonly gate. Can add a fixture + test if you want explicit coverage.
  • The simple-field visibility test asserts exactly 1 toggle — it depends on the fixture having one editable + one readonly multi field; flag if that fixture changes.

@vivekjyani

Copy link
Copy Markdown
Collaborator Author

Review: approve ✅

Clean approach — one reusable SelectAllToggleButton, reuses the existing onChange / temp-array→Apply paths, hidden in readonly on both surfaces. No public-API or schema impact (new mode is a transient view-model flag, not serialized). Good catch switching TableMultiSelection.isSelected from @State to computed so Select All actually updates the checkmarks.

No blocking issues. Minor nits (non-blocking):

  • SelectAllToggleButton<T: Identifiable> — the Identifiable constraint is unused (selection goes through the getId closure); can drop it.
  • allOptionsSelected / allTableOptionsSelected are duplicate logic — could be one helper.
  • Readonly-hide path for table/collection cells is untested (no readonly multi:true fixture); relies on the shared editMode != .readonly gate. testSelectAllToggleShownOnlyForEditableMultiFields asserting count == 1 is fixture-fragile.

Tests meaningfully assert committed values, not just taps — coverage is adequate for the UI layer. Full analysis in review notes.

@vivekjyani vivekjyani left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Inline review notes (all non-blocking — overall approve). See each thread for detail.

Comment thread Sources/JoyfillUI/View/Fields/MultiSelectionView.swift Outdated
Comment thread Sources/JoyfillUI/View/Fields/MultiSelectionView.swift Outdated
Comment thread Sources/JoyfillUI/View/Fields/MultiSelectionView.swift
Comment thread Sources/JoyfillUI/View/Fields/TableView/TableMultiSelectView.swift
Comment thread Sources/JoyfillUI/View/Fields/TableView/TableMultiSelectView.swift
Comment thread Sources/JoyfillUI/View/Fields/TableView/TableMultiSelectView.swift
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.

1 participant