Skip to content

Commit bfcf12c

Browse files
committed
feat(a11y): add pause/resume toggle to workspace events stream
The Events tab streams Kubernetes events continuously with no way to stop them, violating WCAG 2.2.2 (Pause, Stop, Hide). Users could only implicitly slow the stream by manually scrolling. Adds a PauseIcon / PlayIcon toggle button next to the "Streaming events..." label. When paused the event list freezes and a pending count badge shows how many new events are queued. Resuming renders all accumulated events at once. The aria-live attribute on the log container switches to "off" while paused so screen readers stop announcing updates. Fixes: https://redhat.atlassian.net/browse/CRW-10283 Assisted-by: Claude Sonnet 4.6 Signed-off-by: Oleksii Orel <oorel@redhat.com>
1 parent 9089260 commit bfcf12c

8 files changed

Lines changed: 530 additions & 166 deletions

File tree

packages/dashboard-frontend/src/components/WorkspaceEvents/Item/__tests__/__snapshots__/index.spec.tsx.snap

Lines changed: 92 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,105 +2,123 @@
22

33
exports[`WorkspaceEventsItem component snapshot 1`] = `
44
<div
5-
className="pf-v6-c-content"
6-
data-ouia-component-id="OUIA-Generated-Content-1"
7-
data-ouia-component-type="PF6/Content"
8-
data-ouia-safe={true}
9-
data-pf-content={true}
5+
className="eventRow"
106
>
117
<div
12-
className="pf-v6-c-card pf-m-compact"
13-
data-ouia-component-id="OUIA-Generated-Card-1"
14-
data-ouia-component-type="PF6/Card"
15-
data-ouia-safe={true}
16-
id=""
8+
className="iconBox"
179
>
10+
<i
11+
className="eventIcon"
12+
/>
1813
<div
19-
className="pf-v6-c-card__header header"
14+
className="iconLine"
15+
/>
16+
</div>
17+
<div
18+
className="eventBox"
19+
>
20+
<div
21+
className="pf-v6-c-content"
22+
data-ouia-component-id="OUIA-Generated-Content-1"
23+
data-ouia-component-type="PF6/Content"
24+
data-ouia-safe={true}
25+
data-pf-content={true}
2026
>
2127
<div
22-
className="pf-v6-c-card__header-main"
28+
className="pf-v6-c-card pf-m-compact"
29+
data-ouia-component-id="OUIA-Generated-Card-1"
30+
data-ouia-component-type="PF6/Card"
31+
data-ouia-safe={true}
32+
id=""
2333
>
2434
<div
25-
className="pf-v6-l-flex"
35+
className="pf-v6-c-card__header header"
2636
>
2737
<div
28-
className=""
38+
className="pf-v6-c-card__header-main"
2939
>
30-
<small
31-
className="pf-v6-c-content--small"
32-
data-ouia-component-id="OUIA-Generated-Content-2"
33-
data-ouia-component-type="PF6/Content"
34-
data-ouia-safe={true}
35-
data-pf-content={true}
40+
<div
41+
className="pf-v6-l-flex"
3642
>
37-
<span
38-
data-testid="event-involved-object"
43+
<div
44+
className=""
3945
>
40-
<div
41-
data-testid="patternfly-tooltip"
46+
<small
47+
className="pf-v6-c-content--small"
48+
data-ouia-component-id="OUIA-Generated-Content-2"
49+
data-ouia-component-type="PF6/Content"
50+
data-ouia-safe={true}
51+
data-pf-content={true}
4252
>
4353
<span
44-
data-testid="tooltip-props"
54+
data-testid="event-involved-object"
4555
>
46-
{"aria":"none","aria-live":"polite"}
56+
<div
57+
data-testid="patternfly-tooltip"
58+
>
59+
<span
60+
data-testid="tooltip-props"
61+
>
62+
{"aria":"none","aria-live":"polite"}
63+
</span>
64+
<div
65+
data-testid="tooltip-content"
66+
>
67+
Pod
68+
</div>
69+
<div
70+
data-testid="tooltip-placed-to"
71+
>
72+
<span
73+
className="icon"
74+
>
75+
P
76+
</span>
77+
</div>
78+
</div>
79+
pod-name
4780
</span>
48-
<div
49-
data-testid="tooltip-content"
50-
>
51-
Pod
81+
<div>
82+
Generated from
83+
component on 0.0.0.0
5284
</div>
53-
<div
54-
data-testid="tooltip-placed-to"
85+
</small>
86+
</div>
87+
<div
88+
className="pf-m-align-right"
89+
>
90+
<small
91+
className="pf-v6-c-content--small"
92+
data-ouia-component-id="OUIA-Generated-Content-3"
93+
data-ouia-component-type="PF6/Content"
94+
data-ouia-safe={true}
95+
data-pf-content={true}
96+
>
97+
<span
98+
data-testid="event-time"
5599
>
56-
<span
57-
className="icon"
58-
>
59-
P
60-
</span>
61-
</div>
62-
</div>
63-
pod-name
64-
</span>
65-
<div>
66-
Generated from
67-
component on 0.0.0.0
100+
14:00:00
101+
</span>
102+
</small>
68103
</div>
69-
</small>
104+
</div>
70105
</div>
71-
<div
72-
className="pf-m-align-right"
106+
</div>
107+
<div
108+
className="pf-v6-c-card__body"
109+
>
110+
<p
111+
className="pf-v6-c-content--p"
112+
data-ouia-component-id="OUIA-Generated-Content-4"
113+
data-ouia-component-type="PF6/Content"
114+
data-ouia-safe={true}
115+
data-pf-content={true}
73116
>
74-
<small
75-
className="pf-v6-c-content--small"
76-
data-ouia-component-id="OUIA-Generated-Content-3"
77-
data-ouia-component-type="PF6/Content"
78-
data-ouia-safe={true}
79-
data-pf-content={true}
80-
>
81-
<span
82-
data-testid="event-time"
83-
>
84-
14:00:00
85-
</span>
86-
</small>
87-
</div>
117+
Event message
118+
</p>
88119
</div>
89120
</div>
90121
</div>
91-
<div
92-
className="pf-v6-c-card__body"
93-
>
94-
<p
95-
className="pf-v6-c-content--p"
96-
data-ouia-component-id="OUIA-Generated-Content-4"
97-
data-ouia-component-type="PF6/Content"
98-
data-ouia-safe={true}
99-
data-pf-content={true}
100-
>
101-
Event message
102-
</p>
103-
</div>
104122
</div>
105123
</div>
106124
`;

packages/dashboard-frontend/src/components/WorkspaceEvents/Item/index.module.css

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,53 @@
2020

2121
display: block;
2222
}
23+
24+
/* Timeline row layout */
25+
26+
.eventRow {
27+
display: flex;
28+
}
29+
30+
.iconBox {
31+
position: relative;
32+
flex: 0 0 50px;
33+
}
34+
35+
.eventIcon {
36+
position: absolute;
37+
z-index: 10;
38+
top: 50%;
39+
left: -10px;
40+
transform: translateY(-50%);
41+
42+
display: inline-block;
43+
44+
width: 18px;
45+
height: 18px;
46+
47+
background-color: var(--che-events-icon-bg);
48+
border: var(--pf-t--global--border--width--extra-strong) solid var(--che-events-border);
49+
border-radius: var(--pf-t--global--border--radius--400);
50+
}
51+
52+
@media (width < 75rem) {
53+
.iconBox {
54+
display: none;
55+
}
56+
}
57+
58+
.iconLine {
59+
position: absolute;
60+
top: 50%;
61+
right: 0;
62+
left: 0;
63+
64+
height: 3px;
65+
66+
background-color: var(--che-events-border);
67+
}
68+
69+
.eventBox {
70+
flex: 1 1 auto;
71+
min-width: 0;
72+
}

packages/dashboard-frontend/src/components/WorkspaceEvents/Item/index.tsx

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,26 +80,34 @@ export class WorkspaceEventsItem extends React.PureComponent<Props> {
8080
const { event } = this.props;
8181

8282
return (
83-
<Content>
84-
<Card isCompact>
85-
<CardHeader className={styles.header}>
86-
<Flex>
87-
<FlexItem>
88-
<Content component={ContentVariants.small}>
89-
{this.getInvolvedObj(event)}
90-
{this.getSource(event)}
91-
</Content>
92-
</FlexItem>
93-
<FlexItem align={{ default: 'alignRight' }}>
94-
<Content component={ContentVariants.small}>{this.getTime(event)}</Content>
95-
</FlexItem>
96-
</Flex>
97-
</CardHeader>
98-
<CardBody>
99-
<Content component="p">{event.message}</Content>
100-
</CardBody>
101-
</Card>
102-
</Content>
83+
<div className={styles.eventRow}>
84+
<div className={styles.iconBox}>
85+
<i className={styles.eventIcon} title={event.reason ?? undefined} />
86+
<div className={styles.iconLine} />
87+
</div>
88+
<div className={styles.eventBox}>
89+
<Content>
90+
<Card isCompact>
91+
<CardHeader className={styles.header}>
92+
<Flex>
93+
<FlexItem>
94+
<Content component={ContentVariants.small}>
95+
{this.getInvolvedObj(event)}
96+
{this.getSource(event)}
97+
</Content>
98+
</FlexItem>
99+
<FlexItem align={{ default: 'alignRight' }}>
100+
<Content component={ContentVariants.small}>{this.getTime(event)}</Content>
101+
</FlexItem>
102+
</Flex>
103+
</CardHeader>
104+
<CardBody>
105+
<Content component="p">{event.message}</Content>
106+
</CardBody>
107+
</Card>
108+
</Content>
109+
</div>
110+
</div>
103111
);
104112
}
105113
}

0 commit comments

Comments
 (0)