@clicktap/ui 2.4.6 → 2.4.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{jsxs as
|
|
1
|
+
"use client";import{jsxs as d,jsx as r}from"react/jsx-runtime";import{useRef as l}from"react";import{useSelect as G,useButton as J,HiddenSelect as Q}from"react-aria";import{useInteractOutside as T}from"@react-aria/interactions";import{useSelectState as U}from"react-stately";import{cn as e}from"../../utils/cn.js";import{ChevronIcon as V,SelectPopover as W,SelectListBox as X}from"./parts.js";import"react-aria-components";function le(c){const{label:a,description:u,errorMessage:f,children:g,placeholder:x,slots:h,popoverOffset:v,className:N,classNames:s,isLoading:y,name:b,selectedKey:I,defaultSelectedKey:P,onSelectionChange:S,isRequired:B,items:w,disabledKeys:R,autoFocus:C,isInvalid:O,id:L,"aria-label":j,"aria-labelledby":D,"aria-describedby":H}=c,n=!!(c.isDisabled||y),m={label:a,children:g,items:w,name:b,selectedKey:I,defaultSelectedKey:P,onSelectionChange:S,isDisabled:n,disabledKeys:R,isRequired:B,autoFocus:C,isInvalid:O,id:L,"aria-label":j,"aria-labelledby":D,"aria-describedby":H},t=U(m),p=l(null),o=l(null),K=l(null),M=l(null),{labelProps:_,triggerProps:k,valueProps:q,menuProps:F,descriptionProps:z,errorMessageProps:A,isInvalid:i}=G(m,t,o),{buttonProps:E}=J(k,o);return T({ref:p,onInteractOutside:()=>t.close()}),d("div",{ref:p,className:e("flex flex-col","w-full",N),children:[r("label",{..._,className:e("flex","text-xs text-fg-muted",s?.label),children:a}),r(Q,{state:t,triggerRef:o,label:a,name:b,isDisabled:n}),d("div",{className:e("flex","relative","w-full",s?.comboBoxContainer),children:[d("button",{...E,ref:o,className:e("flex items-center justify-between gap-2","border border-solid border-border","text-sm text-fg text-left","py-0 px-3","h-10 w-full","m-0","rounded-md","bg-surface","cursor-pointer","transition-all ease-in-out duration-200","hover:border-border-emphasis","focus:outline focus:outline-2 focus:outline-border-subtle focus:border-border-emphasis","disabled:bg-surface-subtle disabled:border-border disabled:cursor-default",i&&"border-danger-soft bg-danger-surface text-danger hover:border-danger focus:border-danger focus:outline-danger-ring",s?.input),children:[r("span",{...q,className:e("truncate",!t.selectedItem&&"text-fg-subtle",s?.value),children:t.selectedItem?t.selectedItem.rendered:x??"Select..."}),r("span",{className:e("shrink-0 [&_svg]:w-4 [&_svg]:h-4",s?.arrowButton),children:r(V,{isOpen:t.isOpen,isDisabled:n,isInvalid:i,buttonIcon:h?.buttonIcon})})]}),t.isOpen&&r(W,{popoverRef:M,offset:v,className:e(s?.listContainer),children:r(X,{state:t,listBoxProps:F,listBoxRef:K,optionClassName:e(s?.option),sectionHeadingClassName:e(s?.sectionHeading),className:e("max-h-80","overflow-y-auto","outline-none",s?.list)})})]}),u&&r("span",{...z,className:e("flex","text-xs","text-fg-muted",s?.description),children:u}),i&&typeof f=="string"&&r("span",{...A,className:e("flex","text-xs","text-danger-soft",s?.errorMessage),children:f})]})}export{le as ButtonSelect,le as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsxs as d,jsx as o}from"react/jsx-runtime";import{useRef as
|
|
1
|
+
"use client";import{jsxs as d,jsx as o}from"react/jsx-runtime";import{useRef as r}from"react";import{useButton as F}from"react-aria";import{useListState as H,useOverlayTriggerState as _}from"react-stately";import{cn as e}from"../../utils/cn.js";import{ChevronIcon as k,SelectOverlay as T,SelectListBox as V}from"./parts.js";import"react-aria-components";function Q(u){const{label:f,description:b,errorMessage:m,children:x,placeholder:h,slots:v,popoverOffset:y,className:N,classNames:t,isLoading:S,items:B,disabledKeys:I,isInvalid:O,"aria-label":p,"aria-labelledby":w,selectedKeys:C,defaultSelectedKeys:R,onSelectionChange:L}=u,g=!!(u.isDisabled||S),a=H({children:x,items:B,disabledKeys:I,selectionMode:"multiple",selectionBehavior:"toggle",selectedKeys:C,defaultSelectedKeys:R,onSelectionChange:s=>{s!=="all"&&L?.([...s])}}),l=_({}),M=r(null),n=r(null),j=r(null),K=r(null),{buttonProps:P}=F({isDisabled:g,onPress:()=>l.toggle(),"aria-label":p,"aria-labelledby":w},n),i=!!O,c=[...a.collection.getKeys()].map(s=>a.collection.getItem(s)).filter(s=>s!=null&&s.type==="item"&&a.selectionManager.isSelected(s.key)),D=c.map(s=>s.textValue).join(", ");return d("div",{ref:M,className:e("flex flex-col","w-full",N),children:[o("label",{className:e("flex","text-xs text-fg-muted",t?.label),children:f}),d("div",{className:e("flex","relative","w-full",t?.comboBoxContainer),children:[d("button",{...P,ref:n,"aria-haspopup":"listbox","aria-expanded":l.isOpen,className:e("flex items-center justify-between gap-2","border border-solid border-border","text-sm text-fg text-left","py-0 px-3","h-10 w-full","m-0","rounded-md","bg-surface","cursor-pointer","transition-all ease-in-out duration-200","hover:border-border-emphasis","focus:outline focus:outline-2 focus:outline-border-subtle focus:border-border-emphasis","disabled:bg-surface-subtle disabled:border-border disabled:cursor-default",i&&"border-danger-soft bg-danger-surface text-danger hover:border-danger focus:border-danger focus:outline-danger-ring",t?.input),children:[o("span",{className:e("truncate",c.length===0&&"text-fg-subtle",t?.value),children:c.length>0?D:h??"Select..."}),o("span",{className:e("shrink-0 [&_svg]:w-4 [&_svg]:h-4",t?.arrowButton),children:o(k,{isOpen:l.isOpen,isDisabled:g,isInvalid:i,buttonIcon:v?.buttonIcon})})]}),l.isOpen&&o(T,{state:l,triggerRef:n,popoverRef:K,offset:y,className:e(t?.listContainer),children:o(V,{state:a,listBoxProps:{"aria-label":f??p??"Options",autoFocus:"first",shouldFocusWrap:!0},listBoxRef:j,optionClassName:e(t?.option),sectionHeadingClassName:e(t?.sectionHeading),className:e("max-h-80","overflow-y-auto","outline-none",t?.list)})})]}),b&&o("span",{className:e("flex","text-xs","text-fg-muted",t?.description),children:b}),i&&typeof m=="string"&&o("span",{className:e("flex","text-xs","text-danger-soft",t?.errorMessage),children:m})]})}export{Q as MultiButtonSelect,Q as default};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Select: `disabledKeys` works in button mode
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-10-09
|
|
4
|
+
**Affects:** `components/Select` (`ButtonSelect`, `MultiButtonSelect`)
|
|
5
|
+
**Classification:** BUGFIX
|
|
6
|
+
**Breaking:** No
|
|
7
|
+
**Ticket:** CTAP-2018
|
|
8
|
+
|
|
9
|
+
## Dependencies
|
|
10
|
+
|
|
11
|
+
*None*
|
|
12
|
+
|
|
13
|
+
See also: `apps/frontend/docs/migrations/2026/10/frontend-2026-10-09-pdp-out-of-stock-options.md`, the
|
|
14
|
+
first consumer.
|
|
15
|
+
|
|
16
|
+
## Problem
|
|
17
|
+
|
|
18
|
+
`SelectProps` accepts `disabledKeys` (it extends react-aria's combobox props), but only the searchable
|
|
19
|
+
mode honored it. The default button mode (`ButtonSelect`) and the multiple-selection button mode
|
|
20
|
+
(`MultiButtonSelect`) built their react-stately state from a fixed list of props that left
|
|
21
|
+
`disabledKeys` out. A caller could pass it, see no type error, and still have every option selectable.
|
|
22
|
+
|
|
23
|
+
## Changes
|
|
24
|
+
|
|
25
|
+
Both button modes pass `disabledKeys` to their state hook (`useSelectState` / `useListState`).
|
|
26
|
+
A disabled option stays in the list, carries `aria-disabled="true"`, and a press on it changes
|
|
27
|
+
nothing. `onSelectionChange` is not called.
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
<Select
|
|
31
|
+
aria-label="Size"
|
|
32
|
+
disabledKeys={['m']}
|
|
33
|
+
onSelectionChange={setSize}
|
|
34
|
+
>
|
|
35
|
+
<Option key="s">Small</Option>
|
|
36
|
+
<Option key="m">Medium (out of stock)</Option>
|
|
37
|
+
<Option key="l">Large</Option>
|
|
38
|
+
</Select>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Migration Steps
|
|
42
|
+
|
|
43
|
+
None required. A consumer that worked around the gap, for example by ignoring a pick in
|
|
44
|
+
`onSelectionChange`, can drop the workaround and pass `disabledKeys`.
|
|
45
|
+
|
|
46
|
+
**Verify:** open the select. The disabled option is listed but cannot be picked
|
|
47
|
+
(`ButtonSelect.spec.tsx`, "disabledKeys").
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"path": "2026/10/ui-2026-10-09-select-forwards-disabled-keys.md",
|
|
4
|
+
"module": "libs/frontend-modules/ui",
|
|
5
|
+
"package": "@clicktap/ui",
|
|
6
|
+
"date": "2026-10-09",
|
|
7
|
+
"title": "Select: `disabledKeys` works in button mode",
|
|
8
|
+
"classification": "BUGFIX",
|
|
9
|
+
"breaking": "No",
|
|
10
|
+
"affects": "`components/Select` (`ButtonSelect`, `MultiButtonSelect`)"
|
|
11
|
+
},
|
|
2
12
|
{
|
|
3
13
|
"path": "2026/10/ui-2026-10-06-invalid-styling-keys-on-data-invalid.md",
|
|
4
14
|
"module": "libs/frontend-modules/ui",
|