Skip to content

feat(pm-wizard): label-mapping UX redesign + combobox disabled-item fix#1163

Merged
zbigniewsobiecki merged 5 commits intodevfrom
feat/pm-wizard-label-mapping-ux
Apr 23, 2026
Merged

feat(pm-wizard): label-mapping UX redesign + combobox disabled-item fix#1163
zbigniewsobiecki merged 5 commits intodevfrom
feat/pm-wizard-label-mapping-ux

Conversation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

Summary

  • Label-mapping step redesign: replaces NativeSelect with the shared searchable Combobox (with color swatches), adds a bulk-create banner for unmapped slots that have defaults, and collapses the per-row create form once a slot is already mapped — reducing visual clutter in the happy path.
  • Color swatch in Combobox: new optional swatch field on ComboboxOption renders an 8×8 dot in both the trigger and each list item, letting operators distinguish cascade-* labels at a glance.
  • Critical combobox fix: data-[disabled]:data-[disabled=true]: — cmdk v1+ always sets data-disabled="false" on every non-disabled item; the bare Tailwind attribute selector [data-disabled] matched even "false", giving every option pointer-events-none + opacity-50. All PM-wizard selectors (team picker, label picker, project-scope picker) were broken — options appeared greyed out and could not be clicked.

Test plan

  • New tests/unit/web/combobox.test.ts pins the exact Tailwind class strings to prevent silent regression of the fix
  • tests/unit/web/steps/label-mapping.test.ts fully rewritten for the new element-tree traversal pattern (enum mode uses Combobox which can't SSR); covers bulk-banner visibility, per-row create-form collapse, and free-text mode
  • All 448 unit test files pass locally (pnpm test:unit)
  • pnpm typecheck clean
  • Pre-push hook (fast test + typecheck) green

🤖 Generated with Claude Code

zbigniewsobiecki and others added 5 commits April 18, 2026 20:11
Optional `swatch` field on ComboboxOption. When set, the Combobox
renders a 12×12 rounded dot before the label in both the trigger
(selected value) and every option row in the dropdown. Accepts any
CSS color value — hex/rgb/named. Additive; existing callers that
don't set `swatch` are unchanged.

PM wizard label picker (next commit) uses this so operators can
distinguish `cascade-*` labels at a glance.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
The Labels step shipped with three controls per row — a dropdown, a
text input pre-filled with the SAME value as the dropdown, and a
Create button — repeated for every slot whether mapped or not.
Confusing, noisy, and makes fresh setup a 5-click chore.

This rewrite:

- **Searchable Combobox** replaces the plain <select> for enum-mode
  rows (Linear + Trello). Each label shows a color swatch so
  `cascade-*` labels are visually distinguishable.
- **Bulk-create banner** at the top: when ≥1 slot has a default but
  no mapping, one click creates every missing label with canonical
  defaults (5 clicks → 1). Hidden when every slot is mapped or when
  the provider doesn't declare `createLabel`. Wires the existing but
  previously unused `createMissingLabelsMutation` hook.
- **Per-row Create form** now renders only when the slot is unmapped.
  Mapped rows show a single clean Combobox. Cuts visual clutter ~50%.

Free-text mode (JIRA) is unchanged — plain Input per slot.

The Trello and Linear `useProviderHooks` expose the new
`onCreateMissingLabels` + `creatingMissingLabels` values; the
adapters pass them through. JIRA doesn't declare `createLabel` so
the banner stays hidden automatically.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Migrates the test strategy: enum mode uses element-tree traversal
(same pattern as container-pick.test.ts) because the new Combobox-
based rendering can't SSR through radix Popover. Free-text mode stays
on renderToStaticMarkup.

Adds coverage for the new UX contracts:
- Each slot renders a Combobox with `swatch` mapped from label.color.
- Mapped rows get `data-mapped="true"` and `data-has-create-form="false"`.
- Bulk banner renders when missing-with-defaults ≥ 1 and both
  onCreateLabel + onCreateMissingLabels are supplied; hides otherwise.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
…eyout

cmdk v1+ always sets data-disabled="false" on every non-disabled
CommandItem. The bare Tailwind variant data-[disabled]: generates the
CSS selector [data-disabled], which matches any element that *has* the
attribute — including data-disabled="false". This caused every option
in every PM-wizard combobox (team picker, label picker, project-scope
picker) to receive pointer-events-none + opacity-50, making all
options visually greyed-out and impossible to click.

Fix: data-[disabled=true]: generates [data-disabled="true"], which
only fires on explicitly disabled items.

Also:
- Make renderBulkBanner's onCreate param required (always guarded at
  call site by showBulkBanner check)
- Add seed-once comment to CreateLabelForm to document intentional
  useState(defaultName) behaviour
- Add structural regression test that pins both Tailwind class strings
  so the fix can't be silently reverted

Tally: 3 files changed, 57 insertions(+), 5 deletions(-)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolved conflict in label-mapping.tsx: kept required (non-optional)
`onCreate` param and direct `onCreate(...)` call — the optional-chaining
variant on dev was superseded by the type-tightening in this branch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zbigniewsobiecki zbigniewsobiecki merged commit caac90b into dev Apr 23, 2026
2 checks passed
@zbigniewsobiecki zbigniewsobiecki deleted the feat/pm-wizard-label-mapping-ux branch April 23, 2026 09:13
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