|
| 1 | +--- |
| 2 | +'@object-ui/types': minor |
| 3 | +'@object-ui/components': minor |
| 4 | +--- |
| 5 | + |
| 6 | +`MenuItem` is now a discriminated union, and all three menu renderers read the keys it |
| 7 | +declares (objectui#6523, objectui#6346, maintainer ruling 2026-08-27 — "one answer for the |
| 8 | +whole `MenuItem` family"). |
| 9 | + |
| 10 | +**The break, spelled out.** `MenuItem` (`@object-ui/types`, shared by `ui:dropdown-menu`, |
| 11 | +`ui:context-menu` and `ui:menubar`) used to be a single object with `label: string` |
| 12 | +required unconditionally. It is now `MenuCommandItem | MenuDividerItem`: a command item |
| 13 | +(`label` required, plus `icon`/`disabled`/`onClick`/`shortcut`/`children`) or a divider |
| 14 | +(`{ separator: true }`, nothing else). The union — not `label?: string` — is deliberate: it |
| 15 | +is what the data actually is, and it keeps the command arm's label protection intact rather |
| 16 | +than weakening it repo-wide to accommodate the divider. Both arms also tombstone `type` |
| 17 | +(`type?: never` / `z.never().optional()`): the retired `{ type: 'separator' }` (and its |
| 18 | +sibling `{ type: 'label' }`) is now a **declared refusal** at parse time, not a silent strip. |
| 19 | +A consumer's own `MenuItem[]` authored with either retired spelling now fails |
| 20 | +`MenuItemSchema.safeParse` and fails `tsc` under the published `.d.ts`; a consumer authoring |
| 21 | +the declared `{ separator: true }` divider now **succeeds** for the first time — before this |
| 22 | +change it failed a strict parse too, because `label` had no way to be omitted. |
| 23 | + |
| 24 | +**Renderer accept behaviour changes to match.** `dropdown-menu` and `context-menu` used to |
| 25 | +branch on the undeclared `item.type === 'separator'`; an author who instead wrote the |
| 26 | +DECLARED `{ separator: true }` got a value that validated, published, and rendered a blank |
| 27 | +menu row (the divider fell through to the ordinary item branch with no `label`). Both |
| 28 | +renderers now branch on `item.separator`, matching `menubar` — which had this right all |
| 29 | +along and is the evidence the type, not those two renderers, was correct. Their registry |
| 30 | +`defaultProps` and `description` strings stop teaching the retired dialect; the 4 places it |
| 31 | +appeared in this repo (2 schema-catalog fixtures, 2 registry `defaultProps`) are migrated. |
| 32 | + |
| 33 | +**The item handler moves to the declared key (objectui#6346).** All three renderers now |
| 34 | +fire `item.onClick` — the key `MenuItem` has always declared (TS source, built `.d.ts`, and |
| 35 | +the Zod mirror all agreed) but that `dropdown-menu`/`context-menu` never read (they read an |
| 36 | +undeclared `item.onSelect` instead) and that `menubar` wired nowhere at all. An author who |
| 37 | +followed the published type and set `onClick` got a value that validated, published, and |
| 38 | +never fired; that is fixed. `renderMenuItems`/`renderContextMenuItems` also tighten from |
| 39 | +`items: any[]` to `items: MenuItem[]` — the widening that let the mismatch type-check in the |
| 40 | +first place. Migration cost measured **zero** in this repo: no fixture, doc or test authored |
| 41 | +`onSelect` on a menu item before this change. |
| 42 | + |
| 43 | +**Rider, recorded as parity not new capability.** `menubar` now also renders the declared |
| 44 | +`shortcut` string — `dropdown-menu` and `context-menu` already drew it, so this aligns the |
| 45 | +third container rather than expanding the surface. |
| 46 | + |
| 47 | +Everything that rendered correctly before this change still renders the same way; the |
| 48 | +narrowing only refuses spellings that were already unprotected (silently stripped or never |
| 49 | +read at all). |
0 commit comments