Skip to content

Commit a32d93c

Browse files
committed
WIP inputs
1 parent 2779fc6 commit a32d93c

File tree

20 files changed

+688
-2
lines changed

20 files changed

+688
-2
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { Message } from "@axa-fr/design-system-slash-react";
2+
import {
3+
Canvas,
4+
Controls,
5+
Description,
6+
Meta,
7+
Primary,
8+
Subtitle,
9+
Title,
10+
} from "@storybook/blocks";
11+
import * as Stories from "./TextInput.stories";
12+
13+
<Meta of={Stories} title="Form/Experimental/TextInput" />
14+
15+
<Title />
16+
<Subtitle />
17+
<Description />
18+
19+
## ⚠️ Experimental Component
20+
21+
This component is **experimental** and may change in future releases. You can however use it in your projects, but be aware that the API might change.
22+
We welcome your feedback and contributions to improve this component.
23+
24+
## Playground
25+
26+
<Primary />
27+
<Controls />
28+
29+
## Usage with form libraries
30+
31+
This component can be used with form libraries like [react-hook-form](https://react-hook-form.com/) or [Formik](https://formik.org/). It supports controlled and uncontrolled usage patterns.
32+
33+
You can pass a ref to the component to access the underlying input element, which is useful for form libraries that require direct access to the input.
34+
35+
```tsx
36+
import { TextInput } from "@axa-fr/design-system-slash-react";
37+
import { useForm } from "react-hook-form";
38+
const MyForm = () => {
39+
const { register, handleSubmit } = useForm();
40+
41+
const onSubmit = (data) => {
42+
console.log(data);
43+
};
44+
45+
return (
46+
<form onSubmit={handleSubmit(onSubmit)}>
47+
<TextInput label="My Input" {...register("myInput")} />
48+
<button type="submit">Submit</button>
49+
</form>
50+
);
51+
};
52+
```
53+
54+
## Customization examples
55+
56+
### Disabled Input
57+
58+
Set the `disabled` prop to true to disable the input field.
59+
60+
<Canvas of={Stories.Disabled} />
61+
62+
### Rich Label
63+
64+
If you need to display a label with HTML content, you can pass a ReactNode in the `label` prop.
65+
66+
<Canvas of={Stories.RichLabel} />
67+
68+
### Error Message
69+
70+
If you want to display an error message, you can use the `error` prop.
71+
This will render the error message below the input field, and apply the appropriate styles to indicate an error state. The input will also be marked as `aria-invalid`.
72+
73+
<Canvas of={Stories.Error} />
74+
75+
### With Unit
76+
77+
If you want to display a unit next to the input field, you can use the `rightContent` prop. It can be a string or a ReactNode. This is useful for inputs that require a unit of measurement, like currency or percentage.
78+
79+
It can technically be used to display any content on the right side of the input, such as an icon or a button, however, it is primarily intended for units.
80+
81+
<Canvas of={Stories.WithUnit} />
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import { TextInput } from "@axa-fr/design-system-slash-react/Form/Experimental";
2+
import type { Meta, StoryObj } from "@storybook/react";
3+
import { fn } from "@storybook/test";
4+
5+
const meta: Meta<typeof TextInput> = {
6+
component: TextInput,
7+
title: "Components/Form/Experimental/TextInput",
8+
argTypes: {
9+
onChange: {
10+
action: "onChange",
11+
table: {
12+
disable: true,
13+
},
14+
},
15+
label: {
16+
table: {
17+
category: "Visual Content",
18+
},
19+
},
20+
helpMessage: {
21+
table: {
22+
category: "Visual Content",
23+
},
24+
},
25+
errorMessage: {
26+
table: {
27+
category: "Visual Content",
28+
},
29+
},
30+
placeholder: {
31+
table: {
32+
category: "Visual Content",
33+
},
34+
},
35+
labelPosition: {
36+
table: {
37+
category: "Visual Content",
38+
},
39+
},
40+
contentRight: {
41+
table: {
42+
category: "Visual Content",
43+
},
44+
control: {
45+
type: "text",
46+
},
47+
},
48+
required: {
49+
table: {
50+
category: "Field state",
51+
},
52+
},
53+
disabled: {
54+
table: {
55+
category: "Field state",
56+
},
57+
},
58+
value: {
59+
table: {
60+
category: "Field state",
61+
},
62+
},
63+
id: {
64+
table: {
65+
category: "Technical Details",
66+
},
67+
},
68+
name: {
69+
table: {
70+
category: "Technical Details",
71+
},
72+
},
73+
inputClassName: {
74+
table: {
75+
category: "Technical Details",
76+
},
77+
},
78+
labelClassName: {
79+
table: {
80+
category: "Technical Details",
81+
},
82+
},
83+
containerClassName: {
84+
table: {
85+
category: "Technical Details",
86+
},
87+
},
88+
},
89+
args: {
90+
label: "What is your name?",
91+
helpMessage: "This is your government name",
92+
errorMessage: "",
93+
placeholder: "Your name",
94+
required: true,
95+
disabled: false,
96+
value: "John Doe",
97+
id: "nameid",
98+
name: "myTextInput",
99+
onChange: fn(),
100+
},
101+
};
102+
103+
export default meta;
104+
105+
type Story = StoryObj<typeof TextInput>;
106+
107+
export const Default: Story = {
108+
render: ({ onChange, ...args }) => (
109+
<TextInput onChange={onChange} {...args} />
110+
),
111+
args: {},
112+
};
113+
114+
export const Vertical: Story = {
115+
render: ({ onChange, ...args }) => (
116+
<TextInput onChange={onChange} {...args} />
117+
),
118+
args: {
119+
disabled: true,
120+
labelPosition: "leftAbove",
121+
},
122+
};
123+
124+
export const Error: Story = {
125+
render: ({ onChange, ...args }) => (
126+
<TextInput
127+
onChange={onChange}
128+
{...args}
129+
errorMessage="This field is required"
130+
/>
131+
),
132+
args: {
133+
required: true,
134+
errorMessage: "This field is required",
135+
helpMessage: undefined,
136+
value: "",
137+
name: "errorInput",
138+
},
139+
};
140+
141+
export const Disabled: Story = {
142+
render: ({ onChange, ...args }) => (
143+
<TextInput onChange={onChange} {...args} />
144+
),
145+
args: {
146+
disabled: true,
147+
value: "Disabled input",
148+
name: "disabledInput",
149+
},
150+
};
151+
152+
export const WithUnit: Story = {
153+
render: ({ onChange, ...args }) => (
154+
<TextInput onChange={onChange} {...args} />
155+
),
156+
args: {
157+
label: "Price",
158+
placeholder: "Enter amount",
159+
contentRight: "€",
160+
name: "unitInput",
161+
},
162+
};
163+
164+
export const RichLabel: Story = {
165+
render: ({ onChange, ...args }) => (
166+
<TextInput onChange={onChange} {...args} />
167+
),
168+
args: {
169+
label: (
170+
<span>
171+
Place name <em>optional</em>
172+
</span>
173+
),
174+
name: "richLabelInput",
175+
},
176+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.af-input__helper-text {
2+
display: flex;
3+
margin-top: 8px;
4+
grid-area: helper-message;
5+
align-items: center;
6+
font-size: calc(13rem / 16);
7+
line-height: 16px;
8+
color: var(--help-color);
9+
10+
svg {
11+
width: 16px;
12+
height: 16px;
13+
margin-right: 4px;
14+
fill: currentcolor;
15+
}
16+
}
17+
18+
.af-input__helper-text--error {
19+
grid-area: error-message;
20+
color: var(--error-color);
21+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
.af-input__input {
2+
--color: var(--text-color);
3+
--border-color: var(--input-border-color);
4+
--background-color: var(--white);
5+
6+
padding: 0 0 0 1rem;
7+
border: 1px solid var(--border-color);
8+
grid-area: input;
9+
font-size: 1rem;
10+
line-height: 40px;
11+
color: var(--color);
12+
background-color: var(--background-color);
13+
}
14+
15+
.af-input__input[aria-invalid="true"] {
16+
--color: var(--error-color);
17+
--border-color: var(--error-color);
18+
--background-color: var(--white);
19+
}
20+
21+
.af-input__input:disabled {
22+
--color: var(--disabled-color);
23+
--border-color: var(--disabled-color);
24+
--background-color: var(--gray10);
25+
26+
cursor: not-allowed;
27+
}
28+
29+
.af-input__input:hover:not(:disabled),
30+
.af-input__input:active:not(:disabled) {
31+
--border-color: var(--active-input-border-color);
32+
--color: var(--active-button-border-color);
33+
}
34+
35+
.af-input__input:focus:not(:disabled) {
36+
outline: 2px solid var(--active-input-border-color);
37+
outline-offset: 2px;
38+
39+
--border-color: var(--input-border-color);
40+
--color: var(--active-button-border-color);
41+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.af-input__container {
2+
display: grid;
3+
width: fit-content;
4+
margin-bottom: 3rem;
5+
grid-template-areas:
6+
"label input unit"
7+
". helper-message helper-message"
8+
". error-message error-message";
9+
grid-template-columns: 280px auto auto;
10+
align-items: center;
11+
12+
--label-margin-bottom: 0;
13+
}
14+
15+
.af-input__container--vertical {
16+
--label-margin-bottom: 8px;
17+
18+
grid-template-areas:
19+
"label label"
20+
"input unit"
21+
"helper-message helper-message"
22+
"error-message error-message";
23+
grid-template-columns: 1fr auto;
24+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.af-input__unit {
2+
margin-left: 8px;
3+
grid-area: unit;
4+
font-size: 0.875rem;
5+
line-height: 16px;
6+
color: var(--text-color);
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.af-label {
2+
margin-bottom: var(--label-margin-bottom, 0);
3+
grid-area: label;
4+
font-size: 1rem;
5+
line-height: 20px;
6+
}
7+
8+
.af-label__required {
9+
padding-left: 0.25rem;
10+
color: var(--error-color);
11+
}

slash/css/src/common/tokens.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
--disabled-color: var(--gray40);
6464
--error-color: var(--red30);
6565
--input-border-color: var(--gray50);
66+
--active-input-border-color: var(--axablue80);
6667
--brand-primary: var(--axablue80);
6768
--warning-color: var(--orange40);
6869
--help-color: var(--gray60);

slash/react/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
"./utilities": {
1313
"import": "./dist/utilities.js",
1414
"types": "./dist/utilities.d.ts"
15+
},
16+
"./Form/Experimental": {
17+
"import": "./dist/Form/Experimental/index.js",
18+
"types": "./dist/Form/Experimental/index.d.ts"
1519
}
1620
},
1721
"files": [
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {
2+
ItemMessageHelper,
3+
type ItemMessageHelperProps,
4+
} from "./ItemMessageHelper";
5+
6+
type ErrorMessageProps = Omit<ItemMessageHelperProps, "error">;
7+
8+
export const ErrorMessage = ({ ...props }: ErrorMessageProps) => {
9+
return <ItemMessageHelper error {...props} />;
10+
};

0 commit comments

Comments
 (0)