@digital-realty/ui-knowledgebase 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,483 @@
1
+ # ui-knowledgebase
2
+
3
+ Knowledge Base micro-frontend for the Global Portal. Rebuilds the legacy iframe-based
4
+ KB (Next.js + Strapi v4) as a native Lit MFE reading from Strapi 5 REST.
5
+
6
+ MFE standup and shell/host integration are tracked separately. This README logs the
7
+ component and API-integration tickets as we define them.
8
+
9
+ **Component approach:** self-contained components per content type / Post `behavior`
10
+ (mirroring v1: `kb-post-text`, `kb-post-accordion`, `kb-post-video-card`,
11
+ `kb-post-document-card`, `kb-category-tile`, `kb-topic-tile`, …) rather than reusable
12
+ atoms — favouring fast, easy-to-follow code. The one shared exception is `kb-rich-text`
13
+ (KB-2), which all post components delegate to so sanitization stays centralized.
14
+
15
+ ## Page component maps
16
+
17
+ Annotated UAT screenshots mapping every component per page (boxes named with their ticket).
18
+
19
+ **Landing** (`/`)
20
+ ![Landing page component map](docs/audit-landing-page.png)
21
+
22
+ **Category** (`/category/:uid`) — note the chrome tickets: back button, category sidebar nav, breadcrumb.
23
+ ![Category page component map](docs/audit-category-page.png)
24
+
25
+ **Topic** (`/topic/:code`) — back button + breadcrumb, no sidebar.
26
+ ![Topic page component map](docs/audit-topic-page.png)
27
+
28
+ **What's New category** — adds the year filter.
29
+ ![What's New page component map](docs/audit-whatsnew-page.png)
30
+
31
+ _Details (`/details/:uid`) is just title + rich-text body in UAT — no back button or breadcrumb._
32
+
33
+ ## Tickets
34
+
35
+ ### KB-1 · KB Strapi API client
36
+
37
+ _No UI — API / data layer._
38
+
39
+ **Type:** Story
40
+
41
+ **Summary**
42
+ The data-access layer the MFE uses to read content from Strapi 5. Wraps `fetch`
43
+ against the Strapi REST API, normalizes Strapi's response shape into stable view
44
+ models, and exposes typed methods per KB view. UI components consume these methods and
45
+ never touch raw Strapi JSON.
46
+
47
+ **Background**
48
+ Content is public-read. Strapi 5 responses are flat (no `data.attributes` wrapper),
49
+ `documentId` replaces numeric `id` for lookups, and `status` replaces `publicationState`.
50
+ Media URLs vary by environment (Azurite locally, Azure Blob in prod), so they resolve
51
+ through a configurable base. Base URLs come from `window.gpConfig['ui-knowledge-base']`
52
+ via the MFE's `config-helper.ts`, matching the other MFEs.
53
+
54
+ **Scope**
55
+ - A `StrapiClient` service reading `apiBaseUrl` and a media base URL from config; no hardcoded hosts.
56
+ - Typed methods for the four views:
57
+ - `getLanding()` — index-page single type + categories + topics + featured + what's-new posts
58
+ - `getCategory(uid)` — category by uid plus its published posts, with sort and topic-filter params
59
+ - `getTopic(code)` — published posts for a topic
60
+ - `getPost(uid)` — single post, populating thumbnail, video, documents, topics, category
61
+ - Normalization into KB view-model types: flatten Strapi shape, expose `documentId`, resolve media URLs to absolute.
62
+ - Query building for `populate`, `locale`, `status=published`, pagination, and `filters`.
63
+ - Consistent error handling: non-2xx and network failures surface as a typed error for page views to render.
64
+
65
+ **Out of scope**
66
+ - Search (separate ticket), rich-text rendering, and any write operations.
67
+ - Auth/token handling — assumes public read; an API token is a follow-up if gating is later required.
68
+
69
+ **Acceptance criteria**
70
+ - All four methods return normalized view models; no component references `data.attributes` or raw Strapi fields.
71
+ - Base URLs are config-driven; switching config points the client at local vs deployed Strapi with no code change.
72
+ - Media URLs in returned models are absolute and load in both local (Azurite) and deployed environments.
73
+ - Only published content is returned by default; `locale` is honored when passed.
74
+ - Failures return a typed error; a smoke test covers a 404 and a network error.
75
+ - Unit tests cover normalization and query building for each method.
76
+
77
+ **Dependencies**
78
+ - Config-helper + config keys ticket (provides `apiBaseUrl` / media base).
79
+ - View-model types ticket.
80
+ - Strapi public-role read access enabled.
81
+
82
+ ### KB-2 · Build sanitized rich-text (HTML) renderer
83
+
84
+ ![KB-2 rich-text renderer](docs/KB-02-rich-text.png)
85
+ _UAT reference: the sanitized HTML body (paragraph, list, PDF link) inside an expanded What's New accordion._
86
+
87
+ **Type:** Story
88
+
89
+ **Summary**
90
+ Render a post's `text` field (raw CKEditor HTML from Strapi) safely. A pure helper
91
+ sanitizes and rewrites URLs; a thin Lit element applies it so post HTML can only be
92
+ rendered through the sanitizer.
93
+
94
+ **Background**
95
+ The legacy frontend did **no** sanitization — it injected the HTML directly (or parsed it
96
+ with `html-react-parser`, which only rewrote anchors/images). It got away with this because
97
+ it ran in a separate-origin iframe. This MFE runs in the portal's own origin, so unsafe HTML
98
+ could run with portal privileges. The `text` HTML also contains messy pasted-Word markup and
99
+ inline `/uploads/...` links. GPv1 already wrapped rendering in a reused `RawHtml` component;
100
+ we mirror that and add the missing sanitize step.
101
+
102
+ **Scope**
103
+ - `sanitizeRichText(html, mediaBase): string` — pure helper using DOMPurify (allowlist
104
+ tolerant of the legacy Word markup). Rewrite `<a href>` / `<img src>` via a DOMPurify
105
+ `afterSanitizeAttributes` hook: resolve relative `/uploads/...` against the media base URL,
106
+ leave absolute URLs as-is, add `target="_blank" rel="noopener noreferrer"` to external links.
107
+ (Ports the URL logic from GPv1's `renderTextAsJsx`; the old `/api/files?url=` proxy is dropped.)
108
+ - `<kb-rich-text .value>` — thin Lit element that calls the helper and renders via `unsafeHTML`;
109
+ the single place `unsafeHTML` is used. Renders in **light DOM** (`createRenderRoot()` returns
110
+ `this`) so portal styles cascade onto the CMS content; content-specific CSS (tables, lists,
111
+ headings) lives as a scoped `.kb-rich-text` class in the MFE stylesheet, not `static styles`.
112
+ Trade-off: no style encapsulation — acceptable for CMS content; the sanitizer, not the DOM
113
+ boundary, is the security control.
114
+
115
+ **Out of scope**
116
+ - Video embeds and document/attachment lists (separate components).
117
+
118
+ **Acceptance criteria**
119
+ - `<script>`, event handlers, and other unsafe content are stripped; a test asserts a script payload does not execute.
120
+ - Relative media URLs resolve to absolute against the media base and load locally (Azurite) and deployed.
121
+ - External links open in a new tab with `rel="noopener noreferrer"`.
122
+ - Representative legacy content (incl. the pasted-Word Terms & Conditions post) renders without breaking layout.
123
+
124
+ **Dependencies**
125
+ - KB-1 (media base URL / normalized models).
126
+
127
+ ### KB-3 · Build Knowledge Base landing page view
128
+
129
+ ![KB-3 landing view](docs/KB-03-landing-view.png)
130
+
131
+ **Type:** Story
132
+
133
+ **Summary**
134
+ The landing route (`/`) of the KB MFE. Fetches the landing data and composes the page
135
+ sections, wiring navigation into category, topic, and post-detail routes.
136
+
137
+ **Background**
138
+ Mirrors the legacy KB home: a hero (from the `IndexPage` single type) plus Categories,
139
+ Topics, Featured Resources, and a What's New panel. This ticket is the page composition
140
+ only — each section is its own component ticket.
141
+
142
+ **Scope**
143
+ - A Lit route view at `/` that calls `StrapiClient.getLanding()` (KB-1).
144
+ - Compose the sections: hero + search, categories grid, topics list, featured resources, what's-new panel.
145
+ - Wire clicks: category → `/category/:uid`, topic → `/topic/:code`, post → `/details/:uid`.
146
+ - Render loading, empty, and error states from the client result.
147
+
148
+ **Out of scope**
149
+ - The section components themselves (hero, category card, topics list, featured card, what's-new) — separate tickets.
150
+ - Search behaviour (separate ticket); this view only places the search input.
151
+
152
+ **Acceptance criteria**
153
+ - Landing renders all five sections from a single `getLanding()` call.
154
+ - Category, topic, and post links navigate to the correct routes.
155
+ - Loading, empty, and error states render; an error from the client shows the error state, not a blank page.
156
+
157
+ **Dependencies**
158
+ - KB-1 (client + `getLanding()`).
159
+ - Content components: `kb-category-tile`, `kb-topic-tile`, featured post cards, `kb-whats-new-panel` (hero inline or `kb-hero`).
160
+
161
+ ### KB-4 · `kb-hero`
162
+
163
+ ![KB-4 hero](docs/KB-04-hero.png)
164
+
165
+ **Type:** Story
166
+
167
+ **Summary**
168
+ Landing banner built from the `IndexPage` single type.
169
+
170
+ **Data:** `heading`, `description`, `backgroundImage`.
171
+
172
+ **Renders:** background image with heading + description; provides the slot/placement for the search input (search behaviour is a separate ticket).
173
+
174
+ **Dependencies:** KB-1.
175
+
176
+ ### KB-5 · `kb-category-tile`
177
+
178
+ ![KB-5 category tile](docs/KB-05-category-tile.png)
179
+
180
+ **Type:** Story
181
+
182
+ **Summary**
183
+ A single category card, repeated in the landing categories grid.
184
+
185
+ **Data:** `uid`, `title`, `subtitle`, `icon` (Material icon name), `image`.
186
+
187
+ **Renders:** card with icon/image, title, subtitle; click navigates to `/category/:uid`.
188
+
189
+ **Dependencies:** KB-1.
190
+
191
+ ### KB-6 · `kb-topic-tile`
192
+
193
+ ![KB-6 topic tile](docs/KB-06-topic-tile.png)
194
+
195
+ **Type:** Story
196
+
197
+ **Summary**
198
+ A single topic link/chip, repeated in the landing topics list.
199
+
200
+ **Data:** `code`, `name`, `icon`.
201
+
202
+ **Renders:** icon + name; click navigates to `/topic/:code`.
203
+
204
+ **Dependencies:** KB-1.
205
+
206
+ ### KB-7 · `kb-featured-card`
207
+
208
+ ![KB-7 featured card](docs/KB-07-featured-card.png)
209
+
210
+ **Type:** Story
211
+
212
+ **Summary**
213
+ A post preview card used in the landing "Featured Resources" row.
214
+
215
+ **Data:** `uid`, `title`, `abstract`, `thumbnail`, `topics`.
216
+
217
+ **Renders:** thumbnail, topic tag(s), title, abstract; click navigates to `/details/:uid`.
218
+
219
+ **Dependencies:** KB-1.
220
+
221
+ ### KB-8 · `kb-whats-new-panel`
222
+
223
+ ![KB-8 what's-new panel](docs/KB-08-whats-new-panel.png)
224
+
225
+ **Type:** Story
226
+
227
+ **Summary**
228
+ The landing "What's New" side panel.
229
+
230
+ **Data:** the what's-new category (`title`, `subtitle`) + its recent posts (`title`, `dateAdded`, `uid`).
231
+
232
+ **Renders:** heading + subtitle, a short list of recent posts (title/date, link to `/details/:uid`), and a "view all" link to `/category/whats-new`.
233
+
234
+ **Dependencies:** KB-1.
235
+
236
+ ### KB-9 · `kb-post-text`
237
+
238
+ ![KB-9 post text](docs/KB-09-post-text.png)
239
+ _UAT reference: the Terms, Policies & Disclaimers category (text-behavior posts)._
240
+
241
+ **Type:** Story
242
+
243
+ **Summary**
244
+ Post renderer for `behavior = text` (article-style body).
245
+
246
+ **Data:** `title`, `dateAdded`, `topics`, `text`.
247
+
248
+ **Renders:** title, date, topic chips, and body HTML via `<kb-rich-text>`.
249
+
250
+ **Dependencies:** KB-1, KB-2.
251
+
252
+ ### KB-10 · `kb-post-accordion`
253
+
254
+ ![KB-10 post accordion](docs/KB-10-post-accordion.png)
255
+ _UAT reference: What's New category (also accordion-behavior); V2 FAQs is empty in UAT._
256
+
257
+ **Type:** Story
258
+
259
+ **Summary**
260
+ Post renderer for `behavior = accordion` (used by FAQ categories).
261
+
262
+ **Data:** `title`, `text`.
263
+
264
+ **Renders:** an accordion item — title as the header, body HTML via `<kb-rich-text>` in the expandable panel (may use `ix-accordion`).
265
+
266
+ **Dependencies:** KB-1, KB-2.
267
+
268
+ ### KB-11 · `kb-post-video-card`
269
+
270
+ ![KB-11 post video card](docs/KB-11-post-video-card.png)
271
+ _UAT reference: the video player from the video post's detail page — UAT has no `video_card` category, so the card layout isn't shown (use Figma for that)._
272
+
273
+ **Type:** Story
274
+
275
+ **Summary**
276
+ Post renderer for `behavior = video_card`.
277
+
278
+ **Data:** `title`, `topics`, `thumbnail`, `video`, `duration`, `text`.
279
+
280
+ **Renders:** card with video thumbnail + duration and a play action that opens the video; title, topic chips, and body HTML via `<kb-rich-text>`.
281
+
282
+ **Dependencies:** KB-1, KB-2 (video player/embed is part of this component).
283
+
284
+ ### KB-12 · `kb-post-document-card`
285
+
286
+ ![KB-12 post document card](docs/KB-12-post-document-card.png)
287
+ _UAT reference: the User Guides category (document_card-behavior posts)._
288
+
289
+ **Type:** Story
290
+
291
+ **Summary**
292
+ Post renderer for `behavior = document_card`.
293
+
294
+ **Data:** `title`, `topics`, `documents[]`, `text`.
295
+
296
+ **Renders:** card with title, topic chips, body HTML via `<kb-rich-text>`, and a list of downloadable document links from `documents[]`.
297
+
298
+ **Dependencies:** KB-1, KB-2.
299
+
300
+ ### KB-13 · Build category listing page view
301
+
302
+ ![KB-13 category view](docs/KB-13-category-view.png)
303
+ ![KB-13 sort + topic filter controls](docs/KB-13b-sort-filter-controls.png)
304
+ _Second image: the flag-driven sort + topic-filter controls (shown only when `showSortBy` / `showFilterByTopic` are set)._
305
+
306
+ **Type:** Story
307
+
308
+ **Summary**
309
+ The category route (`/category/:uid`). Fetches a category and its posts, and renders the
310
+ posts using the category's `behavior`.
311
+
312
+ **Background**
313
+ Mirrors the legacy category page. A category's `behavior` (`text | accordion |
314
+ video_card | document_card`) decides which post component renders its posts, and its
315
+ `showSortBy` / `showFilterByTopic` flags decide whether the sort and topic-filter
316
+ controls appear.
317
+
318
+ **Scope**
319
+ - A Lit route view at `/category/:uid` calling `StrapiClient.getCategory(uid)` (KB-1).
320
+ - Header from the category (title, subtitle, icon/image).
321
+ - Render the posts by selecting the post component from the category `behavior`
322
+ (KB-9 `text`, KB-10 `accordion`, KB-11 `video_card`, KB-12 `document_card`).
323
+ - Show inline sort and filter-by-topic controls only when `showSortBy` / `showFilterByTopic` are set.
324
+ - Post click → `/details/:uid`; loading, empty, and error states.
325
+
326
+ **Out of scope**
327
+ - The post components themselves (KB-9–12); search.
328
+
329
+ **Acceptance criteria**
330
+ - Posts render with the component matching the category `behavior`.
331
+ - Sort / topic-filter controls appear only when their flags are set, and filter/sort the list.
332
+ - Loading, empty, and error states render.
333
+
334
+ **Dependencies:** KB-1, KB-9, KB-10, KB-11, KB-12, KB-17 (back button), KB-18 (sidebar nav), KB-19 (breadcrumb); KB-20 (year filter) on the What's New category.
335
+
336
+ ### KB-14 · Build topic listing page view
337
+
338
+ ![KB-14 topic view](docs/KB-14-topic-view.png)
339
+
340
+ **Type:** Story
341
+
342
+ **Summary**
343
+ The topic route (`/topic/:code`). Fetches the posts for a topic and renders each by its
344
+ own `behavior`.
345
+
346
+ **Background**
347
+ Mirrors the legacy topic page. Unlike a category, a topic's posts span categories, so
348
+ each post renders according to its own `behavior` field.
349
+
350
+ **Scope**
351
+ - A Lit route view at `/topic/:code` calling `StrapiClient.getTopic(code)` (KB-1).
352
+ - Header showing the topic (icon + name).
353
+ - Render each post by selecting the post component from that post's `behavior` (KB-9–12).
354
+ - Post click → `/details/:uid`; loading, empty, and error states.
355
+
356
+ **Out of scope**
357
+ - The post components themselves (KB-9–12); search.
358
+
359
+ **Acceptance criteria**
360
+ - Each post renders with the component matching its own `behavior`.
361
+ - Loading, empty, and error states render.
362
+
363
+ **Dependencies:** KB-1, KB-9, KB-10, KB-11, KB-12, KB-17 (back button), KB-19 (breadcrumb). No sidebar on topic pages.
364
+
365
+ ### KB-15 · Build post details page view
366
+
367
+ ![KB-15 details view](docs/KB-15-details-view.png)
368
+
369
+ **Type:** Story
370
+
371
+ **Summary**
372
+ The post detail route (`/details/:uid`). Renders a single post in full.
373
+
374
+ **Background**
375
+ Mirrors the legacy details page — the full post: title, topics, date, body, and any
376
+ video or document attachments. Keep the `/details/:uid` path; deep links and the
377
+ launchpad widget depend on it.
378
+
379
+ **Scope**
380
+ - A Lit route view at `/details/:uid` calling `StrapiClient.getPost(uid)` (KB-1).
381
+ - Render: title, topic chips, `dateAdded`, body HTML via `<kb-rich-text>` (KB-2),
382
+ the `video` if present, and the `documents[]` as downloadable links.
383
+ - Loading, not-found (bad uid), and error states.
384
+
385
+ **Out of scope**
386
+ - Search; related-posts / navigation beyond back.
387
+
388
+ **Acceptance criteria**
389
+ - A post renders its title, body, and any video/documents.
390
+ - Body HTML is sanitized via `<kb-rich-text>` and media links resolve.
391
+ - An unknown `uid` shows a not-found state, not a blank page.
392
+
393
+ **Dependencies:** KB-1, KB-2.
394
+
395
+ ### KB-16 · Knowledge Base search
396
+
397
+ ![KB-16 search](docs/KB-16-search.png)
398
+
399
+ **Type:** Story
400
+
401
+ **Summary**
402
+ Search across KB posts from the hero search input, with a results list that links to posts.
403
+
404
+ **Background**
405
+ The legacy KB hero has a search box. Content is small, so a simple substring match is
406
+ enough — no ranking engine. Runs against Strapi REST.
407
+
408
+ **Scope**
409
+ - Add `StrapiClient.search(query)` (KB-1) using Strapi filters (`$containsi` on `title`,
410
+ `abstract`, `text`), published-only and locale-aware, returning normalized post previews.
411
+ - Wire the hero search input (KB-4): debounce input, call `search`, and show results.
412
+ - Render results as a simple list (title, abstract, category/topic); click → `/details/:uid`.
413
+ - Handle empty query, no-results, and error states.
414
+
415
+ **Out of scope**
416
+ - Ranking / relevance scoring, faceting, and search analytics.
417
+
418
+ **Acceptance criteria**
419
+ - Typing a query returns matching published posts; matches are case-insensitive across title/abstract/text.
420
+ - Selecting a result navigates to `/details/:uid`.
421
+ - Empty query clears results; no-results and error states render.
422
+
423
+ **Dependencies:** KB-1, KB-4.
424
+
425
+ _Note: the search input also appears on the category and topic pages (same component, different placement), not only the hero._
426
+
427
+ ### KB-17 · `kb-back-button`
428
+
429
+ **Type:** Story
430
+
431
+ ![KB-17 back button](docs/KB-17-back-button.png)
432
+
433
+ **Summary**
434
+ The "← BACK" control shown top-left on the category, topic, and any nested page.
435
+
436
+ **Renders:** a back arrow + "BACK" label; navigates to the previous route (history back, falling back to the KB landing).
437
+
438
+ **Dependencies:** none (pure navigation).
439
+
440
+ ### KB-18 · `kb-category-nav`
441
+
442
+ **Type:** Story
443
+
444
+ ![KB-18 category sidebar nav](docs/KB-18-category-nav.png)
445
+
446
+ **Summary**
447
+ The left-hand sidebar listing all categories on a category page, with the current one highlighted.
448
+
449
+ **Data:** all categories (`uid`, `title`) — from KB-1.
450
+
451
+ **Renders:** a vertical list of category links; current category highlighted; each links to `/category/:uid`. (Category pages only — topic/landing/details don't show it.)
452
+
453
+ **Dependencies:** KB-1.
454
+
455
+ ### KB-19 · `kb-breadcrumb`
456
+
457
+ **Type:** Story
458
+
459
+ ![KB-19 breadcrumb](docs/KB-19-breadcrumb.png)
460
+
461
+ **Summary**
462
+ The breadcrumb trail (e.g. `Knowledge Base › User Guides`) on category and topic pages.
463
+
464
+ **Data:** the current page's label + parent (Knowledge Base root).
465
+
466
+ **Renders:** links back to `/` (Knowledge Base) and shows the current page; last crumb is non-clickable.
467
+
468
+ **Dependencies:** none (labels passed in by the page view).
469
+
470
+ ### KB-20 · `kb-whats-new-year-filter`
471
+
472
+ **Type:** Story
473
+
474
+ ![KB-20 What's New year filter](docs/KB-20-year-filter.png)
475
+
476
+ **Summary**
477
+ The year pills (e.g. `2025` / `2026`) on the What's New category that filter release-note posts by year.
478
+
479
+ **Data:** the set of years present in the category's posts (`dateAdded`).
480
+
481
+ **Renders:** a row of selectable year pills; selecting one filters the accordion list to that year. (What's New category only.)
482
+
483
+ **Dependencies:** KB-1, KB-13.
@@ -0,0 +1,39 @@
1
+ import{LitElement as e,css as t,html as n,nothing as r}from"lit";import{customElement as i,property as a,state as o}from"lit/decorators.js";import{Router as s}from"@vaadin/router";var c=null;function l(e,t,n){c=new s(e,{baseUrl:t}),c.setRoutes(n)}function u(){return c}function d(){c?.unsubscribe(),c?.removeRoutes(),c=null}function f(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}var p=class extends e{static{this.styles=t`
2
+ :host {
3
+ display: block;
4
+ padding: 1rem;
5
+ }
6
+ `}render(){return n`
7
+ <h1>Knowledge Base</h1>
8
+ <nav>
9
+ <ul>
10
+ <li><a href="/knowledge-base/category/demo">Category: demo</a></li>
11
+ <li><a href="/knowledge-base/topic/demo">Topic: demo</a></li>
12
+ <li><a href="/knowledge-base/details/demo">Details: demo</a></li>
13
+ </ul>
14
+ </nav>
15
+ `}};p=f([i(`kb-landing-view`)],p);var m=class extends e{constructor(...e){super(...e),this.uid=``}static{this.styles=t`
16
+ :host {
17
+ display: block;
18
+ padding: 1rem;
19
+ }
20
+ `}onAfterEnter(e){this.uid=String(e.params.uid??``)}render(){return n`
21
+ <a href="/knowledge-base">&larr; Knowledge Base</a>
22
+ <h1>Category: ${this.uid}</h1>
23
+ `}};f([o()],m.prototype,`uid`,void 0),m=f([i(`kb-category-view`)],m);var h=class extends e{constructor(...e){super(...e),this.code=``}static{this.styles=t`
24
+ :host {
25
+ display: block;
26
+ padding: 1rem;
27
+ }
28
+ `}onAfterEnter(e){this.code=String(e.params.code??``)}render(){return n`
29
+ <a href="/knowledge-base">&larr; Knowledge Base</a>
30
+ <h1>Topic: ${this.code}</h1>
31
+ `}};f([o()],h.prototype,`code`,void 0),h=f([i(`kb-topic-view`)],h);var g=class extends e{constructor(...e){super(...e),this.uid=``}static{this.styles=t`
32
+ :host {
33
+ display: block;
34
+ padding: 1rem;
35
+ }
36
+ `}onAfterEnter(e){this.uid=String(e.params.uid??``)}render(){return n`
37
+ <a href="/knowledge-base">&larr; Knowledge Base</a>
38
+ <h1>Details: ${this.uid}</h1>
39
+ `}};f([o()],g.prototype,`uid`,void 0),g=f([i(`kb-details-view`)],g);var _=`/knowledge-base`,v=e=>e===`/knowledge-base`||e.startsWith(`/knowledge-base/`),y={Landing:`kb-landing`,Category:`kb-category`,Topic:`kb-topic`,Details:`kb-details`},b=[{path:_,children:[{name:y.Landing,path:`/`,component:`kb-landing-view`},{name:y.Category,path:`/category/:uid`,component:`kb-category-view`},{name:y.Topic,path:`/topic/:code`,component:`kb-topic-view`},{name:y.Details,path:`/details/:uid`,component:`kb-details-view`}]},{path:`(.*)`,component:`no-elem`}],x=class extends e{constructor(...e){super(...e),this.baseUrl=`/`,this.active=!1,this.outlet=document.createElement(`main`),this.onLocationChanged=e=>{let{router:t,location:n}=e.detail;t===u()&&(this.active=v(n.pathname))}}connectedCallback(){super.connectedCallback(),window.addEventListener(`vaadin-router-location-changed`,this.onLocationChanged),l(this.outlet,this.baseUrl,b),this.dispatchEvent(new CustomEvent(`mfe-loaded`,{detail:{routes:b}}))}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener(`vaadin-router-location-changed`,this.onLocationChanged),d(),this.active=!1}render(){return this.active?n`${this.outlet}`:r}};f([a({type:String})],x.prototype,`baseUrl`,void 0),f([o()],x.prototype,`active`,void 0),x=f([i(`ui-knowledgebase`)],x);export{x as UiKnowledgebase};
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@digital-realty/ui-knowledgebase",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "packageManager": "pnpm@11.7.0",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "exports": {
13
+ "./ui-knowledgebase.js": "./dist/ui-knowledgebase.js"
14
+ },
15
+ "scripts": {
16
+ "dev": "vite",
17
+ "build": "tsc --noEmit && vite build",
18
+ "build:workspace": "pnpm run build",
19
+ "preview": "vite preview",
20
+ "clean": "rimraf node_modules dist"
21
+ },
22
+ "dependencies": {
23
+ "@vaadin/router": "^1.7.5",
24
+ "lit": "^3.1.4"
25
+ },
26
+ "devDependencies": {
27
+ "rimraf": "^6.0.1",
28
+ "typescript": "^5.5.3",
29
+ "vite": "^8.1.0"
30
+ }
31
+ }