@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.
Files changed (90) hide show
  1. package/.storybook/vitest.setup.ts +1 -1
  2. package/bundle/openbridge-webcomponents.bundle.js +4717 -4485
  3. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  4. package/custom-elements.json +374 -52
  5. package/dist/alert-severity.d.ts +19 -1
  6. package/dist/alert-severity.d.ts.map +1 -1
  7. package/dist/alert-severity.js +48 -2
  8. package/dist/alert-severity.js.map +1 -1
  9. package/dist/components/alert-button/alert-button.css.js +7 -1513
  10. package/dist/components/alert-button/alert-button.css.js.map +1 -1
  11. package/dist/components/alert-button/alert-button.d.ts +40 -96
  12. package/dist/components/alert-button/alert-button.d.ts.map +1 -1
  13. package/dist/components/alert-button/alert-button.js +40 -107
  14. package/dist/components/alert-button/alert-button.js.map +1 -1
  15. package/dist/components/alert-button-item/alert-button-item.css.js +1570 -0
  16. package/dist/components/alert-button-item/alert-button-item.css.js.map +1 -0
  17. package/dist/components/alert-button-item/alert-button-item.d.ts +134 -0
  18. package/dist/components/alert-button-item/alert-button-item.d.ts.map +1 -0
  19. package/dist/components/alert-button-item/alert-button-item.js +210 -0
  20. package/dist/components/alert-button-item/alert-button-item.js.map +1 -0
  21. package/dist/components/alert-counter-item/alert-counter-item.css.js +25 -0
  22. package/dist/components/alert-counter-item/alert-counter-item.css.js.map +1 -0
  23. package/dist/components/alert-counter-item/alert-counter-item.d.ts +77 -0
  24. package/dist/components/alert-counter-item/alert-counter-item.d.ts.map +1 -0
  25. package/dist/components/alert-counter-item/alert-counter-item.js +87 -0
  26. package/dist/components/alert-counter-item/alert-counter-item.js.map +1 -0
  27. package/dist/components/alert-frame/alert-frame.css.js +23 -27
  28. package/dist/components/alert-frame/alert-frame.css.js.map +1 -1
  29. package/dist/components/alert-frame/alert-frame.d.ts +7 -6
  30. package/dist/components/alert-frame/alert-frame.d.ts.map +1 -1
  31. package/dist/components/alert-frame/alert-frame.js +6 -9
  32. package/dist/components/alert-frame/alert-frame.js.map +1 -1
  33. package/dist/components/tree-navigation-item/tree-navigation-item.d.ts +7 -34
  34. package/dist/components/tree-navigation-item/tree-navigation-item.d.ts.map +1 -1
  35. package/dist/components/tree-navigation-item/tree-navigation-item.js +3 -32
  36. package/dist/components/tree-navigation-item/tree-navigation-item.js.map +1 -1
  37. package/dist/generated/locales/es-419.d.ts +11 -3
  38. package/dist/generated/locales/es-419.d.ts.map +1 -1
  39. package/dist/generated/locales/es-419.js +11 -3
  40. package/dist/generated/locales/es-419.js.map +1 -1
  41. package/dist/generated/locales/fi-FI.d.ts +8 -0
  42. package/dist/generated/locales/fi-FI.d.ts.map +1 -1
  43. package/dist/generated/locales/fi-FI.js +8 -0
  44. package/dist/generated/locales/fi-FI.js.map +1 -1
  45. package/dist/navigation-instruments/readout/readout.css.js +4 -29
  46. package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
  47. package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
  48. package/dist/navigation-instruments/readout/readout.js +1 -9
  49. package/dist/navigation-instruments/readout/readout.js.map +1 -1
  50. package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js +5 -34
  51. package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js.map +1 -1
  52. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
  53. package/dist/navigation-instruments/readout-list-item/readout-list-item.js +1 -9
  54. package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
  55. package/dist/palettes/blinking.d.ts +6 -2
  56. package/dist/palettes/blinking.d.ts.map +1 -1
  57. package/dist/palettes/blinking.js +6 -2
  58. package/dist/palettes/blinking.js.map +1 -1
  59. package/dist/types.d.ts +15 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/dist/types.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/alert-severity.spec.ts +81 -2
  64. package/src/alert-severity.ts +74 -0
  65. package/src/components/alert-button/alert-button.css +7 -196
  66. package/src/components/alert-button/alert-button.spec.ts +119 -68
  67. package/src/components/alert-button/alert-button.stories.ts +24 -0
  68. package/src/components/alert-button/alert-button.ts +49 -211
  69. package/src/components/alert-button-item/alert-button-item.css +242 -0
  70. package/src/components/alert-button-item/alert-button-item.spec.ts +299 -0
  71. package/src/components/alert-button-item/alert-button-item.stories.ts +178 -0
  72. package/src/components/alert-button-item/alert-button-item.ts +289 -0
  73. package/src/components/alert-counter-item/alert-counter-item.css +15 -0
  74. package/src/components/alert-counter-item/alert-counter-item.spec.ts +91 -0
  75. package/src/components/alert-counter-item/alert-counter-item.stories.ts +66 -0
  76. package/src/components/alert-counter-item/alert-counter-item.ts +128 -0
  77. package/src/components/alert-frame/alert-frame.css +23 -27
  78. package/src/components/alert-frame/alert-frame.spec.ts +19 -1
  79. package/src/components/alert-frame/alert-frame.stories.ts +47 -0
  80. package/src/components/alert-frame/alert-frame.ts +16 -17
  81. package/src/components/tree-navigation-item/tree-navigation-item.ts +10 -60
  82. package/src/generated/locales/es-419.ts +11 -3
  83. package/src/generated/locales/fi-FI.ts +8 -0
  84. package/src/navigation-instruments/readout/readout.css +4 -29
  85. package/src/navigation-instruments/readout/readout.ts +1 -9
  86. package/src/navigation-instruments/readout-list-item/readout-list-item.css +5 -33
  87. package/src/navigation-instruments/readout-list-item/readout-list-item.ts +1 -9
  88. package/src/palettes/blinking.spec.ts +38 -23
  89. package/src/palettes/blinking.ts +12 -4
  90. package/src/types.ts +16 -0
@@ -1,6 +1,17 @@
1
1
  import {describe, expect, it} from 'vitest';
2
- import {AlertFlashPhase, resolveFlashingSpeed} from './alert-severity.js';
3
- import {AlertType, FlashingSpeed} from './types.js';
2
+ import {
3
+ AlertFlashPhase,
4
+ alertCountsLabel,
5
+ alertTypeLabel,
6
+ rankAlertCounts,
7
+ resolveFlashingSpeed,
8
+ } from './alert-severity.js';
9
+ import {
10
+ ALERT_SEVERITY_PRIORITY,
11
+ AlertType,
12
+ FlashingSpeed,
13
+ type AlertCounts,
14
+ } from './types.js';
4
15
 
5
16
  describe('resolveFlashingSpeed', () => {
6
17
  it.each([
@@ -54,3 +65,71 @@ describe('resolveFlashingSpeed', () => {
54
65
  );
55
66
  });
56
67
  });
68
+
69
+ describe('rankAlertCounts', () => {
70
+ it('keeps positive counts, most severe first', () => {
71
+ expect(
72
+ rankAlertCounts({
73
+ countCaution: 6,
74
+ countLevelDiagnostic: 1,
75
+ countAlarm: 2,
76
+ countWarning: 0,
77
+ countLevelCritical: 1,
78
+ countLevelLow: -3,
79
+ })
80
+ ).toEqual([
81
+ {type: AlertType.LevelCritical, count: 1},
82
+ {type: AlertType.Alarm, count: 2},
83
+ {type: AlertType.Caution, count: 6},
84
+ {type: AlertType.LevelDiagnostic, count: 1},
85
+ ]);
86
+ });
87
+
88
+ it('orders every severity by ALERT_SEVERITY_PRIORITY', () => {
89
+ const one: AlertCounts = {
90
+ countLevelDiagnostic: 1,
91
+ countLevelLow: 1,
92
+ countCaution: 1,
93
+ countLevelMedium: 1,
94
+ countWarning: 1,
95
+ countLevelHigh: 1,
96
+ countAlarm: 1,
97
+ countLevelCritical: 1,
98
+ };
99
+ expect(rankAlertCounts(one).map((entry) => entry.type)).toEqual(
100
+ ALERT_SEVERITY_PRIORITY
101
+ );
102
+ });
103
+
104
+ it('combines into the total typed as the most severe present', () => {
105
+ expect(
106
+ rankAlertCounts({countWarning: 4, countCaution: 6, countAlarm: 2}, true)
107
+ ).toEqual([{type: AlertType.Alarm, count: 12}]);
108
+ });
109
+
110
+ it('returns nothing when nothing is counted', () => {
111
+ expect(rankAlertCounts({})).toEqual([]);
112
+ expect(rankAlertCounts({countAlarm: 0}, true)).toEqual([]);
113
+ });
114
+ });
115
+
116
+ describe('alertTypeLabel', () => {
117
+ it('names every severity', () => {
118
+ for (const type of Object.values(AlertType)) {
119
+ expect(alertTypeLabel(type)).not.toBe('');
120
+ }
121
+ expect(alertTypeLabel(AlertType.LevelHigh)).toBe('High');
122
+ });
123
+ });
124
+
125
+ describe('alertCountsLabel', () => {
126
+ it('lists the ranked counts, then the shelved count', () => {
127
+ expect(
128
+ alertCountsLabel({countCaution: 6, countAlarm: 2, countWarning: 4}, 9)
129
+ ).toBe('2 Alarm, 4 Warning, 6 Caution, 9 Shelved');
130
+ });
131
+
132
+ it('is empty when nothing is counted', () => {
133
+ expect(alertCountsLabel({})).toBe('');
134
+ });
135
+ });
@@ -25,6 +25,8 @@
25
25
  * alarm-style no-ack icon.
26
26
  * - Priority: re-exports `ALERT_SEVERITY_PRIORITY`, the ordered severity list
27
27
  * (most → least severe) used to sort alerts.
28
+ * - Counts: `rankAlertCounts` orders per-severity counts and can combine
29
+ * them; `alertTypeLabel` and `alertCountsLabel` build accessible names.
28
30
  *
29
31
  * Usage:
30
32
  * ```ts
@@ -44,10 +46,12 @@
44
46
  * );
45
47
  * ```
46
48
  */
49
+ import {msg} from '@lit/localize';
47
50
  import {
48
51
  AlertType,
49
52
  ALERT_SEVERITY_PRIORITY,
50
53
  FlashingSpeed,
54
+ type AlertCounts,
51
55
  type ResolvedFlashingSpeed,
52
56
  } from './types.js';
53
57
 
@@ -234,3 +238,73 @@ export function usesAlarmNoAckIcon(type: AlertType): boolean {
234
238
  AlertType.LevelHigh,
235
239
  ].includes(type);
236
240
  }
241
+
242
+ /** One severity and its alert count, as `rankAlertCounts` returns them. */
243
+ export interface RankedAlertCount {
244
+ type: AlertType;
245
+ count: number;
246
+ }
247
+
248
+ const ALERT_COUNT_FIELD: Record<AlertType, keyof AlertCounts> = {
249
+ [AlertType.Alarm]: 'countAlarm',
250
+ [AlertType.Warning]: 'countWarning',
251
+ [AlertType.Caution]: 'countCaution',
252
+ [AlertType.LevelCritical]: 'countLevelCritical',
253
+ [AlertType.LevelHigh]: 'countLevelHigh',
254
+ [AlertType.LevelMedium]: 'countLevelMedium',
255
+ [AlertType.LevelLow]: 'countLevelLow',
256
+ [AlertType.LevelDiagnostic]: 'countLevelDiagnostic',
257
+ };
258
+
259
+ /**
260
+ * The positive counts, most severe first by `ALERT_SEVERITY_PRIORITY`.
261
+ * `combine` folds them into one entry: the total, typed as the most severe
262
+ * category present.
263
+ */
264
+ export function rankAlertCounts(
265
+ counts: AlertCounts,
266
+ combine = false
267
+ ): RankedAlertCount[] {
268
+ const ranked = ALERT_SEVERITY_PRIORITY.map((type) => ({
269
+ type,
270
+ count: counts[ALERT_COUNT_FIELD[type]] ?? 0,
271
+ })).filter((entry) => entry.count > 0);
272
+ if (!combine || ranked.length === 0) {
273
+ return ranked;
274
+ }
275
+ const total = ranked.reduce((sum, entry) => sum + entry.count, 0);
276
+ return [{type: ranked[0].type, count: total}];
277
+ }
278
+
279
+ const ALERT_TYPE_LABEL: Record<AlertType, () => string> = {
280
+ [AlertType.Alarm]: () => msg('Alarm'),
281
+ [AlertType.Warning]: () => msg('Warning'),
282
+ [AlertType.Caution]: () => msg('Caution'),
283
+ [AlertType.LevelCritical]: () => msg('Critical'),
284
+ [AlertType.LevelHigh]: () => msg('High'),
285
+ [AlertType.LevelMedium]: () => msg('Medium'),
286
+ [AlertType.LevelLow]: () => msg('Low'),
287
+ [AlertType.LevelDiagnostic]: () => msg('Diagnostic'),
288
+ };
289
+
290
+ /** Localized severity name, read at render time so a locale switch applies. */
291
+ export function alertTypeLabel(type: AlertType): string {
292
+ return ALERT_TYPE_LABEL[type]();
293
+ }
294
+
295
+ /**
296
+ * Accessible summary of the counts, e.g. "2 Alarm, 4 Warning, 9 Shelved";
297
+ * empty when nothing is counted.
298
+ */
299
+ export function alertCountsLabel(
300
+ counts: AlertCounts,
301
+ shelvedCount = 0
302
+ ): string {
303
+ const parts = rankAlertCounts(counts).map(
304
+ ({type, count}) => `${count} ${alertTypeLabel(type)}`
305
+ );
306
+ if (shelvedCount > 0) {
307
+ parts.push(`${shelvedCount} ${msg('Shelved')}`);
308
+ }
309
+ return parts.join(', ');
310
+ }
@@ -16,8 +16,7 @@
16
16
  padding-top: var(--app-components-alert-button-tall-padding-vertical);
17
17
  padding-bottom: var(--app-components-alert-button-tall-padding-vertical);
18
18
 
19
- & .visible-wrapper,
20
- & .blink {
19
+ & .silence-button .visible-wrapper {
21
20
  height: 100%;
22
21
  }
23
22
  }
@@ -27,6 +26,11 @@
27
26
  }
28
27
  }
29
28
 
29
+ obc-alert-button-item {
30
+ flex-grow: 1; /* the bell takes the width the silence button leaves */
31
+ flex-basis: var(--global-size-spacing-touch-target-min);
32
+ }
33
+
30
34
  button {
31
35
  appearance: none;
32
36
  background: none;
@@ -46,199 +50,6 @@ button {
46
50
  height: var(--global-size-spacing-icon-icon-size-regular);
47
51
  }
48
52
 
49
- .alert-button {
50
- position: relative;
51
- display: flex;
52
- justify-content: center;
53
- .type-flat & {
54
- @mixin style style=flat visibleWrapperClass=.visible-wrapper;
55
- @mixin style style=flat visibleWrapperClass=.blink;
56
- }
57
-
58
- .type-normal & .visible-wrapper,
59
- .type-enhanced & .visible-wrapper {
60
- width: 100%;
61
- }
62
-
63
- .type-normal & {
64
- @mixin style style=normal visibleWrapperClass=.visible-wrapper;
65
- @mixin style style=normal visibleWrapperClass=.blink;
66
- }
67
-
68
- .type-enhanced.alert-type-alarm & {
69
- @mixin style style=normal visibleWrapperClass=.blink;
70
- @mixin style style=alarm visibleWrapperClass=.visible-wrapper;
71
- & .visible-wrapper {
72
- color: var(--on-alarm-active-color);
73
- }
74
- }
75
-
76
- .type-enhanced.alert-type-warning & {
77
- @mixin style style=normal visibleWrapperClass=.blink;
78
- @mixin style style=warning visibleWrapperClass=.visible-wrapper;
79
- & .visible-wrapper {
80
- color: var(--on-warning-active-color);
81
- }
82
- }
83
-
84
- .type-enhanced.alert-type-caution & {
85
- @mixin style style=normal visibleWrapperClass=.blink;
86
- @mixin style style=caution visibleWrapperClass=.visible-wrapper;
87
- & .visible-wrapper {
88
- color: var(--on-caution-active-color);
89
- }
90
- }
91
-
92
- .type-enhanced.alert-type-level-critical & {
93
- @mixin style style=normal visibleWrapperClass=.blink;
94
- @mixin style style=critical visibleWrapperClass=.visible-wrapper;
95
- & .visible-wrapper {
96
- color: var(--on-critical-active-color);
97
- }
98
- }
99
-
100
- .type-enhanced.alert-type-level-high & {
101
- @mixin style style=normal visibleWrapperClass=.blink;
102
- @mixin style style=alarm visibleWrapperClass=.visible-wrapper;
103
- & .visible-wrapper {
104
- color: var(--on-alarm-active-color);
105
- }
106
- }
107
-
108
- .type-enhanced.alert-type-level-medium & {
109
- @mixin style style=normal visibleWrapperClass=.blink;
110
- @mixin style style=warning visibleWrapperClass=.visible-wrapper;
111
- & .visible-wrapper {
112
- color: var(--on-warning-active-color);
113
- }
114
- }
115
-
116
- .type-enhanced.alert-type-level-low & {
117
- @mixin style style=normal visibleWrapperClass=.blink;
118
- @mixin style style=caution visibleWrapperClass=.visible-wrapper;
119
- & .visible-wrapper {
120
- color: var(--on-caution-active-color);
121
- }
122
- }
123
-
124
- .type-enhanced.alert-type-level-diagnostic & {
125
- @mixin style style=normal visibleWrapperClass=.blink;
126
- @mixin style style=notification visibleWrapperClass=.visible-wrapper;
127
- & .visible-wrapper {
128
- color: var(--on-notification-active-color);
129
- }
130
- }
131
-
132
- & .visible-wrapper,
133
- & .blink {
134
- border-radius: var(--ui-components-button-border-radius-top-left)
135
- var(--ui-components-button-border-radius-top-right)
136
- var(--ui-components-button-border-radius-bottom-right)
137
- var(--ui-components-button-border-radius-bottom-left);
138
- height: var(--ui-components-icon-button-visual-target-size);
139
- min-width: var(--ui-components-icon-button-visual-target-size);
140
- display: flex;
141
- justify-content: center;
142
- align-items: center;
143
- color: var(--on-normal-neutral-color);
144
- @mixin font-button;
145
-
146
- .has-silence & {
147
- border-top-right-radius: 0;
148
- border-bottom-right-radius: 0;
149
- }
150
- }
151
-
152
- .type-flat & .visible-wrapper {
153
- color: var(--on-flat-neutral-color);
154
- }
155
-
156
- & .blink {
157
- position: absolute;
158
- top: 0;
159
- right: 0;
160
- bottom: 0;
161
- left: 0;
162
- margin: auto;
163
-
164
- .type-flat & {
165
- width: min-content;
166
- }
167
- }
168
- }
169
-
170
- .flash-fast .alert-button {
171
- .visible-wrapper {
172
- opacity: var(--flash-fast-on);
173
- }
174
-
175
- .blink {
176
- opacity: var(--flash-fast-off);
177
- }
178
- }
179
-
180
- .flash-slow .alert-button {
181
- .visible-wrapper {
182
- opacity: var(--flash-slow-on);
183
- }
184
-
185
- .blink {
186
- opacity: var(--flash-slow-off);
187
- }
188
- }
189
-
190
- .flash-very-slow .alert-button {
191
- .visible-wrapper {
192
- opacity: var(--flash-very-slow-on);
193
- }
194
-
195
- .blink {
196
- opacity: var(--flash-very-slow-off);
197
- }
198
- }
199
-
200
- .counter .alert-button .visible-wrapper,
201
- .counter .alert-button .blink {
202
- padding: 0px var(--ui-components-button-padding-horizontal);
203
- gap: var(--app-components-alert-button-item-counter-spacing);
204
- }
205
-
206
- .alert-type-alarm {
207
- --icon-fill: var(--alarm-enabled-background-color);
208
- --icon-stroke: var(--alarm-pressed-border-color);
209
- }
210
-
211
- .alert-type-warning {
212
- --icon-fill: var(--warning-enabled-background-color);
213
- --icon-stroke: var(--warning-pressed-border-color);
214
- }
215
-
216
- .alert-type-caution,
217
- .alert-type-level-low {
218
- --icon-fill: var(--caution-enabled-background-color);
219
- --icon-stroke: var(--caution-pressed-border-color);
220
- }
221
-
222
- .alert-type-level-critical {
223
- --icon-fill: var(--alert-critical-color);
224
- --icon-stroke: var(--alert-critical-outline-color);
225
- }
226
-
227
- .alert-type-level-high {
228
- --icon-fill: var(--alarm-enabled-background-color);
229
- --icon-stroke: var(--alarm-pressed-border-color);
230
- }
231
-
232
- .alert-type-level-medium {
233
- --icon-fill: var(--warning-enabled-background-color);
234
- --icon-stroke: var(--warning-pressed-border-color);
235
- }
236
-
237
- .alert-type-level-diagnostic {
238
- --icon-fill: var(--alert-diagnostic-color);
239
- --icon-stroke: var(--alert-diagnostic-outline-color);
240
- }
241
-
242
53
  .silence-button {
243
54
  @mixin style style=normal visibleWrapperClass=.visible-wrapper;
244
55
  color: var(--on-normal-neutral-color);
@@ -248,7 +59,7 @@ button {
248
59
  height: var(--ui-components-icon-button-visual-target-size);
249
60
  border-radius: 0 var(--ui-components-button-border-radius-top-right)
250
61
  var(--ui-components-button-border-radius-bottom-right) 0;
251
- border-left: 0;
62
+ border-left: 0; /* the item's right border is the divider */
252
63
  display: flex;
253
64
  justify-content: center;
254
65
  align-items: center;
@@ -1,99 +1,150 @@
1
1
  import {describe, expect, it} from 'vitest';
2
- import './alert-button.js';
3
- import {ObcAlertButton} from './alert-button.js';
4
2
  import {render} from 'vitest-browser-lit';
5
- import {html} from 'lit';
3
+ import {html, type TemplateResult} from 'lit';
4
+ import './alert-button.js';
5
+ import {ObcAlertButton, ObcAlertButtonType} from './alert-button.js';
6
+ import type {ObcAlertButtonItem} from '../alert-button-item/alert-button-item.js';
6
7
  import {AlertType, FlashingSpeed} from '../../types.js';
7
8
 
8
- function durations(el: HTMLElement): number[] {
9
- return el
10
- .getAnimations()
11
- .map((a) => (a.effect as KeyframeEffect).getTiming().duration as number);
9
+ async function setup(
10
+ template: TemplateResult = html`<obc-alert-button
11
+ .nAlerts=${3}
12
+ .alertType=${AlertType.Alarm}
13
+ counter
14
+ blinking
15
+ ></obc-alert-button>`
16
+ ) {
17
+ const screen = render(template);
18
+ const el = screen.container.querySelector(
19
+ 'obc-alert-button'
20
+ ) as ObcAlertButton;
21
+ await el.updateComplete;
22
+ const item = el.shadowRoot!.querySelector(
23
+ 'obc-alert-button-item'
24
+ ) as ObcAlertButtonItem;
25
+ await item.updateComplete;
26
+ return {el, item};
12
27
  }
13
28
 
14
- describe('obc-alert-button flashing lifecycle', () => {
15
- async function setup(
16
- props: Partial<
17
- Pick<
18
- ObcAlertButton,
19
- 'nAlerts' | 'alertType' | 'blinking' | 'flashingSpeed'
20
- >
21
- > = {}
22
- ) {
23
- const screen = render(
29
+ describe('obc-alert-button', () => {
30
+ it('forwards the alert state to its item', async () => {
31
+ const {item} = await setup(
24
32
  html`<obc-alert-button
25
- .nAlerts=${props.nAlerts ?? 1}
26
- .alertType=${props.alertType ?? AlertType.Alarm}
27
- .blinking=${props.blinking ?? true}
28
- .flashingSpeed=${props.flashingSpeed ?? FlashingSpeed.Default}
33
+ .nAlerts=${3}
34
+ .alertType=${AlertType.Warning}
35
+ .type=${ObcAlertButtonType.Enhanced}
36
+ .flashingSpeed=${FlashingSpeed.Slow}
37
+ counter
38
+ blinking
39
+ large
29
40
  ></obc-alert-button>`
30
41
  );
31
- const el = screen.container.querySelector(
32
- 'obc-alert-button'
33
- ) as ObcAlertButton;
34
- await el.updateComplete;
35
- return el;
36
- }
37
42
 
38
- it('flashes fast for a blinking alarm button with alerts', async () => {
39
- const el = await setup();
40
-
41
- expect(durations(el)).toEqual([800]);
42
- expect(el.shadowRoot!.querySelector('.wrapper.flash-fast')).not.toBeNull();
43
+ expect([
44
+ item.type,
45
+ item.alertType,
46
+ item.nAlerts,
47
+ item.counter,
48
+ item.blinking,
49
+ item.flashingSpeed,
50
+ item.fillHeight,
51
+ ]).toEqual([
52
+ ObcAlertButtonType.Enhanced,
53
+ AlertType.Warning,
54
+ 3,
55
+ true,
56
+ true,
57
+ FlashingSpeed.Slow,
58
+ true,
59
+ ]);
43
60
  });
44
61
 
45
- it('flashes slow for a warning and never for caution', async () => {
46
- expect(durations(await setup({alertType: AlertType.Warning}))).toEqual([
47
- 1600,
62
+ it('forwards the global counter and drops it in flat mode', async () => {
63
+ const counts = {countAlarm: 1, countWarning: 10, countCaution: 15};
64
+ const {el, item} = await setup(
65
+ html`<obc-alert-button
66
+ globalCounter
67
+ .nAlerts=${26}
68
+ .counts=${counts}
69
+ .shelvedCount=${9}
70
+ ></obc-alert-button>`
71
+ );
72
+ expect([item.globalCounter, item.counts, item.shelvedCount]).toEqual([
73
+ true,
74
+ counts,
75
+ 9,
48
76
  ]);
49
- expect(durations(await setup({alertType: AlertType.Caution}))).toEqual([]);
50
- });
51
77
 
52
- it('keeps blinking as the gate', async () => {
53
- expect(durations(await setup({blinking: false}))).toEqual([]);
54
- expect(durations(await setup({nAlerts: 0}))).toEqual([]);
55
- });
78
+ el.flatMaxBreakpointPx = window.innerWidth + 1;
79
+ await el.updateComplete;
80
+ await item.updateComplete;
56
81
 
57
- it('honours an explicit flashingSpeed', async () => {
58
- expect(
59
- durations(await setup({flashingSpeed: FlashingSpeed.VerySlow}))
60
- ).toEqual([3200]);
61
- expect(
62
- durations(await setup({flashingSpeed: FlashingSpeed.Fixed}))
63
- ).toEqual([]);
82
+ expect([item.type, item.globalCounter]).toEqual([
83
+ ObcAlertButtonType.Flat,
84
+ false,
85
+ ]);
64
86
  });
65
87
 
66
- it('cancels the animation on disconnect', async () => {
67
- const el = await setup();
68
-
69
- el.parentElement!.removeChild(el);
88
+ it('passes the flat breakpoint on as the item type', async () => {
89
+ const {item} = await setup(
90
+ html`<obc-alert-button
91
+ .nAlerts=${3}
92
+ .flatMaxBreakpointPx=${window.innerWidth + 1}
93
+ ></obc-alert-button>`
94
+ );
70
95
 
71
- expect(el.getAnimations()).toHaveLength(0);
96
+ expect(item.type).toBe(ObcAlertButtonType.Flat);
72
97
  });
73
98
 
74
- it('resumes flashing after disconnect and reconnect', async () => {
75
- const el = await setup();
76
- const parent = el.parentElement!;
77
-
78
- parent.removeChild(el);
99
+ it('flashes through its item', async () => {
100
+ const {el, item} = await setup();
101
+ expect(item.getAnimations()).toHaveLength(1);
79
102
  expect(el.getAnimations()).toHaveLength(0);
80
103
 
81
- // Reconnect without touching any property. firstUpdated() will not run
82
- // again, so this only passes if the controller re-syncs on connect.
83
- parent.appendChild(el);
104
+ el.blinking = false;
84
105
  await el.updateComplete;
106
+ await item.updateComplete;
85
107
 
86
- expect(durations(el)).toEqual([800]);
108
+ expect(item.getAnimations()).toHaveLength(0);
87
109
  });
88
110
 
89
- it('does not accumulate animations across repeated updates', async () => {
90
- const el = await setup();
111
+ it('joins the item to the silence button', async () => {
112
+ const {el, item} = await setup(
113
+ html`<obc-alert-button
114
+ .nAlerts=${3}
115
+ showSilenceButton
116
+ ></obc-alert-button>`
117
+ );
118
+ expect(item.hasAttribute('data-group-item-not-last')).toBe(true);
91
119
 
92
- el.large = true;
93
- await el.updateComplete;
94
- el.counter = true;
120
+ el.showSilenceButton = false;
95
121
  await el.updateComplete;
96
122
 
97
- expect(el.getAnimations()).toHaveLength(1);
123
+ expect(item.hasAttribute('data-group-item-not-last')).toBe(false);
124
+ });
125
+
126
+ it('fires click-alert when the item is clicked', async () => {
127
+ const {el, item} = await setup();
128
+ let fired = 0;
129
+ el.addEventListener('click-alert', () => fired++);
130
+
131
+ item.shadowRoot!.querySelector('button')!.click();
132
+
133
+ expect(fired).toBe(1);
134
+ });
135
+
136
+ it('names the silence button', async () => {
137
+ const {el} = await setup(
138
+ html`<obc-alert-button
139
+ .nAlerts=${3}
140
+ showSilenceButton
141
+ ></obc-alert-button>`
142
+ );
143
+
144
+ expect(
145
+ el
146
+ .shadowRoot!.querySelector('.silence-button')!
147
+ .getAttribute('aria-label')
148
+ ).toBe('Silence');
98
149
  });
99
150
  });
@@ -31,6 +31,8 @@ const meta: Meta<typeof ObcAlertButton> = {
31
31
  options: Object.values(FlashingSpeed),
32
32
  control: {type: 'select'},
33
33
  },
34
+ counts: {control: {type: 'object'}},
35
+ shelvedCount: {control: {type: 'number', min: 0}},
34
36
  width: {control: {type: 'range', min: 64, max: 1028, step: 1}},
35
37
  },
36
38
  decorators: [widthDecorator],
@@ -254,3 +256,25 @@ export const NormalLevelDiagnostic: Story = {
254
256
  nAlerts: 1,
255
257
  },
256
258
  };
259
+
260
+ const GLOBAL_COUNTS = {countAlarm: 1, countWarning: 10, countCaution: 15};
261
+
262
+ export const GlobalCounter: Story = {
263
+ args: {
264
+ globalCounter: true,
265
+ nAlerts: 26,
266
+ counts: GLOBAL_COUNTS,
267
+ width: 168,
268
+ },
269
+ };
270
+
271
+ export const GlobalCounterShelvedSilence: Story = {
272
+ args: {
273
+ globalCounter: true,
274
+ nAlerts: 26,
275
+ counts: GLOBAL_COUNTS,
276
+ shelvedCount: 9,
277
+ showSilenceButton: true,
278
+ width: 244,
279
+ },
280
+ };