@payfit/unity-components 2.55.13 → 2.55.15

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.
Files changed (60) hide show
  1. package/dist/esm/components/autocomplete/parts/AutocompletePanel.js +1 -1
  2. package/dist/esm/components/avatar/Avatar.context.js +1 -1
  3. package/dist/esm/components/bottom-sheet/parts/BottomSheetDragIndicator.js +1 -1
  4. package/dist/esm/components/breadcrumbs/Breadcrumbs.context.js +1 -1
  5. package/dist/esm/components/breadcrumbs/Breadcrumbs.js +1 -1
  6. package/dist/esm/components/carousel/Carousel.options.js +1 -1
  7. package/dist/esm/components/carousel/hooks/useCarouselState.js +8 -4
  8. package/dist/esm/components/client-side-pagination/ClientSidePagination.js +1 -1
  9. package/dist/esm/components/client-side-pagination/parts/RawPaginationLink.js +1 -1
  10. package/dist/esm/components/client-side-pagination/utils/pagination-window.js +18 -19
  11. package/dist/esm/components/code/Code.js +1 -1
  12. package/dist/esm/components/collapsible/Collapsible.js +1 -1
  13. package/dist/esm/components/dialog/test-utils.js +4 -2
  14. package/dist/esm/components/error-state/initConfig.js +1 -1
  15. package/dist/esm/components/filter/parts/FilterButton.js +1 -1
  16. package/dist/esm/components/filter/parts/FilterLabel.js +1 -1
  17. package/dist/esm/components/filter/utils/value-formatters.js +1 -1
  18. package/dist/esm/components/filter-toolbar/FilterToolbar.js +27 -27
  19. package/dist/esm/components/filter-toolbar/parts/AddFilterItem.js +1 -1
  20. package/dist/esm/components/form/Form.context.js +1 -1
  21. package/dist/esm/components/form-field/FormField.context.js +1 -1
  22. package/dist/esm/components/inline-field/parts/InlineFieldEditView.js +7 -4
  23. package/dist/esm/components/inline-field-group/InlineFieldGroup.js +14 -8
  24. package/dist/esm/components/multi-select/MultiSelect.js +2 -2
  25. package/dist/esm/components/pagination/utils/pagination-window.js +25 -26
  26. package/dist/esm/components/phone-number/parts/PhoneNumberItem.js +1 -1
  27. package/dist/esm/components/popover/Popover.js +1 -1
  28. package/dist/esm/components/radio-button-group/parts/RadioButton.js +1 -1
  29. package/dist/esm/components/select/parts/SelectOption.context.js +1 -1
  30. package/dist/esm/components/select-list/helpers.js +2 -2
  31. package/dist/esm/components/select-list/parts/SelectListOptGroup.js +1 -1
  32. package/dist/esm/components/selectable-card/internals/Content.js +1 -1
  33. package/dist/esm/components/selectable-card/internals/Description.js +1 -1
  34. package/dist/esm/components/selectable-card/internals/Illustration.js +1 -1
  35. package/dist/esm/components/selectable-card/internals/SelectedIndicator.js +1 -1
  36. package/dist/esm/components/side-panel/parts/SidePanelDragIndicator.js +1 -1
  37. package/dist/esm/components/stepper/Stepper.context.js +1 -1
  38. package/dist/esm/components/table/Table.context.js +1 -1
  39. package/dist/esm/components/table/hooks/useTableKeyboardNavigation.js +10 -8
  40. package/dist/esm/components/tabs/Tabs.context.js +1 -1
  41. package/dist/esm/components/task-menu/TaskMenu.context.js +1 -1
  42. package/dist/esm/components/timeline/Timeline.context.js +1 -1
  43. package/dist/esm/components/timeline/parts/TimelineStepContent.context.js +1 -1
  44. package/dist/esm/hooks/use-has-scroll.js +3 -1
  45. package/dist/esm/index.js +2 -2
  46. package/package.json +14 -14
  47. package/skills/unity-data-table/SKILL.md +24 -240
  48. package/skills/unity-data-table/references/patterns.md +237 -0
  49. package/skills/unity-find-component/SKILL.md +20 -105
  50. package/skills/unity-find-component/references/component-catalog.md +94 -0
  51. package/skills/unity-layout-and-styling/SKILL.md +14 -141
  52. package/skills/unity-layout-and-styling/references/patterns.md +139 -0
  53. package/skills/unity-migrate-from-midnight/SKILL.md +9 -9
  54. package/skills/unity-navigation/SKILL.md +12 -116
  55. package/skills/unity-navigation/references/patterns.md +115 -0
  56. package/skills/unity-overlays/SKILL.md +11 -132
  57. package/skills/unity-overlays/references/patterns.md +131 -0
  58. package/skills/unity-setup-feature-plugin/SKILL.md +4 -3
  59. package/skills/unity-tanstack-form/SKILL.md +15 -102
  60. package/skills/unity-tanstack-form/references/patterns.md +102 -0
@@ -0,0 +1,139 @@
1
+ # Unity layout and styling patterns
2
+
3
+ ### Flex for 1D, Grid for 2D
4
+
5
+ `Flex` is for one-dimensional rows/columns; `Grid` is for the 12-column (or 6-column)
6
+ two-dimensional layout. Each exposes layout props; everything else goes via
7
+ `className` with `uy:` utilities.
8
+
9
+ ```tsx
10
+ import { Flex, FlexItem, Grid, GridItem } from '@payfit/unity-components'
11
+
12
+ // Flex props: asElement, inline, direction, isReversed, wrap,
13
+ // gap, gapX, gapY, justify, align, alignContent, className
14
+ <Flex direction="row" gap="200" justify="between" align="center">
15
+ <FlexItem grow="1">Left</FlexItem>
16
+ <FlexItem>Right</FlexItem>
17
+ </Flex>
18
+
19
+ // Grid props: asElement, inline, cols (6 | 12), rows, areas, flow,
20
+ // justifyItems, alignItems, className
21
+ // GridItem positions via colSpan/colStart/colEnd OR area (mutually exclusive)
22
+ <Grid cols={12} className="uy:gap-200">
23
+ <GridItem colSpan={8}>Main</GridItem>
24
+ <GridItem colSpan={4}>Aside</GridItem>
25
+ </Grid>
26
+ ```
27
+
28
+ ### Responsive classes via uy:md:
29
+
30
+ There is no responsive prop-object API. Responsive behavior is driven by
31
+ TailwindCSS v4 modifiers on `className`.
32
+
33
+ ```tsx
34
+ <Flex gap="100" className="uy:md:gap-200 uy:lg:gap-300">
35
+ <span>Item</span>
36
+ </Flex>
37
+
38
+ <Grid cols={12} className="uy:grid-cols-1 uy:md:grid-cols-2 uy:lg:grid-cols-3" />
39
+ ```
40
+
41
+ ### Variants with uyTv
42
+
43
+ `uyTv` from `@payfit/unity-themes` is the pre-configured tailwind-variants
44
+ factory. It applies the Unity `twMergeConfig` so variant collisions resolve
45
+ against Unity tokens. Export the variant function and derive its typed props
46
+ with `VariantProps`.
47
+
48
+ ```tsx
49
+ import type { VariantProps } from '@payfit/unity-themes'
50
+
51
+ import { uyTv } from '@payfit/unity-themes'
52
+
53
+ export const callout = uyTv({
54
+ base: 'uy:inline-flex uy:items-center uy:gap-100 uy:rounded-100 uy:px-200 uy:py-100',
55
+ variants: {
56
+ intent: {
57
+ info: 'uy:bg-surface-primary-default uy:text-content-inverted-default',
58
+ danger: 'uy:bg-surface-danger-default uy:text-content-inverted-default',
59
+ neutral: 'uy:bg-surface-neutral-default uy:text-content-neutral-default',
60
+ },
61
+ size: {
62
+ sm: 'uy:typography-body-small',
63
+ md: 'uy:typography-body',
64
+ },
65
+ },
66
+ defaultVariants: { intent: 'info', size: 'md' },
67
+ })
68
+
69
+ export type CalloutVariantProps = VariantProps<typeof callout>
70
+ ```
71
+
72
+ ### Class merging with uyMerge
73
+
74
+ `uyMerge` is `tailwind-merge` configured with Unity's class groups. Use it
75
+ whenever an external `className` may collide with internal classes.
76
+
77
+ ```tsx
78
+ import { uyMerge } from '@payfit/unity-themes'
79
+
80
+ uyMerge('uy:p-100', 'uy:p-200') // → 'uy:p-200'
81
+ uyMerge('uy:bg-surface-primary-default', 'uy:bg-surface-danger-default')
82
+ // → 'uy:bg-surface-danger-default'
83
+ uyMerge('uy:p-100', 'uy:px-200', 'uy:py-300') // p, px, py don't collide
84
+ ```
85
+
86
+ ### Conditional classes with cn / classNames / clsx
87
+
88
+ `cn`, `classNames`, and `clsx` are aliases for the same Unity-configured helper
89
+ in `@payfit/unity-themes`. Use them for ad-hoc conditional strings — not for
90
+ component-scoped variant APIs.
91
+
92
+ ```tsx
93
+ import { cn } from '@payfit/unity-themes'
94
+
95
+ function Row({
96
+ isActive,
97
+ className,
98
+ }: {
99
+ isActive: boolean
100
+ className?: string
101
+ }) {
102
+ return (
103
+ <div
104
+ className={cn(
105
+ 'uy:flex uy:items-center uy:px-200 uy:py-100',
106
+ isActive && 'uy:bg-surface-primary-default',
107
+ className,
108
+ )}
109
+ />
110
+ )
111
+ }
112
+ ```
113
+
114
+ ### Typography with `<Text>`
115
+
116
+ Use `<Text variant=...>` instead of a `<div>` + typography class. `Text`
117
+ picks a semantic element from the variant (e.g. `variant="h1"` → `<h1>`),
118
+ applies the typography variant, and exposes `color`, `isTruncated`,
119
+ `lineClamp`, and `maxWidthCh`.
120
+
121
+ ```tsx
122
+ import { Text } from '@payfit/unity-components'
123
+
124
+ <Text variant="h1" color="content.primary">Title</Text>
125
+ <Text variant="body" color="content.neutral">Description</Text>
126
+ // Override the semantic element when needed:
127
+ <Text variant="h1" asElement="h2">Visual h1, semantic h2</Text>
128
+ ```
129
+
130
+ ### Data-attribute pseudo-states
131
+
132
+ Several Unity components expose internal state via `data-hovered`,
133
+ `data-pressed`, `data-focus-visible`, etc. Target the component-owned state
134
+ attribute rather than the native CSS pseudo-state.
135
+
136
+ ```tsx
137
+ import { ListViewItem } from '@payfit/unity-components'
138
+ ;<ListViewItem className="uy:data-[hovered=true]:bg-surface-primary-hover" />
139
+ ```
@@ -4,9 +4,10 @@ description: >
4
4
  Load when replacing @payfit/midnight UI with Unity. Use it to choose Unity
5
5
  equivalents, avoid obsolete Midnight patterns, and route unclear component
6
6
  mappings to unity-find-component.
7
- type: lifecycle
8
- library: '@payfit/unity-components'
9
- library_version: '2.x'
7
+ metadata:
8
+ type: lifecycle
9
+ library: '@payfit/unity-components'
10
+ library_version: '2.x'
10
11
  sources:
11
12
  - 'PayFit/hr-apps:libs/shared/unity/components/src/components/tooltip/Tooltip.tsx'
12
13
  - 'PayFit/hr-apps:libs/shared/unity/components/src/components/button/Button.tsx'
@@ -71,10 +72,9 @@ Unity enforces several WCAG rules that Midnight allowed:
71
72
 
72
73
  Midnight-era screens commonly use React Hook Form. When porting, replace
73
74
  the form with `useTanstackUnityForm` and the Tanstack-bound `*Field`
74
- components (`TextField`, `SelectField`, `NumberField`, etc.). The legacy
75
- RHF `useUnityForm` + `*Field` wrappers in the same package index are
76
- deprecated and will be removed after the rebrand do not pause on the
77
- RHF intermediate step. See `unity-tanstack-form`.
75
+ components (`TextField`, `SelectField`, `NumberField`, etc.). The legacy RHF
76
+ `useUnityForm` + `*Field` wrappers in the same package index are deprecated.
77
+ Do not pause on the RHF intermediate step. See `unity-tanstack-form`.
78
78
 
79
79
  ## Common Mistakes
80
80
 
@@ -106,7 +106,7 @@ rule that disabled controls must not have tooltips because they are not
106
106
  keyboard-focusable, so the tooltip is unreachable — Unity's `Tooltip`
107
107
  component rejects this pairing at the library level.
108
108
 
109
- Source: libs/shared/unity/components/src/components/tooltip/Tooltip.tsx; maintainer interview (Unity enforces)
109
+ Source: libs/shared/unity/components/src/components/tooltip/Tooltip.tsx
110
110
 
111
111
  ### HIGH Assume Midnight props map 1:1 to Unity
112
112
 
@@ -151,7 +151,7 @@ Midnight and Unity ship divergent theme tokens — colors, spacing, and
151
151
  typography differ — so a mixed screen renders with mismatched scales and
152
152
  the visual regression lands silently because both stylesheets are valid.
153
153
 
154
- Source: AGENTS.md migration awareness; maintainer interview
154
+ Source: AGENTS.md migration awareness
155
155
 
156
156
  ### MEDIUM Port Midnight transition utility instead of using uy: classes
157
157
 
@@ -4,9 +4,10 @@ description: >
4
4
  Load when building Unity links, tabs, breadcrumbs, nav, or pagination. Use it
5
5
  to choose router-aware Tanstack Router components versus Raw* href
6
6
  primitives.
7
- type: core
8
- library: '@payfit/unity-components'
9
- library_version: '2.x'
7
+ metadata:
8
+ type: core
9
+ library: '@payfit/unity-components'
10
+ library_version: '2.x'
10
11
  sources:
11
12
  - 'PayFit/hr-apps:libs/shared/unity/components/src/components/link/RawLink.tsx'
12
13
  - 'PayFit/hr-apps:libs/shared/unity/components/src/components/breadcrumbs/Breadcrumbs.tsx'
@@ -73,119 +74,14 @@ export function EmployeesShell() {
73
74
 
74
75
  ## Core Patterns
75
76
 
76
- ### Raw and integration map
77
+ - Use router-aware integration exports in TanStack Router features and `Raw*`
78
+ primitives for explicit `href` navigation.
79
+ - Preserve the required Breadcrumbs, Pagination, and Tabs composition.
80
+ - In non-TanStack applications, provide the appropriate router adapter before
81
+ using `Raw*` navigation components.
77
82
 
78
- | Base entry (`@payfit/unity-components`) | Integration (`@payfit/unity-components/integrations/tanstack-router`) | Prop |
79
- | --------------------------------------- | --------------------------------------------------------------------- | --------------------------------------- |
80
- | `RawLink` | `Link` | `href` (raw) / `to` (integration) |
81
- | `RawNavItem` | `NavItem` | same |
82
- | `RawBreadcrumbLink` | `BreadcrumbLink` | same |
83
- | `RawPaginationLink` | `PaginationLink` | same |
84
- | `RawTab` (+ `Tabs`) | `Tab` (+ `Tabs`) | route-aware `to` selects the active tab |
85
- | `RawLinkButton` | `LinkButton` | same |
86
-
87
- The integration components are produced via `createLink(RawLink)`. They render
88
- exactly the same DOM as the raw versions but accept Tanstack's typed `to`,
89
- `params`, `search`, and `preload` props.
90
-
91
- ### Compose Breadcrumbs / Pagination / Tabs
92
-
93
- Each navigation composite has a strict child contract. Direct children of the
94
- container must be the wrapper part — loose children are silently filtered.
95
-
96
- ```tsx
97
- import {
98
- Breadcrumb,
99
- BreadcrumbLink,
100
- Breadcrumbs,
101
- Pagination,
102
- PaginationContent,
103
- PaginationItem,
104
- RawPaginationLink,
105
- RawTab,
106
- TabList,
107
- TabPanel,
108
- Tabs,
109
- } from '@payfit/unity-components'
110
-
111
- export function CompositionExamples({
112
- page,
113
- pageCount,
114
- onPageChange,
115
- }: {
116
- page: number
117
- pageCount: number
118
- onPageChange: (next: number, prev: number, dir: -1 | 1) => void
119
- }) {
120
- return (
121
- <>
122
- <Breadcrumbs>
123
- <Breadcrumb>
124
- <BreadcrumbLink href="/">Home</BreadcrumbLink>
125
- </Breadcrumb>
126
- <Breadcrumb>
127
- <BreadcrumbLink href="/employees">Employees</BreadcrumbLink>
128
- </Breadcrumb>
129
- </Breadcrumbs>
130
-
131
- <Pagination
132
- currentPage={page}
133
- pageCount={pageCount}
134
- onPageChange={onPageChange}
135
- >
136
- <PaginationContent>
137
- {Array.from({ length: pageCount }, (_, i) => i + 1).map(n => (
138
- <PaginationItem key={n}>
139
- <RawPaginationLink value={n} isActive={n === page}>
140
- {n}
141
- </RawPaginationLink>
142
- </PaginationItem>
143
- ))}
144
- </PaginationContent>
145
- </Pagination>
146
-
147
- <Tabs>
148
- <TabList>
149
- <RawTab id="active">Active</RawTab>
150
- <RawTab id="archived">Archived</RawTab>
151
- </TabList>
152
- <TabPanel id="active">Active rows</TabPanel>
153
- <TabPanel id="archived">Archived rows</TabPanel>
154
- </Tabs>
155
- </>
156
- )
157
- }
158
- ```
159
-
160
- ### RouterProvider for Raw\* in non-Tanstack apps
161
-
162
- When you use `Raw*` components in an app that uses a different router (e.g.
163
- React Router), wrap the app once in `RouterProvider` so `isActive` and
164
- client-side navigation work. The Tanstack integration entry does NOT need
165
- this — it reads from Tanstack hooks directly.
166
-
167
- ```tsx
168
- import { RouterProvider } from '@payfit/unity-components'
169
- import { matchPath, useLocation, useNavigate } from 'react-router-dom'
170
-
171
- export function UnityRouterBridge({ children }: { children: React.ReactNode }) {
172
- const navigate = useNavigate()
173
- const location = useLocation()
174
-
175
- return (
176
- <RouterProvider
177
- navigate={to => navigate(to)}
178
- isActive={(to, isExact) =>
179
- Boolean(
180
- matchPath({ path: to, end: Boolean(isExact) }, location.pathname),
181
- )
182
- }
183
- >
184
- {children}
185
- </RouterProvider>
186
- )
187
- }
188
- ```
83
+ Read [references/patterns.md](references/patterns.md) for complete compositions
84
+ and provider examples.
189
85
 
190
86
  ## Common Mistakes
191
87
 
@@ -260,7 +156,7 @@ import { Pagination, PaginationContent, PaginationItem, RawPaginationLink }
260
156
 
261
157
  v2 Pagination is compositional; passing currentPage/pageCount with no children renders an empty container. The old monolithic component (ClientSidePagination) now lives only inside DataTable as an internal — there is no public monolithic export. For standalone pagination, compose it yourself; for paginated tables, use DataTable which has it built in.
262
158
 
263
- Source: components/pagination/Pagination.tsx; index.ts; maintainer interview (ClientSidePagination is internal to DataTable)
159
+ Source: components/pagination/Pagination.tsx; index.ts
264
160
 
265
161
  ### MEDIUM Forget to wrap BreadcrumbLink in Breadcrumb
266
162
 
@@ -0,0 +1,115 @@
1
+ # Unity navigation patterns
2
+
3
+ ### Raw and integration map
4
+
5
+ | Base entry (`@payfit/unity-components`) | Integration (`@payfit/unity-components/integrations/tanstack-router`) | Prop |
6
+ | --------------------------------------- | --------------------------------------------------------------------- | --------------------------------------- |
7
+ | `RawLink` | `Link` | `href` (raw) / `to` (integration) |
8
+ | `RawNavItem` | `NavItem` | same |
9
+ | `RawBreadcrumbLink` | `BreadcrumbLink` | same |
10
+ | `RawPaginationLink` | `PaginationLink` | same |
11
+ | `RawTab` (+ `Tabs`) | `Tab` (+ `Tabs`) | route-aware `to` selects the active tab |
12
+ | `RawLinkButton` | `LinkButton` | same |
13
+
14
+ The integration components are produced via `createLink(RawLink)`. They render
15
+ exactly the same DOM as the raw versions but accept Tanstack's typed `to`,
16
+ `params`, `search`, and `preload` props.
17
+
18
+ ### Compose Breadcrumbs / Pagination / Tabs
19
+
20
+ Each navigation composite has a strict child contract. Direct children of the
21
+ container must be the wrapper part — loose children are silently filtered.
22
+
23
+ ```tsx
24
+ import {
25
+ Breadcrumb,
26
+ BreadcrumbLink,
27
+ Breadcrumbs,
28
+ Pagination,
29
+ PaginationContent,
30
+ PaginationItem,
31
+ RawPaginationLink,
32
+ RawTab,
33
+ TabList,
34
+ TabPanel,
35
+ Tabs,
36
+ } from '@payfit/unity-components'
37
+
38
+ export function CompositionExamples({
39
+ page,
40
+ pageCount,
41
+ onPageChange,
42
+ }: {
43
+ page: number
44
+ pageCount: number
45
+ onPageChange: (next: number, prev: number, dir: -1 | 1) => void
46
+ }) {
47
+ return (
48
+ <>
49
+ <Breadcrumbs>
50
+ <Breadcrumb>
51
+ <BreadcrumbLink href="/">Home</BreadcrumbLink>
52
+ </Breadcrumb>
53
+ <Breadcrumb>
54
+ <BreadcrumbLink href="/employees">Employees</BreadcrumbLink>
55
+ </Breadcrumb>
56
+ </Breadcrumbs>
57
+
58
+ <Pagination
59
+ currentPage={page}
60
+ pageCount={pageCount}
61
+ onPageChange={onPageChange}
62
+ >
63
+ <PaginationContent>
64
+ {Array.from({ length: pageCount }, (_, i) => i + 1).map(n => (
65
+ <PaginationItem key={n}>
66
+ <RawPaginationLink value={n} isActive={n === page}>
67
+ {n}
68
+ </RawPaginationLink>
69
+ </PaginationItem>
70
+ ))}
71
+ </PaginationContent>
72
+ </Pagination>
73
+
74
+ <Tabs>
75
+ <TabList>
76
+ <RawTab id="active">Active</RawTab>
77
+ <RawTab id="archived">Archived</RawTab>
78
+ </TabList>
79
+ <TabPanel id="active">Active rows</TabPanel>
80
+ <TabPanel id="archived">Archived rows</TabPanel>
81
+ </Tabs>
82
+ </>
83
+ )
84
+ }
85
+ ```
86
+
87
+ ### RouterProvider for Raw\* in non-Tanstack apps
88
+
89
+ When you use `Raw*` components in an app that uses a different router (e.g.
90
+ React Router), wrap the app once in `RouterProvider` so `isActive` and
91
+ client-side navigation work. The Tanstack integration entry does NOT need
92
+ this — it reads from Tanstack hooks directly.
93
+
94
+ ```tsx
95
+ import { RouterProvider } from '@payfit/unity-components'
96
+ import { matchPath, useLocation, useNavigate } from 'react-router-dom'
97
+
98
+ export function UnityRouterBridge({ children }: { children: React.ReactNode }) {
99
+ const navigate = useNavigate()
100
+ const location = useLocation()
101
+
102
+ return (
103
+ <RouterProvider
104
+ navigate={to => navigate(to)}
105
+ isActive={(to, isExact) =>
106
+ Boolean(
107
+ matchPath({ path: to, end: Boolean(isExact) }, location.pathname),
108
+ )
109
+ }
110
+ >
111
+ {children}
112
+ </RouterProvider>
113
+ )
114
+ }
115
+ ```
@@ -4,9 +4,10 @@ description: >
4
4
  Load when adding Unity dialogs, side panels, bottom sheets, tooltips,
5
5
  popovers, or menus. Use it to choose modal versus non-modal overlays and keep
6
6
  disabled-control tooltip behavior accessible.
7
- type: core
8
- library: '@payfit/unity-components'
9
- library_version: '2.x'
7
+ metadata:
8
+ type: core
9
+ library: '@payfit/unity-components'
10
+ library_version: '2.x'
10
11
  sources:
11
12
  - 'PayFit/hr-apps:libs/shared/unity/components/src/components/dialog/Dialog.tsx'
12
13
  - 'PayFit/hr-apps:libs/shared/unity/components/src/components/side-panel/SidePanel.tsx'
@@ -73,135 +74,13 @@ manage focus, restoration, scroll lock, and Escape automatically.
73
74
 
74
75
  ## Core Patterns
75
76
 
76
- ### Dialog with DialogTrigger + DialogActions
77
+ - Compose blocking decisions with `DialogTrigger`, `Dialog`, and
78
+ `DialogActions`.
79
+ - Use `SidePanel` for side-anchored detail or editing flows.
80
+ - Use `Popover` for focusable informational content and `Menu` for action
81
+ lists.
77
82
 
78
- ```tsx
79
- import {
80
- Button,
81
- Dialog,
82
- DialogActions,
83
- DialogButton,
84
- DialogContent,
85
- DialogTitle,
86
- DialogTrigger,
87
- } from '@payfit/unity-components'
88
-
89
- export function ArchiveDialog({ onArchive }: { onArchive: () => void }) {
90
- return (
91
- <DialogTrigger>
92
- <Button>Archive</Button>
93
- <Dialog size="md">
94
- <DialogTitle>Archive this employee?</DialogTitle>
95
- <DialogContent>
96
- They will no longer appear in active lists. You can restore them
97
- later.
98
- </DialogContent>
99
- <DialogActions>
100
- <DialogButton variant="close">Cancel</DialogButton>
101
- <DialogButton variant="confirm" onPress={onArchive}>
102
- Archive
103
- </DialogButton>
104
- </DialogActions>
105
- </Dialog>
106
- </DialogTrigger>
107
- )
108
- }
109
- ```
110
-
111
- ### SidePanel for detail views
112
-
113
- ```tsx
114
- import { useState } from 'react'
115
-
116
- import {
117
- Button,
118
- SidePanel,
119
- SidePanelContent,
120
- SidePanelFooter,
121
- SidePanelHeader,
122
- } from '@payfit/unity-components'
123
-
124
- export function EmployeeDetailPanel({ employee }: { employee: Employee }) {
125
- const [isOpen, setIsOpen] = useState(false)
126
-
127
- return (
128
- <>
129
- <Button onPress={() => setIsOpen(true)}>View details</Button>
130
- <SidePanel isOpen={isOpen} onOpenChange={setIsOpen}>
131
- <SidePanelHeader>{employee.name}</SidePanelHeader>
132
- <SidePanelContent>
133
- <p>{employee.position}</p>
134
- <p>{employee.team}</p>
135
- </SidePanelContent>
136
- <SidePanelFooter>
137
- <Button variant="secondary" onPress={() => setIsOpen(false)}>
138
- Close
139
- </Button>
140
- </SidePanelFooter>
141
- </SidePanel>
142
- </>
143
- )
144
- }
145
- ```
146
-
147
- ### Popover for interactive informational content
148
-
149
- `title` is required (use `isTitleSrOnly` if you don't want it visible).
150
- Children may be focusable; the Popover is non-modal so the page stays
151
- interactive behind it.
152
-
153
- ```tsx
154
- import { Button, Popover, PopoverTrigger } from '@payfit/unity-components'
155
-
156
- export function HelpPopover() {
157
- return (
158
- <PopoverTrigger>
159
- <Button variant="tertiary">What is a working agreement?</Button>
160
- <Popover title="Working agreement">
161
- <p>
162
- A working agreement defines when, where and how an employee works. See
163
- the <a href="/handbook/working-agreement">handbook</a> for details.
164
- </p>
165
- </Popover>
166
- </PopoverTrigger>
167
- )
168
- }
169
- ```
170
-
171
- ### Menu for action lists
172
-
173
- `Menu` expects exactly two children: a trigger and a content block. Use
174
- `MenuTrigger` / `MenuContent` / `RawMenuItem` for the standard composition.
175
-
176
- ```tsx
177
- import {
178
- IconButton,
179
- Menu,
180
- MenuContent,
181
- MenuTrigger,
182
- RawMenuItem,
183
- } from '@payfit/unity-components'
184
-
185
- export function RowActions({
186
- onEdit,
187
- onArchive,
188
- }: {
189
- onEdit: () => void
190
- onArchive: () => void
191
- }) {
192
- return (
193
- <Menu>
194
- <MenuTrigger>
195
- <IconButton icon="MoreFilled" title="Actions" />
196
- </MenuTrigger>
197
- <MenuContent>
198
- <RawMenuItem onAction={onEdit}>Edit</RawMenuItem>
199
- <RawMenuItem onAction={onArchive}>Archive</RawMenuItem>
200
- </MenuContent>
201
- </Menu>
202
- )
203
- }
204
- ```
83
+ Read [references/patterns.md](references/patterns.md) for complete examples.
205
84
 
206
85
  ## Common Mistakes
207
86
 
@@ -224,7 +103,7 @@ Correct:
224
103
 
225
104
  Unity enforces the WCAG rule that disabled controls must not carry tooltips (the disabled element is not keyboard-focusable, so the tooltip is unreachable). This is enforced at the library level, not just policy.
226
105
 
227
- Source: components/tooltip/Tooltip.tsx; maintainer interview (enforced by Unity)
106
+ Source: components/tooltip/Tooltip.tsx
228
107
 
229
108
  ### MEDIUM Use Dialog for a non-blocking hint
230
109