@checkstack/auth-frontend 0.5.32 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # @checkstack/auth-frontend
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 950d6ec: Fix mobile UserMenu items rendering at zero height, group menu items by
8
+ section, and unstack cramped card headers on small viewports.
9
+
10
+ - **UserMenu mobile bug**: On mobile, the user-menu Sheet rendered every
11
+ menu item as a grid row, which combined with `flex-shrink: 1` on each
12
+ item collapsed the buttons whose internal layout uses `display: flex`
13
+ (the items registered with `useNavigate` rather than `<Link>`) to zero
14
+ content height. Switched the mobile container to a flex column with
15
+ `[&>*]:shrink-0` and added `min-h-0` so the sheet scrolls correctly
16
+ when the list overflows.
17
+
18
+ - **UserMenu grouping**: Slot extensions now accept an optional `group`
19
+ field. The user menu buckets `UserMenuItemsSlot` extensions by `group`
20
+ and renders each group under a labeled header (`Workspace`,
21
+ `Reliability`, `Configuration`, `Documentation`, `Account`). Existing
22
+ core plugins are tagged with the appropriate group; third-party plugins
23
+ can pick any of these or supply their own label. Untagged extensions
24
+ render last with no header. `UserMenuItemsBottomSlot` is unaffected.
25
+
26
+ - **Card header responsiveness**: `CardHeaderRow` (the primitive shared by
27
+ Incident, Maintenance, Auth, Catalog, GitOps and other config cards) now
28
+ stacks vertically on narrow viewports and only switches to a single row
29
+ at the `sm` breakpoint, so titles and adjacent filter controls (e.g.
30
+ status `Select`, "Show resolved" checkbox) no longer cram together on
31
+ mobile. Refactored the Incident and Maintenance config pages to use the
32
+ primitive instead of a hand-rolled `flex items-center justify-between`
33
+ row, and made their `Select` triggers full-width on mobile.
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [42abfff]
38
+ - Updated dependencies [3547670]
39
+ - Updated dependencies [1ef2e79]
40
+ - Updated dependencies [aa89bc5]
41
+ - Updated dependencies [950d6ec]
42
+ - Updated dependencies [3547670]
43
+ - @checkstack/common@0.9.0
44
+ - @checkstack/ui@1.8.0
45
+ - @checkstack/frontend-api@0.5.0
46
+ - @checkstack/auth-common@0.6.6
47
+
48
+ ## 0.5.33
49
+
50
+ ### Patch Changes
51
+
52
+ - Updated dependencies [50e5f5f]
53
+ - @checkstack/auth-common@0.6.5
54
+ - @checkstack/common@0.8.0
55
+ - @checkstack/ui@1.7.1
56
+ - @checkstack/frontend-api@0.4.2
57
+
3
58
  ## 0.5.32
4
59
 
5
60
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@checkstack/auth-frontend",
3
- "version": "0.5.32",
3
+ "version": "0.6.0",
4
+ "license": "Elastic-2.0",
4
5
  "type": "module",
5
6
  "main": "src/index.tsx",
6
7
  "checkstack": {
@@ -11,27 +12,27 @@
11
12
  "./api": "./src/api.ts"
12
13
  },
13
14
  "scripts": {
14
- "typecheck": "tsc --noEmit",
15
+ "typecheck": "tsgo -b",
15
16
  "lint": "bun run lint:code",
16
17
  "lint:code": "eslint . --max-warnings 0",
17
18
  "test:e2e": "bunx playwright test"
18
19
  },
19
20
  "dependencies": {
20
- "@checkstack/frontend-api": "0.4.0",
21
- "@checkstack/common": "0.7.0",
22
- "@checkstack/ui": "1.6.1",
21
+ "@checkstack/frontend-api": "0.4.2",
22
+ "@checkstack/common": "0.8.0",
23
+ "@checkstack/ui": "1.7.1",
23
24
  "react": "^18.2.0",
24
25
  "react-router-dom": "^6.22.0",
25
26
  "lucide-react": "^0.344.0",
26
27
  "better-auth": "^1.1.8",
27
- "@checkstack/auth-common": "0.6.3"
28
+ "@checkstack/auth-common": "0.6.5"
28
29
  },
29
30
  "devDependencies": {
30
31
  "typescript": "^5.0.0",
31
32
  "@types/react": "^18.2.0",
32
33
  "@playwright/test": "^1.49.0",
33
- "@checkstack/test-utils-frontend": "0.0.4",
34
- "@checkstack/tsconfig": "0.0.5",
35
- "@checkstack/scripts": "0.1.2"
34
+ "@checkstack/test-utils-frontend": "0.0.5",
35
+ "@checkstack/tsconfig": "0.0.7",
36
+ "@checkstack/scripts": "0.3.0"
36
37
  }
37
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();
package/tsconfig.json CHANGED
@@ -2,5 +2,22 @@
2
2
  "extends": "@checkstack/tsconfig/frontend.json",
3
3
  "include": [
4
4
  "src"
5
+ ],
6
+ "references": [
7
+ {
8
+ "path": "../auth-common"
9
+ },
10
+ {
11
+ "path": "../common"
12
+ },
13
+ {
14
+ "path": "../frontend-api"
15
+ },
16
+ {
17
+ "path": "../test-utils-frontend"
18
+ },
19
+ {
20
+ "path": "../ui"
21
+ }
5
22
  ]
6
- }
23
+ }