@clicktap/ui 2.0.6 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as d,Fragment as O}from"react/jsx-runtime";import{Children as V,isValidElement as _,Fragment as $}from"react";import{DisclosureGroup as q,Disclosure as H,Button as J,DisclosurePanel as L}from"react-aria-components";import{cn as r}from"../../utils/cn.js";import{DownArrow as x}from"../Icon/DownArrow.js";function Z(p){return null}function E({children:p,selectionMode:y="single",defaultSelectedKeys:N,defaultExpandedKeys:w,selectedKeys:k,onSelectionChange:A,disallowEmptySelection:I=!1,hideIndicator:v=!1,showDivider:S=!0,dividerProps:D,variant:i,isCompact:s,className:F,itemClasses:n}){const c=V.toArray(p).filter(_),K=c.map((t,l)=>String(t.key??l)),b=t=>t==="all"?K:t?[...t].map(String):void 0,j=b(N??w)??[],P=b(k);return e(q,{allowsMultipleExpanded:y==="multiple",defaultExpandedKeys:j,expandedKeys:P,onExpandedChange:t=>{const l=new Set([...t].map(String));I&&l.size===0||A?.(l)},className:r("px-0",i==="bordered"&&["px-4 py-4 rounded-xl border-2 border-border-subtle"],i==="shadow"&&["px-4 py-4 rounded-xl bg-surface-subtle shadow-slate-200 border border-border-subtle"],F),children:c.map((t,l)=>{const h=String(t.key??l),{title:W,subtitle:f,startContent:m,indicator:u,hideIndicator:z,disableIndicatorAnimation:g,isDisabled:B,classNames:o,onPress:G,children:M}=t.props;return d($,{children:[e(H,{id:h,isDisabled:B,className:r("w-full py-2","data-disabled:pointer-events-none data-disabled:opacity-50",i==="bordered"&&["rounded-xl py-0"],i==="splitted"&&["flex flex-col bg-surface-subtle rounded-xl border border-border-subtle"],s&&"py-0",n?.base,o?.base),children:({isExpanded:a})=>d(O,{children:[e("h2",{"data-open":a||void 0,className:r(n?.heading,o?.heading),children:d(J,{slot:"trigger","data-open":a||void 0,onPress:G,"aria-label":t.props["aria-label"],className:r("w-full h-full flex items-center gap-3 py-0 appearance-none cursor-pointer select-none","border-0 focus-visible:outline-2 focus-visible:outline focus-visible:outline-slate-100","bg-transparent text-inherit",s&&["py-2"],n?.trigger,o?.trigger),children:[m!=null&&e("span",{className:r("shrink-0",n?.startContent,o?.startContent),children:m}),d("span",{className:r("flex-1 text-left",n?.titleWrapper,o?.titleWrapper),children:[e("span",{className:r("block text-xl font-semibold",s&&"text-base",n?.title,o?.title),children:W}),f!=null&&e("span",{className:r("block text-base",s&&"text-sm",n?.subtitle,o?.subtitle),children:f})]}),z||v?null:e("span",{"aria-hidden":"true","data-open":a||void 0,className:r("shrink-0",!g&&"transition-transform duration-200",!g&&a&&"-rotate-180",n?.indicator,o?.indicator),children:typeof u=="function"?e(u,{isOpen:a}):u??e(x,{className:"w-4 h-4"})})]})}),e("div",{className:r("grid transition-[grid-template-rows] duration-200 ease-in-out",a?"grid-rows-[1fr]":"grid-rows-[0fr]"),children:e("div",{className:"overflow-hidden min-h-0",children:e(L,{className:r(s?"py-1":"py-2",n?.content,o?.content),children:M})})})]})}),S&&l<c.length-1&&e("hr",{className:r("border-solid border-b-0 border-x-0",D?.className??["my-2 border-border-subtle",i==="shadow"&&"border-border"])})]},h)})})}function C(){return e(x,{className:"w-4 h-4"})}export{E as Accordion,Z as AccordionItem,C as AccordionItemArrow,E as default};
1
+ "use client";import{jsx as e,jsxs as d,Fragment as O}from"react/jsx-runtime";import{Children as V,isValidElement as _,Fragment as q}from"react";import{DisclosureGroup as H,Disclosure as J,Button as L,DisclosurePanel as Q}from"react-aria-components";import{cn as r}from"../../utils/cn.js";import{DownArrow as y}from"../Icon/DownArrow.js";function E(p){return null}function C({children:p,selectionMode:N="single",defaultSelectedKeys:w,defaultExpandedKeys:k,selectedKeys:A,onSelectionChange:I,disallowEmptySelection:v=!1,hideIndicator:D=!1,showDivider:S=!0,dividerProps:K,variant:s,isCompact:i,className:F,itemClasses:n}){const c=V.toArray(p).filter(_),b=(t,l)=>String(t.key??l).replace(/^\.\$/,"").replace(/=2/g,":").replace(/=0/g,"="),j=c.map(b),h=t=>t==="all"?j:t?[...t].map(String):void 0,P=h(w??k)??[],W=h(A);return e(H,{allowsMultipleExpanded:N==="multiple",defaultExpandedKeys:P,expandedKeys:W,onExpandedChange:t=>{const l=new Set([...t].map(String));v&&l.size===0||I?.(l)},className:r("px-0",s==="bordered"&&["px-4 py-4 rounded-xl border-2 border-border-subtle"],s==="shadow"&&["px-4 py-4 rounded-xl bg-surface-subtle shadow-slate-200 border border-border-subtle"],F),children:c.map((t,l)=>{const f=b(t,l),{title:$,subtitle:m,startContent:g,indicator:u,hideIndicator:z,disableIndicatorAnimation:x,isDisabled:B,classNames:o,onPress:G,children:M}=t.props;return d(q,{children:[e(J,{id:f,isDisabled:B,className:r("w-full py-2","data-disabled:pointer-events-none data-disabled:opacity-50",s==="bordered"&&["rounded-xl py-0"],s==="splitted"&&["flex flex-col bg-surface-subtle rounded-xl border border-border-subtle"],i&&"py-0",n?.base,o?.base),children:({isExpanded:a})=>d(O,{children:[e("h2",{"data-open":a||void 0,className:r(n?.heading,o?.heading),children:d(L,{slot:"trigger","data-open":a||void 0,onPress:G,"aria-label":t.props["aria-label"],className:r("w-full h-full flex items-center gap-3 py-0 appearance-none cursor-pointer select-none","border-0 focus-visible:outline-2 focus-visible:outline focus-visible:outline-slate-100","bg-transparent text-inherit",i&&["py-2"],n?.trigger,o?.trigger),children:[g!=null&&e("span",{className:r("shrink-0",n?.startContent,o?.startContent),children:g}),d("span",{className:r("flex-1 text-left",n?.titleWrapper,o?.titleWrapper),children:[e("span",{className:r("block text-xl font-semibold",i&&"text-base",n?.title,o?.title),children:$}),m!=null&&e("span",{className:r("block text-base",i&&"text-sm",n?.subtitle,o?.subtitle),children:m})]}),z||D?null:e("span",{"aria-hidden":"true","data-open":a||void 0,className:r("shrink-0",!x&&"transition-transform duration-200",!x&&a&&"-rotate-180",n?.indicator,o?.indicator),children:typeof u=="function"?e(u,{isOpen:a}):u??e(y,{className:"w-4 h-4"})})]})}),e("div",{className:r("grid transition-[grid-template-rows] duration-200 ease-in-out",a?"grid-rows-[1fr]":"grid-rows-[0fr]"),children:e("div",{className:"overflow-hidden min-h-0",children:e(Q,{className:r(i?"py-1":"py-2",n?.content,o?.content),children:M})})})]})}),S&&l<c.length-1&&e("hr",{className:r("border-solid border-b-0 border-x-0",K?.className??["my-2 border-border-subtle",s==="shadow"&&"border-border"])})]},f)})})}function ee(){return e(y,{className:"w-4 h-4"})}export{C as Accordion,E as AccordionItem,ee as AccordionItemArrow,C as default};
@@ -0,0 +1,50 @@
1
+ # Accordion controlled selection matches authored keys again
2
+
3
+ **Date:** 2026-08-24
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** every controlled or default-expanded `Accordion` — checkout payment method selection, checkout's `defaultExpandedKeys={['cart']}`, totals/discount panel, PDP mobile overview, filter groups.
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No — unless you shipped a workaround that pre-prefixes keys (see crispi note).
8
+ **Ticket:** CTAP-1404
9
+
10
+ ## The defect (2.0.x regression from the CTAP-1383 react-aria rewrite)
11
+
12
+ The component derived internal item ids from `Children.toArray(children)`, whose
13
+ keys are NOT the author's keys: React prefixes them (`key="CYBERSOURCE"` arrives
14
+ as `".$CYBERSOURCE"`) and escapes them (`:` → `=2`, `=` → `=0`). Consumer keys
15
+ were only `String()`ed, so:
16
+
17
+ 1. `selectedKeys` / `defaultSelectedKeys` / `defaultExpandedKeys` never matched
18
+ any item — nothing opened, nothing highlighted. The checkout payment step's
19
+ method selection "not working" was this.
20
+ 2. `onSelectionChange` handed the PREFIXED id back out — crispi's checkout
21
+ dispatched `".$CYBERSOURCE"` as a payment method code to the backend.
22
+
23
+ The heroui accordion this replaced handled keys correctly. Diagnosed by the
24
+ crispi session against the installed 2.0.6 bundle.
25
+
26
+ ## The fix
27
+
28
+ Internal ids now live in the author's key space: the `.$` prefix is stripped AND
29
+ React's key escaping is reversed (`=2` → `:`, `=0` → `=` — without the unescape,
30
+ `Module:Entity`-style keys would still mangle). The same author-space id is used
31
+ as the `Disclosure` id, so what react-aria reports through `onExpandedChange`
32
+ round-trips with no output translation. `"all"` and unkeyed children (positional
33
+ `".0"` ids) behave as before.
34
+
35
+ ## Consumer checklist
36
+
37
+ 1. Take the release; no call-site changes needed — controlled accordions start
38
+ working as written.
39
+ 2. **Delete compensating shims.** crispi: `libs/crispi-ui/.../Accordion.tsx`
40
+ wrapper (crispi-monorepo `7a44704f0`) must be REMOVED when taking this
41
+ release — it translates keys into the prefixed space, which no longer
42
+ exists, so keeping it reintroduces the bug in the other direction. Check the
43
+ shim's own escape handling is not needed elsewhere (this fix also covers
44
+ escaped keys, which a bare `.$`-strip shim does not).
45
+ 3. If any code path stored or transmitted a prefixed id (`.$…`) — crispi saw
46
+ them reach the backend as payment codes — check persisted data for the
47
+ `.$` prefix.
48
+
49
+ Regression tests ship with the component (`Accordion.test.tsx`), proven to fail
50
+ 4/6 against the unfixed 2.0.6 behavior.
@@ -0,0 +1,90 @@
1
+ # The build fails if it emits the development JSX runtime
2
+
3
+ **Date:** 2026-08-24
4
+ **Package:** `@clicktap/ui` (also `@clicktap/state`, `@clicktap/csx`)
5
+ **Affects:** anyone building or publishing these packages. Consumers receive a guarantee, not a change.
6
+ **Classification:** BUGFIX
7
+ **Breaking:** No. A correct build is unaffected; an incorrect one now fails instead of publishing.
8
+
9
+ ## Problem
10
+
11
+ A build could emit React's **development** JSX runtime — `jsxDEV(...)` call sites
12
+ plus a bundled `assets/jsx-dev-runtime-*.js` chunk. A consumer's production React
13
+ does not export `jsxDEV`, so the package compiles and then dies at prerender:
14
+
15
+ ```
16
+ TypeError: r.j.jsxDEV is not a function
17
+ at .next/server/app/page.js
18
+ Error occurred prerendering page "/"
19
+ ```
20
+
21
+ ⚠️ **Nothing in the build looked at the JSX runtime.** Not
22
+ `verifyUseClientDirectives`, not typecheck, not lint. The artifact was
23
+ structurally valid and publishable; only a consumer's `next build` found it.
24
+
25
+ ## It was never intermittent
26
+
27
+ CTAP-1393 recorded this as intermittent — seen twice, then not reproducible across
28
+ five clean builds. It is deterministic, and the trigger was in `project.json`:
29
+
30
+ ```json
31
+ "configurations": {
32
+ "development": { "mode": "development" },
33
+ "production": { "mode": "production" }
34
+ }
35
+ ```
36
+
37
+ `@vitejs/plugin-react` selects the dev JSX runtime from the resolved mode. So
38
+ `--configuration=development` — or any dependent target that propagated it —
39
+ produced a dev-runtime artifact, every time. The five "clean" builds used the
40
+ default production configuration.
41
+
42
+ Measured on this repo, and matching the original report's numbers exactly:
43
+
44
+ | build | emitted files referencing `jsxDEV` |
45
+ | --- | --- |
46
+ | default (production) | **0** |
47
+ | `--mode=development` | **206** |
48
+
49
+ ## Fix — three layers, and only one of them is load-bearing
50
+
51
+ 1. **The `development` build configuration is removed** from `ui`, `state`, `csx`
52
+ and `conversion-tracking-facebook`. A published library artifact has no
53
+ legitimate development mode, and nothing in the repo invoked it. This removes
54
+ the mechanism.
55
+ 2. **`verifyProductionJsxRuntime`** now runs in the same `closeBundle` family as
56
+ `verifyUseClientDirectives`, on `ui`, `state` and `csx`. It fails the build if
57
+ any emitted `.js` references `jsxDEV` or `jsx-dev-runtime`.
58
+ 3. It also asserts the **positive** direction: a package whose output references
59
+ React must show at least one `react/jsx-runtime` import. Checking only "no
60
+ jsxDEV" passes vacuously on a build that emitted nothing.
61
+
62
+ ⚠️ **(2) is the one that holds.** A configuration fix cannot cover a mode arriving
63
+ from a CLI flag, an inherited configuration on a dependent target, or a direct
64
+ vite invocation. The assertion looks at what was actually EMITTED, so it holds
65
+ however the mode was arrived at — which is the difference between making this
66
+ unlikely and making it impossible.
67
+
68
+ The positive check is conditioned on React output rather than required outright:
69
+ `csx` legitimately emits no components (two files, no React), and an assertion
70
+ that fails a correct build gets deleted.
71
+
72
+ ## Verified in both directions
73
+
74
+ ```
75
+ ui production 248 file(s) clean; 205 import react/jsx-runtime -> build passes
76
+ ui --mode=dev 206 emitted file(s) reference the DEV runtime -> build FAILS
77
+ state production 44 file(s) clean; 12 import react/jsx-runtime -> build passes
78
+ state --mode=dev 14 emitted file(s) reference the DEV runtime -> build FAILS
79
+ csx production 2 file(s) scanned; no React output to check -> build passes
80
+ ```
81
+
82
+ ## Migration checklist
83
+
84
+ - [ ] Nothing to do as a consumer.
85
+ - [ ] If you fork these packages and script a build with
86
+ `--configuration=development`, that configuration no longer exists — drop
87
+ the flag. It was producing an artifact that could not be published.
88
+ - [ ] If you add a new published frontend library, wire
89
+ `verifyProductionJsxRuntime(<its dist dir>)` into its vite plugins and do
90
+ not give it a `development` build configuration.
@@ -32,5 +32,6 @@ For migrations affecting the frontend app, see [frontend migrations](../../../..
32
32
  | 2026-08-06 | [`Image` is framework-free; Next's optimizer moves to `@clicktap/ui/next`](./2026/08/ui-2026-08-06-clicktap-ui-image-next-subpath.md) ↔ | Yes (Next consumers) | *None* |
33
33
  | 2026-08-11 | [Password reveal is a toggle again, not press-and-hold](./2026/08/ui-2026-08-11-password-reveal-toggle.md) | Behavioural | Supersedes CTAP-1128 |
34
34
  | 2026-08-11 | [`next` / `@heroui/*` peers capped; `react-stately` pinned](./2026/08/ui-2026-08-11-peer-ranges-capped.md) | Yes (consumers past a ceiling) | *None* |
35
+ | 2026-08-24 | [Accordion controlled selection matches authored keys again](./2026/08/ui-2026-08-24-accordion-author-key-space.md) | No | CTAP-1383 regression (CTAP-1404) |
35
36
 
36
37
  ↔ = cross-app migration (has counterpart in frontend app)
@@ -1,4 +1,34 @@
1
1
  [
2
+ {
3
+ "path": "2026/08/ui-2026-08-24-production-jsx-runtime-guard.md",
4
+ "module": "libs/frontend-modules/ui",
5
+ "package": "@clicktap/ui",
6
+ "date": "2026-08-24",
7
+ "title": "The build fails if it emits the development JSX runtime",
8
+ "classification": "BUGFIX",
9
+ "breaking": "No. A correct build is unaffected; an incorrect one now fails instead of publishing.",
10
+ "affects": "anyone building or publishing these packages. Consumers receive a guarantee, not a change."
11
+ },
12
+ {
13
+ "path": "2026/08/ui-2026-08-24-accordion-author-key-space.md",
14
+ "module": "libs/frontend-modules/ui",
15
+ "package": "@clicktap/ui",
16
+ "date": "2026-08-24",
17
+ "title": "Accordion controlled selection matches authored keys again",
18
+ "classification": "BUGFIX",
19
+ "breaking": "No — unless you shipped a workaround that pre-prefixes keys (see crispi note).",
20
+ "affects": "every controlled or default-expanded `Accordion` — checkout payment method selection, checkout's `defaultExpandedKeys={['cart']}`, totals/discount panel, PDP mobile overview, filter groups."
21
+ },
22
+ {
23
+ "path": "2026/08/ui-2026-08-23-use-client-propagates-to-barrels.md",
24
+ "module": "libs/frontend-modules/ui",
25
+ "package": "@clicktap/ui",
26
+ "date": "2026-08-23",
27
+ "title": "'use client' now reaches the re-export barrels",
28
+ "classification": "BUGFIX (packaging)",
29
+ "breaking": "No.",
30
+ "affects": "every emitted `components/**/index.js`; `Checkbox` and `RadioGroup` sources; the build's directive verification"
31
+ },
2
32
  {
3
33
  "path": "2026/08/ui-2026-08-22-react-aria-moves-as-one-version-set.md",
4
34
  "module": "libs/frontend-modules/ui",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clicktap/ui",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "private": false,
5
5
  "author": "Clicktap",
6
6
  "description": "A library of React UI components and low-level hooks.",