@aircall/ds 0.14.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.
Files changed (34) hide show
  1. package/README.md +31 -0
  2. package/dist/globals.css +1 -1
  3. package/dist/index.d.ts +28 -28
  4. package/dist/index.js +1 -1
  5. package/package.json +12 -2
  6. package/skills/aircall-ds/migrate-icons/SKILL.md +346 -0
  7. package/skills/aircall-ds/migrate-tractor/SKILL.md +314 -0
  8. package/skills/aircall-ds/migrate-tractor/accordion/SKILL.md +276 -0
  9. package/skills/aircall-ds/migrate-tractor/alert/SKILL.md +225 -0
  10. package/skills/aircall-ds/migrate-tractor/avatar/SKILL.md +272 -0
  11. package/skills/aircall-ds/migrate-tractor/badge/SKILL.md +274 -0
  12. package/skills/aircall-ds/migrate-tractor/button/SKILL.md +277 -0
  13. package/skills/aircall-ds/migrate-tractor/card/SKILL.md +278 -0
  14. package/skills/aircall-ds/migrate-tractor/combobox/SKILL.md +346 -0
  15. package/skills/aircall-ds/migrate-tractor/data-table/SKILL.md +333 -0
  16. package/skills/aircall-ds/migrate-tractor/dialog/SKILL.md +206 -0
  17. package/skills/aircall-ds/migrate-tractor/divider/SKILL.md +226 -0
  18. package/skills/aircall-ds/migrate-tractor/dropdown-menu/SKILL.md +266 -0
  19. package/skills/aircall-ds/migrate-tractor/dropzone/SKILL.md +338 -0
  20. package/skills/aircall-ds/migrate-tractor/form-and-field/SKILL.md +325 -0
  21. package/skills/aircall-ds/migrate-tractor/gauge/SKILL.md +248 -0
  22. package/skills/aircall-ds/migrate-tractor/input/SKILL.md +261 -0
  23. package/skills/aircall-ds/migrate-tractor/item/SKILL.md +298 -0
  24. package/skills/aircall-ds/migrate-tractor/link/SKILL.md +263 -0
  25. package/skills/aircall-ds/migrate-tractor/popover/SKILL.md +214 -0
  26. package/skills/aircall-ds/migrate-tractor/select/SKILL.md +245 -0
  27. package/skills/aircall-ds/migrate-tractor/sheet-vs-drawer/SKILL.md +272 -0
  28. package/skills/aircall-ds/migrate-tractor/skeleton/SKILL.md +190 -0
  29. package/skills/aircall-ds/migrate-tractor/styling/SKILL.md +421 -0
  30. package/skills/aircall-ds/migrate-tractor/tabs/SKILL.md +250 -0
  31. package/skills/aircall-ds/migrate-tractor/toast/SKILL.md +322 -0
  32. package/skills/aircall-ds/migrate-tractor/tooltip/SKILL.md +204 -0
  33. package/skills/aircall-ds/migrate-tractor/tree/SKILL.md +346 -0
  34. package/skills/aircall-ds/setup/SKILL.md +347 -0
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: aircall-ds/migrate-tractor/divider
3
+ description: >
4
+ Migrate Tractor Divider to the @aircall/ds Separator. Load when a file imports
5
+ Divider from @aircall/tractor. Maps orientation (vertical|horizontal), drops the
6
+ size and bg props, and replaces Box-level color overrides with Tailwind className.
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:packages/ds/src/components/separator.tsx"
15
+ ---
16
+
17
+ This skill builds on aircall-ds/migrate-tractor.
18
+
19
+ ## 1. Component mapping
20
+
21
+ | Tractor part | @aircall/ds part | Notes |
22
+ | --- | --- | --- |
23
+ | `<Divider>` | `<Separator>` | Name change; orientation prop is preserved |
24
+ | `size` prop | _(removed)_ | DS Separator is always 1 px; no thickness control |
25
+ | `bg` prop | `className` | Pass a Tailwind background utility to override the border color |
26
+ | Extends `BoxProps` | Extends `SeparatorPrimitive.Props` | DS root is a Base UI element, not a styled Box |
27
+
28
+ ## 2. Verified DS export (`packages/ds/src/index.ts`)
29
+
30
+ ```
31
+ Separator
32
+ ```
33
+
34
+ ## 3. Imports
35
+
36
+ **Before (Tractor):**
37
+ ```tsx
38
+ import { Divider } from '@aircall/tractor';
39
+ ```
40
+
41
+ **After (DS):**
42
+ ```tsx
43
+ import { Separator } from '@aircall/ds';
44
+ ```
45
+
46
+ ## 4. Prop changes
47
+
48
+ | Tractor prop | DS prop | Action |
49
+ | --- | --- | --- |
50
+ | `orientation?: 'vertical' \| 'horizontal'` | `orientation?: 'vertical' \| 'horizontal'` | Same values — pass through as-is |
51
+ | `size?: 'xSmall' \| 'small'` | _(removed)_ | Delete the prop; DS Separator is always 1 px thick |
52
+ | `bg?: string` | `className` | Replace with a Tailwind `bg-*` utility on `className` |
53
+
54
+ The DS `Separator` accepts all standard HTML attributes (`id`, `aria-*`, `data-*`, `style`, `className`) plus `orientation`. It does not accept `children`.
55
+
56
+ ### Default orientation difference
57
+
58
+ Tractor's `Divider` defaulted to `orientation="vertical"`. The DS `Separator` defaults to `orientation="horizontal"`. Always pass `orientation` explicitly on both sides of the migration to avoid a silent layout change.
59
+
60
+ ## 5. Sizing behavior
61
+
62
+ Tractor's `Divider` used a `size` prop to control thickness (`xSmall` = 1 px, `small` = 2 px). The DS `Separator` is always 1 px thick via Tailwind classes applied internally (`data-[orientation=horizontal]:h-px` / `data-[orientation=vertical]:w-px`). There is no thickness prop — if you need a 2 px separator, add `className="data-[orientation=horizontal]:h-0.5 data-[orientation=vertical]:w-0.5"`.
63
+
64
+ ## 6. Color behavior
65
+
66
+ Tractor's `Divider` accepted a Tractor design-token string via `bg` (e.g. `"graphic-default"`, `"neutral-700"`). The DS `Separator` uses `bg-border` by default (mapped to the DS border token). To override, pass a Tailwind utility class via `className`:
67
+
68
+ ```tsx
69
+ // Tractor custom color
70
+ <Divider orientation="vertical" bg="neutral-700" />
71
+
72
+ // DS equivalent
73
+ <Separator orientation="vertical" className="bg-neutral-700" />
74
+ ```
75
+
76
+ ## 7. Before / After examples
77
+
78
+ ### 7a. Horizontal separator between sections (default use case)
79
+
80
+ **Before (Tractor):**
81
+ ```tsx
82
+ import { Divider } from '@aircall/tractor';
83
+
84
+ export function SectionBreak() {
85
+ return <Divider orientation="horizontal" />;
86
+ }
87
+ ```
88
+
89
+ **After (DS):**
90
+ ```tsx
91
+ import { Separator } from '@aircall/ds';
92
+
93
+ export function SectionBreak() {
94
+ return <Separator orientation="horizontal" />;
95
+ }
96
+ ```
97
+
98
+ ### 7b. Vertical separator between inline items
99
+
100
+ **Before (Tractor):**
101
+ ```tsx
102
+ import { Divider } from '@aircall/tractor';
103
+
104
+ export function InlineActions() {
105
+ return (
106
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
107
+ <span>Calls</span>
108
+ <Divider orientation="vertical" />
109
+ <span>Contacts</span>
110
+ </div>
111
+ );
112
+ }
113
+ ```
114
+
115
+ **After (DS):**
116
+ ```tsx
117
+ import { Separator } from '@aircall/ds';
118
+
119
+ export function InlineActions() {
120
+ return (
121
+ <div className="flex items-center gap-2">
122
+ <span>Calls</span>
123
+ <Separator orientation="vertical" />
124
+ <span>Contacts</span>
125
+ </div>
126
+ );
127
+ }
128
+ ```
129
+
130
+ ### 7c. Thin divider with `size="xSmall"` dropped
131
+
132
+ **Before (Tractor):**
133
+ ```tsx
134
+ import { Divider } from '@aircall/tractor';
135
+
136
+ export function MenuDivider() {
137
+ return <Divider orientation="horizontal" size="xSmall" />;
138
+ }
139
+ ```
140
+
141
+ **After (DS):**
142
+ ```tsx
143
+ import { Separator } from '@aircall/ds';
144
+
145
+ export function MenuDivider() {
146
+ return <Separator orientation="horizontal" />;
147
+ }
148
+ ```
149
+
150
+ > Both `xSmall` (1 px) and DS default (1 px) are identical. Drop `size` without any replacement.
151
+
152
+ ### 7d. Custom color via `bg` → `className`
153
+
154
+ **Before (Tractor):**
155
+ ```tsx
156
+ import { Divider } from '@aircall/tractor';
157
+
158
+ export function BoldDivider() {
159
+ return <Divider orientation="horizontal" size="small" bg="neutral-700" />;
160
+ }
161
+ ```
162
+
163
+ **After (DS):**
164
+ ```tsx
165
+ import { Separator } from '@aircall/ds';
166
+
167
+ export function BoldDivider() {
168
+ return (
169
+ <Separator
170
+ orientation="horizontal"
171
+ className="bg-neutral-700 data-[orientation=horizontal]:h-0.5"
172
+ />
173
+ );
174
+ }
175
+ ```
176
+
177
+ > `size="small"` (2 px) has no direct DS equivalent — use `data-[orientation=horizontal]:h-0.5` to restore the 2 px thickness.
178
+
179
+ ## 8. Common mistakes
180
+
181
+ ### Mistake 1: Relying on the default orientation matching Tractor's default
182
+
183
+ ```tsx
184
+ // WRONG — Tractor default is "vertical"; DS default is "horizontal"; omitting orientation
185
+ // silently changes the rendered layout
186
+ <Separator />
187
+
188
+ // CORRECT — always pass orientation explicitly
189
+ <Separator orientation="vertical" />
190
+ ```
191
+
192
+ Tractor's `Divider` defaulted to `orientation="vertical"`. The DS `Separator` defaults to `orientation="horizontal"` (matching the Base UI primitive default). Omitting the prop after renaming the component causes a horizontal line to appear where a vertical one was expected — no warning, no error.
193
+
194
+ Source: `packages/ds/src/components/separator.tsx` — `orientation = 'horizontal'` is the destructured default.
195
+
196
+ ---
197
+
198
+ ### Mistake 2: Passing the `size` prop expecting thickness control
199
+
200
+ ```tsx
201
+ // WRONG — DS Separator has no `size` prop; it spreads onto the DOM element as a string attribute
202
+ <Separator orientation="horizontal" size="small" />
203
+
204
+ // CORRECT — drop `size`; for 2 px thickness add a Tailwind class
205
+ <Separator orientation="horizontal" className="data-[orientation=horizontal]:h-0.5" />
206
+ ```
207
+
208
+ `SeparatorProps` extends `SeparatorPrimitive.Props`, which has no `size` field. Because `Separator` spreads unknown props onto the Base UI primitive, `size="small"` becomes a non-standard DOM attribute, triggering a React warning and failing strict DOM validation.
209
+
210
+ Source: `packages/ds/src/components/separator.tsx` — `SeparatorProps` extends `SeparatorPrimitive.Props` with no additional fields.
211
+
212
+ ---
213
+
214
+ ### Mistake 3: Passing a Tractor design-token string to `bg` or `color`
215
+
216
+ ```tsx
217
+ // WRONG — DS Separator has no `bg` prop; passing it spreads a raw string onto the DOM
218
+ <Separator orientation="vertical" bg="graphic-default" />
219
+
220
+ // CORRECT — use a Tailwind bg-* utility via className
221
+ <Separator orientation="vertical" className="bg-border" />
222
+ ```
223
+
224
+ Tractor's `Divider` was built on `Box`, which accepted Tractor design-token strings for `bg`. `Separator` is a Base UI element with no styled-system layer — there is no `bg` prop. Passing it results in `bg="graphic-default"` becoming an invalid HTML attribute on the rendered DOM element.
225
+
226
+ Source: `packages/ds/src/components/separator.tsx` — root renders `<SeparatorPrimitive>` directly; `bg` is not in `SeparatorPrimitive.Props`.
@@ -0,0 +1,266 @@
1
+ ---
2
+ name: aircall-ds/migrate-tractor/dropdown-menu
3
+ description: >
4
+ Migrate Tractor Dropdown and ActionMenu to the @aircall/ds DropdownMenu compound.
5
+ Load when a file imports Dropdown, ActionMenu, or ActionMenuItem from @aircall/tractor.
6
+ Covers trigger render prop, destructive item variant, icon placement, and the Group-wraps-Label rule.
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/dropdown-menu.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 dropdown-menu-specific steps below.
18
+
19
+ ## 1. Component mapping
20
+
21
+ Tractor `Dropdown` (labelled button trigger) and `ActionMenu` (icon-only "•••" trigger) both map to the same DS `DropdownMenu` compound. The visual difference is achieved through the `Button` variant/size passed to `DropdownMenuTrigger`'s `render` prop.
22
+
23
+ | Tractor part | DS compound part | Role |
24
+ | --- | --- | --- |
25
+ | `<Dropdown>` / `<ActionMenu>` | `<DropdownMenu>` | State owner (open, modal) |
26
+ | _(trigger button)_ | `<DropdownMenuTrigger>` | Clickable trigger — accepts any button via `render` |
27
+ | `<DropdownItem>` / `<ActionMenuItem>` | `<DropdownMenuItem>` | Individual action item |
28
+ | _(no equivalent)_ | `<DropdownMenuContent>` | Floating panel container |
29
+ | _(no equivalent)_ | `<DropdownMenuGroup>` | Required wrapper around a labelled section |
30
+ | _(no equivalent)_ | `<DropdownMenuLabel>` | Section heading — must be inside `DropdownMenuGroup` |
31
+ | _(no equivalent)_ | `<DropdownMenuSeparator>` | Horizontal rule between sections |
32
+ | _(no equivalent)_ | `<DropdownMenuAddon>` | Trailing right-aligned slot inside an item |
33
+
34
+ ## 2. Verified DS exports (`packages/ds/src/index.ts`)
35
+
36
+ ```
37
+ DropdownMenu, DropdownMenuContent, DropdownMenuGroup,
38
+ DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator,
39
+ DropdownMenuTrigger, DropdownMenuAddon,
40
+ DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent,
41
+ DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem,
42
+ DropdownMenuPortal, DropdownMenuShortcut,
43
+ Button
44
+ ```
45
+
46
+ ## 3. Imports
47
+
48
+ ```tsx
49
+ import {
50
+ DropdownMenu,
51
+ DropdownMenuContent,
52
+ DropdownMenuGroup,
53
+ DropdownMenuItem,
54
+ DropdownMenuLabel,
55
+ DropdownMenuSeparator,
56
+ DropdownMenuTrigger,
57
+ Button,
58
+ } from '@aircall/ds';
59
+ ```
60
+
61
+ For ActionMenu (icon trigger + icon items), also import from `@aircall/react-icons`:
62
+
63
+ ```tsx
64
+ import { MoreVerticalFill, EditPencilFill, DeleteTrashFill } from '@aircall/react-icons';
65
+ ```
66
+
67
+ ## 4. Prop and pattern changes
68
+
69
+ | Tractor | DS | Notes |
70
+ | --- | --- | --- |
71
+ | `<Dropdown label="Options">` | `<DropdownMenuTrigger render={<Button variant="outline" size="lg" />}>Options</DropdownMenuTrigger>` | Trigger button is composed via `render` prop |
72
+ | `<ActionMenu>` | `<DropdownMenuTrigger render={<Button variant="ghost" size="icon" />}><MoreVerticalFill /></DropdownMenuTrigger>` | Icon-only trigger uses `size="icon"` |
73
+ | `<ActionMenuItem variant="critical">` | `<DropdownMenuItem variant="destructive">` | Use `variant` prop, not `className` |
74
+ | `icon={EditOutlined}` on `ActionMenuItem` | Icon as first child of `DropdownMenuItem` | Render icon directly inside the item |
75
+ | `<DropdownSection label="X">` | `<DropdownMenuGroup><DropdownMenuLabel>X</DropdownMenuLabel>…</DropdownMenuGroup>` | Label must be inside a `DropdownMenuGroup` |
76
+
77
+ ## 5. Before / After — plain Dropdown
78
+
79
+ ### Before (Tractor)
80
+
81
+ ```tsx
82
+ import { Dropdown, DropdownItem } from '@aircall/tractor';
83
+
84
+ <Dropdown label="Options">
85
+ <DropdownItem onClick={handleEdit}>Edit</DropdownItem>
86
+ <DropdownItem onClick={handleDelete}>Delete</DropdownItem>
87
+ </Dropdown>
88
+ ```
89
+
90
+ ### After (DS)
91
+
92
+ ```tsx
93
+ import {
94
+ DropdownMenu,
95
+ DropdownMenuContent,
96
+ DropdownMenuItem,
97
+ DropdownMenuTrigger,
98
+ Button,
99
+ } from '@aircall/ds';
100
+
101
+ <DropdownMenu>
102
+ <DropdownMenuTrigger render={<Button variant="outline" size="lg" />}>
103
+ Options
104
+ </DropdownMenuTrigger>
105
+ <DropdownMenuContent>
106
+ <DropdownMenuItem onClick={handleEdit}>Edit</DropdownMenuItem>
107
+ <DropdownMenuItem onClick={handleDelete}>Delete</DropdownMenuItem>
108
+ </DropdownMenuContent>
109
+ </DropdownMenu>
110
+ ```
111
+
112
+ ## 6. Before / After — ActionMenu (icon trigger + icon items)
113
+
114
+ ### Before (Tractor)
115
+
116
+ ```tsx
117
+ import { ActionMenu, ActionMenuItem } from '@aircall/tractor';
118
+
119
+ <ActionMenu>
120
+ <ActionMenuItem icon={EditOutlined} onClick={handleEdit}>Edit</ActionMenuItem>
121
+ <ActionMenuItem icon={DeleteOutlined} variant="critical" onClick={handleDelete}>Delete</ActionMenuItem>
122
+ </ActionMenu>
123
+ ```
124
+
125
+ ### After (DS)
126
+
127
+ ```tsx
128
+ import {
129
+ DropdownMenu,
130
+ DropdownMenuContent,
131
+ DropdownMenuItem,
132
+ DropdownMenuTrigger,
133
+ Button,
134
+ } from '@aircall/ds';
135
+ import { MoreVerticalFill, EditPencilFill, DeleteTrashFill } from '@aircall/react-icons';
136
+
137
+ <DropdownMenu>
138
+ <DropdownMenuTrigger render={<Button variant="ghost" size="icon" />}>
139
+ <MoreVerticalFill className="size-4" />
140
+ </DropdownMenuTrigger>
141
+ <DropdownMenuContent>
142
+ <DropdownMenuItem onClick={handleEdit}>
143
+ <EditPencilFill className="mr-2 size-4" />
144
+ Edit
145
+ </DropdownMenuItem>
146
+ <DropdownMenuItem variant="destructive" onClick={handleDelete}>
147
+ <DeleteTrashFill className="mr-2 size-4" />
148
+ Delete
149
+ </DropdownMenuItem>
150
+ </DropdownMenuContent>
151
+ </DropdownMenu>
152
+ ```
153
+
154
+ ## 7. Sections with labels
155
+
156
+ `DropdownMenuLabel` must be placed inside a `DropdownMenuGroup` alongside its items. A label as a direct child of `DropdownMenuContent` is not accessibility-correct in Base UI.
157
+
158
+ ```tsx
159
+ import {
160
+ DropdownMenu,
161
+ DropdownMenuContent,
162
+ DropdownMenuGroup,
163
+ DropdownMenuItem,
164
+ DropdownMenuLabel,
165
+ DropdownMenuSeparator,
166
+ DropdownMenuTrigger,
167
+ Button,
168
+ } from '@aircall/ds';
169
+
170
+ <DropdownMenu>
171
+ <DropdownMenuTrigger render={<Button variant="outline" size="lg" />}>
172
+ Account
173
+ </DropdownMenuTrigger>
174
+ <DropdownMenuContent>
175
+ <DropdownMenuGroup>
176
+ <DropdownMenuLabel>Account</DropdownMenuLabel>
177
+ <DropdownMenuItem>Profile</DropdownMenuItem>
178
+ <DropdownMenuItem>Settings</DropdownMenuItem>
179
+ </DropdownMenuGroup>
180
+ <DropdownMenuSeparator />
181
+ <DropdownMenuItem>Logout</DropdownMenuItem>
182
+ </DropdownMenuContent>
183
+ </DropdownMenu>
184
+ ```
185
+
186
+ ## 8. Common Mistakes
187
+
188
+ ### Mistake 1 — Using `className="text-destructive"` instead of `variant="destructive"`
189
+
190
+ ```tsx
191
+ // Wrong
192
+ <DropdownMenuItem className="text-destructive" onClick={handleDelete}>
193
+ Delete
194
+ </DropdownMenuItem>
195
+
196
+ // Correct
197
+ <DropdownMenuItem variant="destructive" onClick={handleDelete}>
198
+ Delete
199
+ </DropdownMenuItem>
200
+ ```
201
+
202
+ `DropdownMenuItem` has a first-class `variant` prop (`'default' | 'destructive'`). Using `variant="destructive"` applies the correct destructive text colour, hover background, and dark-mode overrides via `data-[variant=destructive]` selectors. A bare `className="text-destructive"` only sets the text colour and loses the focus-state and dark-mode styling baked into the variant.
203
+ Source: `packages/ds/src/components/dropdown-menu.tsx`
204
+
205
+ ---
206
+
207
+ ### Mistake 2 — Placing `DropdownMenuLabel` as a direct child of `DropdownMenuContent`
208
+
209
+ ```tsx
210
+ // Wrong
211
+ <DropdownMenuContent>
212
+ <DropdownMenuLabel>Account</DropdownMenuLabel>
213
+ <DropdownMenuItem>Profile</DropdownMenuItem>
214
+ </DropdownMenuContent>
215
+
216
+ // Correct
217
+ <DropdownMenuContent>
218
+ <DropdownMenuGroup>
219
+ <DropdownMenuLabel>Account</DropdownMenuLabel>
220
+ <DropdownMenuItem>Profile</DropdownMenuItem>
221
+ </DropdownMenuGroup>
222
+ </DropdownMenuContent>
223
+ ```
224
+
225
+ `DropdownMenuLabel` renders a Base UI `MenuPrimitive.GroupLabel`, which must be a sibling of its items inside a `MenuPrimitive.Group`. Placing it directly in `DropdownMenuContent` breaks the ARIA group association that screen readers depend on.
226
+ Source: `packages/ds/src/components/dropdown-menu.tsx`
227
+
228
+ ---
229
+
230
+ ### Mistake 3 — Omitting the `render` prop on `DropdownMenuTrigger`
231
+
232
+ ```tsx
233
+ // Wrong
234
+ <DropdownMenuTrigger>
235
+ Options
236
+ </DropdownMenuTrigger>
237
+
238
+ // Correct
239
+ <DropdownMenuTrigger render={<Button variant="outline" size="lg" />}>
240
+ Options
241
+ </DropdownMenuTrigger>
242
+ ```
243
+
244
+ Without a `render` prop, `DropdownMenuTrigger` renders a plain unstyled `<button>`. In the DS, trigger appearance is composed by passing a `Button` via the Base UI `render` prop — the button's variant and size control the visual style. This follows the DS-wide `render` prop pattern that replaces the old `asChild` prop.
245
+ Source: `packages/ds/src/components/dropdown-menu.tsx`
246
+
247
+ ---
248
+
249
+ ### Mistake 4 — Passing icon as a prop instead of rendering it as a child
250
+
251
+ ```tsx
252
+ // Wrong
253
+ import { ActionMenu, ActionMenuItem } from '@aircall/tractor';
254
+ <ActionMenuItem icon={EditOutlined}>Edit</ActionMenuItem>
255
+
256
+ // Correct
257
+ import { DropdownMenuItem } from '@aircall/ds';
258
+ import { EditPencilFill } from '@aircall/react-icons';
259
+ <DropdownMenuItem>
260
+ <EditPencilFill className="mr-2 size-4" />
261
+ Edit
262
+ </DropdownMenuItem>
263
+ ```
264
+
265
+ Tractor `ActionMenuItem` accepted an `icon` prop. DS `DropdownMenuItem` has no `icon` prop — the icon is placed as the first child. The `[&_svg:not([class*='size-'])]:size-4` selector on the item auto-sizes icon children that lack an explicit size class.
266
+ Source: `packages/ds/src/components/dropdown-menu.tsx`