@astryxdesign/lab 0.7.3-canary.fba04ed → 0.7.4-canary.31e54e4

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,6 +1,6 @@
1
1
  /**
2
2
  * @file ListInput.tsx
3
- * @input React, StyleX, Astryx field/list/button/icon/layer/empty-state primitives, compact size context, browser scroll geometry, programmatic theme tokens, and shared Lab reorder styles
3
+ * @input React, StyleX, Astryx field/list/button/icon/layer/empty-state primitives, the container-reveal hook, compact size context, browser scroll geometry, programmatic theme tokens, and shared Lab reorder styles
4
4
  * @output Exports ListInput and its controlled data, column, renderer, and change types
5
5
  * @position Lab experiment (RFC facebook/astryx#4531) for editing compact repeated records
6
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ListInput.d.ts","sourceRoot":"","sources":["../../src/ListInput/ListInput.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ListInput.d.ts","sourceRoot":"","sources":["../../src/ListInput/ListInput.tsx"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file ListInput.tsx
7
- * @input React, StyleX, Astryx field/list/button/icon/layer/empty-state primitives, compact size context, browser scroll geometry, programmatic theme tokens, and shared Lab reorder styles
7
+ * @input React, StyleX, Astryx field/list/button/icon/layer/empty-state primitives, the container-reveal hook, compact size context, browser scroll geometry, programmatic theme tokens, and shared Lab reorder styles
8
8
  * @output Exports ListInput and its controlled data, column, renderer, and change types
9
9
  * @position Lab experiment (RFC facebook/astryx#4531) for editing compact repeated records
10
10
  *
@@ -22,7 +22,7 @@ import { EmptyState } from '@astryxdesign/core/EmptyState';
22
22
  import { Field } from '@astryxdesign/core/Field';
23
23
  import { FieldStatus } from '@astryxdesign/core/FieldStatus';
24
24
  import { Icon } from '@astryxdesign/core/Icon';
25
- import { useAnnounce, useMediaQuery } from '@astryxdesign/core/hooks';
25
+ import { useAnnounce, useContainerReveal, useMediaQuery } from '@astryxdesign/core/hooks';
26
26
  import { IconButton } from '@astryxdesign/core/IconButton';
27
27
  import { useTranslator } from '@astryxdesign/core/i18n';
28
28
  import { useLayer } from '@astryxdesign/core/Layer';
@@ -31,7 +31,7 @@ import { useTheme } from '@astryxdesign/core/theme';
31
31
  import { VisuallyHidden } from '@astryxdesign/core/VisuallyHidden';
32
32
  import '@astryxdesign/core/theme/tokens.stylex';
33
33
  import { colorVars, fontWeightVars, sizeVars, spacingVars, typeScaleVars } from '@astryxdesign/core/theme/tokens.stylex';
34
- import { mergeProps, themeProps } from '@astryxdesign/core/utils';
34
+ import { focusOutlineStyles, mergeProps, themeProps } from '@astryxdesign/core/utils';
35
35
  import { reorderStyles } from "../reorderStyles.js";
36
36
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
37
37
  const styles = {
@@ -54,18 +54,10 @@ const styles = {
54
54
  kumcoG: "x1mkdm3x",
55
55
  $$css: true
56
56
  },
57
- rowWithReorder: {
58
- kumcoG: "x1ni329g",
59
- $$css: true
60
- },
61
57
  rowWithRemove: {
62
58
  kumcoG: "x1ni329g",
63
59
  $$css: true
64
60
  },
65
- rowWithBothControls: {
66
- kumcoG: "x1nra26t",
67
- $$css: true
68
- },
69
61
  fields: {
70
62
  k1xSpc: "xrvj5dj x12gktpv",
71
63
  kGNEyG: "xpqajaz",
@@ -81,25 +73,22 @@ const styles = {
81
73
  kBCFzs: "x1rlpkp8",
82
74
  $$css: true
83
75
  },
84
- contentBeforeBothControls: {
85
- kBCFzs: "xez1zrf",
86
- $$css: true
87
- },
88
76
  itemStatus: {
89
77
  kbNqZ1: "x7sv70a x1nh3qrq",
90
78
  keoZOQ: "xcsaf9d xc47mtf",
91
79
  k7Eaqz: "xeuugli",
92
80
  $$css: true
93
81
  },
94
- actionRow: {
95
- k1xSpc: "xrvj5dj",
82
+ reorderGrip: {
83
+ k1xSpc: "x78zum5",
96
84
  kGNEyG: "x6s0dn4",
97
- k1C7PZ: "x1lfs0n9",
98
- keoZOQ: "xtbrsbv",
99
- $$css: true
100
- },
101
- actionContent: {
102
- k7Eaqz: "xeuugli",
85
+ kjj79g: "xl56j7k",
86
+ kzqmXN: "x1f0dxl5",
87
+ kmVPX3: "x1717udv",
88
+ kogj98: "x1ghz6dp",
89
+ ksu8eU: "xng3xce",
90
+ kWkggS: "xjbqb8w",
91
+ kMwMTN: "xtbr613 x100q762",
103
92
  $$css: true
104
93
  }
105
94
  };
@@ -128,40 +117,44 @@ const dynamicStyles = {
128
117
  "--x-viewTransitionName": name != null ? name : undefined
129
118
  }]
130
119
  };
120
+
121
+ // The six-dot grip drawn at its own size, so the narrow reorder target needs no
122
+ // square icon box around it.
131
123
  function GripVerticalIcon(props) {
132
124
  return /*#__PURE__*/_jsxs("svg", {
133
- viewBox: "0 0 24 24",
125
+ viewBox: "0 0 6 10",
134
126
  fill: "none",
127
+ "aria-hidden": "true",
135
128
  ...props,
136
129
  children: [/*#__PURE__*/_jsx("circle", {
137
- cx: "9",
138
- cy: "6",
139
- r: "1.5",
130
+ cx: "1",
131
+ cy: "1",
132
+ r: "1",
140
133
  fill: "currentColor"
141
134
  }), /*#__PURE__*/_jsx("circle", {
142
- cx: "15",
143
- cy: "6",
144
- r: "1.5",
135
+ cx: "5",
136
+ cy: "1",
137
+ r: "1",
145
138
  fill: "currentColor"
146
139
  }), /*#__PURE__*/_jsx("circle", {
147
- cx: "9",
148
- cy: "12",
149
- r: "1.5",
140
+ cx: "1",
141
+ cy: "5",
142
+ r: "1",
150
143
  fill: "currentColor"
151
144
  }), /*#__PURE__*/_jsx("circle", {
152
- cx: "15",
153
- cy: "12",
154
- r: "1.5",
145
+ cx: "5",
146
+ cy: "5",
147
+ r: "1",
155
148
  fill: "currentColor"
156
149
  }), /*#__PURE__*/_jsx("circle", {
157
- cx: "9",
158
- cy: "18",
159
- r: "1.5",
150
+ cx: "1",
151
+ cy: "9",
152
+ r: "1",
160
153
  fill: "currentColor"
161
154
  }), /*#__PURE__*/_jsx("circle", {
162
- cx: "15",
163
- cy: "18",
164
- r: "1.5",
155
+ cx: "5",
156
+ cy: "9",
157
+ r: "1",
165
158
  fill: "currentColor"
166
159
  })]
167
160
  });
@@ -187,7 +180,9 @@ function joinIDs(...ids) {
187
180
  const resolved = ids.filter(Boolean);
188
181
  return resolved.length > 0 ? resolved.join(' ') : undefined;
189
182
  }
190
- const focusableFieldSelector = '[data-list-input-cell] input, [data-list-input-cell] select, [data-list-input-cell] textarea, [data-list-input-cell] button, [data-list-input-cell] [tabindex]:not([tabindex="-1"])';
183
+
184
+ // The reorder grip shares the first field's cell but is not a field.
185
+ const focusableFieldSelector = '[data-list-input-cell] input, [data-list-input-cell] select, [data-list-input-cell] textarea, [data-list-input-cell] button:not([data-list-input-reorder]), [data-list-input-cell] [tabindex]:not([tabindex="-1"]):not([data-list-input-reorder])';
191
186
  function getScrollableAncestors(element) {
192
187
  const scrollRoots = [];
193
188
  let current = element.parentElement;
@@ -411,6 +406,12 @@ export function ListInput({
411
406
  } = useLayer({
412
407
  mode: 'fixed'
413
408
  });
409
+ // Each record row reveals its row actions (Remove and, when reorderable, the
410
+ // grip) on hover or keyboard focus; touch keeps them visible.
411
+ const {
412
+ getContainerProps,
413
+ getContentRevealProps
414
+ } = useContainerReveal();
414
415
  const setReorderState = nextState => {
415
416
  reorderStateRef.current = nextState;
416
417
  setReorderStateState(nextState);
@@ -424,7 +425,7 @@ export function ListInput({
424
425
  }
425
426
  return moveItem(reorderState.originalValue, reorderState.fromIndex, reorderState.toIndex);
426
427
  }, [reorderState, value]);
427
- const showReorderColumn = isReorderable;
428
+ const showReorderHandle = isReorderable;
428
429
  const showRemoveColumn = true;
429
430
  const fieldGridTemplate = useMemo(() => columns.map(column => resolveColumnTrack(column.width)).join(' '), [columns]);
430
431
  const pointerPlacement = useMemo(() => {
@@ -446,6 +447,7 @@ export function ListInput({
446
447
  };
447
448
  }, [getItemKey, reorderState]);
448
449
  const previewCloneKey = reorderState?.mode === 'pointer' ? String(reorderState.itemKey) : null;
450
+ const isPointerReorderActive = previewCloneKey != null;
449
451
  useEffect(() => {
450
452
  if (process.env.NODE_ENV === 'production') {
451
453
  return;
@@ -1008,8 +1010,8 @@ export function ListInput({
1008
1010
  }));
1009
1011
  }
1010
1012
  };
1011
- const rowLayoutStyle = showReorderColumn ? showRemoveColumn ? styles.rowWithBothControls : styles.rowWithReorder : showRemoveColumn ? styles.rowWithRemove : styles.rowFieldsOnly;
1012
- const contentGridStyle = showReorderColumn ? styles.contentBeforeBothControls : showRemoveColumn ? styles.contentBeforeOneControl : styles.contentOnly;
1013
+ const rowLayoutStyle = showRemoveColumn ? styles.rowWithRemove : styles.rowFieldsOnly;
1014
+ const contentGridStyle = showRemoveColumn ? styles.contentBeforeOneControl : styles.contentOnly;
1013
1015
  const dragPreviewPosition = reorderState?.mode === 'pointer' && reorderState.pointerClientX != null && reorderState.pointerClientY != null && reorderState.pointerOffsetX != null && reorderState.pointerOffsetY != null && reorderState.previewWidth != null ? {
1014
1016
  x: reorderState.pointerClientX - reorderState.pointerOffsetX,
1015
1017
  y: reorderState.pointerClientY - reorderState.pointerOffsetY,
@@ -1078,6 +1080,91 @@ export function ListInput({
1078
1080
  const isPointerReorderSource = isActiveReorder && reorderState?.mode === 'pointer';
1079
1081
  const itemStatus = getItemStatus?.(item, index);
1080
1082
  const itemStatusID = itemStatus?.message ? `${groupID}-item-${index}-status` : undefined;
1083
+ const reorderHandle = showReorderHandle ? /*#__PURE__*/_jsx("div", {
1084
+ ...{
1085
+ className: "x10l6tqk x13vifvy x1ey2m1c x1eu7cjg x78zum5 x1qjc9v5 x1djylfy"
1086
+ },
1087
+ children: /*#__PURE__*/_jsx("div", {
1088
+ ...mergeProps(getContentRevealProps({
1089
+ isLayoutPreserved: true,
1090
+ // The grip owns an active reorder, so it stays
1091
+ // visible however the pointer moves.
1092
+ forceVisibility: isActiveReorder ? 'shown' : undefined
1093
+ }), // A flex row, so the grip stretches to the field's height.
1094
+ {
1095
+ className: "x78zum5"
1096
+ }),
1097
+ children: /*#__PURE__*/_jsx("button", {
1098
+ type: "button",
1099
+ "aria-label": t('@astryx.listInput.reorderItem', {
1100
+ itemName,
1101
+ position: index + 1
1102
+ }),
1103
+ disabled: isDisabled || isLoading,
1104
+ "aria-describedby": reorderInstructionsID,
1105
+ "aria-pressed": isActiveReorder || undefined,
1106
+ "data-list-input-reorder": "",
1107
+ ...stylex.props(focusOutlineStyles.focusVisible, styles.reorderGrip, reorderStyles.handle, isActiveReorder && reorderStyles.handleActive),
1108
+ onKeyDown: event => handleReorderKeyDown(event, item, index),
1109
+ onBlur: () => {
1110
+ const activeState = reorderStateRef.current;
1111
+ if (activeState?.mode === 'keyboard' && String(activeState.itemKey) === itemKeyString) {
1112
+ cancelReorder();
1113
+ }
1114
+ },
1115
+ onPointerDown: event => {
1116
+ if (event.button !== 0 || isDisabled || isLoading) {
1117
+ return;
1118
+ }
1119
+ const sourceRow = rowRefs.current.get(itemKeyString);
1120
+ if (sourceRow == null) {
1121
+ return;
1122
+ }
1123
+ const bounds = sourceRow.getBoundingClientRect();
1124
+ event.currentTarget.setPointerCapture?.(event.pointerId);
1125
+ startReorder(item, index, 'pointer', event.pointerId, {
1126
+ pointerStartX: event.clientX,
1127
+ pointerStartY: event.clientY,
1128
+ pointerOffsetX: event.clientX - bounds.left,
1129
+ pointerOffsetY: event.clientY - bounds.top,
1130
+ pointerClientX: event.clientX,
1131
+ pointerClientY: event.clientY,
1132
+ previewWidth: bounds.width
1133
+ });
1134
+ },
1135
+ onPointerMove: handlePointerMove,
1136
+ onPointerUp: event => {
1137
+ const activeState = reorderStateRef.current;
1138
+ if (activeState?.mode === 'pointer' && activeState.pointerId === event.pointerId) {
1139
+ commitReorder();
1140
+ if (event.currentTarget.hasPointerCapture?.(event.pointerId)) {
1141
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
1142
+ }
1143
+ }
1144
+ },
1145
+ onPointerCancel: event => {
1146
+ const activeState = reorderStateRef.current;
1147
+ if (activeState?.mode === 'pointer' && activeState.pointerId === event.pointerId) {
1148
+ cancelReorder();
1149
+ if (event.currentTarget.hasPointerCapture?.(event.pointerId)) {
1150
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
1151
+ }
1152
+ }
1153
+ },
1154
+ onLostPointerCapture: event => {
1155
+ const activeState = reorderStateRef.current;
1156
+ if (activeState?.mode === 'pointer' && activeState.pointerId === event.pointerId) {
1157
+ cancelReorder();
1158
+ }
1159
+ },
1160
+ children: /*#__PURE__*/_jsx(GripVerticalIcon, {
1161
+ ...{
1162
+ className: "x1lliihq x2lah0s x1wu31ai x14r7d11"
1163
+ }
1164
+ })
1165
+ })
1166
+ })
1167
+ }) : null;
1081
1168
  return /*#__PURE__*/_jsx("li", {
1082
1169
  "data-list-input-motion-key": `item:${itemKeyString}`,
1083
1170
  "data-list-input-drop-target": dropPosition ?? undefined,
@@ -1096,7 +1183,11 @@ export function ListInput({
1096
1183
  }
1097
1184
  },
1098
1185
  "data-list-input-row": itemKeyString,
1099
- ...stylex.props(styles.row, rowLayoutStyle, dynamicStyles.rowTransition(transitionName(groupID, itemKey)), isPointerReorderSource && reorderStyles.source),
1186
+ ...mergeProps(getContainerProps({
1187
+ // A pointer drag owns the list: rows the pointer
1188
+ // crosses stay quiet instead of lighting their grips.
1189
+ forceState: isPointerReorderActive && !isActiveReorder ? 'inactive' : undefined
1190
+ }), stylex.props(styles.row, rowLayoutStyle, dynamicStyles.rowTransition(transitionName(groupID, itemKey)), isPointerReorderSource && reorderStyles.source)),
1100
1191
  children: [/*#__PURE__*/_jsx("div", {
1101
1192
  ...stylex.props(styles.fields, dynamicStyles.fields(fieldGridTemplate)),
1102
1193
  children: columns.map((column, columnIndex) => {
@@ -1139,18 +1230,28 @@ export function ListInput({
1139
1230
  }
1140
1231
  }[!!isRepeatedLabel << 0],
1141
1232
  children: column.header
1142
- }), /*#__PURE__*/_jsx("div", {
1233
+ }), /*#__PURE__*/_jsxs("div", {
1143
1234
  ...{
1144
- className: "xeuugli"
1145
- },
1146
- children: content
1235
+ 0: {
1236
+ className: "xeuugli"
1237
+ },
1238
+ 1: {
1239
+ className: "xeuugli x1n2onr6"
1240
+ }
1241
+ }[!!(columnIndex === 0 && showReorderHandle) << 0],
1242
+ children: [columnIndex === 0 ? reorderHandle : null, content]
1147
1243
  })]
1148
1244
  }, column.key);
1149
1245
  })
1150
1246
  }), showRemoveColumn ? /*#__PURE__*/_jsx("div", {
1151
- ...{
1247
+ ...mergeProps(
1248
+ // Opacity only: Remove keeps its column, so the
1249
+ // fields never shift when it appears.
1250
+ getContentRevealProps({
1251
+ isLayoutPreserved: true
1252
+ }), {
1152
1253
  className: "x78zum5 x6s0dn4 x7fd4wk xl56j7k x1uogy3y xm5moh1 x4qyour x15daork x1y9m8ji"
1153
- },
1254
+ }),
1154
1255
  children: /*#__PURE__*/_jsx(IconButton, {
1155
1256
  label: t('@astryx.listInput.removeItem', {
1156
1257
  itemName,
@@ -1172,79 +1273,6 @@ export function ListInput({
1172
1273
  "data-list-input-remove": "",
1173
1274
  onClick: () => handleRemove(item)
1174
1275
  })
1175
- }) : null, showReorderColumn ? /*#__PURE__*/_jsx("div", {
1176
- ...{
1177
- className: "x78zum5 x6s0dn4 x7fd4wk xl56j7k x1uogy3y xm5moh1 x1b1z6u9 x15daork x1y9m8ji"
1178
- },
1179
- children: /*#__PURE__*/_jsx(IconButton, {
1180
- label: t('@astryx.listInput.reorderItem', {
1181
- itemName,
1182
- position: index + 1
1183
- }),
1184
- icon: /*#__PURE__*/_jsx(Icon, {
1185
- icon: GripVerticalIcon,
1186
- size: "sm"
1187
- }),
1188
- variant: "ghost",
1189
- size: "md",
1190
- isDisabled: isDisabled || isLoading,
1191
- "aria-describedby": reorderInstructionsID,
1192
- "aria-pressed": isActiveReorder || undefined,
1193
- "data-list-input-reorder": "",
1194
- xstyle: [reorderStyles.handle, isActiveReorder && reorderStyles.handleActive],
1195
- onKeyDown: event => handleReorderKeyDown(event, item, index),
1196
- onBlur: () => {
1197
- const activeState = reorderStateRef.current;
1198
- if (activeState?.mode === 'keyboard' && String(activeState.itemKey) === itemKeyString) {
1199
- cancelReorder();
1200
- }
1201
- },
1202
- onPointerDown: event => {
1203
- if (event.button !== 0 || isDisabled || isLoading) {
1204
- return;
1205
- }
1206
- const sourceRow = rowRefs.current.get(itemKeyString);
1207
- if (sourceRow == null) {
1208
- return;
1209
- }
1210
- const bounds = sourceRow.getBoundingClientRect();
1211
- event.currentTarget.setPointerCapture?.(event.pointerId);
1212
- startReorder(item, index, 'pointer', event.pointerId, {
1213
- pointerStartX: event.clientX,
1214
- pointerStartY: event.clientY,
1215
- pointerOffsetX: event.clientX - bounds.left,
1216
- pointerOffsetY: event.clientY - bounds.top,
1217
- pointerClientX: event.clientX,
1218
- pointerClientY: event.clientY,
1219
- previewWidth: bounds.width
1220
- });
1221
- },
1222
- onPointerMove: handlePointerMove,
1223
- onPointerUp: event => {
1224
- const activeState = reorderStateRef.current;
1225
- if (activeState?.mode === 'pointer' && activeState.pointerId === event.pointerId) {
1226
- commitReorder();
1227
- if (event.currentTarget.hasPointerCapture?.(event.pointerId)) {
1228
- event.currentTarget.releasePointerCapture?.(event.pointerId);
1229
- }
1230
- }
1231
- },
1232
- onPointerCancel: event => {
1233
- const activeState = reorderStateRef.current;
1234
- if (activeState?.mode === 'pointer' && activeState.pointerId === event.pointerId) {
1235
- cancelReorder();
1236
- if (event.currentTarget.hasPointerCapture?.(event.pointerId)) {
1237
- event.currentTarget.releasePointerCapture?.(event.pointerId);
1238
- }
1239
- }
1240
- },
1241
- onLostPointerCapture: event => {
1242
- const activeState = reorderStateRef.current;
1243
- if (activeState?.mode === 'pointer' && activeState.pointerId === event.pointerId) {
1244
- cancelReorder();
1245
- }
1246
- }
1247
- })
1248
1276
  }) : null, itemStatus?.message ? /*#__PURE__*/_jsx(FieldStatus, {
1249
1277
  id: itemStatusID,
1250
1278
  type: itemStatus.type,
@@ -1259,10 +1287,14 @@ export function ListInput({
1259
1287
  }), /*#__PURE__*/_jsx("div", {
1260
1288
  ref: addActionRef,
1261
1289
  "data-list-input-motion-key": "action:add",
1262
- ...stylex.props(styles.actionRow, rowLayoutStyle),
1290
+ ...{
1291
+ className: "xrvj5dj x6s0dn4 x1lfs0n9 xtbrsbv x1ni329g"
1292
+ },
1263
1293
  children: /*#__PURE__*/_jsx("div", {
1264
1294
  "data-list-input-add-content": "",
1265
- ...stylex.props(styles.actionContent, contentGridStyle),
1295
+ ...{
1296
+ className: "xeuugli x1rlpkp8"
1297
+ },
1266
1298
  children: /*#__PURE__*/_jsx(Button, {
1267
1299
  ref: addButtonRef,
1268
1300
  label: t('@astryx.listInput.addItem', {
@@ -1280,7 +1312,7 @@ export function ListInput({
1280
1312
  onClick: handleAdd
1281
1313
  })
1282
1314
  })
1283
- }), showReorderColumn ? /*#__PURE__*/_jsx(VisuallyHidden, {
1315
+ }), showReorderHandle ? /*#__PURE__*/_jsx(VisuallyHidden, {
1284
1316
  as: "div",
1285
1317
  id: reorderInstructionsID,
1286
1318
  children: t('@astryx.listInput.reorderInstructions')
package/dist/lab.css CHANGED
@@ -115,7 +115,6 @@
115
115
  .x4h4c4w:not(#\#):not(#\#){grid-column:-2 / -1}
116
116
  .x1osaytk:not(#\#):not(#\#){grid-column:1 / -1}
117
117
  .x1rlpkp8:not(#\#):not(#\#){grid-column:1 / -2}
118
- .xez1zrf:not(#\#):not(#\#){grid-column:1 / -3}
119
118
  .xgkxs2y:not(#\#):not(#\#){grid-column:1}
120
119
  .x17d9j0x:not(#\#):not(#\#){grid-column:2 / -1}
121
120
  .x1npkx4u:not(#\#):not(#\#){grid-column:2}
@@ -165,7 +164,6 @@
165
164
  @container list-input (max-width: 640px){.x6pnyrg.x6pnyrg:not(#\#):not(#\#){gap:var(--spacing-8)}}
166
165
  @container list-input (max-width: 640px){.x1v2w52t.x1v2w52t:not(#\#):not(#\#){grid-column:1}}
167
166
  @container list-input (max-width: 640px){.x4qyour.x4qyour:not(#\#):not(#\#){grid-column:2}}
168
- @container list-input (max-width: 640px){.x1b1z6u9.x1b1z6u9:not(#\#):not(#\#){grid-column:3}}
169
167
  @container list-input (max-width: 640px){.x1y9m8ji.x1y9m8ji:not(#\#):not(#\#){grid-row:1}}
170
168
  @container list-input (max-width: 640px){.x1nh3qrq.x1nh3qrq:not(#\#):not(#\#){grid-row:auto}}
171
169
  @container (max-width: 40rem){.xf3ta42.xf3ta42:not(#\#):not(#\#){overscroll-behavior:auto}}
@@ -177,6 +175,7 @@
177
175
  .xuk3077:not(#\#):not(#\#):not(#\#){align-items:flex-end}
178
176
  .x1cy8zhl:not(#\#):not(#\#):not(#\#){align-items:flex-start}
179
177
  .x7a106z:not(#\#):not(#\#):not(#\#){align-items:start}
178
+ .x1qjc9v5:not(#\#):not(#\#):not(#\#){align-items:stretch}
180
179
  .x7fd4wk:not(#\#):not(#\#):not(#\#){align-self:end}
181
180
  .xqcrz7y:not(#\#):not(#\#):not(#\#){align-self:flex-start}
182
181
  .x1y8v6su:not(#\#):not(#\#):not(#\#){align-self:start}
@@ -203,6 +202,7 @@
203
202
  .x128ha8g:not(#\#):not(#\#):not(#\#){animation-timing-function:var(--ease-standard)}
204
203
  .xjyslct:not(#\#):not(#\#):not(#\#){appearance:none}
205
204
  .x1plog1:not(#\#):not(#\#):not(#\#){aspect-ratio:1}
205
+ .x14r7d11:not(#\#):not(#\#):not(#\#){aspect-ratio:3 / 5}
206
206
  .x11fneen:not(#\#):not(#\#):not(#\#){aspect-ratio:auto}
207
207
  .x1ta4xzc:not(#\#):not(#\#):not(#\#){background-clip:text}
208
208
  .x2kq1z0:not(#\#):not(#\#):not(#\#){background-color:#0a0a0a}
@@ -423,7 +423,6 @@
423
423
  .x1dl91lv:not(#\#):not(#\#):not(#\#){grid-template-columns:96px 52px minmax(0,1fr)}
424
424
  .x37c5m6:not(#\#):not(#\#):not(#\#){grid-template-columns:minmax(0,1fr) auto minmax(0,1fr)}
425
425
  .xcjjqqt:not(#\#):not(#\#):not(#\#){grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
426
- .x1nra26t:not(#\#):not(#\#):not(#\#){grid-template-columns:minmax(0,1fr) var(--size-element-md) var(--size-element-md)}
427
426
  .x1ni329g:not(#\#):not(#\#):not(#\#){grid-template-columns:minmax(0,1fr) var(--size-element-md)}
428
427
  .x1mkdm3x:not(#\#):not(#\#):not(#\#){grid-template-columns:minmax(0,1fr)}
429
428
  .x1gj6vdr:not(#\#):not(#\#):not(#\#){grid-template-columns:repeat(7,minmax(0,1fr))}
@@ -437,6 +436,7 @@
437
436
  .x1eiw8i6:not(#\#):not(#\#):not(#\#){grid-template-rows:56px auto auto}
438
437
  .x1clt5hq:not(#\#):not(#\#):not(#\#){grid-template-rows:subgrid}
439
438
  .x1xxtoay:not(#\#):not(#\#):not(#\#){grid-template-rows:var(--x-gridTemplateRows)}
439
+ .x1eu7cjg:not(#\#):not(#\#):not(#\#){inset-inline-end:100%}
440
440
  .x1itk590:not(#\#):not(#\#):not(#\#){inset-inline-end:calc(var(--spacing-0-5) + var(--border-width))}
441
441
  .x1mcfs9z:not(#\#):not(#\#):not(#\#){inset-inline-end:var(--spacing-3)}
442
442
  .x1o0tod:not(#\#):not(#\#):not(#\#){inset-inline-start:0}
@@ -584,6 +584,7 @@
584
584
  .x173emx3:is([dir="rtl"] *):not(#\#):not(#\#):not(#\#){transform-origin:right bottom}
585
585
  .x135w3n3:focus-visible:not(#\#):not(#\#):not(#\#){z-index:1}
586
586
  .x16iypot:focus-visible:not(#\#):not(#\#):not(#\#){z-index:2}
587
+ .x100q762:disabled:not(#\#):not(#\#):not(#\#){color:var(--color-icon-disabled)}
587
588
  .xuqm82a:active:not(#\#):not(#\#):not(#\#){background-image:none}
588
589
  @media (prefers-reduced-motion: reduce){.xdn7m2a.xdn7m2a:not(#\#):not(#\#):not(#\#){animation-duration:1ms}}
589
590
  @media (prefers-reduced-motion: reduce){.xnh0sag.xnh0sag:not(#\#):not(#\#):not(#\#){animation-duration:3s}}
@@ -731,6 +732,8 @@
731
732
  .x1lout4y:not(#\#):not(#\#):not(#\#):not(#\#){width:var(--astryx-chat-typing-indicator-dots-column-width,auto)}
732
733
  .x1oai4fc:not(#\#):not(#\#):not(#\#):not(#\#){width:var(--icon-size)}
733
734
  .xw7hby4:not(#\#):not(#\#):not(#\#):not(#\#){width:var(--spacing-0-5)}
735
+ .x1wu31ai:not(#\#):not(#\#):not(#\#):not(#\#){width:var(--spacing-1-5)}
736
+ .x1f0dxl5:not(#\#):not(#\#):not(#\#):not(#\#){width:var(--spacing-2)}
734
737
  .x1uyokj7:not(#\#):not(#\#):not(#\#):not(#\#){width:var(--x-inlineSize)}
735
738
  .x5lhr3w:not(#\#):not(#\#):not(#\#):not(#\#){width:var(--x-width)}
736
739
  .x14l7nz5:first-child:not(#\#):not(#\#):not(#\#):not(#\#){margin-top:0}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/lab",
3
- "version": "0.7.3-canary.fba04ed",
3
+ "version": "0.7.4-canary.31e54e4",
4
4
  "description": "Experimental Astryx components — published to npm only under the @canary dist-tag for early testing; never released as a stable (latest) version.",
5
5
  "author": "Meta Open Source",
6
6
  "license": "MIT",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "peerDependencies": {
51
51
  "@stylexjs/stylex": ">=0.10.0",
52
- "@astryxdesign/core": "0.7.3-canary.fba04ed",
52
+ "@astryxdesign/core": "0.7.4-canary.31e54e4",
53
53
  "react": ">=19.0.0",
54
54
  "react-dom": ">=19.0.0"
55
55
  },
@@ -59,8 +59,8 @@
59
59
  "d3-shape": "^3.2.0"
60
60
  },
61
61
  "devDependencies": {
62
- "@astryxdesign/a11y-spec": "0.7.3-canary.fba04ed",
63
- "@astryxdesign/cli": "0.7.3-canary.fba04ed",
62
+ "@astryxdesign/a11y-spec": "0.7.4-canary.31e54e4",
63
+ "@astryxdesign/cli": "0.7.4-canary.31e54e4",
64
64
  "@babel/cli": "^7.29.7",
65
65
  "@babel/core": "^7.29.7",
66
66
  "@babel/preset-react": "^7.29.7",
@@ -0,0 +1,114 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file CircularProgress.a11y.chromium.spec.ts
5
+ * @input Built CircularProgress stories and PROGRESSBAR_PATTERN in real Chromium
6
+ * @output Browser evidence for every progressbar expectation across the binding's states, reconciled against the exact known failures
7
+ * @position Chromium lane of the CircularProgress binding
8
+ */
9
+
10
+ import {
11
+ expect,
12
+ test,
13
+ type CDPSession,
14
+ type Locator,
15
+ type Page,
16
+ } from '@playwright/test';
17
+ import {
18
+ PROGRESSBAR_PATTERN,
19
+ blockingResults,
20
+ checkAccessibilitySpec,
21
+ formatFailures,
22
+ neverExercised,
23
+ summarize,
24
+ unmatchedKnownFailures,
25
+ type BindingResult,
26
+ } from '@astryxdesign/a11y-spec';
27
+ import {
28
+ createChromiumHarness,
29
+ holdMotionStill,
30
+ } from '@astryxdesign/a11y-spec/chromium';
31
+ import {
32
+ DEFAULT_STORYBOOK_DIR,
33
+ serveStorybook,
34
+ type StaticServer,
35
+ } from '@astryxdesign/a11y-spec/storybook';
36
+ import {CIRCULAR_PROGRESS_KNOWN_FAILURES} from './CircularProgress.a11y.known-failures';
37
+ import {
38
+ CIRCULAR_PROGRESS_BINDING_STATES,
39
+ type CircularProgressBindingState,
40
+ } from './CircularProgress.a11y.states';
41
+
42
+ let storybook: StaticServer;
43
+
44
+ test.beforeAll(async () => {
45
+ storybook = await serveStorybook(
46
+ process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
47
+ );
48
+ });
49
+
50
+ test.afterAll(async () => {
51
+ await storybook?.close();
52
+ });
53
+
54
+ function storyUrl(storyId: string): string {
55
+ return `${storybook.origin}/iframe.html?id=${storyId}&viewMode=story`;
56
+ }
57
+
58
+ async function mountState(
59
+ page: Page,
60
+ state: CircularProgressBindingState,
61
+ ): Promise<{subject: Locator; related: Record<string, Locator>}> {
62
+ await page.goto(storyUrl(state.storyId), {waitUntil: 'load'});
63
+ await holdMotionStill(page);
64
+ const subject = page
65
+ .locator('#storybook-root')
66
+ .getByRole('progressbar', {name: state.facts.name});
67
+ await subject.waitFor({state: 'attached'});
68
+ return {subject, related: {}};
69
+ }
70
+
71
+ async function runState(
72
+ page: Page,
73
+ cdp: CDPSession,
74
+ state: CircularProgressBindingState,
75
+ ): Promise<BindingResult> {
76
+ return checkAccessibilitySpec({
77
+ spec: PROGRESSBAR_PATTERN,
78
+ binding: 'CircularProgress',
79
+ state: state.id,
80
+ facts: state.facts,
81
+ knownFailures: CIRCULAR_PROGRESS_KNOWN_FAILURES,
82
+ mount: async () => {
83
+ const {subject, related} = await mountState(page, state);
84
+ return createChromiumHarness({page, cdp, subject, related});
85
+ },
86
+ });
87
+ }
88
+
89
+ test('every progressbar expectation is exercised by a CircularProgress state', async ({
90
+ page,
91
+ }) => {
92
+ const cdp = await page.context().newCDPSession(page);
93
+ const results: BindingResult[] = [];
94
+ for (const state of CIRCULAR_PROGRESS_BINDING_STATES) {
95
+ results.push(await runState(page, cdp, state));
96
+ }
97
+ expect(neverExercised(results)).toEqual([]);
98
+ expect(
99
+ unmatchedKnownFailures(CIRCULAR_PROGRESS_KNOWN_FAILURES, results),
100
+ ).toEqual([]);
101
+ });
102
+
103
+ for (const state of CIRCULAR_PROGRESS_BINDING_STATES) {
104
+ test(`CircularProgress [${state.id}] — ${state.summary}`, async ({page}) => {
105
+ const cdp = await page.context().newCDPSession(page);
106
+ const result = await runState(page, cdp, state);
107
+ const report = summarize(PROGRESSBAR_PATTERN, [result]);
108
+ // Required rows block (AST-020 FR9); advisory rows and exact known
109
+ // failures report.
110
+ expect(formatFailures(blockingResults([result]))).toBe('');
111
+ expect(report.unrunLayers).toEqual([]);
112
+ expect(report.counts.unexpectedPass).toBe(0);
113
+ });
114
+ }