@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.
Files changed (28) hide show
  1. package/lib/index.css +0 -33
  2. package/lib/index.d.ts +17 -20
  3. package/lib/index.esm.css +0 -33
  4. package/lib/index.esm.js +31 -9
  5. package/lib/index.esm.js.map +1 -1
  6. package/lib/index.js +33 -11
  7. package/lib/index.js.map +1 -1
  8. package/lib/src/components/forms/input/Input.d.ts +5 -7
  9. package/lib/src/components/forms/passwordInput/PasswordInput.d.ts +5 -7
  10. package/lib/src/components/forms/subcomponents/Label.d.ts +6 -3
  11. package/lib/src/components/forms/textarea/Textarea.d.ts +5 -7
  12. package/lib/src/storybook/labelArgTypes.d.ts +3 -0
  13. package/package.json +1 -1
  14. package/src/components/forms/input/Input.mdx +15 -2
  15. package/src/components/forms/input/Input.stories.tsx +10 -45
  16. package/src/components/forms/input/Input.tsx +22 -15
  17. package/src/components/forms/input/styles/Input.scss +0 -11
  18. package/src/components/forms/passwordInput/PasswordInput.mdx +10 -8
  19. package/src/components/forms/passwordInput/PasswordInput.stories.tsx +3 -44
  20. package/src/components/forms/passwordInput/PasswordInput.tsx +20 -15
  21. package/src/components/forms/passwordInput/styles/PasswordInput.scss +0 -11
  22. package/src/components/forms/subcomponents/Label.tsx +29 -6
  23. package/src/components/forms/subcomponents/__tests__/Label.test.tsx +63 -15
  24. package/src/components/forms/textarea/Textarea.mdx +12 -2
  25. package/src/components/forms/textarea/Textarea.stories.tsx +4 -46
  26. package/src/components/forms/textarea/Textarea.tsx +15 -13
  27. package/src/components/forms/textarea/styles/Textarea.scss +0 -11
  28. 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` astherisc when the label is required', () => {
6
- render(<Label label={'name'} name={'name'} isRequired={true} hasHiddenLabel={false} />);
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
- it('does not render the `required` astherisc when the label is not required', () => {
11
- render(<Label label={'name'} name={'name'} isRequired={false} hasHiddenLabel={false} />);
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
- it('renders the label for screen readers while hidden visually', () => {
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={'name'} name={'name'} isRequired={false} hasHiddenLabel={false} />);
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
- it('renders the label text when the label is hidden', () => {
29
- render(<Label label={'name'} name={'name'} isRequired={false} hasHiddenLabel={true} />);
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
- expect(label).toHaveClass('is-visually-hidden');
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. (***Our components are compatible with but do not provide the plugin***)
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
- <Textarea name="first_name" isRequired helpText="This Is Help Text" placeholder="This is a placeholder" value="''" onChange={handleChange} label="Label Name" />
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 { Textarea, TextareaProps } from './Textarea';
3
- import { SetStateAction, useEffect, useState } from 'react';
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 { Label } from '../subcomponents/Label';
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
- label: string;
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
- export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
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
- <div className="form-control">
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={`textarea ${hasErrors ? 'error' : ''}`}
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
- </div>
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;