Skip to content

Commit 996adbc

Browse files
committed
Add errorMessage
Add invalid to combobox Add errorMessage to date-field Add errorMessage to number field Add errorMessage to radio Add error message to radio group Add errorMessage to search Add errorMessage to select Add errorMessage to slider Add errorMessage to switch Add errorMessage to text area Add errorMessage to text field Align stories arg name and element attribute Add new attribute to react props Add visual test for error message
1 parent 3047b25 commit 996adbc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1281
-489
lines changed

packages/components/.storybook/preview-head.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@
2222
/* For testing reaction to JupyterLab theme */
2323
:root {
2424
--jp-brand-color1: var(--md-blue-700);
25+
--jp-error-color1: var(--md-red-700);
2526
--jp-border-width: 1px;
2627
--jp-border-color1: var(--md-grey-400);
2728
--jp-ui-font-size1: 13px;
2829
--md-grey-400: #78909C;
2930
--md-blue-700: #1976D2;
31+
--md-red-700: #d32f2f;
32+
}
33+
34+
form {
35+
display: flex;
36+
align-items: center;
3037
}
3138
</style>

packages/components/src/anchor/anchor.stories.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ export default {
1010
appearance: {
1111
control: 'select',
1212
options: [
13-
'Accent',
14-
'Lightweight',
15-
'Neutral',
16-
'Outline',
17-
'Stealth',
18-
'Hypertext'
13+
'accent',
14+
'lightweight',
15+
'neutral',
16+
'outline',
17+
'stealth',
18+
'hypertext'
1919
]
2020
},
2121
startIcon: { control: 'boolean' },
@@ -26,7 +26,7 @@ export default {
2626
const Template: StoryFn = (args, context): string => {
2727
return `<jp-anchor
2828
href="#"
29-
${args.appearance ? `appearance="${args.appearance.toLowerCase()}` : ''}">
29+
${args.appearance ? `appearance="${args.appearance}` : ''}">
3030
${args.startIcon ? getFaIcon('plus', 'start') : ''}${args.label ?? 'Link'}
3131
${args.endIcon ? getFaIcon('plus', 'end') : ''}
3232
</jp-anchor>`;

packages/components/src/button/button.stories.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ export default {
1919
'Stealth'
2020
]
2121
},
22-
isDisabled: { control: 'boolean' },
23-
isAutoFocused: { control: 'boolean' },
24-
isMinimal: { control: 'boolean' },
22+
disabled: { control: 'boolean' },
23+
autofocus: { control: 'boolean' },
24+
minimal: { control: 'boolean' },
2525
startIcon: { control: 'boolean' },
2626
onClick: {
2727
action: 'clicked',
@@ -42,9 +42,9 @@ const Template: StoryFn = (args): HTMLElement => {
4242
'afterbegin',
4343
`<jp-button
4444
${args.appearance ? `appearance=${args.appearance.toLowerCase()}` : ''}
45-
${args.isDisabled ? 'disabled' : ''}
46-
${args.isAutoFocused ? 'autofocus' : ''}
47-
${args.isMinimal ? 'minimal' : ''}
45+
${args.disabled ? 'disabled' : ''}
46+
${args.autofocus ? 'autofocus' : ''}
47+
${args.minimal ? 'minimal' : ''}
4848
${args.ariaPressed !== 'none' ? `aria-pressed=${args.ariaPressed}` : ''}
4949
>${args.startIcon ? getFaIcon('plus', args.label ? 'start' : null) : ''}${
5050
args.label ?? ''
@@ -62,9 +62,9 @@ export const Accent: StoryObj = { render: Template.bind({}) };
6262
Accent.args = {
6363
label: 'Button Text',
6464
appearance: 'Accent',
65-
isDisabled: false,
66-
isAutoFocused: false,
67-
isMinimal: false,
65+
disabled: false,
66+
autofocus: false,
67+
minimal: false,
6868
startIcon: false,
6969
ariaPressed: 'none',
7070
onClick: action('button-clicked')
@@ -91,13 +91,13 @@ Lightweight.args = {
9191
export const WithAutofocus: StoryObj = { render: Template.bind({}) };
9292
WithAutofocus.args = {
9393
...Accent.args,
94-
isAutoFocused: true
94+
autofocus: true
9595
};
9696

9797
export const WithDisabled: StoryObj = { render: Template.bind({}) };
9898
WithDisabled.args = {
9999
...Accent.args,
100-
isDisabled: true
100+
disabled: true
101101
};
102102

103103
export const WithStartIcon: StoryObj = { render: Template.bind({}) };

packages/components/src/button/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { buttonStyles as styles } from './button.styles.js';
1515
*/
1616
export type ButtonAppearance =
1717
| 'accent'
18+
| 'error'
1819
| 'lightweight'
1920
| 'neutral'
2021
| 'outline'
Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,96 @@
11
// Copyright (c) Jupyter Development Team.
22
// Distributed under the terms of the Modified BSD License.
3-
import type { StoryFn, Meta, StoryObj } from '@storybook/html';
3+
import type { Meta, StoryFn, StoryObj } from '@storybook/html';
44
import { action } from '@storybook/addon-actions';
55
import { Checkbox } from './index';
6+
import { withForm } from '../utilities/storybook';
67

78
export default {
89
title: 'Components/Checkbox',
910
argTypes: {
1011
label: { control: 'text' },
11-
isChecked: { control: 'boolean' },
12-
isDisabled: { control: 'boolean' },
13-
isIndeterminate: { control: 'boolean' },
12+
checked: { control: 'boolean' },
13+
disabled: { control: 'boolean' },
14+
indeterminate: { control: 'boolean' },
15+
errorMessage: { control: 'text' },
16+
inForm: { control: 'boolean' },
1417
onChange: {
1518
action: 'changed',
1619
table: {
1720
disable: true
1821
}
22+
},
23+
onInvalid: {
24+
action: 'invalid',
25+
table: {
26+
disable: true
27+
}
1928
}
20-
}
29+
},
30+
decorators: [withForm]
2131
} as Meta;
2232

2333
const Template: StoryFn = (args): HTMLElement => {
2434
const container = document.createElement('div');
2535
container.insertAdjacentHTML(
2636
'afterbegin',
2737
`<jp-checkbox
28-
${args.isChecked ? 'checked' : ''}
29-
${args.isDisabled ? 'disabled' : ''}
38+
${args.checked ? 'checked' : ''}
39+
${args.disabled ? 'disabled' : ''}
40+
${args.errorMessage ? `error-message="${args.errorMessage}"` : ''}
3041
>
3142
${args.label}
3243
</jp-checkbox>`
3344
);
3445

3546
const checkbox = container.firstChild as Checkbox;
3647

37-
if (args.isIndeterminate) {
48+
if (args.indeterminate) {
3849
checkbox.indeterminate = true;
3950
}
4051

4152
if (args.onChange) {
4253
checkbox.addEventListener('change', args.onChange);
4354
}
55+
if (args.onInvalid) {
56+
checkbox.addEventListener('invalid', args.onInvalid);
57+
}
4458

4559
return checkbox;
4660
};
4761

4862
export const Default: StoryObj = { render: Template.bind({}) };
4963
Default.args = {
5064
label: 'Checkbox',
51-
isChecked: false,
52-
isDisabled: false,
53-
isIndeterminate: false,
54-
onChange: action('checkbox-onchange')
65+
checked: false,
66+
disabled: false,
67+
indeterminate: false,
68+
errorMessage: '',
69+
onChange: action('change'),
70+
onInvalid: action('invalid'),
71+
inForm: false
5572
};
5673

5774
export const WithChecked: StoryObj = { render: Template.bind({}) };
5875
WithChecked.args = {
5976
...Default.args,
60-
isChecked: true
77+
checked: true
6178
};
6279

6380
export const WithDisabled: StoryObj = { render: Template.bind({}) };
6481
WithDisabled.args = {
6582
...Default.args,
66-
isDisabled: true
83+
disabled: true
6784
};
6885

6986
export const WithIndeterminate: StoryObj = { render: Template.bind({}) };
7087
WithIndeterminate.args = {
7188
...Default.args,
72-
isIndeterminate: true
89+
indeterminate: true
90+
};
91+
92+
export const WithError: StoryObj = { render: Template.bind({}) };
93+
WithError.args = {
94+
...Default.args,
95+
errorMessage: 'Invalid checkbox value'
7396
};

packages/components/src/checkbox/checkbox.styles.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import {
2121
controlCornerRadius,
2222
designUnit,
2323
disabledOpacity,
24+
errorFillActive,
25+
errorFillFocus,
26+
errorFillHover,
27+
errorFillRest,
2428
focusStrokeWidth,
2529
foregroundOnAccentActive,
2630
foregroundOnAccentHover,
@@ -68,6 +72,10 @@ export const checkboxStyles: FoundationElementTemplate<
6872
cursor: pointer;
6973
}
7074
75+
:host(.invalid) .control {
76+
border-color: ${errorFillRest};
77+
}
78+
7179
.label {
7280
font-family: ${bodyFont};
7381
color: ${neutralForegroundRest};
@@ -116,11 +124,23 @@ export const checkboxStyles: FoundationElementTemplate<
116124
border-color: ${neutralStrokeActive};
117125
}
118126
127+
:host(.invalid:not([disabled])) .control:hover {
128+
border-color: ${errorFillHover};
129+
}
130+
131+
:host(.invalid:not([disabled])) .control:active {
132+
border-color: ${errorFillActive};
133+
}
134+
119135
:host(:${focusVisible}) .control {
120136
outline: calc(${focusStrokeWidth} * 1px) solid ${accentFillFocus};
121137
outline-offset: 2px;
122138
}
123139
140+
:host(.invalid:${focusVisible}) .control {
141+
outline-color: ${errorFillFocus};
142+
}
143+
124144
:host([aria-checked='true']) .control {
125145
background: ${accentFillRest};
126146
border: calc(${strokeWidth} * 1px) solid ${accentFillRest};
@@ -131,6 +151,16 @@ export const checkboxStyles: FoundationElementTemplate<
131151
border: calc(${strokeWidth} * 1px) solid ${accentFillHover};
132152
}
133153
154+
:host(.invalid[aria-checked='true']) .control {
155+
background-color: ${errorFillRest};
156+
border-color: ${errorFillRest};
157+
}
158+
159+
:host(.invalid[aria-checked='true']:not([disabled])) .control:hover {
160+
background-color: ${errorFillHover};
161+
border-color: ${errorFillHover};
162+
}
163+
134164
:host([aria-checked='true']:not([disabled]))
135165
.control:hover
136166
.checked-indicator {
@@ -148,6 +178,11 @@ export const checkboxStyles: FoundationElementTemplate<
148178
border: calc(${strokeWidth} * 1px) solid ${accentFillActive};
149179
}
150180
181+
:host(.invalid[aria-checked='true']:not([disabled])) .control:active {
182+
background-color: ${errorFillActive};
183+
border-color: ${errorFillActive};
184+
}
185+
151186
:host([aria-checked='true']:not([disabled]))
152187
.control:active
153188
.checked-indicator {
@@ -165,6 +200,10 @@ export const checkboxStyles: FoundationElementTemplate<
165200
outline-offset: 2px;
166201
}
167202
203+
:host(.invalid[aria-checked="true"]:${focusVisible}:not([disabled])) .control {
204+
outline-color: ${errorFillFocus};
205+
}
206+
168207
:host([disabled]) .label,
169208
:host([readonly]) .label,
170209
:host([readonly]) .control,
@@ -187,6 +226,9 @@ export const checkboxStyles: FoundationElementTemplate<
187226
border-color: ${SystemColors.FieldText};
188227
background: ${SystemColors.Field};
189228
}
229+
:host(.invalid) .control {
230+
border-style: dashed;
231+
}
190232
.checked-indicator {
191233
fill: ${SystemColors.FieldText};
192234
}

packages/components/src/checkbox/checkbox.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,11 @@ test('Indeterminate', async ({ page }) => {
3434
'checkbox-indeterminate.png'
3535
);
3636
});
37+
38+
test('Error', async ({ page }) => {
39+
await page.goto('/iframe.html?id=components-checkbox--with-error');
40+
41+
expect(await page.locator('jp-checkbox').screenshot()).toMatchSnapshot(
42+
'checkbox-error.png'
43+
);
44+
});

0 commit comments

Comments
 (0)