@aircall/ds 0.13.0 → 0.15.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/README.md +31 -0
- package/dist/globals.css +1 -1
- package/dist/index.d.ts +94 -33
- package/dist/index.js +292 -42
- package/package.json +16 -3
- package/skills/aircall-ds/migrate-icons/SKILL.md +346 -0
- package/skills/aircall-ds/migrate-tractor/SKILL.md +314 -0
- package/skills/aircall-ds/migrate-tractor/accordion/SKILL.md +276 -0
- package/skills/aircall-ds/migrate-tractor/alert/SKILL.md +225 -0
- package/skills/aircall-ds/migrate-tractor/avatar/SKILL.md +272 -0
- package/skills/aircall-ds/migrate-tractor/badge/SKILL.md +274 -0
- package/skills/aircall-ds/migrate-tractor/button/SKILL.md +277 -0
- package/skills/aircall-ds/migrate-tractor/card/SKILL.md +278 -0
- package/skills/aircall-ds/migrate-tractor/combobox/SKILL.md +346 -0
- package/skills/aircall-ds/migrate-tractor/data-table/SKILL.md +333 -0
- package/skills/aircall-ds/migrate-tractor/dialog/SKILL.md +206 -0
- package/skills/aircall-ds/migrate-tractor/divider/SKILL.md +226 -0
- package/skills/aircall-ds/migrate-tractor/dropdown-menu/SKILL.md +266 -0
- package/skills/aircall-ds/migrate-tractor/dropzone/SKILL.md +338 -0
- package/skills/aircall-ds/migrate-tractor/form-and-field/SKILL.md +325 -0
- package/skills/aircall-ds/migrate-tractor/gauge/SKILL.md +248 -0
- package/skills/aircall-ds/migrate-tractor/input/SKILL.md +261 -0
- package/skills/aircall-ds/migrate-tractor/item/SKILL.md +298 -0
- package/skills/aircall-ds/migrate-tractor/link/SKILL.md +263 -0
- package/skills/aircall-ds/migrate-tractor/popover/SKILL.md +214 -0
- package/skills/aircall-ds/migrate-tractor/select/SKILL.md +245 -0
- package/skills/aircall-ds/migrate-tractor/sheet-vs-drawer/SKILL.md +272 -0
- package/skills/aircall-ds/migrate-tractor/skeleton/SKILL.md +190 -0
- package/skills/aircall-ds/migrate-tractor/styling/SKILL.md +421 -0
- package/skills/aircall-ds/migrate-tractor/tabs/SKILL.md +250 -0
- package/skills/aircall-ds/migrate-tractor/toast/SKILL.md +322 -0
- package/skills/aircall-ds/migrate-tractor/tooltip/SKILL.md +204 -0
- package/skills/aircall-ds/migrate-tractor/tree/SKILL.md +346 -0
- package/skills/aircall-ds/setup/SKILL.md +347 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aircall-ds/migrate-tractor/accordion
|
|
3
|
+
description: >
|
|
4
|
+
Migrate Tractor Accordion and AccordionSection to the @aircall/ds Accordion
|
|
5
|
+
compound (AccordionItem, AccordionTrigger, AccordionContent). Load when a file
|
|
6
|
+
imports Accordion or AccordionSection from @aircall/tractor.
|
|
7
|
+
type: sub-skill
|
|
8
|
+
library: aircall-ds
|
|
9
|
+
library_version: "0.13.0"
|
|
10
|
+
requires:
|
|
11
|
+
- aircall-ds/setup
|
|
12
|
+
- aircall-ds/migrate-tractor
|
|
13
|
+
sources:
|
|
14
|
+
- "aircall/hydra:docs/migration-guides/tractor-to-ds/recipes/accordion.md"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
This skill builds on aircall-ds/migrate-tractor. Apply all cross-cutting rules from that skill (prop renames, `render` prop, data attributes) before the accordion-specific steps below.
|
|
18
|
+
|
|
19
|
+
## 1. Component mapping
|
|
20
|
+
|
|
21
|
+
Tractor `Accordion` accepted `AccordionSection` children that bundled header, chevron, and body in a single component. DS splits each section into three named parts:
|
|
22
|
+
|
|
23
|
+
| Tractor | @aircall/ds |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `Accordion` | `Accordion` (state owner) |
|
|
26
|
+
| `AccordionSection` | `AccordionItem` + `AccordionTrigger` + `AccordionContent` |
|
|
27
|
+
| _(built into AccordionSection)_ | `AccordionTrigger` (header + built-in chevron) |
|
|
28
|
+
| _(built into AccordionSection)_ | `AccordionContent` (body panel) |
|
|
29
|
+
|
|
30
|
+
## 2. Verified DS exports (`packages/ds/src/index.ts`)
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Accordion, AccordionItem, AccordionTrigger, AccordionContent
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
All names used in the Before/After section below exist in the published public API.
|
|
37
|
+
|
|
38
|
+
## 3. Imports
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import {
|
|
42
|
+
Accordion,
|
|
43
|
+
AccordionItem,
|
|
44
|
+
AccordionTrigger,
|
|
45
|
+
AccordionContent,
|
|
46
|
+
} from '@aircall/ds';
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 4. Prop changes
|
|
50
|
+
|
|
51
|
+
| Tractor | DS | Notes |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| _(implicit — one open at a time)_ | default (no extra prop needed) | Single-open is the DS default |
|
|
54
|
+
| _(allowMultiple or equivalent)_ | `multiple` on `<Accordion>` | Boolean — allows multiple items open simultaneously |
|
|
55
|
+
| _(section key)_ | `value` on `<AccordionItem>` | Required; `number` or `string`; index works for static lists |
|
|
56
|
+
| _(section disabled)_ | `disabled` on `<AccordionItem>` | Disables the trigger and keeps the panel closed |
|
|
57
|
+
| `defaultExpanded` / open sections | `defaultValue` on `<Accordion>` | Always an array, even in single-open mode: `defaultValue={[0]}` |
|
|
58
|
+
| controlled open | `value` + `onValueChange` on `<Accordion>` | `value` is an array; `onValueChange` receives `(number \| string)[]` |
|
|
59
|
+
|
|
60
|
+
## 5. Before / After
|
|
61
|
+
|
|
62
|
+
### Static list (most common)
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
// Before
|
|
66
|
+
import { Accordion, AccordionSection } from '@aircall/tractor';
|
|
67
|
+
|
|
68
|
+
<Accordion>
|
|
69
|
+
<AccordionSection title="General">
|
|
70
|
+
<p>General settings content.</p>
|
|
71
|
+
</AccordionSection>
|
|
72
|
+
<AccordionSection title="Billing">
|
|
73
|
+
<p>Billing details content.</p>
|
|
74
|
+
</AccordionSection>
|
|
75
|
+
</Accordion>
|
|
76
|
+
|
|
77
|
+
// After
|
|
78
|
+
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@aircall/ds';
|
|
79
|
+
|
|
80
|
+
<Accordion defaultValue={[0]}>
|
|
81
|
+
<AccordionItem value={0}>
|
|
82
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
83
|
+
<AccordionContent>
|
|
84
|
+
<p>General settings content.</p>
|
|
85
|
+
</AccordionContent>
|
|
86
|
+
</AccordionItem>
|
|
87
|
+
<AccordionItem value={1}>
|
|
88
|
+
<AccordionTrigger>Billing</AccordionTrigger>
|
|
89
|
+
<AccordionContent>
|
|
90
|
+
<p>Billing details content.</p>
|
|
91
|
+
</AccordionContent>
|
|
92
|
+
</AccordionItem>
|
|
93
|
+
</Accordion>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Key changes:
|
|
97
|
+
- Each `AccordionSection` becomes three components: `AccordionItem` (identity + state), `AccordionTrigger` (header label), `AccordionContent` (body)
|
|
98
|
+
- `title` prop on `AccordionSection` becomes the children of `AccordionTrigger`
|
|
99
|
+
- Every `AccordionItem` requires a `value` prop — the index is fine for static lists
|
|
100
|
+
- `defaultValue` is always an array on `Accordion`
|
|
101
|
+
|
|
102
|
+
### Dynamic list (map over data)
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// Before
|
|
106
|
+
import { Accordion, AccordionSection } from '@aircall/tractor';
|
|
107
|
+
|
|
108
|
+
<Accordion>
|
|
109
|
+
{sections.map(section => (
|
|
110
|
+
<AccordionSection key={section.id} title={section.title}>
|
|
111
|
+
{section.body}
|
|
112
|
+
</AccordionSection>
|
|
113
|
+
))}
|
|
114
|
+
</Accordion>
|
|
115
|
+
|
|
116
|
+
// After
|
|
117
|
+
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@aircall/ds';
|
|
118
|
+
|
|
119
|
+
<Accordion defaultValue={[0]}>
|
|
120
|
+
{sections.map((section, index) => (
|
|
121
|
+
<AccordionItem key={section.id} value={index}>
|
|
122
|
+
<AccordionTrigger>{section.title}</AccordionTrigger>
|
|
123
|
+
<AccordionContent>{section.body}</AccordionContent>
|
|
124
|
+
</AccordionItem>
|
|
125
|
+
))}
|
|
126
|
+
</Accordion>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Multiple sections open simultaneously
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
// After — allow more than one panel open at a time
|
|
133
|
+
<Accordion multiple defaultValue={[0, 1]}>
|
|
134
|
+
<AccordionItem value={0}>
|
|
135
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
136
|
+
<AccordionContent>General settings content.</AccordionContent>
|
|
137
|
+
</AccordionItem>
|
|
138
|
+
<AccordionItem value={1}>
|
|
139
|
+
<AccordionTrigger>Billing</AccordionTrigger>
|
|
140
|
+
<AccordionContent>Billing details content.</AccordionContent>
|
|
141
|
+
</AccordionItem>
|
|
142
|
+
</Accordion>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Controlled
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
// After — controlled open state
|
|
149
|
+
const [openSections, setOpenSections] = React.useState<number[]>([0]);
|
|
150
|
+
|
|
151
|
+
<Accordion value={openSections} onValueChange={setOpenSections}>
|
|
152
|
+
<AccordionItem value={0}>
|
|
153
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
154
|
+
<AccordionContent>General settings content.</AccordionContent>
|
|
155
|
+
</AccordionItem>
|
|
156
|
+
<AccordionItem value={1}>
|
|
157
|
+
<AccordionTrigger>Billing</AccordionTrigger>
|
|
158
|
+
<AccordionContent>Billing details content.</AccordionContent>
|
|
159
|
+
</AccordionItem>
|
|
160
|
+
</Accordion>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Disabled section
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// After — disable one item
|
|
167
|
+
<Accordion defaultValue={[0]}>
|
|
168
|
+
<AccordionItem value={0}>
|
|
169
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
170
|
+
<AccordionContent>General settings content.</AccordionContent>
|
|
171
|
+
</AccordionItem>
|
|
172
|
+
<AccordionItem value={1} disabled>
|
|
173
|
+
<AccordionTrigger>Billing</AccordionTrigger>
|
|
174
|
+
<AccordionContent>Billing details content.</AccordionContent>
|
|
175
|
+
</AccordionItem>
|
|
176
|
+
</Accordion>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Width / spacing
|
|
180
|
+
|
|
181
|
+
`Accordion` has no layout props. Apply width via `className`:
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
<Accordion defaultValue={[0]} className="w-[450px]">
|
|
185
|
+
...
|
|
186
|
+
</Accordion>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 6. Common Mistakes
|
|
192
|
+
|
|
193
|
+
### Mistake 1 — Adding a custom chevron inside `AccordionTrigger`
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
// Wrong — AccordionTrigger renders its own chevron; adding another doubles it
|
|
197
|
+
<AccordionTrigger>
|
|
198
|
+
General
|
|
199
|
+
<ChevronDownIcon />
|
|
200
|
+
</AccordionTrigger>
|
|
201
|
+
|
|
202
|
+
// Correct — pass only the header label as children
|
|
203
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`AccordionTrigger` internally renders a `ChevronDownIcon` / `ChevronUpIcon` pair that swaps on open/close state. Adding your own icon duplicates it visually. Pass only the label text (or non-icon content) as children.
|
|
207
|
+
|
|
208
|
+
Source: `packages/ds/src/components/accordion.tsx`
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### Mistake 2 — Passing `defaultValue` / `value` as a scalar instead of an array
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
// Wrong — scalar causes a TypeScript error and the panel does not open
|
|
216
|
+
<Accordion defaultValue={0}>
|
|
217
|
+
<AccordionItem value={0}>
|
|
218
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
219
|
+
<AccordionContent>General settings content.</AccordionContent>
|
|
220
|
+
</AccordionItem>
|
|
221
|
+
</Accordion>
|
|
222
|
+
|
|
223
|
+
// Correct — always wrap in an array, even for a single default item
|
|
224
|
+
<Accordion defaultValue={[0]}>
|
|
225
|
+
<AccordionItem value={0}>
|
|
226
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
227
|
+
<AccordionContent>General settings content.</AccordionContent>
|
|
228
|
+
</AccordionItem>
|
|
229
|
+
</Accordion>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Both `defaultValue` and `value` on `Accordion` are typed as `(number | string)[]`. Passing a bare scalar is a type error and the Base UI primitive will not open the panel.
|
|
233
|
+
|
|
234
|
+
Source: `packages/ds/src/components/accordion.tsx`
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### Mistake 3 — Omitting the `value` prop on `AccordionItem`
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
// Wrong — AccordionItem without value cannot be identified for open/close state
|
|
242
|
+
<AccordionItem>
|
|
243
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
244
|
+
<AccordionContent>General settings content.</AccordionContent>
|
|
245
|
+
</AccordionItem>
|
|
246
|
+
|
|
247
|
+
// Correct — every AccordionItem needs a unique value
|
|
248
|
+
<AccordionItem value={0}>
|
|
249
|
+
<AccordionTrigger>General</AccordionTrigger>
|
|
250
|
+
<AccordionContent>General settings content.</AccordionContent>
|
|
251
|
+
</AccordionItem>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`value` is required by the Base UI Accordion primitive to track which items are open. Without it the item cannot be matched against `defaultValue` / `value` on the root `Accordion`, so the panel stays permanently closed and toggling has no effect.
|
|
255
|
+
|
|
256
|
+
Source: `packages/ds/src/components/accordion.tsx`
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
### Mistake 4 — Using `type="multiple"` instead of the `multiple` boolean
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
// Wrong — type prop does not exist on DS Accordion; silently ignored
|
|
264
|
+
<Accordion type="multiple" defaultValue={[0, 1]}>
|
|
265
|
+
...
|
|
266
|
+
</Accordion>
|
|
267
|
+
|
|
268
|
+
// Correct — DS uses a boolean `multiple` prop (same convention as ToggleGroup)
|
|
269
|
+
<Accordion multiple defaultValue={[0, 1]}>
|
|
270
|
+
...
|
|
271
|
+
</Accordion>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Tractor (and some other libraries) use `type="single" | "multiple"` to control this behavior. DS uses the simpler boolean `multiple` prop. Passing `type="multiple"` is silently ignored — the accordion reverts to single-open mode with no TypeScript error.
|
|
275
|
+
|
|
276
|
+
Source: `packages/ds/src/components/accordion.tsx`
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: aircall-ds/migrate-tractor/alert
|
|
3
|
+
description: >
|
|
4
|
+
Migrate Tractor Banner (Banner, BannerHeading, BannerIcon, BannerParagraph) to
|
|
5
|
+
@aircall/ds Alert (rounded) vs Banner (inline). Load when a file imports Banner*
|
|
6
|
+
from @aircall/tractor.
|
|
7
|
+
type: sub-skill
|
|
8
|
+
library: aircall-ds
|
|
9
|
+
library_version: "0.13.0"
|
|
10
|
+
requires:
|
|
11
|
+
- aircall-ds/setup
|
|
12
|
+
- aircall-ds/migrate-tractor
|
|
13
|
+
sources:
|
|
14
|
+
- "aircall/hydra:docs/migration-guides/tractor-to-ds/recipes/alert-and-banner.md"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
This skill builds on aircall-ds/migrate-tractor. Apply all cross-cutting rules from that skill (prop renames, `render` prop, data attributes) before the alert-specific steps below.
|
|
18
|
+
|
|
19
|
+
## 1. Alert vs Banner — pick the right component
|
|
20
|
+
|
|
21
|
+
Tractor `Banner` maps to **two** DS components. Choose based on visual shape:
|
|
22
|
+
|
|
23
|
+
| If the Tractor `Banner` looks like… | Use DS component |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| Rounded card-style callout (`rounded-3xl`, full border) | `Alert` |
|
|
26
|
+
| Full-width inline strip with accent edge (square corners, border on one side) | `Banner` |
|
|
27
|
+
|
|
28
|
+
`Banner` re-exports the `Alert*` sub-parts under `Banner*` aliases — the children grammar is identical. Pick the container; the parts follow.
|
|
29
|
+
|
|
30
|
+
## 2. Sub-part mapping
|
|
31
|
+
|
|
32
|
+
| Tractor | DS (`Alert`) | DS (`Banner`) |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| `BannerHeading` | `AlertTitle` | `BannerTitle` |
|
|
35
|
+
| `BannerIcon` | first `<svg>` child — **no wrapper** | same |
|
|
36
|
+
| `BannerSuffix` | `AlertAction` | `BannerAction` |
|
|
37
|
+
| `BannerParagraph` (body text) | `AlertDescription` | `BannerDescription` |
|
|
38
|
+
|
|
39
|
+
> **Icon is a direct child, not a part.** `Alert`/`Banner` detect a leading icon via `has-[>svg]` CSS. Render the icon as the **first child** — never wrap it. There is no `AlertIcon` or `BannerIcon` component in DS.
|
|
40
|
+
|
|
41
|
+
## 3. Verified DS exports (`packages/ds/src/index.ts`)
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Alert, AlertAction, AlertDescription, AlertTitle,
|
|
45
|
+
Banner, BannerAction, BannerDescription, BannerTitle,
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
All eight names exist in the published public API. Do not use any other Alert/Banner parts.
|
|
49
|
+
|
|
50
|
+
## 4. Imports
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import {
|
|
54
|
+
Alert, AlertTitle, AlertDescription, AlertAction,
|
|
55
|
+
Banner, BannerTitle, BannerDescription, BannerAction,
|
|
56
|
+
Button,
|
|
57
|
+
} from '@aircall/ds';
|
|
58
|
+
import { Info, AlertTriangle, X } from '@aircall/react-icons';
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 5. Before / After
|
|
62
|
+
|
|
63
|
+
### Before (Tractor)
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { Banner, BannerHeading, BannerIcon, BannerParagraph, BannerSuffix } from '@aircall/tractor';
|
|
67
|
+
|
|
68
|
+
// Rounded callout
|
|
69
|
+
<Banner variant="informative">
|
|
70
|
+
<BannerIcon><Info /></BannerIcon>
|
|
71
|
+
<BannerHeading>Heads up</BannerHeading>
|
|
72
|
+
<BannerParagraph>Your trial ends in 3 days.</BannerParagraph>
|
|
73
|
+
<BannerSuffix><button onClick={onDismiss}>✕</button></BannerSuffix>
|
|
74
|
+
</Banner>
|
|
75
|
+
|
|
76
|
+
// Full-width inline strip
|
|
77
|
+
<Banner variant="critical">
|
|
78
|
+
<BannerIcon><AlertTriangle /></BannerIcon>
|
|
79
|
+
<BannerHeading>Limited connectivity</BannerHeading>
|
|
80
|
+
<BannerParagraph>Some calls may not connect.</BannerParagraph>
|
|
81
|
+
<BannerSuffix><button onClick={onRetry}>Retry</button></BannerSuffix>
|
|
82
|
+
</Banner>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### After (DS)
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import {
|
|
89
|
+
Alert, AlertTitle, AlertDescription, AlertAction,
|
|
90
|
+
Banner, BannerTitle, BannerDescription, BannerAction,
|
|
91
|
+
Button,
|
|
92
|
+
} from '@aircall/ds';
|
|
93
|
+
import { Info, AlertTriangle, X } from '@aircall/react-icons';
|
|
94
|
+
|
|
95
|
+
// Rounded callout → Alert
|
|
96
|
+
<Alert variant="info">
|
|
97
|
+
<Info /> {/* BannerIcon → first <svg> child, no wrapper */}
|
|
98
|
+
<AlertTitle>Heads up</AlertTitle>
|
|
99
|
+
<AlertDescription>Your trial ends in 3 days.</AlertDescription>
|
|
100
|
+
<AlertAction>
|
|
101
|
+
<Button variant="ghost" size="icon-sm" onClick={onDismiss}><X /></Button>
|
|
102
|
+
</AlertAction>
|
|
103
|
+
</Alert>
|
|
104
|
+
|
|
105
|
+
// Full-width inline strip → Banner
|
|
106
|
+
<Banner variant="error" borderDirection="top">
|
|
107
|
+
<AlertTriangle />
|
|
108
|
+
<BannerTitle>Limited connectivity</BannerTitle>
|
|
109
|
+
<BannerDescription>Some calls may not connect.</BannerDescription>
|
|
110
|
+
<BannerAction>
|
|
111
|
+
<Button variant="outline" size="sm" onClick={onRetry}>Retry</Button>
|
|
112
|
+
</BannerAction>
|
|
113
|
+
</Banner>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 6. Variant mapping
|
|
117
|
+
|
|
118
|
+
| Tractor variant | DS variant |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| `informative` | `info` |
|
|
121
|
+
| `critical` | `error` |
|
|
122
|
+
| `warning` | `warning` |
|
|
123
|
+
| `positive` / `success` | `success` |
|
|
124
|
+
| (default / none) | `default` |
|
|
125
|
+
|
|
126
|
+
Both `Alert` and `Banner` share the same set: `default` / `info` / `success` / `warning` / `error`.
|
|
127
|
+
|
|
128
|
+
## 7. `Banner`-only prop: `borderDirection`
|
|
129
|
+
|
|
130
|
+
`Banner` accepts `borderDirection="top" | "bottom"` to place the accent border. `Alert` has no border-side prop — it is fully bordered and rounded.
|
|
131
|
+
|
|
132
|
+
## 8. Common Mistakes
|
|
133
|
+
|
|
134
|
+
### Mistake 1 — Wrapping the icon in a part component
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
// Wrong
|
|
138
|
+
<Alert variant="info">
|
|
139
|
+
<AlertIcon><Info /></AlertIcon> {/* AlertIcon does not exist in @aircall/ds */}
|
|
140
|
+
...
|
|
141
|
+
</Alert>
|
|
142
|
+
|
|
143
|
+
// Correct
|
|
144
|
+
<Alert variant="info">
|
|
145
|
+
<Info /> {/* bare <svg> first child activates the grid */}
|
|
146
|
+
...
|
|
147
|
+
</Alert>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`Alert`/`Banner` use `has-[>svg]` to detect the icon slot. Wrapping the SVG in any element breaks the CSS selector and the icon fails to render in the correct grid area.
|
|
151
|
+
Source: `packages/ds/src/components/alert.tsx`
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### Mistake 2 — Using `destructive` instead of `error`
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
// Wrong
|
|
159
|
+
<Alert variant="destructive">...</Alert> {/* destructive is a Button variant, not Alert */}
|
|
160
|
+
|
|
161
|
+
// Correct
|
|
162
|
+
<Alert variant="error">...</Alert> {/* maps Tractor critical */}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`destructive` is a `Button` variant. `Alert`/`Banner` only accept `default | info | success | warning | error`. Passing an unknown variant silently falls through to `default` styling.
|
|
166
|
+
Source: `packages/ds/src/components/alert.tsx`
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Mistake 3 — Using `AlertAction`/`BannerAction` for the icon instead of the suffix
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// Wrong
|
|
174
|
+
<Alert variant="warning">
|
|
175
|
+
<AlertAction><AlertTriangle /></AlertAction> {/* icon in the trailing action slot */}
|
|
176
|
+
<AlertTitle>Watch out</AlertTitle>
|
|
177
|
+
</Alert>
|
|
178
|
+
|
|
179
|
+
// Correct
|
|
180
|
+
<Alert variant="warning">
|
|
181
|
+
<AlertTriangle /> {/* first <svg> child → leading icon slot */}
|
|
182
|
+
<AlertTitle>Watch out</AlertTitle>
|
|
183
|
+
</Alert>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`AlertAction`/`BannerAction` is the **trailing** slot (self-positions right, vertically centered). It is for close buttons or CTAs, not the leading icon. Placing the icon there yields wrong layout.
|
|
187
|
+
Source: `packages/ds/src/components/alert.tsx`
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### Mistake 4 — Setting icon color manually
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
// Wrong
|
|
195
|
+
<Alert variant="info">
|
|
196
|
+
<Info className="text-blue-500" /> {/* manual color overrides variant-driven color */}
|
|
197
|
+
...
|
|
198
|
+
</Alert>
|
|
199
|
+
|
|
200
|
+
// Correct
|
|
201
|
+
<Alert variant="info">
|
|
202
|
+
<Info /> {/* variant applies [&>svg]:text-info automatically */}
|
|
203
|
+
...
|
|
204
|
+
</Alert>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`Alert`/`Banner` apply `[&>svg]:text-<variant>` on the root. Explicit color classes on the icon fight the cascade and cause inconsistency across themes.
|
|
208
|
+
Source: `packages/ds/src/components/alert.tsx`
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### Mistake 5 — Using `borderDirection` on `Alert`
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
// Wrong
|
|
216
|
+
<Alert variant="warning" borderDirection="top">...</Alert> {/* prop not supported on Alert */}
|
|
217
|
+
|
|
218
|
+
// Correct
|
|
219
|
+
<Banner variant="warning" borderDirection="top"> {/* Banner is the inline strip */}
|
|
220
|
+
...
|
|
221
|
+
</Banner>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`borderDirection` is a `Banner`-only prop (inline strip component). `Alert` is always fully bordered and rounded; it has no border-side prop. The prop is silently ignored on `Alert`.
|
|
225
|
+
Source: `packages/ds/src/components/banner.tsx`
|