@clicktap/ui 2.0.3 → 2.0.5
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.
- package/components/Collection/CollectionToolbar/CollectionToolbar.js +1 -1
- package/components/Collection/ProductCardSwatches/ProductCardSwatches.js +1 -1
- package/docs/migrations/2026/08/ui-2026-08-21-graphql-client-string-documents.md +31 -4
- package/docs/migrations/2026/08/ui-2026-08-21-heroui-removed.md +15 -3
- package/docs/migrations/2026/08/ui-2026-08-22-collection-accessibility-fixes.md +28 -0
- package/docs/migrations/2026/08/ui-2026-08-22-react-aria-moves-as-one-version-set.md +19 -0
- package/docs/migrations/index.json +10 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsxs as i,jsx as l}from"react/jsx-runtime";import{Option as p}from"../../Select/Option.js";import{Select as x}from"../../Select/Select.js";import{Button as h}from"../../Button/Button.js";import"../../../utils/cn.js";import{FilterIcon as
|
|
1
|
+
"use client";import{jsxs as i,jsx as l}from"react/jsx-runtime";import{Option as p}from"../../Select/Option.js";import{Select as x}from"../../Select/Select.js";import{Button as h}from"../../Button/Button.js";import"../../../utils/cn.js";import{FilterIcon as u}from"../../Icon/FilterIcon.js";import"react-stately";import"react";import"../../Select/ButtonSelect.js";import"react-aria";import"@react-aria/interactions";import"../../Select/parts.js";import"react-aria-components";import"../../Select/ComboBoxSelect.js";import"../../Loader/Pulse.js";import"../../Select/MultiButtonSelect.js";function D({totalItems:o,startItem:s,endItem:m,setShowMobileFilters:n,availableSortOrders:r,appliedSortOrder:e,onSortChange:a}){const c=[...e?[e]:[],...r];return i("div",{className:"flex flex-col gap-y-5 mb-3",children:[i("div",{className:"flex items-center justify-between lg:justify-end gap-x-2",children:[i(h,{className:"lg:hidden grow-0 shrink-0 basis-auto gap-x-3 px-4",size:"sm",variant:"outline",onPress:()=>n(!0),children:[l(u,{className:"w-3 h-3"}),"Filters"]}),l(x,{className:"w-44",selectedKey:e?`${e.code}_${e.direction}`:null,onSelectionChange:a,placeholder:"Sort by",label:"Sort By:",classNames:{input:"text-xs w-44 h-8 min-h-0",value:"text-xs",label:"hidden lg:block text-xs text-gray-600 whitespace-nowrap"},children:c.map(t=>l(p,{textValue:t.label,children:t.label},`${t.code}_${t.direction}`))})]}),o>0&&i("p",{className:"text-sm text-fg-muted",children:["Displaying ",s," - ",m," of ",o," items"]})]})}export{D as CollectionToolbar};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as c,jsxs as I,Fragment as R}from"react/jsx-runtime";import{Button as p}from"react-aria-components";import{cn as b}from"../../../utils/cn.js";import{CheckIcon as M}from"../../Icon/CheckIcon.js";import{isDevelopment as x}from"../../../utils/env.js";import{isValidHex as P,isVeryLight as j}from"../../../utils/color.js";import{isSwatchRenderer as y,SwatchRenderer as $,isValidImageUrl as _}from"../../../utils/swatch.js";function L(d){const s=new Map;for(const a of d)if("renderer"in a&&y(a.renderer)&&"options"in a)for(const r of a.options)r.swatch&&s.set(r.id,{...r.swatch,attributeCode:a.code,optionCode:r.code});return s}function T(d){const s=new Set;for(const a of d)if(a.simpleProduct?.saleability?.isSaleable!==!1)for(const r of a.attributes)s.add(`${r.attribute.code}:${r.option.code}`);return s}function V(d,s,a,r){const m=new Set,h=[];for(const l of d)for(const i of l.attributes){const f=s.get(i.option.id);f&&!m.has(i.option.id)&&a.has(i.attribute.code)&&(m.add(i.option.id),h.push({optionId:i.option.id,optionCode:f.optionCode,attributeCode:i.attribute.code,label:i.option.label,swatch:f,image:l.simpleProduct.image??null,isSaleable:r.has(`${i.attribute.code}:${i.option.code}`)}))}return h.sort((l,i)=>l.isSaleable===i.isSaleable?0:l.isSaleable?-1:1),h}function O({variants:d,availableFilters:s,attributeSwatchConfigs:a=[],matchedOptionIds:r,selectedOptionId:m,onSelect:h}){const l=new Map;for(const e of a)e.swatch?.listRenderer&&(x&&!y(e.swatch.listRenderer)&&console.warn(`[Swatch] Unknown listRenderer "${e.swatch.listRenderer}" for attribute "${e.code}" — skipping list swatches`),l.set(e.code,e.swatch.listRenderer));const i=L(s),f=T(d),u=V(d,i,l,f);if(u.length===0)return null;const U=new Set(u.map(e=>e.optionId)),k=r.find(e=>U.has(e)),N=m??k??u[0]?.optionId??null,S=u[0]?l.get(u[0].attributeCode)??null:null;return c("div",{className:"flex flex-row flex-wrap items-center gap-
|
|
1
|
+
"use client";import{jsx as c,jsxs as I,Fragment as R}from"react/jsx-runtime";import{Button as p}from"react-aria-components";import{cn as b}from"../../../utils/cn.js";import{CheckIcon as M}from"../../Icon/CheckIcon.js";import{isDevelopment as x}from"../../../utils/env.js";import{isValidHex as P,isVeryLight as j}from"../../../utils/color.js";import{isSwatchRenderer as y,SwatchRenderer as $,isValidImageUrl as _}from"../../../utils/swatch.js";function L(d){const s=new Map;for(const a of d)if("renderer"in a&&y(a.renderer)&&"options"in a)for(const r of a.options)r.swatch&&s.set(r.id,{...r.swatch,attributeCode:a.code,optionCode:r.code});return s}function T(d){const s=new Set;for(const a of d)if(a.simpleProduct?.saleability?.isSaleable!==!1)for(const r of a.attributes)s.add(`${r.attribute.code}:${r.option.code}`);return s}function V(d,s,a,r){const m=new Set,h=[];for(const l of d)for(const i of l.attributes){const f=s.get(i.option.id);f&&!m.has(i.option.id)&&a.has(i.attribute.code)&&(m.add(i.option.id),h.push({optionId:i.option.id,optionCode:f.optionCode,attributeCode:i.attribute.code,label:i.option.label,swatch:f,image:l.simpleProduct.image??null,isSaleable:r.has(`${i.attribute.code}:${i.option.code}`)}))}return h.sort((l,i)=>l.isSaleable===i.isSaleable?0:l.isSaleable?-1:1),h}function O({variants:d,availableFilters:s,attributeSwatchConfigs:a=[],matchedOptionIds:r,selectedOptionId:m,onSelect:h}){const l=new Map;for(const e of a)e.swatch?.listRenderer&&(x&&!y(e.swatch.listRenderer)&&console.warn(`[Swatch] Unknown listRenderer "${e.swatch.listRenderer}" for attribute "${e.code}" — skipping list swatches`),l.set(e.code,e.swatch.listRenderer));const i=L(s),f=T(d),u=V(d,i,l,f);if(u.length===0)return null;const U=new Set(u.map(e=>e.optionId)),k=r.find(e=>U.has(e)),N=m??k??u[0]?.optionId??null,S=u[0]?l.get(u[0].attributeCode)??null:null;return c("div",{className:"flex flex-row flex-wrap items-center gap-2 mt-1.5",children:u.map(e=>{const o=N===e.optionId,w=e.isSaleable?e.label:`${e.label} (out of stock)`;if(S===$.IMAGE)return c(p,{"aria-label":w,className:"cursor-pointer p-0",onPress:()=>{h(e.optionId,e.optionCode,e.image,e.attributeCode)},children:({isHovered:t})=>c("span",{className:b("block w-8 aspect-[2.2/1] border bg-contain bg-center bg-no-repeat transition-all duration-150",o?"border-fg ring-1 ring-fg":"border-border-subtle",t&&!o&&"border-border-emphasis"),title:w,style:e.swatch.imageUrl?{backgroundImage:`url(${e.swatch.imageUrl})`}:void 0})},e.optionId);if(S===$.TEXT)return c(p,{"aria-label":w,className:"cursor-pointer p-0",onPress:()=>{h(e.optionId,e.optionCode,e.image,e.attributeCode)},children:({isHovered:t})=>c("span",{className:b("inline-flex items-center px-1.5 py-0.5 text-[10px] font-medium border transition-colors duration-150",o?"bg-surface-inverse text-white border-fg":"bg-white text-slate-700 border-border",t&&!o&&"border-slate-500"),children:e.swatch.text??e.label})},e.optionId);const n=_(e.swatch.imageUrl),C=P(e.swatch.hex);if(!n&&!C){if(x){const t=[];e.swatch.hex&&!C&&t.push(`invalid hex "${e.swatch.hex}"`),e.swatch.imageUrl&&!n&&t.push(`invalid imageUrl "${e.swatch.imageUrl}"`),!e.swatch.hex&&!e.swatch.imageUrl&&t.push("missing hex and imageUrl"),console.warn(`[Swatch] Skipping list swatch for attribute "${e.attributeCode}" option "${e.optionCode}" — ${t.join("; ")}`)}return null}x&&e.swatch.imageUrl&&!n&&console.warn(`[Swatch] Falling back to SWATCH (hex) renderer for attribute "${e.attributeCode}" option "${e.optionCode}" — invalid imageUrl "${e.swatch.imageUrl}"`);const g=n?!1:j(e.swatch.hex??null),v=n?{backgroundImage:`url(${e.swatch.imageUrl})`}:{backgroundColor:e.swatch.hex??void 0};return c(p,{"aria-label":w,className:"cursor-pointer size-4 flex items-center justify-center p-0",onPress:()=>{h(e.optionId,e.optionCode,e.image,e.attributeCode)},children:({isHovered:t})=>I("span",{className:b("relative inline-flex items-center justify-center size-4 rounded-full transition-all duration-150 overflow-hidden",n&&"bg-cover bg-center",n&&o&&"ring-2 ring-inset ring-white shadow-[0_0_0_2px_#222]",n&&t&&!o&&"ring-1 ring-border-emphasis",!n&&g&&"border border-border"),style:v,children:[!e.isSaleable&&I(R,{children:[c("span",{className:b("absolute top-1/2 left-1/2 w-[141%] h-[1.5px] -translate-x-1/2 -translate-y-1/2 -rotate-45",g?"bg-slate-400":"bg-white drop-shadow-sm",t&&!o&&"opacity-70")}),(o||t)&&c("span",{className:b("absolute top-1/2 left-1/2 w-[141%] h-[1.5px] -translate-x-1/2 -translate-y-1/2 rotate-45",g?"bg-slate-400":"bg-white drop-shadow-sm",t&&!o&&"opacity-70")})]}),!n&&e.isSaleable&&(o||t)&&c(M,{className:b("w-2.5 h-2.5 transition-opacity duration-150",g?"text-fg-subtle":"text-white drop-shadow-sm",t&&!o&&"opacity-70")})]})},e.optionId)})})}export{O as ProductCardSwatches};
|
|
@@ -35,11 +35,38 @@ query text codegen started from.
|
|
|
35
35
|
|
|
36
36
|
## Migration
|
|
37
37
|
|
|
38
|
-
1. Switch codegen to string documents: `
|
|
39
|
-
|
|
38
|
+
1. Switch codegen to string documents. `documentMode: 'string'` goes under **`config`**, NOT
|
|
39
|
+
`presetConfig` — verified against `@graphql-codegen/client-preset` 4.8.3 and 6.1.3. Requires
|
|
40
|
+
client-preset >= 4 for `TypedDocumentString`. Regenerate.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
'./generated/': {
|
|
44
|
+
preset: 'client',
|
|
45
|
+
presetConfig: { fragmentMasking: false },
|
|
46
|
+
config: { documentMode: 'string' }, // <- here
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Verify the regeneration, because the wrong placement fails silently.** Under
|
|
51
|
+
`presetConfig`, codegen reports success and still emits AST documents — and since the new
|
|
52
|
+
client rejects an AST at runtime rather than at build time, both the build and the
|
|
53
|
+
typecheck stay green while every query goes out as the string `"[object Object]"`. A
|
|
54
|
+
consumer shipped exactly that and only caught it by counting emitted documents. After
|
|
55
|
+
regenerating, confirm the output contains `TypedDocumentString` and **zero**
|
|
56
|
+
`as unknown as DocumentNode`:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
grep -c 'TypedDocumentString' generated/graphql.ts # expect > 0
|
|
60
|
+
grep -c 'as unknown as DocumentNode' generated/graphql.ts # expect 0
|
|
61
|
+
```
|
|
40
62
|
2. Replace `TypedDocumentNode`/`RequestDocument` types at wrapper boundaries with
|
|
41
63
|
`GraphqlDocument` / `GraphqlVariables` from this module; `GraphQLClientRequestHeaders`
|
|
42
64
|
with the platform `HeadersInit`.
|
|
43
65
|
3. Sites that imported `ClientError` from `graphql-request` import it from here.
|
|
44
|
-
4.
|
|
45
|
-
`createAuthAwareFetch()`
|
|
66
|
+
4. Audit every site that turns a document into text itself, not just the `FormData` ones.
|
|
67
|
+
`createAuthAwareFetch()` is unchanged, and `String(document)` replaces `print(document)` —
|
|
68
|
+
but the class to search for is broader than uploads: **any hand-rolled
|
|
69
|
+
`fetch(url, { body: JSON.stringify({ query: print(Doc) }) })`**. A consumer found two
|
|
70
|
+
(`getCompanyCollection.ts`, `setCurrentCompanyCookie.ts`) that an inventory of upload call
|
|
71
|
+
sites did not anticipate, and they surfaced only after the codegen switch. Grep for
|
|
72
|
+
`print(` and for `from 'graphql'` across the app, not only for `FormData`.
|
|
@@ -22,16 +22,28 @@ base is react-aria-components; the rewrites cost nothing new:
|
|
|
22
22
|
- **Pagination** → native boundary/sibling windowing.
|
|
23
23
|
- **Badge** → the two wrapper elements, inline.
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Same name, CHANGED signature — read this first
|
|
26
|
+
|
|
27
|
+
These kept their names, so a call site compiles, but the contract moved. A consumer had four
|
|
28
|
+
call sites break on props this document previously listed as simply preserved.
|
|
29
|
+
|
|
30
|
+
| Prop | Was (heroui) | Now |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| `AccordionItem.onPress` | received a `PressEvent` | receives **no arguments**. A handler reading `e.target` type-checks anyway, because an extra declared parameter is accepted in some positions — it just gets `undefined` at runtime. |
|
|
33
|
+
| `Accordion.onSelectionChange` | could emit the string `'all'` | always emits `Set<string>`. A branch testing `keys === 'all'` is now dead code. |
|
|
34
|
+
| `Tooltip` `classNames.arrow` | slot existed | **removed** — the arrow is `OverlayArrow`; style it via `content`/`base` or the arrow element itself. |
|
|
35
|
+
| `Tooltip` `isOpen` | controlled open state | **not supported** — the RAC `TooltipTrigger` owns open state. Use `delay`/`isDisabled`, or drive visibility from the trigger. |
|
|
36
|
+
|
|
37
|
+
## Same name, same behavior
|
|
26
38
|
|
|
27
39
|
- Accordion: `selectionMode`, `defaultSelectedKeys` (incl. `"all"`),
|
|
28
|
-
`defaultExpandedKeys`, controlled `selectedKeys
|
|
40
|
+
`defaultExpandedKeys`, controlled `selectedKeys`,
|
|
29
41
|
`disallowEmptySelection`, `hideIndicator` (group and per-item),
|
|
30
42
|
`showDivider`/`dividerProps` (an `<hr>` between items — `[&_hr]:hidden`
|
|
31
43
|
keeps working), `itemClasses`/per-item `classNames` slots, per-item
|
|
32
44
|
`title`/`subtitle`/`startContent`/`indicator` (a component still receives
|
|
33
45
|
`{ isOpen }` — import `AccordionItemIndicatorProps` from HERE now),
|
|
34
|
-
`disableIndicatorAnimation`,
|
|
46
|
+
`disableIndicatorAnimation`, and the `data-open` attribute on
|
|
35
47
|
heading/trigger/indicator that `data-[open=true]:` selectors style.
|
|
36
48
|
- Tooltip: `content`, `offset`, `placement`, `showArrow`, `delay`,
|
|
37
49
|
`isDisabled`, `classNames.base/content`, arbitrary child as trigger.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# CollectionToolbar and ProductCardSwatches Accessibility Fixes
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-08-22
|
|
4
|
+
**Package:** `@clicktap/ui`
|
|
5
|
+
**Affects:** `Collection/CollectionToolbar`, `Collection/ProductCardSwatches`
|
|
6
|
+
**Classification:** BUGFIX (accessibility)
|
|
7
|
+
**Breaking:** No. Visual change is 2px of extra gap between card swatches.
|
|
8
|
+
**Ticket:** CTAP-1380
|
|
9
|
+
|
|
10
|
+
## Changes
|
|
11
|
+
|
|
12
|
+
**`CollectionToolbar` — invalid `aria-details` removed.** The mobile filter button carried
|
|
13
|
+
`aria-details="show filters"`. `aria-details` takes an **ID reference**, not text, so axe
|
|
14
|
+
flags the value as invalid (`aria-valid-attr-value`). The button already has a visible
|
|
15
|
+
"Filters" label, so the attribute was redundant too.
|
|
16
|
+
|
|
17
|
+
**`ProductCardSwatches` — swatch spacing meets WCAG 2.5.8.** The swatches are 16px, under
|
|
18
|
+
the 24px minimum target size. The standard's spacing exception allows a smaller target
|
|
19
|
+
when no neighbouring target falls inside a 24px-diameter circle centred on it — centres
|
|
20
|
+
at least 24px apart. The row used `gap-1.5` (6px), leaving centres 22px apart, so every
|
|
21
|
+
swatch failed `target-size` (37 elements on a single collection page). Now `gap-2` (8px),
|
|
22
|
+
which puts centres at exactly 24px. The swatches themselves are unchanged.
|
|
23
|
+
|
|
24
|
+
## What to check
|
|
25
|
+
|
|
26
|
+
Swatch rows are 2px wider per gap — confirm no wrapping regression at the narrowest
|
|
27
|
+
breakpoint. Consumers overriding the swatch container's spacing should re-check they
|
|
28
|
+
still satisfy the 24px centre-to-centre rule.
|
|
@@ -38,6 +38,25 @@ reference app pins the identical versions. When bumping, take the trio from the
|
|
|
38
38
|
react-aria release announcement and re-run the production-bundle checks below —
|
|
39
39
|
aligning to the coherent set fixed the Table failure outright with no code change.
|
|
40
40
|
|
|
41
|
+
## If you still depend on `@heroui/*`, pinning is not enough
|
|
42
|
+
|
|
43
|
+
`@clicktap/ui` 2.x dropped heroui, so this case is invisible upstream — but a fork still carrying
|
|
44
|
+
it will install **both** trios no matter what it pins. heroui drags the old set in transitively:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
@heroui/badge -> @heroui/system -> @react-spectrum/provider -> @adobe/react-spectrum
|
|
48
|
+
-> react-aria-components (old)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
A consumer pinned one release *above* the verified set and ended up with two full trios and six
|
|
52
|
+
`@react-aria/*` subpackages at two versions each — precisely the skew this guide warns is invisible
|
|
53
|
+
outside production bundles. Aligning **down** to the verified set unified them.
|
|
54
|
+
|
|
55
|
+
So: pin to the verified set exactly (not "at least"), and if the duplicate persists, the remaining
|
|
56
|
+
copy is being pulled by a heroui/react-spectrum branch. Check with the instance-directory diff
|
|
57
|
+
below — and note that removing heroui is the only complete fix, since its transitive floor is not
|
|
58
|
+
something a pin can override.
|
|
59
|
+
|
|
41
60
|
## How to verify (jsdom cannot)
|
|
42
61
|
|
|
43
62
|
Probe a production build in a real browser: open an uncontrolled DialogTrigger modal
|
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
"affects": "every consumer of react-aria-components — dependency policy, not an API change",
|
|
11
11
|
"read_before_updating": "never bump `react-aria`, `react-aria-components`, or `react-stately` independently — skewed internals fail silently, and only in production bundles."
|
|
12
12
|
},
|
|
13
|
+
{
|
|
14
|
+
"path": "2026/08/ui-2026-08-22-collection-accessibility-fixes.md",
|
|
15
|
+
"module": "libs/frontend-modules/ui",
|
|
16
|
+
"package": "@clicktap/ui",
|
|
17
|
+
"date": "2026-08-22",
|
|
18
|
+
"title": "CollectionToolbar and ProductCardSwatches Accessibility Fixes",
|
|
19
|
+
"classification": "BUGFIX (accessibility)",
|
|
20
|
+
"breaking": "No. Visual change is 2px of extra gap between card swatches.",
|
|
21
|
+
"affects": "`Collection/CollectionToolbar`, `Collection/ProductCardSwatches`"
|
|
22
|
+
},
|
|
13
23
|
{
|
|
14
24
|
"path": "2026/08/ui-2026-08-22-accordion-divider-class-ownership.md",
|
|
15
25
|
"module": "libs/frontend-modules/ui",
|