@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.48 → 2.0.0-next.49

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 (43) hide show
  1. package/.storybook/ComponentPreview.tsx +26 -0
  2. package/.storybook/PreviewTemplate.tsx +99 -0
  3. package/.storybook/action-handler.ts +81 -0
  4. package/.storybook/channels.ts +44 -0
  5. package/.storybook/main.ts +313 -0
  6. package/.storybook/manager.ts +71 -0
  7. package/.storybook/openbridgeTheme.ts +197 -0
  8. package/.storybook/preview-head.html +15 -0
  9. package/.storybook/preview.tsx +129 -0
  10. package/.storybook/vitest.setup.ts +23 -0
  11. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  12. package/custom-elements.json +7 -1
  13. package/dist/components/icon-button/icon-button.d.ts +4 -0
  14. package/dist/components/icon-button/icon-button.d.ts.map +1 -1
  15. package/dist/components/icon-button/icon-button.js.map +1 -1
  16. package/eslint.config.mjs +589 -0
  17. package/fix-imports.mjs +185 -0
  18. package/fix-js-extensions.mjs +44 -0
  19. package/lit-localize.json +15 -0
  20. package/new-component.ts +148 -0
  21. package/package.json +55 -3
  22. package/postcss.config.mjs +195 -0
  23. package/script/check-css-mixins.ts +191 -0
  24. package/script/check-css-variables.ts +280 -0
  25. package/script/convert-icons.ts +202 -0
  26. package/script/convert-vessel-svg-to-ts.ts +110 -0
  27. package/script/docgen/README.md +95 -0
  28. package/script/docgen/docs-gen.ts +225 -0
  29. package/script/docgen/prompt-system.txt +187 -0
  30. package/script/download-alert-icons.ts +193 -0
  31. package/script/download-icons.ts +314 -0
  32. package/script/figmavariables.json +139 -0
  33. package/script/generate-bundle-entry.ts +77 -0
  34. package/script/prepare-full-bundle.ts +105 -0
  35. package/script/sort-custom-element-manifest.ts +67 -0
  36. package/src/components/icon-button/icon-button.ts +4 -0
  37. package/vite.config.ts +107 -0
  38. package/vitest.browser.config.ts +14 -0
  39. package/vitest.config.ts +51 -0
  40. package/xliff/es-419.xlf +111 -0
  41. package/xliff/fi-FI.xlf +139 -0
  42. package/dist/NotoSans.ttf +0 -0
  43. package/dist/oicl.svg +0 -4
@@ -0,0 +1,110 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ type ColorMapping = ReadonlyArray<{
5
+ from: RegExp;
6
+ to: string;
7
+ }>;
8
+
9
+ const COLOR_MAPPINGS: ColorMapping = [
10
+ {
11
+ from: /(fill|stroke)="#8E8E8E"/gi,
12
+ to: '$1="var(--instrument-tick-mark-secondary-color)"',
13
+ },
14
+ {
15
+ from: /(fill|stroke)="white"/gi,
16
+ to: '$1="var(--instrument-frame-primary-color)"',
17
+ },
18
+ {
19
+ from: /(fill|stroke)="#F0F0F0"/gi,
20
+ to: '$1="var(--instrument-frame-secondary-color)"',
21
+ },
22
+ {
23
+ from: /(fill|stroke)="#F7F7F7"/gi,
24
+ to: '$1="var(--container-background-color)"',
25
+ },
26
+ ];
27
+
28
+ function ensureVectorEffectOnStrokes(svg: string): string {
29
+ return svg.replaceAll(
30
+ /<([a-zA-Z][\w:-]*)([^>]*?)\sstroke="([^"]+)"([^>]*?)\/?>/g,
31
+ (
32
+ full,
33
+ tagName: string,
34
+ beforeStroke: string,
35
+ strokeValue: string,
36
+ after: string
37
+ ) => {
38
+ const attrs = `${beforeStroke} stroke="${strokeValue}"${after}`;
39
+ if (/\svector-effect="/i.test(attrs)) {
40
+ return full;
41
+ }
42
+
43
+ const tagOpen = `<${tagName}${beforeStroke} vector-effect="non-scaling-stroke" stroke="${strokeValue}"${after}`;
44
+ return full.endsWith('/>') ? `${tagOpen}/>` : `${tagOpen}>`;
45
+ }
46
+ );
47
+ }
48
+
49
+ function applyColorMappings(svg: string): string {
50
+ let out = svg;
51
+ for (const mapping of COLOR_MAPPINGS) {
52
+ out = out.replace(mapping.from, mapping.to);
53
+ }
54
+ return out;
55
+ }
56
+
57
+ function toLitSvgModule(svgContent: string): string {
58
+ const trimmed = svgContent.trim();
59
+ return `import {svg} from 'lit';\n\nexport default svg\`${trimmed}\n\`;\n`;
60
+ }
61
+
62
+ function parseArgs(argv: string[]) {
63
+ const args = argv.slice(2);
64
+ const input = args.find((a) => !a.startsWith('--'));
65
+ const outputFlagIdx = args.findIndex((a) => a === '--out' || a === '-o');
66
+ const output =
67
+ outputFlagIdx >= 0
68
+ ? args[outputFlagIdx + 1]
69
+ : input
70
+ ? input.replace(/\.svg$/i, '.ts')
71
+ : undefined;
72
+ const addVectorEffect = true;
73
+
74
+ if (!input || !output) {
75
+ const scriptName = path.basename(argv[1] ?? 'convert-vessel-svg-to-ts');
76
+ console.error(
77
+ [
78
+ 'Usage:',
79
+ ` ${scriptName} <input.svg> --out <output.ts>`,
80
+ '',
81
+ 'Options:',
82
+ ' --out, -o Output path (default: input.svg -> input.ts)',
83
+ ].join('\n')
84
+ );
85
+ process.exitCode = 1;
86
+ return null;
87
+ }
88
+
89
+ return {input, output, addVectorEffect};
90
+ }
91
+
92
+ function main() {
93
+ const parsed = parseArgs(process.argv);
94
+ if (!parsed) return;
95
+
96
+ const svgPath = path.resolve(process.cwd(), parsed.input);
97
+ const outPath = path.resolve(process.cwd(), parsed.output);
98
+
99
+ const originalSvg = fs.readFileSync(svgPath, 'utf-8');
100
+ let processed = applyColorMappings(originalSvg);
101
+ processed = ensureVectorEffectOnStrokes(processed);
102
+
103
+ const moduleContent = toLitSvgModule(processed);
104
+ fs.mkdirSync(path.dirname(outPath), {recursive: true});
105
+ fs.writeFileSync(outPath, moduleContent, 'utf-8');
106
+
107
+ console.log(`✓ Wrote ${path.relative(process.cwd(), outPath)}`);
108
+ }
109
+
110
+ main();
@@ -0,0 +1,95 @@
1
+ # OpenBridge Doc-Generator
2
+
3
+ A tiny CLI that injects rich JSDoc comments into any OpenBridge source file.
4
+ It reads the `.ts` plus sibling `.stories.ts` / `.css` (if present),
5
+ auto-detects the code pattern (concrete component, pure function module, or abstract base class),
6
+ feeds everything to GPT using a shared prompt, and writes a
7
+ `*.generated.ts` review copy next to the original.
8
+
9
+ ---
10
+
11
+ ## 1 - Install dev-dependencies
12
+
13
+ ```bash
14
+ npm i -D tsx typescript dotenv openai globby
15
+ ```
16
+
17
+ ---
18
+
19
+ ## 2 - Create `.env` in the repo root (git-ignored)
20
+
21
+ ```env
22
+ OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
23
+ OPENAI_MODEL=gpt-4.1 (or whichever you prefer)
24
+ ```
25
+
26
+ ---
27
+
28
+ ## 3 - Run
29
+
30
+ ### A single source file
31
+
32
+ ```bash
33
+ npx tsx script/docgen/docs-gen.ts \
34
+ src/building-blocks/external-scale/external-scale.ts
35
+ ```
36
+
37
+ ### Every `*.ts` in one folder (non-recursive) (easiest to use)
38
+
39
+ ```bash
40
+ npx tsx script/docgen/docs-gen.ts \
41
+ src/building-blocks/external-scale
42
+ ```
43
+
44
+ ### The whole source tree (recursive)
45
+
46
+ ```bash
47
+ npx tsx script/docgen/docs-gen.ts --all
48
+ ```
49
+
50
+ Each run produces `<name>.generated.ts` next to the original.
51
+ Inspect the diff, then rename or copy the new JSDoc into the real file.
52
+
53
+ ---
54
+
55
+ ## Code pattern detection
56
+
57
+ The script auto-detects three patterns and tailors the GPT prompt accordingly:
58
+
59
+ | Pattern | Detection heuristic | Prompt behavior |
60
+ | ------------------------ | ---------------------------------------------------------- | ------------------------------------------------------------------------- |
61
+ | **Concrete component** | Has `@customElement(...)` decorator | JSDoc on class + properties; `@slot`/`@fires` tags |
62
+ | **Pure function module** | No class or no LitElement - only exported functions | Module-level JSDoc at top of file; short JSDoc per exported function/type |
63
+ | **Abstract base class** | Has `class ... extends LitElement` but no `@customElement` | JSDoc on class + properties; `@ignore` tag; documents concrete subclasses |
64
+
65
+ ---
66
+
67
+ ## What gets added
68
+
69
+ - Component/module-level doc block (Overview, Features, Usage, Slots, etc.)
70
+ - Inline `/** ... */` for each `@property`
71
+ - Final tag block with `@slot` and `@fires` so `custom-elements.json` stays complete
72
+ - _(No extra `@property` tags - the inline comments cover those)_
73
+ - Executable code is **never** modified.
74
+
75
+ ---
76
+
77
+ ## Extras
78
+
79
+ | Need | One-liner |
80
+ | ------------ | -------------------------------------------------------- |
81
+ | VS Code task | `"command": "npx tsx script/docgen/docs-gen.ts ${file}"` |
82
+
83
+ ---
84
+
85
+ ## Troubleshooting
86
+
87
+ | Error | Fix |
88
+ | ---------------------------------- | --------------------------------------- |
89
+ | `OPENAI_API_KEY missing` | Add key to `.env`. |
90
+ | `ERR_UNKNOWN_FILE_EXTENSION ".ts"` | Always run via `tsx`, not plain `node`. |
91
+ | `insufficient_quota` | Add billing to your OpenAI account. |
92
+
93
+ ---
94
+
95
+ All helper scripts live in `script/docgen/` so they never ship with the web-component bundle.
@@ -0,0 +1,225 @@
1
+ /*─────────────────────────────────────────────────────────────────────────*\
2
+ │ docs-gen.ts – generate JSDoc comments for OpenBridge source files. │
3
+ │ │
4
+ │ Supports three code patterns: │
5
+ │ a) Concrete web components (class with @customElement) │
6
+ │ b) Pure function modules (no class, only exported functions) │
7
+ │ c) Abstract base classes (class without @customElement) │
8
+ │ │
9
+ │ Usage examples (run from packages/openbridge-webcomponents/): │
10
+ │ npx tsx script/docgen/docs-gen.ts src/components/radio/radio.ts │
11
+ │ npx tsx script/docgen/docs-gen.ts src/building-blocks/external-scale │
12
+ │ npx tsx script/docgen/docs-gen.ts --all │
13
+ \*─────────────────────────────────────────────────────────────────────────*/
14
+
15
+ /* ▲1 Standard library + deps */
16
+ import fs from 'fs/promises';
17
+ import path from 'path';
18
+ import {fileURLToPath} from 'url';
19
+ import {config} from 'dotenv';
20
+ import OpenAI from 'openai';
21
+ import {globby} from 'globby';
22
+ import {statSync} from 'node:fs';
23
+
24
+ /* ▲2 __dirname shim because we're in ESM land */
25
+ const __filename = fileURLToPath(import.meta.url);
26
+ const __dirname = path.dirname(__filename);
27
+
28
+ /* ▲3 Load secrets from .env */
29
+ config(); // populates process.env
30
+ const MODEL = process.env.OPENAI_MODEL ?? 'gpt-4.1';
31
+ const apiKey = process.env.OPENAI_API_KEY!;
32
+ if (!apiKey) throw new Error('OPENAI_API_KEY missing in .env');
33
+
34
+ /* ▲4 Read the big, static system-prompt once */
35
+ const SYSTEM_PROMPT = await fs.readFile(
36
+ path.join(__dirname, 'prompt-system.txt'),
37
+ 'utf8'
38
+ );
39
+
40
+ /* ▲5 Tiny helper: read a file if it exists, otherwise stub text */
41
+ async function readIf(file: string) {
42
+ try {
43
+ return await fs.readFile(file, 'utf8');
44
+ } catch {
45
+ return '(no file found)';
46
+ }
47
+ }
48
+
49
+ /* ▲5b Detect which code pattern the file uses */
50
+ enum CodePattern {
51
+ concreteComponent = 'concrete-component',
52
+ pureFunctionModule = 'pure-function-module',
53
+ abstractBaseClass = 'abstract-base-class',
54
+ }
55
+
56
+ function detectCodePattern(tsCode: string): CodePattern {
57
+ const hasCustomElement = /@customElement\s*\(/.test(tsCode);
58
+ // Match named classes (`class Foo`) and anonymous class expressions (`class extends`)
59
+ const hasClassDecl = /\bclass\s+(\w+\s+)?extends\b/.test(tsCode);
60
+ const hasAbstract = /\babstract\s+class\b/.test(tsCode);
61
+
62
+ if (hasCustomElement) return CodePattern.concreteComponent;
63
+ if (hasAbstract || hasClassDecl) {
64
+ return CodePattern.abstractBaseClass;
65
+ }
66
+ return CodePattern.pureFunctionModule;
67
+ }
68
+
69
+ function patternInstructions(pattern: CodePattern): string {
70
+ switch (pattern) {
71
+ case CodePattern.concreteComponent:
72
+ return [
73
+ 'This file is a CONCRETE WEB COMPONENT (has @customElement decorator).',
74
+ 'Insert JSDoc for the class and every public property/event.',
75
+ "The JSDoc should go right before @customElement('name').",
76
+ 'Use @slot and @fires tags at the end of the class JSDoc.',
77
+ 'Do NOT include @property tags in the tag block.',
78
+ ].join('\n');
79
+
80
+ case CodePattern.pureFunctionModule:
81
+ return [
82
+ 'This file is a PURE FUNCTION MODULE (no component class - only exported functions).',
83
+ 'Place a comprehensive JSDoc block comment at the TOP of the module (above the first export).',
84
+ 'Document the module purpose, what it renders/computes, layout model, and usage examples.',
85
+ 'Also add short JSDoc to each exported function, interface, enum, and type.',
86
+ 'There is no @customElement - do NOT invent one.',
87
+ ].join('\n');
88
+
89
+ case CodePattern.abstractBaseClass:
90
+ return [
91
+ 'This file is an ABSTRACT BASE CLASS (not registered as a custom element).',
92
+ 'Insert JSDoc for the class and every public property/event.',
93
+ 'At the end of the class JSDoc tag block, include @ignore to signal this is not a standalone API.',
94
+ 'Document which concrete subclasses use this base (if visible from imports/comments).',
95
+ 'Include @slot and @fires tags as usual.',
96
+ ].join('\n');
97
+ }
98
+ }
99
+
100
+ /*─────────────────────────────────────────────────────────────────────────*\
101
+ | ▲6 Core routine: generateDocsFor |
102
+ | • Loads component TS, story, CSS |
103
+ | • Detects code pattern (concrete / pure-fn / abstract) |
104
+ | • Builds GPT prompt |
105
+ | • Calls OpenAI |
106
+ | • Writes <name>.generated.ts next to original |
107
+ \*─────────────────────────────────────────────────────────────────────────*/
108
+ async function generateDocsFor(tsPath: string) {
109
+ // 6a. Pull in main file + optional siblings
110
+ const tsCode = await fs.readFile(tsPath, 'utf8');
111
+ const {dir, name, ext} = path.parse(tsPath);
112
+ const basePath = path.join(dir, name);
113
+ const story = await readIf(`${basePath}.stories.ts`);
114
+ const css = await readIf(`${basePath}.css`);
115
+
116
+ // 6b. Detect code pattern to tailor the prompt
117
+ const pattern = detectCodePattern(tsCode);
118
+ console.log(' 📂 %s → pattern: %s', tsPath, pattern);
119
+
120
+ // 6c. At the moment we don't do live web guideline fetch → keep blank
121
+ const guidelines = '';
122
+
123
+ // 6d. Compose the per-file USER prompt (system prompt is separate)
124
+ const userPrompt = [
125
+ '<---CODE--->',
126
+ tsCode,
127
+ '',
128
+ '<---STORY--->',
129
+ story,
130
+ '',
131
+ '<---CSS--->',
132
+ css,
133
+ '',
134
+ '<---GUIDELINE--->',
135
+ guidelines,
136
+ '',
137
+ '<---PATTERN--->',
138
+ patternInstructions(pattern),
139
+ '',
140
+ '<---END--->',
141
+ patternInstructions(pattern),
142
+ '',
143
+ 'Do NOT modify code, ONLY add documentation. This includes imports and enums.',
144
+ "DON'T change or remove any code. It is very IMPORTANT that you don't remove anything.",
145
+ 'You can modify comments and documentation, but ONLY that.',
146
+ 'Remember that comments in code is not good code. Only docs.',
147
+ 'If usage guidance is missing, add a TODO(designer) note.',
148
+ '',
149
+ 'Return ONLY the full .ts file with JSDoc comments inserted - also document enums.',
150
+ 'DO NOT wrap in Markdown (no triple backticks).',
151
+ ].join('\n');
152
+
153
+ // 6e. One OpenAI chat completion
154
+ const openai = new OpenAI({apiKey});
155
+ const chat = await openai.chat.completions.create({
156
+ model: MODEL,
157
+ temperature: 0.2, // low → deterministic
158
+ messages: [
159
+ {role: 'system', content: SYSTEM_PROMPT}, // rules/template
160
+ {role: 'user', content: userPrompt}, // file-specific
161
+ ],
162
+ });
163
+
164
+ // 6f. Write output next to source (<name>.generated.ts)
165
+ const newCode = chat.choices[0].message!.content!;
166
+ const generatedPath = `${basePath}.generated${ext}`;
167
+ await fs.writeFile(generatedPath, newCode);
168
+ console.log(' ✅ Wrote', generatedPath);
169
+ }
170
+
171
+ /*─────────────────────────────────────────────────────────────────────────*\
172
+ | ▲7 CLI dispatcher |
173
+ | • <folder> → every *.ts in that folder (ignores stories/tests) |
174
+ | • <file.ts> → exactly that file |
175
+ | • --all → whole source tree |
176
+ \*─────────────────────────────────────────────────────────────────────────*/
177
+
178
+ const IGNORE = [
179
+ '**/*.stories.*',
180
+ '**/*.story.*', // covers .story.ts if you ever use that pattern
181
+ '**/*.test.*',
182
+ '**/*.generated.*',
183
+ ];
184
+
185
+ const arg = process.argv[2];
186
+ if (!arg) {
187
+ console.error(`Usage:
188
+ docs-gen.ts <component.ts>
189
+ docs-gen.ts <component-folder>
190
+ docs-gen.ts --all`);
191
+ process.exit(1);
192
+ }
193
+
194
+ if (arg === '--all') {
195
+ const files = await globby(
196
+ [
197
+ 'src/building-blocks/**/*.{ts,tsx}',
198
+ 'src/navigation-instruments/**/*.{ts,tsx}',
199
+ 'src/bars-graphs/**/*.{ts,tsx}',
200
+ 'src/components/**/*.{ts,tsx}',
201
+ 'src/automation/**/*.{ts,tsx}',
202
+ 'src/integration-systems/**/*.{ts,tsx}',
203
+ 'src/pages/**/*.{ts,tsx}',
204
+ 'src/svghelpers/**/*.{ts,tsx}',
205
+ 'src/charthelpers/**/*.{ts,tsx}',
206
+ ],
207
+ {
208
+ gitignore: true,
209
+ ignore: IGNORE,
210
+ }
211
+ );
212
+ for (const f of files) await generateDocsFor(f);
213
+ } else if (statSync(arg).isDirectory()) {
214
+ // process every .ts/.tsx directly inside that folder
215
+ const files = await globby([`${arg}/*.ts`, `${arg}/*.tsx`], {
216
+ ignore: IGNORE,
217
+ });
218
+ if (files.length === 0) {
219
+ console.error('No .ts files found in', arg);
220
+ process.exit(1);
221
+ }
222
+ for (const f of files) await generateDocsFor(f);
223
+ } else {
224
+ await generateDocsFor(arg); // treat arg as explicit file path
225
+ }
@@ -0,0 +1,187 @@
1
+ OpenBridge Component Documentation Guidelines
2
+ Overview and Purpose
3
+ Each component’s JSDoc should start with a clear, one-line summary that identifies what the component is and its primary purpose. Use the component’s tag name and a brief description (including a common synonym if the name is non-standard). For example:
4
+ /**
5
+ * `<obc-floating-item>` – A transient toast notification component for brief messages.
6
+ *
7
+ * ...
8
+ */
9
+ This opening line helps developers quickly recognize the component (e.g., “floating message” is essentially a toast/snackbar notification). It also ensures the description contains searchable keywords (like toast, notification, snackbar) so that a RAG system can retrieve it for relevant queries. After the one-liner, include 1-2 sentences expanding on the component’s purpose and context. This should cover the what and why: what the component does and in what scenario it’s used. Emphasize practical use cases without repeating the OpenBridge domain context (assume it is already known).
10
+ **Tone rule:** Do NOT mention “maritime”, “industrial”, “bridge”, or similar environmental qualifiers; keep text domain-agnostic. For example: “Appears temporarily to display non-critical feedback or status updates, floating above the UI so it doesn’t interrupt the user’s workflow.”
11
+ Key Features and Variants
12
+ Use a “Features” section with bullet points (or sub-sections) to highlight the component’s main capabilities, configuration options, and variants. This makes it easy to scan. For instance:
13
+ Variants or Types: List distinct visual/behavioral variants (e.g., regular vs. application messages, checked vs. unchecked states, etc.). If the component has named types (perhaps via an enum), mention each and what it means.
14
+ State or Style Options: Note major style configurations (like horizontal vs. vertical layout, single-line vs. multi-line content, palette variations, size options, etc.).
15
+ Interactive Elements: Include if it supports actions (like buttons or icons) or dynamic content.
16
+ Notable Behaviors: For example, auto-dismiss timing, focus handling, or responsiveness. Anything the component does automatically (or expects from the developer) should be called out here.
17
+ Each bullet should be concise but descriptive. E.g.: “Layout directions: Supports horizontal (side-by-side layout) or vertical (stacked layout) to adapt to available space.” This section gives a quick feature summary at a glance. If the component has multiple distinct modes or sub-variants, you can break them out with subheadings or bold labels for clarity. For example, for a toggle component like obc-check-button, you might have:
18
+ Regular mode: Description…
19
+ Checkbox mode: Description…
20
+ Detail what each mode is meant for and how they differ (as in the user’s draft, where Regular Type vs Checkbox Type are explained in separate paragraphs). This helps a reader understand the nuances of each variant.
21
+ Usage Guidelines and Use Cases
22
+ - important! When describing a property, base the explanation strictly on its code usage or story; if the purpose is unclear, insert **TODO(designer)** instead of guessing. This is IMPORTANT for all the documentation. If a bit unsure, write TODO so it's easy to catch and have a designer write the indended purpose.
23
+ After features, provide guidance on when and how to use the component. This can be a short paragraph or a list of use cases. Frame it as advice: what scenarios is this component ideal for, and how it fits into the UI/UX. For example:
24
+ “Use obc-floating-item for brief, transient feedback (e.g., form submissions, status updates). It’s ideal when you need to confirm an action or show a non-critical alert without disrupting the workflow. Avoid using it for persistent or critical alerts – those might require a dialog or an alert banner.”
25
+ If relevant, contrast the component with similar ones to clarify choices. For instance: “Unlike a standard obc-alert banner that stays in the content flow, a floating message is ephemeral and overlays other content.” This helps developers decide if this is the right component for their need, which is crucial for the RAG model to answer “What component should I use for X?”. Include searchable keywords and synonyms in these explanations. (E.g., for a filter chip component, mention terms like “tag”, “pill”, or “token” if those are common synonyms.) This improves discovery via search queries.
26
+ Slots and Content Structure
27
+ For Web Components, document all content slots clearly in a Slots section (preferably as a table for readability). List each slot name, conditions when it’s used, and its purpose:
28
+ Slot Name Renders When... Purpose
29
+ primary-icon Always (for all messages) Main icon to represent the message’s category.
30
+ secondary-icon type="application" only Additional icon for application-type messages.
31
+ title Always Title or heading of the message.
32
+ description Always Detailed message text.
33
+ time If hasTimestamp is true Timestamp label (e.g., “12:45”).
34
+ day If hasTimestamp && hasDay Day label (e.g., “Mon”).
35
+ action If action property is true Label for the primary action button.
36
+ action2 If action2 is true Label for the secondary action button.
37
+
38
+ This example (based on obc-floating-item) shows how to communicate what each slot is for and under what conditions it appears. It’s crucial for developers to understand how to supply content (icons, text, etc.) to the component. If a component doesn’t use slots (for example, a plain <obc-radio> might just have a text label property instead), you can omit this section or mention how content is provided (e.g., “text content is set via the label attribute”).
39
+ Properties and Attributes
40
+ Even though each property will have its own JSDoc comment in code, the component’s main description should highlight any particularly important or complex properties that affect usage. For example:
41
+ Configuration Flags: e.g., hasTimestamp or action2 – explain what turning them on/off does in practical terms (like “enabling action2 adds a second action button, but only if action (primary) is enabled first”). This manages expectations for how properties interact.
42
+ Enum Properties: If a property uses an enum (like type or direction), ensure the Features or Variants section already explains each possible value. You might not need a separate list here if it’s covered above.
43
+ Defaults: It’s helpful to mention default values or behaviors (“by default, type is regular”). This can be in parentheses after describing the feature.
44
+ Any Special Cases: For example, if a certain property combination is not allowed or if some attribute must be set for another to take effect. Document those clearly to prevent misuse.
45
+ By covering key properties in the narrative, you ensure that even if someone skims the top comments (or searches the docs) they catch the crucial configuration info. The full API (with every property and method) will still be in code, but the description should tie it all together in plain language.
46
+ Events and Custom Events
47
+ If the component emits custom events, include an Events section listing each event name and when it fires. For clarity, use a bullet list format:
48
+ action-click – Fired when the primary action button is clicked.
49
+ action2-click – Fired when the secondary action button is clicked.
50
+ dismiss-click – Fired when the component is dismissed (e.g., close icon clicked or auto-hide, if applicable).
51
+ Also use the JSDoc @fires annotation for each event (as in the code snippet) so that IDEs and documentation generators capture the event contract. For example:
52
+
53
+ * @fires action-click {CustomEvent<void>} When the first action button is clicked.
54
+
55
+ Documenting events is important for developers to know how to listen for interactions. It’s also helpful for the RAG model – if a user asks “How do I know when the toast is dismissed?”, the documentation should make it clear that a dismiss-click event is dispatched.
56
+ Best Practices and Constraints
57
+ Include any dos and don’ts or design constraints that aren’t obvious from the API. This may be a short note or embedded in the usage section. For example: “Only use one primary action in a toast to keep the interaction simple (Material Design recommends at most one action in a snackbar). Use the second action sparingly, e.g., for an extra ‘Undo’ alongside a primary confirmation.” Such guidance might come from Material Design or OpenBridge’s own design rules. Mention timing or auto-dismiss behavior if relevant (e.g., “Floating messages should typically auto-close after a few seconds unless they require user dismissal” — if that’s a design guideline the component supports or expects). For form elements, note things like “for group behavior, ensure all obc-radio in a group share the same name attribute”, or “use obc-checkbox for independent binary choices, and obc-radio when only one selection is allowed in a set.” These practical tips help prevent misuse and answer common usage questions.
58
+ Example Usage (Optional)
59
+ If a component’s usage is not immediately obvious, consider providing a small code example in the JSDoc. This is especially useful for complex components or ones that require multiple slots/properties to work together. For instance:
60
+ * **Example:**
61
+ * <obc-floating-item type="regular" hasTimestamp action>
62
+ * <span slot="primary-icon">ℹ️</span>
63
+ * <span slot="title">Network Connected</span>
64
+ * <span slot="description">You are now online.</span>
65
+ * <span slot="time">14:32</span>
66
+ * <button slot="action">View</button>
67
+ * </obc-floating-item>
68
+ * In this example, the message appears with an info icon, a title, description, timestamp, and a single action button.
69
+ Ensure the example is concise and focused on a primary use case. Not every primitive needs an example (simple ones like a basic radio might be self-evident), but if there’s any potential confusion (like how to supply icons to a custom checkbox, or how to structure actions in a card), an example can clarify it immediately.
70
+ Consistency and Formatting
71
+ Use Markdown headings and lists within the JSDoc comment to structure the information (as shown above). This improves readability in generated docs and in code editors that render JSDoc Markdown. Key sections to include when applicable (in this order):
72
+ Overview: One-liner and short description (as covered in Overview and Purpose).
73
+ Features/Variants: Bullet list of main features, or sub-sections for each variant.
74
+ Usage Guidelines: When to use (and when not to), common scenarios.
75
+ Slots/Content: Table or list of slots and their purpose (if the component uses slots).
76
+ Events: List of custom events (if any).
77
+ Any special Best Practices: (optional, if not already woven into above sections).
78
+ Example: (optional, if needed for clarity).
79
+ Not every component will need all these sections – for example, a simple obc-radio might omit Slots (no slots) and perhaps just have Overview, Usage, and maybe a note on grouping by name. But maintaining a consistent order and format helps users quickly find the info they need across all component docs.
80
+ Leveraging External Design Guidelines
81
+ Because OpenBridge UI components align with common UI patterns, pull in standard usage rules from design systems like Material Design or similar resources:
82
+ Material Design Guidelines: Before writing a component’s doc, check Material Design documentation (Material 3 or Material 2 specs) for that type of component. For instance, Material Design has detailed guidance on snackbars, buttons, checkboxes, etc. Extract key points such as recommended usage, constraints (e.g., “Snackbars should appear at the bottom of the screen and only one at a time”), and any terminology (like “snackbars (toasts) are for brief messages to the user”). Incorporate these in our description in our own words and adapted to OpenBridge context. This ensures our docs carry well-established best practices.
83
+ Other Design Systems: If Material doesn’t cover it or for a second perspective, look at systems like Fluent UI, Ant Design, or Lightning (Salesforce) for how they describe similar components. Sometimes they highlight different considerations (accessibility tips, etc.).
84
+ Web Platform Standards: For form controls (checkbox, radio), also consider HTML’s default behavior (we saw in obc-radio that it uses light DOM for native grouping). If the component leverages or mimics a native element, mention how it stays consistent (e.g., “This wraps an underlying <input type="radio"> to ensure proper group behavior via the native browser mechanics.”).
85
+ By scraping or researching these external sources first, the documentation generator script can gather a pool of facts and recommendations to include. Just make sure to adapt the tone to match OpenBridge’s professional context (e.g., focusing on maritime/industrial reliability if relevant) without explicitly naming OpenBridge or being too generic. The description should feel specific to the component at hand.
86
+ Questions to Clarify with Designers
87
+ When the code or external docs don’t provide enough insight, the script should insert TODO questions for designers within the output (clearly marked so they can be found). These questions ensure that any missing piece of information can be filled in by a human. Common things to ask designers include:
88
+ Component Purpose and Context: “What specific real-world scenario was this component designed for?” (If we aren’t sure about its role or if it overlaps with another component’s usage.) For example, “Is obc-floating-item meant to replace standard toast notifications system-wide, or is it for in-app chat messages?”
89
+ Design Intents for Variants: “When should a developer use type=application vs. regular? What does ‘application’ signify visually or contextually?” If an enum or variant isn’t obvious, get the design rationale to document it correctly.
90
+ Default Behaviors: “Should this component auto-dismiss after a timeout? If so, how long? Or is it always manual dismiss?” – If the code doesn’t clearly enforce something that might be a guideline, ask. Similarly, “Are there recommended default icons or colors for the different states?” (e.g., should a warning icon be used with a certain variant by default?).
91
+ Content Limitations: “Is there a character limit or preferred length for the title/description text?” (Designers might have guidelines like keep toast messages short and one-line if possible.)
92
+ Interaction Design: “Can two action buttons truly be used simultaneously, or is one meant to be primary? Are there any rules about using the second action?” Clarify anything that might be a design convention not enforced by code.
93
+ Relation to Other Components: “We have obc-check-button and obc-checkbox – in what situations should each be used so we can note the difference?” This ensures our documentation can guide users to the right choice. If a component is a foundation (like obc-elevated-card being the basis for card variants), confirm that understanding so the docs can mention it accurately.
94
+ By collecting answers to these questions, you can enrich the JSDoc comments with authoritative information. The script might output a placeholder like:
95
+ /**
96
+ * **TODO (Designer):** Confirm whether the floating message should automatically disappear after a few seconds, or only close on user action.
97
+ */
98
+ Having these in the interim documentation will remind the team to get clarifications, and they can be replaced with actual info later. It’s better to ask and be accurate than to guess, especially since this content will be used by an AI assistant to answer user queries.
99
+ Conclusion
100
+ In summary, a good component description layout for OpenBridge UI components should be comprehensive yet structured for easy reading. It must include:
101
+ A clear summary of the component and its purpose.
102
+ Detailed features/variants explanation.
103
+ Usage guidelines and real-world use cases (so developers know when to use it).
104
+ Technical specifics like slots and events listings for full understanding of its API.
105
+ Inclusion of standard UI/UX guidelines (via Material Design or similar) to reinforce best practices.
106
+ Pointers to related components or differences to avoid confusion.
107
+ All written in a way that surfaces important keywords for searchability.
108
+ By following this layout for each component, we’ll create consistent, rich documentation. This will not only assist developers directly but also feed our embeddings for the RAG model, enabling it to answer user questions like “Which component should I use for X?” or “How do I properly implement Y component?” with accurate, context-aware information. We’ll refine this structure as we generate a few and get feedback, but this provides a strong starting template for our automated JSDoc generation process.
109
+
110
+ ## Structured-tag rules (apply to EVERY component)
111
+
112
+ ● After all Markdown sections, append a short **tag block** that contains only:
113
+ - one `@slot` tag for each content slot
114
+ - one `@fires` (or `@event`) tag for each custom event
115
+
116
+ ● Do **NOT** include `@property` tags in that block—properties are already
117
+ documented inline above their field declarations.
118
+
119
+ ● Do **NOT** mix Markdown headings inside the tag block.
120
+ Example skeleton:
121
+
122
+ ```js
123
+ /**
124
+ * <markdown sections …>
125
+ *
126
+ * @slot - Default leading-icon slot (shown when `showIcon` is true)
127
+ * @fires remove-chip {CustomEvent<{label:string}>}
128
+ */
129
+
130
+ ● When you're using icons as examples, instead of writing emojis, use <obi-placeholder></obi-placeholder>, or other similar icons. OpenBridge has 1000+ icons and you can use them in slots by using this format. Another working icon import example: <obi-arrow></obi-arrow>, <obi-search></obi-search>.
131
+
132
+ ## Documentation by code pattern (regular components, pure functions, abstract classes)
133
+
134
+ Not all code in this repo is a concrete Lit web component. The three main patterns require different documentation approaches because Storybook's autodocs system relies on the `custom-elements.json` manifest, which only contains entries for registered custom elements.
135
+
136
+ ### a) Regular concrete components (default case)
137
+
138
+ Examples: `obc-area-graph`, `obc-line-graph`, `obc-bar-vertical`
139
+
140
+ - JSDoc lives **on the class** (following the full template above).
141
+ - The story meta uses `component: 'obc-tag-name'` to link Storybook autodocs to the `custom-elements.json` entry.
142
+ - Storybook **automatically extracts** the class JSDoc, `@property` types, `@slot` tags, and `@fires` events.
143
+ - The story file does **not** need `parameters.docs.description.component` — autodocs handles it.
144
+
145
+ This is the standard path. The template sections above (Overview, Features, Slots, Events, etc.) apply directly.
146
+
147
+ ### b) Pure function modules (no component class)
148
+
149
+ Examples: `external-scale.ts` (exports `renderExternalScale()`, `computeExternalScaleLayout()`, etc.)
150
+
151
+ These modules export pure functions that return `SVGTemplateResult` fragments, not a LitElement. There is no custom element tag and no `custom-elements.json` entry, so autodocs cannot extract anything automatically.
152
+
153
+ **Source file:**
154
+ - Place a comprehensive JSDoc block comment at the **top of the module** (above the first export). Use the same structure as a component JSDoc (overview, features, usage examples) — but write it as a module description rather than a component description.
155
+
156
+ **Story file:**
157
+ - **Omit** `component:` from the story meta (there is no tag to point to).
158
+ - **Provide** the full documentation via `parameters.docs.description.component` as a Markdown string.
159
+ - **Manually define** all `argTypes` (since there is no manifest to auto-extract from).
160
+ - If the module's functions need a DOM host to render, create a minimal **throwaway inline wrapper** element to give Storybook a renderable surface.
161
+
162
+ **Keeping docs in sync:** The module-level JSDoc is the **source of truth**. The story's `parameters.docs.description.component` should mirror it.
163
+
164
+ ### c) Abstract base classes
165
+
166
+ Examples: `ObcChartLineBase` (abstract base for `obc-line-graph` and `obc-area-graph`)
167
+
168
+ The class has rich JSDoc and `@property` declarations, but it cannot be instantiated and is not registered as a custom element. Storybook cannot auto-extract its docs via `custom-elements.json`.
169
+
170
+ **Source file:**
171
+ - Place the full JSDoc on the abstract class just like a regular component, but append `@ignore` at the end of the JSDoc tag block. This signals to doc generators that the class is not meant to appear as a standalone API entry.
172
+
173
+ **Story file:**
174
+ - Set `component:` to a **concrete subclass tag** (e.g., `'obc-area-graph'`) so Storybook can at least resolve property controls.
175
+ - **Override** the auto-extracted description with `parameters.docs.description.component` containing the base class documentation as Markdown.
176
+
177
+ **Keeping docs in sync:** Same as pure functions — the abstract class JSDoc is the source of truth, and the story description should mirror/replicate it.
178
+
179
+ ### Summary table
180
+
181
+ | Aspect | Concrete component | Pure function module | Abstract base class |
182
+ |----------------------------------------|---------------------------|-----------------------------------------|-------------------------------------------|
183
+ | JSDoc location | On the class | Module-level block comment | On the abstract class (with `@ignore`) |
184
+ | Story `meta.component` | `'obc-tag-name'` | Omitted | Concrete subclass tag |
185
+ | Story `parameters.docs.description` | Not needed (auto) | Required (full Markdown) | Required (override with base class docs) |
186
+ | `argTypes` | Auto from manifest | Manual | Partially auto (from concrete subclass) |
187
+ | Rendering in story | Direct `<obc-tag>` | Throwaway inline wrapper | Concrete subclass element |