@tailor-platform/app-shell 1.11.0 → 1.12.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.
@@ -0,0 +1,59 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { describe, expect, it } from "vitest";
5
+
6
+ /**
7
+ * The bridge is what makes semantic tokens reachable as Tailwind utilities in
8
+ * consumer apps (`bg-alert-info-background`). Nothing else in the suite covers
9
+ * it: component snapshots record *class names*, not resolved CSS, so dropping a
10
+ * bridge entry would leave every other test green while the utility silently
11
+ * stops producing colour.
12
+ */
13
+ const here = dirname(fileURLToPath(import.meta.url));
14
+ const bridge = readFileSync(join(here, "theme.bridge.css"), "utf8");
15
+ const defaultTheme = readFileSync(join(here, "themes/default.css"), "utf8");
16
+
17
+ /** Token names defined in the default palette for a given prefix, e.g. `alert`. */
18
+ function definedTokens(prefix: string): string[] {
19
+ const matches = defaultTheme.matchAll(new RegExp(`^\\s*--(${prefix}-[a-z-]+):`, "gm"));
20
+ return [...new Set([...matches].map((m) => m[1]))].toSorted();
21
+ }
22
+
23
+ /** Token names bridged as `--color-<name>`, mapped to the var() they resolve to. */
24
+ function bridgedTokens(prefix: string): Map<string, string> {
25
+ const matches = bridge.matchAll(
26
+ new RegExp(`^\\s*--color-(${prefix}-[a-z-]+):\\s*var\\(--([a-z-]+)\\);`, "gm"),
27
+ );
28
+ return new Map([...matches].map((m) => [m[1], m[2]]));
29
+ }
30
+
31
+ describe("theme bridge", () => {
32
+ describe.each(["alert", "status"])("--%s-* tokens", (prefix) => {
33
+ const defined = definedTokens(prefix);
34
+ const bridged = bridgedTokens(prefix);
35
+
36
+ it("defines at least one token in the default palette", () => {
37
+ expect(defined.length).toBeGreaterThan(0);
38
+ });
39
+
40
+ it("bridges every token defined in the default palette", () => {
41
+ expect([...bridged.keys()].toSorted()).toEqual(defined);
42
+ });
43
+
44
+ it("maps each bridged token to its own source variable", () => {
45
+ // Guards against cross-wiring, e.g. --color-alert-info-foreground
46
+ // accidentally resolving to var(--alert-info-foreground-muted).
47
+ for (const [name, target] of bridged) {
48
+ expect(target).toBe(name);
49
+ }
50
+ });
51
+ });
52
+
53
+ it("bridges all 20 alert slots", () => {
54
+ // 5 variants x 4 slots. Pinned so that dropping a whole variant — which
55
+ // would still satisfy the symmetry checks above if it were removed from
56
+ // both files — fails loudly.
57
+ expect(bridgedTokens("alert").size).toBe(20);
58
+ });
59
+ });
@@ -51,6 +51,27 @@
51
51
  --color-status-attention: var(--status-attention);
52
52
  --color-status-danger: var(--status-danger);
53
53
 
54
+ --color-alert-neutral-background: var(--alert-neutral-background);
55
+ --color-alert-neutral-foreground: var(--alert-neutral-foreground);
56
+ --color-alert-neutral-foreground-muted: var(--alert-neutral-foreground-muted);
57
+ --color-alert-neutral-border: var(--alert-neutral-border);
58
+ --color-alert-success-background: var(--alert-success-background);
59
+ --color-alert-success-foreground: var(--alert-success-foreground);
60
+ --color-alert-success-foreground-muted: var(--alert-success-foreground-muted);
61
+ --color-alert-success-border: var(--alert-success-border);
62
+ --color-alert-warning-background: var(--alert-warning-background);
63
+ --color-alert-warning-foreground: var(--alert-warning-foreground);
64
+ --color-alert-warning-foreground-muted: var(--alert-warning-foreground-muted);
65
+ --color-alert-warning-border: var(--alert-warning-border);
66
+ --color-alert-error-background: var(--alert-error-background);
67
+ --color-alert-error-foreground: var(--alert-error-foreground);
68
+ --color-alert-error-foreground-muted: var(--alert-error-foreground-muted);
69
+ --color-alert-error-border: var(--alert-error-border);
70
+ --color-alert-info-background: var(--alert-info-background);
71
+ --color-alert-info-foreground: var(--alert-info-foreground);
72
+ --color-alert-info-foreground-muted: var(--alert-info-foreground-muted);
73
+ --color-alert-info-border: var(--alert-info-border);
74
+
54
75
  --shadow-xs: var(--semantic-shadow-xs);
55
76
  --shadow-sm: var(--semantic-shadow-sm);
56
77
  --shadow-md: var(--semantic-shadow-md);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/app-shell",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "An opinionated React application framework for building ERP applications on Tailor Platform",
5
5
  "keywords": [
6
6
  "app-shell",
@@ -43,7 +43,7 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "@base-ui/react": "^1.6.0",
46
+ "@base-ui/react": "1.6.0",
47
47
  "@internationalized/date": "3.12.2",
48
48
  "@standard-schema/spec": "^1.1.0",
49
49
  "@tailor-platform/auth-public-client": "^0.5.1",
@@ -61,26 +61,26 @@
61
61
  },
62
62
  "devDependencies": {
63
63
  "@tailwindcss/postcss": "^4.3.0",
64
- "@testing-library/react": "^16.3.2",
65
- "@testing-library/user-event": "^14.6.3",
64
+ "@testing-library/react": "16.3.2",
65
+ "@testing-library/user-event": "14.6.3",
66
66
  "@types/encoding-japanese": "^2.2.1",
67
67
  "@types/node": "^25.9.5",
68
68
  "@types/papaparse": "^5.5.2",
69
69
  "@types/react": "^19",
70
70
  "@types/react-dom": "^19",
71
71
  "@vitejs/plugin-react": "^5.2.0",
72
- "happy-dom": "^20.9.0",
73
- "postcss": "^8.5.26",
72
+ "happy-dom": "20.9.0",
73
+ "postcss": "8.5.26",
74
74
  "react": "^19.2.7",
75
75
  "react-dom": "^19.2.7",
76
76
  "tailwindcss": "^4.2.4",
77
77
  "tw-animate-css": "^1.4.0",
78
- "typescript": "^5.9.3",
78
+ "typescript": "5.9.3",
79
79
  "vite": "^7.3.2",
80
80
  "vite-plugin-dts": "^4.5.0",
81
81
  "vite-plugin-externalize-deps": "^0.10.0",
82
82
  "vite-tsconfig-paths": "^6.1.1",
83
- "vitest": "^4.1.6"
83
+ "vitest": "^4.1.10"
84
84
  },
85
85
  "peerDependencies": {
86
86
  "react": ">=19.2.7",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: app-shell-patterns
3
- description: "Best-practice UI patterns and correct component usage for building pages in apps that use @tailor-platform/app-shell. Use when: building or editing any screen, page, list, table, detail view, form, modal, dialog, wizard, or bulk/confirm/toast interaction in an app with @tailor-platform/app-shell installed — or when choosing the right AppShell component, layout, or design token for a UI."
3
+ description: "Best-practice UI patterns and correct component usage for building pages in apps that use @tailor-platform/app-shell. Use when: building or editing any screen, page, list, table, detail view, form, modal, dialog, wizard, or bulk/confirm/toast interaction in an app with @tailor-platform/app-shell installed — when choosing the right AppShell component, layout, or design token for a UI — or when upgrading @tailor-platform/app-shell, or diagnosing styling, theming, or dark-mode breakage that appeared after a version bump."
4
4
  ---
5
5
 
6
6
  # App-Shell Patterns
@@ -19,6 +19,12 @@ These are the foundational rules that underpin all patterns. All patterns build
19
19
  | [design-system.md](references/fundamental/design-system.md) | design-system reference |
20
20
  | [graphql.md](references/fundamental/graphql.md) | graphql reference |
21
21
 
22
+ ## Migrations
23
+
24
+ [`migrations.md`](references/migrations.md) lists every change that requires editing the consuming app, newest first — what breaks, how to detect it, and what to change.
25
+
26
+ Read it when upgrading `@tailor-platform/app-shell`, and whenever styling, theming, or dark mode looks wrong after a version bump. These breakages are silent: the build succeeds and nothing warns, so the cause is not discoverable from the error output. The package ships no CHANGELOG, so this file is the only migration record available locally.
27
+
22
28
  ## Available Patterns
23
29
 
24
30
  ### detail
@@ -250,7 +250,7 @@ import { Button, Link } from '@tailor-platform/app-shell';
250
250
 
251
251
  **Used in patterns:** `list/*` (filter sheet variant).
252
252
 
253
- **Notes:** Size the panel with **`contentClassName`** (often `astw:*` utilities). Rules → **`design-system.md`** §5.
253
+ **Notes:** Size the panel with `Sheet.Content`'s **`size`** prop (`"sm" | "md" | "lg" | "xl" | "full"`, default `"sm"`) — not a width utility. `astw:` rules → **`design-system.md`** §5.
254
254
 
255
255
  ### `Menu`
256
256
 
@@ -378,7 +378,7 @@ Plus `badgeVariants` CVA for custom-styled siblings.
378
378
 
379
379
  **Notes:**
380
380
 
381
- - **Inside a card?** Pass `containerClassName="astw:px-6"` on `Table.Root` for the horizontal inset, and either drop `Card.Content` (bare list form) or pass `Card.Content className="astw:px-0"` (header+content form). Skipping the `containerClassName` lands the first column flush against the card edge. See the `Card` entry for the two canonical forms and a DON'T example. Dense cell typography (**`text-body-sm`**, **`text-mono`**) → **`design-system.md`** §4 Typography.
381
+ - **Inside a card?** Pass `containerClassName="astw:px-6"` on `Table.Root` for the horizontal inset, and either drop `Card.Content` (bare list form) or pass `Card.Content className="astw:px-0"` (header+content form). Skipping the `containerClassName` lands the first column flush against the card edge. See the `Card` entry for the two canonical forms and a DON'T example. Dense cell typography (**`text-sm`**, **`tabular-nums`** for figures, **`font-mono`** for identifiers) → **`design-system.md`** §4 Typography.
382
382
  - **Whole row is clickable.** Use `<Table.Row onClick={() => navigate(detailPath)} className="astw:cursor-pointer">`. For keyboard and screen-reader users, also wrap the primary identifier cell content in `<Link>` (so the row is reachable via Tab; `Table.Row` is a `<tr>` and cannot itself be a Link — wrapping a `<tr>` in `<a>` is invalid HTML). **No per-row "View" / "Open" / "→" buttons.** Per-row `Menu` (overflow `…`) is the only allowed per-row action surface and is reserved for non-navigation actions like Archive, Duplicate.
383
383
 
384
384
  ### `DataTable`
@@ -387,7 +387,7 @@ Plus `badgeVariants` CVA for custom-styled siblings.
387
387
 
388
388
  **Import:** compound namespace + helpers from `'@tailor-platform/app-shell'`, e.g. `DataTable`, `useDataTable`, `useCollectionVariables`, `createColumnHelper`, and types such as `Column`, `UseDataTableReturn`.
389
389
 
390
- **Purpose:** Production list screens over GraphQL **connections**. Owns toolbar filter chips (**`DataTable.Filters`** from column `filter` configs), header sort, **`DataTable.Pagination`** (cursor-first; First/Last when `total` is provided), loading skeleton/error row, **`onClickRow`**, **`rowActions`** (kebab column), **`onSelectionChange`** (checkbox column), **column pinning** (`pin: "left" | "right"`) and built-in **column settings** (`<DataTable.Toolbar columnSettings>` — user show/hide + reorder + pin, persisted per-user via **`tableId`**).
390
+ **Purpose:** Production list screens over GraphQL **connections**. Owns toolbar filter chips (**`DataTable.Filters`** from column `filter` configs), header sort, **`DataTable.Pagination`** (cursor-first; First/Last when `total` is provided), loading skeleton/error row, **`onClickRow`**, **`rowActions`** (kebab column), **`onSelectionChange`** (checkbox column), **expandable rows** (**`rowExpansion`** — a chevron column plus a full-width detail panel per row; `render` with optional `canExpand` / `getLabel`, and `expandedIds` + `onChange` for controlled mode), **column pinning** (`pin: "left" | "right"`) and built-in **column settings** (`<DataTable.Toolbar columnSettings>` — user show/hide + reorder + pin, persisted per-user via **`tableId`**).
391
391
 
392
392
  **Primitives:** Builds on low-level **`Table`**; do not reinvent pagination/filters manually unless the dataset is trivial.
393
393