@charcuterie/ui 4.27.0 → 4.27.1
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/dist/SegmentedControl/SegmentedOption.js +1 -1
- package/dist/SegmentedControl/SegmentedOption.js.map +1 -1
- package/package.json +1 -1
- package/src/SegmentedControl/SegmentedControl.stories.tsx +41 -0
- package/src/SegmentedControl/SegmentedControl.test.tsx +38 -3
- package/src/SegmentedControl/SegmentedOption.tsx +1 -1
|
@@ -23,7 +23,7 @@ export const SegmentedOption = ({ isChecked, isFullWidth, item, onChoose, regist
|
|
|
23
23
|
}
|
|
24
24
|
return registerFocus(value);
|
|
25
25
|
}, [isDisabled, registerFocus, value]);
|
|
26
|
-
return (_jsx("button", { "aria-checked": isChecked, className: toClassName("cursor-pointer rounded-sm border border-transparent font-medium tabular-nums transition-colors duration-(--duration-fast) ease-standard",
|
|
26
|
+
return (_jsx("button", { "aria-checked": isChecked, className: toClassName("inline-flex cursor-pointer items-center justify-center rounded-sm border border-transparent font-medium tabular-nums transition-colors duration-(--duration-fast) ease-standard",
|
|
27
27
|
// In a very narrow board, three full labels cannot share
|
|
28
28
|
// one row. The minimum lets two fit, then the final option
|
|
29
29
|
// takes a full second row instead of wrapping its label or
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedOption.js","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedOption.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAoB/C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAC9B,SAAS,EACT,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,IAAI,EACJ,QAAQ,EACR,YAAY,GACS,EAAa,EAAE;IACpC,MAAM,EAAE,UAAU,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;IAEjD,SAAS,CACP,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAC9B,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAC3B,CAAA;IAED,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QAED,OAAO,aAAa,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAA;IAEtC,OAAO,CAEL,iCACgB,SAAS,EACvB,SAAS,EAAE,WAAW,CACpB,
|
|
1
|
+
{"version":3,"file":"SegmentedOption.js","sourceRoot":"","sources":["../../src/SegmentedControl/SegmentedOption.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAoB/C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAC9B,SAAS,EACT,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,IAAI,EACJ,QAAQ,EACR,YAAY,GACS,EAAa,EAAE;IACpC,MAAM,EAAE,UAAU,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;IAEjD,SAAS,CACP,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAC9B,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAC3B,CAAA;IAED,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QAED,OAAO,aAAa,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,CAAA;IAEtC,OAAO,CAEL,iCACgB,SAAS,EACvB,SAAS,EAAE,WAAW,CACpB,iLAAiL;QACjL,yDAAyD;QACzD,2DAA2D;QAC3D,2DAA2D;QAC3D,kCAAkC;QAClC,WAAW,IAAI,0BAA0B,EACzC,kBAAkB,CAAC,IAAI,CAAC;QACxB,mDAAmD;QACnD,6DAA6D;QAC7D,0DAA0D;QAC1D,2CAA2C;QAC3C,SAAS;YACP,CAAC,CAAC,oDAAoD;YACtD,CAAC,CAAC,mFAAmF,EACvF,UAAU;YACR,2FAA2F,EAC7F,gBAAgB,CACjB,EACD,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,GAAG,EAAE;YACZ,QAAQ,CAAC,KAAK,CAAC,CAAA;QACjB,CAAC,EACD,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE;YACf,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAC9B,CAAC,EACD,IAAI,EAAC,OAAO,EACZ,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAC,QAAQ,YAEZ,KAAK,GACC,CACV,CAAA;AACH,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -200,3 +200,44 @@ export const FullWidth: Story = {
|
|
|
200
200
|
export const Interactive: Story = {
|
|
201
201
|
args: { items: COLUMN_ITEMS, label: "Columns" },
|
|
202
202
|
}
|
|
203
|
+
|
|
204
|
+
/** Inline rich labels must be centered as boxes, not aligned to an anonymous text baseline. */
|
|
205
|
+
export const RichLabels: Story = {
|
|
206
|
+
args: {
|
|
207
|
+
isFullWidth: true,
|
|
208
|
+
label: "Message type",
|
|
209
|
+
items: [
|
|
210
|
+
{
|
|
211
|
+
value: "text",
|
|
212
|
+
label: (
|
|
213
|
+
<span className="inline-flex items-center gap-2 whitespace-nowrap">
|
|
214
|
+
<span
|
|
215
|
+
aria-hidden="true"
|
|
216
|
+
className="size-4 rounded-sm bg-current"
|
|
217
|
+
/>
|
|
218
|
+
<span className="min-w-0 truncate">
|
|
219
|
+
Type a message
|
|
220
|
+
</span>
|
|
221
|
+
</span>
|
|
222
|
+
),
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
value: "voice",
|
|
226
|
+
label: (
|
|
227
|
+
<span className="inline-flex items-center gap-2 whitespace-nowrap">
|
|
228
|
+
<span
|
|
229
|
+
aria-hidden="true"
|
|
230
|
+
className="size-4 rounded-full bg-current"
|
|
231
|
+
/>
|
|
232
|
+
<span className="min-w-0 truncate">
|
|
233
|
+
Your voice
|
|
234
|
+
</span>
|
|
235
|
+
<small className="min-w-0 truncate text-xs">
|
|
236
|
+
Later
|
|
237
|
+
</small>
|
|
238
|
+
</span>
|
|
239
|
+
),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
}
|
|
@@ -7,8 +7,43 @@ import { mountStory } from "../mountStory.testHelpers.ts"
|
|
|
7
7
|
import { expectAgentDrivable } from "../testing/index.ts"
|
|
8
8
|
import * as stories from "./SegmentedControl.stories.tsx"
|
|
9
9
|
|
|
10
|
-
const {
|
|
11
|
-
|
|
10
|
+
const {
|
|
11
|
+
AllStates,
|
|
12
|
+
Playground,
|
|
13
|
+
FullWidth,
|
|
14
|
+
Interactive,
|
|
15
|
+
RichLabels,
|
|
16
|
+
} = composeStories(stories)
|
|
17
|
+
|
|
18
|
+
test("rich label boxes are centered inside both segmented options", async () => {
|
|
19
|
+
const { canvas } = await mountStory(RichLabels)
|
|
20
|
+
const group = expectAgentDrivable(canvas, {
|
|
21
|
+
name: "Message type",
|
|
22
|
+
role: "radiogroup",
|
|
23
|
+
})
|
|
24
|
+
for (const option of getOptions(group)) {
|
|
25
|
+
const label = option.firstElementChild
|
|
26
|
+
if (!label) throw new Error("The rich label is missing")
|
|
27
|
+
const optionBox = option.getBoundingClientRect()
|
|
28
|
+
const labelBox = label.getBoundingClientRect()
|
|
29
|
+
expect(
|
|
30
|
+
Math.abs(
|
|
31
|
+
optionBox.y +
|
|
32
|
+
optionBox.height / 2 -
|
|
33
|
+
labelBox.y -
|
|
34
|
+
labelBox.height / 2,
|
|
35
|
+
),
|
|
36
|
+
).toBeLessThan(0.5)
|
|
37
|
+
expect(
|
|
38
|
+
Math.abs(
|
|
39
|
+
optionBox.x +
|
|
40
|
+
optionBox.width / 2 -
|
|
41
|
+
labelBox.x -
|
|
42
|
+
labelBox.width / 2,
|
|
43
|
+
),
|
|
44
|
+
).toBeLessThan(0.5)
|
|
45
|
+
}
|
|
46
|
+
})
|
|
12
47
|
|
|
13
48
|
/**
|
|
14
49
|
* `querySelectorAll` rather than `getAllByRole("radio")`, and not
|
|
@@ -237,7 +272,7 @@ test("full-width mode fills its container without wrapping labels", async () =>
|
|
|
237
272
|
)
|
|
238
273
|
expect(
|
|
239
274
|
Math.max(...widths) - Math.min(...widths),
|
|
240
|
-
).toBeLessThan(
|
|
275
|
+
).toBeLessThan(0.5)
|
|
241
276
|
}
|
|
242
277
|
}
|
|
243
278
|
})
|
|
@@ -66,7 +66,7 @@ export const SegmentedOption = ({
|
|
|
66
66
|
<button
|
|
67
67
|
aria-checked={isChecked}
|
|
68
68
|
className={toClassName(
|
|
69
|
-
"cursor-pointer rounded-sm border border-transparent font-medium tabular-nums transition-colors duration-(--duration-fast) ease-standard",
|
|
69
|
+
"inline-flex cursor-pointer items-center justify-center rounded-sm border border-transparent font-medium tabular-nums transition-colors duration-(--duration-fast) ease-standard",
|
|
70
70
|
// In a very narrow board, three full labels cannot share
|
|
71
71
|
// one row. The minimum lets two fit, then the final option
|
|
72
72
|
// takes a full second row instead of wrapping its label or
|