@coinbase/cds-mcp-server 8.17.5 → 8.18.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/CHANGELOG.md CHANGED
@@ -8,6 +8,14 @@ All notable changes to this project will be documented in this file.
8
8
 
9
9
  <!-- template-start -->
10
10
 
11
+ ## 8.18.0 ((10/29/2025, 10:09 AM PST))
12
+
13
+ This is an artificial version bump with no new change.
14
+
15
+ ## 8.17.6 ((10/28/2025, 02:28 PM PST))
16
+
17
+ This is an artificial version bump with no new change.
18
+
11
19
  ## 8.17.5 ((10/27/2025, 04:19 PM PST))
12
20
 
13
21
  This is an artificial version bump with no new change.
@@ -114,6 +114,7 @@ Note: The mobile ContentCellFallback uses theme-based line heights and reuses th
114
114
  | Prop | Type | Required | Default | Description |
115
115
  | --- | --- | --- | --- | --- |
116
116
  | `accessory` | `more \| selected \| arrow` | No | `-` | Accessory to display at the end of the cell. |
117
+ | `accessoryNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Custom accessory node rendered at the end of the cell. Takes precedence over accessory. |
117
118
  | `alignContent` | `flex-start \| flex-end \| center \| stretch \| space-between \| space-around \| space-evenly` | No | `-` | - |
118
119
  | `alignItems` | `flex-start \| flex-end \| center \| stretch \| baseline` | No | `-` | - |
119
120
  | `alignSelf` | `auto \| FlexAlignType` | No | `-` | - |
@@ -10,7 +10,17 @@ import { ListCell } from '@coinbase/cds-mobile/cells/ListCell'
10
10
 
11
11
  ## Examples
12
12
 
13
- ### Basic usage
13
+ ### Overview
14
+
15
+ A ListCell row is divided into the following 5 columns:
16
+
17
+ - Media
18
+ - Title & description
19
+ - Intermediary
20
+ - End (detail & subdetail or action)
21
+ - Accessory
22
+
23
+ #### Basic Usage
14
24
 
15
25
  ```tsx
16
26
  <ListCell
@@ -20,10 +30,12 @@ import { ListCell } from '@coinbase/cds-mobile/cells/ListCell'
20
30
  />
21
31
  ```
22
32
 
23
- :::note
33
+ :::tip
24
34
  Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` may be removed in a future major release.
25
35
  :::
26
36
 
37
+ #### Spacing Variant
38
+
27
39
  ```tsx
28
40
  <VStack>
29
41
  {/* Preferred (new design) */}
@@ -33,7 +45,7 @@ Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` m
33
45
  detail="$12,345.00"
34
46
  spacingVariant="condensed"
35
47
  media={<Avatar src={assets.eth.imageUrl} size="m" />}
36
- onClick={console.log}
48
+ onPress={console.log}
37
49
  title="Condensed"
38
50
  variant="positive"
39
51
  />
@@ -44,7 +56,7 @@ Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` m
44
56
  detail="$12,345.00"
45
57
  spacingVariant="compact"
46
58
  media={<Avatar src={assets.eth.imageUrl} size="m" />}
47
- onClick={console.log}
59
+ onPress={console.log}
48
60
  title="Compact (deprecated)"
49
61
  variant="positive"
50
62
  />
@@ -53,90 +65,243 @@ Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` m
53
65
  detail="$12,345.00"
54
66
  spacingVariant="normal"
55
67
  media={<Avatar src={assets.eth.imageUrl} size="m" />}
56
- onClick={console.log}
68
+ onPress={console.log}
57
69
  title="Normal"
58
70
  variant="positive"
59
71
  />
60
72
  </VStack>
61
73
  ```
62
74
 
63
- ### With Media
75
+ ### Media
76
+
77
+ ::::note
78
+ We have deprecated `CellMedia`; pass media directly as shown below.
79
+ ::::
80
+
81
+ #### Leading Icon
64
82
 
65
83
  ```tsx
66
84
  <ListCell
67
85
  spacingVariant="condensed"
68
- title="List Cell with Media"
69
- description="Shows usage with media"
86
+ title="List Cell with Icon"
87
+ description="Shows usage with a leading icon"
70
88
  media={<Icon active name="info" />}
71
89
  />
72
90
  ```
73
91
 
74
- ### With Detail and Subdetail
92
+ #### Leading Avatar
93
+
94
+ ```tsx
95
+ <ListCell
96
+ spacingVariant="condensed"
97
+ title="List Cell with Icon"
98
+ description="Shows usage with a leading icon"
99
+ media={<Avatar src={assets.btc.imageUrl} size="l" />}
100
+ />
101
+ ```
102
+
103
+ ### Title & Description
104
+
105
+ #### Title Line Limits
106
+
107
+ - In condensed spacing (`spacingVariant="condensed"`), the title shows up to two lines by default, regardless of whether a description is present.
108
+ - In normal and compact spacing, the title shows up to two lines when there is no description; if a description is present, the title is limited to one line.
109
+ - Use `disableMultilineTitle` to force the title to one line in all cases.
110
+
111
+ ::::warning
112
+ The `title` and `description` props are rendered inside a CDS `Text` with default fonts and truncation. To render arbitrary React nodes without being wrapped by a `<Text>`, use `titleNode` and `descriptionNode`.
113
+ When using the Node props, you are responsible for styling, layout, and truncation behavior.
114
+ ::::
115
+
116
+ #### Custom Title/Description via Node Props
117
+
118
+ ```tsx
119
+ <ListCell
120
+ spacingVariant="condensed"
121
+ media={<Avatar src={assets.eth.imageUrl} size="m" />}
122
+ titleNode={
123
+ <HStack gap={1} alignItems="center">
124
+ <Icon name="checkmark" />
125
+ <span>Verified account</span>
126
+ </HStack>
127
+ }
128
+ descriptionNode={
129
+ <HStack gap={1} alignItems="center">
130
+ <span>Composed description with any React nodes</span>
131
+ <Icon name="info" />
132
+ </HStack>
133
+ }
134
+ />
135
+ ```
136
+
137
+ #### Multiline Description
138
+
139
+ ```tsx
140
+ <ListCell
141
+ spacingVariant="condensed"
142
+ title="Multiline Description"
143
+ description="This is a longer description that demonstrates how the text wraps when the multiline prop is enabled. It can span multiple lines without truncating."
144
+ multiline
145
+ />
146
+ ```
147
+
148
+ ### Intermediary
149
+
150
+ ```tsx
151
+ function Intermediary() {
152
+ const dimensions = { width: 62, height: 18 };
153
+ const sparklineData = prices
154
+ .map((price) => parseFloat(price))
155
+ .filter((price, index) => index % 10 === 0);
156
+ const referenceY = sparklineData[Math.floor(sparklineData.length / 3)];
157
+
158
+ const CompactChart = memo(
159
+ ({ data, color = 'var(--color-fgPositive)', showArea = false, referenceY }) => (
160
+ <Box style={{ padding: 1 }}>
161
+ <LineChart
162
+ {...dimensions}
163
+ enableScrubbing={false}
164
+ overflow="visible"
165
+ inset={0}
166
+ showArea={showArea}
167
+ series={[
168
+ {
169
+ id: 'series',
170
+ data,
171
+ color,
172
+ },
173
+ ]}
174
+ >
175
+ <ReferenceLine dataY={referenceY} />
176
+ </LineChart>
177
+ </Box>
178
+ ),
179
+ );
180
+
181
+ return (
182
+ <ListCell
183
+ media={<Avatar src={assets.btc.imageUrl} />}
184
+ spacingVariant="condensed"
185
+ title="Bitcoin"
186
+ description="BTC"
187
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
188
+ detail="$334,239.03"
189
+ subdetail="+4.06%"
190
+ priority="start"
191
+ variant="positive"
192
+ />
193
+ );
194
+ }
195
+ ```
196
+
197
+ ### End
198
+
199
+ #### Detail and Subdetail
75
200
 
76
201
  ```tsx
77
202
  <ListCell
78
203
  spacingVariant="condensed"
79
204
  title="List Cell with Details"
80
205
  description="Shows usage with detail and subdetail"
81
- detail="$1,234.56"
82
- subdetail="+2.5%"
83
- variant="positive"
206
+ detail="Primary detail"
207
+ subdetail="Secondary detail"
84
208
  />
85
209
  ```
86
210
 
87
- ### With Accessory
211
+ ::::warning
212
+ Like `title` and `description`, `detail` and `subdetail` props are also rendered inside a CDS `Text` with default fonts. To render arbitrary React nodes without being wrapped by a `<Text>`, use `detailNode` and `subdetailNode`.
213
+ ::::
214
+
215
+ #### Custom Detail/Subdetail via Node Props
88
216
 
89
217
  ```tsx
90
218
  <ListCell
91
219
  spacingVariant="condensed"
92
- title="List Cell with Accessory"
93
- description="Shows usage with accessory"
94
- accessory="arrow"
220
+ media={<Avatar src={assets.btc.imageUrl} size="m" />}
221
+ title="Custom end content"
222
+ description="Detail and subdetail rendered with custom nodes"
223
+ detailNode={
224
+ <HStack gap={2} alignItems="center" justifyContent="flex-end">
225
+ <Icon name="info" />
226
+ <Text as="div" font="body" overflow="truncate" textAlign="end">
227
+ $12,345.00
228
+ </Text>
229
+ </HStack>
230
+ }
231
+ subdetailNode={
232
+ <HStack gap={1} alignItems="center" justifyContent="flex-end">
233
+ <Icon name="info" />
234
+ <Text as="div" color="fgPositive" font="label2" overflow="truncate" textAlign="end">
235
+ +5.43%
236
+ </Text>
237
+ </HStack>
238
+ }
95
239
  />
96
240
  ```
97
241
 
98
- ### Interactive Cell
242
+ #### End Action
243
+
244
+ When you pass the `end` prop, it overrides the `detail`/`subdetail`/`detailNode`/`subdetailNode`.
245
+
246
+ ```tsx
247
+ <ListCell
248
+ spacingVariant="condensed"
249
+ title="End action"
250
+ detail="This is overridden and won't show up"
251
+ subdetail="This is overridden and won't show up"
252
+ detailNode="This is overridden and won't show up"
253
+ subdetailNode="This is overridden and won't show up"
254
+ end={
255
+ <Button
256
+ compact
257
+ onPress={() => {
258
+ alert('Action clicked');
259
+ }}
260
+ >
261
+ Action
262
+ </Button>
263
+ }
264
+ />
265
+ ```
266
+
267
+ ### Accessory
268
+
269
+ #### Interactive Cell with Accessory
99
270
 
100
271
  ```tsx
101
272
  <ListCell
102
273
  spacingVariant="condensed"
103
274
  title="Interactive List Cell"
104
- description="Tap to interact"
105
- media={<Icon active name="info" />}
275
+ description="Click or tap to interact"
106
276
  accessory="arrow"
107
- onPress={() => alert('Cell pressed!')}
277
+ onPress={() => alert('Cell clicked!')}
108
278
  />
109
279
  ```
110
280
 
111
- ### With Helper Text
281
+ #### Custom Accessory via Node Prop
112
282
 
113
283
  ```tsx
114
- <VStack gap={3}>
115
- <ListCell
116
- spacingVariant="condensed"
117
- title="List Cell with Helper Text"
118
- description="Shows usage with helper text below the cell"
119
- helperText={<CellHelperText>This is a default helper message.</CellHelperText>}
120
- media={<Avatar src={assets.btc.imageUrl} />}
121
- end={<Button compact>Action</Button>}
122
- />
123
- <ListCell
124
- spacingVariant="condensed"
125
- title="List Cell with Warning"
126
- description="Shows usage with a warning message"
127
- helperText={<CellHelperText variant="warning">This is a warning message.</CellHelperText>}
128
- media={<Avatar src={assets.btc.imageUrl} />}
129
- end={<Button compact>Action</Button>}
130
- />
131
- <ListCell
132
- spacingVariant="condensed"
133
- title="List Cell with Error"
134
- description="Shows usage with an error message"
135
- helperText={<CellHelperText variant="error">This is an error message.</CellHelperText>}
136
- media={<Avatar src={assets.btc.imageUrl} />}
137
- end={<Button compact>Action</Button>}
138
- />
139
- </VStack>
284
+ <ListCell
285
+ spacingVariant="condensed"
286
+ title="Accessory Node"
287
+ description="Custom accessory with its own onPress"
288
+ media={<Avatar src={assets.eth.imageUrl} size="m" />}
289
+ end={
290
+ <Button
291
+ compact
292
+ onPress={() => {
293
+ alert('Action clicked');
294
+ }}
295
+ >
296
+ Action
297
+ </Button>
298
+ }
299
+ accessoryNode={
300
+ <Tooltip content="question">
301
+ <Icon size="s" name="questionMark" compact variant="secondary" />
302
+ </Tooltip>
303
+ }
304
+ />
140
305
  ```
141
306
 
142
307
  ### Accessibility Label
@@ -149,55 +314,77 @@ The accessibility props are only applied when the `<ListCell>` has a value for t
149
314
  accessibilityLabel="Accessibility label. Describes content for entire list cell. Applied when onPress prop has a value"
150
315
  intermediary={<Icon name="chartLine" />}
151
316
  media={<Avatar src={assets.btc.imageUrl} />}
152
- onPress={() => console.log('List cell pressed')}
317
+ onPress={() => window.alert('ListCell clicked!')}
153
318
  title="BTC"
319
+ spacingVariant="condensed"
154
320
  />
155
321
 
156
322
  <ListCell
157
323
  intermediary={<Icon accessibilityLabel="Chart icon" name="chartLine" />}
158
324
  media={<Avatar accessibilityLabel="Bitcoin" src={assets.btc.imageUrl} />}
159
325
  title="BTC"
326
+ spacingVariant="condensed"
160
327
  />
161
328
  </VStack>
162
329
  ```
163
330
 
164
- ### Compact Mode
165
-
166
- ```tsx
167
- <ListCell
168
- spacingVariant="compact"
169
- title="Compact List Cell"
170
- description="Shows compact variant"
171
- media={<Icon active name="info" />}
172
- />
173
- ```
174
-
175
- ### Selected State
331
+ ### Helper text
176
332
 
177
333
  ```tsx
178
- <ListCell
179
- spacingVariant="condensed"
180
- selected
181
- title="Selected List Cell"
182
- description="Shows selected state"
183
- media={<Icon active name="info" />}
184
- />
334
+ <VStack gap={3}>
335
+ <ListCell
336
+ spacingVariant="condensed"
337
+ title="List Cell with Helper Text"
338
+ description="Shows usage with helper text below the cell"
339
+ helperText={
340
+ <CellHelperText font="label2" paddingStart={6}>
341
+ This is a default helper message.
342
+ </CellHelperText>
343
+ }
344
+ media={<Avatar src={assets.btc.imageUrl} />}
345
+ end={<Button compact>Action</Button>}
346
+ />
347
+ <ListCell
348
+ spacingVariant="condensed"
349
+ title="List Cell with Warning"
350
+ description="Shows usage with a warning message"
351
+ helperText={
352
+ <CellHelperText font="label2" variant="warning" paddingStart={6}>
353
+ This is a warning message.
354
+ </CellHelperText>
355
+ }
356
+ media={<Avatar src={assets.btc.imageUrl} />}
357
+ end={<Button compact>Action</Button>}
358
+ />
359
+ <ListCell
360
+ spacingVariant="condensed"
361
+ title="List Cell with Error"
362
+ description="Shows usage with an error message"
363
+ helperText={
364
+ <CellHelperText font="label2" variant="error" paddingStart={6}>
365
+ This is an error message.
366
+ </CellHelperText>
367
+ }
368
+ media={<Avatar src={assets.btc.imageUrl} />}
369
+ end={<Button compact>Action</Button>}
370
+ />
371
+ </VStack>
185
372
  ```
186
373
 
187
374
  ### Loading States
188
375
 
189
- The ListCellFallback component provides loading state representations of ListCell. It uses placeholder rectangles to indicate where content will appear, creating a smooth loading experience. The mobile version leverages the theme system for consistent line heights and reuses the ListCell component structure for layout consistency.
376
+ The ListCellFallback component provides loading state representations of ListCell. It uses placeholder rectangles to indicate where content will appear, creating a smooth loading experience. The web version uses percentage-based widths and custom layouts to match the ListCell's four-column structure.
190
377
 
191
378
  ```tsx
192
379
  <VStack gap={3}>
193
380
  {/* Basic loading state */}
194
- <ListCellFallback spacingVariant="condensed" title description />
381
+ <ListCellFallback title description spacingVariant="condensed" />
195
382
 
196
383
  {/* Loading state with media */}
197
- <ListCellFallback spacingVariant="condensed" title description media="icon" />
384
+ <ListCellFallback title description media="icon" spacingVariant="condensed" />
198
385
 
199
386
  {/* Loading state with details */}
200
- <ListCellFallback spacingVariant="condensed" title description detail subdetail />
387
+ <ListCellFallback title description detail subdetail spacingVariant="condensed" />
201
388
 
202
389
  {/* Full loading state with custom widths */}
203
390
  <ListCellFallback
@@ -206,17 +393,96 @@ The ListCellFallback component provides loading state representations of ListCel
206
393
  description
207
394
  detail
208
395
  subdetail
396
+ helperText
209
397
  media="icon"
210
- compact
211
- rectWidthVariant={1} // Creates a deterministic variant of the loading state
398
+ rectWidthVariant={2} // Creates a deterministic variant of the loading state
212
399
  disableRandomRectWidth
400
+ styles={{ helperText: { paddingLeft: 48 } }}
213
401
  />
214
402
  </VStack>
215
403
  ```
216
404
 
217
- :::note
218
- The mobile ListCellFallback uses theme-based line heights and reuses the ListCell component structure, resulting in a more consistent loading state appearance. The compact prop is also supported for loading states.
219
- :::
405
+ ### Priority
406
+
407
+ The priority prop controls which parts of the cell are protected from shrinking and truncation when horizontal space is limited. It accepts start, middle, and end as a string or an array of strings.
408
+
409
+ ```tsx
410
+ function PriorityContent() {
411
+ const dimensions = { width: 62, height: 18 };
412
+ const sparklineData = prices
413
+ .map((price) => parseFloat(price))
414
+ .filter((price, index) => index % 10 === 0);
415
+ const referenceY = sparklineData[Math.floor(sparklineData.length / 3)];
416
+
417
+ const CompactChart = memo(
418
+ ({ data, color = 'var(--color-fgPositive)', showArea = false, referenceY }) => (
419
+ <Box style={{ padding: 1 }}>
420
+ <LineChart
421
+ {...dimensions}
422
+ enableScrubbing={false}
423
+ overflow="visible"
424
+ inset={0}
425
+ showArea={showArea}
426
+ series={[
427
+ {
428
+ id: 'series',
429
+ data,
430
+ color,
431
+ },
432
+ ]}
433
+ >
434
+ <ReferenceLine dataY={referenceY} />
435
+ </LineChart>
436
+ </Box>
437
+ ),
438
+ );
439
+
440
+ return (
441
+ <VStack gap={3} style={{ width: '100%', maxWidth: 320, overflow: 'hidden' }}>
442
+ <ListCell
443
+ spacingVariant="condensed"
444
+ title="Asset with a really long name"
445
+ description="Some description of the asset"
446
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
447
+ detail="$334,239.03"
448
+ subdetail="+4.06%"
449
+ priority="start"
450
+ variant="positive"
451
+ />
452
+ <ListCell
453
+ spacingVariant="condensed"
454
+ title="Asset with a really long name"
455
+ description="Some description of the asset"
456
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
457
+ detail="$334,239.03"
458
+ subdetail="+4.06%"
459
+ priority="middle"
460
+ variant="positive"
461
+ />
462
+ <ListCell
463
+ spacingVariant="condensed"
464
+ title="Asset with a really long name"
465
+ description="Some description of the asset"
466
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
467
+ detail="$334,239.03"
468
+ subdetail="+4.06%"
469
+ priority="end"
470
+ variant="positive"
471
+ />
472
+ <ListCell
473
+ spacingVariant="condensed"
474
+ title="Asset with a really long name"
475
+ description="Some description of the asset"
476
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
477
+ detail="$334,239.03"
478
+ subdetail="+4.06%"
479
+ priority={['start', 'middle', 'end']}
480
+ variant="warning"
481
+ />
482
+ </VStack>
483
+ );
484
+ }
485
+ ```
220
486
 
221
487
  ### Anatomy
222
488
 
@@ -290,6 +556,7 @@ Mapping to `styles` / `classNames` keys:
290
556
  | Prop | Type | Required | Default | Description |
291
557
  | --- | --- | --- | --- | --- |
292
558
  | `accessory` | `more \| selected \| arrow` | No | `-` | Accessory to display at the end of the cell. |
559
+ | `accessoryNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Custom accessory node rendered at the end of the cell. Takes precedence over accessory. |
293
560
  | `action` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | - |
294
561
  | `adjustsFontSizeToFit` | `boolean` | No | `-` | Specifies whether font should be scaled down automatically to fit given style constraints. |
295
562
  | `alignContent` | `flex-start \| flex-end \| center \| stretch \| space-between \| space-around \| space-evenly` | No | `-` | - |
@@ -322,8 +589,10 @@ Mapping to `styles` / `classNames` keys:
322
589
  | `columnGap` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `-` | - |
323
590
  | `compact` | `boolean` | No | `-` | - |
324
591
  | `dangerouslySetBackground` | `string` | No | `-` | - |
325
- | `description` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Description of content. Max 1 line (with title) or 2 lines (without), otherwise will truncate. |
326
- | `detail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Label and/or extra detail. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. |
592
+ | `description` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Description of content. Max 1 line (with title) or 2 lines (without), otherwise will truncate. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use descriptionNode. |
593
+ | `descriptionNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to render description. Takes precedence over description. |
594
+ | `detail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Label and/or extra detail. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use detailNode. |
595
+ | `detailNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to render label and/or extra detail. Takes precedence over detail. |
327
596
  | `detailWidth` | `string \| number` | No | `-` | - |
328
597
  | `disableMultilineTitle` | `boolean` | No | `false When there is no description the title will take up two lines by default. When this is set to true multiline title behavior is overwritten, and regardless of description text state the title will take up a single line truncating with ellipses.` | - |
329
598
  | `disableSelectionAccessory` | `boolean` | No | `-` | Disable the default accessory that is displayed when the cell is selected. If accessory is provided, that will continue to be displayed, otherwise no accessory will be displayed when the cell is selected. |
@@ -394,14 +663,16 @@ Mapping to `styles` / `classNames` keys:
394
663
  | `spacingVariant` | `compact \| normal \| condensed` | No | `'normal'` | Spacing variant configuration. Deprecated value: compact. Prefer condensed. When spacingVariant=normal: 1. min-height is 80px 2. padding is var(--space-2) var(--space-3) 3. border-radius is var(--borderRadius-200) 4. when there is a description, titles numberOfLines={1} otherwise titles numberOfLines={2} 5. description and subdetail have font body When spacingVariant=compact: 1. same as spacingVariant=normal, except min-height is 40px When spacingVariant=condensed: 1. min-height is undefined 2. padding is var(--space-1) var(--space-2) 3. border-radius is --borderRadius-0 4. titles numberOfLines={2} 5. description and subdetail have font label2 |
395
664
  | `style` | `false \| RegisteredStyle<ViewStyle> \| Value \| AnimatedInterpolation<string \| number> \| WithAnimatedObject<ViewStyle> \| WithAnimatedArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle> \| RecursiveArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>> \| readonly (Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>)[]> \| null` | No | `-` | - |
396
665
  | `styles` | `({ root?: StyleProp<ViewStyle>; contentContainer?: StyleProp<ViewStyle>; topContent?: StyleProp<ViewStyle>; bottomContent?: StyleProp<ViewStyle>; pressable?: StyleProp<ViewStyle>; media?: StyleProp<ViewStyle>; intermediary?: StyleProp<ViewStyle>; end?: StyleProp<ViewStyle>; accessory?: StyleProp<ViewStyle>; } & { root?: StyleProp<ViewStyle>; media?: StyleProp<ViewStyle>; intermediary?: StyleProp<ViewStyle>; end?: StyleProp<ViewStyle>; accessory?: StyleProp<ViewStyle>; contentContainer?: StyleProp<ViewStyle>; pressable?: StyleProp<ViewStyle>; mainContent?: StyleProp<ViewStyle>; helperText?: StyleProp<ViewStyle>; title?: StyleProp<TextStyle>; description?: StyleProp<TextStyle>; })` | No | `-` | Styles for the components |
397
- | `subdetail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Subdetail providing more information. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. |
666
+ | `subdetail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Subdetail providing more information. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use subdetailNode. |
398
667
  | `subdetailFont` | `inherit \| FontFamily` | No | `-` | Font to apply to the subdetail text. |
668
+ | `subdetailNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to render subdetail. Takes precedence over subdetail. |
399
669
  | `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID Used to locate this element in unit and end-to-end tests. Used to locate this view in end-to-end tests. |
400
670
  | `textAlign` | `left \| right \| auto \| center \| justify` | No | `-` | - |
401
671
  | `textDecorationLine` | `none \| underline \| line-through \| underline line-through` | No | `-` | - |
402
672
  | `textDecorationStyle` | `solid \| dotted \| dashed \| double` | No | `-` | - |
403
673
  | `textTransform` | `none \| capitalize \| uppercase \| lowercase` | No | `-` | - |
404
- | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Title of content. Max 1 line (with description) or 2 lines (without), otherwise will truncate. |
674
+ | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Title of content. Max 1 line (with description) or 2 lines (without), otherwise will truncate. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use titleNode. |
675
+ | `titleNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to render title. Takes precedence over title. |
405
676
  | `top` | `string \| number` | No | `-` | - |
406
677
  | `transform` | `string \| (({ scaleX: AnimatableNumericValue; } & { scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ scaleY: AnimatableNumericValue; } & { scaleX?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ translateX: AnimatableNumericValue \| ${number}%; } & { scaleX?: undefined; scaleY?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ translateY: AnimatableNumericValue \| ${number}%; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ perspective: AnimatableNumericValue; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ rotate: AnimatableStringValue; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ rotateX: AnimatableStringValue; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ rotateY: AnimatableStringValue; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ rotateZ: AnimatableStringValue; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ scale: AnimatableNumericValue; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; skewX?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ skewX: AnimatableStringValue; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewY?: undefined; matrix?: undefined; }) \| ({ skewY: AnimatableStringValue; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; matrix?: undefined; }) \| ({ matrix: AnimatableNumericValue[]; } & { scaleX?: undefined; scaleY?: undefined; translateX?: undefined; translateY?: undefined; perspective?: undefined; rotate?: undefined; rotateX?: undefined; rotateY?: undefined; rotateZ?: undefined; scale?: undefined; skewX?: undefined; skewY?: undefined; }))[]` | No | `-` | - |
407
678
  | `userSelect` | `none \| auto \| contain \| text \| all` | No | `-` | - |
@@ -62,9 +62,10 @@ const SelectMobile = () => {
62
62
  | --- | --- | --- | --- | --- |
63
63
  | `value` | `string` | Yes | `-` | Unique identifier for each option |
64
64
  | `accessory` | `ReactElement<CellAccessoryProps, string \| JSXElementConstructor<any>>` | No | `-` | Accessory element rendered at the end of the cell (e.g., chevron). |
65
+ | `accessoryNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Custom accessory node rendered at the end of the cell. Takes precedence over accessory. |
65
66
  | `borderRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
66
67
  | `bottomContent` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | The content to display below the main cell content. |
67
- | `description` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Description of content. Max 1 line (with title) or 2 lines (without), otherwise will truncate. |
68
+ | `description` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Description of content. Max 1 line (with title) or 2 lines (without), otherwise will truncate. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use descriptionNode. |
68
69
  | `detail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | - |
69
70
  | `detailWidth` | `string \| number` | No | `-` | - |
70
71
  | `disabled` | `boolean` | No | `-` | Is the cell disabled? Will apply opacity and disable interaction. |
@@ -79,6 +80,6 @@ const SelectMobile = () => {
79
80
  | `priority` | `end \| start \| middle \| (end \| start \| middle)[]` | No | `-` | Which piece of content has the highest priority in regards to text truncation, growing, and shrinking. |
80
81
  | `styles` | `{ root?: StyleProp<ViewStyle>; contentContainer?: StyleProp<ViewStyle>; topContent?: StyleProp<ViewStyle>; bottomContent?: StyleProp<ViewStyle>; pressable?: StyleProp<ViewStyle>; media?: StyleProp<ViewStyle>; intermediary?: StyleProp<ViewStyle>; end?: StyleProp<ViewStyle>; accessory?: StyleProp<ViewStyle>; }` | No | `-` | Styles for the components |
81
82
  | `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
82
- | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Title of content. Max 1 line (with description) or 2 lines (without), otherwise will truncate. |
83
+ | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Title of content. Max 1 line (with description) or 2 lines (without), otherwise will truncate. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use titleNode. |
83
84
 
84
85
 
@@ -97,6 +97,7 @@ The ContentCellFallback component provides loading state representations of Cont
97
97
  | Prop | Type | Required | Default | Description |
98
98
  | --- | --- | --- | --- | --- |
99
99
  | `accessory` | `more \| selected \| arrow` | No | `-` | Accessory to display at the end of the cell. |
100
+ | `accessoryNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Custom accessory node rendered at the end of the cell. Takes precedence over accessory. |
100
101
  | `alignContent` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| space-between \| space-around \| space-evenly>` | No | `-` | - |
101
102
  | `alignItems` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| self-start \| self-end \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
102
103
  | `alignSelf` | `ResponsiveProp<center \| normal \| auto \| start \| end \| flex-start \| flex-end \| self-start \| self-end \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
@@ -10,6 +10,8 @@ import { ListCell } from '@coinbase/cds-web/cells/ListCell'
10
10
 
11
11
  ## Examples
12
12
 
13
+ ### Overview
14
+
13
15
  A ListCell row is divided into the following 5 columns:
14
16
 
15
17
  - Media
@@ -18,13 +20,7 @@ A ListCell row is divided into the following 5 columns:
18
20
  - End (detail & subdetail or action)
19
21
  - Accessory
20
22
 
21
- :::warning
22
-
23
- The `title`, `description`, `detail` and `subdetail` props are only intended to accept a string or `Text` component. Other use cases, while allowed, are not supported and may result in unexpected behavior.
24
-
25
- :::
26
-
27
- ### Basic usage
23
+ #### Basic Usage
28
24
 
29
25
  ```tsx live
30
26
  <ListCell
@@ -34,10 +30,12 @@ The `title`, `description`, `detail` and `subdetail` props are only intended to
34
30
  />
35
31
  ```
36
32
 
37
- :::note
33
+ :::tip
38
34
  Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` may be removed in a future major release.
39
35
  :::
40
36
 
37
+ #### Spacing Variant
38
+
41
39
  ```tsx live
42
40
  <VStack>
43
41
  {/* Preferred (new design) */}
@@ -74,7 +72,13 @@ Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` m
74
72
  </VStack>
75
73
  ```
76
74
 
77
- ### With Leading Icon
75
+ ### Media
76
+
77
+ ::::note
78
+ We have deprecated `CellMedia`; pass media directly as shown below.
79
+ ::::
80
+
81
+ #### Leading Icon
78
82
 
79
83
  ```tsx live
80
84
  <ListCell
@@ -85,7 +89,114 @@ Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` m
85
89
  />
86
90
  ```
87
91
 
88
- ### With Detail and Subdetail
92
+ #### Leading Avatar
93
+
94
+ ```tsx live
95
+ <ListCell
96
+ spacingVariant="condensed"
97
+ title="List Cell with Icon"
98
+ description="Shows usage with a leading icon"
99
+ media={<Avatar src={assets.btc.imageUrl} size="l" />}
100
+ />
101
+ ```
102
+
103
+ ### Title & Description
104
+
105
+ #### Title Line Limits
106
+
107
+ - In condensed spacing (`spacingVariant="condensed"`), the title shows up to two lines by default, regardless of whether a description is present.
108
+ - In normal and compact spacing, the title shows up to two lines when there is no description; if a description is present, the title is limited to one line.
109
+ - Use `disableMultilineTitle` to force the title to one line in all cases.
110
+
111
+ ::::warning
112
+ The `title` and `description` props are rendered inside a CDS `Text` with default fonts and truncation. To render arbitrary React nodes without being wrapped by a `<Text>`, use `titleNode` and `descriptionNode`.
113
+ When using the Node props, you are responsible for styling, layout, and truncation behavior.
114
+ ::::
115
+
116
+ #### Custom Title/Description via Node Props
117
+
118
+ ```tsx live
119
+ <ListCell
120
+ spacingVariant="condensed"
121
+ media={<Avatar src={assets.eth.imageUrl} size="m" />}
122
+ titleNode={
123
+ <HStack gap={1} alignItems="center">
124
+ <Icon name="checkmark" />
125
+ <span>Verified account</span>
126
+ </HStack>
127
+ }
128
+ descriptionNode={
129
+ <HStack gap={1} alignItems="center">
130
+ <span>Composed description with any React nodes</span>
131
+ <Icon name="info" />
132
+ </HStack>
133
+ }
134
+ />
135
+ ```
136
+
137
+ #### Multiline Description
138
+
139
+ ```tsx live
140
+ <ListCell
141
+ spacingVariant="condensed"
142
+ title="Multiline Description"
143
+ description="This is a longer description that demonstrates how the text wraps when the multiline prop is enabled. It can span multiple lines without truncating."
144
+ multiline
145
+ />
146
+ ```
147
+
148
+ ### Intermediary
149
+
150
+ ```tsx live
151
+ function Intermediary() {
152
+ const dimensions = { width: 62, height: 18 };
153
+ const sparklineData = prices
154
+ .map((price) => parseFloat(price))
155
+ .filter((price, index) => index % 10 === 0);
156
+ const referenceY = sparklineData[Math.floor(sparklineData.length / 3)];
157
+
158
+ const CompactChart = memo(
159
+ ({ data, color = 'var(--color-fgPositive)', showArea = false, referenceY }) => (
160
+ <Box style={{ padding: 1 }}>
161
+ <LineChart
162
+ {...dimensions}
163
+ enableScrubbing={false}
164
+ overflow="visible"
165
+ inset={0}
166
+ showArea={showArea}
167
+ series={[
168
+ {
169
+ id: 'series',
170
+ data,
171
+ color,
172
+ },
173
+ ]}
174
+ >
175
+ <ReferenceLine dataY={referenceY} />
176
+ </LineChart>
177
+ </Box>
178
+ ),
179
+ );
180
+
181
+ return (
182
+ <ListCell
183
+ media={<Avatar src={assets.btc.imageUrl} />}
184
+ spacingVariant="condensed"
185
+ title="Bitcoin"
186
+ description="BTC"
187
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
188
+ detail="$334,239.03"
189
+ subdetail="+4.06%"
190
+ priority="start"
191
+ variant="positive"
192
+ />
193
+ );
194
+ }
195
+ ```
196
+
197
+ ### End
198
+
199
+ #### Detail and Subdetail
89
200
 
90
201
  ```tsx live
91
202
  <ListCell
@@ -97,7 +208,65 @@ Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` m
97
208
  />
98
209
  ```
99
210
 
100
- ### Interactive Cell with Accessory
211
+ ::::warning
212
+ Like `title` and `description`, `detail` and `subdetail` props are also rendered inside a CDS `Text` with default fonts. To render arbitrary React nodes without being wrapped by a `<Text>`, use `detailNode` and `subdetailNode`.
213
+ ::::
214
+
215
+ #### Custom Detail/Subdetail via Node Props
216
+
217
+ ```tsx live
218
+ <ListCell
219
+ spacingVariant="condensed"
220
+ media={<Avatar src={assets.btc.imageUrl} size="m" />}
221
+ title="Custom end content"
222
+ description="Detail and subdetail rendered with custom nodes"
223
+ detailNode={
224
+ <HStack gap={2} alignItems="center" justifyContent="flex-end">
225
+ <Icon name="info" />
226
+ <Text as="div" font="body" overflow="truncate" textAlign="end">
227
+ $12,345.00
228
+ </Text>
229
+ </HStack>
230
+ }
231
+ subdetailNode={
232
+ <HStack gap={1} alignItems="center" justifyContent="flex-end">
233
+ <Icon name="info" />
234
+ <Text as="div" color="fgPositive" font="label2" overflow="truncate" textAlign="end">
235
+ +5.43%
236
+ </Text>
237
+ </HStack>
238
+ }
239
+ />
240
+ ```
241
+
242
+ #### End Action
243
+
244
+ When you pass the `end` prop, it overrides the `detail`/`subdetail`/`detailNode`/`subdetailNode`.
245
+
246
+ ```tsx live
247
+ <ListCell
248
+ spacingVariant="condensed"
249
+ title="End action"
250
+ detail="This is overridden and won't show up"
251
+ subdetail="This is overridden and won't show up"
252
+ detailNode="This is overridden and won't show up"
253
+ subdetailNode="This is overridden and won't show up"
254
+ end={
255
+ <Button
256
+ compact
257
+ onClick={() => {
258
+ alert('Action clicked');
259
+ }}
260
+ >
261
+ Action
262
+ </Button>
263
+ }
264
+ />
265
+ ```
266
+
267
+ ### Accessory
268
+
269
+ #### Interactive Cell with Accessory
101
270
 
102
271
  ```tsx live
103
272
  <ListCell
@@ -109,7 +278,57 @@ Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` m
109
278
  />
110
279
  ```
111
280
 
112
- ### With Helper Text
281
+ #### Custom Accessory via Node Prop
282
+
283
+ ```tsx live
284
+ <ListCell
285
+ spacingVariant="condensed"
286
+ title="Accessory Node"
287
+ description="Custom accessory with its own onClick"
288
+ media={<Avatar src={assets.eth.imageUrl} size="m" />}
289
+ end={
290
+ <Button
291
+ compact
292
+ onClick={() => {
293
+ alert('Action clicked');
294
+ }}
295
+ >
296
+ Action
297
+ </Button>
298
+ }
299
+ accessoryNode={
300
+ <Tooltip content="question">
301
+ <Icon size="s" name="questionMark" compact variant="secondary" />
302
+ </Tooltip>
303
+ }
304
+ />
305
+ ```
306
+
307
+ ### Accessibility Label
308
+
309
+ The accessibility props are only applied when the `<ListCell>` has a value for the `onClick` prop. Otherwise, content passed into the `<ListCell>` must use accessibility props and attributes as needed.
310
+
311
+ ```tsx live
312
+ <VStack gap={1}>
313
+ <ListCell
314
+ accessibilityLabel="Accessibility label. Describes content for entire list cell. Applied when onClick prop has a value"
315
+ intermediary={<Icon name="chartLine" />}
316
+ media={<Avatar src={assets.btc.imageUrl} />}
317
+ onClick={() => window.alert('ListCell clicked!')}
318
+ title="BTC"
319
+ spacingVariant="condensed"
320
+ />
321
+
322
+ <ListCell
323
+ intermediary={<Icon accessibilityLabel="Chart icon" name="chartLine" />}
324
+ media={<Avatar accessibilityLabel="Bitcoin" src={assets.btc.imageUrl} />}
325
+ title="BTC"
326
+ spacingVariant="condensed"
327
+ />
328
+ </VStack>
329
+ ```
330
+
331
+ ### Helper text
113
332
 
114
333
  ```tsx live
115
334
  <VStack gap={3}>
@@ -152,39 +371,6 @@ Prefer `spacingVariant="condensed"` for the new ListCell design. The `compact` m
152
371
  </VStack>
153
372
  ```
154
373
 
155
- ### Accessibility Label
156
-
157
- The accessibility props are only applied when the `<ListCell>` has a value for the `onClick` prop. Otherwise, content passed into the `<ListCell>` must use accessibility props and attributes as needed.
158
-
159
- ```tsx live
160
- <VStack gap={1}>
161
- <ListCell
162
- accessibilityLabel="Accessibility label. Describes content for entire list cell. Applied when onClick prop has a value"
163
- intermediary={<Icon name="chartLine" />}
164
- media={<Avatar src={assets.btc.imageUrl} />}
165
- onClick={() => window.alert('ListCell clicked!')}
166
- title="BTC"
167
- />
168
-
169
- <ListCell
170
- intermediary={<Icon accessibilityLabel="Chart icon" name="chartLine" />}
171
- media={<Avatar accessibilityLabel="Bitcoin" src={assets.btc.imageUrl} />}
172
- title="BTC"
173
- />
174
- </VStack>
175
- ```
176
-
177
- ### Multiline Description
178
-
179
- ```tsx live
180
- <ListCell
181
- spacingVariant="condensed"
182
- title="Multiline Description"
183
- description="This is a longer description that demonstrates how the text wraps when the multiline prop is enabled. It can span multiple lines without truncating."
184
- multiline
185
- />
186
- ```
187
-
188
374
  ### Loading States
189
375
 
190
376
  The ListCellFallback component provides loading state representations of ListCell. It uses placeholder rectangles to indicate where content will appear, creating a smooth loading experience. The web version uses percentage-based widths and custom layouts to match the ListCell's four-column structure.
@@ -216,6 +402,88 @@ The ListCellFallback component provides loading state representations of ListCel
216
402
  </VStack>
217
403
  ```
218
404
 
405
+ ### Priority
406
+
407
+ The priority prop controls which parts of the cell are protected from shrinking and truncation when horizontal space is limited. It accepts start, middle, and end as a string or an array of strings.
408
+
409
+ ```tsx live
410
+ function PriorityContent() {
411
+ const dimensions = { width: 62, height: 18 };
412
+ const sparklineData = prices
413
+ .map((price) => parseFloat(price))
414
+ .filter((price, index) => index % 10 === 0);
415
+ const referenceY = sparklineData[Math.floor(sparklineData.length / 3)];
416
+
417
+ const CompactChart = memo(
418
+ ({ data, color = 'var(--color-fgPositive)', showArea = false, referenceY }) => (
419
+ <Box style={{ padding: 1 }}>
420
+ <LineChart
421
+ {...dimensions}
422
+ enableScrubbing={false}
423
+ overflow="visible"
424
+ inset={0}
425
+ showArea={showArea}
426
+ series={[
427
+ {
428
+ id: 'series',
429
+ data,
430
+ color,
431
+ },
432
+ ]}
433
+ >
434
+ <ReferenceLine dataY={referenceY} />
435
+ </LineChart>
436
+ </Box>
437
+ ),
438
+ );
439
+
440
+ return (
441
+ <VStack gap={3} style={{ width: '100%', maxWidth: 320, overflow: 'hidden' }}>
442
+ <ListCell
443
+ spacingVariant="condensed"
444
+ title="Asset with a really long name"
445
+ description="Some description of the asset"
446
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
447
+ detail="$334,239.03"
448
+ subdetail="+4.06%"
449
+ priority="start"
450
+ variant="positive"
451
+ />
452
+ <ListCell
453
+ spacingVariant="condensed"
454
+ title="Asset with a really long name"
455
+ description="Some description of the asset"
456
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
457
+ detail="$334,239.03"
458
+ subdetail="+4.06%"
459
+ priority="middle"
460
+ variant="positive"
461
+ />
462
+ <ListCell
463
+ spacingVariant="condensed"
464
+ title="Asset with a really long name"
465
+ description="Some description of the asset"
466
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
467
+ detail="$334,239.03"
468
+ subdetail="+4.06%"
469
+ priority="end"
470
+ variant="positive"
471
+ />
472
+ <ListCell
473
+ spacingVariant="condensed"
474
+ title="Asset with a really long name"
475
+ description="Some description of the asset"
476
+ intermediary={<CompactChart data={sparklineData} referenceY={referenceY} />}
477
+ detail="$334,239.03"
478
+ subdetail="+4.06%"
479
+ priority={['start', 'middle', 'end']}
480
+ variant="warning"
481
+ />
482
+ </VStack>
483
+ );
484
+ }
485
+ ```
486
+
219
487
  ### Anatomy
220
488
 
221
489
  Without helper text (top-only layout):
@@ -288,6 +556,7 @@ Mapping to `styles` / `classNames` keys:
288
556
  | Prop | Type | Required | Default | Description |
289
557
  | --- | --- | --- | --- | --- |
290
558
  | `accessory` | `more \| selected \| arrow` | No | `-` | Accessory to display at the end of the cell. |
559
+ | `accessoryNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Custom accessory node rendered at the end of the cell. Takes precedence over accessory. |
291
560
  | `action` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | - |
292
561
  | `alignContent` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| space-between \| space-around \| space-evenly>` | No | `-` | - |
293
562
  | `alignItems` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| self-start \| self-end \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
@@ -321,8 +590,10 @@ Mapping to `styles` / `classNames` keys:
321
590
  | `compact` | `boolean` | No | `-` | - |
322
591
  | `contentClassName` | `string` | No | `-` | - |
323
592
  | `dangerouslySetBackground` | `string` | No | `-` | - |
324
- | `description` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Description of content. Max 1 line (with title) or 2 lines (without), otherwise will truncate. |
325
- | `detail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Label and/or extra detail. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. |
593
+ | `description` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Description of content. Max 1 line (with title) or 2 lines (without), otherwise will truncate. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use descriptionNode. |
594
+ | `descriptionNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to render description. Takes precedence over description. |
595
+ | `detail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Label and/or extra detail. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use detailNode. |
596
+ | `detailNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to render label and/or extra detail. Takes precedence over detail. |
326
597
  | `detailWidth` | `string \| number` | No | `-` | - |
327
598
  | `disableMultilineTitle` | `boolean` | No | `-` | When there is no description the title will take up two lines by default. When this is set to true multiline title behavior is overwritten, and regardless of description text state the title will take up a single line truncating with ellipses. |
328
599
  | `disableSelectionAccessory` | `boolean` | No | `-` | Disable the default accessory that is displayed when the cell is selected. If accessory is provided, that will continue to be displayed, otherwise no accessory will be displayed when the cell is selected. |
@@ -400,13 +671,15 @@ Mapping to `styles` / `classNames` keys:
400
671
  | `spacingVariant` | `normal \| compact \| condensed` | No | `'normal'` | Spacing variant configuration. Deprecated value: compact. Prefer condensed. When spacingVariant=normal: 1. min-height is 80px 2. padding is var(--space-2) var(--space-3) 3. border-radius is var(--borderRadius-200) 4. when there is a description, titles numberOfLines={1} otherwise titles numberOfLines={2} 5. description and subdetail have font body When spacingVariant=compact: 1. same as spacingVariant=normal, except min-height is 40px When spacingVariant=condensed: 1. min-height is undefined 2. padding is var(--space-1) var(--space-2) 3. border-radius is --borderRadius-0 4. titles numberOfLines={2} 5. description and subdetail have font label2 |
401
672
  | `style` | `CSSProperties` | No | `-` | - |
402
673
  | `styles` | `{ root?: CSSProperties; media?: CSSProperties \| undefined; intermediary?: CSSProperties \| undefined; end?: CSSProperties \| undefined; accessory?: CSSProperties \| undefined; contentContainer?: CSSProperties \| undefined; pressable?: CSSProperties \| undefined; mainContent?: CSSProperties \| undefined; helperText?: CSSProperties \| undefined; title?: CSSProperties \| undefined; description?: CSSProperties \| undefined; } \| undefined` | No | `-` | Styles for the components |
403
- | `subdetail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Subdetail providing more information. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. |
674
+ | `subdetail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Subdetail providing more information. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use subdetailNode. |
404
675
  | `subdetailFont` | `ResponsiveProp<FontFamily \| inherit>` | No | `-` | Font to apply to the subdetail text. |
676
+ | `subdetailNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to render subdetail. Takes precedence over subdetail. |
405
677
  | `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
406
678
  | `textAlign` | `ResponsiveProp<center \| start \| end \| justify>` | No | `-` | - |
407
679
  | `textDecoration` | `ResponsiveProp<none \| underline \| overline \| line-through \| underline overline \| underline double>` | No | `-` | - |
408
680
  | `textTransform` | `ResponsiveProp<capitalize \| lowercase \| none \| uppercase>` | No | `-` | - |
409
- | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Title of content. Max 1 line (with description) or 2 lines (without), otherwise will truncate. |
681
+ | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Title of content. Max 1 line (with description) or 2 lines (without), otherwise will truncate. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use titleNode. |
682
+ | `titleNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | React node to render title. Takes precedence over title. |
410
683
  | `top` | `ResponsiveProp<Top<string \| number>>` | No | `-` | - |
411
684
  | `transform` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - |
412
685
  | `userSelect` | `ResponsiveProp<text \| none \| auto \| all>` | No | `-` | - |
@@ -44,6 +44,7 @@ function DefaultSelect() {
44
44
  | --- | --- | --- | --- | --- |
45
45
  | `value` | `string` | Yes | `-` | Unique identifier for each option |
46
46
  | `accessory` | `ReactElement<CellAccessoryProps, string \| JSXElementConstructor<any>>` | No | `-` | Accessory element rendered at the end of the cell (e.g., chevron). |
47
+ | `accessoryNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Custom accessory node rendered at the end of the cell. Takes precedence over accessory. |
47
48
  | `alignContent` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| space-between \| space-around \| space-evenly>` | No | `-` | - |
48
49
  | `alignItems` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| self-start \| self-end \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
49
50
  | `alignSelf` | `ResponsiveProp<center \| normal \| auto \| start \| end \| flex-start \| flex-end \| self-start \| self-end \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
@@ -75,7 +76,7 @@ function DefaultSelect() {
75
76
  | `compact` | `boolean` | No | `-` | - |
76
77
  | `contentClassName` | `string` | No | `-` | - |
77
78
  | `dangerouslySetBackground` | `string` | No | `-` | - |
78
- | `description` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Description of content. Max 1 line (with title) or 2 lines (without), otherwise will truncate. |
79
+ | `description` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Description of content. Max 1 line (with title) or 2 lines (without), otherwise will truncate. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use descriptionNode. |
79
80
  | `detail` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | - |
80
81
  | `detailWidth` | `string \| number` | No | `-` | - |
81
82
  | `disableCloseOnOptionChange` | `boolean` | No | `-` | Prevent menu from closing when an option is selected |
@@ -154,7 +155,7 @@ function DefaultSelect() {
154
155
  | `textAlign` | `ResponsiveProp<center \| start \| end \| justify>` | No | `-` | - |
155
156
  | `textDecoration` | `ResponsiveProp<none \| underline \| overline \| line-through \| underline overline \| underline double>` | No | `-` | - |
156
157
  | `textTransform` | `ResponsiveProp<capitalize \| lowercase \| none \| uppercase>` | No | `-` | - |
157
- | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Title of content. Max 1 line (with description) or 2 lines (without), otherwise will truncate. |
158
+ | `title` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Title of content. Max 1 line (with description) or 2 lines (without), otherwise will truncate. This prop is only intended to accept a string or Text component; other use cases, while allowed, are not supported and may result in unexpected behavior. For arbitrary content, use titleNode. |
158
159
  | `top` | `ResponsiveProp<Top<string \| number>>` | No | `-` | - |
159
160
  | `transform` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - |
160
161
  | `userSelect` | `ResponsiveProp<text \| none \| auto \| all>` | No | `-` | - |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinbase/cds-mcp-server",
3
- "version": "8.17.5",
3
+ "version": "8.18.0",
4
4
  "description": "Coinbase Design System - MCP Server",
5
5
  "repository": {
6
6
  "type": "git",