@indico-data/design-system 3.20.0 → 3.22.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/lib/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type SelectOption } from './components/forms/select/types';
2
2
  import { type IconSizes, type IconName } from './components/icons/types';
3
- import { type PillColor, type PillSize } from './components/pill/types';
3
+ import { type PillColor, type PillSize, type PillVariant, type PillType } from './components/pill/types';
4
4
  import { type TableProps, type TableColumn, type Direction, type Alignment } from './components/table/types';
5
5
  export type PermafrostComponent = {
6
6
  id?: string;
@@ -14,4 +14,4 @@ export type Color = ChromaticColor | 'black' | 'white';
14
14
  export type SemanticColor = 'neutral' | 'info' | 'warning' | 'error' | 'success' | 'pending';
15
15
  export type { SelectOption };
16
16
  export type { TableProps, TableColumn, Direction, Alignment };
17
- export type { PillColor, PillSize };
17
+ export type { PillColor, PillSize, PillVariant, PillType };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indico-data/design-system",
3
- "version": "3.20.0",
3
+ "version": "3.22.0",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "main": "lib/index.js",
@@ -26,7 +26,7 @@ form {
26
26
  }
27
27
 
28
28
  &:hover {
29
- border-color: var(--pf-semantic-border-hover);
29
+ border-color: var(--pf-semantic-border-quaternary);
30
30
  }
31
31
  &:focus {
32
32
  border-color: var(--pf-semantic-border-accent);
@@ -7,7 +7,7 @@
7
7
  box-shadow: none;
8
8
 
9
9
  &:hover:not(:focus) {
10
- border-color: var(--pf-semantic-border-hover);
10
+ border-color: var(--pf-semantic-border-quaternary);
11
11
  }
12
12
 
13
13
  &:focus,
@@ -20,6 +20,7 @@ export { SingleInputDatePicker } from './forms/date/inputDatePicker';
20
20
  export { InputDateRangePicker } from './forms/date/inputDateRangePicker';
21
21
  export { Modal, ConfirmationModal } from './modal';
22
22
  export { Badge } from './badge';
23
+ export { Pill } from './pill';
23
24
  export { Pagination } from './pagination';
24
25
  export { TanstackTable } from './tanstackTable';
25
26
  export { Tooltip } from './tooltip';
@@ -1,11 +1,20 @@
1
1
  import { Canvas, Meta, Controls, Story } from '@storybook/blocks';
2
2
  import * as Pill from './Pill.stories';
3
3
 
4
- <Meta title="Layout/Pill" name="Pill" of={Pill} />
4
+ <Meta title="Components/Pill" name="Pill" of={Pill} />
5
5
 
6
6
  # Pill
7
7
 
8
- Pills are used to display information in a compact way. They are often used to display tags, categories, or statuses.
8
+ A visual indicator for a value or status descriptor for UI elements.
9
+ Pills help highlight important information such as notifications, statuses, or
10
+ categories. They're primarily used for communicating secondary or additional
11
+ information alongside text.
12
+
13
+ Use `color` and `variant` (solid/outline) to control appearance, `type` to choose
14
+ between pill (fully rounded, default) or badge (rounded corners), and `size` for sm/md/lg.
15
+ Add `dot`, `iconLeft`, `iconRight`, or `onClose` to extend functionality as needed.
16
+
17
+ [Figma source](https://www.figma.com/design/FTAaDRrAYaBCKoUvlbx2BD/Indico---Design-System?node-id=1766-27393&m=dev)
9
18
 
10
19
  <Canvas of={Pill.Default} />
11
20
 
@@ -13,12 +22,62 @@ Pills are used to display information in a compact way. They are often used to d
13
22
 
14
23
  ## Sizes
15
24
 
16
- <Story of={Pill.AllSizes} />
25
+ <Canvas of={Pill.AllSizes} />
26
+
27
+ ## Solid Colors
28
+
29
+ <Canvas of={Pill.SolidColors} />
30
+
31
+ ## Outline Colors
32
+
33
+ <Canvas of={Pill.OutlineColors} />
34
+
35
+ ## Badge vs Pill
36
+
37
+ Badge uses a small border-radius; Pill is fully rounded.
38
+
39
+ <Canvas of={Pill.BadgeVsPill} />
40
+
41
+ ## With Icon Left
42
+
43
+ <Canvas of={Pill.WithIconLeft} />
44
+
45
+ ## With Icon Right
46
+
47
+ <Canvas of={Pill.WithIconRight} />
48
+
49
+ ## With Dot
50
+
51
+ <Canvas of={Pill.WithDot} />
52
+
53
+ ## Icon Only
54
+
55
+ Omit `children` to render an icon-only pill.
56
+
57
+ <Canvas of={Pill.IconOnly} />
58
+
59
+ ## Closeable
60
+
61
+ Pass `onClose` to render a dismiss button.
62
+
63
+ <Canvas of={Pill.Closeable} />
64
+
65
+ ## Combined
66
+
67
+ Multiple features can be combined freely.
68
+
69
+ <Canvas of={Pill.Combined} />
70
+
71
+ ## Full Color × Variant Matrix
72
+
73
+ <Canvas of={Pill.FullMatrix} />
74
+
75
+ ## Closeable Matrix
17
76
 
18
- ## Default Colors
77
+ <Canvas of={Pill.CloseableMatrix} />
19
78
 
20
- <Story of={Pill.AllDefaultColors} />
79
+ ## Figma Matrix
21
80
 
22
- # Shades
81
+ Complete matrix matching the [Figma design spec](https://www.figma.com/design/FTAaDRrAYaBCKoUvlbx2BD/Indico---Design-System?node-id=1766-27393&m=dev).
23
82
 
24
- <Story of={Pill.AllShades} />
83
+ <Canvas of={Pill.FigmaMatrix} />
@@ -1,82 +1,72 @@
1
+ import {
2
+ faCheck,
3
+ faCircleExclamation,
4
+ faTriangleExclamation,
5
+ faXmark,
6
+ faArrowRight,
7
+ faArrowUp,
8
+ } from '@fortawesome/free-solid-svg-icons';
1
9
  import { type Meta, type StoryObj } from '@storybook/react';
2
- import { Container, Row, Col } from 'react-grid-system';
10
+ import { fn } from '@storybook/test';
3
11
 
4
- import { type ChromaticColor } from '@/types';
12
+ import { registerFontAwesomeIcons } from '@/setup/setupIcons';
5
13
 
6
14
  import { Pill } from './Pill';
7
- import { type PillShade } from './types';
15
+ import { type PillColor, type PillVariant, type PillType } from './types';
8
16
 
17
+ registerFontAwesomeIcons(
18
+ faCheck,
19
+ faCircleExclamation,
20
+ faTriangleExclamation,
21
+ faXmark,
22
+ faArrowRight,
23
+ faArrowUp,
24
+ );
9
25
 
26
+ const SIZES = ['sm', 'md', 'lg'] as const;
27
+ const SIZE_LABELS: Record<(typeof SIZES)[number], string> = {
28
+ sm: 'Small',
29
+ md: 'Medium',
30
+ lg: 'Large',
31
+ };
10
32
 
11
- const CHROMATIC_COLORS: ChromaticColor[] = [
12
- 'blue',
13
- 'purple',
33
+ const COLORS: PillColor[] = [
14
34
  'red',
35
+ 'purple',
15
36
  'yellow',
16
- 'gray',
37
+ 'blue',
17
38
  'green',
39
+ 'gray',
18
40
  'pink',
19
41
  'orange',
20
42
  'teal',
43
+ 'soft',
21
44
  ];
22
45
 
23
- const SHADES: PillShade[] = [1, 2, 3, 4, 5];
46
+ const VARIANTS: PillVariant[] = ['solid', 'outline'];
47
+ const TYPES: PillType[] = ['pill', 'badge'];
24
48
 
25
- const meta: Meta = {
49
+ const row: React.CSSProperties = {
50
+ display: 'flex',
51
+ flexWrap: 'wrap',
52
+ gap: 8,
53
+ alignItems: 'center',
54
+ };
55
+ const section: React.CSSProperties = { marginBottom: 16 };
56
+
57
+ const meta: Meta<typeof Pill> = {
26
58
  title: 'Components/Pill',
27
59
  component: Pill,
28
60
  argTypes: {
29
- className: {
30
- control: false,
31
- table: {
32
- category: 'Props',
33
- type: {
34
- summary: 'css class name',
35
- },
36
- },
37
- },
38
- children: {
39
- control: 'text',
40
- table: {
41
- category: 'Props',
42
- type: {
43
- summary: 'React.ReactNode',
44
- },
45
- },
46
- },
47
- color: {
48
- control: 'select',
49
- options: [
50
- 'success',
51
- 'warning',
52
- 'error',
53
- 'teal',
54
- 'neutral',
55
- 'purple',
56
- 'info',
57
- 'pending',
58
- 'orange',
59
- ],
60
- table: {
61
- category: 'Props',
62
- type: {
63
- summary: 'success | warning | error | teal | neutral | purple | info | pending | orange',
64
- },
65
- },
66
- },
67
-
68
- size: {
69
- control: {
70
- type: 'select',
71
- options: ['sm', 'md', 'lg'],
72
- },
73
- table: {
74
- category: 'Props',
75
- type: {
76
- summary: 'sm | md | lg',
77
- },
78
- },
79
- },
61
+ color: { control: 'select', options: COLORS },
62
+ size: { control: 'select', options: SIZES },
63
+ variant: { control: 'select', options: VARIANTS },
64
+ type: { control: 'select', options: TYPES },
65
+ iconLeft: { control: 'text' },
66
+ iconRight: { control: 'text' },
67
+ dot: { control: 'boolean' },
68
+ onClose: { control: false },
69
+ children: { control: 'text' },
80
70
  },
81
71
  };
82
72
 
@@ -86,72 +76,350 @@ type Story = StoryObj<typeof Pill>;
86
76
 
87
77
  export const Default: Story = {
88
78
  args: {
89
- children: 'Green',
90
- color: 'green',
91
- },
92
- render: (args) => {
93
- return (
94
- <Pill color={args.color} size={args.size}>
95
- {args.children}
96
- </Pill>
97
- );
79
+ children: 'Status',
80
+ color: 'blue',
81
+ size: 'md',
82
+ variant: 'solid',
83
+ type: 'pill',
98
84
  },
99
85
  };
100
86
 
101
87
  export const AllSizes: Story = {
102
- render: () => {
103
- return (
104
- <Container>
105
- <Row>
106
- <Col>
107
- <Pill size="lg">Large (lg)</Pill>
108
- </Col>
109
- <Col>
110
- <Pill size="md">Medium (md)</Pill>
111
- </Col>
112
- <Col>
113
- <Pill size="sm">Small (sm)</Pill>
114
- </Col>
115
- </Row>
116
- </Container>
117
- );
118
- },
88
+ render: () => (
89
+ <div style={row}>
90
+ <Pill size="sm" color="blue" variant="solid">
91
+ Small
92
+ </Pill>
93
+ <Pill size="md" color="blue" variant="solid">
94
+ Medium
95
+ </Pill>
96
+ <Pill size="lg" color="blue" variant="solid">
97
+ Large
98
+ </Pill>
99
+ </div>
100
+ ),
101
+ };
102
+
103
+ export const SolidColors: Story = {
104
+ render: () => (
105
+ <div style={row}>
106
+ {COLORS.map((color) => (
107
+ <Pill key={color} color={color} variant="solid" size="md">
108
+ {color}
109
+ </Pill>
110
+ ))}
111
+ </div>
112
+ ),
113
+ };
114
+
115
+ export const OutlineColors: Story = {
116
+ render: () => (
117
+ <div style={row}>
118
+ {COLORS.map((color) => (
119
+ <Pill key={color} color={color} variant="outline" size="md">
120
+ {color}
121
+ </Pill>
122
+ ))}
123
+ </div>
124
+ ),
119
125
  };
120
126
 
121
- export const AllDefaultColors: Story = {
122
- render: () => {
123
- return (
124
- <Container>
125
- <Row justify="start">
126
- {CHROMATIC_COLORS.map((color: ChromaticColor) => (
127
- <Col key={color} xs="content" className="mb-2">
128
- <Pill color={color} size="md">
127
+ export const BadgeVsPill: Story = {
128
+ render: () => (
129
+ <div style={row}>
130
+ {TYPES.map((t) => (
131
+ <Pill key={t} color="blue" variant="solid" size="md" type={t}>
132
+ {t}
133
+ </Pill>
134
+ ))}
135
+ </div>
136
+ ),
137
+ };
138
+
139
+ export const WithIconLeft: Story = {
140
+ render: () => (
141
+ <div style={row}>
142
+ <Pill color="green" variant="solid" size="sm" iconLeft="fa-check">
143
+ Approved
144
+ </Pill>
145
+ <Pill color="red" variant="outline" size="md" iconLeft="fa-circle-exclamation">
146
+ Error
147
+ </Pill>
148
+ <Pill color="yellow" variant="outline" size="lg" iconLeft="fa-triangle-exclamation">
149
+ Warning
150
+ </Pill>
151
+ </div>
152
+ ),
153
+ };
154
+
155
+ export const WithIconRight: Story = {
156
+ render: () => (
157
+ <div style={row}>
158
+ <Pill color="blue" variant="solid" size="sm" iconRight="fa-arrow-right">
159
+ Next
160
+ </Pill>
161
+ <Pill color="purple" variant="outline" size="md" iconRight="fa-arrow-right">
162
+ Continue
163
+ </Pill>
164
+ <Pill color="teal" variant="solid" size="lg" iconRight="fa-arrow-right">
165
+ Proceed
166
+ </Pill>
167
+ </div>
168
+ ),
169
+ };
170
+
171
+ export const WithDot: Story = {
172
+ render: () => (
173
+ <>
174
+ {VARIANTS.map((variant) => (
175
+ <div key={variant} style={section}>
176
+ <h4 style={{ marginBottom: 8, textTransform: 'capitalize' }}>{variant} + Dot</h4>
177
+ <div style={row}>
178
+ {COLORS.map((color) => (
179
+ <Pill key={`${variant}-${color}`} color={color} variant={variant} size="md" dot>
129
180
  {color}
130
181
  </Pill>
131
- </Col>
132
- ))}
133
- </Row>
134
- </Container>
135
- );
136
- },
182
+ ))}
183
+ </div>
184
+ </div>
185
+ ))}
186
+ </>
187
+ ),
137
188
  };
138
189
 
139
- export const AllShades: Story = {
140
- render: () => {
141
- return (
142
- <Container>
143
- {CHROMATIC_COLORS.map((color: ChromaticColor) => (
144
- <Row key={color}>
145
- {SHADES.map((shade: PillShade) => (
146
- <Col key={`${color}-${shade}`} width={100} className="mb-2">
147
- <Pill shade={shade} color={color} size="md">
148
- {color}-{shade}
149
- </Pill>
150
- </Col>
190
+ export const IconOnly: Story = {
191
+ render: () => (
192
+ <div style={row}>
193
+ <Pill color="green" variant="solid" size="sm" iconLeft="fa-check" />
194
+ <Pill color="red" variant="outline" size="md" iconLeft="fa-circle-exclamation" />
195
+ <Pill color="yellow" variant="solid" size="lg" iconLeft="fa-triangle-exclamation" />
196
+ </div>
197
+ ),
198
+ };
199
+
200
+ export const Closeable: Story = {
201
+ render: () => (
202
+ <div style={row}>
203
+ <Pill color="blue" variant="outline" type="pill" size="md" onClose={fn()}>
204
+ Filter: Active
205
+ </Pill>
206
+ </div>
207
+ ),
208
+ };
209
+
210
+ export const Combined: Story = {
211
+ render: () => (
212
+ <div style={row}>
213
+ <Pill
214
+ color="red"
215
+ variant="outline"
216
+ size="md"
217
+ dot
218
+ iconLeft="fa-circle-exclamation"
219
+ onClose={fn()}
220
+ >
221
+ Critical
222
+ </Pill>
223
+ <Pill color="green" variant="solid" size="md" dot iconLeft="fa-check">
224
+ Complete
225
+ </Pill>
226
+ <Pill color="blue" variant="outline" size="md" iconLeft="fa-check" iconRight="fa-arrow-right">
227
+ Both Icons
228
+ </Pill>
229
+ </div>
230
+ ),
231
+ };
232
+
233
+ export const FullMatrix: Story = {
234
+ render: () => (
235
+ <>
236
+ {VARIANTS.map((variant) => (
237
+ <div key={variant} style={section}>
238
+ <h4 style={{ marginBottom: 8, textTransform: 'capitalize' }}>{variant}</h4>
239
+ <div style={row}>
240
+ {COLORS.map((color) => (
241
+ <Pill key={`${variant}-${color}`} color={color} variant={variant} size="md">
242
+ {color}
243
+ </Pill>
151
244
  ))}
152
- </Row>
153
- ))}
154
- </Container>
155
- );
245
+ </div>
246
+ </div>
247
+ ))}
248
+ </>
249
+ ),
250
+ };
251
+
252
+ export const CloseableMatrix: Story = {
253
+ render: () => (
254
+ <>
255
+ {VARIANTS.map((variant) => (
256
+ <div key={variant} style={section}>
257
+ <h4 style={{ marginBottom: 8, textTransform: 'capitalize' }}>{variant} + Close</h4>
258
+ <div style={row}>
259
+ {COLORS.map((color) => (
260
+ <Pill
261
+ key={`${variant}-${color}`}
262
+ color={color}
263
+ variant={variant}
264
+ size="md"
265
+ onClose={fn()}
266
+ >
267
+ {color}
268
+ </Pill>
269
+ ))}
270
+ </div>
271
+ </div>
272
+ ))}
273
+ </>
274
+ ),
275
+ };
276
+
277
+ const th: React.CSSProperties = {
278
+ padding: '6px 10px',
279
+ fontSize: 11,
280
+ fontWeight: 500,
281
+ textAlign: 'center',
282
+ borderBottom: '1px solid #ddd',
283
+ whiteSpace: 'nowrap',
284
+ };
285
+
286
+ const groupTh: React.CSSProperties = {
287
+ ...th,
288
+ borderBottom: '2px solid #ccc',
289
+ paddingBottom: 4,
290
+ };
291
+
292
+ const td: React.CSSProperties = {
293
+ padding: '10px 8px',
294
+ textAlign: 'center',
295
+ verticalAlign: 'middle',
296
+ };
297
+
298
+ const groupSep: React.CSSProperties = {
299
+ ...td,
300
+ borderLeft: '1px solid #e8e8e8',
301
+ };
302
+
303
+ const typeLabel: React.CSSProperties = {
304
+ writingMode: 'vertical-rl',
305
+ textOrientation: 'mixed',
306
+ transform: 'rotate(180deg)',
307
+ fontSize: 12,
308
+ fontWeight: 600,
309
+ padding: '8px 4px',
310
+ textAlign: 'center',
311
+ verticalAlign: 'middle',
312
+ };
313
+
314
+ const colorLabel: React.CSSProperties = {
315
+ ...td,
316
+ fontSize: 11,
317
+ fontWeight: 500,
318
+ textTransform: 'capitalize',
319
+ textAlign: 'right',
320
+ paddingRight: 12,
321
+ };
322
+
323
+ const FEATURE_GROUPS = [
324
+ {
325
+ label: 'False',
326
+ render: (color: PillColor, size: (typeof SIZES)[number], pillType: PillType) => (
327
+ <Pill color={color} variant="solid" size={size} type={pillType}>
328
+ Label
329
+ </Pill>
330
+ ),
156
331
  },
332
+ {
333
+ label: 'Dot',
334
+ render: (color: PillColor, size: (typeof SIZES)[number], pillType: PillType) => (
335
+ <Pill color={color} variant="solid" size={size} type={pillType} dot>
336
+ Label
337
+ </Pill>
338
+ ),
339
+ },
340
+ {
341
+ label: 'x-close',
342
+ render: (color: PillColor, size: (typeof SIZES)[number], pillType: PillType) => (
343
+ <Pill color={color} variant="solid" size={size} type={pillType} onClose={fn()}>
344
+ Label
345
+ </Pill>
346
+ ),
347
+ },
348
+ {
349
+ label: 'Icons',
350
+ render: (color: PillColor, size: (typeof SIZES)[number], pillType: PillType) => (
351
+ <Pill
352
+ color={color}
353
+ variant="solid"
354
+ size={size}
355
+ type={pillType}
356
+ iconLeft="fa-arrow-up"
357
+ iconRight="fa-arrow-up"
358
+ >
359
+ Label
360
+ </Pill>
361
+ ),
362
+ },
363
+ {
364
+ label: 'Only',
365
+ render: (color: PillColor, size: (typeof SIZES)[number], pillType: PillType) => (
366
+ <Pill color={color} variant="solid" size={size} type={pillType} iconLeft="fa-arrow-up" />
367
+ ),
368
+ },
369
+ ] as const;
370
+
371
+ export const FigmaMatrix: Story = {
372
+ render: () => (
373
+ <table style={{ borderCollapse: 'collapse', fontSize: 13 }}>
374
+ <thead>
375
+ <tr>
376
+ <th style={th} />
377
+ <th style={th} />
378
+ {FEATURE_GROUPS.map((g) => (
379
+ <th key={g.label} style={groupTh} colSpan={3}>
380
+ {g.label}
381
+ </th>
382
+ ))}
383
+ </tr>
384
+ <tr>
385
+ <th style={th} />
386
+ <th style={th} />
387
+ {FEATURE_GROUPS.map((g) =>
388
+ SIZES.map((s, si) => (
389
+ <th
390
+ key={`${g.label}-${s}`}
391
+ style={si === 0 ? { ...th, borderLeft: '1px solid #e8e8e8' } : th}
392
+ >
393
+ {SIZE_LABELS[s]}
394
+ </th>
395
+ )),
396
+ )}
397
+ </tr>
398
+ </thead>
399
+ <tbody>
400
+ {TYPES.map((pillType) =>
401
+ COLORS.map((color, ci) => (
402
+ <tr
403
+ key={`${pillType}-${color}`}
404
+ style={ci === COLORS.length - 1 ? { borderBottom: '2px solid #ddd' } : undefined}
405
+ >
406
+ {ci === 0 && (
407
+ <td style={typeLabel} rowSpan={COLORS.length}>
408
+ {pillType}
409
+ </td>
410
+ )}
411
+ <td style={colorLabel}>{color}</td>
412
+ {FEATURE_GROUPS.map((g) =>
413
+ SIZES.map((s, si) => (
414
+ <td key={`${g.label}-${s}`} style={si === 0 ? groupSep : td}>
415
+ {g.render(color, s, pillType)}
416
+ </td>
417
+ )),
418
+ )}
419
+ </tr>
420
+ )),
421
+ )}
422
+ </tbody>
423
+ </table>
424
+ ),
157
425
  };