@checkstack/auth-frontend 0.5.33 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # @checkstack/auth-frontend
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [9016526]
8
+ - @checkstack/common@0.10.0
9
+ - @checkstack/auth-common@0.7.0
10
+ - @checkstack/frontend-api@0.5.1
11
+ - @checkstack/ui@1.8.1
12
+
13
+ ## 0.6.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 950d6ec: Fix mobile UserMenu items rendering at zero height, group menu items by
18
+ section, and unstack cramped card headers on small viewports.
19
+
20
+ - **UserMenu mobile bug**: On mobile, the user-menu Sheet rendered every
21
+ menu item as a grid row, which combined with `flex-shrink: 1` on each
22
+ item collapsed the buttons whose internal layout uses `display: flex`
23
+ (the items registered with `useNavigate` rather than `<Link>`) to zero
24
+ content height. Switched the mobile container to a flex column with
25
+ `[&>*]:shrink-0` and added `min-h-0` so the sheet scrolls correctly
26
+ when the list overflows.
27
+
28
+ - **UserMenu grouping**: Slot extensions now accept an optional `group`
29
+ field. The user menu buckets `UserMenuItemsSlot` extensions by `group`
30
+ and renders each group under a labeled header (`Workspace`,
31
+ `Reliability`, `Configuration`, `Documentation`, `Account`). Existing
32
+ core plugins are tagged with the appropriate group; third-party plugins
33
+ can pick any of these or supply their own label. Untagged extensions
34
+ render last with no header. `UserMenuItemsBottomSlot` is unaffected.
35
+
36
+ - **Card header responsiveness**: `CardHeaderRow` (the primitive shared by
37
+ Incident, Maintenance, Auth, Catalog, GitOps and other config cards) now
38
+ stacks vertically on narrow viewports and only switches to a single row
39
+ at the `sm` breakpoint, so titles and adjacent filter controls (e.g.
40
+ status `Select`, "Show resolved" checkbox) no longer cram together on
41
+ mobile. Refactored the Incident and Maintenance config pages to use the
42
+ primitive instead of a hand-rolled `flex items-center justify-between`
43
+ row, and made their `Select` triggers full-width on mobile.
44
+
45
+ ### Patch Changes
46
+
47
+ - Updated dependencies [42abfff]
48
+ - Updated dependencies [3547670]
49
+ - Updated dependencies [1ef2e79]
50
+ - Updated dependencies [aa89bc5]
51
+ - Updated dependencies [950d6ec]
52
+ - Updated dependencies [3547670]
53
+ - @checkstack/common@0.9.0
54
+ - @checkstack/ui@1.8.0
55
+ - @checkstack/frontend-api@0.5.0
56
+ - @checkstack/auth-common@0.6.6
57
+
3
58
  ## 0.5.33
4
59
 
5
60
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/auth-frontend",
3
- "version": "0.5.33",
3
+ "version": "0.6.1",
4
4
  "license": "Elastic-2.0",
5
5
  "type": "module",
6
6
  "main": "src/index.tsx",
@@ -18,21 +18,21 @@
18
18
  "test:e2e": "bunx playwright test"
19
19
  },
20
20
  "dependencies": {
21
- "@checkstack/frontend-api": "0.4.1",
22
- "@checkstack/common": "0.7.0",
23
- "@checkstack/ui": "1.7.0",
21
+ "@checkstack/frontend-api": "0.5.0",
22
+ "@checkstack/common": "0.9.0",
23
+ "@checkstack/ui": "1.8.0",
24
24
  "react": "^18.2.0",
25
25
  "react-router-dom": "^6.22.0",
26
26
  "lucide-react": "^0.344.0",
27
27
  "better-auth": "^1.1.8",
28
- "@checkstack/auth-common": "0.6.4"
28
+ "@checkstack/auth-common": "0.6.6"
29
29
  },
30
30
  "devDependencies": {
31
31
  "typescript": "^5.0.0",
32
32
  "@types/react": "^18.2.0",
33
33
  "@playwright/test": "^1.49.0",
34
- "@checkstack/test-utils-frontend": "0.0.4",
35
- "@checkstack/tsconfig": "0.0.6",
36
- "@checkstack/scripts": "0.1.2"
34
+ "@checkstack/test-utils-frontend": "0.0.5",
35
+ "@checkstack/tsconfig": "0.0.7",
36
+ "@checkstack/scripts": "0.3.1"
37
37
  }
38
38
  }
@@ -9,6 +9,7 @@ import {
9
9
  UserMenuItemsSlot,
10
10
  UserMenuItemsBottomSlot,
11
11
  UserMenuItemsContext,
12
+ Extension,
12
13
  } from "@checkstack/frontend-api";
13
14
  import { AuthApi, authRoutes } from "@checkstack/auth-common";
14
15
  import { resolveRoute } from "@checkstack/common";
@@ -24,6 +25,7 @@ import {
24
25
  CardFooter,
25
26
  UserMenu,
26
27
  DropdownMenuItem,
28
+ DropdownMenuLabel,
27
29
  DropdownMenuSeparator,
28
30
  Alert,
29
31
  AlertIcon,
@@ -416,6 +418,62 @@ export const LogoutMenuItem = (_props: UserMenuItemsContext) => {
416
418
  );
417
419
  };
418
420
 
421
+ /**
422
+ * Canonical user-menu group labels and their display order. Extensions tag
423
+ * themselves via `group` on their slot registration; anything that doesn't
424
+ * match a known group falls into a trailing label-less bucket.
425
+ */
426
+ const USER_MENU_GROUP_ORDER: readonly string[] = [
427
+ "Workspace",
428
+ "Reliability",
429
+ "Configuration",
430
+ "Documentation",
431
+ "Account",
432
+ ];
433
+
434
+ type UserMenuExtension = Extension<typeof UserMenuItemsSlot>;
435
+
436
+ function groupTopExtensions(
437
+ extensions: UserMenuExtension[],
438
+ ): Array<{ label: string | undefined; items: UserMenuExtension[] }> {
439
+ const buckets = new Map<string, UserMenuExtension[]>();
440
+ const ungrouped: UserMenuExtension[] = [];
441
+
442
+ for (const ext of extensions) {
443
+ const groupName = ext.metadata?.group;
444
+ if (!groupName) {
445
+ ungrouped.push(ext);
446
+ continue;
447
+ }
448
+ const list = buckets.get(groupName);
449
+ if (list) {
450
+ list.push(ext);
451
+ } else {
452
+ buckets.set(groupName, [ext]);
453
+ }
454
+ }
455
+
456
+ const result: Array<{ label: string | undefined; items: UserMenuExtension[] }> = [];
457
+ // Known groups in canonical order.
458
+ for (const name of USER_MENU_GROUP_ORDER) {
459
+ const items = buckets.get(name);
460
+ if (items) {
461
+ result.push({ label: name, items });
462
+ buckets.delete(name);
463
+ }
464
+ }
465
+ // Any extra groups plugins introduced — render in encounter order, alphabetised.
466
+ const extras = [...buckets.entries()].toSorted(([a], [b]) => a.localeCompare(b));
467
+ for (const [name, items] of extras) {
468
+ result.push({ label: name, items });
469
+ }
470
+ // Untagged extensions go last with no header.
471
+ if (ungrouped.length > 0) {
472
+ result.push({ label: undefined, items: ungrouped });
473
+ }
474
+ return result;
475
+ }
476
+
419
477
  export const LoginNavbarAction = () => {
420
478
  const authApi = useApi(authApiRef);
421
479
  const { data: session, isPending } = authApi.useSession();
@@ -445,7 +503,9 @@ export const LoginNavbarAction = () => {
445
503
  }
446
504
 
447
505
  if (session?.user) {
448
- // Check if we have any bottom items to decide if we need a separator
506
+ const topExtensions = pluginRegistry.getExtensions(
507
+ UserMenuItemsSlot.id,
508
+ ) as UserMenuExtension[];
449
509
  const bottomExtensions = pluginRegistry.getExtensions(
450
510
  UserMenuItemsBottomSlot.id,
451
511
  );
@@ -454,10 +514,20 @@ export const LoginNavbarAction = () => {
454
514
  accessRules,
455
515
  hasCredentialAccount,
456
516
  };
517
+ const groups = groupTopExtensions(topExtensions);
457
518
 
458
519
  return (
459
520
  <UserMenu user={session.user}>
460
- <ExtensionSlot slot={UserMenuItemsSlot} context={menuContext} />
521
+ {groups.map(({ label, items }, groupIndex) => (
522
+ <React.Fragment key={label ?? `__group-${groupIndex}`}>
523
+ {groupIndex > 0 && <DropdownMenuSeparator />}
524
+ {label && <DropdownMenuLabel>{label}</DropdownMenuLabel>}
525
+ {items.map((ext) => {
526
+ const Component = ext.component as React.ComponentType<UserMenuItemsContext>;
527
+ return <Component key={ext.id} {...menuContext} />;
528
+ })}
529
+ </React.Fragment>
530
+ ))}
461
531
  {hasBottomItems && <DropdownMenuSeparator />}
462
532
  <ExtensionSlot slot={UserMenuItemsBottomSlot} context={menuContext} />
463
533
  </UserMenu>
package/src/index.tsx CHANGED
@@ -178,6 +178,7 @@ export const authPlugin = createFrontendPlugin({
178
178
  },
179
179
  createSlotExtension(UserMenuItemsSlot, {
180
180
  id: "auth.user-menu.settings",
181
+ metadata: { group: "Configuration" },
181
182
  component: ({ accessRules: userPerms }: UserMenuItemsContext) => {
182
183
  // eslint-disable-next-line react-hooks/rules-of-hooks -- Inline component used via createSlotExtension
183
184
  const navigate = useNavigate();
@@ -199,6 +200,7 @@ export const authPlugin = createFrontendPlugin({
199
200
  }),
200
201
  createSlotExtension(UserMenuItemsSlot, {
201
202
  id: "auth.user-menu.profile",
203
+ metadata: { group: "Account" },
202
204
  component: () => {
203
205
  // eslint-disable-next-line react-hooks/rules-of-hooks -- Inline component used via createSlotExtension
204
206
  const navigate = useNavigate();