@kroo-web/design-system 1.0.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.
@@ -0,0 +1,19 @@
1
+ /** @type { import('@storybook/react-vite').StorybookConfig } */
2
+ const config = {
3
+ stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
4
+ addons: [
5
+ "@storybook/addon-links",
6
+ "@storybook/addon-essentials",
7
+ "@storybook/addon-onboarding",
8
+ "@storybook/addon-interactions",
9
+ "@storybook/addon-docs"
10
+ ],
11
+ framework: {
12
+ name: "@storybook/react-vite",
13
+ options: {},
14
+ },
15
+ docs: {
16
+ autodocs: "tag",
17
+ },
18
+ };
19
+ export default config;
@@ -0,0 +1,8 @@
1
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3
+ <link
4
+ href="https://fonts.googleapis.com/css2?family=Brygada+1918:ital,wght@0,400;0,500;0,700;1,500&family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"
5
+ rel="stylesheet"
6
+ crossorigin="anonymous"
7
+ />
8
+
@@ -0,0 +1,21 @@
1
+ /** @type { import('@storybook/react').Preview } */
2
+ import "../src/styles/global.css";
3
+ const preview = {
4
+ parameters: {
5
+ docs: {
6
+ toc: {
7
+ headingSelector: "h2, h3, h4, h5, h6",
8
+ title: "Contents"
9
+ },
10
+ },
11
+ actions: { argTypesRegex: "^on[A-Z].*" },
12
+ controls: {
13
+ matchers: {
14
+ color: /(background|color)$/i,
15
+ date: /Date$/i,
16
+ },
17
+ },
18
+ },
19
+ };
20
+
21
+ export default preview;
package/README.MD ADDED
@@ -0,0 +1,17 @@
1
+ # Kroo Web Component Lib / Design system
2
+
3
+ ## Usage
4
+
5
+ ### Install
6
+
7
+ ```bash
8
+ npm install @kroo-web/design-system -S
9
+ ```
10
+ or
11
+ ```bash
12
+ yarn add @kroo-web/design-system -S
13
+ ```
14
+ or
15
+ ```bash
16
+ pnpm add @kroo-web/design-system -S
17
+ ```
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@kroo-web/design-system",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "src/index.ts",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "storybook": "storybook dev -p 6006",
9
+ "build-storybook": "storybook build",
10
+ "deploy": "npm publish --access public"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "devDependencies": {
16
+ "@storybook/addon-essentials": "^7.6.17",
17
+ "@storybook/addon-interactions": "^7.6.17",
18
+ "@storybook/addon-links": "^7.6.17",
19
+ "@storybook/addon-onboarding": "^1.0.11",
20
+ "@storybook/blocks": "^7.6.17",
21
+ "@storybook/react": "^7.6.17",
22
+ "@storybook/react-vite": "^7.6.17",
23
+ "@storybook/test": "^7.6.17",
24
+ "prop-types": "^15.8.1",
25
+ "react": "^18.2.0",
26
+ "react-dom": "^18.2.0",
27
+ "storybook": "^7.6.17"
28
+ },
29
+ "dependencies": {
30
+ "@storybook/addon-docs": "^7.6.17",
31
+ "clsx": "^2.1.0",
32
+ "framer-motion": "^11.0.8",
33
+ "react-hook-form": "^7.51.0"
34
+ }
35
+ }
@@ -0,0 +1,29 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ export function useWindowSize() {
4
+ const [windowSize, setWindowSize] = useState<{
5
+ width: number;
6
+ height?: number;
7
+ }>({
8
+ width: 600,
9
+ height: undefined,
10
+ });
11
+
12
+ useEffect(() => {
13
+ // Handler to call on window resize
14
+ function handleResize() {
15
+ // Set window width/height to state
16
+ setWindowSize({
17
+ width: window.innerWidth || 600,
18
+ height: window.innerHeight,
19
+ });
20
+ }
21
+ // Add event listener
22
+ window.addEventListener('resize', handleResize, { passive: true });
23
+ // Call handler right away so state gets updated with initial window size
24
+ handleResize();
25
+ // Remove event listener on cleanup
26
+ return () => window.removeEventListener('resize', handleResize);
27
+ }, []); // Empty array ensures that effect is only run on mount
28
+ return windowSize;
29
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+
2
+ // ? All of the Product components
3
+ export * from "./product";
File without changes
@@ -0,0 +1,126 @@
1
+ import React, {
2
+ DetailedHTMLProps,
3
+ InputHTMLAttributes,
4
+ ReactNode,
5
+ useState,
6
+ } from 'react';
7
+ import { motion } from 'framer-motion';
8
+ import clsx from 'clsx';
9
+ import { FieldValues, Path, UseFormRegister } from 'react-hook-form';
10
+ import { useWindowSize } from '../../../hooks/useWindowSize';
11
+ import styles from "./textField.module.css"
12
+
13
+ export type TTextFieldProps<T extends FieldValues> = {
14
+ id: string;
15
+ label: string;
16
+ name: Path<T>;
17
+ disabled?: boolean;
18
+ helper?: {
19
+ message: ReactNode | string;
20
+ };
21
+ error?: {
22
+ message: ReactNode | string;
23
+ };
24
+ /** React Hook form requirement if you dont pass it in the component will act like a normal uncontrolled input */
25
+ register?: UseFormRegister<T>;
26
+ type?: DetailedHTMLProps<
27
+ InputHTMLAttributes<HTMLInputElement>,
28
+ HTMLInputElement
29
+ >['type'];
30
+ prefix?: string | ReactNode;
31
+ suffix?: string | ReactNode;
32
+ value?: string;
33
+ className?: string;
34
+ rightContent?: ReactNode | string;
35
+ leftContent?: ReactNode | string;
36
+ };
37
+
38
+ export const TextField = <T extends FieldValues>({
39
+ id,
40
+ label,
41
+ helper,
42
+ error,
43
+ register,
44
+ name,
45
+ type,
46
+ prefix,
47
+ suffix,
48
+ disabled,
49
+ value,
50
+ className,
51
+ rightContent,
52
+ leftContent,
53
+ }: TTextFieldProps<T>) => {
54
+ const { width } = useWindowSize();
55
+ const [hasValue, setHasValue] = useState(value || false);
56
+ const isDesktop = width > 768;
57
+
58
+ const variants = {
59
+ focused: {
60
+ fontSize: isDesktop ? '1rem' : '0.875rem',
61
+ top: '1rem',
62
+ },
63
+ notFocused: {
64
+ transform: 'translateY(-50%)',
65
+ fontSize: isDesktop ? '1.25rem' : '1.125rem',
66
+ top: '50%',
67
+ },
68
+ };
69
+
70
+ return (
71
+ <div className={styles.outer}>
72
+ <div
73
+ className={clsx(styles.container, error && styles['container--error'])}
74
+ >
75
+ <motion.label
76
+ className={styles.label}
77
+ initial="notFocused"
78
+ variants={variants}
79
+ htmlFor={`input-${id}`}
80
+ animate={hasValue ? 'focused' : 'notFocused'}
81
+ >
82
+ {prefix && <span>{prefix}</span>} {label}{' '}
83
+ {suffix && <span>{suffix}</span>}
84
+ </motion.label>
85
+ <input
86
+ className={clsx(styles[`input--${className}`], styles.input)}
87
+ id={`input-${id}`}
88
+ type={type}
89
+ aria-describedby={`error-${id}`}
90
+ disabled={disabled}
91
+ {...(register && register(name),
92
+ {
93
+ onFocus: () => {
94
+ setHasValue(true);
95
+ },
96
+
97
+ onBlur: (e) => {
98
+ setHasValue(!!e.target.value);
99
+ },
100
+ })}
101
+ value={value}
102
+ />
103
+ <div>
104
+ {rightContent && (
105
+ <div className={styles.rightContent}>{rightContent}</div>
106
+ )}
107
+ {leftContent && (
108
+ <div className={styles.leftContent}>{leftContent}</div>
109
+ )}
110
+ </div>
111
+ </div>
112
+ <div className={styles.inputDescription}>
113
+ {helper && (
114
+ <span id={`feedback-${id}`} className={styles.helper}>
115
+ {helper.message}
116
+ </span>
117
+ )}
118
+ {error && (
119
+ <span id={`error-${id}`} className={styles.error}>
120
+ {error.message}
121
+ </span>
122
+ )}
123
+ </div>
124
+ </div>
125
+ );
126
+ };
@@ -0,0 +1,115 @@
1
+ .outer {
2
+ margin-bottom: 1rem;
3
+ }
4
+
5
+ .container {
6
+ position: relative;
7
+ display: flex;
8
+ flex-direction: column;
9
+ }
10
+
11
+ .label {
12
+ position: absolute;
13
+ z-index: 3;
14
+ left: 1.25rem;
15
+ color: #666666;
16
+ pointer-events: none;
17
+ }
18
+
19
+ .input {
20
+ all: unset;
21
+ background: white;
22
+ padding: 1rem 1.25rem;
23
+ padding-top: 1.70rem;
24
+ border-radius: var(--product-radius-2);
25
+ z-index: 1;
26
+ font-size: 1.125rem;
27
+ caret-color: var(--primary-love-pink-100);
28
+ }
29
+
30
+ .inputDescription {
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 0.5rem;
34
+ padding-top: 0.5rem;
35
+ padding-left: 1.5rem;
36
+ }
37
+
38
+ .container--error > label {
39
+ color: var(--product-error);
40
+ }
41
+
42
+ .container--error > input {
43
+ border: var(--product-border-width-1) solid var(--product-error);
44
+ }
45
+
46
+ .error {
47
+ color: var(--product-error);
48
+ }
49
+
50
+ .helper {
51
+ color: rgba(var(--grey-60-rgb));
52
+ }
53
+
54
+ .rightContent {
55
+ position: absolute;
56
+ right: 1.5rem;
57
+ top: 50%;
58
+ transform: translateY(-50%);
59
+ z-index: 2;
60
+ }
61
+
62
+ .leftContent {
63
+ position: absolute;
64
+ left: 1.5rem;
65
+ top: 50%;
66
+ transform: translateY(-50%);
67
+ z-index: 2;
68
+ }
69
+
70
+
71
+ .input:focus {
72
+ border: var(--product-border-width-1) solid rgba(var(--primary-dark-rgb));
73
+ }
74
+
75
+ .input--focus {
76
+ border: var(--product-border-width-1) solid rgba(var(--primary-dark-rgb));
77
+ }
78
+
79
+ .input:hover {
80
+ outline: var(--product-border-width-1) solid #E6E6E6;
81
+ }
82
+
83
+ .input--hover {
84
+ outline: var(--product-border-width-1) solid #E6E6E6;
85
+ }
86
+
87
+ .input:disabled {
88
+ background: #E6E6E6;
89
+ cursor: not-allowed;
90
+ }
91
+
92
+ .input:disabled:hover {
93
+ outline: none;
94
+ }
95
+
96
+
97
+
98
+ @media (min-width: 768px) {
99
+ .input {
100
+ padding: 1.5rem;
101
+ padding-top: 1.8rem;
102
+ font-size: 1.25rem;
103
+ }
104
+
105
+ .label {
106
+ left: 1.5rem;
107
+ }
108
+ }
109
+
110
+
111
+
112
+
113
+
114
+
115
+
@@ -0,0 +1,113 @@
1
+ import { Meta, Canvas } from "@storybook/addon-docs";
2
+ import { TextField } from '.'
3
+
4
+
5
+ <Meta title="Design System Product/C1 - TextField / Documentation" />
6
+
7
+ # TextField
8
+
9
+ TextFields are text inputs that allow users to input custom text entries with a keyboard. Various decorations can be displayed around the field to communicate the entry requirements.
10
+
11
+
12
+ ## Example
13
+
14
+ <Canvas>
15
+ <TextField
16
+ label="Label"
17
+ />
18
+ </Canvas>
19
+
20
+
21
+ ### Usage
22
+
23
+ In the Nextjs repo and this component is expected to be used with react-hook-form and accepts the
24
+ register prop from useForm to hook it upto the form.
25
+
26
+ However, it can be used as a standalone component as well without registering in a HTML form if needed.
27
+
28
+ ```tsx
29
+ import { useForm } from 'react-hook-form'
30
+ import { TextField } from 'design-system-product'
31
+
32
+ const { register, handleSubmit } = useForm()
33
+
34
+ const onSubmit = (data) => {
35
+ console.log(data)
36
+ }
37
+
38
+ return (
39
+ <form onSubmit={handleSubmit(onSubmit)}>
40
+ <TextField
41
+ label="Label"
42
+ {...register('example')}
43
+ />
44
+ </form>
45
+ )
46
+ ```
47
+
48
+ ### Labelling
49
+
50
+ The label prop is required and is used to display the label for the input field. A label is required for accessiblity and should
51
+ always be provided.
52
+
53
+ <Canvas>
54
+ <TextField
55
+ label="Label" />
56
+ </Canvas>
57
+
58
+
59
+
60
+
61
+ ### Required Fields
62
+
63
+ Currently we do not support the required prop with the TextField component due to currently our forms assumes that everything
64
+ is required and we have no reason to ask for everything to be required. Instead please use the suffix prop to add an (optional) tag
65
+ to the label.
66
+
67
+ <Canvas>
68
+ <TextField
69
+ label="Label"
70
+ suffix="(optional)" />
71
+ </Canvas>
72
+
73
+
74
+ ## Visual Options
75
+
76
+ ### Validation
77
+ Validation will be handled by react-hook-form or by passing an error with a message to the error prop.
78
+ The error prop will display an error message below the input field and change the border of the input field to red.
79
+
80
+ <Canvas>
81
+ <TextField
82
+ label="Label"
83
+ error="Error Message" />
84
+ </Canvas>
85
+
86
+
87
+
88
+
89
+ ### Read Only
90
+ The ReadOnly prop is used to make the input field read only. This is useful when you want to display and copy the value of the input field but not allow the user to edit it. Use this instead of the disabled state.
91
+
92
+ <Canvas>
93
+ <TextField
94
+ value="Read Only Value"
95
+ label="Label"
96
+ readOnly />
97
+ </Canvas>
98
+
99
+
100
+
101
+ ### Disabled
102
+ The disabled prop is used to disable the input field. This is useful when you want to prevent the user from editing the input field. Use this instead of the readOnly state.
103
+
104
+ <Canvas>
105
+ <TextField
106
+ value="Example Value"
107
+ label="Label"
108
+ disabled />
109
+ </Canvas>
110
+
111
+
112
+
113
+
@@ -0,0 +1,263 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { TextField } from '.';
4
+
5
+ export default {
6
+ title: 'Design System Product/C1 - TextField',
7
+ component: TextField,
8
+ parameters: {},
9
+ } as Meta<typeof TextField>;
10
+
11
+ type Story = StoryObj<typeof TextField>;
12
+
13
+ export const Default: Story = {
14
+ render: () => (
15
+ <>
16
+ <TextField id="default-no-value" label="Default" name="example" />
17
+ <TextField
18
+ className="hover"
19
+ id="default2-no-value"
20
+ label="Hovered"
21
+ name="example2"
22
+ />
23
+ <TextField
24
+ className="focus"
25
+ id="default3-no-value"
26
+ label="Focused"
27
+ name="example3"
28
+ />
29
+
30
+ <TextField
31
+ id="default-value"
32
+ label="Default"
33
+ name="example"
34
+ value="Example text"
35
+ />
36
+ <TextField
37
+ className="hover"
38
+ id="default2-value"
39
+ label="Hovered"
40
+ name="example2"
41
+ value="Example text"
42
+ />
43
+ <TextField
44
+ className="focus"
45
+ id="default3-value"
46
+ label="Focused"
47
+ name="example3"
48
+ value="Example text"
49
+ />
50
+ <TextField
51
+ id="default4-value"
52
+ label="Disabled "
53
+ name="example4"
54
+ disabled
55
+ />
56
+ </>
57
+ ),
58
+ };
59
+
60
+ export const Error: Story = {
61
+ render: () => (
62
+ <>
63
+ <TextField
64
+ id="error"
65
+ label="Default"
66
+ name="error1"
67
+ error={{ message: 'This is an error' }}
68
+ />
69
+ <TextField
70
+ className="hover"
71
+ id="error2"
72
+ label="Hovered"
73
+ name="error2"
74
+ error={{ message: 'This is an error' }}
75
+ />
76
+ <TextField
77
+ className="focus"
78
+ id="error3"
79
+ label="Focused"
80
+ name="error3"
81
+ error={{ message: 'This is an error' }}
82
+ />
83
+ <TextField
84
+ id="error4-value"
85
+ label="Default"
86
+ name="error4-value"
87
+ value="Example text"
88
+ error={{ message: 'This is an error' }}
89
+ />
90
+ <TextField
91
+ className="hover"
92
+ id="error5-value"
93
+ label="Hovered"
94
+ name="error5-value"
95
+ value="Example text"
96
+ error={{ message: 'This is an error' }}
97
+ />
98
+ <TextField
99
+ className="focus"
100
+ id="error5-value"
101
+ label="Focused"
102
+ name="error5-value"
103
+ value="Example text"
104
+ error={{ message: 'This is an error' }}
105
+ />
106
+ <TextField
107
+ id="error4"
108
+ label="Disabled"
109
+ name="error4"
110
+ error={{ message: 'This is an error' }}
111
+ disabled
112
+ />
113
+ </>
114
+ ),
115
+ };
116
+
117
+ export const HelperText: Story = {
118
+ render: () => (
119
+ <>
120
+ <TextField
121
+ id="helper"
122
+ label="Default"
123
+ name="helper1"
124
+ helper={{ message: 'This is helper text' }}
125
+ />
126
+ <TextField
127
+ className="hover"
128
+ id="helper2"
129
+ label="Hovered"
130
+ name="helper2"
131
+ helper={{ message: 'This is helper text' }}
132
+ />
133
+ <TextField
134
+ className="focus"
135
+ id="helper3"
136
+ label="Focused"
137
+ name="helper3"
138
+ helper={{ message: 'This is helper text' }}
139
+ />
140
+ <TextField
141
+ id="helper4-text"
142
+ label="Default"
143
+ name="helper4-text"
144
+ helper={{ message: 'This is helper text' }}
145
+ value="Example text"
146
+ />
147
+ <TextField
148
+ className="hover"
149
+ id="helper5-text"
150
+ label="Hovered"
151
+ name="helper5-text"
152
+ helper={{ message: 'This is helper text' }}
153
+ value="Example text"
154
+ />
155
+ <TextField
156
+ className="focus"
157
+ id="helper6-text"
158
+ label="Focused"
159
+ name="helper6-text"
160
+ helper={{ message: 'This is helper text' }}
161
+ value="Example text"
162
+ />
163
+ <TextField
164
+ id="helper4"
165
+ label="Disabled"
166
+ name="helper4"
167
+ helper={{ message: 'This is helper text' }}
168
+ disabled
169
+ />
170
+ </>
171
+ ),
172
+ };
173
+
174
+ export const RightContent: Story = {
175
+ render: () => (
176
+ <>
177
+ <TextField
178
+ id="right"
179
+ label="Default"
180
+ name="right1"
181
+ rightContent={<button>Example</button>}
182
+ />
183
+ <TextField
184
+ className="hover"
185
+ id="right2"
186
+ label="Hovered"
187
+ name="right2"
188
+ rightContent={<button>Example</button>}
189
+ />
190
+ <TextField
191
+ className="focus"
192
+ id="right3"
193
+ label="Focused"
194
+ name="right3"
195
+ rightContent={<button>Example</button>}
196
+ />
197
+ <TextField
198
+ id="right4-text"
199
+ label="Default"
200
+ name="right4-text"
201
+ value="Example text"
202
+ rightContent={<button>Example</button>}
203
+ />
204
+ <TextField
205
+ className="hover"
206
+ id="right5-text"
207
+ label="Hovered"
208
+ name="right5-text"
209
+ value="Example text"
210
+ rightContent={<button>Example</button>}
211
+ />
212
+ <TextField
213
+ className="focus"
214
+ id="right6-text"
215
+ label="Focused"
216
+ name="right6-text"
217
+ value="Example text"
218
+ rightContent={<button>Example</button>}
219
+ />
220
+
221
+ <TextField
222
+ id="right4"
223
+ label="Disabled"
224
+ name="right4"
225
+ rightContent={<button>Example</button>}
226
+ disabled
227
+ />
228
+ </>
229
+ ),
230
+ };
231
+
232
+ export const LeftContent: Story = {
233
+ render: () => (
234
+ <>
235
+ <h1>No Designs</h1>
236
+ <br />
237
+ <br />
238
+
239
+ <TextField id="left" label="Default" name="left1" leftContent="Example" />
240
+ <TextField
241
+ className="hover"
242
+ id="left2"
243
+ label="Hovered"
244
+ name="left2"
245
+ leftContent="Example"
246
+ />
247
+ <TextField
248
+ className="focus"
249
+ id="left3"
250
+ label="Focused"
251
+ name="left3"
252
+ leftContent="Example"
253
+ />
254
+ <TextField
255
+ id="left4"
256
+ label="Disabled"
257
+ name="left4"
258
+ leftContent="Example"
259
+ disabled
260
+ />
261
+ </>
262
+ ),
263
+ };
@@ -0,0 +1,111 @@
1
+ import { render } from '@testing-library/react';
2
+ import { describe, it, expect } from 'vitest';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { TextField } from '.';
5
+
6
+ describe('<TextField />', () => {
7
+ it('should render', () => {
8
+ const component = render(
9
+ <TextField
10
+ id="example-TextField"
11
+ label="example-TextField"
12
+ name="example"
13
+ />,
14
+ );
15
+
16
+ expect(component).toBeTruthy();
17
+ });
18
+
19
+ it('should show the helper text when supplied to the component', () => {
20
+ const component = render(
21
+ <TextField
22
+ helper={{ message: 'Helper text' }}
23
+ id="example-TextField"
24
+ label="example-TextField"
25
+ name="example"
26
+ />,
27
+ );
28
+
29
+ expect(component.getByText('Helper text')).toBeTruthy();
30
+ });
31
+
32
+ it('should show the error text when supplied to the component', () => {
33
+ const component = render(
34
+ <TextField
35
+ error={{ message: 'Error text' }}
36
+ id="example-TextField"
37
+ label="example-TextField"
38
+ name="example"
39
+ />,
40
+ );
41
+
42
+ expect(component.getByText('Error text')).toBeTruthy();
43
+ });
44
+
45
+ it('should show the prefix when supplied to the component', () => {
46
+ const component = render(
47
+ <TextField
48
+ id="example-TextField"
49
+ label="example-TextField"
50
+ name="example"
51
+ prefix="Prefix"
52
+ />,
53
+ );
54
+
55
+ expect(component.getByText('Prefix')).toBeTruthy();
56
+ });
57
+
58
+ it('should show the suffix when supplied to the component', () => {
59
+ const component = render(
60
+ <TextField
61
+ id="example-TextField"
62
+ label="example-TextField"
63
+ name="example"
64
+ suffix="Suffix"
65
+ />,
66
+ );
67
+
68
+ expect(component.getByText('Suffix')).toBeTruthy();
69
+ });
70
+
71
+ it('should show the label when supplied to the component', () => {
72
+ const component = render(
73
+ <TextField
74
+ id="example-TextField"
75
+ label="example-TextField"
76
+ name="example"
77
+ />,
78
+ );
79
+
80
+ expect(component.getByText('example-TextField')).toBeTruthy();
81
+ });
82
+
83
+ it('Should show an error message if one is present', () => {
84
+ const component = render(
85
+ <TextField
86
+ error={{ message: 'Error text' }}
87
+ id="example-TextField"
88
+ label="example-TextField"
89
+ name="example"
90
+ />,
91
+ );
92
+
93
+ expect(component.getByText('Error text')).toBeTruthy();
94
+ });
95
+
96
+ it('should be able to input text into the input field', async () => {
97
+ const component = render(
98
+ <TextField
99
+ id="example-TextField"
100
+ label="example-TextField"
101
+ name="example"
102
+ />,
103
+ );
104
+
105
+ const input = component.getByLabelText('example-TextField');
106
+
107
+ await userEvent.type(input, 'Hello, World!');
108
+
109
+ expect(input).toHaveValue('Hello, World!');
110
+ });
111
+ });
@@ -0,0 +1,3 @@
1
+ import { TextField } from "./components/TextField";
2
+
3
+ export { TextField };
@@ -0,0 +1,36 @@
1
+
2
+
3
+ /* Product Variables */
4
+
5
+ :root {
6
+ /* Colors */
7
+ --product-error: rgba(var(--system-red-rgb));
8
+
9
+ /* Spacing */
10
+ --product-spacing-1: 0.25rem;
11
+ --product-spacing-2: 0.50rem;
12
+ --product-spacing-3: 0.75rem;
13
+ --product-spacing-4: 1rem;
14
+ --product-spacing-5: 1.25rem;
15
+ --product-spacing-6: 1.50rem;
16
+ --product-spacing-7: 2rem;
17
+ --product-spacing-8: 2.50rem;
18
+ --product-spacing-9: 3rem;
19
+ --product-spacing-10: 3.50rem;
20
+ --product-spacing-11: 4rem;
21
+
22
+ /* Border Radius */
23
+ --product-radius-1: 0.25rem;
24
+ --product-radius-2: 0.50rem;
25
+ --product-radius-3: 0.75rem;
26
+ --product-radius-4: 1rem;
27
+
28
+ /* Border Width */
29
+ --product-border-width-1: 2px;
30
+
31
+
32
+ /* Component Specific Variables */
33
+
34
+ --product-text-field-spacing: 0.625rem;
35
+ --product-radio-select-spacing: 1.25rem;
36
+ }
@@ -0,0 +1,20 @@
1
+
2
+ /* Import all tokens from each section of Design System */
3
+ @import "./tokens/variables.css";
4
+
5
+ * {
6
+ box-sizing: border-box;
7
+ padding: 0;
8
+ margin: 0;
9
+ font-family: var(--font-primary);
10
+ }
11
+
12
+ .sr-only:not(:focus):not(:active) {
13
+ clip: rect(0 0 0 0);
14
+ clip-path: inset(50%);
15
+ height: 1px;
16
+ overflow: hidden;
17
+ position: absolute;
18
+ white-space: nowrap;
19
+ width: 1px;
20
+ }
@@ -0,0 +1,15 @@
1
+ /* PRODUCT TOKENS */
2
+ @import url("../../product/styles/tokens/variables.css");
3
+
4
+ /* MARKETING TOKENS */
5
+
6
+ @import url("../../marketing/styles/tokens/variables.css");
7
+
8
+ /* GLOBAL TOKENS */
9
+
10
+ :root {
11
+ --font-primary: "Inter", sans-serif;
12
+ --font-secondary: "Brygada 1918", sans-serif;
13
+ }
14
+
15
+
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": [
4
+ "dom",
5
+ "dom.iterable",
6
+ "esnext"
7
+ ],
8
+ "allowJs": true,
9
+ "skipLibCheck": true,
10
+ "strict": true,
11
+ "noEmit": true,
12
+ "esModuleInterop": true,
13
+ "module": "esnext",
14
+ "moduleResolution": "node",
15
+ "resolveJsonModule": true,
16
+ "isolatedModules": true,
17
+ "jsx": "preserve",
18
+ "incremental": true,
19
+ "paths": {
20
+ "@/*": ["./src/*"],
21
+ "@hooks/*": ["./src/hooks/*"]
22
+ },
23
+ "forceConsistentCasingInFileNames": true,
24
+ },
25
+
26
+ }