Skip to content

Commit 8781a84

Browse files
committed
fix(a11y): announce workspace condition sub-steps to screen readers
Follow-up to the workspace status live-region fix. Condition sub-steps (StorageReady, RoutingReady, DeploymentReady, etc.) were not being announced because ProgressStepTitle received mixed children [string, ReactElement] and the previous typeof check only handled plain-string children. Changes: ProgressStepTitle: - Add extractText() helper that recursively extracts plain text from any ReactNode (string, array, or React element with nested children). - Replace typeof children === 'string' check with extractText(children) so the live region is populated regardless of whether children are plain strings or mixed string + JSX nodes. - Add optional parentStepName prop. When set, the live-region announcement reads "Step: {parentStepName} / {stepText}" so screen readers hear the full context for sub-steps (e.g. "Step: Waiting for workspace to start / StorageReady"). StartingStepWorkspaceConditions: - Pass parentStepName="Waiting for workspace to start" to ProgressStepTitle so each condition sub-step is announced with the parent step context. Assisted-by: Claude Sonnet 4.6 Signed-off-by: Oleksii Orel <oorel@redhat.com>
1 parent e80f923 commit 8781a84

2 files changed

Lines changed: 28 additions & 2 deletions

File tree

  • packages/dashboard-frontend/src/components/WorkspaceProgress

packages/dashboard-frontend/src/components/WorkspaceProgress/StartingSteps/WorkspaceConditions/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ class StartingStepWorkspaceConditions extends ProgressStep<Props, State> {
176176
hasChildren={hasChildren}
177177
isError={isError}
178178
isWarning={isWarning}
179+
parentStepName="Waiting for workspace to start"
179180
>
180181
{this.name}
181182
<PureSubCondition distance={distance} title={subConditionTitle} />

packages/dashboard-frontend/src/components/WorkspaceProgress/StepTitle/index.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,29 @@ export type Props = PropsWithChildren<{
2121
hasChildren?: boolean;
2222
isError?: boolean;
2323
isWarning?: boolean;
24+
/** When set, the live-region announcement reads "Step: {parentStepName} / {step text}".
25+
* Use for condition sub-steps so screen readers hear the full context. */
26+
parentStepName?: string;
2427
}>;
2528

29+
/** Recursively extracts plain-text content from any ReactNode. */
30+
function extractText(node: React.ReactNode): string {
31+
if (typeof node === 'string' || typeof node === 'number') {
32+
return String(node);
33+
}
34+
if (Array.isArray(node)) {
35+
return node.map(extractText).join('');
36+
}
37+
if (React.isValidElement(node) && node.props.children) {
38+
return extractText(node.props.children as React.ReactNode);
39+
}
40+
return '';
41+
}
42+
2643
export class ProgressStepTitle extends React.Component<Props> {
2744
render(): React.ReactElement {
28-
const { children, className, hasChildren, distance, isError, isWarning } = this.props;
45+
const { children, className, hasChildren, distance, isError, isWarning, parentStepName } =
46+
this.props;
2947

3048
let readiness = styles.ready;
3149
if (distance === 0) {
@@ -53,10 +71,17 @@ export class ProgressStepTitle extends React.Component<Props> {
5371
The region is always present so screen readers register it on page load;
5472
content is only set when the step is active (distance === 0) so the
5573
announcement fires exactly once per step transition.
74+
For sub-steps (parentStepName set) the format is
75+
"Step: {parent} / {substep}" so screen readers hear the full context.
5676
The "Step: " prefix ensures the live region text differs from the visible
5777
step title, preventing ambiguous matches in DOM queries. */}
5878
<span role="status" aria-live="polite" aria-atomic="true" className="pf-v6-screen-reader">
59-
{distance === 0 && typeof children === 'string' ? `Step: ${children}` : ''}
79+
{(() => {
80+
if (distance !== 0) return '';
81+
const stepText = extractText(children);
82+
if (!stepText) return '';
83+
return parentStepName ? `Step: ${parentStepName} / ${stepText}` : `Step: ${stepText}`;
84+
})()}
6085
</span>
6186
</>
6287
);

0 commit comments

Comments
 (0)