@indico-data/design-system 3.22.1 → 3.23.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/AGENTS.md +110 -0
- package/CLAUDE.md +1 -0
- package/lib/components/alert/Alert.d.ts +2 -0
- package/lib/components/alert/Alert.stories.d.ts +15 -0
- package/lib/components/alert/__tests__/Alert.test.d.ts +1 -0
- package/lib/components/alert/index.d.ts +2 -0
- package/lib/components/alert/types.d.ts +26 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/pill/Pill.d.ts +1 -1
- package/lib/components/pill/Pill.stories.d.ts +3 -0
- package/lib/components/pill/types.d.ts +4 -0
- package/lib/index.css +214 -30
- package/lib/index.d.ts +34 -2
- package/lib/index.esm.css +214 -30
- package/lib/index.esm.js +41 -5
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +40 -3
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/alert/Alert.mdx +65 -0
- package/src/components/alert/Alert.stories.tsx +162 -0
- package/src/components/alert/Alert.tsx +68 -0
- package/src/components/alert/__tests__/Alert.test.tsx +52 -0
- package/src/components/alert/index.ts +2 -0
- package/src/components/alert/styles/Alert.scss +139 -0
- package/src/components/alert/styles/_tokens.scss +71 -0
- package/src/components/alert/types.ts +28 -0
- package/src/components/index.ts +1 -0
- package/src/components/pill/Pill.mdx +27 -0
- package/src/components/pill/Pill.stories.tsx +87 -0
- package/src/components/pill/Pill.tsx +36 -0
- package/src/components/pill/__tests__/Pill.test.tsx +93 -0
- package/src/components/pill/styles/Pill.scss +15 -2
- package/src/components/pill/types.ts +4 -0
- package/src/index.ts +1 -0
- package/src/setup/setupIcons.ts +8 -0
- package/src/styles/index.scss +2 -1
- package/src/types.ts +2 -0
|
@@ -65,6 +65,8 @@ const meta: Meta<typeof Pill> = {
|
|
|
65
65
|
iconLeft: { control: 'text' },
|
|
66
66
|
iconRight: { control: 'text' },
|
|
67
67
|
dot: { control: 'boolean' },
|
|
68
|
+
shade: { control: { type: 'number' } },
|
|
69
|
+
shadeCount: { control: { type: 'number' } },
|
|
68
70
|
onClose: { control: false },
|
|
69
71
|
children: { control: 'text' },
|
|
70
72
|
},
|
|
@@ -274,6 +276,91 @@ export const CloseableMatrix: Story = {
|
|
|
274
276
|
),
|
|
275
277
|
};
|
|
276
278
|
|
|
279
|
+
export const Shaded: Story = {
|
|
280
|
+
args: {
|
|
281
|
+
children: 'Medium',
|
|
282
|
+
color: 'green',
|
|
283
|
+
shade: 3,
|
|
284
|
+
shadeCount: 5,
|
|
285
|
+
},
|
|
286
|
+
argTypes: {
|
|
287
|
+
shade: { control: { type: 'range', min: 1, max: 10, step: 1 } },
|
|
288
|
+
shadeCount: { control: { type: 'range', min: 2, max: 10, step: 1 } },
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
export const ShadedSpectrum: Story = {
|
|
293
|
+
render: () => {
|
|
294
|
+
const labels = ['Very Low', 'Low', 'Medium', 'High', 'Very High'];
|
|
295
|
+
return (
|
|
296
|
+
<>
|
|
297
|
+
{(['solid', 'outline'] as const).map((variant) => (
|
|
298
|
+
<div key={variant} style={section}>
|
|
299
|
+
<h4 style={{ marginBottom: 8, textTransform: 'capitalize' }}>{variant}</h4>
|
|
300
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
301
|
+
{COLORS.filter((c) => c !== 'soft').map((color) => (
|
|
302
|
+
<div key={color} style={row}>
|
|
303
|
+
<span
|
|
304
|
+
style={{
|
|
305
|
+
width: 60,
|
|
306
|
+
fontSize: 12,
|
|
307
|
+
fontWeight: 500,
|
|
308
|
+
textTransform: 'capitalize',
|
|
309
|
+
}}
|
|
310
|
+
>
|
|
311
|
+
{color}
|
|
312
|
+
</span>
|
|
313
|
+
{labels.map((label, i) => (
|
|
314
|
+
<Pill
|
|
315
|
+
key={label}
|
|
316
|
+
color={color}
|
|
317
|
+
variant={variant}
|
|
318
|
+
size="md"
|
|
319
|
+
shade={i + 1}
|
|
320
|
+
shadeCount={labels.length}
|
|
321
|
+
>
|
|
322
|
+
{label}
|
|
323
|
+
</Pill>
|
|
324
|
+
))}
|
|
325
|
+
</div>
|
|
326
|
+
))}
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
))}
|
|
330
|
+
</>
|
|
331
|
+
);
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export const ShadedDynamicCount: Story = {
|
|
336
|
+
render: () => {
|
|
337
|
+
const counts = [3, 5, 7];
|
|
338
|
+
return (
|
|
339
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
340
|
+
{counts.map((count) => (
|
|
341
|
+
<div key={count}>
|
|
342
|
+
<h4 style={{ marginBottom: 8 }}>{count} shades</h4>
|
|
343
|
+
<div style={row}>
|
|
344
|
+
{Array.from({ length: count }, (_, i) => (
|
|
345
|
+
<Pill
|
|
346
|
+
key={i}
|
|
347
|
+
color="blue"
|
|
348
|
+
variant="solid"
|
|
349
|
+
size="md"
|
|
350
|
+
shade={i + 1}
|
|
351
|
+
shadeCount={count}
|
|
352
|
+
>
|
|
353
|
+
{i + 1} of {count}
|
|
354
|
+
</Pill>
|
|
355
|
+
))}
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
))}
|
|
359
|
+
</div>
|
|
360
|
+
);
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
|
|
277
364
|
const th: React.CSSProperties = {
|
|
278
365
|
padding: '6px 10px',
|
|
279
366
|
fontSize: 11,
|
|
@@ -19,11 +19,46 @@ export const Pill = ({
|
|
|
19
19
|
iconLeft,
|
|
20
20
|
iconRight,
|
|
21
21
|
dot,
|
|
22
|
+
shade,
|
|
23
|
+
shadeCount,
|
|
22
24
|
onClose,
|
|
23
25
|
closeAriaLabel = 'Remove',
|
|
24
26
|
className,
|
|
27
|
+
style,
|
|
25
28
|
...rest
|
|
26
29
|
}: PillProps) => {
|
|
30
|
+
const shadeStyle = (() => {
|
|
31
|
+
if (
|
|
32
|
+
shade === undefined ||
|
|
33
|
+
shade === null ||
|
|
34
|
+
shadeCount === undefined ||
|
|
35
|
+
shadeCount === null ||
|
|
36
|
+
shadeCount <= 1
|
|
37
|
+
)
|
|
38
|
+
return undefined;
|
|
39
|
+
|
|
40
|
+
const s = Math.max(1, Math.min(shadeCount, Math.round(shade)));
|
|
41
|
+
const midpoint = (shadeCount + 1) / 2;
|
|
42
|
+
|
|
43
|
+
if (s < midpoint) {
|
|
44
|
+
const distance = (midpoint - s) / (midpoint - 1);
|
|
45
|
+
return {
|
|
46
|
+
'--pill-shade-mix': `${Math.round(100 - distance * 30)}%`,
|
|
47
|
+
'--pill-shade-target': 'white',
|
|
48
|
+
} as React.CSSProperties;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (s > midpoint) {
|
|
52
|
+
const distance = (s - midpoint) / (shadeCount - midpoint);
|
|
53
|
+
return {
|
|
54
|
+
'--pill-shade-mix': `${Math.round(100 - distance * 30)}%`,
|
|
55
|
+
'--pill-shade-target': 'black',
|
|
56
|
+
} as React.CSSProperties;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return undefined;
|
|
60
|
+
})();
|
|
61
|
+
|
|
27
62
|
return (
|
|
28
63
|
<div
|
|
29
64
|
className={classNames(
|
|
@@ -37,6 +72,7 @@ export const Pill = ({
|
|
|
37
72
|
'pill--closeable': !!onClose,
|
|
38
73
|
},
|
|
39
74
|
)}
|
|
75
|
+
style={shadeStyle ? { ...shadeStyle, ...style } : style}
|
|
40
76
|
{...rest}
|
|
41
77
|
>
|
|
42
78
|
{dot && <span className="pill__dot" />}
|
|
@@ -122,4 +122,97 @@ describe('Pill', () => {
|
|
|
122
122
|
expect(container.querySelector('.pill__close')).toBeInTheDocument();
|
|
123
123
|
expect(screen.getByText('All Features')).toBeInTheDocument();
|
|
124
124
|
});
|
|
125
|
+
|
|
126
|
+
describe('shade', () => {
|
|
127
|
+
const getStyle = (container: HTMLElement) => (container.firstChild as HTMLElement).style;
|
|
128
|
+
|
|
129
|
+
it('sets no shade vars when both shade and shadeCount are omitted', () => {
|
|
130
|
+
const { container } = render(<Pill>Plain</Pill>);
|
|
131
|
+
const style = getStyle(container);
|
|
132
|
+
expect(style.getPropertyValue('--pill-shade-mix')).toBe('');
|
|
133
|
+
expect(style.getPropertyValue('--pill-shade-target')).toBe('');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('sets no shade vars when only shade is provided', () => {
|
|
137
|
+
const { container } = render(<Pill shade={2}>Shade Only</Pill>);
|
|
138
|
+
const style = getStyle(container);
|
|
139
|
+
expect(style.getPropertyValue('--pill-shade-mix')).toBe('');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('sets no shade vars when only shadeCount is provided', () => {
|
|
143
|
+
const { container } = render(<Pill shadeCount={5}>Count Only</Pill>);
|
|
144
|
+
const style = getStyle(container);
|
|
145
|
+
expect(style.getPropertyValue('--pill-shade-mix')).toBe('');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('mixes toward white for shades below midpoint', () => {
|
|
149
|
+
const { container } = render(
|
|
150
|
+
<Pill shade={1} shadeCount={5}>
|
|
151
|
+
Lightest
|
|
152
|
+
</Pill>,
|
|
153
|
+
);
|
|
154
|
+
const style = getStyle(container);
|
|
155
|
+
expect(style.getPropertyValue('--pill-shade-mix')).toBe('70%');
|
|
156
|
+
expect(style.getPropertyValue('--pill-shade-target')).toBe('white');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('mixes toward black for shades above midpoint', () => {
|
|
160
|
+
const { container } = render(
|
|
161
|
+
<Pill shade={5} shadeCount={5}>
|
|
162
|
+
Darkest
|
|
163
|
+
</Pill>,
|
|
164
|
+
);
|
|
165
|
+
const style = getStyle(container);
|
|
166
|
+
expect(style.getPropertyValue('--pill-shade-mix')).toBe('70%');
|
|
167
|
+
expect(style.getPropertyValue('--pill-shade-target')).toBe('black');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('sets no shade vars at the exact midpoint (base color)', () => {
|
|
171
|
+
const { container } = render(
|
|
172
|
+
<Pill shade={3} shadeCount={5}>
|
|
173
|
+
Middle
|
|
174
|
+
</Pill>,
|
|
175
|
+
);
|
|
176
|
+
const style = getStyle(container);
|
|
177
|
+
expect(style.getPropertyValue('--pill-shade-mix')).toBe('');
|
|
178
|
+
expect(style.getPropertyValue('--pill-shade-target')).toBe('');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('handles even shadeCount (no exact midpoint)', () => {
|
|
182
|
+
const { container: c1 } = render(
|
|
183
|
+
<Pill shade={2} shadeCount={4}>
|
|
184
|
+
Below
|
|
185
|
+
</Pill>,
|
|
186
|
+
);
|
|
187
|
+
const { container: c2 } = render(
|
|
188
|
+
<Pill shade={3} shadeCount={4}>
|
|
189
|
+
Above
|
|
190
|
+
</Pill>,
|
|
191
|
+
);
|
|
192
|
+
expect(getStyle(c1).getPropertyValue('--pill-shade-target')).toBe('white');
|
|
193
|
+
expect(getStyle(c2).getPropertyValue('--pill-shade-target')).toBe('black');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('ignores shade when shadeCount is 1 or missing', () => {
|
|
197
|
+
const { container: c1 } = render(
|
|
198
|
+
<Pill shade={1} shadeCount={1}>
|
|
199
|
+
Single
|
|
200
|
+
</Pill>,
|
|
201
|
+
);
|
|
202
|
+
const { container: c2 } = render(<Pill shade={2}>No Count</Pill>);
|
|
203
|
+
expect(getStyle(c1).getPropertyValue('--pill-shade-mix')).toBe('');
|
|
204
|
+
expect(getStyle(c2).getPropertyValue('--pill-shade-mix')).toBe('');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('preserves consumer style prop alongside shade vars', () => {
|
|
208
|
+
const { container } = render(
|
|
209
|
+
<Pill shade={1} shadeCount={3} style={{ maxWidth: 200 }}>
|
|
210
|
+
Styled
|
|
211
|
+
</Pill>,
|
|
212
|
+
);
|
|
213
|
+
const style = getStyle(container);
|
|
214
|
+
expect(style.getPropertyValue('--pill-shade-mix')).not.toBe('');
|
|
215
|
+
expect(style.maxWidth).toBe('200px');
|
|
216
|
+
});
|
|
217
|
+
});
|
|
125
218
|
});
|
|
@@ -112,11 +112,24 @@ $pill-variants: 'solid', 'outline';
|
|
|
112
112
|
@each $variant in $pill-variants {
|
|
113
113
|
@each $color in $pill-colors {
|
|
114
114
|
&--#{$variant}-#{$color} {
|
|
115
|
-
background-color:
|
|
115
|
+
background-color: color-mix(
|
|
116
|
+
in srgb,
|
|
117
|
+
var(--pf-pill-#{$variant}-#{$color}-bg) var(--pill-shade-mix, 100%),
|
|
118
|
+
var(--pill-shade-target, white)
|
|
119
|
+
);
|
|
116
120
|
color: var(--pf-pill-#{$variant}-#{$color}-text);
|
|
117
121
|
|
|
118
122
|
@if $variant == 'outline' {
|
|
119
|
-
box-shadow: inset
|
|
123
|
+
box-shadow: inset
|
|
124
|
+
0
|
|
125
|
+
0
|
|
126
|
+
0
|
|
127
|
+
1px
|
|
128
|
+
color-mix(
|
|
129
|
+
in srgb,
|
|
130
|
+
var(--pf-pill-#{$variant}-#{$color}-border) var(--pill-shade-mix, 100%),
|
|
131
|
+
var(--pill-shade-target, white)
|
|
132
|
+
);
|
|
120
133
|
}
|
|
121
134
|
|
|
122
135
|
> .icon {
|
|
@@ -25,6 +25,10 @@ export interface PillProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
25
25
|
iconRight?: IconName;
|
|
26
26
|
/** When true, renders a small colored dot indicator before the content */
|
|
27
27
|
dot?: boolean;
|
|
28
|
+
/** 1-based shade index within a group. Used with shadeCount to vary background lightness via color-mix(). */
|
|
29
|
+
shade?: number;
|
|
30
|
+
/** Total shades in the group. Background blends lighter (white) below midpoint, darker (black) above. */
|
|
31
|
+
shadeCount?: number;
|
|
28
32
|
/** When provided, renders a close button that calls this handler */
|
|
29
33
|
onClose?: () => void;
|
|
30
34
|
/** Accessible label for the close button (for i18n) */
|
package/src/index.ts
CHANGED
|
@@ -27,6 +27,7 @@ export { Card } from './components/card';
|
|
|
27
27
|
export { FloatUI } from './components/floatUI';
|
|
28
28
|
export { Menu } from './components/menu';
|
|
29
29
|
export { Pill } from './components/pill';
|
|
30
|
+
export { Alert } from './components/alert';
|
|
30
31
|
export { Badge } from './components/badge';
|
|
31
32
|
export { Modal, ConfirmationModal } from './components/modal';
|
|
32
33
|
export { TanstackTable } from './components/tanstackTable';
|
package/src/setup/setupIcons.ts
CHANGED
|
@@ -6,9 +6,13 @@ import {
|
|
|
6
6
|
faArrowLeft,
|
|
7
7
|
faCalculator,
|
|
8
8
|
faCheck,
|
|
9
|
+
faCircleCheck,
|
|
10
|
+
faCircleExclamation,
|
|
11
|
+
faCircleInfo,
|
|
9
12
|
faCircleNotch,
|
|
10
13
|
faMountainSun,
|
|
11
14
|
faRocket,
|
|
15
|
+
faTriangleExclamation,
|
|
12
16
|
faWind,
|
|
13
17
|
faEyeSlash,
|
|
14
18
|
faEye,
|
|
@@ -40,9 +44,13 @@ registerFontAwesomeIcons(
|
|
|
40
44
|
faArrowLeft,
|
|
41
45
|
faCalculator,
|
|
42
46
|
faCheck,
|
|
47
|
+
faCircleCheck,
|
|
48
|
+
faCircleExclamation,
|
|
49
|
+
faCircleInfo,
|
|
43
50
|
faCircleNotch,
|
|
44
51
|
faMountainSun,
|
|
45
52
|
faRocket,
|
|
53
|
+
faTriangleExclamation,
|
|
46
54
|
faWind,
|
|
47
55
|
faEyeSlash,
|
|
48
56
|
faEye,
|
package/src/styles/index.scss
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
@import 'variables/index.scss';
|
|
4
4
|
@import 'primitives/index.scss';
|
|
5
5
|
@import 'tokens/semantic-tokens';
|
|
6
|
-
|
|
7
6
|
@import 'utilities';
|
|
8
7
|
@import 'typography';
|
|
9
8
|
@import 'colors';
|
|
@@ -36,6 +35,8 @@
|
|
|
36
35
|
@import '../components/tanstackTable/styles/table.scss';
|
|
37
36
|
@import '../components/pill/styles/tokens';
|
|
38
37
|
@import '../components/pill/styles/Pill.scss';
|
|
38
|
+
@import '../components/alert/styles/tokens';
|
|
39
|
+
@import '../components/alert/styles/Alert.scss';
|
|
39
40
|
@import '../components/loading-indicators/BarSpinner/styles/BarSpinner.scss';
|
|
40
41
|
@import '../components/loading-indicators/CirclePulse/CirclePulse.scss';
|
|
41
42
|
@import '../components/truncate/styles/Truncate.scss';
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AlertType, type AlertSize, type AlertVariant } from './components/alert/types';
|
|
1
2
|
import { type SelectOption } from './components/forms/select/types';
|
|
2
3
|
import { type IconSizes, type IconName } from './components/icons/types';
|
|
3
4
|
import {
|
|
@@ -40,3 +41,4 @@ export type SemanticColor = 'neutral' | 'info' | 'warning' | 'error' | 'success'
|
|
|
40
41
|
export type { SelectOption };
|
|
41
42
|
export type { TableProps, TableColumn, Direction, Alignment };
|
|
42
43
|
export type { PillColor, PillSize, PillVariant, PillType };
|
|
44
|
+
export type { AlertType, AlertSize, AlertVariant };
|