@indico-data/design-system 2.18.0 → 2.19.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/lib/index.css +0 -33
- package/lib/index.d.ts +17 -20
- package/lib/index.esm.css +0 -33
- package/lib/index.esm.js +31 -9
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +33 -11
- package/lib/index.js.map +1 -1
- package/lib/src/components/forms/input/Input.d.ts +5 -7
- package/lib/src/components/forms/passwordInput/PasswordInput.d.ts +5 -7
- package/lib/src/components/forms/subcomponents/Label.d.ts +6 -3
- package/lib/src/components/forms/textarea/Textarea.d.ts +5 -7
- package/lib/src/storybook/labelArgTypes.d.ts +3 -0
- package/package.json +1 -1
- package/src/components/forms/input/Input.mdx +15 -2
- package/src/components/forms/input/Input.stories.tsx +10 -45
- package/src/components/forms/input/Input.tsx +22 -15
- package/src/components/forms/input/styles/Input.scss +0 -11
- package/src/components/forms/passwordInput/PasswordInput.mdx +10 -8
- package/src/components/forms/passwordInput/PasswordInput.stories.tsx +3 -44
- package/src/components/forms/passwordInput/PasswordInput.tsx +20 -15
- package/src/components/forms/passwordInput/styles/PasswordInput.scss +0 -11
- package/src/components/forms/subcomponents/Label.tsx +29 -6
- package/src/components/forms/subcomponents/__tests__/Label.test.tsx +63 -15
- package/src/components/forms/textarea/Textarea.mdx +12 -2
- package/src/components/forms/textarea/Textarea.stories.tsx +4 -46
- package/src/components/forms/textarea/Textarea.tsx +15 -13
- package/src/components/forms/textarea/styles/Textarea.scss +0 -11
- package/src/storybook/labelArgTypes.ts +50 -0
|
@@ -1,33 +1,81 @@
|
|
|
1
1
|
import { render, screen } from '@testing-library/react';
|
|
2
|
-
import { Label } from '@/components/forms/subcomponents/Label';
|
|
2
|
+
import { Label, withLabel } from '@/components/forms/subcomponents/Label';
|
|
3
|
+
|
|
4
|
+
// Mock component to wrap with HOC
|
|
5
|
+
const MockComponent: React.FC = (props) => {
|
|
6
|
+
return <input {...props} />;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const LabeledMockComponent = withLabel(MockComponent);
|
|
3
10
|
|
|
4
11
|
describe('Label', () => {
|
|
5
|
-
it('renders the `required`
|
|
6
|
-
render(<Label label=
|
|
12
|
+
it('renders the `required` asterisk when the label is required', () => {
|
|
13
|
+
render(<Label label="name" name="name" isRequired={true} />);
|
|
7
14
|
expect(screen.getByText('*')).toBeInTheDocument();
|
|
8
15
|
expect(screen.getByText('*')).toBeVisible();
|
|
9
16
|
});
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
|
|
18
|
+
it('does not render the `required` asterisk when the label is not required', () => {
|
|
19
|
+
render(<Label label="name" name="name" isRequired={false} />);
|
|
12
20
|
expect(screen.queryByText('*')).not.toBeInTheDocument();
|
|
13
21
|
expect(screen.queryByText('*')).toBeNull();
|
|
14
22
|
});
|
|
15
|
-
|
|
16
|
-
render(<Label label={'name'} name={'name'} isRequired={false} hasHiddenLabel={true} />);
|
|
17
|
-
expect(screen.getByText('name')).toBeInTheDocument();
|
|
18
|
-
expect(screen.getByText('name')).toBeVisible();
|
|
19
|
-
});
|
|
23
|
+
|
|
20
24
|
it('renders the label text', () => {
|
|
21
|
-
render(<Label label=
|
|
25
|
+
render(<Label label="name" name="name" isRequired={false} />);
|
|
22
26
|
const label = screen.getByTestId('name-testId');
|
|
23
27
|
expect(label).toBeInTheDocument();
|
|
24
28
|
expect(label).toBeVisible();
|
|
25
|
-
expect(label).not.toHaveClass('is-visually-hidden');
|
|
26
29
|
});
|
|
30
|
+
});
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
describe('withLabel HOC', () => {
|
|
33
|
+
it('renders the wrapped component with a visible label', () => {
|
|
34
|
+
render(
|
|
35
|
+
<LabeledMockComponent label="name" name="name" isRequired={false} hasHiddenLabel={false} />,
|
|
36
|
+
);
|
|
30
37
|
const label = screen.getByTestId('name-testId');
|
|
31
|
-
|
|
38
|
+
const input = screen.getByRole('textbox');
|
|
39
|
+
|
|
40
|
+
expect(label).toBeInTheDocument();
|
|
41
|
+
expect(label).toBeVisible();
|
|
42
|
+
expect(input).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders the wrapped component with an aria-label when hasHiddenLabel is true', () => {
|
|
46
|
+
render(<LabeledMockComponent label="name" name="name" isRequired={false} hasHiddenLabel />);
|
|
47
|
+
const input = screen.getByRole('textbox');
|
|
48
|
+
|
|
49
|
+
expect(input).toBeInTheDocument();
|
|
50
|
+
expect(input).toHaveAttribute('aria-label', 'name');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders the wrapped component with an aria-label including "required" when isRequired is true and hasHiddenLabel is true', () => {
|
|
54
|
+
render(<LabeledMockComponent label="name" name="name" isRequired={true} hasHiddenLabel />);
|
|
55
|
+
const input = screen.getByRole('textbox');
|
|
56
|
+
|
|
57
|
+
expect(input).toBeInTheDocument();
|
|
58
|
+
expect(input).toHaveAttribute('aria-label', 'name (required)');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('does not render the visible label when hasHiddenLabel is true', () => {
|
|
62
|
+
render(<LabeledMockComponent label="name" name="name" isRequired={false} hasHiddenLabel />);
|
|
63
|
+
expect(screen.queryByTestId('name-testId')).not.toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('renders the `required` asterisk when isRequired is true and hasHiddenLabel is false', () => {
|
|
67
|
+
render(
|
|
68
|
+
<LabeledMockComponent label="name" name="name" isRequired={true} hasHiddenLabel={false} />,
|
|
69
|
+
);
|
|
70
|
+
expect(screen.getByText('*')).toBeInTheDocument();
|
|
71
|
+
expect(screen.getByText('*')).toBeVisible();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('does not render the `required` asterisk when isRequired is false and hasHiddenLabel is false', () => {
|
|
75
|
+
render(
|
|
76
|
+
<LabeledMockComponent label="name" name="name" isRequired={false} hasHiddenLabel={false} />,
|
|
77
|
+
);
|
|
78
|
+
expect(screen.queryByText('*')).not.toBeInTheDocument();
|
|
79
|
+
expect(screen.queryByText('*')).toBeNull();
|
|
32
80
|
});
|
|
33
81
|
});
|
|
@@ -5,13 +5,23 @@ import * as Textarea from './Textarea.stories';
|
|
|
5
5
|
|
|
6
6
|
# Textarea
|
|
7
7
|
|
|
8
|
-
The Textarea component is the building block of any form. Below you will find the accepted properties for this component. It is encouraged to build forms utilizing [React Hook Form](https://react-hook-form.com/) library in your application. This will facilitate form state management and enforce best practices. (
|
|
8
|
+
The Textarea component is the building block of any form. Below you will find the accepted properties for this component. It is encouraged to build forms utilizing [React Hook Form](https://react-hook-form.com/) library in your application. This will facilitate form state management and enforce best practices. (**_Our components are compatible with but do not provide the plugin_**)
|
|
9
9
|
|
|
10
10
|
<Canvas
|
|
11
11
|
of={Textarea.Default}
|
|
12
12
|
source={{
|
|
13
13
|
code: `
|
|
14
|
-
|
|
14
|
+
<Textarea
|
|
15
|
+
label="Label Name"
|
|
16
|
+
name="textarea"
|
|
17
|
+
placeholder="Please enter a value"
|
|
18
|
+
helpText="This Is Help Text"
|
|
19
|
+
isRequired
|
|
20
|
+
isDisabled={false}
|
|
21
|
+
errorMessage=""
|
|
22
|
+
value=""
|
|
23
|
+
onChange={() => {}}
|
|
24
|
+
/>
|
|
15
25
|
`,
|
|
16
26
|
}}
|
|
17
27
|
/>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
1
2
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
3
|
+
import labelArgTypes from '@/storybook/labelArgTypes';
|
|
4
|
+
import { Textarea } from './Textarea';
|
|
4
5
|
|
|
5
6
|
const meta: Meta = {
|
|
6
7
|
title: 'Forms/Textarea',
|
|
@@ -17,28 +18,6 @@ const meta: Meta = {
|
|
|
17
18
|
},
|
|
18
19
|
action: 'onChange',
|
|
19
20
|
},
|
|
20
|
-
label: {
|
|
21
|
-
control: 'text',
|
|
22
|
-
description: 'The label for the textarea field',
|
|
23
|
-
table: {
|
|
24
|
-
category: 'Props',
|
|
25
|
-
type: {
|
|
26
|
-
summary: 'string',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
defaultValue: { summary: '' },
|
|
30
|
-
},
|
|
31
|
-
name: {
|
|
32
|
-
control: 'text',
|
|
33
|
-
description: 'The name for the textarea field',
|
|
34
|
-
table: {
|
|
35
|
-
category: 'Props',
|
|
36
|
-
type: {
|
|
37
|
-
summary: 'string',
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
defaultValue: { summary: '' },
|
|
41
|
-
},
|
|
42
21
|
placeholder: {
|
|
43
22
|
control: 'text',
|
|
44
23
|
description: 'The placeholder for the textarea field',
|
|
@@ -61,17 +40,6 @@ const meta: Meta = {
|
|
|
61
40
|
},
|
|
62
41
|
defaultValue: { summary: '' },
|
|
63
42
|
},
|
|
64
|
-
isRequired: {
|
|
65
|
-
control: 'boolean',
|
|
66
|
-
description: 'Toggles the required astherisc on the label',
|
|
67
|
-
table: {
|
|
68
|
-
category: 'Props',
|
|
69
|
-
type: {
|
|
70
|
-
summary: 'boolean',
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
defaultValue: { summary: 'false' },
|
|
74
|
-
},
|
|
75
43
|
isDisabled: {
|
|
76
44
|
control: 'boolean',
|
|
77
45
|
description: 'Toggles the disabled state of the textarea field',
|
|
@@ -105,17 +73,6 @@ const meta: Meta = {
|
|
|
105
73
|
},
|
|
106
74
|
defaultValue: { summary: '' },
|
|
107
75
|
},
|
|
108
|
-
hasHiddenLabel: {
|
|
109
|
-
control: 'boolean',
|
|
110
|
-
description: 'Hides the label visually (retains it for screen readers)',
|
|
111
|
-
table: {
|
|
112
|
-
category: 'Props',
|
|
113
|
-
type: {
|
|
114
|
-
summary: 'boolean',
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
defaultValue: { summary: 'false' },
|
|
118
|
-
},
|
|
119
76
|
autofocus: {
|
|
120
77
|
control: 'boolean',
|
|
121
78
|
description: ' Specifies that a text area should automatically get focus when the page loads',
|
|
@@ -198,6 +155,7 @@ const meta: Meta = {
|
|
|
198
155
|
disable: true,
|
|
199
156
|
},
|
|
200
157
|
},
|
|
158
|
+
...labelArgTypes,
|
|
201
159
|
},
|
|
202
160
|
};
|
|
203
161
|
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
|
|
4
|
+
import { withLabel, LabelProps, WithLabelProps } from '../subcomponents/Label';
|
|
3
5
|
import { DisplayFormError } from '../subcomponents/DisplayFormError';
|
|
4
6
|
|
|
5
|
-
export interface TextareaProps {
|
|
7
|
+
export interface TextareaProps extends WithLabelProps {
|
|
6
8
|
ref?: React.LegacyRef<HTMLTextAreaElement>;
|
|
7
|
-
|
|
8
|
-
name: string;
|
|
9
|
-
placeholder: string;
|
|
9
|
+
placeholder?: string;
|
|
10
10
|
value?: string | undefined;
|
|
11
11
|
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
|
12
|
-
isRequired?: boolean;
|
|
13
12
|
isDisabled?: boolean;
|
|
14
13
|
errorMessage?: string | undefined;
|
|
15
14
|
helpText?: string;
|
|
16
|
-
hasHiddenLabel?: boolean;
|
|
17
15
|
rows?: number;
|
|
18
16
|
cols?: number;
|
|
19
17
|
readonly?: boolean;
|
|
@@ -23,7 +21,7 @@ export interface TextareaProps {
|
|
|
23
21
|
autofocus?: boolean;
|
|
24
22
|
defaultValue?: string;
|
|
25
23
|
}
|
|
26
|
-
|
|
24
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
27
25
|
(
|
|
28
26
|
{
|
|
29
27
|
label,
|
|
@@ -49,9 +47,10 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
49
47
|
) => {
|
|
50
48
|
const hasErrors = errorMessage && errorMessage.length > 0;
|
|
51
49
|
|
|
50
|
+
const textareaClasses = classNames('textarea', { error: hasErrors });
|
|
51
|
+
|
|
52
52
|
return (
|
|
53
|
-
|
|
54
|
-
<Label label={label} name={name} isRequired={isRequired} hasHiddenLabel={hasHiddenLabel} />
|
|
53
|
+
<>
|
|
55
54
|
<div className="textarea-wrapper">
|
|
56
55
|
<textarea
|
|
57
56
|
ref={ref}
|
|
@@ -67,11 +66,10 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
67
66
|
disabled={isDisabled}
|
|
68
67
|
placeholder={placeholder}
|
|
69
68
|
onChange={onChange}
|
|
70
|
-
className={
|
|
69
|
+
className={textareaClasses}
|
|
71
70
|
aria-invalid={hasErrors ? true : undefined}
|
|
72
71
|
aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
|
|
73
72
|
aria-required={isRequired}
|
|
74
|
-
aria-label={label}
|
|
75
73
|
{...rest}
|
|
76
74
|
/>
|
|
77
75
|
</div>
|
|
@@ -81,7 +79,11 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
81
79
|
{helpText}
|
|
82
80
|
</div>
|
|
83
81
|
)}
|
|
84
|
-
|
|
82
|
+
</>
|
|
85
83
|
);
|
|
86
84
|
},
|
|
87
85
|
);
|
|
86
|
+
|
|
87
|
+
const LabeledTextarea = withLabel(Textarea);
|
|
88
|
+
|
|
89
|
+
export { LabeledTextarea as Textarea };
|
|
@@ -84,17 +84,6 @@
|
|
|
84
84
|
color: var(--pf-textarea-help-text-color);
|
|
85
85
|
font-size: var(--pf-font-size-subtitle2);
|
|
86
86
|
}
|
|
87
|
-
.is-visually-hidden {
|
|
88
|
-
position: absolute;
|
|
89
|
-
width: 1px;
|
|
90
|
-
height: 1px;
|
|
91
|
-
padding: 0;
|
|
92
|
-
margin: -1px;
|
|
93
|
-
overflow: hidden;
|
|
94
|
-
clip: rect(0, 0, 0, 0);
|
|
95
|
-
white-space: nowrap;
|
|
96
|
-
border: 0;
|
|
97
|
-
}
|
|
98
87
|
.form-label {
|
|
99
88
|
margin-bottom: var(--pf-margin-2);
|
|
100
89
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Common argTypes for form components that include a label
|
|
2
|
+
import { ArgTypes } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
const labelArgTypes: ArgTypes = {
|
|
5
|
+
label: {
|
|
6
|
+
control: 'text',
|
|
7
|
+
description: 'The text to display as the label for the form component',
|
|
8
|
+
table: {
|
|
9
|
+
category: 'Props',
|
|
10
|
+
type: {
|
|
11
|
+
summary: 'string',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
isRequired: {
|
|
16
|
+
control: 'boolean',
|
|
17
|
+
description: 'Toggles the required asterisk on the label',
|
|
18
|
+
table: {
|
|
19
|
+
category: 'Props',
|
|
20
|
+
type: {
|
|
21
|
+
summary: 'boolean',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultValue: { summary: 'false' },
|
|
25
|
+
},
|
|
26
|
+
hasHiddenLabel: {
|
|
27
|
+
control: 'boolean',
|
|
28
|
+
description:
|
|
29
|
+
'Determines whether the label should be displayed or not. Included as aria-label if true',
|
|
30
|
+
table: {
|
|
31
|
+
category: 'Props',
|
|
32
|
+
type: {
|
|
33
|
+
summary: 'boolean',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
defaultValue: { summary: 'false' },
|
|
37
|
+
},
|
|
38
|
+
name: {
|
|
39
|
+
control: 'text',
|
|
40
|
+
description: 'The name attribute for the form component, used for identifying the field',
|
|
41
|
+
table: {
|
|
42
|
+
category: 'Props',
|
|
43
|
+
type: {
|
|
44
|
+
summary: 'string',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default labelArgTypes;
|