@simplybusiness/mobius 4.8.7 → 4.9.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 +18 -0
- package/dist/cjs/components/Button/Button.js +8 -18
- package/dist/cjs/components/Button/Button.js.map +1 -1
- package/dist/cjs/components/Checkbox/Checkbox.js +8 -5
- package/dist/cjs/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/cjs/components/Checkbox/CheckboxGroup.js +8 -5
- package/dist/cjs/components/Checkbox/CheckboxGroup.js.map +1 -1
- package/dist/cjs/components/Modal/Modal.js +8 -8
- package/dist/cjs/components/Modal/Modal.js.map +1 -1
- package/dist/cjs/components/NumberField/NumberField.js +8 -8
- package/dist/cjs/components/NumberField/NumberField.js.map +1 -1
- package/dist/cjs/components/Radio/RadioGroup.js +9 -6
- package/dist/cjs/components/Radio/RadioGroup.js.map +1 -1
- package/dist/cjs/components/Select/Select.js +11 -7
- package/dist/cjs/components/Select/Select.js.map +1 -1
- package/dist/cjs/components/TextArea/TextArea.js +6 -7
- package/dist/cjs/components/TextArea/TextArea.js.map +1 -1
- package/dist/cjs/components/TextField/TextField.js +11 -10
- package/dist/cjs/components/TextField/TextField.js.map +1 -1
- package/dist/cjs/hooks/index.js +2 -0
- package/dist/cjs/hooks/index.js.map +1 -1
- package/dist/cjs/hooks/useDeprecationWarning/index.js +20 -0
- package/dist/cjs/hooks/useDeprecationWarning/index.js.map +1 -0
- package/dist/cjs/hooks/useDeprecationWarning/useDeprecationWarning.js +32 -0
- package/dist/cjs/hooks/useDeprecationWarning/useDeprecationWarning.js.map +1 -0
- package/dist/cjs/hooks/useTextField/useTextField.js +1 -1
- package/dist/cjs/hooks/useTextField/useTextField.js.map +1 -1
- package/dist/cjs/hooks/useValidationClasses/index.js +20 -0
- package/dist/cjs/hooks/useValidationClasses/index.js.map +1 -0
- package/dist/cjs/hooks/useValidationClasses/useValidationClasses.js +26 -0
- package/dist/cjs/hooks/useValidationClasses/useValidationClasses.js.map +1 -0
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/components/Button/Button.js +5 -7
- package/dist/esm/components/Button/Button.js.map +1 -1
- package/dist/esm/components/Checkbox/Checkbox.js +8 -5
- package/dist/esm/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/esm/components/Checkbox/CheckboxGroup.js +8 -5
- package/dist/esm/components/Checkbox/CheckboxGroup.js.map +1 -1
- package/dist/esm/components/Checkbox/types.js.map +1 -1
- package/dist/esm/components/Modal/Modal.js +8 -8
- package/dist/esm/components/Modal/Modal.js.map +1 -1
- package/dist/esm/components/NumberField/NumberField.js +9 -9
- package/dist/esm/components/NumberField/NumberField.js.map +1 -1
- package/dist/esm/components/Radio/RadioGroup.js +9 -6
- package/dist/esm/components/Radio/RadioGroup.js.map +1 -1
- package/dist/esm/components/Select/Select.js +11 -7
- package/dist/esm/components/Select/Select.js.map +1 -1
- package/dist/esm/components/TextArea/TextArea.js +7 -8
- package/dist/esm/components/TextArea/TextArea.js.map +1 -1
- package/dist/esm/components/TextField/TextField.js +12 -11
- package/dist/esm/components/TextField/TextField.js.map +1 -1
- package/dist/esm/hooks/index.js +2 -0
- package/dist/esm/hooks/index.js.map +1 -1
- package/dist/esm/hooks/useDeprecationWarning/index.js +3 -0
- package/dist/esm/hooks/useDeprecationWarning/index.js.map +1 -0
- package/dist/esm/hooks/useDeprecationWarning/useDeprecationWarning.js +22 -0
- package/dist/esm/hooks/useDeprecationWarning/useDeprecationWarning.js.map +1 -0
- package/dist/esm/hooks/useTextField/types.js.map +1 -1
- package/dist/esm/hooks/useTextField/useTextField.js +1 -1
- package/dist/esm/hooks/useTextField/useTextField.js.map +1 -1
- package/dist/esm/hooks/useValidationClasses/index.js +3 -0
- package/dist/esm/hooks/useValidationClasses/index.js.map +1 -0
- package/dist/esm/hooks/useValidationClasses/useValidationClasses.js +16 -0
- package/dist/esm/hooks/useValidationClasses/useValidationClasses.js.map +1 -0
- package/dist/types/components/Button/Button.d.ts +0 -1
- package/dist/types/components/Checkbox/types.d.ts +3 -4
- package/dist/types/components/NumberField/NumberField.d.ts +2 -2
- package/dist/types/components/Radio/RadioGroup.d.ts +2 -2
- package/dist/types/components/TextArea/TextArea.d.ts +0 -1
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/hooks/useDeprecationWarning/index.d.ts +1 -0
- package/dist/types/hooks/useDeprecationWarning/useDeprecationWarning.d.ts +2 -0
- package/dist/types/hooks/useDeprecationWarning/useDeprecationWarning.test.d.ts +1 -0
- package/dist/types/hooks/useTextField/types.d.ts +2 -3
- package/dist/types/hooks/useValidationClasses/index.d.ts +1 -0
- package/dist/types/hooks/useValidationClasses/useValidationClasses.d.ts +3 -0
- package/dist/types/hooks/useValidationClasses/useValidationClasses.test.d.ts +1 -0
- package/package.json +18 -18
- package/src/components/Accordion/Accordion.stories.tsx +2 -2
- package/src/components/Button/Button.test.tsx +4 -2
- package/src/components/Button/Button.tsx +5 -13
- package/src/components/Checkbox/Checkbox.stories.tsx +3 -11
- package/src/components/Checkbox/Checkbox.test.tsx +3 -3
- package/src/components/Checkbox/Checkbox.tsx +8 -3
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +9 -12
- package/src/components/Checkbox/CheckboxGroup.test.tsx +2 -5
- package/src/components/Checkbox/CheckboxGroup.tsx +12 -5
- package/src/components/Checkbox/types.ts +3 -4
- package/src/components/Modal/Modal.tsx +9 -16
- package/src/components/NumberField/NumberField.stories.tsx +3 -12
- package/src/components/NumberField/NumberField.tsx +26 -14
- package/src/components/PasswordField/PasswordField.stories.tsx +1 -10
- package/src/components/Radio/Radio.stories.tsx +3 -11
- package/src/components/Radio/Radio.test.tsx +3 -3
- package/src/components/Radio/RadioGroup.tsx +11 -6
- package/src/components/Select/Select.mdx +2 -2
- package/src/components/Select/Select.stories.tsx +4 -11
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/Select/Select.tsx +13 -6
- package/src/components/TextArea/TextArea.stories.tsx +4 -13
- package/src/components/TextArea/TextArea.test.tsx +2 -6
- package/src/components/TextArea/TextArea.tsx +16 -10
- package/src/components/TextField/TextField.stories.tsx +3 -11
- package/src/components/TextField/TextField.test.tsx +5 -13
- package/src/components/TextField/TextField.tsx +22 -11
- package/src/hooks/index.tsx +2 -0
- package/src/hooks/useDeprecationWarning/index.ts +1 -0
- package/src/hooks/useDeprecationWarning/useDeprecationWarning.test.ts +51 -0
- package/src/hooks/useDeprecationWarning/useDeprecationWarning.ts +33 -0
- package/src/hooks/useTextField/types.tsx +2 -2
- package/src/hooks/useTextField/useTextField.test.tsx +7 -9
- package/src/hooks/useTextField/useTextField.tsx +1 -6
- package/src/hooks/useValidationClasses/index.ts +1 -0
- package/src/hooks/useValidationClasses/useValidationClasses.test.ts +85 -0
- package/src/hooks/useValidationClasses/useValidationClasses.ts +23 -0
|
@@ -9,15 +9,6 @@ const meta: Meta<typeof Checkbox> = {
|
|
|
9
9
|
title: "Forms/Checkbox",
|
|
10
10
|
component: Checkbox,
|
|
11
11
|
argTypes: {
|
|
12
|
-
validationState: {
|
|
13
|
-
options: ["valid", "invalid", "neither"],
|
|
14
|
-
control: { type: "radio" },
|
|
15
|
-
mapping: {
|
|
16
|
-
valid: "valid",
|
|
17
|
-
invalid: "invalid",
|
|
18
|
-
neither: "",
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
12
|
label: {
|
|
22
13
|
control: { type: "text" },
|
|
23
14
|
},
|
|
@@ -29,6 +20,7 @@ const meta: Meta<typeof Checkbox> = {
|
|
|
29
20
|
"defaultSelected",
|
|
30
21
|
"value",
|
|
31
22
|
"groupDisabled",
|
|
23
|
+
"validationState",
|
|
32
24
|
),
|
|
33
25
|
},
|
|
34
26
|
decorators: [
|
|
@@ -71,7 +63,7 @@ export const Valid: StoryType = {
|
|
|
71
63
|
value: "agree",
|
|
72
64
|
isDisabled: false,
|
|
73
65
|
errorMessage: "",
|
|
74
|
-
|
|
66
|
+
isInvalid: false,
|
|
75
67
|
defaultSelected: false,
|
|
76
68
|
isRequired: false,
|
|
77
69
|
},
|
|
@@ -83,7 +75,7 @@ export const Invalid: StoryType = {
|
|
|
83
75
|
label: "I agree",
|
|
84
76
|
value: "agree",
|
|
85
77
|
isDisabled: false,
|
|
86
|
-
|
|
78
|
+
isInvalid: true,
|
|
87
79
|
errorMessage: "This is an error message",
|
|
88
80
|
defaultSelected: false,
|
|
89
81
|
isRequired: false,
|
|
@@ -167,7 +167,7 @@ describe("Checkbox", () => {
|
|
|
167
167
|
const optionText = "Agree";
|
|
168
168
|
|
|
169
169
|
const { getByLabelText } = render(
|
|
170
|
-
<Checkbox label={optionText}
|
|
170
|
+
<Checkbox label={optionText} isInvalid={false} value="value" />,
|
|
171
171
|
);
|
|
172
172
|
|
|
173
173
|
const option = getByLabelText(optionText);
|
|
@@ -181,7 +181,7 @@ describe("Checkbox", () => {
|
|
|
181
181
|
const { getByLabelText } = render(
|
|
182
182
|
<Checkbox
|
|
183
183
|
label={optionText}
|
|
184
|
-
|
|
184
|
+
isInvalid
|
|
185
185
|
errorMessage="There is a problem"
|
|
186
186
|
value="value"
|
|
187
187
|
/>,
|
|
@@ -260,7 +260,7 @@ describe("Checkbox", () => {
|
|
|
260
260
|
const optionText = "Agree";
|
|
261
261
|
|
|
262
262
|
const { getByLabelText } = render(
|
|
263
|
-
<Checkbox label={optionText} value="value"
|
|
263
|
+
<Checkbox label={optionText} value="value" isInvalid />,
|
|
264
264
|
);
|
|
265
265
|
|
|
266
266
|
const option = getByLabelText(optionText);
|
|
@@ -14,6 +14,7 @@ import { ErrorMessage } from "../ErrorMessage";
|
|
|
14
14
|
import { ForwardedRefComponent } from "../../types/components";
|
|
15
15
|
import { CheckboxElementType, CheckboxProps, CheckboxRef } from "./types";
|
|
16
16
|
import { spaceDelimitedList } from "../../utils/spaceDelimitedList";
|
|
17
|
+
import { useValidationClasses } from "../../hooks";
|
|
17
18
|
|
|
18
19
|
export const Checkbox: ForwardedRefComponent<
|
|
19
20
|
CheckboxProps,
|
|
@@ -25,6 +26,7 @@ export const Checkbox: ForwardedRefComponent<
|
|
|
25
26
|
isDisabled,
|
|
26
27
|
isRequired,
|
|
27
28
|
validationState,
|
|
29
|
+
isInvalid,
|
|
28
30
|
onChange,
|
|
29
31
|
className,
|
|
30
32
|
errorMessage,
|
|
@@ -38,15 +40,18 @@ export const Checkbox: ForwardedRefComponent<
|
|
|
38
40
|
const fallbackRef = useRef<HTMLInputElement>(null);
|
|
39
41
|
const refObj = ref || fallbackRef;
|
|
40
42
|
const inputId = useId();
|
|
43
|
+
const validationClasses = useValidationClasses({
|
|
44
|
+
validationState,
|
|
45
|
+
isInvalid,
|
|
46
|
+
});
|
|
41
47
|
const checkboxClasses = classNames(
|
|
42
48
|
{
|
|
43
49
|
"--is-disabled": isDisabled,
|
|
44
50
|
"--is-selected": selected,
|
|
45
|
-
"--is-valid": validationState === "valid",
|
|
46
|
-
"--is-invalid": validationState === "invalid",
|
|
47
51
|
"--is-required": typeof isRequired === "boolean" && isRequired,
|
|
48
52
|
"--is-optional": typeof isRequired === "boolean" && !isRequired,
|
|
49
53
|
},
|
|
54
|
+
validationClasses,
|
|
50
55
|
className,
|
|
51
56
|
);
|
|
52
57
|
// Append an additional wrapper class name to differenitate from input
|
|
@@ -94,7 +99,7 @@ export const Checkbox: ForwardedRefComponent<
|
|
|
94
99
|
<input
|
|
95
100
|
aria-describedby={describedBy}
|
|
96
101
|
aria-errormessage={shouldErrorMessageShow}
|
|
97
|
-
aria-invalid={
|
|
102
|
+
aria-invalid={isInvalid}
|
|
98
103
|
readOnly={isReadOnly}
|
|
99
104
|
disabled={isDisabled}
|
|
100
105
|
ref={refObj}
|
|
@@ -9,16 +9,13 @@ const meta: Meta<typeof CheckboxGroup> = {
|
|
|
9
9
|
title: "Forms/CheckboxGroup",
|
|
10
10
|
component: CheckboxGroup,
|
|
11
11
|
argTypes: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
...excludeControls("isRequired", "isReadOnly", "defaultValue", "value"),
|
|
12
|
+
...excludeControls(
|
|
13
|
+
"isRequired",
|
|
14
|
+
"isReadOnly",
|
|
15
|
+
"defaultValue",
|
|
16
|
+
"value",
|
|
17
|
+
"validationState",
|
|
18
|
+
),
|
|
22
19
|
},
|
|
23
20
|
decorators: [
|
|
24
21
|
Story => (
|
|
@@ -96,7 +93,7 @@ export const Valid: StoryType = {
|
|
|
96
93
|
label: "Value",
|
|
97
94
|
errorMessage: "",
|
|
98
95
|
isDisabled: false,
|
|
99
|
-
|
|
96
|
+
isInvalid: false,
|
|
100
97
|
orientation: "vertical",
|
|
101
98
|
isRequired: false,
|
|
102
99
|
},
|
|
@@ -114,7 +111,7 @@ export const Invalid: StoryType = {
|
|
|
114
111
|
label: "Value",
|
|
115
112
|
errorMessage: "This field is required",
|
|
116
113
|
isDisabled: false,
|
|
117
|
-
|
|
114
|
+
isInvalid: true,
|
|
118
115
|
orientation: "vertical",
|
|
119
116
|
isRequired: false,
|
|
120
117
|
},
|
|
@@ -379,7 +379,7 @@ describe("CheckboxGroup", () => {
|
|
|
379
379
|
const optionText = "Agree";
|
|
380
380
|
|
|
381
381
|
const { getByLabelText } = render(
|
|
382
|
-
<CheckboxGroup
|
|
382
|
+
<CheckboxGroup isInvalid={false}>
|
|
383
383
|
<Checkbox label={optionText} value="value" />
|
|
384
384
|
</CheckboxGroup>,
|
|
385
385
|
);
|
|
@@ -393,10 +393,7 @@ describe("CheckboxGroup", () => {
|
|
|
393
393
|
const optionText = "Agree";
|
|
394
394
|
|
|
395
395
|
const { getByLabelText } = render(
|
|
396
|
-
<CheckboxGroup
|
|
397
|
-
validationState="invalid"
|
|
398
|
-
errorMessage="There is a problem"
|
|
399
|
-
>
|
|
396
|
+
<CheckboxGroup isInvalid errorMessage="There is a problem">
|
|
400
397
|
<Checkbox label={optionText} value="value" />,
|
|
401
398
|
</CheckboxGroup>,
|
|
402
399
|
);
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { Label } from "../Label";
|
|
23
23
|
import { ErrorMessage } from "../ErrorMessage";
|
|
24
24
|
import { spaceDelimitedList } from "../../utils/spaceDelimitedList";
|
|
25
|
+
import { useValidationClasses } from "../../hooks";
|
|
25
26
|
|
|
26
27
|
export const CheckboxGroup: ForwardedRefComponent<
|
|
27
28
|
CheckboxGroupProps,
|
|
@@ -32,6 +33,7 @@ export const CheckboxGroup: ForwardedRefComponent<
|
|
|
32
33
|
isDisabled = false,
|
|
33
34
|
isRequired,
|
|
34
35
|
validationState,
|
|
36
|
+
isInvalid,
|
|
35
37
|
orientation = "vertical",
|
|
36
38
|
onChange,
|
|
37
39
|
className,
|
|
@@ -54,11 +56,16 @@ export const CheckboxGroup: ForwardedRefComponent<
|
|
|
54
56
|
"--is-optional": typeof isRequired === "boolean" && !isRequired,
|
|
55
57
|
},
|
|
56
58
|
);
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"--is-disabled": isDisabled,
|
|
59
|
+
const validationClasses = useValidationClasses({
|
|
60
|
+
validationState,
|
|
61
|
+
isInvalid,
|
|
61
62
|
});
|
|
63
|
+
const labelClasses = classNames(
|
|
64
|
+
{
|
|
65
|
+
"--is-disabled": isDisabled,
|
|
66
|
+
},
|
|
67
|
+
validationClasses,
|
|
68
|
+
);
|
|
62
69
|
const errorMessageId = useId();
|
|
63
70
|
const shouldErrorMessageShow = errorMessage ? errorMessageId : undefined;
|
|
64
71
|
const describedBy = spaceDelimitedList([
|
|
@@ -112,7 +119,7 @@ export const CheckboxGroup: ForwardedRefComponent<
|
|
|
112
119
|
isDisabled,
|
|
113
120
|
isRequired,
|
|
114
121
|
isReadOnly,
|
|
115
|
-
|
|
122
|
+
isInvalid,
|
|
116
123
|
defaultSelected: selected.includes(child.props.value),
|
|
117
124
|
onChange: handleChange,
|
|
118
125
|
"aria-describedby": describedBy,
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { ChangeEvent, ReactNode, Ref, RefAttributes } from "react";
|
|
2
2
|
import { DOMProps } from "../../types/dom";
|
|
3
|
+
import { Validation } from "../../types";
|
|
3
4
|
|
|
4
5
|
export type CheckboxElementType = HTMLInputElement;
|
|
5
6
|
export type CheckboxRef = Ref<CheckboxElementType>;
|
|
6
7
|
|
|
7
8
|
export interface CheckboxProps
|
|
8
9
|
extends DOMProps,
|
|
10
|
+
Validation,
|
|
9
11
|
RefAttributes<CheckboxElementType> {
|
|
10
12
|
className?: string;
|
|
11
13
|
// The content to display as the label.
|
|
@@ -18,8 +20,6 @@ export interface CheckboxProps
|
|
|
18
20
|
onChange?: (event: ChangeEvent<CheckboxElementType>) => void;
|
|
19
21
|
// The default value (uncontrolled).
|
|
20
22
|
defaultSelected?: boolean;
|
|
21
|
-
// Whether the input should display its "valid" or "invalid" visual styling.
|
|
22
|
-
validationState?: "valid" | "invalid";
|
|
23
23
|
// Whether the input can be selected but not changed by the user.
|
|
24
24
|
isReadOnly?: boolean;
|
|
25
25
|
// Whether user input is required on the input before form submission.
|
|
@@ -47,6 +47,7 @@ export interface CheckboxProps
|
|
|
47
47
|
export type CheckboxGroupElementType = HTMLDivElement;
|
|
48
48
|
interface CheckboxGroupPropsInternal
|
|
49
49
|
extends DOMProps,
|
|
50
|
+
Validation,
|
|
50
51
|
RefAttributes<CheckboxGroupElementType> {
|
|
51
52
|
children: ReactNode;
|
|
52
53
|
className?: string;
|
|
@@ -63,8 +64,6 @@ interface CheckboxGroupPropsInternal
|
|
|
63
64
|
// "aria-errormessage"?: string;
|
|
64
65
|
// Identifies the element (or elements) that describes the object.
|
|
65
66
|
"aria-describedby"?: string;
|
|
66
|
-
// Whether the input should display its "valid" or "invalid" visual styling.
|
|
67
|
-
validationState?: "valid" | "invalid";
|
|
68
67
|
// Whether user input is required on the input before form submission.
|
|
69
68
|
isRequired?: boolean;
|
|
70
69
|
// Whether the input is disabled.
|
|
@@ -7,6 +7,7 @@ import { ModalProps } from "./types";
|
|
|
7
7
|
import { mergeRefs } from "../../utils";
|
|
8
8
|
import { ModalContext } from "./ModalContext";
|
|
9
9
|
import { useDialog } from "../../hooks";
|
|
10
|
+
import { useDeprecationWarning } from "../../hooks/useDeprecationWarning";
|
|
10
11
|
|
|
11
12
|
export type ModalElementType = HTMLDialogElement;
|
|
12
13
|
export type ModalRef = Ref<ModalElementType>;
|
|
@@ -30,22 +31,14 @@ const Modal = forwardRef((props: ModalProps, ref: ModalRef) => {
|
|
|
30
31
|
shouldFocusAfterRender,
|
|
31
32
|
parentSelector,
|
|
32
33
|
} = props;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
parentSelector
|
|
42
|
-
) {
|
|
43
|
-
console.warn(
|
|
44
|
-
`Deprecation warning: Mobius Modal no longer supports the following props: size, appElement, preventCloseOnEsc, shouldFocusAfterRender and parentSelector.`,
|
|
45
|
-
);
|
|
46
|
-
hasWarnedAboutMissingLabels.current = true;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
34
|
+
|
|
35
|
+
useDeprecationWarning({
|
|
36
|
+
size,
|
|
37
|
+
appElement,
|
|
38
|
+
preventCloseOnEsc,
|
|
39
|
+
shouldFocusAfterRender,
|
|
40
|
+
parentSelector,
|
|
41
|
+
});
|
|
49
42
|
|
|
50
43
|
const shouldTransition = supportsDialog();
|
|
51
44
|
const dialogRef = useRef<HTMLDialogElement | null>(null);
|
|
@@ -9,15 +9,6 @@ const meta: Meta<typeof NumberField> = {
|
|
|
9
9
|
title: "Forms/NumberField",
|
|
10
10
|
component: NumberField,
|
|
11
11
|
argTypes: {
|
|
12
|
-
validationState: {
|
|
13
|
-
options: ["valid", "invalid", "neither"],
|
|
14
|
-
control: { type: "radio" },
|
|
15
|
-
mapping: {
|
|
16
|
-
valid: "valid",
|
|
17
|
-
invalid: "invalid",
|
|
18
|
-
neither: "",
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
12
|
minValue: {
|
|
22
13
|
control: {
|
|
23
14
|
control: { type: "range" },
|
|
@@ -34,7 +25,7 @@ const meta: Meta<typeof NumberField> = {
|
|
|
34
25
|
step: 1,
|
|
35
26
|
},
|
|
36
27
|
},
|
|
37
|
-
...excludeControls("value"),
|
|
28
|
+
...excludeControls("value", "validationState"),
|
|
38
29
|
},
|
|
39
30
|
args: {
|
|
40
31
|
minValue: 0,
|
|
@@ -80,7 +71,7 @@ export const Valid: StoryType = {
|
|
|
80
71
|
render: (args: NumberFieldProps) => <NumberField {...args} />,
|
|
81
72
|
args: {
|
|
82
73
|
label: "Number field",
|
|
83
|
-
|
|
74
|
+
isInvalid: false,
|
|
84
75
|
defaultValue: 0,
|
|
85
76
|
errorMessage: "",
|
|
86
77
|
step: 1,
|
|
@@ -92,7 +83,7 @@ export const Invalid: StoryType = {
|
|
|
92
83
|
render: (args: NumberFieldProps) => <NumberField {...args} />,
|
|
93
84
|
args: {
|
|
94
85
|
label: "Number field",
|
|
95
|
-
|
|
86
|
+
isInvalid: true,
|
|
96
87
|
errorMessage: "Error message",
|
|
97
88
|
defaultValue: 0,
|
|
98
89
|
step: 1,
|
|
@@ -12,22 +12,23 @@ import {
|
|
|
12
12
|
useState,
|
|
13
13
|
} from "react";
|
|
14
14
|
import { DOMProps } from "../../types/dom";
|
|
15
|
-
import { UseLabelProps, useLabel } from "../../hooks";
|
|
15
|
+
import { UseLabelProps, useLabel, useValidationClasses } from "../../hooks";
|
|
16
16
|
import { ForwardedRefComponent } from "../../types/components";
|
|
17
17
|
import { spaceDelimitedList } from "../../utils/spaceDelimitedList";
|
|
18
18
|
import { ErrorMessage } from "../ErrorMessage";
|
|
19
19
|
import { Label } from "../Label";
|
|
20
|
+
import { Validation } from "../../types";
|
|
20
21
|
|
|
21
22
|
export type NumberFieldElementType = HTMLInputElement;
|
|
22
23
|
|
|
23
24
|
export interface NumberFieldProps
|
|
24
25
|
extends DOMProps,
|
|
26
|
+
Validation,
|
|
25
27
|
UseLabelProps,
|
|
26
28
|
RefAttributes<NumberFieldElementType> {
|
|
27
29
|
id?: string;
|
|
28
30
|
/** Custom class name for setting specific CSS */
|
|
29
31
|
className?: string;
|
|
30
|
-
validationState?: "valid" | "invalid" | undefined;
|
|
31
32
|
/** The smallest value allowed for the input. */
|
|
32
33
|
minValue?: number;
|
|
33
34
|
/** The largest value allowed for the input. */
|
|
@@ -69,6 +70,7 @@ export const NumberField: ForwardedRefComponent<
|
|
|
69
70
|
className,
|
|
70
71
|
errorMessage,
|
|
71
72
|
validationState,
|
|
73
|
+
isInvalid,
|
|
72
74
|
isReadOnly,
|
|
73
75
|
minValue,
|
|
74
76
|
maxValue,
|
|
@@ -94,19 +96,29 @@ export const NumberField: ForwardedRefComponent<
|
|
|
94
96
|
"--is-optional": optional,
|
|
95
97
|
});
|
|
96
98
|
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"--is-valid": validationState === "valid",
|
|
101
|
-
"--is-invalid": validationState === "invalid",
|
|
102
|
-
"--is-disabled": isDisabled,
|
|
99
|
+
const validationClasses = useValidationClasses({
|
|
100
|
+
validationState,
|
|
101
|
+
isInvalid,
|
|
103
102
|
});
|
|
104
103
|
|
|
105
|
-
const
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
const inputClasses = classNames(
|
|
105
|
+
"mobius/NumberFieldInput",
|
|
106
|
+
className,
|
|
107
|
+
{
|
|
108
|
+
"--is-required": required,
|
|
109
|
+
"--is-optional": optional,
|
|
110
|
+
"--is-disabled": isDisabled,
|
|
111
|
+
},
|
|
112
|
+
validationClasses,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const labelClasses = classNames(
|
|
116
|
+
"mobius/NumberFieldLabel",
|
|
117
|
+
{
|
|
118
|
+
"--is-disabled": isDisabled,
|
|
119
|
+
},
|
|
120
|
+
validationClasses,
|
|
121
|
+
);
|
|
110
122
|
|
|
111
123
|
const errorMessageId = useId();
|
|
112
124
|
const shouldErrorMessageShow = errorMessage ? errorMessageId : undefined;
|
|
@@ -165,7 +177,7 @@ export const NumberField: ForwardedRefComponent<
|
|
|
165
177
|
required={required}
|
|
166
178
|
aria-describedby={describedBy}
|
|
167
179
|
aria-errormessage={shouldErrorMessageShow}
|
|
168
|
-
aria-invalid={
|
|
180
|
+
aria-invalid={isInvalid}
|
|
169
181
|
readOnly={isReadOnly}
|
|
170
182
|
disabled={isDisabled}
|
|
171
183
|
autoComplete="off"
|
|
@@ -9,20 +9,11 @@ const meta: Meta<typeof PasswordField> = {
|
|
|
9
9
|
title: "Forms/PasswordField",
|
|
10
10
|
component: PasswordField,
|
|
11
11
|
argTypes: {
|
|
12
|
-
validationState: {
|
|
13
|
-
options: ["valid", "invalid", "neither"],
|
|
14
|
-
control: { type: "radio" },
|
|
15
|
-
mapping: {
|
|
16
|
-
valid: "valid",
|
|
17
|
-
invalid: "invalid",
|
|
18
|
-
neither: "",
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
12
|
type: {
|
|
22
13
|
control: { type: "radio" },
|
|
23
14
|
options: ["text", "password"],
|
|
24
15
|
},
|
|
25
|
-
...excludeControls("description"),
|
|
16
|
+
...excludeControls("description", "validationState"),
|
|
26
17
|
},
|
|
27
18
|
args: {
|
|
28
19
|
isReadOnly: false,
|
|
@@ -12,15 +12,6 @@ const meta: Meta<typeof RadioGroup> = {
|
|
|
12
12
|
title: "Forms/Radio",
|
|
13
13
|
component: RadioGroup,
|
|
14
14
|
argTypes: {
|
|
15
|
-
validationState: {
|
|
16
|
-
options: ["valid", "invalid", "neither"],
|
|
17
|
-
control: { type: "radio" },
|
|
18
|
-
mapping: {
|
|
19
|
-
valid: "valid",
|
|
20
|
-
invalid: "invalid",
|
|
21
|
-
neither: "",
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
15
|
orientation: {
|
|
25
16
|
control: { type: "radio" },
|
|
26
17
|
options: ["horizontal", "vertical"],
|
|
@@ -35,6 +26,7 @@ const meta: Meta<typeof RadioGroup> = {
|
|
|
35
26
|
"setSelected",
|
|
36
27
|
"defaultValue",
|
|
37
28
|
"value",
|
|
29
|
+
"validationState",
|
|
38
30
|
),
|
|
39
31
|
},
|
|
40
32
|
decorators: [
|
|
@@ -107,7 +99,7 @@ export const Valid: StoryType = {
|
|
|
107
99
|
isDisabled: false,
|
|
108
100
|
isRequired: false,
|
|
109
101
|
errorMessage: "",
|
|
110
|
-
|
|
102
|
+
isInvalid: false,
|
|
111
103
|
orientation: "vertical",
|
|
112
104
|
},
|
|
113
105
|
};
|
|
@@ -124,7 +116,7 @@ export const Invalid: StoryType = {
|
|
|
124
116
|
isDisabled: false,
|
|
125
117
|
isRequired: false,
|
|
126
118
|
errorMessage: "Please select a color",
|
|
127
|
-
|
|
119
|
+
isInvalid: true,
|
|
128
120
|
orientation: "vertical",
|
|
129
121
|
},
|
|
130
122
|
};
|
|
@@ -375,7 +375,7 @@ describe("Radio", () => {
|
|
|
375
375
|
|
|
376
376
|
it("sets classes for valid state", () => {
|
|
377
377
|
const { container } = render(
|
|
378
|
-
<RadioGroup label="Color"
|
|
378
|
+
<RadioGroup label="Color" isInvalid={false}>
|
|
379
379
|
<Radio value="red">Red</Radio>
|
|
380
380
|
<Radio value="blue">Blue</Radio>
|
|
381
381
|
</RadioGroup>,
|
|
@@ -386,7 +386,7 @@ describe("Radio", () => {
|
|
|
386
386
|
|
|
387
387
|
it("sets classes for invalid state", () => {
|
|
388
388
|
const { container } = render(
|
|
389
|
-
<RadioGroup label="Color"
|
|
389
|
+
<RadioGroup label="Color" isInvalid>
|
|
390
390
|
<Radio value="red">Red</Radio>
|
|
391
391
|
<Radio value="blue">Blue</Radio>
|
|
392
392
|
</RadioGroup>,
|
|
@@ -448,7 +448,7 @@ describe("Radio", () => {
|
|
|
448
448
|
describe("given an invalid state", () => {
|
|
449
449
|
it("renders aria-invalid attribute on container", () => {
|
|
450
450
|
const { container } = render(
|
|
451
|
-
<RadioGroup label="Color"
|
|
451
|
+
<RadioGroup label="Color" isInvalid>
|
|
452
452
|
<Radio value="red">Red</Radio>
|
|
453
453
|
<Radio value="blue">Blue</Radio>
|
|
454
454
|
</RadioGroup>,
|
|
@@ -19,11 +19,14 @@ import { HTMLElementEvent } from "../../types/events";
|
|
|
19
19
|
import { spaceDelimitedList } from "../../utils/spaceDelimitedList";
|
|
20
20
|
import { ErrorMessage } from "../ErrorMessage";
|
|
21
21
|
import { Label } from "../Label";
|
|
22
|
+
import { Validation } from "../../types";
|
|
23
|
+
import { useValidationClasses } from "../../hooks";
|
|
22
24
|
|
|
23
25
|
export type RadioGroupElementType = HTMLDivElement;
|
|
24
26
|
|
|
25
27
|
export interface RadioGroupProps
|
|
26
28
|
extends DOMProps,
|
|
29
|
+
Validation,
|
|
27
30
|
RefAttributes<RadioGroupElementType> {
|
|
28
31
|
children: ReactNode;
|
|
29
32
|
className?: string;
|
|
@@ -39,8 +42,6 @@ export interface RadioGroupProps
|
|
|
39
42
|
"aria-errormessage"?: string;
|
|
40
43
|
// Identifies the element (or elements) that describes the object.
|
|
41
44
|
"aria-describedby"?: string;
|
|
42
|
-
// Whether the input should display its "valid" or "invalid" visual styling.
|
|
43
|
-
validationState?: "valid" | "invalid";
|
|
44
45
|
// Whether user input is required on the input before form submission.
|
|
45
46
|
isRequired?: boolean;
|
|
46
47
|
// Whether the input is disabled.
|
|
@@ -82,6 +83,7 @@ const RadioGroup: ForwardedRefComponent<
|
|
|
82
83
|
isDisabled = false,
|
|
83
84
|
isRequired,
|
|
84
85
|
validationState,
|
|
86
|
+
isInvalid,
|
|
85
87
|
orientation = "vertical",
|
|
86
88
|
className,
|
|
87
89
|
errorMessage,
|
|
@@ -94,10 +96,12 @@ const RadioGroup: ForwardedRefComponent<
|
|
|
94
96
|
} = props;
|
|
95
97
|
const defaultSelected = getDefaultVal(children, defaultValue);
|
|
96
98
|
const [selected, setSelected] = useState<string>(defaultSelected);
|
|
99
|
+
const validationClasses = useValidationClasses({
|
|
100
|
+
validationState,
|
|
101
|
+
isInvalid,
|
|
102
|
+
});
|
|
97
103
|
const radioClasses = {
|
|
98
104
|
"--is-disabled": isDisabled,
|
|
99
|
-
"--is-valid": validationState === "valid",
|
|
100
|
-
"--is-invalid": validationState === "invalid",
|
|
101
105
|
"--is-required": typeof isRequired === "boolean" && isRequired,
|
|
102
106
|
"--is-optional": typeof isRequired === "boolean" && !isRequired,
|
|
103
107
|
[`--is-${orientation}`]: true,
|
|
@@ -107,11 +111,12 @@ const RadioGroup: ForwardedRefComponent<
|
|
|
107
111
|
"mobius",
|
|
108
112
|
"mobius/RadioGroup",
|
|
109
113
|
radioClasses,
|
|
114
|
+
validationClasses,
|
|
110
115
|
);
|
|
111
116
|
const radioWrapperClasses = classNames("mobius/RadioWrapper", {
|
|
112
117
|
[`--is-${orientation}`]: true,
|
|
113
118
|
});
|
|
114
|
-
const labelClasses = classNames(radioClasses);
|
|
119
|
+
const labelClasses = classNames(radioClasses, validationClasses);
|
|
115
120
|
const errorMessageId = useId();
|
|
116
121
|
const defaultNameAttrId = useId();
|
|
117
122
|
const nameAttribute = name || defaultNameAttrId;
|
|
@@ -128,7 +133,7 @@ const RadioGroup: ForwardedRefComponent<
|
|
|
128
133
|
aria-describedby={props["aria-describedby"]}
|
|
129
134
|
aria-disabled={isDisabled}
|
|
130
135
|
aria-errormessage={shouldErrorMessageShow}
|
|
131
|
-
aria-invalid={
|
|
136
|
+
aria-invalid={isInvalid}
|
|
132
137
|
aria-label={props["aria-label"]}
|
|
133
138
|
aria-labelledby={props["aria-labelledby"] || labelId}
|
|
134
139
|
aria-orientation={orientation}
|
|
@@ -106,7 +106,7 @@ import { Select, Option } from "@simplybusiness/mobius";
|
|
|
106
106
|
name="color"
|
|
107
107
|
label="Colors"
|
|
108
108
|
defaultValue="green"
|
|
109
|
-
|
|
109
|
+
isInvalid={false}
|
|
110
110
|
>
|
|
111
111
|
<Option key="none" value="" isDisabled>
|
|
112
112
|
Please select a color
|
|
@@ -135,7 +135,7 @@ import { Select, Option } from "@simplybusiness/mobius";
|
|
|
135
135
|
name="color"
|
|
136
136
|
label="Colors"
|
|
137
137
|
defaultValue="green"
|
|
138
|
-
|
|
138
|
+
isInvalid
|
|
139
139
|
errorMessage="Unacceptable color"
|
|
140
140
|
>
|
|
141
141
|
<Option key="none" value="" isDisabled>
|
|
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react";
|
|
|
2
2
|
import { StoryContainer } from "../../utils/StoryContainer";
|
|
3
3
|
import { Option } from "../Option";
|
|
4
4
|
import { Select, SelectProps } from "./Select";
|
|
5
|
+
import { excludeControls } from "../../utils";
|
|
5
6
|
|
|
6
7
|
type StoryType = StoryObj<typeof Select>;
|
|
7
8
|
|
|
@@ -9,19 +10,11 @@ const meta: Meta<typeof Select> = {
|
|
|
9
10
|
title: "Forms/Select",
|
|
10
11
|
component: Select,
|
|
11
12
|
argTypes: {
|
|
12
|
-
validationState: {
|
|
13
|
-
options: ["valid", "invalid", "neither"],
|
|
14
|
-
control: { type: "radio" },
|
|
15
|
-
mapping: {
|
|
16
|
-
valid: "valid",
|
|
17
|
-
invalid: "invalid",
|
|
18
|
-
neither: "",
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
13
|
value: {
|
|
22
14
|
control: { type: "select" },
|
|
23
15
|
options: ["", "red", "blue", "green"],
|
|
24
16
|
},
|
|
17
|
+
...excludeControls("validationState"),
|
|
25
18
|
},
|
|
26
19
|
decorators: [
|
|
27
20
|
Story => (
|
|
@@ -135,7 +128,7 @@ export const Valid: StoryType = {
|
|
|
135
128
|
name: "color",
|
|
136
129
|
label: "Colors",
|
|
137
130
|
defaultValue: "green",
|
|
138
|
-
|
|
131
|
+
isInvalid: false,
|
|
139
132
|
errorMessage: "",
|
|
140
133
|
isDisabled: false,
|
|
141
134
|
isRequired: false,
|
|
@@ -163,7 +156,7 @@ export const Invalid: StoryType = {
|
|
|
163
156
|
name: "color",
|
|
164
157
|
label: "Colors",
|
|
165
158
|
defaultValue: "green",
|
|
166
|
-
|
|
159
|
+
isInvalid: true,
|
|
167
160
|
errorMessage: "Unacceptable color",
|
|
168
161
|
isDisabled: false,
|
|
169
162
|
isRequired: false,
|
|
@@ -204,7 +204,7 @@ describe("Select", () => {
|
|
|
204
204
|
|
|
205
205
|
it("includes valid state class name", () => {
|
|
206
206
|
render(
|
|
207
|
-
<Select label="Color" data-testid="test"
|
|
207
|
+
<Select label="Color" data-testid="test" isInvalid={false}>
|
|
208
208
|
<Option>Red</Option>
|
|
209
209
|
<Option>Blue</Option>
|
|
210
210
|
</Select>,
|