@payfit/unity-components 2.35.4 → 2.35.6

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,377 @@
1
+ ---
2
+ name: unity-find-component
3
+ description: >
4
+ Find or build the right Unity component. Catalog of ~226 named exports
5
+ in @payfit/unity-components grouped by purpose (layout: Flex/Grid/Card,
6
+ navigation: RawLink/Nav/Tabs/Breadcrumbs/Pagination, form fields:
7
+ TextField/SelectField/NumberField/CheckboxField/etc., buttons: Button/
8
+ IconButton/RawLinkButton, overlays: Dialog/Popover/Menu/Tooltip,
9
+ content: Pill/Badge/Alert/DataTable/Carousel). Decision tree: prefer
10
+ Unity → fall back to React Aria + uy:* classes → Midnight as last resort.
11
+ Typed UnityIcon src convention. RHF deprecated; Tanstack Form everywhere.
12
+ type: core
13
+ library: '@payfit/unity-components'
14
+ library_version: '2.x'
15
+ sources:
16
+ - 'PayFit/hr-apps:libs/shared/unity/components/src/index.ts'
17
+ - 'PayFit/hr-apps:libs/shared/unity/icons/src/components/icon/parts/IconSprite.tsx'
18
+ - 'PayFit/hr-apps:libs/shared/unity/icons/src/generated/index.ts'
19
+ - 'PayFit/hr-apps:libs/shared/unity/components/OVERVIEW.md'
20
+ - 'PayFit/hr-apps:AGENTS.md'
21
+ ---
22
+
23
+ Routing skill for selecting a Unity component before writing UI code. Walk
24
+ the catalog, then the decision tree, then the commonly-confused pairs.
25
+
26
+ ## Quick Reference
27
+
28
+ All exports come from a single entry: `@payfit/unity-components`. There are
29
+ no sub-paths for runtime components; the only sub-paths are
30
+ `@payfit/unity-components/integrations/tanstack-router` (router-aware
31
+ navigation) and `@payfit/unity-components/i18n/<locale>.json` (message
32
+ bundles). Names below are grouped by purpose.
33
+
34
+ ### Layout
35
+
36
+ `Flex`, `FlexItem`, `Grid`, `GridItem`, `Card`, `CardTitle`,
37
+ `CardContent`, `SelectableCard...`, `NavigationCard`, `NavigationCardGroup`,
38
+ `Page`, `PageHeader`, `PageHeading`, `AppLayout`, `AppMenu`,
39
+ `FunnelLayout`, `FunnelPage`, `FunnelBody`, `FunnelSidebar`, `FunnelTopBar`,
40
+ `FunnelPageHeader`, `FunnelPageContent`, `FunnelPageFooter`,
41
+ `FunnelPageActions`, `FunnelBackButton`.
42
+
43
+ ### Navigation
44
+
45
+ Router-agnostic (base entry): `RawLink`, `RawNavItem`, `RawBreadcrumbLink`,
46
+ `RawPaginationLink`, `RawPaginationPrevious`, `RawPaginationNext`, `RawTab`,
47
+ `Nav`, `NavGroup`, `Breadcrumbs`, `Breadcrumb`, `Pagination`,
48
+ `PaginationContent`, `PaginationItem`, `PaginationEllipsis`, `Tabs`,
49
+ `TabList`, `TabPanel`, `SkipLinks`, `TaskMenu`, `RawTask`, `RawSubTask`,
50
+ `TaskGroup`, `ListView`, `RawListViewItem`, `ListViewSection`,
51
+ `ListViewItemLabel`, `ListViewItemText`.
52
+
53
+ Router-aware (from
54
+ `@payfit/unity-components/integrations/tanstack-router`): `Link`,
55
+ `NavItem`, `BreadcrumbLink`, `PaginationLink`, `Tab`.
56
+
57
+ ### Form fields (Tanstack Form — current)
58
+
59
+ Bound via `form.AppField` then `field.<Name>`: `TextField`, `SelectField`,
60
+ `NumberField`, `CheckboxField`, `CheckboxGroupField`, `DatePickerField`,
61
+ `DateRangePickerField`, `MultiSelectField`, `RadioButtonGroupField`,
62
+ `SelectableButtonGroupField`, `SelectableCardCheckboxGroupField`,
63
+ `SelectableCardRadioGroupField`, `ToggleSwitchField`,
64
+ `ToggleSwitchGroupField`, `PasswordField`.
65
+
66
+ ### Form primitives (no form state)
67
+
68
+ `Input`, `NumberInput`, `Select`, `SelectButton`, `SelectOption`,
69
+ `SelectOptionGroup`, `SelectOptionHelper`, `MultiSelect`,
70
+ `MultiSelectOption`, `MultiSelectOptGroup`, `Checkbox`, `CheckboxStandalone`,
71
+ `CheckboxGroup`, `RadioButtonGroup`, `RadioButton`, `RadioButtonHelper`,
72
+ `TextArea`, `ToggleSwitch`, `ToggleSwitchGroup`, `SegmentedButtonGroup`,
73
+ `ToggleButton`, `SelectableButtonGroup`, `SelectableButton`, `Search`,
74
+ `PhoneNumberInput`, `DatePicker`, `DateCalendar`, `DateRangePicker`,
75
+ `DateRangeCalendar`, `Autocomplete`, `AutocompleteItem`,
76
+ `AutocompleteItemGroup`, `Fieldset`, `FieldGroup`, `Label`, `FormField`,
77
+ `FormControl`, `FormLabel`, `FormHelperText`, `FormFeedbackText`.
78
+
79
+ ### Buttons and actions
80
+
81
+ `Button`, `IconButton`, `CircularIconButton`, `RawLinkButton`, `Actionable`,
82
+ `ActionBar`, `ActionBarRoot`, `ActionBarButton`, `ActionBarIconButton`,
83
+ `FloatingActionBar`, `Anchor`.
84
+
85
+ ### Overlays
86
+
87
+ Modal: `Dialog`, `DialogContent`, `DialogTitle`, `DialogActions`,
88
+ `DialogButton`, `PromoDialog`, `PromoDialogHero`, `PromoDialogTitle`,
89
+ `PromoDialogSubtitle`, `PromoDialogContent`, `PromoDialogActions`,
90
+ `SidePanel`, `SidePanelHeader`, `SidePanelContent`, `SidePanelFooter`,
91
+ `BottomSheet`, `BottomSheetHeader`, `BottomSheetContent`,
92
+ `BottomSheetFooter`.
93
+
94
+ Non-modal: `Tooltip`, `DefinitionTooltip`, `Popover`, `Menu`, `MenuTrigger`,
95
+ `MenuContent`, `MenuHeader`, `RawMenuItem`, `MenuSeparator`.
96
+
97
+ ### Content and data
98
+
99
+ `Text`, `Icon`, `Pill`, `Badge`, `Alert`, `AlertTitle`, `AlertContent`,
100
+ `AlertActions`, `Avatar`, `AvatarFallback`, `AvatarIcon`, `AvatarImage`,
101
+ `AvatarPair`, `DataTable`, `DataTableRoot`, `DataTableBulkActions`,
102
+ `Table`, `TableBody`, `TableHeader`, `TableColumnHeader`, `TableRow`,
103
+ `TableCell`, `TableEmptyState`, `TablePagination`, `Filter`,
104
+ `FilterToolbar`, `Carousel`, `CarouselHeader`, `CarouselContent`,
105
+ `CarouselSlide`, `CarouselNav`, `Collapsible`, `CollapsibleTitle`,
106
+ `CollapsibleContent`, `Timeline`, `TimelineStep`, `TimelineStepHeader`,
107
+ `TimelineStepDescription`.
108
+
109
+ ### Status and loading
110
+
111
+ `Spinner`, `ProgressBar`, `FullPageLoader`, `EmptyState`, `EmptyStateIcon`,
112
+ `EmptyStateContent`, `EmptyStateActions`, `EmptyStateGetStarted`,
113
+ `EmptyStateWaitingForData`, `EmptyStateGoodJob`,
114
+ `EmptyStateUpgradeRequired`, `EmptyStateNoSearchResults`,
115
+ `EmptyStateUseDesktop`, `ErrorState`, `ToastManager`, `toast`.
116
+
117
+ ### Semantic and brand
118
+
119
+ `PayFitBrand`, `PayFitPreprod`.
120
+
121
+ ## Decision Tree
122
+
123
+ For every UI need, walk these three levels in order. Stop at the first that
124
+ fits.
125
+
126
+ ### Level 1 — Use Unity directly
127
+
128
+ If a named export covers the use case, import it. No abstractions over the
129
+ top.
130
+
131
+ ```tsx
132
+ import {
133
+ Button,
134
+ Dialog,
135
+ DialogActions,
136
+ DialogContent,
137
+ Pill,
138
+ } from '@payfit/unity-components'
139
+
140
+ export function ConfirmDelete({
141
+ isOpen,
142
+ onClose,
143
+ }: {
144
+ isOpen: boolean
145
+ onClose: () => void
146
+ }) {
147
+ return (
148
+ <Dialog isOpen={isOpen} onOpenChange={onClose}>
149
+ <DialogContent>
150
+ <Pill color="danger">Destructive</Pill>
151
+ </DialogContent>
152
+ <DialogActions>
153
+ <Button variant="secondary" onPress={onClose}>
154
+ Cancel
155
+ </Button>
156
+ <Button color="danger" onPress={onClose}>
157
+ Delete
158
+ </Button>
159
+ </DialogActions>
160
+ </Dialog>
161
+ )
162
+ }
163
+ ```
164
+
165
+ ### Level 2 — Fall back to React Aria + uy:\* classes
166
+
167
+ Build your own primitive only when Unity has no equivalent. Compose React
168
+ Aria primitives, style with the `uy:` prefix, and merge classes with
169
+ `uyMerge` / `uyTv`.
170
+
171
+ ```tsx
172
+ import { uyTv } from '@payfit/unity-themes'
173
+ import { ToggleButton } from 'react-aria-components'
174
+
175
+ const toggleStyles = uyTv({
176
+ base: 'uy:inline-flex uy:items-center uy:gap-100 uy:rounded-100 uy:px-200 uy:py-100',
177
+ variants: {
178
+ isSelected: {
179
+ true: 'uy:bg-surface-action uy:text-content-on-action',
180
+ false: 'uy:bg-surface-secondary uy:text-content-neutral',
181
+ },
182
+ },
183
+ })
184
+
185
+ export function CustomPivot({ label }: { label: string }) {
186
+ return (
187
+ <ToggleButton className={({ isSelected }) => toggleStyles({ isSelected })}>
188
+ {label}
189
+ </ToggleButton>
190
+ )
191
+ }
192
+ ```
193
+
194
+ ### Level 3 — Midnight (last resort, deprecated)
195
+
196
+ Only when (a) Unity has no equivalent, (b) React Aria + `uy:*` cannot
197
+ realistically rebuild it, and (c) the feature ships against a deadline. Open
198
+ a follow-up to migrate. Per `AGENTS.md`, Midnight is deprecated; never
199
+ import it into a new module.
200
+
201
+ ## Commonly Confused Pairs
202
+
203
+ - `Badge` vs `Pill`: `Badge` is a numeric/dot indicator anchored to another
204
+ element (notification count). `Pill` is a standalone label/status chip
205
+ with text content.
206
+ - `Card` vs `SelectableCard...` vs `NavigationCard`: `Card` is the generic
207
+ container. `SelectableCardCheckboxGroup` / `SelectableCardRadioGroup`
208
+ wrap cards as form inputs. `NavigationCard` wraps a card as a router-aware
209
+ link.
210
+ - `Button` vs `IconButton` vs `RawLinkButton`: `Button` for actions with
211
+ text. `IconButton` / `CircularIconButton` for icon-only actions
212
+ (requires `aria-label`). `RawLinkButton` renders as an anchor but styled
213
+ like a button — use when the action navigates.
214
+ - `Menu` vs `Popover`: `Menu` is a list of actionable items keyed by
215
+ keyboard (Enter/Arrow). `Popover` is a free-form floating panel and
216
+ requires a `title`.
217
+ - `Dialog` vs `PromoDialog`: `Dialog` for confirmation / edit flows.
218
+ `PromoDialog` for marketing / onboarding announcements; requires
219
+ `PromoDialogHero`.
220
+ - `Table` vs `DataTable`: `Table` is the layout primitive (header, body,
221
+ rows, cells) with no behavior. `DataTable` wires Tanstack Table for
222
+ sorting, filtering, pagination, virtualization, bulk actions.
223
+ - `ErrorState` vs `Alert`: `ErrorState` is a full-area empty-replacement
224
+ for "this section failed to load." `Alert` is an inline banner that
225
+ coexists with surrounding content.
226
+
227
+ ## Icon Source Convention
228
+
229
+ `Icon` takes a typed `src` prop of type `UnityIcon` — a literal union of
230
+ PascalCase names with a `Filled` or `Outlined` suffix (~310 values, from
231
+ `@payfit/unity-icons`). Strings outside that union are a type error. Do
232
+ not cast to `UnityIcon`; the cast bypasses the sprite-id guard and the
233
+ icon silently renders empty.
234
+
235
+ ```tsx
236
+ import type { UnityIcon } from '@payfit/unity-icons'
237
+
238
+ import { Icon } from '@payfit/unity-components'
239
+
240
+ const icon: UnityIcon = 'MagnifyingGlassOutlined'
241
+ ;<Icon src={icon} size={20} />
242
+ ```
243
+
244
+ ## Forms Notice
245
+
246
+ Tanstack Form (`useTanstackUnityForm`) is the only supported form system.
247
+ The React Hook Form path — `useUnityForm` plus the legacy `TextField` /
248
+ `SelectField` / `NumberField` etc. RHF wrappers exported from the same
249
+ index — is deprecated and scheduled for removal after the rebrand. Do not
250
+ author new code with `useUnityForm`. See `unity-tanstack-form`.
251
+
252
+ ## Common Mistakes
253
+
254
+ ### HIGH Hand-roll component that already exists
255
+
256
+ Wrong:
257
+
258
+ ```tsx
259
+ const Tag = ({ children }) => (
260
+ <span className="uy:rounded-full uy:px-200 uy:py-100 uy:bg-surface-primary">
261
+ {children}
262
+ </span>
263
+ )
264
+ ```
265
+
266
+ Correct:
267
+
268
+ ```tsx
269
+ import { Pill } from '@payfit/unity-components'
270
+
271
+ ;<Pill>{children}</Pill>
272
+ ```
273
+
274
+ The hand-rolled span re-derives Pill's tokens by guesswork and drifts from
275
+ the design-system source-of-truth on every theme update.
276
+
277
+ Source: libs/shared/unity/components/src/components/pill/Pill.tsx
278
+
279
+ ### HIGH Use React Aria primitive directly when Unity wraps it
280
+
281
+ Wrong:
282
+
283
+ ```tsx
284
+ import { Button as AriaButton } from 'react-aria-components'
285
+
286
+ ;<AriaButton>Click</AriaButton>
287
+ ```
288
+
289
+ Correct:
290
+
291
+ ```tsx
292
+ import { Button } from '@payfit/unity-components'
293
+
294
+ ;<Button variant="primary">Click</Button>
295
+ ```
296
+
297
+ The bare React Aria Button has no Unity theming, intl, or styling
298
+ defaults; you ship an unstyled element with no `uy:*` classes.
299
+
300
+ Source: libs/shared/unity/components/src/components/button/Button.tsx
301
+
302
+ ### HIGH Reach for Midnight when Unity has an equivalent
303
+
304
+ Wrong:
305
+
306
+ ```tsx
307
+ import { Button, Modal } from '@payfit/midnight'
308
+ ```
309
+
310
+ Correct:
311
+
312
+ ```tsx
313
+ import { Button, Dialog } from '@payfit/unity-components'
314
+ ```
315
+
316
+ Midnight is deprecated; new screens that import it cannot match the Unity
317
+ theme tokens and will require a migration pass later anyway.
318
+
319
+ Source: AGENTS.md "Do NOT use (deprecated)"; maintainer interview
320
+
321
+ ### MEDIUM Combine Input + FormField when \*Field exists
322
+
323
+ Wrong:
324
+
325
+ ```tsx
326
+ <FormField label="Name" error={errors.name}>
327
+ <Input {...register('name')} />
328
+ </FormField>
329
+ ```
330
+
331
+ Correct:
332
+
333
+ ```tsx
334
+ <form.AppField name="name">
335
+ {field => <field.TextField label="Name" />}
336
+ </form.AppField>
337
+ ```
338
+
339
+ Manual `FormField` + `Input` skips the label-for/aria-describedby wiring,
340
+ the `field.state.meta` error plumbing, and the required-state inference
341
+ that the `*Field` components handle.
342
+
343
+ Source: libs/shared/unity/components/src/components/form-field/FormField.tsx; index.ts:205-223
344
+
345
+ ### HIGH Pass an untyped string to Icon src and guess the name
346
+
347
+ Wrong:
348
+
349
+ ```tsx
350
+ <Icon src="search" size={20} />
351
+ <Icon src="trash-filled" />
352
+ const name: string = 'trash'
353
+ <Icon src={name as UnityIcon} />
354
+ ```
355
+
356
+ Correct:
357
+
358
+ ```tsx
359
+ import { Icon } from '@payfit/unity-components'
360
+ import type { UnityIcon } from '@payfit/unity-icons'
361
+ <Icon src="MagnifyingGlassOutlined" size={20} />
362
+ <Icon src="TrashFilled" />
363
+ type Props = { icon: UnityIcon }
364
+ ```
365
+
366
+ The `UnityIcon` literal union encodes the exact sprite ids; lowercase or
367
+ kebab-case strings have no matching `<symbol id>` in the injected sprite,
368
+ so `<use href="#search">` resolves to nothing and the SVG renders empty.
369
+
370
+ Source: libs/shared/unity/icons/src/components/icon/parts/IconSprite.tsx; generated/index.ts (UnityIcon type); maintainer interview
371
+
372
+ ## See also
373
+
374
+ - `unity-setup-feature-plugin` — required before any Unity import will work
375
+ - `unity-migrate-from-midnight` — when Level 3 fallback hits a Midnight
376
+ screen, follow this skill to replace it
377
+ - `unity-tanstack-form` — the only supported form authoring path