@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.152 → 2.0.0-next.153
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/.storybook/vitest.setup.ts +1 -1
- package/bundle/openbridge-webcomponents.bundle.js +4717 -4485
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +374 -52
- package/dist/alert-severity.d.ts +19 -1
- package/dist/alert-severity.d.ts.map +1 -1
- package/dist/alert-severity.js +48 -2
- package/dist/alert-severity.js.map +1 -1
- package/dist/components/alert-button/alert-button.css.js +7 -1513
- package/dist/components/alert-button/alert-button.css.js.map +1 -1
- package/dist/components/alert-button/alert-button.d.ts +40 -96
- package/dist/components/alert-button/alert-button.d.ts.map +1 -1
- package/dist/components/alert-button/alert-button.js +40 -107
- package/dist/components/alert-button/alert-button.js.map +1 -1
- package/dist/components/alert-button-item/alert-button-item.css.js +1570 -0
- package/dist/components/alert-button-item/alert-button-item.css.js.map +1 -0
- package/dist/components/alert-button-item/alert-button-item.d.ts +134 -0
- package/dist/components/alert-button-item/alert-button-item.d.ts.map +1 -0
- package/dist/components/alert-button-item/alert-button-item.js +210 -0
- package/dist/components/alert-button-item/alert-button-item.js.map +1 -0
- package/dist/components/alert-counter-item/alert-counter-item.css.js +25 -0
- package/dist/components/alert-counter-item/alert-counter-item.css.js.map +1 -0
- package/dist/components/alert-counter-item/alert-counter-item.d.ts +77 -0
- package/dist/components/alert-counter-item/alert-counter-item.d.ts.map +1 -0
- package/dist/components/alert-counter-item/alert-counter-item.js +87 -0
- package/dist/components/alert-counter-item/alert-counter-item.js.map +1 -0
- package/dist/components/alert-frame/alert-frame.css.js +23 -27
- package/dist/components/alert-frame/alert-frame.css.js.map +1 -1
- package/dist/components/alert-frame/alert-frame.d.ts +7 -6
- package/dist/components/alert-frame/alert-frame.d.ts.map +1 -1
- package/dist/components/alert-frame/alert-frame.js +6 -9
- package/dist/components/alert-frame/alert-frame.js.map +1 -1
- package/dist/components/tree-navigation-item/tree-navigation-item.d.ts +7 -34
- package/dist/components/tree-navigation-item/tree-navigation-item.d.ts.map +1 -1
- package/dist/components/tree-navigation-item/tree-navigation-item.js +3 -32
- package/dist/components/tree-navigation-item/tree-navigation-item.js.map +1 -1
- package/dist/generated/locales/es-419.d.ts +11 -3
- package/dist/generated/locales/es-419.d.ts.map +1 -1
- package/dist/generated/locales/es-419.js +11 -3
- package/dist/generated/locales/es-419.js.map +1 -1
- package/dist/generated/locales/fi-FI.d.ts +8 -0
- package/dist/generated/locales/fi-FI.d.ts.map +1 -1
- package/dist/generated/locales/fi-FI.js +8 -0
- package/dist/generated/locales/fi-FI.js.map +1 -1
- package/dist/navigation-instruments/readout/readout.css.js +4 -29
- package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
- package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
- package/dist/navigation-instruments/readout/readout.js +1 -9
- package/dist/navigation-instruments/readout/readout.js.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js +5 -34
- package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js +1 -9
- package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
- package/dist/palettes/blinking.d.ts +6 -2
- package/dist/palettes/blinking.d.ts.map +1 -1
- package/dist/palettes/blinking.js +6 -2
- package/dist/palettes/blinking.js.map +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/alert-severity.spec.ts +81 -2
- package/src/alert-severity.ts +74 -0
- package/src/components/alert-button/alert-button.css +7 -196
- package/src/components/alert-button/alert-button.spec.ts +119 -68
- package/src/components/alert-button/alert-button.stories.ts +24 -0
- package/src/components/alert-button/alert-button.ts +49 -211
- package/src/components/alert-button-item/alert-button-item.css +242 -0
- package/src/components/alert-button-item/alert-button-item.spec.ts +299 -0
- package/src/components/alert-button-item/alert-button-item.stories.ts +178 -0
- package/src/components/alert-button-item/alert-button-item.ts +289 -0
- package/src/components/alert-counter-item/alert-counter-item.css +15 -0
- package/src/components/alert-counter-item/alert-counter-item.spec.ts +91 -0
- package/src/components/alert-counter-item/alert-counter-item.stories.ts +66 -0
- package/src/components/alert-counter-item/alert-counter-item.ts +128 -0
- package/src/components/alert-frame/alert-frame.css +23 -27
- package/src/components/alert-frame/alert-frame.spec.ts +19 -1
- package/src/components/alert-frame/alert-frame.stories.ts +47 -0
- package/src/components/alert-frame/alert-frame.ts +16 -17
- package/src/components/tree-navigation-item/tree-navigation-item.ts +10 -60
- package/src/generated/locales/es-419.ts +11 -3
- package/src/generated/locales/fi-FI.ts +8 -0
- package/src/navigation-instruments/readout/readout.css +4 -29
- package/src/navigation-instruments/readout/readout.ts +1 -9
- package/src/navigation-instruments/readout-list-item/readout-list-item.css +5 -33
- package/src/navigation-instruments/readout-list-item/readout-list-item.ts +1 -9
- package/src/palettes/blinking.spec.ts +38 -23
- package/src/palettes/blinking.ts +12 -4
- package/src/types.ts +16 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import {LitElement, html, nothing, unsafeCSS} from 'lit';
|
|
2
|
+
import {property} from 'lit/decorators.js';
|
|
3
|
+
import {classMap} from 'lit/directives/class-map.js';
|
|
4
|
+
import {localized, msg} from '@lit/localize';
|
|
5
|
+
import componentStyle from './alert-button-item.css?inline';
|
|
6
|
+
import {customElement} from '../../decorator.js';
|
|
7
|
+
import '../../icons/icon-alerts.js';
|
|
8
|
+
import '../../icons/icon-alerts-active.js';
|
|
9
|
+
import '../../icons/icon-alerts-alarm-twotone.js';
|
|
10
|
+
import '../../icons/icon-alerts-warning-twotone.js';
|
|
11
|
+
import '../../icons/icon-alerts-caution-twotone.js';
|
|
12
|
+
import '../../manual-icon/icon-alerts-critical-twotone.js';
|
|
13
|
+
import '../../manual-icon/icon-alerts-diagnostic-twotone.js';
|
|
14
|
+
import '../alert-counter-item/alert-counter-item.js';
|
|
15
|
+
import {ObcAlertCounterItemType} from '../alert-counter-item/alert-counter-item.js';
|
|
16
|
+
import {
|
|
17
|
+
AlertType,
|
|
18
|
+
FlashingSpeed,
|
|
19
|
+
type AlertCounts,
|
|
20
|
+
type ResolvedFlashingSpeed,
|
|
21
|
+
} from '../../types.js';
|
|
22
|
+
import {
|
|
23
|
+
AlertFlashPhase,
|
|
24
|
+
AlertTwotoneComponent,
|
|
25
|
+
alertCountsLabel,
|
|
26
|
+
alertTypeLabel,
|
|
27
|
+
getAlertTwotoneComponent,
|
|
28
|
+
rankAlertCounts,
|
|
29
|
+
resolveFlashingSpeed,
|
|
30
|
+
} from '../../alert-severity.js';
|
|
31
|
+
import {FlashingController} from '../../palettes/flashing-controller.js';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `ObcAlertButtonType` – Enum for alert button visual and behavioral variants.
|
|
35
|
+
*
|
|
36
|
+
* - `flat`: Minimal, icon-only button for compact spaces.
|
|
37
|
+
* - `normal`: Standard button with icon and optional counter.
|
|
38
|
+
* - `enhanced`: Emphasized button with additional styling for high-priority alerts.
|
|
39
|
+
*/
|
|
40
|
+
export enum ObcAlertButtonType {
|
|
41
|
+
Flat = 'flat',
|
|
42
|
+
Normal = 'normal',
|
|
43
|
+
Enhanced = 'enhanced',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* `<obc-alert-button-item>` – The bell button that shows the alert state and count.
|
|
48
|
+
*
|
|
49
|
+
* Shows the alert status as a bell in the style of the given alert type, with
|
|
50
|
+
* an optional counter, or as a global counter: one badge per severity, then
|
|
51
|
+
* the bell and the total. `obc-alert-button` renders the item next to its
|
|
52
|
+
* silence button; use the item on its own where no silence button is needed.
|
|
53
|
+
*
|
|
54
|
+
* ---
|
|
55
|
+
*
|
|
56
|
+
* ### Features
|
|
57
|
+
* - **Variants (`type`):** `flat` (icon only), `normal` (bordered, icon and
|
|
58
|
+
* counter) and `enhanced` (filled in the severity colour).
|
|
59
|
+
* - **Alert types:** `alarm`, `warning`, `caution` and the `level-*` family;
|
|
60
|
+
* `nAlerts` of 0 shows the idle bell.
|
|
61
|
+
* - **Counter:** `counter` shows `nAlerts` next to the bell, except in `flat`.
|
|
62
|
+
* - **Global counter:** `globalCounter` shows the per-severity `counts` as
|
|
63
|
+
* badges, then the bell and `nAlerts`, always in the normal style.
|
|
64
|
+
* - **Flashing:** `blinking` flashes the bell at the tempo `flashingSpeed`
|
|
65
|
+
* resolves (#1224).
|
|
66
|
+
* - **Fill height:** `fillHeight` stretches the visible button to the host
|
|
67
|
+
* height, as the tall `obc-alert-button` does.
|
|
68
|
+
*
|
|
69
|
+
* ---
|
|
70
|
+
*
|
|
71
|
+
* ### Usage Guidelines
|
|
72
|
+
* - Keep `nAlerts` and `counts` consistent; the item shows both as given.
|
|
73
|
+
* - The generated label reads e.g. "Alerts, 12, 2 Alarm, 4 Warning,
|
|
74
|
+
* 6 Caution"; an `aria-label` on the host replaces it.
|
|
75
|
+
* - A native button, so Enter and Space activate it (APG button pattern).
|
|
76
|
+
* - A parent that joins another button to the item's end sets the
|
|
77
|
+
* `data-group-item-not-last` attribute on the host.
|
|
78
|
+
* - **TODO(designer):** the Figma file draws the flat item's hover, active and
|
|
79
|
+
* focused variants at 20 % opacity; the item uses the standard flat states.
|
|
80
|
+
* - **TODO(designer):** the Figma file defines no flashing for the global
|
|
81
|
+
* counter, so it never flashes.
|
|
82
|
+
*
|
|
83
|
+
* ---
|
|
84
|
+
*
|
|
85
|
+
* ### Example
|
|
86
|
+
*
|
|
87
|
+
* ```html
|
|
88
|
+
* <obc-alert-button-item alertType="alarm" nAlerts="3" counter blinking></obc-alert-button-item>
|
|
89
|
+
* <obc-alert-button-item
|
|
90
|
+
* globalCounter
|
|
91
|
+
* nAlerts="12"
|
|
92
|
+
* .counts=${{countAlarm: 2, countWarning: 4, countCaution: 6}}
|
|
93
|
+
* ></obc-alert-button-item>
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @property type - Visual variant: `flat` (icon only), `normal` (default, bordered with the counter) or `enhanced` (filled in the severity colour).
|
|
97
|
+
* @availableWhen type globalCounter==false
|
|
98
|
+
* @property alertType - Alert type that selects the icon and colours: `alarm`, `warning`, `caution` or a `level-*` severity.
|
|
99
|
+
* @availableWhen alertType globalCounter==false
|
|
100
|
+
* @property nAlerts - Number of active alerts; 0 shows the idle bell and hides the counter.
|
|
101
|
+
* @property counter - Shows `nAlerts` next to the bell (not in `flat`).
|
|
102
|
+
* @availableWhen counter globalCounter==false
|
|
103
|
+
* @property globalCounter - Shows the per-severity counts, the bell and the total instead of a single alert status.
|
|
104
|
+
* @property counts - Alert count per severity for the global counter badges.
|
|
105
|
+
* @availableWhen counts globalCounter==true
|
|
106
|
+
* @property shelvedCount - Number of shelved alerts, shown after the global counter badges.
|
|
107
|
+
* @availableWhen shelvedCount globalCounter==true
|
|
108
|
+
* @property blinking - Flashes the bell while there are active alerts of a flashing alert type.
|
|
109
|
+
* @availableWhen blinking globalCounter==false
|
|
110
|
+
* @property flashingSpeed - Flash tempo while `blinking` is on: `default` resolves from `alertType`, `fast`, `slow`, `very-slow` force a tempo, `fixed` never flashes.
|
|
111
|
+
* @availableWhen flashingSpeed blinking==true
|
|
112
|
+
* @property fillHeight - Stretches the visible button to the host height instead of the 32 px visual target.
|
|
113
|
+
* @property ariaLabel - Accessible name forwarded to the inner `<button>`, mapped to the `aria-label` attribute; replaces the generated label. `aria-labelledby` is not supported: ID references cannot cross the shadow boundary.
|
|
114
|
+
* @fires click - Fired when the button is clicked.
|
|
115
|
+
* @experimental
|
|
116
|
+
*/
|
|
117
|
+
@customElement('obc-alert-button-item')
|
|
118
|
+
@localized()
|
|
119
|
+
export class ObcAlertButtonItem extends LitElement {
|
|
120
|
+
@property({type: String}) type: ObcAlertButtonType =
|
|
121
|
+
ObcAlertButtonType.Normal;
|
|
122
|
+
@property({type: String}) alertType?: AlertType;
|
|
123
|
+
@property({type: Number}) nAlerts = 0;
|
|
124
|
+
@property({type: Boolean}) counter = false;
|
|
125
|
+
@property({type: Boolean}) globalCounter = false;
|
|
126
|
+
@property({type: Object, attribute: false}) counts: AlertCounts = {};
|
|
127
|
+
@property({type: Number}) shelvedCount = 0;
|
|
128
|
+
@property({type: Boolean}) blinking = false;
|
|
129
|
+
@property({type: String}) flashingSpeed: FlashingSpeed =
|
|
130
|
+
FlashingSpeed.Default;
|
|
131
|
+
@property({type: Boolean}) fillHeight = false;
|
|
132
|
+
|
|
133
|
+
// Reactive so a consumer changing the name re-renders the shadow button.
|
|
134
|
+
@property({type: String, attribute: 'aria-label'})
|
|
135
|
+
override ariaLabel: string | null = null;
|
|
136
|
+
|
|
137
|
+
protected readonly flashing = new FlashingController(
|
|
138
|
+
this,
|
|
139
|
+
() => this.resolvedFlashingSpeed
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
get resolvedFlashingSpeed(): ResolvedFlashingSpeed {
|
|
143
|
+
if (
|
|
144
|
+
this.globalCounter ||
|
|
145
|
+
!this.blinking ||
|
|
146
|
+
this.nAlerts <= 0 ||
|
|
147
|
+
this.alertType === undefined
|
|
148
|
+
) {
|
|
149
|
+
return FlashingSpeed.Fixed;
|
|
150
|
+
}
|
|
151
|
+
return resolveFlashingSpeed(
|
|
152
|
+
this.flashingSpeed,
|
|
153
|
+
this.alertType,
|
|
154
|
+
AlertFlashPhase.Active
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private get accessibleName(): string {
|
|
159
|
+
if (this.ariaLabel) {
|
|
160
|
+
return this.ariaLabel;
|
|
161
|
+
}
|
|
162
|
+
const parts = [msg('Alerts')];
|
|
163
|
+
if (this.nAlerts > 0) {
|
|
164
|
+
parts.push(String(this.nAlerts));
|
|
165
|
+
}
|
|
166
|
+
if (this.globalCounter) {
|
|
167
|
+
const breakdown = alertCountsLabel(this.counts, this.shelvedCount);
|
|
168
|
+
if (breakdown) {
|
|
169
|
+
parts.push(breakdown);
|
|
170
|
+
}
|
|
171
|
+
} else if (this.nAlerts > 0 && this.alertType) {
|
|
172
|
+
parts.push(alertTypeLabel(this.alertType));
|
|
173
|
+
}
|
|
174
|
+
return parts.join(', ');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private renderTwotoneIcon() {
|
|
178
|
+
const twotone = this.alertType
|
|
179
|
+
? getAlertTwotoneComponent(this.alertType)
|
|
180
|
+
: AlertTwotoneComponent.Caution;
|
|
181
|
+
switch (twotone) {
|
|
182
|
+
case AlertTwotoneComponent.Critical:
|
|
183
|
+
return html`<obi-alerts-critical-twotone
|
|
184
|
+
useCssColor
|
|
185
|
+
class="icon"
|
|
186
|
+
></obi-alerts-critical-twotone>`;
|
|
187
|
+
case AlertTwotoneComponent.Diagnostic:
|
|
188
|
+
return html`<obi-alerts-diagnostic-twotone
|
|
189
|
+
useCssColor
|
|
190
|
+
class="icon"
|
|
191
|
+
></obi-alerts-diagnostic-twotone>`;
|
|
192
|
+
case AlertTwotoneComponent.Warning:
|
|
193
|
+
return html`<obi-alerts-warning-twotone
|
|
194
|
+
useCssColor
|
|
195
|
+
class="icon"
|
|
196
|
+
></obi-alerts-warning-twotone>`;
|
|
197
|
+
case AlertTwotoneComponent.Caution:
|
|
198
|
+
return html`<obi-alerts-caution-twotone
|
|
199
|
+
useCssColor
|
|
200
|
+
class="icon"
|
|
201
|
+
></obi-alerts-caution-twotone>`;
|
|
202
|
+
default:
|
|
203
|
+
return html`<obi-alerts-alarm-twotone
|
|
204
|
+
useCssColor
|
|
205
|
+
class="icon"
|
|
206
|
+
></obi-alerts-alarm-twotone>`;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** The blink layer shows the opposite of the steady layer in each flash. */
|
|
211
|
+
private renderIcon(styleType: ObcAlertButtonType, blinkLayer: boolean) {
|
|
212
|
+
if (this.nAlerts === 0) {
|
|
213
|
+
return html`<obi-alerts class="icon"></obi-alerts>`;
|
|
214
|
+
}
|
|
215
|
+
const enhanced = styleType === ObcAlertButtonType.Enhanced;
|
|
216
|
+
if (blinkLayer) {
|
|
217
|
+
return enhanced
|
|
218
|
+
? this.renderTwotoneIcon()
|
|
219
|
+
: html`<obi-alerts class="icon"></obi-alerts>`;
|
|
220
|
+
}
|
|
221
|
+
return enhanced || this.globalCounter
|
|
222
|
+
? html`<obi-alerts-active class="icon"></obi-alerts-active>`
|
|
223
|
+
: this.renderTwotoneIcon();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private renderCount() {
|
|
227
|
+
return html`<span class="count">${this.nAlerts}</span>`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private renderCounts() {
|
|
231
|
+
if (rankAlertCounts(this.counts).length === 0 && this.shelvedCount <= 0) {
|
|
232
|
+
return nothing;
|
|
233
|
+
}
|
|
234
|
+
return html`<obc-alert-counter-item
|
|
235
|
+
.type=${ObcAlertCounterItemType.Badges}
|
|
236
|
+
.counts=${this.counts}
|
|
237
|
+
.shelvedCount=${this.shelvedCount}
|
|
238
|
+
></obc-alert-counter-item>`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
override render() {
|
|
242
|
+
const styleType = this.globalCounter
|
|
243
|
+
? ObcAlertButtonType.Normal
|
|
244
|
+
: this.type;
|
|
245
|
+
const showCount =
|
|
246
|
+
this.nAlerts > 0 &&
|
|
247
|
+
(this.globalCounter ||
|
|
248
|
+
(this.counter && styleType !== ObcAlertButtonType.Flat));
|
|
249
|
+
const tempo = this.resolvedFlashingSpeed;
|
|
250
|
+
return html`
|
|
251
|
+
<button
|
|
252
|
+
class=${classMap({
|
|
253
|
+
'alert-button': true,
|
|
254
|
+
[`type-${styleType}`]: true,
|
|
255
|
+
[`alert-type-${this.alertType ?? 'none'}`]: true,
|
|
256
|
+
counter: showCount && !this.globalCounter,
|
|
257
|
+
'global-counter': this.globalCounter,
|
|
258
|
+
'fill-height': this.fillHeight,
|
|
259
|
+
[`flash-${tempo}`]: true,
|
|
260
|
+
})}
|
|
261
|
+
aria-label=${this.accessibleName}
|
|
262
|
+
>
|
|
263
|
+
${tempo === FlashingSpeed.Fixed
|
|
264
|
+
? nothing
|
|
265
|
+
: html`<div class="blink" aria-hidden="true">
|
|
266
|
+
<div class="content">
|
|
267
|
+
${this.renderIcon(styleType, true)}
|
|
268
|
+
${showCount ? this.renderCount() : nothing}
|
|
269
|
+
</div>
|
|
270
|
+
</div>`}
|
|
271
|
+
<div class="visible-wrapper">
|
|
272
|
+
${this.globalCounter ? this.renderCounts() : nothing}
|
|
273
|
+
<div class="content">
|
|
274
|
+
${this.renderIcon(styleType, false)}
|
|
275
|
+
${showCount ? this.renderCount() : nothing}
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
</button>
|
|
279
|
+
`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
static override styles = unsafeCSS(componentStyle);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
declare global {
|
|
286
|
+
interface HTMLElementTagNameMap {
|
|
287
|
+
'obc-alert-button-item': ObcAlertButtonItem;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.row {
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
gap: var(--app-components-alert-counter-item-badge-spacing);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
obi-alerts-shelf {
|
|
12
|
+
display: block; /* fills the badge icon box that sizes the badge-icon slot */
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 100%;
|
|
15
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest';
|
|
2
|
+
import {render} from 'vitest-browser-lit';
|
|
3
|
+
import {html} from 'lit';
|
|
4
|
+
import './alert-counter-item.js';
|
|
5
|
+
import {
|
|
6
|
+
ObcAlertCounterItem,
|
|
7
|
+
ObcAlertCounterItemType,
|
|
8
|
+
} from './alert-counter-item.js';
|
|
9
|
+
import type {ObcBadge} from '../badge/badge.js';
|
|
10
|
+
import type {AlertCounts} from '../../types.js';
|
|
11
|
+
|
|
12
|
+
async function setup(
|
|
13
|
+
counts: AlertCounts,
|
|
14
|
+
options: {type?: ObcAlertCounterItemType; shelvedCount?: number} = {}
|
|
15
|
+
) {
|
|
16
|
+
const screen = render(
|
|
17
|
+
html`<obc-alert-counter-item
|
|
18
|
+
.counts=${counts}
|
|
19
|
+
.type=${options.type ?? ObcAlertCounterItemType.Badges}
|
|
20
|
+
.shelvedCount=${options.shelvedCount ?? 0}
|
|
21
|
+
></obc-alert-counter-item>`
|
|
22
|
+
);
|
|
23
|
+
const el = screen.container.querySelector(
|
|
24
|
+
'obc-alert-counter-item'
|
|
25
|
+
) as ObcAlertCounterItem;
|
|
26
|
+
await el.updateComplete;
|
|
27
|
+
return el;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function badges(el: ObcAlertCounterItem): ObcBadge[] {
|
|
31
|
+
return [...el.shadowRoot!.querySelectorAll<ObcBadge>('obc-badge')];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function lastBadge(el: ObcAlertCounterItem): ObcBadge {
|
|
35
|
+
const all = badges(el);
|
|
36
|
+
return all[all.length - 1];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('obc-alert-counter-item', () => {
|
|
40
|
+
it('renders one filled badge per positive count, most severe first', async () => {
|
|
41
|
+
const el = await setup({countCaution: 6, countAlarm: 2, countWarning: 0});
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
badges(el).map((b) => [b.type, b.number, b.variant, b.showIcon])
|
|
45
|
+
).toEqual([
|
|
46
|
+
['alarm', 2, 'default', false],
|
|
47
|
+
['caution', 6, 'default', false],
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('draws flat badges with glyphs in the alert-level type', async () => {
|
|
52
|
+
const el = await setup(
|
|
53
|
+
{countWarning: 4},
|
|
54
|
+
{type: ObcAlertCounterItemType.AlertLevel}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(badges(el).map((b) => [b.type, b.variant, b.showIcon])).toEqual([
|
|
58
|
+
['warning', 'flat', true],
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('adds the shelved count last', async () => {
|
|
63
|
+
const filled = await setup({countAlarm: 2}, {shelvedCount: 9});
|
|
64
|
+
expect([lastBadge(filled).type, lastBadge(filled).number]).toEqual([
|
|
65
|
+
'regular',
|
|
66
|
+
9,
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
const level = await setup(
|
|
70
|
+
{countAlarm: 2},
|
|
71
|
+
{type: ObcAlertCounterItemType.AlertLevel, shelvedCount: 9}
|
|
72
|
+
);
|
|
73
|
+
expect(lastBadge(level).querySelector('obi-alerts-shelf')).not.toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('renders nothing when nothing is counted', async () => {
|
|
77
|
+
const el = await setup({countAlarm: 0});
|
|
78
|
+
|
|
79
|
+
expect(el.shadowRoot!.querySelector('.row')).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('labels the row with the counts', async () => {
|
|
83
|
+
const el = await setup({countWarning: 4, countAlarm: 2}, {shelvedCount: 1});
|
|
84
|
+
const row = el.shadowRoot!.querySelector('.row')!;
|
|
85
|
+
|
|
86
|
+
expect(row.getAttribute('role')).toBe('img');
|
|
87
|
+
expect(row.getAttribute('aria-label')).toBe(
|
|
88
|
+
'2 Alarm, 4 Warning, 1 Shelved'
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type {Meta, StoryObj} from '@storybook/web-components-vite';
|
|
2
|
+
import {html} from 'lit';
|
|
3
|
+
import {
|
|
4
|
+
ObcAlertCounterItem,
|
|
5
|
+
ObcAlertCounterItemType,
|
|
6
|
+
} from './alert-counter-item.js';
|
|
7
|
+
import './alert-counter-item.js';
|
|
8
|
+
import type {AlertCounts} from '../../types.js';
|
|
9
|
+
|
|
10
|
+
const LEVEL_COUNTS: AlertCounts = {
|
|
11
|
+
countLevelCritical: 1,
|
|
12
|
+
countLevelHigh: 9,
|
|
13
|
+
countLevelMedium: 4,
|
|
14
|
+
countLevelLow: 2,
|
|
15
|
+
countLevelDiagnostic: 7,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const meta: Meta<typeof ObcAlertCounterItem> = {
|
|
19
|
+
title: 'Application Components/Alerts/Alert Counter Item',
|
|
20
|
+
tags: ['autodocs', '6.1', 'experimental'],
|
|
21
|
+
component: 'obc-alert-counter-item',
|
|
22
|
+
args: {
|
|
23
|
+
type: ObcAlertCounterItemType.Badges,
|
|
24
|
+
counts: {countAlarm: 2, countWarning: 4, countCaution: 6},
|
|
25
|
+
shelvedCount: 0,
|
|
26
|
+
},
|
|
27
|
+
argTypes: {
|
|
28
|
+
type: {
|
|
29
|
+
control: {type: 'inline-radio'},
|
|
30
|
+
options: Object.values(ObcAlertCounterItemType),
|
|
31
|
+
},
|
|
32
|
+
counts: {control: {type: 'object'}},
|
|
33
|
+
shelvedCount: {control: {type: 'number', min: 0}},
|
|
34
|
+
},
|
|
35
|
+
render: (args) =>
|
|
36
|
+
html`<obc-alert-counter-item
|
|
37
|
+
.type=${args.type}
|
|
38
|
+
.counts=${args.counts}
|
|
39
|
+
.shelvedCount=${args.shelvedCount}
|
|
40
|
+
></obc-alert-counter-item>`,
|
|
41
|
+
} satisfies Meta<ObcAlertCounterItem>;
|
|
42
|
+
|
|
43
|
+
export default meta;
|
|
44
|
+
type Story = StoryObj<ObcAlertCounterItem>;
|
|
45
|
+
|
|
46
|
+
export const Badges: Story = {};
|
|
47
|
+
|
|
48
|
+
export const BadgesWithShelved: Story = {
|
|
49
|
+
args: {shelvedCount: 9},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const AlertLevel: Story = {
|
|
53
|
+
args: {type: ObcAlertCounterItemType.AlertLevel},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const AlertLevelWithShelved: Story = {
|
|
57
|
+
args: {type: ObcAlertCounterItemType.AlertLevel, shelvedCount: 9},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const LevelSeverities: Story = {
|
|
61
|
+
args: {counts: LEVEL_COUNTS},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const LevelSeveritiesAlertLevel: Story = {
|
|
65
|
+
args: {type: ObcAlertCounterItemType.AlertLevel, counts: LEVEL_COUNTS},
|
|
66
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {LitElement, html, nothing, unsafeCSS} from 'lit';
|
|
2
|
+
import {property} from 'lit/decorators.js';
|
|
3
|
+
import {repeat} from 'lit/directives/repeat.js';
|
|
4
|
+
import {localized} from '@lit/localize';
|
|
5
|
+
import componentStyle from './alert-counter-item.css?inline';
|
|
6
|
+
import {customElement} from '../../decorator.js';
|
|
7
|
+
import '../badge/badge.js';
|
|
8
|
+
import {BadgeSize, BadgeType, BadgeVariant} from '../badge/badge.js';
|
|
9
|
+
import '../../icons/icon-alerts-shelf.js';
|
|
10
|
+
import type {AlertCounts} from '../../types.js';
|
|
11
|
+
import {alertCountsLabel, rankAlertCounts} from '../../alert-severity.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `ObcAlertCounterItemType` – How each count is drawn (Figma `Type`).
|
|
15
|
+
*
|
|
16
|
+
* - `badges`: filled badges in the severity colour (Figma Badges counter).
|
|
17
|
+
* - `alert-level`: flat badges with the severity glyph (Figma Alert level counter).
|
|
18
|
+
*/
|
|
19
|
+
export enum ObcAlertCounterItemType {
|
|
20
|
+
Badges = 'badges',
|
|
21
|
+
AlertLevel = 'alert-level',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* `<obc-alert-counter-item>` – A row of alert counts, one badge per severity.
|
|
26
|
+
*
|
|
27
|
+
* Renders a large `obc-badge` for every severity with a count above zero, most
|
|
28
|
+
* severe first, followed by an optional shelved count. Nothing renders when
|
|
29
|
+
* nothing is counted.
|
|
30
|
+
*
|
|
31
|
+
* ---
|
|
32
|
+
*
|
|
33
|
+
* ### Features
|
|
34
|
+
* - **Two types:** `badges` draws filled badges in the severity colour;
|
|
35
|
+
* `alert-level` draws flat badges with the severity glyph.
|
|
36
|
+
* - **Every severity:** `counts` takes the whole `AlertType` vocabulary,
|
|
37
|
+
* including the `level-*` family, ordered by `ALERT_SEVERITY_PRIORITY`.
|
|
38
|
+
* - **Shelved count:** `shelvedCount` adds a neutral badge at the end, with
|
|
39
|
+
* the shelf icon in `alert-level`.
|
|
40
|
+
* - **Accessible summary:** the row is announced as one image labelled with
|
|
41
|
+
* the counts, e.g. "2 Alarm, 4 Warning, 6 Caution".
|
|
42
|
+
*
|
|
43
|
+
* ---
|
|
44
|
+
*
|
|
45
|
+
* ### Usage Guidelines
|
|
46
|
+
* - Show how many alerts are outstanding per severity in a compact space;
|
|
47
|
+
* `obc-alert-button-item` nests the counter for its global counter.
|
|
48
|
+
* - Pass the counts as one object: `.counts=${{countAlarm: 2, countWarning: 4}}`.
|
|
49
|
+
* - The counter is display-only: it renders no button and fires no events.
|
|
50
|
+
* - For a framed per-subsystem summary with a label, see
|
|
51
|
+
* `obc-alert-subsystem-counter`.
|
|
52
|
+
* - **TODO(designer):** the Figma component also has an Unacked counter type;
|
|
53
|
+
* how its icons map to counts is not defined yet.
|
|
54
|
+
*
|
|
55
|
+
* ---
|
|
56
|
+
*
|
|
57
|
+
* ### Example
|
|
58
|
+
*
|
|
59
|
+
* ```html
|
|
60
|
+
* <obc-alert-counter-item
|
|
61
|
+
* .counts=${{countAlarm: 2, countWarning: 4, countCaution: 6}}
|
|
62
|
+
* shelvedCount="9"
|
|
63
|
+
* ></obc-alert-counter-item>
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @property type - How each count is drawn: `badges` (filled severity badges, default) or `alert-level` (flat badges with the severity glyph).
|
|
67
|
+
* @property counts - Alert count per severity (`countAlarm`, `countWarning`, `countCaution` and the `level-*` counts); counts of zero or less are not shown.
|
|
68
|
+
* @property shelvedCount - Number of shelved alerts, shown as a neutral badge after the severities; hidden at zero.
|
|
69
|
+
* @experimental
|
|
70
|
+
*/
|
|
71
|
+
@customElement('obc-alert-counter-item')
|
|
72
|
+
@localized()
|
|
73
|
+
export class ObcAlertCounterItem extends LitElement {
|
|
74
|
+
@property({type: String}) type: ObcAlertCounterItemType =
|
|
75
|
+
ObcAlertCounterItemType.Badges;
|
|
76
|
+
@property({type: Object, attribute: false}) counts: AlertCounts = {};
|
|
77
|
+
@property({type: Number}) shelvedCount = 0;
|
|
78
|
+
|
|
79
|
+
override render() {
|
|
80
|
+
const ranked = rankAlertCounts(this.counts);
|
|
81
|
+
if (ranked.length === 0 && this.shelvedCount <= 0) {
|
|
82
|
+
return nothing;
|
|
83
|
+
}
|
|
84
|
+
const alertLevel = this.type === ObcAlertCounterItemType.AlertLevel;
|
|
85
|
+
const variant = alertLevel ? BadgeVariant.flat : BadgeVariant.default;
|
|
86
|
+
return html`
|
|
87
|
+
<div
|
|
88
|
+
class="row"
|
|
89
|
+
role="img"
|
|
90
|
+
aria-label=${alertCountsLabel(this.counts, this.shelvedCount)}
|
|
91
|
+
>
|
|
92
|
+
${repeat(
|
|
93
|
+
ranked,
|
|
94
|
+
(entry) => entry.type,
|
|
95
|
+
(entry) =>
|
|
96
|
+
html`<obc-badge
|
|
97
|
+
size=${BadgeSize.large}
|
|
98
|
+
type=${entry.type}
|
|
99
|
+
.variant=${variant}
|
|
100
|
+
.showIcon=${alertLevel}
|
|
101
|
+
.number=${entry.count}
|
|
102
|
+
></obc-badge>`
|
|
103
|
+
)}
|
|
104
|
+
${this.shelvedCount > 0
|
|
105
|
+
? html`<obc-badge
|
|
106
|
+
size=${BadgeSize.large}
|
|
107
|
+
type=${BadgeType.regular}
|
|
108
|
+
.variant=${variant}
|
|
109
|
+
.showIcon=${alertLevel}
|
|
110
|
+
.number=${this.shelvedCount}
|
|
111
|
+
>
|
|
112
|
+
${alertLevel
|
|
113
|
+
? html`<obi-alerts-shelf slot="badge-icon"></obi-alerts-shelf>`
|
|
114
|
+
: nothing}
|
|
115
|
+
</obc-badge>`
|
|
116
|
+
: nothing}
|
|
117
|
+
</div>
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static override styles = unsafeCSS(componentStyle);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
declare global {
|
|
125
|
+
interface HTMLElementTagNameMap {
|
|
126
|
+
'obc-alert-counter-item': ObcAlertCounterItem;
|
|
127
|
+
}
|
|
128
|
+
}
|