@recursica/mantine-adapter 0.10.0 → 0.11.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 +6 -0
- package/dist/mantine-adapter.cjs +1 -1
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.css +1 -1
- package/dist/mantine-adapter.js +1482 -1316
- package/dist/mantine-adapter.js.map +1 -1
- package/dist/src/components/Checkbox/Checkbox.d.ts +3 -1
- package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +21 -0
- package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +2 -0
- package/dist/src/components/Label/Label.d.ts +1 -3
- package/dist/src/components/Radio/Radio.d.ts +3 -1
- package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +6 -6
- package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +7 -1
- package/dist/src/components/ReadOnlyField/index.d.ts +2 -0
- package/dist/src/components/Switch/Switch.d.ts +3 -1
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/utils/RequireAccessibleLabel.d.ts +18 -0
- package/package.json +1 -1
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +6 -4
- package/src/components/Avatar/Avatar.stories.tsx +10 -5
- package/src/components/Badge/Badge.stories.tsx +2 -1
- package/src/components/Button/Button.stories.tsx +2 -4
- package/src/components/Checkbox/Checkbox.module.css +1 -1
- package/src/components/Checkbox/Checkbox.stories.tsx +24 -3
- package/src/components/Checkbox/Checkbox.tsx +72 -15
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +26 -25
- package/src/components/Checkbox/CheckboxGroup.tsx +38 -58
- package/src/components/Container/Container.stories.tsx +6 -3
- package/src/components/DatePicker/DatePicker.stories.tsx +1 -5
- package/src/components/Dropdown/Dropdown.stories.tsx +0 -3
- package/src/components/Dropdown/Dropdown.tsx +6 -5
- package/src/components/FileInput/FileInput.stories.tsx +1 -5
- package/src/components/FileUpload/FileUpload.stories.tsx +1 -5
- package/src/components/Flex/Flex.stories.tsx +6 -3
- package/src/components/FormControlLayout/FormControlLayout.module.css +80 -0
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +82 -0
- package/src/components/FormControlLayout/FormControlLayout.tsx +78 -0
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +3 -41
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +18 -5
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +28 -41
- package/src/components/Group/Group.stories.tsx +6 -3
- package/src/components/Label/Label.module.css +0 -37
- package/src/components/Label/Label.stories.tsx +8 -9
- package/src/components/Label/Label.tsx +2 -7
- package/src/components/Loader/Loader.stories.tsx +10 -5
- package/src/components/NumberInput/NumberInput.stories.tsx +1 -5
- package/src/components/Radio/Radio.stories.tsx +22 -8
- package/src/components/Radio/Radio.tsx +134 -75
- package/src/components/Radio/RadioGroup.stories.tsx +26 -7
- package/src/components/Radio/RadioGroup.tsx +37 -57
- package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +43 -0
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +30 -3
- package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +3 -1
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +10 -2
- package/src/components/ReadOnlyField/index.ts +2 -0
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +1 -5
- package/src/components/Slider/Slider.stories.tsx +1 -5
- package/src/components/Stack/Stack.stories.tsx +6 -3
- package/src/components/Switch/Switch.module.css +2 -10
- package/src/components/Switch/Switch.stories.tsx +42 -27
- package/src/components/Switch/Switch.tsx +138 -89
- package/src/components/Switch/SwitchGroup.stories.tsx +28 -8
- package/src/components/Switch/SwitchGroup.tsx +37 -57
- package/src/components/TextArea/TextArea.stories.tsx +0 -3
- package/src/components/TextArea/TextArea.tsx +6 -5
- package/src/components/TextField/TextField.tsx +6 -5
- package/src/components/TimePicker/TimePicker.stories.tsx +1 -5
- package/src/components/index.ts +1 -0
- package/src/utils/RequireAccessibleLabel.ts +12 -0
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import React, { forwardRef, useId } from "react";
|
|
2
|
-
import { type InputWrapperProps
|
|
2
|
+
import { type InputWrapperProps } from "@mantine/core";
|
|
3
3
|
import {
|
|
4
4
|
filterStylingProps,
|
|
5
5
|
type RecursicaOverStyled,
|
|
6
6
|
} from "../../utils/filterStylingProps";
|
|
7
7
|
import { Label, type RecursicaLabelProps } from "../Label/Label";
|
|
8
|
+
import { FormControlLayout } from "../FormControlLayout/FormControlLayout";
|
|
8
9
|
import { AssistiveElement } from "../AssistiveElement/AssistiveElement";
|
|
9
10
|
import styles from "./FormControlWrapper.module.css";
|
|
10
11
|
|
|
11
12
|
export interface RecursicaFormControlWrapperProps extends RecursicaLabelProps {
|
|
13
|
+
/** Overall structural flow mapping the Form Control natively cascading down to Label and Input logic. */
|
|
14
|
+
formLayout?: "stacked" | "side-by-side";
|
|
12
15
|
/** Securely replaces standard Mantine descriptions safely providing standard Assistive properties. */
|
|
13
16
|
assistiveText?: React.ReactNode;
|
|
14
17
|
/** Explicit toggle to suppress the Info icon rendering natively alongside the assistiveText. Defaults to true. */
|
|
@@ -93,51 +96,35 @@ export const FormControlWrapper = forwardRef<
|
|
|
93
96
|
)
|
|
94
97
|
: children;
|
|
95
98
|
|
|
99
|
+
const labelNode = label ? (
|
|
100
|
+
<Label
|
|
101
|
+
id={id} // Directly binds ARIA references down
|
|
102
|
+
labelAlignment={labelAlignment}
|
|
103
|
+
labelOptionalText={labelOptionalText}
|
|
104
|
+
labelWithEditIcon={labelWithEditIcon}
|
|
105
|
+
labelActionArea={labelActionArea}
|
|
106
|
+
onLabelEditClick={onLabelEditClick}
|
|
107
|
+
required={withAsterisk ?? required}
|
|
108
|
+
// Manually propagate relevant structural logic if underlying groups dictate Label to act as generic wrapper
|
|
109
|
+
{...(labelElement === "div" ? { as: "div" } : {})}
|
|
110
|
+
>
|
|
111
|
+
{label}
|
|
112
|
+
</Label>
|
|
113
|
+
) : undefined;
|
|
114
|
+
|
|
96
115
|
return (
|
|
97
|
-
<
|
|
116
|
+
<FormControlLayout
|
|
98
117
|
ref={ref}
|
|
99
118
|
className={finalClass}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
...(controlMaxWidth
|
|
106
|
-
? { "--form-control-max-width": controlMaxWidth }
|
|
107
|
-
: {}),
|
|
108
|
-
...(controlMinWidth
|
|
109
|
-
? { "--form-control-min-width": controlMinWidth }
|
|
110
|
-
: {}),
|
|
111
|
-
} as React.CSSProperties
|
|
112
|
-
}
|
|
119
|
+
formLayout={formLayout}
|
|
120
|
+
labelSize={labelSize}
|
|
121
|
+
controlMaxWidth={controlMaxWidth}
|
|
122
|
+
controlMinWidth={controlMinWidth}
|
|
123
|
+
leftSection={labelNode}
|
|
113
124
|
{...restRecord}
|
|
114
125
|
>
|
|
115
|
-
{/*
|
|
116
|
-
Section 1: The Native Custom Label
|
|
117
|
-
Bypasses Mantine's built-in Input.Wrapper Label entirely in favor of explicit Recursica formatting.
|
|
118
|
-
*/}
|
|
119
|
-
{label && (
|
|
120
|
-
<div className={styles.labelSection}>
|
|
121
|
-
<Label
|
|
122
|
-
id={id} // Directly binds ARIA references down
|
|
123
|
-
formLayout={formLayout}
|
|
124
|
-
labelSize={labelSize}
|
|
125
|
-
labelAlignment={labelAlignment}
|
|
126
|
-
labelOptionalText={labelOptionalText}
|
|
127
|
-
labelWithEditIcon={labelWithEditIcon}
|
|
128
|
-
labelActionArea={labelActionArea}
|
|
129
|
-
onLabelEditClick={onLabelEditClick}
|
|
130
|
-
required={withAsterisk ?? required}
|
|
131
|
-
// Manually propagate relevant structural logic if underlying groups dictate Label to act as generic wrapper
|
|
132
|
-
{...(labelElement === "div" ? { as: "div" } : {})}
|
|
133
|
-
>
|
|
134
|
-
{label}
|
|
135
|
-
</Label>
|
|
136
|
-
</div>
|
|
137
|
-
)}
|
|
138
|
-
|
|
139
126
|
{/*
|
|
140
|
-
|
|
127
|
+
The Core Input Wrapper & Controls
|
|
141
128
|
Nakedly inject the actual field elements alongside natively bridged Assistive components mapped dynamically.
|
|
142
129
|
*/}
|
|
143
130
|
<div className={styles.inputSection}>
|
|
@@ -163,7 +150,7 @@ export const FormControlWrapper = forwardRef<
|
|
|
163
150
|
</AssistiveElement>
|
|
164
151
|
)}
|
|
165
152
|
</div>
|
|
166
|
-
</
|
|
153
|
+
</FormControlLayout>
|
|
167
154
|
);
|
|
168
155
|
});
|
|
169
156
|
|
|
@@ -81,7 +81,8 @@ export default meta;
|
|
|
81
81
|
type Story = StoryObj<GroupStoryProps>;
|
|
82
82
|
|
|
83
83
|
export const Default: Story = {
|
|
84
|
-
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
85
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
85
86
|
<Group {...args}>
|
|
86
87
|
<Button variant="solid">Primary</Button>
|
|
87
88
|
<Button variant="outline">Secondary</Button>
|
|
@@ -94,7 +95,8 @@ export const StaticGapSmall: Story = {
|
|
|
94
95
|
args: {
|
|
95
96
|
gap: "rec-sm",
|
|
96
97
|
},
|
|
97
|
-
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
99
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
98
100
|
<Group {...args}>
|
|
99
101
|
<Button variant="solid">Item 1</Button>
|
|
100
102
|
<Button variant="solid">Item 2</Button>
|
|
@@ -107,7 +109,8 @@ export const StaticGapLarge: Story = {
|
|
|
107
109
|
args: {
|
|
108
110
|
gap: "rec-xl",
|
|
109
111
|
},
|
|
110
|
-
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
113
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
111
114
|
<Group {...args}>
|
|
112
115
|
<Button variant="solid">Item 1</Button>
|
|
113
116
|
<Button variant="solid">Item 2</Button>
|
|
@@ -23,43 +23,6 @@
|
|
|
23
23
|
text-align: right;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
.root[data-layout="stacked"] {
|
|
27
|
-
padding-bottom: var(
|
|
28
|
-
--recursica_ui-kit_components_label_variants_layouts_stacked_properties_bottom-padding
|
|
29
|
-
);
|
|
30
|
-
min-height: var(
|
|
31
|
-
--recursica_ui-kit_components_label_variants_layouts_stacked_properties_min-height
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.root[data-layout="side-by-side"] {
|
|
36
|
-
padding-top: var(
|
|
37
|
-
--recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_vertical-padding
|
|
38
|
-
);
|
|
39
|
-
padding-bottom: var(
|
|
40
|
-
--recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_vertical-padding
|
|
41
|
-
);
|
|
42
|
-
min-height: var(
|
|
43
|
-
--recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_min-height
|
|
44
|
-
);
|
|
45
|
-
/* In side-by-side, the label exists to the left of its input, so we use the gutter gap here. */
|
|
46
|
-
margin-right: var(
|
|
47
|
-
--recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_gutter
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.root[data-layout="side-by-side"][data-size="default"] {
|
|
52
|
-
width: var(
|
|
53
|
-
--recursica_ui-kit_components_label_variants_layouts_side-by-side_variants_sizes_default_properties_width
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.root[data-layout="side-by-side"][data-size="small"] {
|
|
58
|
-
width: var(
|
|
59
|
-
--recursica_ui-kit_components_label_variants_layouts_side-by-side_variants_sizes_small_properties_width
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
26
|
.labelText {
|
|
64
27
|
order: 1;
|
|
65
28
|
font-family: var(
|
|
@@ -40,7 +40,7 @@ const renderWithTextField = ({ children, ...args }: LabelStoryProps) => (
|
|
|
40
40
|
export const Default: Story = {
|
|
41
41
|
args: {
|
|
42
42
|
children: "Dynamic Label (Controls)",
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
labelSize: "default",
|
|
45
45
|
labelAlignment: "left",
|
|
46
46
|
required: false,
|
|
@@ -53,7 +53,6 @@ export const Default: Story = {
|
|
|
53
53
|
export const StackedDefault: Story = {
|
|
54
54
|
args: {
|
|
55
55
|
children: "Email Address",
|
|
56
|
-
formLayout: "stacked",
|
|
57
56
|
},
|
|
58
57
|
render: renderWithTextField,
|
|
59
58
|
};
|
|
@@ -61,7 +60,7 @@ export const StackedDefault: Story = {
|
|
|
61
60
|
export const StackedRequired: Story = {
|
|
62
61
|
args: {
|
|
63
62
|
children: "Primary Network Node",
|
|
64
|
-
|
|
63
|
+
|
|
65
64
|
required: true,
|
|
66
65
|
},
|
|
67
66
|
render: renderWithTextField,
|
|
@@ -70,7 +69,7 @@ export const StackedRequired: Story = {
|
|
|
70
69
|
export const StackedWithEditIcon: Story = {
|
|
71
70
|
args: {
|
|
72
71
|
children: "Environment Variables",
|
|
73
|
-
|
|
72
|
+
|
|
74
73
|
labelWithEditIcon: true,
|
|
75
74
|
},
|
|
76
75
|
render: renderWithTextField,
|
|
@@ -79,7 +78,7 @@ export const StackedWithEditIcon: Story = {
|
|
|
79
78
|
export const SideBySideDefault: Story = {
|
|
80
79
|
args: {
|
|
81
80
|
children: "Status",
|
|
82
|
-
|
|
81
|
+
|
|
83
82
|
labelSize: "default",
|
|
84
83
|
},
|
|
85
84
|
render: renderWithTextField,
|
|
@@ -88,7 +87,7 @@ export const SideBySideDefault: Story = {
|
|
|
88
87
|
export const RequiredSuppressesOptionalText: Story = {
|
|
89
88
|
args: {
|
|
90
89
|
children: "Full Name",
|
|
91
|
-
|
|
90
|
+
|
|
92
91
|
required: true,
|
|
93
92
|
labelOptionalText: "This should not render",
|
|
94
93
|
},
|
|
@@ -98,7 +97,7 @@ export const RequiredSuppressesOptionalText: Story = {
|
|
|
98
97
|
export const BooleanOptionalText: Story = {
|
|
99
98
|
args: {
|
|
100
99
|
children: "Middle Initial",
|
|
101
|
-
|
|
100
|
+
|
|
102
101
|
labelOptionalText: true,
|
|
103
102
|
},
|
|
104
103
|
render: renderWithTextField,
|
|
@@ -107,7 +106,7 @@ export const BooleanOptionalText: Story = {
|
|
|
107
106
|
export const WithEditIcon: Story = {
|
|
108
107
|
args: {
|
|
109
108
|
children: "Shipping Address",
|
|
110
|
-
|
|
109
|
+
|
|
111
110
|
labelWithEditIcon: true,
|
|
112
111
|
},
|
|
113
112
|
render: renderWithTextField,
|
|
@@ -116,7 +115,7 @@ export const WithEditIcon: Story = {
|
|
|
116
115
|
export const LayerOneSideBySide: Story = {
|
|
117
116
|
args: {
|
|
118
117
|
children: "Configuration",
|
|
119
|
-
|
|
118
|
+
|
|
120
119
|
labelWithEditIcon: true,
|
|
121
120
|
},
|
|
122
121
|
render: renderWithTextField,
|
|
@@ -7,10 +7,8 @@ import {
|
|
|
7
7
|
import styles from "./Label.module.css";
|
|
8
8
|
|
|
9
9
|
export interface RecursicaLabelProps {
|
|
10
|
-
/** Overall structural flow mapping the Form Control natively cascading down to Label and Input logic. */
|
|
11
|
-
formLayout?: "stacked" | "side-by-side";
|
|
12
10
|
/** Specifies the sizing metrics natively mapping the Label boundaries. */
|
|
13
|
-
labelSize?: "default" | "small";
|
|
11
|
+
labelSize?: "default" | "small" | "md";
|
|
14
12
|
/** Overall alignment directive for the label strings natively forcing Left/Right justification. */
|
|
15
13
|
labelAlignment?: "left" | "right";
|
|
16
14
|
/** Injects an indicator text block alongside the label. Can be boolean (`true` maps to '(Optional)') or custom React nodes. */
|
|
@@ -29,7 +27,6 @@ export type LabelProps = RecursicaOverStyled<
|
|
|
29
27
|
|
|
30
28
|
export const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
31
29
|
{
|
|
32
|
-
formLayout = "stacked",
|
|
33
30
|
labelSize = "default",
|
|
34
31
|
labelAlignment,
|
|
35
32
|
required = false,
|
|
@@ -43,8 +40,7 @@ export const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
|
43
40
|
},
|
|
44
41
|
ref,
|
|
45
42
|
) {
|
|
46
|
-
const resolvedAlignment =
|
|
47
|
-
labelAlignment || (formLayout === "side-by-side" ? "right" : "left");
|
|
43
|
+
const resolvedAlignment = labelAlignment || "left";
|
|
48
44
|
|
|
49
45
|
let resolvedOptionalText: React.ReactNode | undefined;
|
|
50
46
|
if (!required) {
|
|
@@ -85,7 +81,6 @@ export const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(
|
|
|
85
81
|
ref={ref}
|
|
86
82
|
className={finalClass}
|
|
87
83
|
classNames={mergedClassNames}
|
|
88
|
-
data-layout={formLayout}
|
|
89
84
|
data-size={labelSize}
|
|
90
85
|
data-alignment={resolvedAlignment}
|
|
91
86
|
required={required && !labelWithEditIcon}
|
|
@@ -54,7 +54,8 @@ export const Default: Story = {
|
|
|
54
54
|
size: "default",
|
|
55
55
|
layer: 0,
|
|
56
56
|
},
|
|
57
|
-
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
58
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
58
59
|
<Layer layer={layer ?? 0} style={{ padding: "24px" }}>
|
|
59
60
|
<Loader {...args} />
|
|
60
61
|
</Layer>
|
|
@@ -66,7 +67,8 @@ export const StaticOvalDefault: Story = {
|
|
|
66
67
|
variant: "oval",
|
|
67
68
|
size: "default",
|
|
68
69
|
},
|
|
69
|
-
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
71
|
+
render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
|
|
70
72
|
};
|
|
71
73
|
|
|
72
74
|
export const StaticBarsLarge: Story = {
|
|
@@ -74,7 +76,8 @@ export const StaticBarsLarge: Story = {
|
|
|
74
76
|
variant: "bars",
|
|
75
77
|
size: "large",
|
|
76
78
|
},
|
|
77
|
-
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
80
|
+
render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
|
|
78
81
|
};
|
|
79
82
|
|
|
80
83
|
export const StaticDotsSmall: Story = {
|
|
@@ -82,7 +85,8 @@ export const StaticDotsSmall: Story = {
|
|
|
82
85
|
variant: "dots",
|
|
83
86
|
size: "sm",
|
|
84
87
|
},
|
|
85
|
-
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
89
|
+
render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
|
|
86
90
|
};
|
|
87
91
|
|
|
88
92
|
export const LayerTwoOval: Story = {
|
|
@@ -90,7 +94,8 @@ export const LayerTwoOval: Story = {
|
|
|
90
94
|
variant: "oval",
|
|
91
95
|
size: "default",
|
|
92
96
|
},
|
|
93
|
-
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
98
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
94
99
|
<Layer layer={2} style={{ padding: "24px" }}>
|
|
95
100
|
<Loader {...args} />
|
|
96
101
|
</Layer>
|
|
@@ -18,9 +18,6 @@ The \`Radio\` component represents a fundamental standalone boolean selection no
|
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
argTypes: {
|
|
21
|
-
disabled: {
|
|
22
|
-
control: "boolean",
|
|
23
|
-
},
|
|
24
21
|
checked: {
|
|
25
22
|
control: "boolean",
|
|
26
23
|
},
|
|
@@ -33,6 +30,8 @@ The \`Radio\` component represents a fundamental standalone boolean selection no
|
|
|
33
30
|
defaultChecked: {
|
|
34
31
|
control: "boolean",
|
|
35
32
|
},
|
|
33
|
+
controlMaxWidth: { table: { disable: true } },
|
|
34
|
+
controlMinWidth: { table: { disable: true } },
|
|
36
35
|
},
|
|
37
36
|
};
|
|
38
37
|
|
|
@@ -46,6 +45,13 @@ export const Default: Story = {
|
|
|
46
45
|
},
|
|
47
46
|
};
|
|
48
47
|
|
|
48
|
+
export const SideBySideLayout: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
label: "Opt-in form alignment",
|
|
51
|
+
formLayout: "side-by-side",
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
49
55
|
export const CheckedState: Story = {
|
|
50
56
|
args: {
|
|
51
57
|
...Default.args,
|
|
@@ -68,9 +74,17 @@ export const DisabledChecked: Story = {
|
|
|
68
74
|
},
|
|
69
75
|
};
|
|
70
76
|
|
|
71
|
-
export const
|
|
72
|
-
args: {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
export const ReadOnly: Story = {
|
|
78
|
+
args: {},
|
|
79
|
+
render: () => (
|
|
80
|
+
<div
|
|
81
|
+
style={{
|
|
82
|
+
display: "flex",
|
|
83
|
+
flexDirection: "column",
|
|
84
|
+
gap: "24px",
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<Radio label="Account Type" defaultChecked readOnly />
|
|
88
|
+
</div>
|
|
89
|
+
),
|
|
76
90
|
};
|
|
@@ -9,6 +9,12 @@ import {
|
|
|
9
9
|
filterStylingProps,
|
|
10
10
|
type RecursicaOverStyled,
|
|
11
11
|
} from "../../utils/filterStylingProps";
|
|
12
|
+
import { type RequireAccessibleLabel } from "../../utils/RequireAccessibleLabel";
|
|
13
|
+
import {
|
|
14
|
+
FormControlLayout,
|
|
15
|
+
type FormControlLayoutProps,
|
|
16
|
+
} from "../FormControlLayout/FormControlLayout";
|
|
17
|
+
|
|
12
18
|
import styles from "./Radio.module.css";
|
|
13
19
|
|
|
14
20
|
const RadioIcon: React.FC<{
|
|
@@ -26,11 +32,17 @@ const RadioIcon: React.FC<{
|
|
|
26
32
|
</svg>
|
|
27
33
|
);
|
|
28
34
|
|
|
29
|
-
export type RecursicaRadioProps =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
export type RecursicaRadioProps = RequireAccessibleLabel<
|
|
36
|
+
Omit<
|
|
37
|
+
MantineRadioProps,
|
|
38
|
+
"size" | "color" | "radius" | "iconColor" | "variant"
|
|
39
|
+
> &
|
|
40
|
+
ReadOnlyControlProps &
|
|
41
|
+
Pick<
|
|
42
|
+
FormControlLayoutProps,
|
|
43
|
+
"formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
|
|
44
|
+
>
|
|
45
|
+
>;
|
|
34
46
|
|
|
35
47
|
export type RadioProps = RecursicaOverStyled<RecursicaRadioProps>;
|
|
36
48
|
|
|
@@ -40,76 +52,123 @@ type RadioComponent = React.ForwardRefExoticComponent<
|
|
|
40
52
|
Group: typeof RadioGroup;
|
|
41
53
|
};
|
|
42
54
|
|
|
43
|
-
export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
: styles.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
55
|
+
export const Radio = forwardRef<HTMLInputElement, RadioProps>(
|
|
56
|
+
function Radio(props, ref) {
|
|
57
|
+
const {
|
|
58
|
+
overStyled = false,
|
|
59
|
+
readOnly,
|
|
60
|
+
readOnlyComponent,
|
|
61
|
+
disabled,
|
|
62
|
+
formLayout,
|
|
63
|
+
labelSize,
|
|
64
|
+
controlMaxWidth,
|
|
65
|
+
controlMinWidth,
|
|
66
|
+
...rest
|
|
67
|
+
} = props;
|
|
68
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
69
|
+
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
70
|
+
|
|
71
|
+
// Actively delete dimension bindings that bypass the abstraction
|
|
72
|
+
delete restRecord["size"];
|
|
73
|
+
delete restRecord["color"];
|
|
74
|
+
delete restRecord["radius"];
|
|
75
|
+
delete restRecord["variant"];
|
|
76
|
+
delete restRecord["iconColor"];
|
|
77
|
+
|
|
78
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
79
|
+
root: styles.root,
|
|
80
|
+
body: styles.body,
|
|
81
|
+
inner: styles.inner,
|
|
82
|
+
radio: styles.radio,
|
|
83
|
+
icon: styles.icon,
|
|
84
|
+
labelWrapper: styles.labelWrapper,
|
|
85
|
+
label: styles.label,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const classNamesProp = restRecord.classNames;
|
|
89
|
+
if (
|
|
90
|
+
classNamesProp &&
|
|
91
|
+
typeof classNamesProp === "object" &&
|
|
92
|
+
!Array.isArray(classNamesProp)
|
|
93
|
+
) {
|
|
94
|
+
const o = classNamesProp as Partial<Record<string, string>>;
|
|
95
|
+
mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
|
|
96
|
+
mergedClassNames.body = o.body ? `${styles.body} ${o.body}` : styles.body;
|
|
97
|
+
mergedClassNames.inner = o.inner
|
|
98
|
+
? `${styles.inner} ${o.inner}`
|
|
99
|
+
: styles.inner;
|
|
100
|
+
mergedClassNames.radio = o.radio
|
|
101
|
+
? `${styles.radio} ${o.radio}`
|
|
102
|
+
: styles.radio;
|
|
103
|
+
mergedClassNames.icon = o.icon ? `${styles.icon} ${o.icon}` : styles.icon;
|
|
104
|
+
mergedClassNames.labelWrapper = o.labelWrapper
|
|
105
|
+
? `${styles.labelWrapper} ${o.labelWrapper}`
|
|
106
|
+
: styles.labelWrapper;
|
|
107
|
+
mergedClassNames.label = o.label
|
|
108
|
+
? `${styles.label} ${o.label}`
|
|
109
|
+
: styles.label;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const classNameProp = restRecord.className as string | undefined;
|
|
113
|
+
const finalClass = classNameProp
|
|
114
|
+
? `${styles.root} ${classNameProp}`
|
|
115
|
+
: styles.root;
|
|
116
|
+
|
|
117
|
+
if (readOnly && !!readOnlyComponent) {
|
|
118
|
+
const isChecked = !!(restRecord.checked ?? restRecord.defaultChecked);
|
|
119
|
+
const ReadOnlyComp = readOnlyComponent;
|
|
120
|
+
const roNode = (
|
|
121
|
+
<ReadOnlyComp
|
|
122
|
+
{...props}
|
|
123
|
+
checked={isChecked}
|
|
124
|
+
label={restRecord.label as React.ReactNode}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (formLayout) {
|
|
129
|
+
return (
|
|
130
|
+
<FormControlLayout
|
|
131
|
+
formLayout={formLayout}
|
|
132
|
+
labelSize={labelSize}
|
|
133
|
+
controlMaxWidth={controlMaxWidth}
|
|
134
|
+
controlMinWidth={controlMinWidth}
|
|
135
|
+
>
|
|
136
|
+
{roNode}
|
|
137
|
+
</FormControlLayout>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return <>{roNode}</>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// We omit Mantine's sizing/coloring so we rely strictly on variables from Radio.module.css
|
|
145
|
+
const radioNode = (
|
|
146
|
+
<MantineRadio
|
|
147
|
+
ref={ref}
|
|
148
|
+
icon={RadioIcon}
|
|
149
|
+
className={finalClass}
|
|
150
|
+
classNames={mergedClassNames}
|
|
151
|
+
disabled={readOnly || disabled}
|
|
152
|
+
{...(sanitizedProps as unknown as MantineRadioProps)}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (formLayout) {
|
|
157
|
+
return (
|
|
158
|
+
<FormControlLayout
|
|
159
|
+
formLayout={formLayout}
|
|
160
|
+
labelSize={labelSize}
|
|
161
|
+
controlMaxWidth={controlMaxWidth}
|
|
162
|
+
controlMinWidth={controlMinWidth}
|
|
163
|
+
>
|
|
164
|
+
{radioNode}
|
|
165
|
+
</FormControlLayout>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return radioNode;
|
|
170
|
+
},
|
|
171
|
+
) as RadioComponent;
|
|
113
172
|
|
|
114
173
|
Radio.displayName = "Radio";
|
|
115
174
|
Radio.Group = RadioGroup;
|