@indico-data/design-system 2.33.0 → 2.34.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 (36) hide show
  1. package/.storybook/preview-head.html +0 -4
  2. package/lib/index.css +95 -3
  3. package/lib/index.d.ts +8 -8
  4. package/lib/index.esm.css +95 -3
  5. package/lib/index.esm.js +1 -1
  6. package/lib/index.esm.js.map +1 -1
  7. package/lib/index.js +1 -1
  8. package/lib/index.js.map +1 -1
  9. package/lib/src/components/forms/input/Input.d.ts +5 -3
  10. package/lib/src/components/forms/numberInput/NumberInput.d.ts +11 -0
  11. package/lib/src/components/forms/numberInput/NumberInput.stories.d.ts +12 -0
  12. package/lib/src/components/forms/numberInput/__tests__/NumberInput.test.d.ts +1 -0
  13. package/lib/src/components/forms/numberInput/index.d.ts +1 -0
  14. package/lib/src/components/forms/passwordInput/PasswordInput.d.ts +3 -3
  15. package/lib/src/components/forms/subcomponents/Label.d.ts +1 -3
  16. package/lib/src/components/forms/textarea/Textarea.d.ts +3 -3
  17. package/lib/src/storybook/formArgTypes.d.ts +5 -0
  18. package/package.json +1 -1
  19. package/src/components/button/styles/Button.scss +0 -4
  20. package/src/components/forms/input/Input.stories.tsx +2 -96
  21. package/src/components/forms/input/Input.tsx +5 -3
  22. package/src/components/forms/numberInput/NumberInput.mdx +32 -0
  23. package/src/components/forms/numberInput/NumberInput.stories.tsx +215 -0
  24. package/src/components/forms/numberInput/NumberInput.tsx +90 -0
  25. package/src/components/forms/numberInput/__tests__/NumberInput.test.tsx +94 -0
  26. package/src/components/forms/numberInput/index.ts +1 -0
  27. package/src/components/forms/numberInput/styles/NumberInput.scss +108 -0
  28. package/src/components/forms/passwordInput/PasswordInput.stories.tsx +3 -70
  29. package/src/components/forms/passwordInput/PasswordInput.tsx +2 -2
  30. package/src/components/forms/subcomponents/Label.tsx +1 -4
  31. package/src/components/forms/textarea/Textarea.stories.tsx +3 -68
  32. package/src/components/forms/textarea/Textarea.tsx +2 -2
  33. package/src/storybook/formArgTypes.ts +152 -0
  34. package/src/styles/index.scss +1 -0
  35. package/lib/src/storybook/labelArgTypes.d.ts +0 -3
  36. package/src/storybook/labelArgTypes.ts +0 -50
@@ -0,0 +1,94 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { NumberInput, NumberInputProps } from '../NumberInput';
4
+
5
+ const handleOnChange = jest.fn();
6
+
7
+ const defaultProps: NumberInputProps = {
8
+ isRequired: true,
9
+ label: 'Enter a number',
10
+ helpText: 'In order to submit the form, this field is required.',
11
+ name: 'number',
12
+ placeholder: 'Please enter a value',
13
+ iconName: 'fa-calculator',
14
+ isClearable: true,
15
+ onChange: handleOnChange,
16
+ };
17
+
18
+ describe('NumberInput', () => {
19
+ beforeEach(jest.clearAllMocks);
20
+
21
+ it('renders the number input field', () => {
22
+ render(<NumberInput {...defaultProps} />);
23
+ expect(screen.getByText('Enter a number')).toBeInTheDocument();
24
+ });
25
+
26
+ it('shows an x clear icon when the number input is clearable', () => {
27
+ render(<NumberInput {...defaultProps} />);
28
+ const icon = screen.getByTestId('number-clearable-icon');
29
+ expect(icon).toBeInTheDocument();
30
+ expect(icon).toBeVisible();
31
+ });
32
+
33
+ it('does not show an x when the number input is not clearable', () => {
34
+ render(<NumberInput {...defaultProps} isClearable={false} />);
35
+ const icon = screen.queryByTestId('number-clearable-icon');
36
+ expect(icon).not.toBeInTheDocument();
37
+ });
38
+
39
+ it('clicking on the x clears the value', async () => {
40
+ render(<NumberInput {...defaultProps} value={42} />);
41
+ const input = screen.getByTestId('form-number-input-number');
42
+ const icon = screen.getByTestId('number-clearable-icon');
43
+ expect(input).toHaveValue(42);
44
+ await userEvent.click(icon);
45
+ expect(handleOnChange).toHaveBeenCalledWith(expect.objectContaining({ target: { value: '' } }));
46
+ });
47
+
48
+ it('renders an icon on the left when one exists', () => {
49
+ render(<NumberInput {...defaultProps} />);
50
+ const icon = screen.getByTestId('number-embedded-icon');
51
+ expect(icon).toBeInTheDocument();
52
+ expect(icon).toBeVisible();
53
+ });
54
+
55
+ it('does not render an embedded icon when one does not exist', () => {
56
+ render(<NumberInput {...defaultProps} iconName={undefined} />);
57
+ const icon = screen.queryByTestId('number-embedded-icon');
58
+ expect(icon).not.toBeInTheDocument();
59
+ });
60
+
61
+ it('adds the error class when errors exist', () => {
62
+ render(
63
+ <NumberInput {...defaultProps} errorMessage="You require a numeric value." value={42} />,
64
+ );
65
+ const input = screen.getByTestId('form-number-input-number');
66
+ expect(input).toHaveClass('error');
67
+ });
68
+
69
+ it('does not highlight the input when no errors exist', () => {
70
+ render(<NumberInput {...defaultProps} value={42} />);
71
+ const input = screen.getByTestId('form-number-input-number');
72
+ expect(input).not.toHaveClass('error');
73
+ });
74
+
75
+ it('renders help text when help text exists', () => {
76
+ render(<NumberInput {...defaultProps} value={42} />);
77
+ const helpText = screen.getByText('In order to submit the form, this field is required.');
78
+ expect(helpText).toBeInTheDocument();
79
+ expect(helpText).toBeVisible();
80
+ });
81
+
82
+ it('does not render help text when help text does not exist', () => {
83
+ render(<NumberInput {...defaultProps} helpText={undefined} value={42} />);
84
+ const helpText = screen.queryByTestId('number-help-text');
85
+ expect(helpText).not.toBeInTheDocument();
86
+ });
87
+
88
+ it('emits the value when user types', async () => {
89
+ render(<NumberInput {...defaultProps} />);
90
+ const input = screen.getByTestId('form-number-input-number');
91
+ await userEvent.type(input, '42');
92
+ expect(handleOnChange).toHaveBeenCalled();
93
+ });
94
+ });
@@ -0,0 +1 @@
1
+ export { NumberInput } from './NumberInput';
@@ -0,0 +1,108 @@
1
+ // Common Variables
2
+ :root,
3
+ :root [data-theme='light'],
4
+ :root [data-theme='dark'] {
5
+ // Typography
6
+ --pf-number-input-background-color: var(--pf-white-color);
7
+ --pf-number-input-border-color: var(--pf-form-input-border-color);
8
+ --pf-number-input-text-color: var(--pf-gray-color);
9
+ --pf-number-input-placeholder-text-color: var(--pf-gray-color-300);
10
+ --pf-number-input-help-text-color: var(--pf-gray-color-400);
11
+ --pf-number-input-disabled-background-color: var(--pf-gray-color-100);
12
+ --pf-number-input-disabled-color: var(--pf-gray-color-400);
13
+ --pf-number-input-padding: 10px;
14
+
15
+ // Input Radius
16
+ --pf-number-input-rounded: var(--pf-rounded);
17
+ }
18
+
19
+ // Dark Theme Specific Variables
20
+ :root [data-theme='dark'] {
21
+ --pf-number-input-background-color: var(--pf-primary-color);
22
+ --pf-number-input-border-color: var(--pf-form-input-border-color);
23
+ --pf-number-input-text-color: var(--pf-gray-color-100);
24
+ --pf-number-input-placeholder-text-color: var(--pf-gray-color-600);
25
+ --pf-number-input-help-text-color: var(--pf-gray-color-200);
26
+ --pf-number-input-disabled-background-color: var(--pf-primary-color-200);
27
+ --pf-number-input-disabled-border-color: var(--pf-gray-color-300);
28
+ --pf-number-input-disabled-color: var(--pf-gray-color-400);
29
+ }
30
+
31
+ .number-input {
32
+ background-color: var(--pf-number-input-background-color);
33
+ border: var(--pf-border-thin) solid var(--pf-input-border-color);
34
+ border-radius: var(--pf-number-input-rounded);
35
+ color: var(--pf-number-input-text-color);
36
+ width: 100%;
37
+ box-sizing: border-box;
38
+ height: var(--pf-size-9);
39
+
40
+ padding-right: var(--pf-number-input-padding);
41
+ &--is-clearable {
42
+ padding-right: var(--pf-padding-7);
43
+ }
44
+
45
+ padding-left: var(--pf-number-input-padding);
46
+ &--has-icon {
47
+ padding-left: var(--pf-padding-7);
48
+ }
49
+
50
+ &::placeholder {
51
+ color: var(--pf-number-input-placeholder-text-color);
52
+ }
53
+
54
+ &:focus {
55
+ border-color: var(--pf-primary-color);
56
+ }
57
+
58
+ &.error {
59
+ border-color: var(--pf-error-color);
60
+ }
61
+
62
+ &.success {
63
+ border-color: var(--pf-success-color);
64
+ }
65
+
66
+ &.warning {
67
+ border-color: var(--pf-warning-color);
68
+ }
69
+
70
+ &.info {
71
+ border-color: var(--pf-info-color);
72
+ }
73
+
74
+ &:disabled {
75
+ background-color: var(--pf-number-input-disabled-background-color);
76
+ border-color: var(--pf-number-input-disabled-border-color);
77
+ color: var(--pf-number-input-disabled-color);
78
+ }
79
+ }
80
+
81
+ .form-control {
82
+ .help-text {
83
+ margin-top: var(--pf-margin-2);
84
+ margin-bottom: var(--pf-margin-2);
85
+ color: var(--pf-number-input-help-text-color);
86
+ font-size: var(--pf-font-size-subtitle2);
87
+ }
88
+ .number-input-wrapper {
89
+ position: relative;
90
+ .embedded-icon {
91
+ position: absolute;
92
+ top: 10px;
93
+ left: var(--pf-margin-2);
94
+ color: var(--pf-number-input-text-color);
95
+ }
96
+
97
+ .clearable-icon {
98
+ position: absolute;
99
+ top: var(--pf-margin-3);
100
+ right: var(--pf-margin-2);
101
+ color: var(--pf-number-input-text-color);
102
+ cursor: pointer;
103
+ }
104
+ }
105
+ .form-label {
106
+ margin-bottom: var(--pf-margin-2);
107
+ }
108
+ }
@@ -1,12 +1,14 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
2
  import { PasswordInput, PasswordInputProps } from './PasswordInput';
3
3
  import { SetStateAction, useEffect, useState } from 'react';
4
- import labelArgTypes from '@/storybook/labelArgTypes';
4
+ import { baseInputArgTypes, inputArgTypes, labelArgTypes } from '@/storybook/formArgTypes';
5
5
 
6
6
  const meta: Meta = {
7
7
  title: 'Forms/PasswordInput',
8
8
  component: PasswordInput,
9
9
  argTypes: {
10
+ ...labelArgTypes,
11
+ ...baseInputArgTypes,
10
12
  hasShowPassword: {
11
13
  control: 'boolean',
12
14
  description: 'Toggles the visibility of the password',
@@ -18,75 +20,6 @@ const meta: Meta = {
18
20
  },
19
21
  defaultValue: { summary: true },
20
22
  },
21
- onChange: {
22
- control: false,
23
- description: 'onChange event handler',
24
- table: {
25
- category: 'Callbacks',
26
- type: {
27
- summary: '(e: React.ChangeEvent<HTMLInputElement>) => void',
28
- },
29
- },
30
- action: 'onChange',
31
- },
32
- placeholder: {
33
- control: 'text',
34
- description: 'The placeholder for the password field',
35
- table: {
36
- category: 'Props',
37
- type: {
38
- summary: 'string',
39
- },
40
- },
41
- },
42
- value: {
43
- control: 'text',
44
- description: 'The value for the password field',
45
- table: {
46
- category: 'Props',
47
- type: {
48
- summary: 'string',
49
- },
50
- },
51
- },
52
- isDisabled: {
53
- control: 'boolean',
54
- description: 'Toggles the disabled state of the input',
55
- table: {
56
- category: 'Props',
57
- type: {
58
- summary: 'boolean',
59
- },
60
- },
61
- defaultValue: { summary: 'false' },
62
- },
63
- errorMessage: {
64
- control: false,
65
- description: 'An array of error messages',
66
- table: {
67
- category: 'Props',
68
- type: {
69
- summary: 'string',
70
- },
71
- },
72
- defaultValue: { summary: undefined },
73
- },
74
- helpText: {
75
- control: 'text',
76
- description: 'The help text for the password field',
77
- table: {
78
- category: 'Props',
79
- type: {
80
- summary: 'string',
81
- },
82
- },
83
- },
84
- ref: {
85
- table: {
86
- disable: true,
87
- },
88
- },
89
- ...labelArgTypes,
90
23
  },
91
24
  };
92
25
 
@@ -1,10 +1,10 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Icon } from '@/components/icons';
3
- import { withLabel, WithLabelProps } from '../subcomponents/Label';
3
+ import { withLabel, LabelProps } from '../subcomponents/Label';
4
4
  import { DisplayFormError } from '../subcomponents/DisplayFormError';
5
5
  import classNames from 'classnames';
6
6
 
7
- export interface PasswordInputProps extends WithLabelProps {
7
+ export interface PasswordInputProps extends LabelProps {
8
8
  ref?: React.LegacyRef<HTMLInputElement>;
9
9
  value?: string | undefined;
10
10
  placeholder?: string;
@@ -4,9 +4,6 @@ export interface LabelProps {
4
4
  label: string;
5
5
  name: string;
6
6
  isRequired?: boolean;
7
- }
8
-
9
- export interface WithLabelProps extends LabelProps {
10
7
  hasHiddenLabel?: boolean;
11
8
  }
12
9
 
@@ -24,7 +21,7 @@ export const Label = ({ label, name, isRequired }: LabelProps) => {
24
21
  // HOC to add common label functionality to components
25
22
  export function withLabel<P extends object>(WrappedComponent: React.ComponentType<P>) {
26
23
  const WithLabelComponent = (
27
- { label, hasHiddenLabel = false, name, isRequired, ...rest }: P & WithLabelProps,
24
+ { label, hasHiddenLabel = false, name, isRequired, ...rest }: P & LabelProps,
28
25
  ref: React.Ref<any>,
29
26
  ) => {
30
27
  const ariaLabel = hasHiddenLabel
@@ -1,78 +1,14 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { Meta, StoryObj } from '@storybook/react';
3
- import labelArgTypes from '@/storybook/labelArgTypes';
3
+ import { labelArgTypes, inputArgTypes, baseInputArgTypes } from '@/storybook/formArgTypes';
4
4
  import { Textarea } from './Textarea';
5
5
 
6
6
  const meta: Meta = {
7
7
  title: 'Forms/Textarea',
8
8
  component: Textarea,
9
9
  argTypes: {
10
- onChange: {
11
- control: false,
12
- description: 'onChange event handler',
13
- table: {
14
- category: 'Callbacks',
15
- type: {
16
- summary: '(e: React.ChangeEvent<HTMLInputElement>) => void',
17
- },
18
- },
19
- action: 'onChange',
20
- },
21
- placeholder: {
22
- control: 'text',
23
- description: 'The placeholder for the textarea field',
24
- table: {
25
- category: 'Props',
26
- type: {
27
- summary: 'string',
28
- },
29
- },
30
- defaultValue: { summary: '' },
31
- },
32
- value: {
33
- control: 'text',
34
- description: 'The value for the textarea field',
35
- table: {
36
- category: 'Props',
37
- type: {
38
- summary: 'string',
39
- },
40
- },
41
- defaultValue: { summary: '' },
42
- },
43
- isDisabled: {
44
- control: 'boolean',
45
- description: 'Toggles the disabled state of the textarea field',
46
- table: {
47
- category: 'Props',
48
- type: {
49
- summary: 'boolean',
50
- },
51
- },
52
- defaultValue: { summary: 'false' },
53
- },
54
- errorMessage: {
55
- control: false,
56
- description: 'An array of error messages',
57
- table: {
58
- category: 'Props',
59
- type: {
60
- summary: 'string[]',
61
- },
62
- },
63
- defaultValue: { summary: '[]' },
64
- },
65
- helpText: {
66
- control: 'text',
67
- description: 'The help text for the textarea field',
68
- table: {
69
- category: 'Props',
70
- type: {
71
- summary: 'string',
72
- },
73
- },
74
- defaultValue: { summary: '' },
75
- },
10
+ ...labelArgTypes,
11
+ ...baseInputArgTypes,
76
12
  autofocus: {
77
13
  control: 'boolean',
78
14
  description: ' Specifies that a text area should automatically get focus when the page loads',
@@ -155,7 +91,6 @@ const meta: Meta = {
155
91
  disable: true,
156
92
  },
157
93
  },
158
- ...labelArgTypes,
159
94
  },
160
95
  };
161
96
 
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
2
  import classNames from 'classnames';
3
3
 
4
- import { withLabel, LabelProps, WithLabelProps } from '../subcomponents/Label';
4
+ import { withLabel, LabelProps } from '../subcomponents/Label';
5
5
  import { DisplayFormError } from '../subcomponents/DisplayFormError';
6
6
 
7
- export interface TextareaProps extends WithLabelProps {
7
+ export interface TextareaProps extends LabelProps {
8
8
  ref?: React.LegacyRef<HTMLTextAreaElement>;
9
9
  placeholder?: string;
10
10
  value?: string | undefined;
@@ -0,0 +1,152 @@
1
+ // Common argTypes for form components that include a label and input
2
+ import { ArgTypes } from '@storybook/react';
3
+ import { iconNames } from 'build/generated/iconTypes';
4
+
5
+ const labelArgTypes: ArgTypes = {
6
+ label: {
7
+ control: 'text',
8
+ description: 'The text to display as the label for the form component',
9
+ table: {
10
+ category: 'Props',
11
+ type: {
12
+ summary: 'string',
13
+ },
14
+ },
15
+ },
16
+ isRequired: {
17
+ control: 'boolean',
18
+ description: 'Toggles the required asterisk on the label',
19
+ table: {
20
+ category: 'Props',
21
+ type: {
22
+ summary: 'boolean',
23
+ },
24
+ },
25
+ defaultValue: { summary: 'false' },
26
+ },
27
+ hasHiddenLabel: {
28
+ control: 'boolean',
29
+ description:
30
+ 'Determines whether the label should be displayed or not. Included as aria-label if true',
31
+ table: {
32
+ category: 'Props',
33
+ type: {
34
+ summary: 'boolean',
35
+ },
36
+ },
37
+ defaultValue: { summary: 'false' },
38
+ },
39
+ name: {
40
+ control: 'text',
41
+ description: 'The name attribute for the form component, used for identifying the field',
42
+ table: {
43
+ category: 'Props',
44
+ type: {
45
+ summary: 'string',
46
+ },
47
+ },
48
+ },
49
+ };
50
+
51
+ const baseInputArgTypes: ArgTypes = {
52
+ onChange: {
53
+ control: false,
54
+ description: 'onChange event handler',
55
+ table: {
56
+ category: 'Callbacks',
57
+ type: {
58
+ summary: '(e: React.ChangeEvent<HTMLInputElement>) => void',
59
+ },
60
+ },
61
+ type: { name: 'function', required: true },
62
+ action: 'onChange',
63
+ },
64
+ placeholder: {
65
+ control: 'text',
66
+ description: 'The placeholder for the form component',
67
+ table: {
68
+ category: 'Props',
69
+ type: {
70
+ summary: 'string',
71
+ },
72
+ },
73
+ type: { name: 'string', required: false },
74
+ },
75
+ value: {
76
+ control: 'text',
77
+ description: 'The value for the form component',
78
+ table: {
79
+ category: 'Props',
80
+ type: {
81
+ summary: 'string',
82
+ },
83
+ },
84
+ type: { name: 'string', required: false },
85
+ },
86
+ isDisabled: {
87
+ control: 'boolean',
88
+ description: 'Toggles the disabled state of the input',
89
+ table: {
90
+ category: 'Props',
91
+ type: {
92
+ summary: 'boolean',
93
+ },
94
+ },
95
+ defaultValue: { summary: 'false' },
96
+ },
97
+ errorMessage: {
98
+ control: 'text',
99
+ description: 'Error message',
100
+ table: {
101
+ category: 'Props',
102
+ type: {
103
+ summary: 'string',
104
+ },
105
+ },
106
+ defaultValue: { summary: '' },
107
+ },
108
+ helpText: {
109
+ control: 'text',
110
+ description: 'The help text for the form component',
111
+ table: {
112
+ category: 'Props',
113
+ type: {
114
+ summary: 'string',
115
+ },
116
+ },
117
+ },
118
+ ref: {
119
+ table: {
120
+ disable: true,
121
+ },
122
+ },
123
+ };
124
+
125
+ const inputArgTypes: ArgTypes = {
126
+ ...baseInputArgTypes,
127
+ iconName: {
128
+ control: 'select',
129
+ options: iconNames,
130
+ description: 'Adds an icon to the left hand side of the form component',
131
+ table: {
132
+ category: 'Props',
133
+ type: {
134
+ summary: 'string',
135
+ },
136
+ },
137
+ defaultValue: { summary: '' },
138
+ },
139
+ isClearable: {
140
+ control: 'boolean',
141
+ description: 'Adds a clear x icon to the right-hand side of the form component',
142
+ table: {
143
+ category: 'Props',
144
+ type: {
145
+ summary: 'boolean',
146
+ },
147
+ },
148
+ defaultValue: { summary: 'false' },
149
+ },
150
+ };
151
+
152
+ export { labelArgTypes, baseInputArgTypes, inputArgTypes };
@@ -7,6 +7,7 @@
7
7
  @import '../components/grid/styles/Grid.scss';
8
8
  @import '../components/table/styles/Table.scss';
9
9
  @import '../components/forms/input/styles/Input.scss';
10
+ @import '../components/forms/numberInput/styles/NumberInput.scss';
10
11
  @import '../components/forms/radio/styles/Radio.scss';
11
12
  @import '../components/forms/checkbox/styles/Checkbox.scss';
12
13
  @import '../components/forms/textarea/styles/Textarea.scss';
@@ -1,3 +0,0 @@
1
- import { ArgTypes } from '@storybook/react';
2
- declare const labelArgTypes: ArgTypes;
3
- export default labelArgTypes;
@@ -1,50 +0,0 @@
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;