@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.96 → 2.0.0-next.97

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 (158) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +19 -9
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +314 -132
  4. package/dist/ar/poi/poi.d.ts +4 -2
  5. package/dist/ar/poi/poi.d.ts.map +1 -1
  6. package/dist/ar/poi/poi.js.map +1 -1
  7. package/dist/ar/poi-layer/poi-layer.d.ts +1 -0
  8. package/dist/ar/poi-layer/poi-layer.d.ts.map +1 -1
  9. package/dist/ar/poi-layer/poi-layer.js.map +1 -1
  10. package/dist/ar/poi-object/poi-object-aton.d.ts +1 -0
  11. package/dist/ar/poi-object/poi-object-aton.d.ts.map +1 -1
  12. package/dist/ar/poi-object/poi-object-aton.js.map +1 -1
  13. package/dist/automation/automation-badge/automation-badge.d.ts +2 -0
  14. package/dist/automation/automation-badge/automation-badge.d.ts.map +1 -1
  15. package/dist/automation/automation-badge/automation-badge.js.map +1 -1
  16. package/dist/automation/automation-button/automation-button.d.ts +9 -0
  17. package/dist/automation/automation-button/automation-button.d.ts.map +1 -1
  18. package/dist/automation/automation-button/automation-button.js.map +1 -1
  19. package/dist/automation/automation-input-modal/automation-input-modal.d.ts +4 -0
  20. package/dist/automation/automation-input-modal/automation-input-modal.d.ts.map +1 -1
  21. package/dist/automation/automation-input-modal/automation-input-modal.js.map +1 -1
  22. package/dist/building-blocks/alert-list/alert-list.d.ts +4 -0
  23. package/dist/building-blocks/alert-list/alert-list.d.ts.map +1 -1
  24. package/dist/building-blocks/alert-list/alert-list.js.map +1 -1
  25. package/dist/building-blocks/bar-horizontal/bar-horizontal.d.ts +2 -0
  26. package/dist/building-blocks/bar-horizontal/bar-horizontal.d.ts.map +1 -1
  27. package/dist/building-blocks/bar-horizontal/bar-horizontal.js.map +1 -1
  28. package/dist/building-blocks/bar-vertical/bar-vertical.d.ts +2 -0
  29. package/dist/building-blocks/bar-vertical/bar-vertical.d.ts.map +1 -1
  30. package/dist/building-blocks/bar-vertical/bar-vertical.js.map +1 -1
  31. package/dist/components/advice-floating-item/advice-floating-item.d.ts +2 -4
  32. package/dist/components/advice-floating-item/advice-floating-item.d.ts.map +1 -1
  33. package/dist/components/advice-floating-item/advice-floating-item.js.map +1 -1
  34. package/dist/components/advice-message-item/advice-message-item.d.ts +6 -19
  35. package/dist/components/advice-message-item/advice-message-item.d.ts.map +1 -1
  36. package/dist/components/advice-message-item/advice-message-item.js.map +1 -1
  37. package/dist/components/alert-floating-item/alert-floating-item.d.ts +12 -0
  38. package/dist/components/alert-floating-item/alert-floating-item.d.ts.map +1 -1
  39. package/dist/components/alert-floating-item/alert-floating-item.js.map +1 -1
  40. package/dist/components/alert-menu/alert-menu.d.ts +6 -20
  41. package/dist/components/alert-menu/alert-menu.d.ts.map +1 -1
  42. package/dist/components/alert-menu/alert-menu.js.map +1 -1
  43. package/dist/components/alert-menu-item/alert-menu-item.d.ts +4 -0
  44. package/dist/components/alert-menu-item/alert-menu-item.d.ts.map +1 -1
  45. package/dist/components/alert-menu-item/alert-menu-item.js.map +1 -1
  46. package/dist/components/chat-message/chat-message.d.ts +3 -0
  47. package/dist/components/chat-message/chat-message.d.ts.map +1 -1
  48. package/dist/components/chat-message/chat-message.js.map +1 -1
  49. package/dist/components/elevated-card-radio/elevated-card-radio.d.ts +2 -6
  50. package/dist/components/elevated-card-radio/elevated-card-radio.d.ts.map +1 -1
  51. package/dist/components/elevated-card-radio/elevated-card-radio.js.map +1 -1
  52. package/dist/components/keyboard-numeric/keyboard-numeric.d.ts +6 -0
  53. package/dist/components/keyboard-numeric/keyboard-numeric.d.ts.map +1 -1
  54. package/dist/components/keyboard-numeric/keyboard-numeric.js.map +1 -1
  55. package/dist/components/notification-floating-item/notification-floating-item.d.ts +2 -4
  56. package/dist/components/notification-floating-item/notification-floating-item.d.ts.map +1 -1
  57. package/dist/components/notification-floating-item/notification-floating-item.js.map +1 -1
  58. package/dist/components/sequence-card/sequence-card.d.ts +9 -0
  59. package/dist/components/sequence-card/sequence-card.d.ts.map +1 -1
  60. package/dist/components/sequence-card/sequence-card.js.map +1 -1
  61. package/dist/components/sequence-item/sequence-item.d.ts +5 -0
  62. package/dist/components/sequence-item/sequence-item.d.ts.map +1 -1
  63. package/dist/components/sequence-item/sequence-item.js.map +1 -1
  64. package/dist/components/sequence-modal/sequence-modal.d.ts +4 -0
  65. package/dist/components/sequence-modal/sequence-modal.d.ts.map +1 -1
  66. package/dist/components/sequence-modal/sequence-modal.js.map +1 -1
  67. package/dist/components/sequence-step/sequence-step.d.ts.map +1 -1
  68. package/dist/components/sequence-step/sequence-step.js.map +1 -1
  69. package/dist/components/slider-double/slider-double.d.ts +4 -8
  70. package/dist/components/slider-double/slider-double.d.ts.map +1 -1
  71. package/dist/components/slider-double/slider-double.js.map +1 -1
  72. package/dist/components/status-indicator/status-indicator.d.ts +1 -0
  73. package/dist/components/status-indicator/status-indicator.d.ts.map +1 -1
  74. package/dist/components/status-indicator/status-indicator.js.map +1 -1
  75. package/dist/components/system-menu/system-menu.d.ts +2 -0
  76. package/dist/components/system-menu/system-menu.d.ts.map +1 -1
  77. package/dist/components/system-menu/system-menu.js.map +1 -1
  78. package/dist/components/table-header-item/table-header-item.d.ts +2 -0
  79. package/dist/components/table-header-item/table-header-item.d.ts.map +1 -1
  80. package/dist/components/table-header-item/table-header-item.js.map +1 -1
  81. package/dist/components/tag/tag.d.ts +1 -0
  82. package/dist/components/tag/tag.d.ts.map +1 -1
  83. package/dist/components/tag/tag.js.map +1 -1
  84. package/dist/components/toggletip/toggletip.d.ts +4 -4
  85. package/dist/components/toggletip/toggletip.js.map +1 -1
  86. package/dist/components/user-menu/user-menu.d.ts +1 -0
  87. package/dist/components/user-menu/user-menu.d.ts.map +1 -1
  88. package/dist/components/user-menu/user-menu.js.map +1 -1
  89. package/dist/integration-systems/integration-app-bar/integration-app-bar.d.ts +1 -0
  90. package/dist/integration-systems/integration-app-bar/integration-app-bar.d.ts.map +1 -1
  91. package/dist/integration-systems/integration-app-bar/integration-app-bar.js.map +1 -1
  92. package/dist/integration-systems/integration-bar/integration-bar.d.ts +0 -1
  93. package/dist/integration-systems/integration-bar/integration-bar.d.ts.map +1 -1
  94. package/dist/integration-systems/integration-bar/integration-bar.js.map +1 -1
  95. package/dist/integration-systems/integration-bar-dropdown/integration-bar-dropdown.d.ts +9 -9
  96. package/dist/integration-systems/integration-bar-dropdown/integration-bar-dropdown.js.map +1 -1
  97. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.d.ts +1 -0
  98. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.d.ts.map +1 -1
  99. package/dist/integration-systems/integration-fleet-button/integration-fleet-button.js.map +1 -1
  100. package/dist/integration-systems/integration-tabs/integration-tabs.d.ts +1 -0
  101. package/dist/integration-systems/integration-tabs/integration-tabs.d.ts.map +1 -1
  102. package/dist/integration-systems/integration-tabs/integration-tabs.js.map +1 -1
  103. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts +0 -1
  104. package/dist/navigation-instruments/compass-sector/compass-sector.d.ts.map +1 -1
  105. package/dist/navigation-instruments/compass-sector/compass-sector.js.map +1 -1
  106. package/dist/navigation-instruments/watch/tickmark.d.ts.map +1 -1
  107. package/dist/navigation-instruments/watch/tickmark.js +17 -7
  108. package/dist/navigation-instruments/watch/tickmark.js.map +1 -1
  109. package/dist/navigation-instruments/watch/watch.d.ts.map +1 -1
  110. package/dist/navigation-instruments/watch/watch.js +2 -2
  111. package/dist/navigation-instruments/watch/watch.js.map +1 -1
  112. package/dist/navigation-instruments/wind-propulsion/wind-propulsion.d.ts +0 -1
  113. package/dist/navigation-instruments/wind-propulsion/wind-propulsion.d.ts.map +1 -1
  114. package/dist/navigation-instruments/wind-propulsion/wind-propulsion.js.map +1 -1
  115. package/dist/pages/alert-detail-page/alert-detail-page.d.ts.map +1 -1
  116. package/dist/pages/alert-detail-page/alert-detail-page.js.map +1 -1
  117. package/package.json +3 -2
  118. package/script/check-slot-event-docs.ts +353 -0
  119. package/src/ar/poi/poi.ts +4 -2
  120. package/src/ar/poi-layer/poi-layer.ts +1 -0
  121. package/src/ar/poi-object/poi-object-aton.ts +1 -0
  122. package/src/automation/automation-badge/automation-badge.ts +2 -0
  123. package/src/automation/automation-button/automation-button.ts +9 -0
  124. package/src/automation/automation-input-modal/automation-input-modal.ts +4 -0
  125. package/src/building-blocks/alert-list/alert-list.ts +4 -0
  126. package/src/building-blocks/bar-horizontal/bar-horizontal.ts +2 -0
  127. package/src/building-blocks/bar-vertical/bar-vertical.ts +2 -0
  128. package/src/components/advice-floating-item/advice-floating-item.ts +2 -4
  129. package/src/components/advice-message-item/advice-message-item.ts +6 -19
  130. package/src/components/alert-floating-item/alert-floating-item.ts +12 -0
  131. package/src/components/alert-menu/alert-menu.ts +6 -20
  132. package/src/components/alert-menu-item/alert-menu-item.ts +4 -0
  133. package/src/components/chat-message/chat-message.ts +3 -0
  134. package/src/components/elevated-card-radio/elevated-card-radio.ts +2 -6
  135. package/src/components/keyboard-numeric/keyboard-numeric.ts +6 -0
  136. package/src/components/notification-floating-item/notification-floating-item.ts +2 -4
  137. package/src/components/sequence-card/sequence-card.ts +9 -0
  138. package/src/components/sequence-item/sequence-item.ts +5 -0
  139. package/src/components/sequence-modal/sequence-modal.ts +4 -0
  140. package/src/components/sequence-step/sequence-step.ts +3 -0
  141. package/src/components/slider-double/slider-double.ts +4 -8
  142. package/src/components/status-indicator/status-indicator.ts +1 -0
  143. package/src/components/system-menu/system-menu.ts +2 -0
  144. package/src/components/table-header-item/table-header-item.ts +2 -0
  145. package/src/components/tag/tag.ts +1 -0
  146. package/src/components/toggletip/toggletip.ts +4 -4
  147. package/src/components/user-menu/user-menu.ts +1 -0
  148. package/src/integration-systems/integration-app-bar/integration-app-bar.ts +1 -0
  149. package/src/integration-systems/integration-bar/integration-bar.ts +0 -1
  150. package/src/integration-systems/integration-bar-dropdown/integration-bar-dropdown.ts +9 -9
  151. package/src/integration-systems/integration-fleet-button/integration-fleet-button.ts +1 -0
  152. package/src/integration-systems/integration-tabs/integration-tabs.ts +1 -0
  153. package/src/navigation-instruments/compass-sector/compass-sector.ts +0 -1
  154. package/src/navigation-instruments/watch/tickmark.spec.ts +99 -0
  155. package/src/navigation-instruments/watch/tickmark.ts +21 -8
  156. package/src/navigation-instruments/watch/watch.ts +7 -2
  157. package/src/navigation-instruments/wind-propulsion/wind-propulsion.ts +0 -1
  158. package/src/pages/alert-detail-page/alert-detail-page.ts +7 -0
@@ -0,0 +1,353 @@
1
+ /**
2
+ * @module SlotEventDocAudit
3
+ * @description
4
+ * Audits every registered web component (`@customElement`) so that its rendered
5
+ * `<slot>` elements and dispatched custom events are declared with matching
6
+ * `@slot` / `@fires` JSDoc tags on the class.
7
+ *
8
+ * Why this matters: `custom-elements.json` is generated by `cem analyze`, which
9
+ * discovers slots and events **only** from `@slot` / `@fires` JSDoc tags — never
10
+ * from the template or from `dispatchEvent(...)` calls. If a component renders a
11
+ * `<slot name="leading-icon">` but forgets the `@slot leading-icon` tag, the slot
12
+ * is invisible to the manifest and therefore to every downstream consumer (the
13
+ * framework wrappers, IDE autocomplete, Storybook autodocs, code playgrounds).
14
+ * A `@slot`/`@fires` tag that has no counterpart in the template/code produces the
15
+ * opposite problem: a phantom control that does nothing. See issue #1033.
16
+ *
17
+ * The checks are intentionally conservative to stay false-positive-free in CI:
18
+ *
19
+ * - **Missing slot (error):** a *static* `<slot name="literal">` or a *bare*
20
+ * default `<slot>` with no matching `@slot` tag. Slots rendered by an inherited
21
+ * base class live in the base file, so subclasses are never falsely flagged.
22
+ * - **Missing event (error):** a literal `new CustomEvent('name')` /
23
+ * `new Event('name')` with no matching `@fires`/`@event name` tag.
24
+ * - **Phantom slot (error):** a `@slot name` tag with no matching static
25
+ * `<slot name="name">` element — only evaluated for files that `extends
26
+ * LitElement` directly and contain no dynamic `<slot name="${…}">`, so
27
+ * inheritance- and dynamic-name-based false positives are skipped. A
28
+ * `slot="name"` *projection attribute* in the template does NOT suppress this
29
+ * (it exposes nothing); only a genuine imperative read of the consumer's
30
+ * children (e.g. `getAttribute('slot') === 'name'`) counts as a real slot.
31
+ * - **Empty description (warning):** a `@slot`/`@fires` tag with only a name and
32
+ * no descriptive text. These become blank cells in the manifest / Storybook
33
+ * controls; warnings do not fail CI.
34
+ *
35
+ * There is deliberately no phantom-`@fires` *error*: unlike slots (which must be
36
+ * a `<slot>` element in the component's own shadow DOM), a documented event may
37
+ * legitimately originate elsewhere — a native event bubbling from an inner
38
+ * element (`click`, `blur`), or an event re-fired by a child — so "documented
39
+ * but not locally dispatched" is not, on its own, a defect.
40
+ *
41
+ * Dynamic slot names (`<slot name="${expr}">` or `<slot name="pre-${id}-post">`)
42
+ * are skipped for the missing-slot check because their concrete names cannot be
43
+ * known statically; document them with a placeholder tag such as
44
+ * `@slot tab-<id>-icon` (see .cursor/rules/comments.mdc § Structured-tag rules).
45
+ *
46
+ * Usage:
47
+ * ```bash
48
+ * npm run lint:slots
49
+ * tsx script/check-slot-event-docs.ts
50
+ * ```
51
+ *
52
+ * Exits with code 1 when any error-level finding is present, suitable for CI.
53
+ *
54
+ * Note: standalone script; exports nothing. Regex-based like the sibling
55
+ * `check-css-*` audits — it does not build a full AST.
56
+ */
57
+
58
+ import fs from 'fs';
59
+ import path from 'path';
60
+ import {globby} from 'globby';
61
+
62
+ interface Finding {
63
+ file: string;
64
+ message: string;
65
+ }
66
+
67
+ /** Remove block and line comments so template scans ignore commented-out code. */
68
+ function stripComments(source: string): string {
69
+ return source
70
+ .replace(/\/\*[\s\S]*?\*\//g, '')
71
+ .replace(/(^|[^:])\/\/[^\n]*/g, '$1');
72
+ }
73
+
74
+ /** Static exposed slot names (`<slot name="literal">`, no interpolation). */
75
+ function findStaticSlots(code: string): Set<string> {
76
+ const slots = new Set<string>();
77
+ const re = /<slot\b([^>]*)>/g;
78
+ let m: RegExpExecArray | null;
79
+ while ((m = re.exec(code)) !== null) {
80
+ const attrs = m[1];
81
+ const nameMatch = /\bname\s*=\s*["']([^"']*)["']/.exec(attrs);
82
+ if (!nameMatch) continue; // bare/default slot handled separately
83
+ const name = nameMatch[1];
84
+ if (name.includes('${')) continue; // dynamic — cannot resolve statically
85
+ slots.add(name);
86
+ }
87
+ return slots;
88
+ }
89
+
90
+ /** True when the template renders a bare default `<slot>` (no `name` attribute). */
91
+ function hasDefaultSlot(code: string): boolean {
92
+ const re = /<slot\b([^>]*)>/g;
93
+ let m: RegExpExecArray | null;
94
+ while ((m = re.exec(code)) !== null) {
95
+ if (!/\bname\s*=/.test(m[1])) return true;
96
+ }
97
+ return false;
98
+ }
99
+
100
+ /** True when any `<slot name="${…}">` uses an interpolated (dynamic) name. */
101
+ function hasDynamicSlot(code: string): boolean {
102
+ const re = /<slot\b([^>]*)>/g;
103
+ let m: RegExpExecArray | null;
104
+ while ((m = re.exec(code)) !== null) {
105
+ const nameMatch = /\bname\s*=\s*(?:["'][^"']*["']|`[^`]*`|[^\s>]+)/.exec(
106
+ m[1]
107
+ );
108
+ if (nameMatch && nameMatch[0].includes('${')) return true;
109
+ }
110
+ return false;
111
+ }
112
+
113
+ /**
114
+ * Literal event names the host itself dispatches:
115
+ * `this.dispatchEvent(new CustomEvent('name'…))`. Restricted to `this.` so
116
+ * events re-dispatched on inner child elements (e.g. a native `<input>`) are
117
+ * not mistaken for the component's own public API.
118
+ */
119
+ function findDispatchedEvents(code: string): Set<string> {
120
+ const events = new Set<string>();
121
+ const re =
122
+ /this\.dispatchEvent\(\s*new\s+(?:Custom)?Event\s*\(\s*['"]([^'"]+)['"]/g;
123
+ let m: RegExpExecArray | null;
124
+ while ((m = re.exec(code)) !== null) events.add(m[1]);
125
+ return events;
126
+ }
127
+
128
+ /** Documented slot names from `@slot name` tags (`-` denotes the default slot). */
129
+ function findDocumentedSlots(source: string): {
130
+ names: Set<string>;
131
+ hasDefault: boolean;
132
+ } {
133
+ const names = new Set<string>();
134
+ let hasDefault = false;
135
+ const re = /@slot\s+(\S+)/g;
136
+ let m: RegExpExecArray | null;
137
+ while ((m = re.exec(source)) !== null) {
138
+ if (m[1] === '-') hasDefault = true;
139
+ else names.add(m[1]);
140
+ }
141
+ return {names, hasDefault};
142
+ }
143
+
144
+ /** Documented event names from `@fires name` / `@event name` tags. */
145
+ function findDocumentedEvents(source: string): Set<string> {
146
+ const names = new Set<string>();
147
+ // Accept both tag orders: `@fires name {Type}` and `@fires {Type} name`.
148
+ // The optional leading `{Type}` may contain one level of nested braces,
149
+ // e.g. `{CustomEvent<{date: Date}>}`.
150
+ const re = /@(?:fires|event)\s+(?:\{(?:[^{}]|\{[^{}]*\})*\}\s+)?(\S+)/g;
151
+ let m: RegExpExecArray | null;
152
+ while ((m = re.exec(source)) !== null) names.add(m[1]);
153
+ return names;
154
+ }
155
+
156
+ interface TagDoc {
157
+ name: string;
158
+ hasDescription: boolean;
159
+ }
160
+
161
+ /** A plausible slot/event name (identifier or kebab-case), or `-` for default. */
162
+ function isValidTagName(name: string): boolean {
163
+ return name === '-' || /^[A-Za-z][\w-]*$/.test(name);
164
+ }
165
+
166
+ /** Parse `@slot` tags into name + whether descriptive text follows. */
167
+ function parseSlotTags(source: string): TagDoc[] {
168
+ const out: TagDoc[] = [];
169
+ const re = /@slot[ \t]+(\S+)([^\n]*)/g;
170
+ let m: RegExpExecArray | null;
171
+ while ((m = re.exec(source)) !== null) {
172
+ if (!isValidTagName(m[1])) continue;
173
+ const rest = m[2].replace(/^\s*-\s*/, '').trim();
174
+ out.push({name: m[1], hasDescription: rest.length > 0});
175
+ }
176
+ return out;
177
+ }
178
+
179
+ /**
180
+ * Parse `@fires` / `@event` tags into name + whether descriptive text follows,
181
+ * tolerating a leading or trailing `{Type}` (with one level of nested braces).
182
+ */
183
+ function parseEventTags(source: string): TagDoc[] {
184
+ const out: TagDoc[] = [];
185
+ const type = '\\{(?:[^{}]|\\{[^{}]*\\})*\\}';
186
+ const re = new RegExp(
187
+ `@(?:fires|event)[ \\t]+(?:${type}[ \\t]+)?(\\S+)([^\\n]*)`,
188
+ 'g'
189
+ );
190
+ let m: RegExpExecArray | null;
191
+ while ((m = re.exec(source)) !== null) {
192
+ if (!isValidTagName(m[1])) continue;
193
+ const rest = m[2]
194
+ .replace(new RegExp(`^\\s*${type}\\s*`), '')
195
+ .replace(/^\s*-\s*/, '')
196
+ .trim();
197
+ out.push({name: m[1], hasDescription: rest.length > 0});
198
+ }
199
+ return out;
200
+ }
201
+
202
+ /** Whether the class extends `LitElement` directly (not a base class or mixin). */
203
+ function extendsLitElementDirectly(source: string): boolean {
204
+ return /class\s+\w+\s+extends\s+LitElement\b/.test(source);
205
+ }
206
+
207
+ /** Whether the (comment-stripped) code references `name` as a string literal. */
208
+ function referencesNameAsString(code: string, name: string): boolean {
209
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
210
+ return new RegExp(`['"\`]${escaped}['"\`]`).test(code);
211
+ }
212
+
213
+ async function run(): Promise<void> {
214
+ const cwd = process.cwd();
215
+ const files = await globby(['src/**/*.ts'], {
216
+ cwd,
217
+ ignore: ['src/**/*.stories.ts', 'src/icons/**'],
218
+ });
219
+
220
+ const errors: Finding[] = [];
221
+ const warnings: Finding[] = [];
222
+ let componentCount = 0;
223
+
224
+ for (const rel of files.sort()) {
225
+ const source = fs.readFileSync(path.join(cwd, rel), 'utf8');
226
+ if (!/@customElement\(/.test(source)) continue;
227
+ componentCount++;
228
+
229
+ const code = stripComments(source);
230
+ const staticSlots = findStaticSlots(code);
231
+ const defaultSlot = hasDefaultSlot(code);
232
+ const dynamicSlot = hasDynamicSlot(code);
233
+ const events = findDispatchedEvents(code);
234
+
235
+ const docSlots = findDocumentedSlots(source);
236
+ const docEvents = findDocumentedEvents(source);
237
+
238
+ // Missing @slot for static exposed slots.
239
+ for (const name of staticSlots) {
240
+ if (!docSlots.names.has(name)) {
241
+ errors.push({
242
+ file: rel,
243
+ message: `renders <slot name="${name}"> but has no matching @slot ${name} tag`,
244
+ });
245
+ }
246
+ }
247
+
248
+ // Missing @slot - for a bare default slot.
249
+ if (defaultSlot && !docSlots.hasDefault) {
250
+ errors.push({
251
+ file: rel,
252
+ message: `renders a default <slot> but has no matching "@slot -" tag`,
253
+ });
254
+ }
255
+
256
+ // Missing @fires for dispatched custom events.
257
+ for (const name of events) {
258
+ if (!docEvents.has(name)) {
259
+ errors.push({
260
+ file: rel,
261
+ message: `dispatches CustomEvent('${name}') but has no matching @fires ${name} tag`,
262
+ });
263
+ }
264
+ }
265
+
266
+ // Phantom @slot — only for direct LitElement subclasses without dynamic
267
+ // slot names, so inherited/dynamic slots never produce false positives.
268
+ if (extendsLitElementDirectly(source) && !dynamicSlot) {
269
+ // A `slot="name"` attribute in the template is a *projection into a child*
270
+ // — it exposes nothing and must NOT suppress the phantom warning. Strip
271
+ // those before the string-literal check so only genuine imperative reads
272
+ // (e.g. `getAttribute('slot') === 'header'`, which relocate the consumer's
273
+ // `slot="name"` children) count as a real, if non-declarative, slot.
274
+ const codeNoProjections = code.replace(
275
+ /\bslot\s*=\s*["'][^"']*["']/g,
276
+ ' '
277
+ );
278
+ for (const name of docSlots.names) {
279
+ if (name.includes('<') || name.includes('${')) continue; // placeholder
280
+ if (staticSlots.has(name)) continue; // rendered as a real <slot>
281
+ if (referencesNameAsString(codeNoProjections, name)) continue;
282
+ errors.push({
283
+ file: rel,
284
+ message: `documents @slot ${name} but renders no <slot name="${name}"> (phantom — likely a slot="${name}" projection into a child)`,
285
+ });
286
+ }
287
+ }
288
+
289
+ // Empty description (warning) — a @slot/@fires tag with only a name produces
290
+ // a blank cell in the manifest / Storybook controls.
291
+ for (const tag of parseSlotTags(source)) {
292
+ if (!tag.hasDescription) {
293
+ const label = tag.name === '-' ? 'default slot' : `@slot ${tag.name}`;
294
+ warnings.push({file: rel, message: `${label} has no description`});
295
+ }
296
+ }
297
+ for (const tag of parseEventTags(source)) {
298
+ if (!tag.hasDescription) {
299
+ warnings.push({
300
+ file: rel,
301
+ message: `@fires ${tag.name} has no description`,
302
+ });
303
+ }
304
+ }
305
+ }
306
+
307
+ console.log('Slot & event documentation audit');
308
+ console.log(`Scanned ${componentCount} registered components under src/`);
309
+
310
+ const printByFile = (
311
+ findings: Finding[],
312
+ log: (msg: string) => void,
313
+ indent = ' '
314
+ ) => {
315
+ const byFile = new Map<string, string[]>();
316
+ for (const f of findings) {
317
+ if (!byFile.has(f.file)) byFile.set(f.file, []);
318
+ byFile.get(f.file)!.push(f.message);
319
+ }
320
+ for (const [file, messages] of [...byFile.entries()].sort()) {
321
+ log(`\n ${file}`);
322
+ for (const msg of messages) log(`${indent}- ${msg}`);
323
+ }
324
+ };
325
+
326
+ if (warnings.length > 0) {
327
+ console.warn(
328
+ `\n⚠️ ${warnings.length} empty-description warning(s) (non-blocking):`
329
+ );
330
+ printByFile(warnings, (m) => console.warn(m));
331
+ }
332
+
333
+ if (errors.length > 0) {
334
+ console.error(`\nFound ${errors.length} issue(s):`);
335
+ printByFile(errors, (m) => console.error(m));
336
+ console.error(
337
+ `\n❌ Slot & event documentation audit failed. Add the missing @slot/@fires ` +
338
+ `tags (or remove the phantom ones). See .cursor/rules/comments.mdc.`
339
+ );
340
+ process.exitCode = 1;
341
+ return;
342
+ }
343
+
344
+ const suffix =
345
+ warnings.length > 0 ? ` (${warnings.length} non-blocking warning(s))` : '';
346
+ console.log(`\n✅ Slot & event documentation audit passed.${suffix}`);
347
+ }
348
+
349
+ run().catch((error: unknown) => {
350
+ const message = error instanceof Error ? error.message : String(error);
351
+ console.error(`❌ Slot & event documentation audit failed: ${message}`);
352
+ process.exitCode = 1;
353
+ });
package/src/ar/poi/poi.ts CHANGED
@@ -164,7 +164,8 @@ const POINT_POINTER_OFFSET_PX = 12;
164
164
  * ## Slots/Content
165
165
  *
166
166
  * - Default slot: Main icon/content rendered inside `obc-poi-button`.
167
- * - `header`: Optional custom header content rendered above the POI object.
167
+ * - `button`: Optional custom button element replacing the default `obc-poi-button`.
168
+ * - `header`: Optional header content. Relocated into the inner `obc-poi-button` at runtime by a `MutationObserver` (there is no `<slot name="header">` element).
168
169
  *
169
170
  * ## Events
170
171
  *
@@ -185,7 +186,8 @@ const POINT_POINTER_OFFSET_PX = 12;
185
186
  * ```
186
187
  *
187
188
  * @slot - Default POI button content.
188
- * @slot header - Optional custom header content.
189
+ * @slot button - Optional custom button element replacing the default `obc-poi-button`.
190
+ * @slot header - Optional header content, relocated into the inner `obc-poi-button` at runtime.
189
191
  * @experimental
190
192
  */
191
193
  @customElement('obc-poi')
@@ -90,6 +90,7 @@ export enum OverlapMode {
90
90
  *
91
91
  * @slot - Default slot for POI targets and optional POI groups.
92
92
  * @fires layer-resize {CustomEvent<{height:number,label:string}>} Fired when the layer height changes.
93
+ * @fires layer-selection-changed {Event} Fired when the layer's `isSelected` state changes. Bubbles.
93
94
  * @experimental
94
95
  */
95
96
  @customElement('obc-poi-layer')
@@ -29,6 +29,7 @@ export enum ObcPoiObjectAtonStyle {
29
29
  export {ObcPoiObjectState as ObcPoiObjectAtonState};
30
30
 
31
31
  /**
32
+ * @slot - Icon content rendered inside the POI object.
32
33
  * @experimental
33
34
  */
34
35
  @customElement('obc-poi-object-aton')
@@ -51,6 +51,8 @@ const ALERT_BADGE_TYPES: ObcAutomationBadgeType[] = [
51
51
  ];
52
52
 
53
53
  /**
54
+ * @slot icon-silhouette - Custom silhouette/outline icon, rendered behind the default slot content when no built-in badge type matches.
55
+ * @slot - Default slot for custom badge content, rendered when no built-in badge type matches.
54
56
  * @stable
55
57
  */
56
58
  @customElement('obc-automation-badge')
@@ -97,6 +97,15 @@ export enum AutomationButtonPositioning {
97
97
  }
98
98
 
99
99
  /**
100
+ * @slot badge-top-right - Content projected into the top-right badge position of the button.
101
+ * @slot badge-top-left - Content projected into the top-left badge position of the button.
102
+ * @slot badge-bottom-left - Content projected into the bottom-left badge position of the button.
103
+ * @slot badge-bottom-right - Content projected into the bottom-right badge position of the button.
104
+ * @slot icon - Primary icon shown inside the button (regular, double, flat and square variants).
105
+ * @slot icon-silhouette - Silhouette/outline icon shown behind the primary icon (flat variant only).
106
+ * @slot alert-icon - Custom icon for the alert frame, shown when alert==true and positioning=="point".
107
+ * @slot alert-label - Label for the alert frame, shown when alert==true and positioning=="point".
108
+ * @slot alert-timer - Timer for the alert frame, shown when alert==true and positioning=="point".
100
109
  * @stable
101
110
  */
102
111
  @customElement('obc-automation-button')
@@ -3,6 +3,10 @@ import {LitElement, html, unsafeCSS} from 'lit';
3
3
  import compentStyle from './automation-input-modal.css?inline';
4
4
 
5
5
  /**
6
+ * @slot header - Header content shown at the top of the modal.
7
+ * @slot preview - Preview content shown in the body of the modal.
8
+ * @slot action-primary - Primary action control shown in the actions row.
9
+ * @slot action-secondary - Secondary action control shown in the actions row.
6
10
  * @experimental
7
11
  */
8
12
  @customElement('obc-automation-input-modal')
@@ -7,6 +7,10 @@ import {ObcScrollbar} from '../../components/scrollbar/scrollbar.js';
7
7
  import {ObcAlertMenuItem} from '../../components/alert-menu-item/alert-menu-item.js';
8
8
 
9
9
  /**
10
+ * @slot - Default slot for the alert list items.
11
+ * @slot empty-icon - Icon shown when the list is empty.
12
+ * @slot empty-title - Title shown when the list is empty.
13
+ * @slot empty-description - Description shown when the list is empty.
10
14
  * @stable
11
15
  */
12
16
  @customElement('obc-alert-list')
@@ -63,6 +63,8 @@ export {
63
63
  * Set `priority` to `Priority.enhanced` to use the blue/enhanced color palette
64
64
  * for bar fill and setpoint instead of the default gray/regular palette
65
65
  * (default: `Priority.regular`).
66
+ *
67
+ * @fires scale-dimensions-changed {CustomEvent} Fired when the scale's computed layout thickness changes; a parent chart listens for this to reserve space for the scale. Bubbles and is composed.
66
68
  * @beta
67
69
  */
68
70
  @customElement('obc-bar-horizontal')
@@ -61,6 +61,8 @@ export {
61
61
  * Set `priority` to `Priority.enhanced` to use the blue/enhanced color palette
62
62
  * for bar fill and setpoint instead of the default gray/regular palette
63
63
  * (default: `Priority.regular`).
64
+ *
65
+ * @fires scale-dimensions-changed {CustomEvent} Fired when the scale's computed layout thickness changes; a parent chart listens for this to reserve space for the scale. Bubbles and is composed.
64
66
  * @beta
65
67
  */
66
68
  @customElement('obc-bar-vertical')
@@ -43,8 +43,7 @@ import {
43
43
  * ## Slots
44
44
  * | Slot Name | Renders When... | Purpose |
45
45
  * | -------------- | ------------------------------------ | ------------------------------------------------------------ |
46
- * | primary-icon | Always (unless overridden) | Main advice icon (default: `<obi-notification-advice-active>`) or custom icon. |
47
- * | secondary-icon | Only when `type="application"` | Additional icon for application-type advice messages. |
46
+ * | primary-icon | Only when `type="application"` | Custom main icon (the built-in advice icon is used otherwise). |
48
47
  * | title | Always | Title or heading of the advice message. |
49
48
  * | description | Always | Detailed advice or message text. |
50
49
  * | time | If `hasTimestamp` is true | Timestamp label (e.g., "09:12:46"). |
@@ -75,8 +74,7 @@ import {
75
74
  * </obc-advice-floating-item>
76
75
  * ```
77
76
  *
78
- * @slot primary-icon - Main advice icon (default: `<obi-notification-advice-active>`), or custom icon.
79
- * @slot secondary-icon - Additional icon for application-type advice messages.
77
+ * @slot primary-icon - Custom main icon, projected into the child only when `type="application"` (the built-in advice icon is used otherwise).
80
78
  * @slot title - Title or heading of the advice message.
81
79
  * @slot description - Detailed advice or message text.
82
80
  * @slot time - Timestamp label (e.g., "09:12:46").
@@ -62,17 +62,12 @@ export enum ObcAdviceMessageItemSize {
62
62
  * ---
63
63
  *
64
64
  * ### Slots
65
- * | Slot Name | Renders When... | Purpose |
66
- * |-------------------|------------------------------------------------|--------------------------------------------------------------|
67
- * | `primary-icon` | Always | Main icon representing the advice (default: advice icon). |
68
- * | `secondary-icon` | `hasSecondaryIcon` is true | Optional overlay icon for additional status or category. |
69
- * | `title` | `hasTitle` is true and `title` is set | Title or heading of the advice message. |
70
- * | `description` | `hasDescription` is true and `description` set | Detailed message text. |
71
- * | `time` | `hasTimestamp` is true and `time` is set | Primary timestamp (e.g., time of advice). |
72
- * | `time-secondary` | `hasTimestamp2` is true and `timeSecondary` set| Secondary timestamp (e.g., duration, relative time). |
73
- * | `action-text` | `type="with-button"` | Label for the primary action button. |
74
- * | `action-icon` | `type="with-icon-button"` | Icon for the action button (default: close icon). |
75
- * | `empty` | `type="inactive"` or `empty` is true | Text to display in the empty/inactive state. |
65
+ * | Slot Name | Renders When... | Purpose |
66
+ * |-------------------|----------------------------|----------------------------------------------------------|
67
+ * | `secondary-icon` | `hasSecondaryIcon` is true | Optional overlay icon for additional status or category. |
68
+ *
69
+ * All other content (title, description, timestamps, action label/icon, empty-state
70
+ * text) is driven by properties and rendered internally; it is not exposed as slots.
76
71
  *
77
72
  * ---
78
73
  *
@@ -104,15 +99,7 @@ export enum ObcAdviceMessageItemSize {
104
99
  *
105
100
  * In this example, the advice message displays a title, description, timestamp, and a "View" action button.
106
101
  *
107
- * @slot primary-icon - Main icon representing the advice (always present)
108
102
  * @slot secondary-icon - Optional overlay icon for additional status/category (shown when `hasSecondaryIcon` is true)
109
- * @slot title - Title or heading of the advice (shown when `hasTitle` is true)
110
- * @slot description - Detailed message text (shown when `hasDescription` is true)
111
- * @slot time - Primary timestamp (shown when `hasTimestamp` is true)
112
- * @slot time-secondary - Secondary timestamp (shown when `hasTimestamp2` is true)
113
- * @slot action-text - Label for the action button (when `type="with-button"`)
114
- * @slot action-icon - Icon for the action button (when `type="with-icon-button"`)
115
- * @slot empty - Text to display in the empty/inactive state (when `type="inactive"` or `empty` is true)
116
103
  * @fires message-click {CustomEvent<void>} When the main message area is clicked
117
104
  * @fires action-click {CustomEvent<void>} When the action button (text or icon) is clicked
118
105
  * @beta
@@ -12,6 +12,18 @@ import {
12
12
  } from '../floating-item/floating-item.js';
13
13
 
14
14
  /**
15
+ * `<obc-alert-floating-item>` – A floating alert message that overlays the interface with a built-in alert icon.
16
+ *
17
+ * @slot primary-icon - Custom main icon, projected into the child only when `type="application"` (the built-in alert icon is used otherwise).
18
+ * @slot title - Title or heading of the alert message.
19
+ * @slot description - Detailed message text.
20
+ * @slot time - Timestamp label (e.g., "09:12:46").
21
+ * @slot day - Day label (e.g., "Yesterday").
22
+ * @slot action - Primary action button label/content.
23
+ * @slot action2 - Secondary action button label/content.
24
+ * @fires action-click {CustomEvent} When the primary action button is clicked.
25
+ * @fires action2-click {CustomEvent} When the secondary action button is clicked.
26
+ * @fires dismiss-click {CustomEvent} When the alert message is dismissed.
15
27
  * @stable
16
28
  */
17
29
  @customElement('obc-alert-floating-item')
@@ -50,15 +50,9 @@ export type ObcAckAllVisibleClickEvent = CustomEvent<{
50
50
  * | Slot Name | Renders When... | Purpose |
51
51
  * |----------------------------------|----------------------------------|----------------------------------------------------------------|
52
52
  * | (default) | Always | Place one or more `<obc-alert-menu-item>` elements as alert rows. |
53
- * | empty-unacked-title | Unacked tab, when empty | Custom title for empty unacknowledged alerts. |
54
- * | empty-unacked-description | Unacked tab, when empty | Custom description for empty unacknowledged alerts. |
55
- * | empty-unacked-icon | Unacked tab, when empty | Custom icon for empty unacknowledged alerts. |
56
- * | empty-all-title | Active tab, when empty | Custom title for empty active alerts. |
57
- * | empty-all-description | Active tab, when empty | Custom description for empty active alerts. |
58
- * | empty-all-icon | Active tab, when empty | Custom icon for empty active alerts. |
59
- * | empty-shelved-title | Shelved tab, when empty | Custom title for empty shelved alerts. |
60
- * | empty-shelved-description | Shelved tab, when empty | Custom description for empty shelved alerts. |
61
- * | empty-shelved-icon | Shelved tab, when empty | Custom icon for empty shelved alerts. |
53
+ * | empty-<tab>-title | Selected tab is empty | Custom title for the empty state (`<tab>` is one of `unacked`, `all`, `shelved`). |
54
+ * | empty-<tab>-description | Selected tab is empty | Custom description for the empty state (`<tab>` is one of `unacked`, `all`, `shelved`). |
55
+ * | empty-<tab>-icon | Selected tab is empty | Custom icon for the empty state (`<tab>` is one of `unacked`, `all`, `shelved`). |
62
56
  *
63
57
  * ### Properties
64
58
  * - `hasShelved` (boolean): If true, displays the "Shelved" tab and enables shelving support. Default: false.
@@ -68,7 +62,6 @@ export type ObcAckAllVisibleClickEvent = CustomEvent<{
68
62
  *
69
63
  * ### Events
70
64
  * - **ack-all-visible-click** – Fired when the "ACK visible" button is clicked. The event detail includes the list of visible alert elements and the current tab name.
71
- * - **alert-list-click** – Fired when the "Alerts" navigation button is clicked.
72
65
  * - **silence-click** – Fired when the "Silence" button is clicked.
73
66
  * - **go-to-alert-list-click** – Fired when the "Alerts" navigation button is clicked.
74
67
  *
@@ -92,17 +85,10 @@ export type ObcAckAllVisibleClickEvent = CustomEvent<{
92
85
  * ```
93
86
  *
94
87
  * @slot - The alerts items as ObcAlertMenuItem
95
- * @slot empty-unacked-title - Custom title for empty unacknowledged alerts (Unacked tab)
96
- * @slot empty-unacked-description - Custom description for empty unacknowledged alerts (Unacked tab)
97
- * @slot empty-unacked-icon - Custom icon for empty unacknowledged alerts (Unacked tab)
98
- * @slot empty-all-title - Custom title for empty active alerts (Active tab)
99
- * @slot empty-all-description - Custom description for empty active alerts (Active tab)
100
- * @slot empty-all-icon - Custom icon for empty active alerts (Active tab)
101
- * @slot empty-shelved-title - Custom title for empty shelved alerts (Shelved tab)
102
- * @slot empty-shelved-description - Custom description for empty shelved alerts (Shelved tab)
103
- * @slot empty-shelved-icon - Custom icon for empty shelved alerts (Shelved tab)
88
+ * @slot empty-<tab>-title - Custom empty-state title for the selected tab (`<tab>` is one of `unacked`, `all`, `shelved`)
89
+ * @slot empty-<tab>-description - Custom empty-state description for the selected tab (`<tab>` is one of `unacked`, `all`, `shelved`)
90
+ * @slot empty-<tab>-icon - Custom empty-state icon for the selected tab (`<tab>` is one of `unacked`, `all`, `shelved`)
104
91
  * @fires ack-all-visible-click {ObcAckAllVisibleClickEvent} - Fired when the ack all visible button is clicked
105
- * @fires alert-list-click {CustomEvent} - Fired when the alert list button is clicked
106
92
  * @fires silence-click {CustomEvent} - Fired when the silence button is clicked
107
93
  * @fires go-to-alert-list-click {CustomEvent} - Fired when the go to alert list button is clicked
108
94
  */
@@ -65,6 +65,8 @@ export enum ObcAlertMenuItemActionState {
65
65
  * | Slot Name | Renders When... | Purpose |
66
66
  * |---------------|-------------------------------|------------------------------------------------------|
67
67
  * | alert-icon | Always | Main alert icon representing the alert type. |
68
+ * | title | Always | Title content; falls back to the `title` property. |
69
+ * | description | Always | Description content; falls back to the `description` property. |
68
70
  * | icon | If `hasIcon` is true | Secondary icon (e.g., system/source of alert). |
69
71
  *
70
72
  * ### Events
@@ -94,6 +96,8 @@ export enum ObcAlertMenuItemActionState {
94
96
  * ```
95
97
  *
96
98
  * @slot alert-icon - The main alert icon representing the alert type.
99
+ * @slot title - Title content; falls back to the `title` property when empty.
100
+ * @slot description - Description content; falls back to the `description` property when empty.
97
101
  * @slot icon - Optional secondary icon (e.g., source/system).
98
102
  *
99
103
  * @fires ack-click {CustomEvent<void>} Fired when the ACK action button is clicked.
@@ -12,6 +12,9 @@ export enum ObcChatMessagePosition {
12
12
  }
13
13
 
14
14
  /**
15
+ * `<obc-chat-message>` – A single chat message bubble with optional name and date header.
16
+ *
17
+ * @slot - The message content (body of the chat bubble).
15
18
  * @stable
16
19
  */
17
20
  @customElement('obc-chat-message')
@@ -38,10 +38,8 @@ import {ObcRadio} from '../radio/radio.js';
38
38
  * ---
39
39
  *
40
40
  * ### Slots
41
- * | Slot Name | Renders When... | Purpose |
42
- * |-------------- |-----------------|-----------------------------------------|
43
- * | leading-icon | Always | Contains the radio button itself. |
44
- * | label | Always | Displays the label text for the option. |
41
+ * This component exposes no slots. The radio input and its label are rendered
42
+ * internally from the `label`, `value`, `checked`, and related properties.
45
43
  *
46
44
  * ---
47
45
  *
@@ -82,8 +80,6 @@ import {ObcRadio} from '../radio/radio.js';
82
80
  * ```
83
81
  * In this example, two card radios form a group; only one can be selected at a time.
84
82
  *
85
- * @slot leading-icon - Contains the radio button input.
86
- * @slot label - Displays the label text for the radio option.
87
83
  * @fires change - Fired when the radio is changed.
88
84
  * @stable
89
85
  */