@countermeasure-platform/web-components 1.3.5-dev.36.1 → 1.3.6-dev.37.1
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/dist/{component-D5sRm1fq.js → component-D_asjmrt.js} +5 -3
- package/dist/component-D_asjmrt.js.map +1 -0
- package/dist/components/brand/index.d.ts.map +1 -1
- package/dist/components/brand/index.js +11 -5
- package/dist/components/brand/index.js.map +1 -1
- package/dist/components/brand/types.d.ts +2 -0
- package/dist/components/brand/types.d.ts.map +1 -1
- package/dist/icons/index.d.ts +7 -2
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons/index.js +7 -2
- package/dist/icons/index.js.map +1 -1
- package/dist/icons/lucide.d.ts +3 -0
- package/dist/icons/lucide.d.ts.map +1 -1
- package/dist/icons/lucide.js +3 -0
- package/dist/icons/lucide.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +126 -125
- package/dist/layout/app-shell.d.ts +7 -0
- package/dist/layout/app-shell.d.ts.map +1 -1
- package/dist/layout/app-shell.js +24 -6
- package/dist/layout/app-shell.js.map +1 -1
- package/dist/layout/core-app-chrome.d.ts.map +1 -1
- package/dist/layout/core-app-chrome.js +8 -5
- package/dist/layout/core-app-chrome.js.map +1 -1
- package/dist/layout/core-app-library-dashboard.d.ts +72 -0
- package/dist/layout/core-app-library-dashboard.d.ts.map +1 -0
- package/dist/layout/core-app-library-dashboard.js +160 -0
- package/dist/layout/core-app-library-dashboard.js.map +1 -0
- package/dist/layout/index.d.ts +2 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +38 -37
- package/dist/layout/index.js.map +1 -1
- package/dist/react/brand/index.d.ts +3 -1
- package/dist/react/brand/index.d.ts.map +1 -1
- package/dist/react/brand.js +19 -13
- package/dist/react/brand.js.map +1 -1
- package/dist/react/layout/core-app-library-dashboard.d.ts +13 -0
- package/dist/react/layout/core-app-library-dashboard.d.ts.map +1 -0
- package/dist/react/layout/core-app-library-dashboard.js +27 -0
- package/dist/react/layout/core-app-library-dashboard.js.map +1 -0
- package/dist/react/layout/index.d.ts +1 -0
- package/dist/react/layout/index.d.ts.map +1 -1
- package/dist/react/layout/index.js +3 -2
- package/dist/react/primitives/alert.d.ts +1 -1
- package/dist/react/primitives/toast.d.ts +1 -1
- package/dist/react/sidebar.js +1 -1
- package/dist/react.js +97 -96
- package/dist/sidebar/component.d.ts.map +1 -1
- package/dist/sidebar/index.js +1 -1
- package/dist/sidebar/types.d.ts +7 -0
- package/dist/sidebar/types.d.ts.map +1 -1
- package/dist/styles/components/brand.css +22 -0
- package/dist/styles/layout.css +577 -0
- package/dist/styles/sidebar.css +26 -0
- package/package.json +11 -1
- package/src/components/brand/index.ts +22 -4
- package/src/components/brand/types.ts +2 -0
- package/src/icons/icons.test.ts +1 -1
- package/src/icons/index.ts +7 -2
- package/src/icons/lucide.ts +4 -0
- package/src/index.ts +2 -0
- package/src/layout/app-shell.test.ts +76 -0
- package/src/layout/app-shell.ts +38 -7
- package/src/layout/core-app-chrome.test.ts +17 -5
- package/src/layout/core-app-chrome.ts +8 -3
- package/src/layout/core-app-library-dashboard.test.ts +397 -0
- package/src/layout/core-app-library-dashboard.ts +519 -0
- package/src/layout/index.ts +18 -0
- package/src/react/brand/index.test.tsx +10 -0
- package/src/react/brand/index.tsx +25 -4
- package/src/react/layout/core-app-chrome.test.tsx +2 -2
- package/src/react/layout/core-app-library-dashboard.test.tsx +42 -0
- package/src/react/layout/core-app-library-dashboard.tsx +56 -0
- package/src/react/layout/index.ts +6 -0
- package/src/sidebar/component.test.ts +21 -1
- package/src/sidebar/component.ts +14 -8
- package/src/sidebar/types.ts +7 -0
- package/src/styles/components/brand.css +22 -0
- package/src/styles/layout.css +577 -0
- package/src/styles/sidebar.css +26 -0
- package/dist/component-D5sRm1fq.js.map +0 -1
package/src/icons/icons.test.ts
CHANGED
|
@@ -106,7 +106,7 @@ describe('Icons Module', () => {
|
|
|
106
106
|
expect(resolveLucideIconName('capacity')).toBe('Gauge')
|
|
107
107
|
expect(resolveLucideIconName('api-access')).toBe('KeyRound')
|
|
108
108
|
expect(resolveLucideIconName('intel-reports')).toBe('FileText')
|
|
109
|
-
expect(resolveLucideIconName('notifications')).toBe('
|
|
109
|
+
expect(resolveLucideIconName('notifications')).toBe('Bell')
|
|
110
110
|
expect(resolveLucideIconName('runners')).toBe('Server')
|
|
111
111
|
expect(getIconSvgInner('lucide', 'capacity')).toBe(getIconSvgInner('lucide', 'Gauge'))
|
|
112
112
|
})
|
package/src/icons/index.ts
CHANGED
|
@@ -52,7 +52,7 @@ export const SEMANTIC_ICON_ALIASES = {
|
|
|
52
52
|
'api-access': 'KeyRound',
|
|
53
53
|
authentication: 'Lock',
|
|
54
54
|
actors: 'Users',
|
|
55
|
-
bell: '
|
|
55
|
+
bell: 'Bell',
|
|
56
56
|
capacity: 'Gauge',
|
|
57
57
|
'circle-help': 'CircleAlert',
|
|
58
58
|
code: 'Code2',
|
|
@@ -63,7 +63,12 @@ export const SEMANTIC_ICON_ALIASES = {
|
|
|
63
63
|
integrations: 'Plug',
|
|
64
64
|
library: 'BookOpen',
|
|
65
65
|
mitre: 'Grid3X3',
|
|
66
|
-
|
|
66
|
+
dark: 'Moon',
|
|
67
|
+
light: 'Sun',
|
|
68
|
+
midnight: 'Monitor',
|
|
69
|
+
monitor: 'Monitor',
|
|
70
|
+
moon: 'Moon',
|
|
71
|
+
notifications: 'Bell',
|
|
67
72
|
passkeys: 'KeyRound',
|
|
68
73
|
pipeline: 'Workflow',
|
|
69
74
|
platform: 'Building2',
|
package/src/icons/lucide.ts
CHANGED
|
@@ -32,6 +32,7 @@ export const LUCIDE_ICON_PATHS = {
|
|
|
32
32
|
BookOpen: 'M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2zM22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z',
|
|
33
33
|
BarChart3: 'M3 3v18h18M7 16v-5M12 16v-9M17 16v-3',
|
|
34
34
|
Activity: 'M22 12h-4l-3 9L9 3l-3 9H2',
|
|
35
|
+
Bell: 'M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0',
|
|
35
36
|
Siren: 'M7 18a5 5 0 1 1 10 0M5 21h14M21 12h1M3 12H2M5.6 5.6l-.7-.7M18.4 5.6l.7-.7M11 4h2v3h-2z',
|
|
36
37
|
Users:
|
|
37
38
|
'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75',
|
|
@@ -41,6 +42,9 @@ export const LUCIDE_ICON_PATHS = {
|
|
|
41
42
|
'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z',
|
|
42
43
|
PanelLeftClose: 'M3 3h18v18H3zM9 3v18M16 15l-3-3 3-3',
|
|
43
44
|
LogOut: 'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9',
|
|
45
|
+
Monitor:
|
|
46
|
+
'M20 3H4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zM8 21h8M12 17v4',
|
|
47
|
+
Moon: 'M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9',
|
|
44
48
|
Sun: 'M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41M12 7a5 5 0 1 0 0 10 5 5 0 0 0 0-10z',
|
|
45
49
|
ChevronDown: 'm6 9 6 6 6-6',
|
|
46
50
|
ChevronRight: 'm9 6 6 6-6 6',
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,9 @@ describe('TopMenuBar', () => {
|
|
|
21
21
|
expect(topbar.el.querySelector('.cmm-topbar__product')?.textContent).toBe('Threat Library')
|
|
22
22
|
expect(topbar.el.querySelector('.cmm-topbar__current')?.textContent).toBe('Library Dashboard')
|
|
23
23
|
expect(topbar.el.querySelector('[data-action-id="notifications"]')).toBeTruthy()
|
|
24
|
+
expect(
|
|
25
|
+
topbar.el.querySelector('[data-action-id="notifications"] svg path')?.getAttribute('d')
|
|
26
|
+
).toContain('M18 8A6')
|
|
24
27
|
expect(topbar.el.querySelector('.cmm-topbar__avatar')?.textContent).toBe('WR')
|
|
25
28
|
|
|
26
29
|
const trigger = topbar.el.querySelector<HTMLButtonElement>('.cmm-topbar__user-trigger')
|
|
@@ -38,6 +41,79 @@ describe('TopMenuBar', () => {
|
|
|
38
41
|
expect(document.body.contains(topbar.el)).toBe(false)
|
|
39
42
|
})
|
|
40
43
|
|
|
44
|
+
it('renders lowercase theme mode icons for native topbar actions', () => {
|
|
45
|
+
const topbar = new TopMenuBar({
|
|
46
|
+
product: 'Threat Library',
|
|
47
|
+
current: 'Library Dashboard',
|
|
48
|
+
actions: [
|
|
49
|
+
{ id: 'dark-mode', label: 'Dark mode', icon: 'moon' },
|
|
50
|
+
{ id: 'system-mode', label: 'System mode', icon: 'monitor' },
|
|
51
|
+
{ id: 'theme', label: 'Theme', icon: 'theme' },
|
|
52
|
+
],
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
expect(
|
|
56
|
+
topbar.el.querySelector('[data-action-id="dark-mode"] svg path')?.getAttribute('d')
|
|
57
|
+
).toContain('M12 3a6')
|
|
58
|
+
expect(
|
|
59
|
+
topbar.el.querySelector('[data-action-id="system-mode"] svg path')?.getAttribute('d')
|
|
60
|
+
).toContain('M20 3H4')
|
|
61
|
+
expect(
|
|
62
|
+
topbar.el.querySelector('[data-action-id="theme"] svg path')?.getAttribute('d')
|
|
63
|
+
).toContain('M12 2v2')
|
|
64
|
+
|
|
65
|
+
topbar.destroy()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('updates the user menu without moving surrounding actions', () => {
|
|
69
|
+
const beforeAction = document.createElement('span')
|
|
70
|
+
const afterAction = document.createElement('span')
|
|
71
|
+
beforeAction.setAttribute('data-before-action', '')
|
|
72
|
+
afterAction.setAttribute('data-after-action', '')
|
|
73
|
+
|
|
74
|
+
const topbar = new TopMenuBar({
|
|
75
|
+
product: 'Threat Library',
|
|
76
|
+
beforeActions: beforeAction,
|
|
77
|
+
afterActions: afterAction,
|
|
78
|
+
actions: [{ id: 'notifications', label: 'Notifications', icon: 'bell' }],
|
|
79
|
+
user: {
|
|
80
|
+
name: 'CounterMeasure User',
|
|
81
|
+
initials: 'CU',
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
expect(topbar.el.querySelector('.cmm-topbar__avatar')?.textContent).toBe('CU')
|
|
86
|
+
|
|
87
|
+
topbar.updateUser({
|
|
88
|
+
name: 'Wyatt Roersma',
|
|
89
|
+
detail: 'wyattroersma@gmail.com',
|
|
90
|
+
initials: 'WR',
|
|
91
|
+
avatarUrl: '/api/v1/users/user-1/avatar?v=profile.png',
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const actionNodes = Array.from(
|
|
95
|
+
topbar.el.querySelector('.cmm-topbar__actions')?.childNodes ?? []
|
|
96
|
+
)
|
|
97
|
+
const beforeIndex = actionNodes.indexOf(beforeAction)
|
|
98
|
+
const userIndex = actionNodes.indexOf(
|
|
99
|
+
topbar.el.querySelector('.cmm-topbar__user') as HTMLElement
|
|
100
|
+
)
|
|
101
|
+
const afterIndex = actionNodes.indexOf(afterAction)
|
|
102
|
+
|
|
103
|
+
expect(beforeIndex).toBeLessThan(userIndex)
|
|
104
|
+
expect(userIndex).toBeLessThan(afterIndex)
|
|
105
|
+
expect(topbar.el.querySelector('.cmm-topbar__avatar')?.textContent).toBe('')
|
|
106
|
+
expect(topbar.el.querySelector('.cmm-topbar__avatar img')?.getAttribute('src')).toBe(
|
|
107
|
+
'/api/v1/users/user-1/avatar?v=profile.png'
|
|
108
|
+
)
|
|
109
|
+
expect(topbar.el.querySelector('.cmm-topbar__user-name')?.textContent).toBe('Wyatt Roersma')
|
|
110
|
+
expect(topbar.el.querySelector('.cmm-topbar__user-detail')?.textContent).toBe(
|
|
111
|
+
'wyattroersma@gmail.com'
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
topbar.destroy()
|
|
115
|
+
})
|
|
116
|
+
|
|
41
117
|
it('supports native custom action nodes and data-hook attributes', () => {
|
|
42
118
|
const notificationWrapper = document.createElement('div')
|
|
43
119
|
notificationWrapper.setAttribute('data-notification-wrapper', '')
|
package/src/layout/app-shell.ts
CHANGED
|
@@ -180,9 +180,15 @@ function createActionElement(action: TopMenuBarActionInput, iconSet: IconSet): H
|
|
|
180
180
|
export class TopMenuBar {
|
|
181
181
|
public readonly el: HTMLElement
|
|
182
182
|
private readonly cleanupFns: (() => void)[] = []
|
|
183
|
+
private readonly iconSet: IconSet
|
|
184
|
+
private readonly actionsEl: HTMLElement
|
|
185
|
+
private readonly userAnchor: Comment
|
|
186
|
+
private userMenuEl: HTMLElement | null = null
|
|
187
|
+
private userCleanupFns: (() => void)[] = []
|
|
183
188
|
|
|
184
189
|
constructor(config: TopMenuBarConfig = {}) {
|
|
185
190
|
const iconSet = config.iconSet ?? 'lucide'
|
|
191
|
+
this.iconSet = iconSet
|
|
186
192
|
this.el = document.createElement('header')
|
|
187
193
|
this.el.className = ['cmm-topbar', config.className].filter(Boolean).join(' ')
|
|
188
194
|
|
|
@@ -231,6 +237,8 @@ export class TopMenuBar {
|
|
|
231
237
|
|
|
232
238
|
const actions = document.createElement('div')
|
|
233
239
|
actions.classList.add('cmm-topbar__actions')
|
|
240
|
+
this.actionsEl = actions
|
|
241
|
+
this.userAnchor = document.createComment('cmm-topbar-user')
|
|
234
242
|
|
|
235
243
|
appendElements(actions, config.beforeActions)
|
|
236
244
|
|
|
@@ -238,8 +246,9 @@ export class TopMenuBar {
|
|
|
238
246
|
actions.appendChild(createActionElement(action, iconSet))
|
|
239
247
|
}
|
|
240
248
|
|
|
249
|
+
actions.appendChild(this.userAnchor)
|
|
241
250
|
if (config.user !== undefined) {
|
|
242
|
-
|
|
251
|
+
this.updateUser(config.user)
|
|
243
252
|
}
|
|
244
253
|
|
|
245
254
|
appendElements(actions, config.afterActions)
|
|
@@ -253,13 +262,33 @@ export class TopMenuBar {
|
|
|
253
262
|
}
|
|
254
263
|
|
|
255
264
|
destroy(): void {
|
|
265
|
+
this.clearUserMenu()
|
|
256
266
|
for (const cleanup of this.cleanupFns.splice(0)) {
|
|
257
267
|
cleanup()
|
|
258
268
|
}
|
|
259
269
|
this.el.remove()
|
|
260
270
|
}
|
|
261
271
|
|
|
262
|
-
|
|
272
|
+
updateUser(user: TopMenuBarUser): void {
|
|
273
|
+
this.clearUserMenu()
|
|
274
|
+
const userMenu = this.createUserMenu(user, this.iconSet)
|
|
275
|
+
this.actionsEl.insertBefore(userMenu.element, this.userAnchor)
|
|
276
|
+
this.userMenuEl = userMenu.element
|
|
277
|
+
this.userCleanupFns = userMenu.cleanup
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private clearUserMenu(): void {
|
|
281
|
+
for (const cleanup of this.userCleanupFns.splice(0)) {
|
|
282
|
+
cleanup()
|
|
283
|
+
}
|
|
284
|
+
this.userMenuEl?.remove()
|
|
285
|
+
this.userMenuEl = null
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private createUserMenu(
|
|
289
|
+
user: TopMenuBarUser,
|
|
290
|
+
iconSet: IconSet
|
|
291
|
+
): { element: HTMLElement; cleanup: (() => void)[] } {
|
|
263
292
|
const root = document.createElement('div')
|
|
264
293
|
root.classList.add('cmm-topbar__user')
|
|
265
294
|
|
|
@@ -338,14 +367,16 @@ export class TopMenuBar {
|
|
|
338
367
|
|
|
339
368
|
document.addEventListener('pointerdown', closeOnOutsidePointer)
|
|
340
369
|
document.addEventListener('keydown', closeOnEscape)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
370
|
+
const cleanup = [
|
|
371
|
+
(): void => {
|
|
372
|
+
document.removeEventListener('pointerdown', closeOnOutsidePointer)
|
|
373
|
+
document.removeEventListener('keydown', closeOnEscape)
|
|
374
|
+
},
|
|
375
|
+
]
|
|
345
376
|
|
|
346
377
|
root.appendChild(trigger)
|
|
347
378
|
root.appendChild(menu)
|
|
348
|
-
return root
|
|
379
|
+
return { element: root, cleanup }
|
|
349
380
|
}
|
|
350
381
|
}
|
|
351
382
|
|
|
@@ -43,7 +43,7 @@ describe('core app chrome preset', () => {
|
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
it('maps native app routes to topbar labels', () => {
|
|
46
|
-
expect(getCoreAppRouteLabel('/app')).toBe('
|
|
46
|
+
expect(getCoreAppRouteLabel('/app')).toBe('Library Dashboard')
|
|
47
47
|
expect(getCoreAppRouteLabel('/app/dashboards/library')).toBe('Library Dashboard')
|
|
48
48
|
expect(getCoreAppRouteLabel('/app/jobs')).toBe('Jobs')
|
|
49
49
|
expect(getCoreAppRouteLabel('/app/api-access/tokens')).toBe('API Access')
|
|
@@ -60,8 +60,14 @@ describe('core app chrome preset', () => {
|
|
|
60
60
|
},
|
|
61
61
|
})
|
|
62
62
|
|
|
63
|
-
expect(config.variant).toBe('
|
|
64
|
-
expect(config.brand).toMatchObject({
|
|
63
|
+
expect(config.variant).toBe('threat-library')
|
|
64
|
+
expect(config.brand).toMatchObject({
|
|
65
|
+
label: 'CounterMeasure',
|
|
66
|
+
subtitle: 'Platform',
|
|
67
|
+
wordmarkCase: 'title',
|
|
68
|
+
markSize: 24,
|
|
69
|
+
href: '/app',
|
|
70
|
+
})
|
|
65
71
|
expect(config.scope?.options[0]?.label).toBe('All Tenants')
|
|
66
72
|
expect(config.collapsible).toBe(true)
|
|
67
73
|
expect(config.collapseButtonVisible).toBe(true)
|
|
@@ -75,7 +81,7 @@ describe('core app chrome preset', () => {
|
|
|
75
81
|
const config = createCoreAppTopbarConfig({ pathname: '/app/jobs' })
|
|
76
82
|
|
|
77
83
|
expect(actions).toHaveLength(3)
|
|
78
|
-
expect(config.product).toBe('
|
|
84
|
+
expect(config.product).toBe('Threat Library')
|
|
79
85
|
expect(config.current).toBe('Jobs')
|
|
80
86
|
expect(config.actions?.[0]).toMatchObject({
|
|
81
87
|
id: 'search',
|
|
@@ -204,7 +210,11 @@ describe('core app chrome preset', () => {
|
|
|
204
210
|
expect(preset.topbar.current).toBe('Jobs')
|
|
205
211
|
expect(preset.sidebar.pathname).toBe('/app/jobs')
|
|
206
212
|
expect(aside?.classList.contains('sidebar--product')).toBe(true)
|
|
207
|
-
expect(aside?.getAttribute('data-sidebar')).toBe('
|
|
213
|
+
expect(aside?.getAttribute('data-sidebar')).toBe('threat-library')
|
|
214
|
+
expect(container.querySelector('.sidebar__header .cmm-brand-lockup--title')).toBeTruthy()
|
|
215
|
+
expect(
|
|
216
|
+
container.querySelector('.sidebar__header .cmm-brand-lockup__subtitle')?.textContent
|
|
217
|
+
).toBe('Platform')
|
|
208
218
|
expect(active?.classList.contains('sidebar__item--active')).toBe(true)
|
|
209
219
|
expect(active?.getAttribute('aria-current')).toBe('page')
|
|
210
220
|
expect(library?.classList.contains('sidebar__item--active')).toBe(false)
|
|
@@ -283,9 +293,11 @@ describe('core app chrome preset', () => {
|
|
|
283
293
|
})
|
|
284
294
|
|
|
285
295
|
const topbar = topbarContainer.querySelector<HTMLElement>('.cmm-topbar')
|
|
296
|
+
const actionGroup = topbarContainer.querySelector<HTMLElement>('.cmm-topbar__actions')
|
|
286
297
|
expect(topbar?.querySelector('[data-app-search-trigger]')).toBeTruthy()
|
|
287
298
|
expect(topbar?.querySelector('[data-before-actions]')).toBe(beforeActions)
|
|
288
299
|
expect(topbar?.querySelector('[data-notification-toggle]')).toBe(action)
|
|
300
|
+
expect(Array.from(actionGroup?.children ?? []).slice(-2)).toEqual([beforeActions, action])
|
|
289
301
|
expect(action.isConnected).toBe(true)
|
|
290
302
|
|
|
291
303
|
mounted.destroy()
|
|
@@ -243,7 +243,7 @@ export function createCoreAppSidebarSections(basePath = '/app'): SidebarSection[
|
|
|
243
243
|
export function getCoreAppRouteLabel(pathname: string, basePath = '/app'): string {
|
|
244
244
|
const relativePathname = stripBasePath(pathname, basePath)
|
|
245
245
|
if (relativePathname === '/' || relativePathname === '') {
|
|
246
|
-
return '
|
|
246
|
+
return 'Library Dashboard'
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
return (
|
|
@@ -258,7 +258,7 @@ export function createCoreAppSidebarConfig(
|
|
|
258
258
|
const {
|
|
259
259
|
basePath = '/app',
|
|
260
260
|
sections,
|
|
261
|
-
variant = '
|
|
261
|
+
variant = 'threat-library',
|
|
262
262
|
brand,
|
|
263
263
|
scope,
|
|
264
264
|
beforeNav,
|
|
@@ -284,6 +284,9 @@ export function createCoreAppSidebarConfig(
|
|
|
284
284
|
|
|
285
285
|
const resolvedBrand: SidebarBrandConfig = brand ?? {
|
|
286
286
|
label: 'CounterMeasure',
|
|
287
|
+
subtitle: 'Platform',
|
|
288
|
+
wordmarkCase: 'title',
|
|
289
|
+
markSize: 24,
|
|
287
290
|
href: joinBasePath(basePath, '/'),
|
|
288
291
|
}
|
|
289
292
|
|
|
@@ -369,7 +372,7 @@ export function createCoreAppTopbarConfig(
|
|
|
369
372
|
const {
|
|
370
373
|
basePath = '/app',
|
|
371
374
|
pathname = typeof window === 'undefined' ? '/' : window.location.pathname,
|
|
372
|
-
product = '
|
|
375
|
+
product = 'Threat Library',
|
|
373
376
|
current = getCoreAppRouteLabel(pathname, basePath),
|
|
374
377
|
actions,
|
|
375
378
|
beforeActions,
|
|
@@ -545,6 +548,8 @@ function buildTopbarMountOptions(
|
|
|
545
548
|
if (options.pathname !== undefined) topbarOptions.pathname = options.pathname
|
|
546
549
|
if (options.topbarUser !== undefined) topbarOptions.user = options.topbarUser
|
|
547
550
|
if (options.topbarActions !== undefined) topbarOptions.actions = options.topbarActions
|
|
551
|
+
if (options.topbar?.beforeActions !== undefined)
|
|
552
|
+
topbarOptions.beforeActions = options.topbar.beforeActions
|
|
548
553
|
if (afterActions !== undefined) topbarOptions.afterActions = afterActions
|
|
549
554
|
dedupeDefaultActionsForPreservedNodes(topbarOptions, preservedActions)
|
|
550
555
|
|