basalt-ui 1.24.0 → 1.26.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/AGENTS.md +8 -6
- package/CHANGELOG.md +25 -0
- package/MIGRATING.md +547 -21
- package/README.md +46 -33
- package/agent/rules/basalt-batteries.md +205 -0
- package/agent/rules/basalt-charts.md +125 -555
- package/agent/rules/basalt-controls.md +155 -0
- package/agent/rules/basalt-mantine.md +146 -439
- package/agent/rules/basalt-state.md +113 -96
- package/agent/rules/basalt-tokens.md +126 -288
- package/agent/skills/basalt-app/SKILL.md +70 -195
- package/agent/skills/basalt-charts/SKILL.md +54 -234
- package/agent/skills/basalt-design/SKILL.md +56 -248
- package/agent/templates/CLAUDE-block.md.tpl +31 -40
- package/agent/templates/DESIGN.md.tpl +22 -51
- package/configs/oxlint-plugin.js +1275 -31
- package/configs/oxlint-plugin.test.ts +947 -11
- package/configs/oxlint.json +11 -3
- package/dist/charts/cursor/resolve.d.ts +18 -0
- package/dist/charts/cursor/resolve.d.ts.map +1 -1
- package/dist/charts/cursor/resolve.js +15 -6
- package/dist/charts/cursor/scope.d.ts +6 -3
- package/dist/charts/cursor/scope.d.ts.map +1 -1
- package/dist/charts/cursor/store.d.ts +5 -1
- package/dist/charts/cursor/store.d.ts.map +1 -1
- package/dist/charts/cursor/store.js +4 -4
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +9 -8
- package/dist/charts/index.d.ts +2 -2
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.d.ts +34 -9
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +29 -114
- package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
- package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
- package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
- package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/BarSparkline.js +18 -4
- package/dist/cli/check-coverage.test.d.ts +2 -0
- package/dist/cli/check-coverage.test.d.ts.map +1 -0
- package/dist/cli/index.d.ts +67 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +225 -7
- package/dist/connectivity/connectivity-indicator.d.ts +4 -1
- package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
- package/dist/connectivity/connectivity-indicator.js +1 -1
- package/dist/content/index.d.ts +5 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -2
- package/dist/content/prose.module.css +7 -28
- package/dist/controls/actions.d.ts +169 -0
- package/dist/controls/actions.d.ts.map +1 -0
- package/dist/controls/actions.js +287 -0
- package/dist/controls/actions.module.css +62 -0
- package/dist/controls/actions.module.css.d.ts +13 -0
- package/dist/controls/actions.test.d.ts +2 -0
- package/dist/controls/actions.test.d.ts.map +1 -0
- package/dist/controls/actions.type-guard.test.d.ts +2 -0
- package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
- package/dist/controls/compare-filter.d.ts +41 -0
- package/dist/controls/compare-filter.d.ts.map +1 -0
- package/dist/controls/compare-filter.js +23 -0
- package/dist/controls/control-group.d.ts +19 -0
- package/dist/controls/control-group.d.ts.map +1 -0
- package/dist/controls/control-group.js +20 -0
- package/dist/controls/control-group.test.d.ts +2 -0
- package/dist/controls/control-group.test.d.ts.map +1 -0
- package/dist/controls/controls.module.css +542 -0
- package/dist/controls/controls.module.css.d.ts +35 -0
- package/dist/controls/controls.router.test.d.ts +2 -0
- package/dist/controls/controls.router.test.d.ts.map +1 -0
- package/dist/controls/enum-filter.d.ts +30 -0
- package/dist/controls/enum-filter.d.ts.map +1 -0
- package/dist/controls/enum-filter.js +91 -0
- package/dist/controls/filter-context.d.ts +55 -0
- package/dist/controls/filter-context.d.ts.map +1 -0
- package/dist/controls/filter-context.js +69 -0
- package/dist/controls/filter-pill.d.ts +28 -0
- package/dist/controls/filter-pill.d.ts.map +1 -0
- package/dist/controls/filter-pill.js +70 -0
- package/dist/controls/filter-set.d.ts +13 -0
- package/dist/controls/filter-set.d.ts.map +1 -0
- package/dist/controls/filter-set.js +125 -0
- package/dist/controls/filter-set.test.d.ts +2 -0
- package/dist/controls/filter-set.test.d.ts.map +1 -0
- package/dist/controls/filter-sheet.d.ts +117 -0
- package/dist/controls/filter-sheet.d.ts.map +1 -0
- package/dist/controls/filter-sheet.js +146 -0
- package/dist/controls/glyphs.d.ts +27 -0
- package/dist/controls/glyphs.d.ts.map +1 -0
- package/dist/controls/glyphs.js +49 -0
- package/dist/controls/index.d.ts +19 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/index.js +44 -0
- package/dist/controls/multi-select-filter.d.ts +23 -0
- package/dist/controls/multi-select-filter.d.ts.map +1 -0
- package/dist/controls/multi-select-filter.js +89 -0
- package/dist/controls/peer-boundary.test.d.ts +2 -0
- package/dist/controls/peer-boundary.test.d.ts.map +1 -0
- package/dist/controls/props-contract.test.d.ts +2 -0
- package/dist/controls/props-contract.test.d.ts.map +1 -0
- package/dist/controls/range-filter.d.ts +29 -0
- package/dist/controls/range-filter.d.ts.map +1 -0
- package/dist/controls/range-filter.js +120 -0
- package/dist/controls/search-filter.d.ts +10 -0
- package/dist/controls/search-filter.d.ts.map +1 -0
- package/dist/controls/search-filter.js +81 -0
- package/dist/controls/select-filter.d.ts +74 -0
- package/dist/controls/select-filter.d.ts.map +1 -0
- package/dist/controls/select-filter.js +10 -0
- package/dist/controls/sync-button.d.ts +38 -0
- package/dist/controls/sync-button.d.ts.map +1 -0
- package/dist/controls/sync-button.js +119 -0
- package/dist/controls/sync-button.module.css +63 -0
- package/dist/controls/sync-button.module.css.d.ts +11 -0
- package/dist/controls/sync-button.test.d.ts +2 -0
- package/dist/controls/sync-button.test.d.ts.map +1 -0
- package/dist/controls/toggle-filter.d.ts +9 -0
- package/dist/controls/toggle-filter.d.ts.map +1 -0
- package/dist/controls/toggle-filter.js +42 -0
- package/dist/controls/view-tabs.d.ts +23 -0
- package/dist/controls/view-tabs.d.ts.map +1 -0
- package/dist/controls/view-tabs.js +59 -0
- package/dist/controls-dates/date-range-picker.d.ts +4 -0
- package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
- package/dist/controls-dates/date-range-picker.js +18 -0
- package/dist/controls-dates/index.d.ts +9 -0
- package/dist/controls-dates/index.d.ts.map +1 -0
- package/dist/controls-dates/index.js +4 -0
- package/dist/dashboard/delta-badge.d.ts +6 -12
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +1 -50
- package/dist/dashboard/query-state.d.ts +1 -1
- package/dist/dashboard/settings-section.d.ts +6 -4
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +23 -16
- package/dist/dashboard/settings-section.module.css +10 -0
- package/dist/dashboard/settings-section.module.css.d.ts +1 -0
- package/dist/dashboard/settings-section.test.d.ts +2 -0
- package/dist/dashboard/settings-section.test.d.ts.map +1 -0
- package/dist/dashboard/stat-card.d.ts +44 -9
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +80 -48
- package/dist/dashboard/stat-card.module.css +82 -0
- package/dist/dashboard/stat-card.module.css.d.ts +11 -0
- package/dist/data/data-table.d.ts +102 -20
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +166 -65
- package/dist/data/table.d.ts +1 -1
- package/dist/data/table.d.ts.map +1 -1
- package/dist/guard/grace.test.d.ts +20 -0
- package/dist/guard/grace.test.d.ts.map +1 -0
- package/dist/guard/index.d.ts +66 -3
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +82 -7
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -10
- package/dist/nav/types.d.ts +78 -2
- package/dist/nav/types.d.ts.map +1 -1
- package/dist/notifications/bell.d.ts +4 -1
- package/dist/notifications/bell.d.ts.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/router-tanstack/field.d.ts +14 -0
- package/dist/router-tanstack/field.d.ts.map +1 -0
- package/dist/router-tanstack/field.js +4 -0
- package/dist/router-tanstack/index.d.ts +6 -2
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +4 -0
- package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/multi-search-param-store.js +19 -27
- package/dist/router-tanstack/search-param-store.d.ts +3 -87
- package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-param-store.js +16 -45
- package/dist/router-tanstack/search-store.d.ts +81 -0
- package/dist/router-tanstack/search-store.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.js +187 -0
- package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
- package/dist/router-tanstack/use-nav.d.ts +0 -2
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +34 -12
- package/dist/router-tanstack/use-nav.test.d.ts +2 -0
- package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
- package/dist/section/index.d.ts +2 -0
- package/dist/section/index.d.ts.map +1 -0
- package/dist/section/index.js +4 -0
- package/dist/section/section.d.ts +24 -0
- package/dist/section/section.d.ts.map +1 -0
- package/dist/section/section.js +126 -0
- package/dist/section/section.module.css +104 -0
- package/dist/section/section.module.css.d.ts +11 -0
- package/dist/section/section.test.d.ts +2 -0
- package/dist/section/section.test.d.ts.map +1 -0
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.js +18 -15
- package/dist/shell/app-header.module.css +77 -37
- package/dist/shell/app-header.module.css.d.ts +2 -1
- package/dist/shell/app-mobile-nav.d.ts +9 -2
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +98 -11
- package/dist/shell/app-mobile-nav.module.css +8 -0
- package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/dist/shell/app-sidebar.d.ts +55 -28
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +116 -67
- package/dist/shell/app-sidebar.module.css +303 -20
- package/dist/shell/app-sidebar.module.css.d.ts +19 -1
- package/dist/shell/index.d.ts +55 -21
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +40 -18
- package/dist/shell/mobile-nav-model.d.ts +13 -2
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +15 -1
- package/dist/shell/nav-count-badge.d.ts +1 -1
- package/dist/shell/page-bar.d.ts +51 -0
- package/dist/shell/page-bar.d.ts.map +1 -0
- package/dist/shell/page-bar.js +154 -0
- package/dist/shell/page-bar.module.css +178 -0
- package/dist/shell/page-bar.module.css.d.ts +17 -0
- package/dist/shell/page-bar.test.d.ts +2 -0
- package/dist/shell/page-bar.test.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.d.ts +48 -0
- package/dist/shell/sidebar-block-model.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.js +39 -0
- package/dist/shell/sidebar-blocks.d.ts +32 -0
- package/dist/shell/sidebar-blocks.d.ts.map +1 -0
- package/dist/shell/sidebar-blocks.js +169 -0
- package/dist/shell/sidebar-search.d.ts +13 -1
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +34 -1
- package/dist/shell/sidebar-search.module.css +26 -0
- package/dist/shell/sidebar-search.module.css.d.ts +2 -0
- package/dist/state/fields.d.ts +392 -0
- package/dist/state/fields.d.ts.map +1 -0
- package/dist/state/fields.js +369 -0
- package/dist/state/fields.test.d.ts +2 -0
- package/dist/state/fields.test.d.ts.map +1 -0
- package/dist/state/persisted.d.ts +48 -0
- package/dist/state/persisted.d.ts.map +1 -0
- package/dist/state/persisted.js +105 -0
- package/dist/state.d.ts +11 -56
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +14 -120
- package/dist/styles.css +13 -0
- package/dist/surfaces.d.ts +106 -24
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +155 -19
- package/dist/theme/controls.module.css +16 -1
- package/dist/theme/ctl-theme.d.ts +32 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -0
- package/dist/theme/ctl-theme.js +54 -0
- package/dist/theme/ctl-theme.test.d.ts +2 -0
- package/dist/theme/ctl-theme.test.d.ts.map +1 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
- package/dist/theme/divider-law.test.d.ts +2 -0
- package/dist/theme/divider-law.test.d.ts.map +1 -0
- package/dist/theme/icon-slot.d.ts +44 -0
- package/dist/theme/icon-slot.d.ts.map +1 -0
- package/dist/theme/icon-slot.js +21 -0
- package/dist/theme/icon-slot.module.css +47 -0
- package/dist/theme/icon-slot.module.css.d.ts +8 -0
- package/dist/theme/icon-slot.test.d.ts +2 -0
- package/dist/theme/icon-slot.test.d.ts.map +1 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +82 -5
- package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
- package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
- package/dist/theme/segmented-control.module.css +20 -0
- package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
- package/dist/tokens/index.d.ts +12 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +38 -8
- package/dist/tokens/palette.d.ts +97 -61
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +76 -38
- package/dist/tokens.css +10 -2
- package/dist/widget-header/delta-badge.d.ts +13 -0
- package/dist/widget-header/delta-badge.d.ts.map +1 -0
- package/dist/widget-header/delta-badge.js +29 -0
- package/dist/widget-header/delta-badge.test.d.ts +2 -0
- package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
- package/dist/widget-header/index.d.ts +3 -0
- package/dist/widget-header/index.d.ts.map +1 -0
- package/dist/widget-header/index.js +6 -0
- package/dist/widget-header/no-mantine.test.d.ts +2 -0
- package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
- package/dist/widget-header/widget-header.d.ts +37 -0
- package/dist/widget-header/widget-header.d.ts.map +1 -0
- package/dist/widget-header/widget-header.js +77 -0
- package/dist/widget-header/widget-header.module.css +335 -0
- package/dist/widget-header/widget-header.module.css.d.ts +24 -0
- package/dist/widget-header/widget-header.test.d.ts +2 -0
- package/dist/widget-header/widget-header.test.d.ts.map +1 -0
- package/llms.txt +30 -17
- package/package.json +14 -1
- package/src/charts/cursor/resolve.test.ts +29 -1
- package/src/charts/cursor/resolve.ts +38 -6
- package/src/charts/cursor/scope.tsx +6 -3
- package/src/charts/cursor/store.test.ts +19 -1
- package/src/charts/cursor/store.ts +10 -5
- package/src/charts/hooks/useChartCursor.ts +16 -8
- package/src/charts/index.ts +2 -2
- package/src/charts/primitives/CartesianChart.test.tsx +124 -0
- package/src/charts/primitives/ChartCard.test.tsx +66 -0
- package/src/charts/primitives/ChartCard.tsx +86 -146
- package/src/charts/sparklines/BarSparkline.tsx +43 -5
- package/src/cli/check-coverage.test.ts +297 -0
- package/src/cli/check-theme.test.ts +38 -14
- package/src/cli/index.ts +392 -16
- package/src/cli/init-sync.test.ts +97 -0
- package/src/connectivity/connectivity-indicator.tsx +16 -9
- package/src/content/index.ts +16 -3
- package/src/content/prose.module.css +7 -28
- package/src/content/prose.module.css.test.ts +8 -24
- package/src/controls/actions.module.css +62 -0
- package/src/controls/actions.module.css.d.ts +13 -0
- package/src/controls/actions.test.tsx +252 -0
- package/src/controls/actions.tsx +655 -0
- package/src/controls/actions.type-guard.test.ts +63 -0
- package/src/controls/compare-filter.tsx +64 -0
- package/src/controls/control-group.test.tsx +229 -0
- package/src/controls/control-group.tsx +63 -0
- package/src/controls/controls.module.css +542 -0
- package/src/controls/controls.module.css.d.ts +35 -0
- package/src/controls/controls.router.test.tsx +637 -0
- package/src/controls/enum-filter.tsx +159 -0
- package/src/controls/filter-context.tsx +153 -0
- package/src/controls/filter-pill.tsx +120 -0
- package/src/controls/filter-set.test.tsx +472 -0
- package/src/controls/filter-set.tsx +225 -0
- package/src/controls/filter-sheet.tsx +285 -0
- package/src/controls/glyphs.tsx +81 -0
- package/src/controls/index.ts +48 -0
- package/src/controls/multi-select-filter.tsx +140 -0
- package/src/controls/peer-boundary.test.ts +85 -0
- package/src/controls/props-contract.test.tsx +106 -0
- package/src/controls/range-filter.tsx +184 -0
- package/src/controls/search-filter.tsx +123 -0
- package/src/controls/select-filter.tsx +89 -0
- package/src/controls/sync-button.module.css +63 -0
- package/src/controls/sync-button.module.css.d.ts +11 -0
- package/src/controls/sync-button.test.tsx +183 -0
- package/src/controls/sync-button.tsx +197 -0
- package/src/controls/toggle-filter.tsx +60 -0
- package/src/controls/view-tabs.tsx +110 -0
- package/src/controls-dates/date-range-picker.tsx +42 -0
- package/src/controls-dates/index.ts +8 -0
- package/src/dashboard/delta-badge.tsx +4 -74
- package/src/dashboard/query-state.tsx +1 -1
- package/src/dashboard/settings-section.module.css +10 -0
- package/src/dashboard/settings-section.module.css.d.ts +1 -0
- package/src/dashboard/settings-section.test.tsx +56 -0
- package/src/dashboard/settings-section.tsx +33 -30
- package/src/dashboard/stat-card.module.css +82 -0
- package/src/dashboard/stat-card.module.css.d.ts +11 -0
- package/src/dashboard/stat-card.test.tsx +67 -1
- package/src/dashboard/stat-card.tsx +209 -68
- package/src/data/data-table.test.tsx +282 -6
- package/src/data/data-table.tsx +429 -94
- package/src/data/table.ts +2 -0
- package/src/guard/check-source.test.ts +126 -13
- package/src/guard/grace.test.ts +149 -0
- package/src/guard/index.ts +172 -29
- package/src/guard/types.ts +4 -2
- package/src/index.ts +46 -5
- package/src/nav/types.ts +84 -2
- package/src/notifications/bell.tsx +6 -3
- package/src/notifications/index.ts +1 -1
- package/src/router-tanstack/field.ts +31 -0
- package/src/router-tanstack/index.ts +24 -2
- package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
- package/src/router-tanstack/multi-search-param-store.ts +46 -77
- package/src/router-tanstack/search-param-store.ts +42 -159
- package/src/router-tanstack/search-store.router.test.tsx +661 -0
- package/src/router-tanstack/search-store.test.ts +418 -0
- package/src/router-tanstack/search-store.ts +380 -0
- package/src/router-tanstack/use-nav.test.tsx +187 -0
- package/src/router-tanstack/use-nav.ts +72 -14
- package/src/section/index.ts +1 -0
- package/src/section/section.module.css +104 -0
- package/src/section/section.module.css.d.ts +11 -0
- package/src/section/section.test.tsx +204 -0
- package/src/section/section.tsx +220 -0
- package/src/shell/app-breadcrumbs.tsx +44 -31
- package/src/shell/app-header.module.css +77 -37
- package/src/shell/app-header.module.css.d.ts +2 -1
- package/src/shell/app-mobile-nav.module.css +8 -0
- package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.tsx +147 -8
- package/src/shell/app-sidebar-account.tsx +2 -2
- package/src/shell/app-sidebar.module.css +303 -20
- package/src/shell/app-sidebar.module.css.d.ts +19 -1
- package/src/shell/app-sidebar.test.tsx +456 -41
- package/src/shell/app-sidebar.tsx +276 -140
- package/src/shell/index.test.tsx +291 -5
- package/src/shell/index.tsx +137 -38
- package/src/shell/mobile-nav-model.test.ts +228 -7
- package/src/shell/mobile-nav-model.ts +53 -3
- package/src/shell/nav-count-badge.tsx +1 -1
- package/src/shell/page-bar.module.css +178 -0
- package/src/shell/page-bar.module.css.d.ts +17 -0
- package/src/shell/page-bar.test.tsx +316 -0
- package/src/shell/page-bar.tsx +315 -0
- package/src/shell/sidebar-block-model.ts +83 -0
- package/src/shell/sidebar-blocks.tsx +301 -0
- package/src/shell/sidebar-search.module.css +26 -0
- package/src/shell/sidebar-search.module.css.d.ts +2 -0
- package/src/shell/sidebar-search.tsx +71 -2
- package/src/state/fields.test.tsx +258 -0
- package/src/state/fields.ts +892 -0
- package/src/state/persisted.ts +211 -0
- package/src/state.ts +46 -240
- package/src/styles.css +13 -0
- package/src/surfaces.ts +203 -36
- package/src/theme/controls.module.css +16 -1
- package/src/theme/ctl-theme.test.tsx +275 -0
- package/src/theme/ctl-theme.tsx +138 -0
- package/src/theme/ctl-tier-coverage.test.ts +170 -0
- package/src/theme/density-relations.test.ts +106 -51
- package/src/theme/divider-law.test.ts +259 -0
- package/src/theme/icon-slot.module.css +47 -0
- package/src/theme/icon-slot.module.css.d.ts +8 -0
- package/src/theme/icon-slot.test.tsx +275 -0
- package/src/theme/icon-slot.tsx +59 -0
- package/src/theme/index.ts +165 -3
- package/src/theme/layout-rhythm-css.test.ts +72 -0
- package/src/theme/radius.test.ts +11 -1
- package/src/theme/segmented-control.module.css +20 -0
- package/src/theme/shadow-surfaces.test.ts +20 -7
- package/src/theme/spacing.test.ts +75 -27
- package/src/theme/use-basalt-spacing.ts +2 -2
- package/src/tokens/density.test.ts +7 -25
- package/src/tokens/index.ts +48 -8
- package/src/tokens/palette.ts +107 -73
- package/src/widget-header/delta-badge.test.tsx +60 -0
- package/src/widget-header/delta-badge.tsx +57 -0
- package/src/widget-header/index.ts +2 -0
- package/src/widget-header/no-mantine.test.ts +33 -0
- package/src/widget-header/widget-header.module.css +335 -0
- package/src/widget-header/widget-header.module.css.d.ts +24 -0
- package/src/widget-header/widget-header.test.tsx +187 -0
- package/src/widget-header/widget-header.tsx +168 -0
- package/agent/rules/basalt-agent.md +0 -452
- package/agent/rules/basalt-app.md +0 -246
- package/agent/rules/basalt-commands.md +0 -320
- package/agent/rules/basalt-content.md +0 -409
- package/agent/rules/basalt-data.md +0 -269
- package/agent/rules/basalt-forms.md +0 -186
- package/agent/rules/basalt-notifications.md +0 -264
- package/agent/rules/basalt-query.md +0 -187
- package/agent/rules/basalt-router.md +0 -336
- package/dist/content/article-filter-bar.d.ts +0 -40
- package/dist/content/article-filter-bar.d.ts.map +0 -1
- package/dist/content/article-filter-bar.js +0 -56
- package/dist/shell/page-header.d.ts +0 -13
- package/dist/shell/page-header.d.ts.map +0 -1
- package/dist/shell/page-header.js +0 -23
- package/src/content/article-filter-bar.tsx +0 -88
- package/src/shell/page-header.tsx +0 -35
|
@@ -1,76 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DeltaBadge
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* with no glyph. The label never carries a leading "+"; pass `format` to control the magnitude
|
|
6
|
-
* string (defaults to a one-decimal percentage of the absolute value).
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* import { DeltaBadge } from 'basalt-ui'
|
|
10
|
-
*
|
|
11
|
-
* <DeltaBadge value={12.4} /> // ▲ 12.4%, success tint
|
|
12
|
-
* <DeltaBadge value={-3.1} /> // ▼ 3.1%, danger tint
|
|
13
|
-
* <DeltaBadge value={0} /> // 0.0%, neutral faint tint, no glyph
|
|
14
|
-
* <DeltaBadge value={182} format={(v) => `${Math.abs(v)}ms`} withGlyph={false} />
|
|
2
|
+
* `DeltaBadge` moved to `src/widget-header/` (docs/CONTROLS-SPEC.md §2.2) so `WidgetHeader` can
|
|
3
|
+
* compose it Mantine-free. Re-exported here unchanged so this deep import path — and `StatCard`'s
|
|
4
|
+
* own `./delta-badge` import — keep working without a wave-3 edit.
|
|
15
5
|
*/
|
|
16
|
-
|
|
17
|
-
import { alpha, VX } from '../tokens'
|
|
18
|
-
|
|
19
|
-
export type DeltaBadgeProps = {
|
|
20
|
-
/** Signed delta driving tone (positive/negative/zero) and, by default, the label magnitude. */
|
|
21
|
-
value: number
|
|
22
|
-
/** Formats the value into the label text. Defaults to `${Math.abs(value).toFixed(1)}%`. */
|
|
23
|
-
format?: (value: number) => string
|
|
24
|
-
/** Render the ▲/▼ direction glyph. Defaults to `true`; a zero value never shows a glyph. */
|
|
25
|
-
withGlyph?: boolean
|
|
26
|
-
/** Optional comparison timeframe rendered directly after the value in a dimmer shade of the same
|
|
27
|
-
* tone (e.g. `MoM`, `WoW`, `YTD`) — states what the delta is measured against without a hover. */
|
|
28
|
-
period?: string | undefined
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const defaultFormat = (value: number): string => `${Math.abs(value).toFixed(1)}%`
|
|
32
|
-
|
|
33
|
-
export function DeltaBadge({
|
|
34
|
-
value,
|
|
35
|
-
format = defaultFormat,
|
|
36
|
-
withGlyph = true,
|
|
37
|
-
period,
|
|
38
|
-
}: DeltaBadgeProps) {
|
|
39
|
-
const tone = value > 0 ? VX.status.good : value < 0 ? VX.status.bad : VX.faint
|
|
40
|
-
const glyph = value > 0 ? '▲' : value < 0 ? '▼' : undefined
|
|
41
|
-
const showGlyph = withGlyph && glyph !== undefined
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<Badge
|
|
45
|
-
styles={{
|
|
46
|
-
root: {
|
|
47
|
-
backgroundColor: alpha(tone, 0.13),
|
|
48
|
-
padding: 'var(--vx-space-badge-inset-y) var(--vx-space-badge-inset-x)',
|
|
49
|
-
height: 'auto',
|
|
50
|
-
},
|
|
51
|
-
label: {
|
|
52
|
-
display: 'inline-flex', // theme-allow inline-display — a Badge `styles.label` part, not a layout node Flex could replace
|
|
53
|
-
alignItems: 'center',
|
|
54
|
-
gap: 3, // theme-allow inline-spacing — sub-scale glyph-to-label gap, below the smallest scale stop
|
|
55
|
-
fontFamily: 'var(--basalt-font-mono)',
|
|
56
|
-
fontSize: VX.text.xs,
|
|
57
|
-
fontWeight: 600,
|
|
58
|
-
textTransform: 'none',
|
|
59
|
-
letterSpacing: 'normal',
|
|
60
|
-
color: tone,
|
|
61
|
-
},
|
|
62
|
-
}}
|
|
63
|
-
>
|
|
64
|
-
{/* optical glyph ratio relative to the label, not a scale step */}
|
|
65
|
-
{showGlyph && <span style={{ fontSize: '0.8em' }}>{glyph}</span>}
|
|
66
|
-
{format(value)}
|
|
67
|
-
{period && (
|
|
68
|
-
<span
|
|
69
|
-
style={{ marginLeft: 4, fontWeight: 500, opacity: 0.6 }} // theme-allow inline-spacing — sub-scale optical period offset, below the smallest scale stop
|
|
70
|
-
>
|
|
71
|
-
{period}
|
|
72
|
-
</span>
|
|
73
|
-
)}
|
|
74
|
-
</Badge>
|
|
75
|
-
)
|
|
76
|
-
}
|
|
6
|
+
export { DeltaBadge, type DeltaBadgeProps } from '../widget-header/delta-badge'
|
|
@@ -26,7 +26,7 @@ export type QueryStateVariant = 'page' | 'section'
|
|
|
26
26
|
* The structural subset of a TanStack `UseQueryResult` these components read.
|
|
27
27
|
*
|
|
28
28
|
* Typed as a SUBSET rather than as `UseQueryResult<T>` on purpose: a composed, derived or
|
|
29
|
-
* hand-rolled result must be passable without a cast
|
|
29
|
+
* hand-rolled result must be passable without a cast, and
|
|
30
30
|
* these components stay independent of which `@tanstack/react-query` major a consumer pins. This
|
|
31
31
|
* is NOT a claim that the root barrel is free of that package — `./connectivity` imports
|
|
32
32
|
* `onlineManager` as a value and is re-exported from `src/index.ts`. The cost of the subset is
|
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
* vertical padding and a 1px `--vx-divider` rule below it, EXCEPT the last row in the section —
|
|
4
4
|
* `:not(:last-child)` strips the divider there so the section doesn't end on a trailing line.
|
|
5
5
|
*/
|
|
6
|
+
|
|
7
|
+
/* The WidgetHeader block (plus DangerZone's eyebrow, when present) sits above `.rows` with the
|
|
8
|
+
* same gap the old inline `Stack gap={2} mb="sm"` produced. */
|
|
9
|
+
.header {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
gap: 2px;
|
|
13
|
+
margin-bottom: var(--mantine-spacing-sm);
|
|
14
|
+
}
|
|
15
|
+
|
|
6
16
|
.rows > * {
|
|
7
17
|
padding: var(--vx-space-settings-row-inset-y) 0;
|
|
8
18
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SettingsSection / DangerZone — the WidgetHeader composition (docs/CONTROLS-SPEC.md §2.2):
|
|
3
|
+
* `description` renamed to `subtitle`, both now render an h2 (`tier="section"`) via `WidgetHeader`,
|
|
4
|
+
* and `actions` reaches the header's actions slot.
|
|
5
|
+
*/
|
|
6
|
+
import { MantineProvider } from '@mantine/core'
|
|
7
|
+
import { render, screen } from '@testing-library/react'
|
|
8
|
+
import { describe, expect, test } from 'bun:test'
|
|
9
|
+
import type { ReactElement } from 'react'
|
|
10
|
+
import { DangerZone, SettingsSection } from './settings-section'
|
|
11
|
+
|
|
12
|
+
function renderWith(node: ReactElement) {
|
|
13
|
+
return render(<MantineProvider>{node}</MantineProvider>)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('SettingsSection composes WidgetHeader at the section tier', () => {
|
|
17
|
+
test('renders an h2 carrying the title', () => {
|
|
18
|
+
renderWith(
|
|
19
|
+
<SettingsSection title="Profile">
|
|
20
|
+
<div>rows</div>
|
|
21
|
+
</SettingsSection>,
|
|
22
|
+
)
|
|
23
|
+
expect(screen.getByRole('heading', { level: 2, name: 'Profile' })).toBeDefined()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('subtitle (renamed from description) renders below the title', () => {
|
|
27
|
+
renderWith(
|
|
28
|
+
<SettingsSection title="Profile" subtitle="Your public identity.">
|
|
29
|
+
<div>rows</div>
|
|
30
|
+
</SettingsSection>,
|
|
31
|
+
)
|
|
32
|
+
expect(screen.getByText('Your public identity.')).toBeDefined()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('actions reach the header actions slot', () => {
|
|
36
|
+
renderWith(
|
|
37
|
+
<SettingsSection title="Profile" actions={<button type="button">Export</button>}>
|
|
38
|
+
<div>rows</div>
|
|
39
|
+
</SettingsSection>,
|
|
40
|
+
)
|
|
41
|
+
expect(screen.getByRole('button', { name: 'Export' })).toBeDefined()
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('DangerZone keeps its eyebrow alongside the WidgetHeader', () => {
|
|
46
|
+
test('renders the eyebrow and an h2 title', () => {
|
|
47
|
+
renderWith(
|
|
48
|
+
<DangerZone title="Delete workspace" subtitle="This action cannot be undone.">
|
|
49
|
+
<div>rows</div>
|
|
50
|
+
</DangerZone>,
|
|
51
|
+
)
|
|
52
|
+
expect(screen.getByText('Danger Zone')).toBeDefined()
|
|
53
|
+
expect(screen.getByRole('heading', { level: 2, name: 'Delete workspace' })).toBeDefined()
|
|
54
|
+
expect(screen.getByText('This action cannot be undone.')).toBeDefined()
|
|
55
|
+
})
|
|
56
|
+
})
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SettingsSection / SettingsRow / DangerZone — the settings-page building blocks
|
|
3
|
-
* (docs/DESIGN-SPEC.md §5). A `SettingsSection` is a titled card
|
|
4
|
-
* `SettingsRow`s, each separated by a 1px `--vx-divider` rule (no border on the
|
|
5
|
-
* handled by the shared `.rows` container in `settings-section.module.css`). `DangerZone`
|
|
6
|
-
* `SettingsSection` variant for irreversible actions: a mono "DANGER ZONE" eyebrow in
|
|
3
|
+
* (docs/DESIGN-SPEC.md §5, docs/CONTROLS-SPEC.md §2.2). A `SettingsSection` is a titled card
|
|
4
|
+
* grouping a stack of `SettingsRow`s, each separated by a 1px `--vx-divider` rule (no border on the
|
|
5
|
+
* last row — handled by the shared `.rows` container in `settings-section.module.css`). `DangerZone`
|
|
6
|
+
* is a `SettingsSection` variant for irreversible actions: a mono "DANGER ZONE" eyebrow in
|
|
7
7
|
* status-danger, and a danger-tinted ring layered atop the card's shadow-card depth.
|
|
8
8
|
*
|
|
9
|
+
* Both compose `WidgetHeader tier="section"` for the title/subtitle/actions row — `actions` is
|
|
10
|
+
* wrapped in `CtlSlot` (C1/C5).
|
|
11
|
+
*
|
|
9
12
|
* @example
|
|
10
13
|
* import { SettingsSection, SettingsRow, DangerZone } from 'basalt-ui'
|
|
11
14
|
*
|
|
12
|
-
* <SettingsSection title="Profile"
|
|
15
|
+
* <SettingsSection title="Profile" subtitle="Your public identity.">
|
|
13
16
|
* <SettingsRow label="Display name" control={<TextInput value={name} onChange={...} />} />
|
|
14
17
|
* <SettingsRow
|
|
15
18
|
* label="Email"
|
|
@@ -18,7 +21,7 @@
|
|
|
18
21
|
* />
|
|
19
22
|
* </SettingsSection>
|
|
20
23
|
*
|
|
21
|
-
* <DangerZone title="Delete workspace"
|
|
24
|
+
* <DangerZone title="Delete workspace" subtitle="This action cannot be undone.">
|
|
22
25
|
* <SettingsRow
|
|
23
26
|
* label="Delete this workspace"
|
|
24
27
|
* control={<Button color="red" variant="outline">Delete</Button>}
|
|
@@ -27,17 +30,11 @@
|
|
|
27
30
|
*/
|
|
28
31
|
import { Card, Stack } from '@mantine/core'
|
|
29
32
|
import type { ReactNode } from 'react'
|
|
33
|
+
import { WidgetHeader } from '../widget-header'
|
|
34
|
+
import { CtlSlot } from '../theme'
|
|
30
35
|
import { alpha, VX } from '../tokens'
|
|
31
36
|
import classes from './settings-section.module.css'
|
|
32
37
|
|
|
33
|
-
const titleStyle = {
|
|
34
|
-
fontFamily: 'var(--basalt-font-head)',
|
|
35
|
-
fontStretch: '88%' as const,
|
|
36
|
-
fontSize: VX.text.md,
|
|
37
|
-
fontWeight: 550,
|
|
38
|
-
color: VX.ink,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
38
|
const eyebrowStyle = {
|
|
42
39
|
fontFamily: 'var(--basalt-font-mono)',
|
|
43
40
|
fontSize: VX.text.micro,
|
|
@@ -48,23 +45,27 @@ const eyebrowStyle = {
|
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
export type SettingsSectionProps = {
|
|
51
|
-
/** Head-font section title
|
|
48
|
+
/** Head-font section title, rendered via `WidgetHeader tier="section"`. */
|
|
52
49
|
title: string
|
|
53
50
|
/** Optional 13px muted description below the title. */
|
|
54
|
-
|
|
51
|
+
subtitle?: string
|
|
52
|
+
/** Header-right slot — wrapped in `CtlSlot` (C1/C5). */
|
|
53
|
+
actions?: ReactNode
|
|
55
54
|
/** Section body — typically a stack of `SettingsRow`s. */
|
|
56
55
|
children: ReactNode
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
export function SettingsSection({ title,
|
|
58
|
+
export function SettingsSection({ title, subtitle, actions, children }: SettingsSectionProps) {
|
|
60
59
|
return (
|
|
61
60
|
<Card style={{ padding: 'var(--mantine-spacing-xs) var(--mantine-spacing-sm)' }}>
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
<div className={classes.header}>
|
|
62
|
+
<WidgetHeader
|
|
63
|
+
tier="section"
|
|
64
|
+
title={title}
|
|
65
|
+
{...(subtitle !== undefined && { subtitle })}
|
|
66
|
+
{...(actions !== undefined && { actions: <CtlSlot>{actions}</CtlSlot> })}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
68
69
|
<div className={classes.rows}>{children}</div>
|
|
69
70
|
</Card>
|
|
70
71
|
)
|
|
@@ -97,7 +98,7 @@ export function SettingsRow({ label, description, control, children }: SettingsR
|
|
|
97
98
|
|
|
98
99
|
export type DangerZoneProps = SettingsSectionProps
|
|
99
100
|
|
|
100
|
-
export function DangerZone({ title,
|
|
101
|
+
export function DangerZone({ title, subtitle, actions, children }: DangerZoneProps) {
|
|
101
102
|
return (
|
|
102
103
|
<Card
|
|
103
104
|
style={{
|
|
@@ -105,13 +106,15 @@ export function DangerZone({ title, description, children }: DangerZoneProps) {
|
|
|
105
106
|
boxShadow: `${VX.shadowCard}, 0 0 0 1px ${alpha(VX.status.bad, 0.25)}`,
|
|
106
107
|
}}
|
|
107
108
|
>
|
|
108
|
-
<
|
|
109
|
+
<div className={classes.header}>
|
|
109
110
|
<span style={eyebrowStyle}>Danger Zone</span>
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
<WidgetHeader
|
|
112
|
+
tier="section"
|
|
113
|
+
title={title}
|
|
114
|
+
{...(subtitle !== undefined && { subtitle })}
|
|
115
|
+
{...(actions !== undefined && { actions: <CtlSlot>{actions}</CtlSlot> })}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
115
118
|
<div className={classes.rows}>{children}</div>
|
|
116
119
|
</Card>
|
|
117
120
|
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* StatCard's sparkline placement (docs/CONTROLS-SPEC.md §2.2). `bleed` (default) keeps the historic
|
|
3
|
+
* full-width row beneath the hero value, bled past the card's own inset padding — same negative
|
|
4
|
+
* margins the pre-WidgetHeader version drew inline. `right` sits the sparkline beside the
|
|
5
|
+
* `WidgetHeader`'s hero-value row instead, the reference-design look, and collapses back to
|
|
6
|
+
* `bleed`'s stacked layout below `sm` — CSS only, no JS branch (`47.99375em` is Mantine's own
|
|
7
|
+
* complement of `min-width: 48em`, same value `content/prose.module.css` derives it from).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
.body {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
gap: var(--vx-space-stat-card-gap);
|
|
14
|
+
height: 100%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* `flex-end`, not `center`: the sparkline belongs to the HERO ROW, and the hero row is the header
|
|
19
|
+
* block's last line. Centred against the whole body it floated between the title and the value,
|
|
20
|
+
* which reads as a third, unrelated element; bottom-aligned to the header block it sits beside the
|
|
21
|
+
* number it describes, the reference layout. `gap` is the card's own control gap so the trend never
|
|
22
|
+
* touches a long value.
|
|
23
|
+
*/
|
|
24
|
+
.body[data-placement='right'] {
|
|
25
|
+
flex-direction: row;
|
|
26
|
+
align-items: flex-end;
|
|
27
|
+
gap: var(--vx-space-control-gap);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.header {
|
|
31
|
+
min-width: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* `flex-shrink: 0` on the header, `1` on the sparkline slot below: the hero value and its delta keep
|
|
36
|
+
* their width and the TREND gives ground. The other way round, a 4-up KPI grid on a 1440 viewport
|
|
37
|
+
* ellipsized `$1,294,900` to `$1294.…` in order to keep 72px of bars intact, which is the wrong
|
|
38
|
+
* thing to protect. The value still carries `overflow: hidden` as the last resort, and `Card` clips.
|
|
39
|
+
*/
|
|
40
|
+
.body[data-placement='right'] .header {
|
|
41
|
+
flex: 1 0 auto;
|
|
42
|
+
min-width: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.sparklineBleed {
|
|
46
|
+
margin-top: auto;
|
|
47
|
+
margin-inline: calc(-1 * var(--mantine-spacing-sm));
|
|
48
|
+
margin-bottom: calc(-1 * var(--mantine-spacing-xs));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Asks for `StatCard`'s `SPARKLINE_RIGHT_WIDTH`/`_HEIGHT` (handed down as custom properties, so this
|
|
52
|
+
* rule and the `< sm` reset below can both reach them) and shrinks below the width before the hero
|
|
53
|
+
* row does — see the `.header` note above. `min-width: 0` is what lets it. */
|
|
54
|
+
.sparklineRight {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex: 0 1 var(--basalt-stat-sparkline-w, 4.5rem);
|
|
57
|
+
min-width: 0;
|
|
58
|
+
height: var(--basalt-stat-sparkline-h, 1.625rem);
|
|
59
|
+
align-items: flex-end;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@media (max-width: 47.99375em) {
|
|
64
|
+
.body[data-placement='right'] {
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
align-items: stretch;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.body[data-placement='right'] .header {
|
|
70
|
+
flex: initial;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* The row became a COLUMN, so `flex-basis` is now the main-axis HEIGHT — the 72px width would
|
|
74
|
+
* become a 72px-tall box around 26px of bars. `flex: none` + the height keeps the bled trend the
|
|
75
|
+
* same 26px it is beside a hero row. */
|
|
76
|
+
.body[data-placement='right'] .sparklineRight {
|
|
77
|
+
flex: none;
|
|
78
|
+
margin-top: auto;
|
|
79
|
+
margin-inline: calc(-1 * var(--mantine-spacing-sm));
|
|
80
|
+
margin-bottom: calc(-1 * var(--mantine-spacing-xs));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed CSS-module declaration (the S4 CSS-modules path). Named string props — not an index
|
|
3
|
+
* signature — so dot access stays verbatim under the package's strict flags.
|
|
4
|
+
*/
|
|
5
|
+
declare const classes: {
|
|
6
|
+
readonly body: string
|
|
7
|
+
readonly header: string
|
|
8
|
+
readonly sparklineBleed: string
|
|
9
|
+
readonly sparklineRight: string
|
|
10
|
+
}
|
|
11
|
+
export default classes
|
|
@@ -30,7 +30,7 @@ function render(tone?: StatCardTone) {
|
|
|
30
30
|
<MantineProvider>
|
|
31
31
|
{/* exactOptionalPropertyTypes forbids an explicit tone={undefined} — spread only when set */}
|
|
32
32
|
<StatCard
|
|
33
|
-
|
|
33
|
+
title="Downtime · last 24h"
|
|
34
34
|
value="0 min"
|
|
35
35
|
{...(tone !== undefined ? { tone } : {})}
|
|
36
36
|
/>
|
|
@@ -86,3 +86,69 @@ describe('omitting tone is not "good" — it is untinted, and says nothing', ()
|
|
|
86
86
|
expect(markup).toContain('0 min')
|
|
87
87
|
})
|
|
88
88
|
})
|
|
89
|
+
|
|
90
|
+
describe('the header composes WidgetHeader at the widget tier', () => {
|
|
91
|
+
test('renders an h3 carrying the title', () => {
|
|
92
|
+
const markup = renderToStaticMarkup(
|
|
93
|
+
<MantineProvider>
|
|
94
|
+
<StatCard title="Active Users" value="12,483" />
|
|
95
|
+
</MantineProvider>,
|
|
96
|
+
)
|
|
97
|
+
expect(markup).toContain('<h3')
|
|
98
|
+
expect(markup).toContain('Active Users')
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe('info and subtitle reach the composed WidgetHeader', () => {
|
|
103
|
+
function renderHeader(props: { info?: string; subtitle?: string }) {
|
|
104
|
+
return renderToStaticMarkup(
|
|
105
|
+
<MantineProvider>
|
|
106
|
+
<StatCard title="Training load" value="412" {...props} />
|
|
107
|
+
</MantineProvider>,
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
test('the subtitle renders as its own line under the hero row', () => {
|
|
112
|
+
expect(renderHeader({ subtitle: 'TSS · 7-day rolling' })).toContain('TSS · 7-day rolling')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('the info text ships with the glyph, and never inside the heading', () => {
|
|
116
|
+
const markup = renderHeader({ info: 'Sum of per-session TSS over 7 days.' })
|
|
117
|
+
// `WidgetHeader` renders the bubble only while open, so the closed markup carries the named
|
|
118
|
+
// trigger — what matters here is that the card FORWARDED the prop and that the text stayed OUT
|
|
119
|
+
// of the `<h3>`, which is what an `info` rendered inside the heading would silently do.
|
|
120
|
+
expect(markup).toContain('aria-label="More information"')
|
|
121
|
+
const heading = /<h3[^>]*>(.*?)<\/h3>/.exec(markup)?.[1] ?? ''
|
|
122
|
+
expect(heading).toContain('Training load')
|
|
123
|
+
expect(heading).not.toContain('Sum of')
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('neither prop renders anything when omitted — no glyph, no empty line', () => {
|
|
127
|
+
expect(renderHeader({})).not.toContain('More information')
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('sparklinePlacement', () => {
|
|
132
|
+
function renderWithSparkline(placement?: 'bleed' | 'right') {
|
|
133
|
+
return renderToStaticMarkup(
|
|
134
|
+
<MantineProvider>
|
|
135
|
+
<StatCard
|
|
136
|
+
title="Active Users"
|
|
137
|
+
value="12,483"
|
|
138
|
+
sparkline={<span data-testid="spark">spark</span>}
|
|
139
|
+
{...(placement !== undefined ? { sparklinePlacement: placement } : {})}
|
|
140
|
+
/>
|
|
141
|
+
</MantineProvider>,
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
test('defaults to bleed — the full-width row bled to the card edges', () => {
|
|
146
|
+
const markup = renderWithSparkline()
|
|
147
|
+
expect(markup).toContain('data-placement="bleed"')
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('right sits the sparkline beside the hero-value row', () => {
|
|
151
|
+
const markup = renderWithSparkline('right')
|
|
152
|
+
expect(markup).toContain('data-placement="right"')
|
|
153
|
+
})
|
|
154
|
+
})
|