@astryxdesign/lab 0.7.6-canary.5cd7a15 → 0.7.6-canary.6224efd
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/README.md +1 -1
- package/dist/CommentPin/CommentPin.d.ts.map +1 -1
- package/dist/CommentPin/CommentPin.js +37 -3
- package/dist/Comments/CommentComposer.d.ts.map +1 -1
- package/dist/Comments/CommentComposer.js +10 -3
- package/dist/Comments/CommentParts.d.ts +9 -2
- package/dist/Comments/CommentParts.d.ts.map +1 -1
- package/dist/Comments/CommentParts.js +32 -15
- package/dist/Comments/CommentRow.d.ts.map +1 -1
- package/dist/Comments/CommentRow.js +5 -2
- package/dist/Comments/Comments.d.ts.map +1 -1
- package/dist/Comments/Comments.js +10 -1
- package/dist/Comments/CommentsContext.d.ts +2 -0
- package/dist/Comments/CommentsContext.d.ts.map +1 -1
- package/dist/Comments/CommentsContext.js +7 -3
- package/dist/Comments/useStatusAnnouncement.d.ts +26 -0
- package/dist/Comments/useStatusAnnouncement.d.ts.map +1 -0
- package/dist/Comments/useStatusAnnouncement.js +48 -0
- package/package.json +4 -4
- package/src/CommentPin/CommentPin.doc.mjs +6 -4
- package/src/CommentPin/CommentPin.spec.md +14 -13
- package/src/CommentPin/CommentPin.test.tsx +177 -1
- package/src/CommentPin/CommentPin.tsx +44 -1
- package/src/Comments/CommentComposer.test.tsx +54 -4
- package/src/Comments/CommentComposer.tsx +10 -2
- package/src/Comments/CommentParts.tsx +50 -14
- package/src/Comments/CommentRow.tsx +5 -6
- package/src/Comments/Comments.doc.mjs +1 -1
- package/src/Comments/Comments.test.tsx +339 -13
- package/src/Comments/Comments.tsx +16 -1
- package/src/Comments/CommentsContext.tsx +13 -1
- package/src/Comments/useStatusAnnouncement.ts +57 -0
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ See the **[Component Lifecycle](https://github.com/meta/astryx/wiki/Component-Li
|
|
|
24
24
|
|
|
25
25
|
The [Accessibility Checklist](https://github.com/meta/astryx/wiki/Accessibility-Checklist) is a hard requirement for graduating to core — every item, verified in the promotion PR, alongside the hardening bar linked above.
|
|
26
26
|
|
|
27
|
-
Lab components get the same scan coverage as core (the `pr-a11y` axe audit and the
|
|
27
|
+
Lab components get the same scan coverage as core (the `pr-a11y` axe audit and the full release-check scan run against lab too), but a11y findings on a lab component do not block lab merges — they block **promotion**. Iterate freely in lab; clear the checklist to graduate.
|
|
28
28
|
|
|
29
29
|
## Usage
|
|
30
30
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentPin.d.ts","sourceRoot":"","sources":["../../src/CommentPin/CommentPin.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AA4BlD,qEAAqE;AACrE,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,sEAAsE;AACtE,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,iBAAiB,CAAC,EAC5B,UAAU,CACX;IACC,0CAA0C;IAC1C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,YAAY,EAAE,SAAS,CAAC,qBAAqB,EAAE,GAAG,qBAAqB,EAAE,CAAC,CAAC;IAC3E;;;;OAIG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"CommentPin.d.ts","sourceRoot":"","sources":["../../src/CommentPin/CommentPin.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AA4BlD,qEAAqE;AACrE,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,sEAAsE;AACtE,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,iBAAiB,CAAC,EAC5B,UAAU,CACX;IACC,0CAA0C;IAC1C,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,YAAY,EAAE,SAAS,CAAC,qBAAqB,EAAE,GAAG,qBAAqB,EAAE,CAAC,CAAC;IAC3E;;;;OAIG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA6OD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,YAAY,EACZ,IAAW,EACX,WAAe,EACf,WAAe,EACf,UAAkB,EAClB,QAAgB,EAChB,UAAkB,EAClB,UAAkB,EAClB,GAAG,EACH,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,eAAe,GAAG,KAAK,CAAC,SAAS,CAwLnC;yBAvMe,UAAU"}
|
|
@@ -88,6 +88,11 @@ const styles = {
|
|
|
88
88
|
kSiTet: "xyd83as x1tbh5bv",
|
|
89
89
|
$$css: true
|
|
90
90
|
},
|
|
91
|
+
disabledUnselected: {
|
|
92
|
+
kVAM5u: "x9r1u3d xmnqt3w",
|
|
93
|
+
kMwMTN: "xv1l7n4 x1bhcspo",
|
|
94
|
+
$$css: true
|
|
95
|
+
},
|
|
91
96
|
overflowCircle: {
|
|
92
97
|
kg3NbH: "xnjsko4",
|
|
93
98
|
$$css: true
|
|
@@ -269,8 +274,37 @@ export function CommentPin({
|
|
|
269
274
|
ref: mergedRef,
|
|
270
275
|
type: "button",
|
|
271
276
|
"aria-label": label,
|
|
272
|
-
"aria-pressed": isSelected
|
|
273
|
-
disabled
|
|
277
|
+
"aria-pressed": isSelected
|
|
278
|
+
// A disabled pin keeps its tab stop and is announced as disabled through
|
|
279
|
+
// aria-disabled instead of the native attribute (spec:AST-085 FR1). The
|
|
280
|
+
// activation guards below keep it inert (FR2); focus is kept when the
|
|
281
|
+
// pin becomes disabled while focused (FR4), and the shared focus outline
|
|
282
|
+
// still paints, dimmed with the rest of the pin (FR6).
|
|
283
|
+
,
|
|
284
|
+
"aria-disabled": isDisabled ? 'true' : undefined,
|
|
285
|
+
onClick: e => {
|
|
286
|
+
if (isDisabled) {
|
|
287
|
+
// A natively disabled element dispatched no click at all, so an
|
|
288
|
+
// aria-disabled pin must not reach the caller's handler, and the
|
|
289
|
+
// click must not leak to a clickable ancestor (annotation surface)
|
|
290
|
+
// the native attribute never let it reach either (FR2).
|
|
291
|
+
e.preventDefault();
|
|
292
|
+
e.stopPropagation();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
rest.onClick?.(e);
|
|
296
|
+
}
|
|
297
|
+
// The keyboard paths to activation are Enter and Space; other keys still
|
|
298
|
+
// reach caller handlers, as on an enabled pin (FR2).
|
|
299
|
+
,
|
|
300
|
+
onKeyDown: e => {
|
|
301
|
+
if (isDisabled && (e.key === 'Enter' || e.key === ' ')) {
|
|
302
|
+
e.preventDefault();
|
|
303
|
+
e.stopPropagation();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
rest.onKeyDown?.(e);
|
|
307
|
+
},
|
|
274
308
|
...mergeProps(themeProps('comment-pin', {
|
|
275
309
|
size: resolvedSize,
|
|
276
310
|
grouped: isGrouped ? 'grouped' : null,
|
|
@@ -279,7 +313,7 @@ export function CommentPin({
|
|
|
279
313
|
dimmed: isDimmed ? 'dimmed' : null,
|
|
280
314
|
resolved: isResolved ? 'resolved' : null,
|
|
281
315
|
disabled: isDisabled ? 'disabled' : null
|
|
282
|
-
}), focusOutlineProps.focusVisible(styles.root, sizeStyles[resolvedSize], interactionOverlayStyles.backgroundImage, (isGrouped || hasUnread) && styles.withCount, isDimmed && !isSelected && styles.dimmed, isSelected && styles.selected, isDisabled && styles.disabled, xstyle), className, style),
|
|
316
|
+
}), focusOutlineProps.focusVisible(styles.root, sizeStyles[resolvedSize], interactionOverlayStyles.backgroundImage, (isGrouped || hasUnread) && styles.withCount, isDimmed && !isSelected && styles.dimmed, isSelected && styles.selected, isDisabled && styles.disabled, isDisabled && !isSelected && styles.disabledUnselected, xstyle), className, style),
|
|
283
317
|
children: [/*#__PURE__*/_jsx("span", {
|
|
284
318
|
"aria-hidden": "true",
|
|
285
319
|
...{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentComposer.d.ts","sourceRoot":"","sources":["../../src/Comments/CommentComposer.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAOL,KAAK,SAAS,EACd,KAAK,GAAG,EACT,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAKL,KAAK,mBAAmB,EAEzB,MAAM,yBAAyB,CAAC;AAqBjC,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,0BAA0B,EAC1B,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAkGjB,0DAA0D;AAC1D,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,sBAAsB,CAAC;IAChC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,cAAc,EAAE,qBAAqB,CAAC;IACtC,cAAc,CAAC,EAAE,CACf,KAAK,EAAE,SAAS,IAAI,EAAE,KAEtB,SAAS,oBAAoB,EAAE,GAAG,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;IAC7E,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC,aAAa,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC9C,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,SAAS,CAAC;IAClE,OAAO,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IAC3B,SAAS,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,CAAC;CACnE;AAED,kDAAkD;AAClD,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,aAAa,EACb,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAY,EACZ,KAAK,EACL,qBAAqB,EACrB,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,WAAW,EACX,cAAc,EACd,SAAS,EACT,OAAO,EACP,QAAQ,EAAE,YAAY,EACtB,KAAK,GACN,EAAE,wBAAwB,+
|
|
1
|
+
{"version":3,"file":"CommentComposer.d.ts","sourceRoot":"","sources":["../../src/Comments/CommentComposer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -169,6 +169,13 @@ export function CommentComposerBase({
|
|
|
169
169
|
}
|
|
170
170
|
}, [focusKey]);
|
|
171
171
|
const [actionError, setActionError] = useState();
|
|
172
|
+
// Counts action failures so each one mounts its own Banner: a second failure
|
|
173
|
+
// with the same words is a new occasion and is announced again (AST-086 FR6).
|
|
174
|
+
const [actionErrorSerial, setActionErrorSerial] = useState(0);
|
|
175
|
+
const reportActionError = content => {
|
|
176
|
+
setActionError(content);
|
|
177
|
+
setActionErrorSerial(serial => serial + 1);
|
|
178
|
+
};
|
|
172
179
|
const [isActionPending, setIsActionPending] = useState(false);
|
|
173
180
|
const [isPickerOpen, setIsPickerOpen] = useState(false);
|
|
174
181
|
const updateActionPending = useCallback(nextPending => {
|
|
@@ -205,7 +212,7 @@ export function CommentComposerBase({
|
|
|
205
212
|
}
|
|
206
213
|
} catch {
|
|
207
214
|
if (isMountedRef.current) {
|
|
208
|
-
|
|
215
|
+
reportActionError({
|
|
209
216
|
title: t('@astryx.commentComposer.uploadFailed'),
|
|
210
217
|
actionLabel: t('@astryx.comments.retry'),
|
|
211
218
|
onAction: () => beginImageUpload(files)
|
|
@@ -223,7 +230,7 @@ export function CommentComposerBase({
|
|
|
223
230
|
}
|
|
224
231
|
const images = files.filter(file => file.type.startsWith('image/'));
|
|
225
232
|
if (images.length !== files.length) {
|
|
226
|
-
|
|
233
|
+
reportActionError({
|
|
227
234
|
title: t('@astryx.commentComposer.imagesOnly'),
|
|
228
235
|
actionLabel: t('@astryx.commentComposer.chooseImages'),
|
|
229
236
|
onAction: () => {
|
|
@@ -276,7 +283,7 @@ export function CommentComposerBase({
|
|
|
276
283
|
,
|
|
277
284
|
onBeforeAction: () => inputRef.current?.focus(),
|
|
278
285
|
"data-composer-error": "banner"
|
|
279
|
-
}), /*#__PURE__*/_jsx(ChatComposer, {
|
|
286
|
+
}, error != null ? 'error' : `action-${actionErrorSerial}`), /*#__PURE__*/_jsx(ChatComposer, {
|
|
280
287
|
value: value,
|
|
281
288
|
onChange: onChange,
|
|
282
289
|
onSubmit: submitInput,
|
|
@@ -132,10 +132,17 @@ export type ErrorBannerContent = {
|
|
|
132
132
|
actionLabel?: string;
|
|
133
133
|
onAction?: () => void;
|
|
134
134
|
isActionDisabled?: boolean;
|
|
135
|
+
/** Its own retry is running: say nothing until the outcome (FR6). */
|
|
136
|
+
isRetrying?: boolean;
|
|
137
|
+
/** The failure was already announced elsewhere; this Banner stays silent. */
|
|
138
|
+
isAnnounced?: boolean;
|
|
135
139
|
};
|
|
136
140
|
/**
|
|
137
|
-
* An inline failure as an error Banner.
|
|
138
|
-
*
|
|
141
|
+
* An inline failure as an error Banner. The Banner carries no live role: it
|
|
142
|
+
* mounts already holding its text, which may not be announced. The failure is
|
|
143
|
+
* announced once, assertively, through the shared channel instead, unless the
|
|
144
|
+
* Banner sits inside a live container such as the chat log, which delivers it
|
|
145
|
+
* (spec:AST-086 FR2, FR4, FR5).
|
|
139
146
|
*/
|
|
140
147
|
export declare function ErrorBanner({ actionRef, content, onBeforeAction, ...dataProps }: {
|
|
141
148
|
actionRef?: Ref<HTMLButtonElement>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentParts.d.ts","sourceRoot":"","sources":["../../src/Comments/CommentParts.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"CommentParts.d.ts","sourceRoot":"","sources":["../../src/Comments/CommentParts.tsx"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* reply rail, activity event row, edit history dialog, and loading skeletons
|
|
13
13
|
* @position Internal building blocks of the Comments family
|
|
14
14
|
*/
|
|
15
|
-
import { useState } from 'react';
|
|
15
|
+
import { useRef, useState } from 'react';
|
|
16
16
|
import * as stylex from '@stylexjs/stylex';
|
|
17
17
|
import { Avatar } from '@astryxdesign/core/Avatar';
|
|
18
18
|
import { Badge } from '@astryxdesign/core/Badge';
|
|
@@ -38,6 +38,7 @@ import { useTranslator } from '@astryxdesign/core/i18n';
|
|
|
38
38
|
import '@astryxdesign/core/theme/tokens.stylex';
|
|
39
39
|
import { borderVars, colorVars, durationVars, easeVars, radiusVars, spacingVars } from '@astryxdesign/core/theme/tokens.stylex';
|
|
40
40
|
import { CommentIcon } from "./icons.js";
|
|
41
|
+
import { useStatusAnnouncement } from "./useStatusAnnouncement.js";
|
|
41
42
|
import { eventAnchorId } from "./utils.js";
|
|
42
43
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
43
44
|
/** The 28px avatar column every comment row, reply, and event aligns to. */
|
|
@@ -243,8 +244,11 @@ const highlightRules = {
|
|
|
243
244
|
/** A failed action as an error Banner with its one recovery action. */
|
|
244
245
|
|
|
245
246
|
/**
|
|
246
|
-
* An inline failure as an error Banner.
|
|
247
|
-
*
|
|
247
|
+
* An inline failure as an error Banner. The Banner carries no live role: it
|
|
248
|
+
* mounts already holding its text, which may not be announced. The failure is
|
|
249
|
+
* announced once, assertively, through the shared channel instead, unless the
|
|
250
|
+
* Banner sits inside a live container such as the chat log, which delivers it
|
|
251
|
+
* (spec:AST-086 FR2, FR4, FR5).
|
|
248
252
|
*/
|
|
249
253
|
export function ErrorBanner({
|
|
250
254
|
actionRef,
|
|
@@ -259,9 +263,15 @@ export function ErrorBanner({
|
|
|
259
263
|
onAction,
|
|
260
264
|
title
|
|
261
265
|
} = content;
|
|
266
|
+
const t = useTranslator();
|
|
267
|
+
const bannerRef = useRef(null);
|
|
268
|
+
useStatusAnnouncement(bannerRef, content.isRetrying === true || content.isAnnounced === true ? null : description != null ? t('@astryx.comments.errorAnnouncement', {
|
|
269
|
+
title,
|
|
270
|
+
description
|
|
271
|
+
}) : title, 'assertive');
|
|
262
272
|
return /*#__PURE__*/_jsx(Banner, {
|
|
273
|
+
ref: bannerRef,
|
|
263
274
|
status: "error",
|
|
264
|
-
role: "alert",
|
|
265
275
|
container: "card",
|
|
266
276
|
title: title,
|
|
267
277
|
description: description,
|
|
@@ -405,6 +415,11 @@ export function CommentAnchorQuote({
|
|
|
405
415
|
export function CommentProgressStatus({
|
|
406
416
|
progress
|
|
407
417
|
}) {
|
|
418
|
+
const labelRef = useRef(null);
|
|
419
|
+
// The label carries no live role: it mounts already holding its text. It is
|
|
420
|
+
// announced politely through the shared channel, unless the chat log around
|
|
421
|
+
// it delivers it (spec:AST-086 FR2, FR4).
|
|
422
|
+
useStatusAnnouncement(labelRef, progress.label, 'polite');
|
|
408
423
|
return /*#__PURE__*/_jsxs(HStack, {
|
|
409
424
|
gap: 1,
|
|
410
425
|
align: "center",
|
|
@@ -412,9 +427,9 @@ export function CommentProgressStatus({
|
|
|
412
427
|
width: "100%",
|
|
413
428
|
"data-comment-progress": "shimmer",
|
|
414
429
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
430
|
+
ref: labelRef,
|
|
415
431
|
type: "body",
|
|
416
432
|
color: "secondary",
|
|
417
|
-
role: "status",
|
|
418
433
|
xstyle: partStyles.progressLabel,
|
|
419
434
|
children: progress.label
|
|
420
435
|
}), progress.link != null && /*#__PURE__*/_jsx(Link, {
|
|
@@ -629,17 +644,19 @@ export function HistoryDialog({
|
|
|
629
644
|
onOpenChange: open => !open && onClose(),
|
|
630
645
|
hasDivider: true
|
|
631
646
|
}),
|
|
632
|
-
content: /*#__PURE__*/
|
|
633
|
-
children:
|
|
647
|
+
content: /*#__PURE__*/_jsxs(LayoutContent, {
|
|
648
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
649
|
+
role: "alert",
|
|
650
|
+
children: !history.isLoading && history.error != null && /*#__PURE__*/_jsx(Banner, {
|
|
651
|
+
status: "error",
|
|
652
|
+
title: t('@astryx.comments.historyFailed'),
|
|
653
|
+
description: history.error,
|
|
654
|
+
container: "section"
|
|
655
|
+
})
|
|
656
|
+
}), history.isLoading ? /*#__PURE__*/_jsx(Spinner, {
|
|
634
657
|
size: "lg",
|
|
635
658
|
label: t('@astryx.comments.historyLoading')
|
|
636
|
-
}) : history.error != null ? /*#__PURE__*/_jsx(
|
|
637
|
-
status: "error",
|
|
638
|
-
role: "alert",
|
|
639
|
-
title: t('@astryx.comments.historyFailed'),
|
|
640
|
-
description: history.error,
|
|
641
|
-
container: "section"
|
|
642
|
-
}) : history.versions.length === 0 ? /*#__PURE__*/_jsx(EmptyState, {
|
|
659
|
+
}) : history.error != null ? null : history.versions.length === 0 ? /*#__PURE__*/_jsx(EmptyState, {
|
|
643
660
|
title: t('@astryx.comments.historyEmptyTitle'),
|
|
644
661
|
description: t('@astryx.comments.historyEmptyDescription'),
|
|
645
662
|
isCompact: true
|
|
@@ -671,7 +688,7 @@ export function HistoryDialog({
|
|
|
671
688
|
}), renderVersionBody(version)]
|
|
672
689
|
})
|
|
673
690
|
}, version.id))
|
|
674
|
-
})
|
|
691
|
+
})]
|
|
675
692
|
})
|
|
676
693
|
})
|
|
677
694
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentRow.d.ts","sourceRoot":"","sources":["../../src/Comments/CommentRow.tsx"],"names":[],"mappings":"AAuEA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,SAAS,CAAC;AAOjB,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,CAAC;AAQ7C,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EAAsB,CAAC;AA6M3E;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,KAAK,EACL,QAA0B,EAC1B,oBAA4B,EAC5B,sBAA8B,EAC9B,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACpC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,kEAAkE;IAClE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;CAClB,+
|
|
1
|
+
{"version":3,"file":"CommentRow.d.ts","sourceRoot":"","sources":["../../src/Comments/CommentRow.tsx"],"names":[],"mappings":"AAuEA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,SAAS,CAAC;AAOjB,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,CAAC;AAQ7C,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EAAsB,CAAC;AA6M3E;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,KAAK,EACL,QAA0B,EAC1B,oBAA4B,EAC5B,sBAA8B,EAC9B,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACpC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,kEAAkE;IAClE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;CAClB,+BAikBA"}
|
|
@@ -371,7 +371,9 @@ export function CommentRow({
|
|
|
371
371
|
description: actionError,
|
|
372
372
|
actionLabel: can.retry ? t('@astryx.comments.retry') : undefined,
|
|
373
373
|
onAction: can.retry ? () => scope.retry(comment) : undefined,
|
|
374
|
-
isActionDisabled: rowActionsBusy
|
|
374
|
+
isActionDisabled: rowActionsBusy,
|
|
375
|
+
isRetrying: isBusy,
|
|
376
|
+
isAnnounced: actionFailure?.isAnnounced
|
|
375
377
|
}
|
|
376
378
|
// The Banner unmounts once the retry starts; keep focus on the comment.
|
|
377
379
|
,
|
|
@@ -397,7 +399,8 @@ export function CommentRow({
|
|
|
397
399
|
content: {
|
|
398
400
|
title: actionFailure.message,
|
|
399
401
|
actionLabel: actionFailure.retry != null ? t('@astryx.comments.retry') : undefined,
|
|
400
|
-
onAction: actionFailure.retry
|
|
402
|
+
onAction: actionFailure.retry,
|
|
403
|
+
isAnnounced: actionFailure.isAnnounced
|
|
401
404
|
},
|
|
402
405
|
onBeforeAction: () => articleRef.current?.focus(),
|
|
403
406
|
"data-comment-action-error": "banner"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Comments.d.ts","sourceRoot":"","sources":["../../src/Comments/Comments.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,GAAG,EACT,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"Comments.d.ts","sourceRoot":"","sources":["../../src/Comments/Comments.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,GAAG,EACT,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAuBlD,OAAO,KAAK,EACV,aAAa,EAEb,iBAAiB,EACjB,wBAAwB,EACxB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,aACf,SACE,wBAAwB,EACxB,IAAI,CACF,SAAS,CAAC,WAAW,CAAC,EACtB,MAAM,wBAAwB,GAAG,UAAU,GAAG,UAAU,CACzD;IACH,yCAAyC;IACzC,GAAG,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IACvB;;;;OAIG;IACH,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC,kEAAkE;IAClE,MAAM,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACzC;sBACkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC7C,0BAA0B;IAC1B,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACjC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kFAAkF;IAClF,sBAAsB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3D;;;;OAIG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,gEAAgE;IAChE,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;IACrC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAC;CAC1D;AAsCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,MAAkB,EAClB,KAAK,EAAE,SAAS,EAChB,gBAA2B,EAC3B,OAAoB,EACpB,MAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,uBAAuB,EACvB,gBAAgB,EAChB,SAAiB,EACjB,SAAS,EACT,WAAW,EACX,gBAAwB,EACxB,cAAsB,EACtB,WAAW,EACX,aAAa,EACb,UAAU,EACV,WAAW,EACX,WAAW,EACX,IAAoB,EACpB,SAAqB,EACrB,mBAAmB,EACnB,cAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,cAAqB,EACrB,YAAY,EACZ,qBAAqB,EACrB,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,OAAO,EACP,cAAc,EACd,eAAe,EACf,cAAc,EACd,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,EACd,GAAG,EACH,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,aAAa,+BA+Zf;yBArde,QAAQ"}
|
|
@@ -31,6 +31,7 @@ import { useTranslator } from '@astryxdesign/core/i18n';
|
|
|
31
31
|
import { CommentComposerBase } from "./CommentComposer.js";
|
|
32
32
|
import { ActivityEventRow, CommentComposerLoadingSkeleton, CommentThreadLoadingSkeleton, ErrorBanner } from "./CommentParts.js";
|
|
33
33
|
import { CommentRow, THREAD_DIVIDERS } from "./CommentRow.js";
|
|
34
|
+
import { useStatusAnnouncement } from "./useStatusAnnouncement.js";
|
|
34
35
|
import { CommentsContextProvider, useCommentsController } from "./CommentsContext.js";
|
|
35
36
|
import { timelineTime } from "./utils.js";
|
|
36
37
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
@@ -148,6 +149,14 @@ export function Comments({
|
|
|
148
149
|
const loadingOlderRef = useRef(false);
|
|
149
150
|
const loadOlderButtonRef = useRef(null);
|
|
150
151
|
const olderRetryRef = useRef(null);
|
|
152
|
+
// The load-failure Banner carries no live role: it mounts already holding
|
|
153
|
+
// its text. It is announced once, assertively, through the shared channel
|
|
154
|
+
// (spec:AST-086 FR2, FR5).
|
|
155
|
+
const loadErrorRef = useRef(null);
|
|
156
|
+
useStatusAnnouncement(loadErrorRef, loadError != null ? t('@astryx.comments.errorAnnouncement', {
|
|
157
|
+
title: t('@astryx.comments.loadFailed'),
|
|
158
|
+
description: loadError
|
|
159
|
+
}) : null, 'assertive');
|
|
151
160
|
const rootRef = useRef(null);
|
|
152
161
|
const setRootRef = useCallback(element => {
|
|
153
162
|
rootRef.current = element;
|
|
@@ -380,8 +389,8 @@ export function Comments({
|
|
|
380
389
|
container
|
|
381
390
|
}), stylex.props(!isEmbedded && styles.root, xstyle), className, style),
|
|
382
391
|
children: [loadError != null && /*#__PURE__*/_jsx(Banner, {
|
|
392
|
+
ref: loadErrorRef,
|
|
383
393
|
status: "error",
|
|
384
|
-
role: "alert",
|
|
385
394
|
title: t('@astryx.comments.loadFailed'),
|
|
386
395
|
description: loadError,
|
|
387
396
|
container: "section",
|
|
@@ -32,6 +32,8 @@ export type EditTarget = {
|
|
|
32
32
|
export type ActionFailure = {
|
|
33
33
|
message: string;
|
|
34
34
|
retry?: () => void;
|
|
35
|
+
/** Already spoken by whoever raised it, so the row's Banner stays silent. */
|
|
36
|
+
isAnnounced?: boolean;
|
|
35
37
|
};
|
|
36
38
|
export interface CommentsContextValue {
|
|
37
39
|
currentUser?: CommentUser;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentsContext.d.ts","sourceRoot":"","sources":["../../src/Comments/CommentsContext.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,qCAAqC,CAAC;AAO7E,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAEL,KAAK,mBAAmB,EAExB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,WAAW,EAChB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,WAAW,EACjB,MAAM,SAAS,CAAC;AAEjB,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,CAAC;IACpB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,CAAC;AAc9D,uDAAuD;AACvD,MAAM,MAAM,aAAa,GAAG;
|
|
1
|
+
{"version":3,"file":"CommentsContext.d.ts","sourceRoot":"","sources":["../../src/Comments/CommentsContext.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,qCAAqC,CAAC;AAO7E,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAEL,KAAK,mBAAmB,EAExB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,WAAW,EAChB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,WAAW,EACjB,MAAM,SAAS,CAAC;AAEjB,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,CAAC;IACpB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,CAAC;AAc9D,uDAAuD;AACvD,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AA4CF,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,SAAS,GAAG,MAAM,CAAC;IAC9B,qEAAqE;IACrE,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,eAAe,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAClD,cAAc,EAAE,qBAAqB,CAAC;IACtC,aAAa,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC9C,gBAAgB,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,eAAe,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD,qBAAqB,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,YAAY,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAC;IACxD,UAAU,CAAC,EAAE,wBAAwB,CAAC,YAAY,CAAC,CAAC;IACpD,uBAAuB,CAAC,EAAE,wBAAwB,CAAC,yBAAyB,CAAC,CAAC;IAC9E,cAAc,CAAC,EAAE,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;IAC5D,sCAAsC;IACtC,GAAG,EAAE;QACH,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,OAAO,CAAC;QAClB,MAAM,EAAE,OAAO,CAAC;QAChB,KAAK,EAAE,OAAO,CAAC;QACf,KAAK,EAAE,OAAO,CAAC;QACf,YAAY,EAAE,OAAO,CAAC;QACtB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IACtD,YAAY,EAAE,OAAO,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;IACjC,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC,KACxB,IAAI,CAAC;IACV,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,WAAW,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,SAAS,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;IACzC,UAAU,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,QAAQ,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,UAAU,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,KAAK,EAAE,CACL,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,qBAAqB,KAC3B,IAAI,CAAC;IACV,OAAO,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;IACvC,MAAM,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,KAAK,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7C,WAAW,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;IAC3C,2BAA2B,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3D;AAID,eAAO,MAAM,uBAAuB,uDAA2B,CAAC;AAEhE,8DAA8D;AAC9D,wBAAgB,kBAAkB,IAAI,oBAAoB,CAQzD;AAED,MAAM,WAAW,yBAA0B,SAAQ,wBAAwB;IACzE,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,sBAAsB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3D,0EAA0E;IAC1E,KAAK,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACpC,4EAA4E;IAC5E,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3D;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,WAAW,EACX,IAAoB,EACpB,SAAqB,EACrB,mBAAmB,EACnB,cAAyB,EACzB,eAA2C,EAC3C,aAAiC,EACjC,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,cAAqB,EACrB,YAAoB,EACpB,qBAAqB,EACrB,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,OAAO,EACP,cAAc,EACd,eAAe,EACf,cAAc,EACd,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,EACd,gBAAgB,EAAE,oBAAoB,EACtC,sBAAsB,EACtB,KAAK,EACL,OAAO,GACR,EAAE,yBAAyB;;;sBAiIf,MAAM,aACJ,MAAM,KAChB,OAAO,CAAC,YAAY,CAAC;;EAyXzB"}
|
|
@@ -183,7 +183,7 @@ export function useCommentsController({
|
|
|
183
183
|
delete next[commentId];
|
|
184
184
|
return next;
|
|
185
185
|
});
|
|
186
|
-
const runCommentAction = async (commentId, action, failureMessage, retry) => {
|
|
186
|
+
const runCommentAction = async (commentId, action, failureMessage, retry, isAnnounced) => {
|
|
187
187
|
setPendingActionIds(previous => new Set(previous).add(commentId));
|
|
188
188
|
clearActionError(commentId);
|
|
189
189
|
try {
|
|
@@ -194,7 +194,8 @@ export function useCommentsController({
|
|
|
194
194
|
...previous,
|
|
195
195
|
[commentId]: {
|
|
196
196
|
message: failureMessage,
|
|
197
|
-
retry: retry ?? (() => void runCommentAction(commentId, action, failureMessage))
|
|
197
|
+
retry: retry ?? (() => void runCommentAction(commentId, action, failureMessage)),
|
|
198
|
+
isAnnounced
|
|
198
199
|
}
|
|
199
200
|
}));
|
|
200
201
|
return false;
|
|
@@ -288,7 +289,10 @@ export function useCommentsController({
|
|
|
288
289
|
setDeleteError(undefined);
|
|
289
290
|
const succeeded = await runCommentAction(target.id, () => onDelete(target.id), t('@astryx.comments.deleteFailed'),
|
|
290
291
|
// Retrying a delete asks for confirmation again.
|
|
291
|
-
() => setDeleteTarget(target)
|
|
292
|
+
() => setDeleteTarget(target),
|
|
293
|
+
// The dialog announces a failed delete (AST-086 FR1, FR6), so the stored
|
|
294
|
+
// failure the row shows is marked before it first renders.
|
|
295
|
+
true);
|
|
292
296
|
if (succeeded) {
|
|
293
297
|
commitAndFocus(() => setDeleteTarget(null), neighborId, neighborId == null ? 'thread' : 'article', rootRef);
|
|
294
298
|
} else {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useStatusAnnouncement.ts
|
|
3
|
+
* @input Uses React, useAnnounce from core hooks
|
|
4
|
+
* @output Exports useStatusAnnouncement hook
|
|
5
|
+
* @position Internal to Comments; announces a transient status element
|
|
6
|
+
* (error banner, progress label) once instead of mounting it as a live region
|
|
7
|
+
*
|
|
8
|
+
* A live region that mounts already holding its text may not be announced, so
|
|
9
|
+
* the visible element carries no live role (spec:AST-086 FR2). This hook says
|
|
10
|
+
* the words once through the shared channel instead, unless a live container
|
|
11
|
+
* such as a chat log already delivers content added inside it (FR4).
|
|
12
|
+
*/
|
|
13
|
+
import { type RefObject } from 'react';
|
|
14
|
+
import type { AnnouncePoliteness } from '@astryxdesign/core/hooks';
|
|
15
|
+
/**
|
|
16
|
+
* Announce `message` once per occasion: when the element first shows it, and
|
|
17
|
+
* again whenever the message changes. A rerender or a StrictMode effect replay
|
|
18
|
+
* does not repeat it, and a message that goes away and comes back is a new
|
|
19
|
+
* occasion (spec:AST-086 FR6).
|
|
20
|
+
*
|
|
21
|
+
* @param ref The visible element, used to find an enclosing live container.
|
|
22
|
+
* @param message The words to say, or `null` for nothing.
|
|
23
|
+
* @param politeness Errors are `'assertive'`, progress is `'polite'` (FR5).
|
|
24
|
+
*/
|
|
25
|
+
export declare function useStatusAnnouncement(ref: RefObject<HTMLElement | null>, message: string | null, politeness: AnnouncePoliteness): void;
|
|
26
|
+
//# sourceMappingURL=useStatusAnnouncement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStatusAnnouncement.d.ts","sourceRoot":"","sources":["../../src/Comments/useStatusAnnouncement.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAExD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,0BAA0B,CAAC;AAKjE;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAClC,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,UAAU,EAAE,kBAAkB,GAC7B,IAAI,CAkBN"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useStatusAnnouncement.ts
|
|
7
|
+
* @input Uses React, useAnnounce from core hooks
|
|
8
|
+
* @output Exports useStatusAnnouncement hook
|
|
9
|
+
* @position Internal to Comments; announces a transient status element
|
|
10
|
+
* (error banner, progress label) once instead of mounting it as a live region
|
|
11
|
+
*
|
|
12
|
+
* A live region that mounts already holding its text may not be announced, so
|
|
13
|
+
* the visible element carries no live role (spec:AST-086 FR2). This hook says
|
|
14
|
+
* the words once through the shared channel instead, unless a live container
|
|
15
|
+
* such as a chat log already delivers content added inside it (FR4).
|
|
16
|
+
*/
|
|
17
|
+
import { useEffect, useRef } from 'react';
|
|
18
|
+
import { useAnnounce } from '@astryxdesign/core/hooks';
|
|
19
|
+
const LIVE_CONTAINER_SELECTOR = '[role="log"], [role="status"], [role="alert"], [aria-live="polite"], [aria-live="assertive"]';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Announce `message` once per occasion: when the element first shows it, and
|
|
23
|
+
* again whenever the message changes. A rerender or a StrictMode effect replay
|
|
24
|
+
* does not repeat it, and a message that goes away and comes back is a new
|
|
25
|
+
* occasion (spec:AST-086 FR6).
|
|
26
|
+
*
|
|
27
|
+
* @param ref The visible element, used to find an enclosing live container.
|
|
28
|
+
* @param message The words to say, or `null` for nothing.
|
|
29
|
+
* @param politeness Errors are `'assertive'`, progress is `'polite'` (FR5).
|
|
30
|
+
*/
|
|
31
|
+
export function useStatusAnnouncement(ref, message, politeness) {
|
|
32
|
+
const announce = useAnnounce();
|
|
33
|
+
const announcedRef = useRef(null);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (message == null || message === '') {
|
|
36
|
+
announcedRef.current = null;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (announcedRef.current === message) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
announcedRef.current = message;
|
|
43
|
+
const isInsideLiveContainer = ref.current?.parentElement?.closest(LIVE_CONTAINER_SELECTOR) != null;
|
|
44
|
+
if (!isInsideLiveContainer) {
|
|
45
|
+
announce(message, politeness);
|
|
46
|
+
}
|
|
47
|
+
}, [announce, message, politeness, ref]);
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/lab",
|
|
3
|
-
"version": "0.7.6-canary.
|
|
3
|
+
"version": "0.7.6-canary.6224efd",
|
|
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.6-canary.
|
|
52
|
+
"@astryxdesign/core": "0.7.6-canary.6224efd",
|
|
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.6-canary.
|
|
63
|
-
"@astryxdesign/cli": "0.7.6-canary.
|
|
62
|
+
"@astryxdesign/a11y-spec": "0.7.6-canary.6224efd",
|
|
63
|
+
"@astryxdesign/cli": "0.7.6-canary.6224efd",
|
|
64
64
|
"@babel/cli": "^7.29.7",
|
|
65
65
|
"@babel/core": "^7.29.7",
|
|
66
66
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -122,7 +122,7 @@ export const docs = {
|
|
|
122
122
|
name: 'isDisabled',
|
|
123
123
|
type: 'boolean',
|
|
124
124
|
description:
|
|
125
|
-
'Disables activation
|
|
125
|
+
'Disables activation: the pin exposes aria-disabled, keeps its tab stop, and blocks every activation path. Useful for a pending anchor before its thread exists.',
|
|
126
126
|
default: 'false',
|
|
127
127
|
},
|
|
128
128
|
{
|
|
@@ -164,7 +164,7 @@ export const docs = {
|
|
|
164
164
|
category: 'Keyboard',
|
|
165
165
|
criterion: '2.1.1 Keyboard',
|
|
166
166
|
description:
|
|
167
|
-
'CommentPin is one native button. Tab reaches an enabled pin; Enter and Space activate it. A disabled pin
|
|
167
|
+
'CommentPin is one native button. Tab reaches an enabled pin; Enter and Space activate it. A disabled pin stays in the tab order and is announced as disabled through aria-disabled, and every activation path is blocked.',
|
|
168
168
|
},
|
|
169
169
|
{
|
|
170
170
|
name: 'Selected state and name',
|
|
@@ -323,7 +323,8 @@ export const docsZh = {
|
|
|
323
323
|
isDimmed:
|
|
324
324
|
'其他标记被选中时降低此标记的视觉突出度,但不禁用操作;未选中任何标记时不应降低。',
|
|
325
325
|
isResolved: '所代表的线程是否都已解决;已解决的标记除非被选中否则不渲染。',
|
|
326
|
-
isDisabled:
|
|
326
|
+
isDisabled:
|
|
327
|
+
'通过 aria-disabled 禁用激活:保留 Tab 焦点,拦截所有激活路径。',
|
|
327
328
|
onClick: '由产品拥有的激活处理函数。',
|
|
328
329
|
xstyle: '根按钮的 StyleX 布局样式;由宿主决定内容上方或边距中的位置。',
|
|
329
330
|
},
|
|
@@ -369,7 +370,8 @@ export const docsDense = {
|
|
|
369
370
|
isDimmed:
|
|
370
371
|
'controlled de-emphasis for non-selected siblings only while another pin is selected; remains interactive',
|
|
371
372
|
isResolved: 'resolved pins render nothing unless selected',
|
|
372
|
-
isDisabled:
|
|
373
|
+
isDisabled:
|
|
374
|
+
'disabled marker: aria-disabled, keeps its tab stop, blocks every activation path',
|
|
373
375
|
onClick: 'host activation handler',
|
|
374
376
|
xstyle: 'root layout/positioning styles; marker owns no coordinates',
|
|
375
377
|
},
|
|
@@ -156,18 +156,18 @@ intentional decision. A `current` contract contains no unresolved rows.
|
|
|
156
156
|
|
|
157
157
|
### Representative states
|
|
158
158
|
|
|
159
|
-
| State | Required invariant
|
|
160
|
-
| ------------------------ |
|
|
161
|
-
| Single | first participant Avatar; no visible count; an accent dot after it when unread
|
|
162
|
-
| Grouped, one identity | one Avatar plus total thread count
|
|
163
|
-
| Grouped, two or three | every distinct participant Avatar plus total thread count
|
|
164
|
-
| Grouped, more than three | first two distinct Avatars, an ellipsis overflow circle, and total thread count
|
|
165
|
-
| Unread | accent dot after the total, or after the participant on a single marker
|
|
166
|
-
| Sizes | 28, 32, or 36px tall with 22, 24, or 28px circles and the same inset on every side
|
|
167
|
-
| Selected | active accent treatment preserves Avatar and count legibility and wins over dimming
|
|
168
|
-
| Dimmed | lower opacity identifies a non-selected sibling without disabling activation
|
|
169
|
-
| Resolved | renders nothing unless selected; selected, it takes the selected treatment
|
|
170
|
-
| Disabled |
|
|
159
|
+
| State | Required invariant | Allowed variation |
|
|
160
|
+
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
161
|
+
| Single | first participant Avatar; no visible count; an accent dot after it when unread | identity, selected/dimmed/resolved/disabled state, placement |
|
|
162
|
+
| Grouped, one identity | one Avatar plus total thread count | count and states |
|
|
163
|
+
| Grouped, two or three | every distinct participant Avatar plus total thread count | ordered identities, count, and states |
|
|
164
|
+
| Grouped, more than three | first two distinct Avatars, an ellipsis overflow circle, and total thread count | ordered identities and states |
|
|
165
|
+
| Unread | accent dot after the total, or after the participant on a single marker | other states |
|
|
166
|
+
| Sizes | 28, 32, or 36px tall with 22, 24, or 28px circles and the same inset on every side | every other state |
|
|
167
|
+
| Selected | active accent treatment preserves Avatar and count legibility and wins over dimming | theme paint |
|
|
168
|
+
| Dimmed | lower opacity identifies a non-selected sibling without disabling activation | disabled may also apply |
|
|
169
|
+
| Resolved | renders nothing unless selected; selected, it takes the selected treatment | disabled may also apply |
|
|
170
|
+
| Disabled | an aria-disabled button does not activate but keeps its tab stop, so keyboard and screen-reader users reach it and hear it is unavailable; an unselected disabled pin paints GrayText color and border under forced colors | selected representation remains visible, keeping its Highlight treatment |
|
|
171
171
|
|
|
172
172
|
### Transformation and precedence order
|
|
173
173
|
|
|
@@ -203,7 +203,8 @@ durable constraints and their verification target.
|
|
|
203
203
|
## Accessibility contract
|
|
204
204
|
|
|
205
205
|
- **AR1 — Action.** The root is one native `button type="button"` named by the
|
|
206
|
-
required `label`; `isDisabled`
|
|
206
|
+
required `label`; `isDisabled` exposes `aria-disabled="true"` and blocks every
|
|
207
|
+
activation path while the pin keeps its tab stop (`spec:AST-085`).
|
|
207
208
|
- **AR2 — Selection.** `isSelected` is exposed through `aria-pressed`, one solid
|
|
208
209
|
2px accent outline outside the pin, and a forced-colors Highlight treatment;
|
|
209
210
|
the outline hugs the pin while the shared focus-visible outline sits farther
|