agroptima-design-system 0.4.0 → 0.5.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/package.json +1 -1
- package/src/atoms/Button.scss +1 -1
- package/src/atoms/Button.tsx +14 -2
- package/src/atoms/IconButton.scss +50 -0
- package/src/atoms/IconButton.tsx +57 -0
- package/src/atoms/Input.tsx +17 -5
- package/src/atoms/Multiselect.tsx +32 -8
- package/src/atoms/Select.tsx +32 -8
- package/src/icons/delete.svg +1 -0
- package/src/icons/edit.svg +1 -0
- package/src/icons/index.tsx +4 -0
- package/src/stories/Button.stories.ts +5 -0
- package/src/stories/Changelog.stories.mdx +13 -0
- package/src/stories/IconButton.stories.ts +58 -0
- package/src/stories/Input.stories.ts +25 -0
- package/src/stories/Multiselect.stories.ts +36 -5
- package/src/stories/Select.stories.ts +34 -2
package/package.json
CHANGED
package/src/atoms/Button.scss
CHANGED
package/src/atoms/Button.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { Icon, IconType } from './Icon'
|
|
|
4
4
|
|
|
5
5
|
export interface BaseButtonProps {
|
|
6
6
|
label: string
|
|
7
|
+
accessibilityLabel?: string
|
|
7
8
|
leftIcon?: IconType
|
|
8
9
|
rightIcon?: IconType
|
|
9
10
|
variant?: ButtonVariant
|
|
@@ -44,6 +45,7 @@ export type ButtonVariant =
|
|
|
44
45
|
|
|
45
46
|
export function Button({
|
|
46
47
|
label,
|
|
48
|
+
accessibilityLabel,
|
|
47
49
|
leftIcon,
|
|
48
50
|
rightIcon,
|
|
49
51
|
disabled,
|
|
@@ -58,7 +60,12 @@ export function Button({
|
|
|
58
60
|
|
|
59
61
|
if (hasHref(props)) {
|
|
60
62
|
return (
|
|
61
|
-
<NextLink
|
|
63
|
+
<NextLink
|
|
64
|
+
href={props.href || ''}
|
|
65
|
+
className={cssClasses}
|
|
66
|
+
aria-label={accessibilityLabel || label}
|
|
67
|
+
{...props}
|
|
68
|
+
>
|
|
62
69
|
{leftIcon && <Icon name={leftIcon} />}
|
|
63
70
|
{label}
|
|
64
71
|
{rightIcon && <Icon name={rightIcon} />}
|
|
@@ -67,7 +74,12 @@ export function Button({
|
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
return (
|
|
70
|
-
<button
|
|
77
|
+
<button
|
|
78
|
+
className={cssClasses}
|
|
79
|
+
disabled={loading || disabled}
|
|
80
|
+
aria-label={accessibilityLabel || label}
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
71
83
|
{leftIcon && <Icon name={leftIcon} />}
|
|
72
84
|
{label}
|
|
73
85
|
{rightIcon && <Icon name={rightIcon} />}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
@use '../settings/color_alias';
|
|
2
|
+
@use '../settings/typography';
|
|
3
|
+
@use '../settings/config';
|
|
4
|
+
|
|
5
|
+
.icon-button {
|
|
6
|
+
border: none;
|
|
7
|
+
background: none;
|
|
8
|
+
|
|
9
|
+
> .icon {
|
|
10
|
+
width: config.$icon-size-5x;
|
|
11
|
+
height: config.$icon-size-5x;
|
|
12
|
+
> svg {
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 100%;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&.primary {
|
|
19
|
+
> .icon {
|
|
20
|
+
> svg {
|
|
21
|
+
fill: color_alias.$primary-color-600;
|
|
22
|
+
path {
|
|
23
|
+
fill: color_alias.$primary-color-600;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:hover {
|
|
29
|
+
> .icon {
|
|
30
|
+
> svg {
|
|
31
|
+
fill: color_alias.$primary-color-1000;
|
|
32
|
+
path {
|
|
33
|
+
fill: color_alias.$primary-color-1000;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&:disabled {
|
|
40
|
+
> .icon {
|
|
41
|
+
> svg {
|
|
42
|
+
fill: color_alias.$neutral-color-400;
|
|
43
|
+
path {
|
|
44
|
+
fill: color_alias.$neutral-color-400;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import NextLink from 'next/link'
|
|
2
|
+
import './IconButton.scss'
|
|
3
|
+
import { Icon, IconType } from './Icon'
|
|
4
|
+
|
|
5
|
+
export type Variant = 'primary'
|
|
6
|
+
|
|
7
|
+
export interface BaseIconButtonProps {
|
|
8
|
+
icon: IconType
|
|
9
|
+
variant?: Variant
|
|
10
|
+
disabled?: boolean
|
|
11
|
+
accessibilityLabel: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type HtmlButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
15
|
+
|
|
16
|
+
type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>
|
|
17
|
+
|
|
18
|
+
export type IconButtonProps =
|
|
19
|
+
| (HtmlButtonProps & BaseIconButtonProps)
|
|
20
|
+
| (AnchorProps & BaseIconButtonProps)
|
|
21
|
+
|
|
22
|
+
const hasHref = (props: HtmlButtonProps | AnchorProps): props is AnchorProps =>
|
|
23
|
+
'href' in props
|
|
24
|
+
|
|
25
|
+
export function IconButton({
|
|
26
|
+
accessibilityLabel,
|
|
27
|
+
icon,
|
|
28
|
+
disabled,
|
|
29
|
+
variant = 'primary',
|
|
30
|
+
...props
|
|
31
|
+
}: IconButtonProps) {
|
|
32
|
+
const cssClasses = ['icon-button', variant].join(' ')
|
|
33
|
+
|
|
34
|
+
if (hasHref(props)) {
|
|
35
|
+
return (
|
|
36
|
+
<NextLink
|
|
37
|
+
href={props.href || ''}
|
|
38
|
+
className={cssClasses}
|
|
39
|
+
aria-label={accessibilityLabel}
|
|
40
|
+
{...props}
|
|
41
|
+
>
|
|
42
|
+
<Icon name={icon} />
|
|
43
|
+
</NextLink>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<button
|
|
49
|
+
className={cssClasses}
|
|
50
|
+
disabled={disabled}
|
|
51
|
+
aria-label={accessibilityLabel}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
<Icon name={icon} />
|
|
55
|
+
</button>
|
|
56
|
+
)
|
|
57
|
+
}
|
package/src/atoms/Input.tsx
CHANGED
|
@@ -6,16 +6,18 @@ export type InputVariant = 'primary'
|
|
|
6
6
|
|
|
7
7
|
export interface InputProps extends React.ComponentPropsWithoutRef<'input'> {
|
|
8
8
|
label: string
|
|
9
|
+
accessibilityLabel?: string
|
|
9
10
|
hideLabel?: boolean
|
|
10
11
|
icon?: IconType
|
|
11
12
|
helpText?: string
|
|
12
13
|
variant?: InputVariant
|
|
13
14
|
id: string
|
|
14
|
-
|
|
15
|
+
errors?: string[]
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function Input({
|
|
18
19
|
label,
|
|
20
|
+
accessibilityLabel,
|
|
19
21
|
hideLabel = false,
|
|
20
22
|
icon,
|
|
21
23
|
helpText,
|
|
@@ -24,12 +26,12 @@ export function Input({
|
|
|
24
26
|
type = 'text',
|
|
25
27
|
name,
|
|
26
28
|
id,
|
|
27
|
-
|
|
29
|
+
errors,
|
|
28
30
|
...props
|
|
29
31
|
}: InputProps): React.JSX.Element {
|
|
30
32
|
const [showPassword, setShowPassword] = useState(false)
|
|
31
33
|
const iconClass = icon ? 'with-icon' : ''
|
|
32
|
-
const invalidClass =
|
|
34
|
+
const invalidClass = errors ? 'invalid' : ''
|
|
33
35
|
const cssClasses = ['input', iconClass, invalidClass].join(' ')
|
|
34
36
|
|
|
35
37
|
function handlePasswordIcon() {
|
|
@@ -61,7 +63,7 @@ export function Input({
|
|
|
61
63
|
disabled={disabled}
|
|
62
64
|
type={handleInputType()}
|
|
63
65
|
name={name}
|
|
64
|
-
aria-label={label}
|
|
66
|
+
aria-label={accessibilityLabel || label}
|
|
65
67
|
{...props}
|
|
66
68
|
/>
|
|
67
69
|
{type === 'password' && (
|
|
@@ -72,7 +74,17 @@ export function Input({
|
|
|
72
74
|
/>
|
|
73
75
|
)}
|
|
74
76
|
</div>
|
|
75
|
-
{helpText &&
|
|
77
|
+
{helpText && !errors && (
|
|
78
|
+
<span className="input-help-text">{helpText}</span>
|
|
79
|
+
)}
|
|
80
|
+
{errors &&
|
|
81
|
+
errors?.map((error, index) => {
|
|
82
|
+
return (
|
|
83
|
+
<span key={`error-${index}`} className="input-help-text">
|
|
84
|
+
{error}
|
|
85
|
+
</span>
|
|
86
|
+
)
|
|
87
|
+
})}
|
|
76
88
|
</div>
|
|
77
89
|
)
|
|
78
90
|
}
|
|
@@ -6,13 +6,14 @@ export type Variant = 'primary'
|
|
|
6
6
|
export type Option = { id: string; label: string }
|
|
7
7
|
|
|
8
8
|
export interface MultiselectProps
|
|
9
|
-
extends React.ComponentPropsWithoutRef<'
|
|
9
|
+
extends React.ComponentPropsWithoutRef<'input'> {
|
|
10
10
|
placeholder?: string
|
|
11
11
|
helpText?: string
|
|
12
12
|
variant?: Variant
|
|
13
13
|
options: Option[]
|
|
14
|
-
|
|
14
|
+
errors?: string[]
|
|
15
15
|
label: string
|
|
16
|
+
accessibilityLabel?: string
|
|
16
17
|
selectedLabel?: string
|
|
17
18
|
hideLabel?: boolean
|
|
18
19
|
selected?: Option[]
|
|
@@ -23,13 +24,15 @@ export function Multiselect({
|
|
|
23
24
|
helpText,
|
|
24
25
|
variant = 'primary',
|
|
25
26
|
disabled,
|
|
26
|
-
|
|
27
|
+
errors,
|
|
27
28
|
name,
|
|
28
29
|
options,
|
|
29
30
|
label,
|
|
31
|
+
accessibilityLabel,
|
|
30
32
|
selectedLabel = 'items selected',
|
|
31
33
|
hideLabel = false,
|
|
32
34
|
selected,
|
|
35
|
+
...props
|
|
33
36
|
}: MultiselectProps): React.JSX.Element {
|
|
34
37
|
const [showOptionsList, setShowOptionsList] = useState(false)
|
|
35
38
|
const [selectedOptionsIds, setSelectedOptionsIds] = useState<string[]>(
|
|
@@ -39,7 +42,7 @@ export function Multiselect({
|
|
|
39
42
|
const optionsListOpenClass = showOptionsList ? 'open' : ''
|
|
40
43
|
const filledSelectClass = selectedOptionsIds.length > 0 ? 'filled' : ''
|
|
41
44
|
const disabledClass = disabled ? 'disabled' : ''
|
|
42
|
-
const invalidClass =
|
|
45
|
+
const invalidClass = errors ? 'invalid' : ''
|
|
43
46
|
|
|
44
47
|
const cssClasses = [
|
|
45
48
|
'selected-option',
|
|
@@ -74,15 +77,22 @@ export function Multiselect({
|
|
|
74
77
|
return selectedOptionsIds.includes(optionId)
|
|
75
78
|
}
|
|
76
79
|
|
|
80
|
+
function handleBlur(event: React.FocusEvent<HTMLDivElement>) {
|
|
81
|
+
const isAComponentElement = event.relatedTarget
|
|
82
|
+
if (!isAComponentElement) {
|
|
83
|
+
setShowOptionsList(false)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
77
87
|
return (
|
|
78
88
|
<div className={`multiselect-group ${variant}`}>
|
|
79
89
|
{!hideLabel && <span className="multiselect-label">{label}</span>}
|
|
80
|
-
<div className="multiselect-container">
|
|
90
|
+
<div className="multiselect-container" onBlur={handleBlur}>
|
|
81
91
|
<div
|
|
82
92
|
className={cssClasses}
|
|
83
93
|
tabIndex={0}
|
|
84
94
|
onClick={handleOptionsList}
|
|
85
|
-
aria-label={label}
|
|
95
|
+
aria-label={accessibilityLabel || label}
|
|
86
96
|
aria-live="assertive"
|
|
87
97
|
role="alert"
|
|
88
98
|
>
|
|
@@ -99,6 +109,7 @@ export function Multiselect({
|
|
|
99
109
|
return (
|
|
100
110
|
<li
|
|
101
111
|
className="option"
|
|
112
|
+
tabIndex={0}
|
|
102
113
|
role="option"
|
|
103
114
|
aria-selected={isOptionSelected(option.id)}
|
|
104
115
|
data-option={option}
|
|
@@ -119,12 +130,25 @@ export function Multiselect({
|
|
|
119
130
|
</ul>
|
|
120
131
|
)}
|
|
121
132
|
</div>
|
|
122
|
-
{helpText && (
|
|
133
|
+
{helpText && !errors && (
|
|
123
134
|
<span className={`multiselect-help-text ${invalidClass}`}>
|
|
124
135
|
{helpText}
|
|
125
136
|
</span>
|
|
126
137
|
)}
|
|
127
|
-
|
|
138
|
+
{errors &&
|
|
139
|
+
errors?.map((error, index) => {
|
|
140
|
+
return (
|
|
141
|
+
<span key={`error-${index}`} className="multiselect-help-text">
|
|
142
|
+
{error}
|
|
143
|
+
</span>
|
|
144
|
+
)
|
|
145
|
+
})}
|
|
146
|
+
<input
|
|
147
|
+
type="hidden"
|
|
148
|
+
name={name}
|
|
149
|
+
value={selectedOptionsIds.toString()}
|
|
150
|
+
{...props}
|
|
151
|
+
/>
|
|
128
152
|
</div>
|
|
129
153
|
)
|
|
130
154
|
}
|
package/src/atoms/Select.tsx
CHANGED
|
@@ -5,13 +5,14 @@ import { Icon } from './Icon'
|
|
|
5
5
|
export type Variant = 'primary'
|
|
6
6
|
export type Option = { id: string; label: string }
|
|
7
7
|
|
|
8
|
-
export interface SelectProps extends React.ComponentPropsWithoutRef<'
|
|
8
|
+
export interface SelectProps extends React.ComponentPropsWithoutRef<'input'> {
|
|
9
9
|
placeholder?: string
|
|
10
10
|
helpText?: string
|
|
11
11
|
variant?: Variant
|
|
12
12
|
options: Option[]
|
|
13
|
-
|
|
13
|
+
errors?: string[]
|
|
14
14
|
label: string
|
|
15
|
+
accessibilityLabel?: string
|
|
15
16
|
hideLabel?: boolean
|
|
16
17
|
selected?: Option
|
|
17
18
|
}
|
|
@@ -21,12 +22,14 @@ export function Select({
|
|
|
21
22
|
helpText,
|
|
22
23
|
variant = 'primary',
|
|
23
24
|
disabled,
|
|
24
|
-
|
|
25
|
+
errors,
|
|
25
26
|
name,
|
|
26
27
|
options,
|
|
27
28
|
label,
|
|
29
|
+
accessibilityLabel,
|
|
28
30
|
hideLabel = false,
|
|
29
31
|
selected,
|
|
32
|
+
...props
|
|
30
33
|
}: SelectProps): React.JSX.Element {
|
|
31
34
|
const [showOptionsList, setShowOptionsList] = useState(false)
|
|
32
35
|
const [selectedOption, setSelectedOption] = useState<Option>({
|
|
@@ -37,7 +40,7 @@ export function Select({
|
|
|
37
40
|
const optionsListOpenClass = showOptionsList ? 'open' : ''
|
|
38
41
|
const filledSelectClass = selectedOption.id ? 'filled' : ''
|
|
39
42
|
const disabledClass = disabled ? 'disabled' : ''
|
|
40
|
-
const invalidClass =
|
|
43
|
+
const invalidClass = errors ? 'invalid' : ''
|
|
41
44
|
|
|
42
45
|
const cssClasses = [
|
|
43
46
|
'selected-option',
|
|
@@ -52,23 +55,33 @@ export function Select({
|
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
function selectOption(option: Option) {
|
|
58
|
+
const { onChange } = props
|
|
55
59
|
setSelectedOption(option)
|
|
56
60
|
setShowOptionsList(false)
|
|
61
|
+
|
|
62
|
+
if (onChange !== undefined) onChange(option.id)
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
function handleSelectIcon() {
|
|
60
66
|
return showOptionsList ? 'AngleUp' : 'AngleDown'
|
|
61
67
|
}
|
|
62
68
|
|
|
69
|
+
function handleBlur(event: React.FocusEvent<HTMLDivElement>) {
|
|
70
|
+
const isAComponentElement = event.relatedTarget
|
|
71
|
+
if (!isAComponentElement) {
|
|
72
|
+
setShowOptionsList(false)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
63
76
|
return (
|
|
64
77
|
<div className={`select-group ${variant}`}>
|
|
65
78
|
{!hideLabel && <span className="select-label">{label}</span>}
|
|
66
|
-
<div className="select-container">
|
|
79
|
+
<div className="select-container" onBlur={handleBlur}>
|
|
67
80
|
<div
|
|
68
81
|
className={cssClasses}
|
|
69
82
|
tabIndex={0}
|
|
70
83
|
onClick={handleOptionsList}
|
|
71
|
-
aria-label={label}
|
|
84
|
+
aria-label={accessibilityLabel || label}
|
|
72
85
|
aria-live="assertive"
|
|
73
86
|
role="alert"
|
|
74
87
|
>
|
|
@@ -81,6 +94,7 @@ export function Select({
|
|
|
81
94
|
return (
|
|
82
95
|
<li
|
|
83
96
|
className="option"
|
|
97
|
+
tabIndex={0}
|
|
84
98
|
role="option"
|
|
85
99
|
aria-selected={selectedOption.id === option.id}
|
|
86
100
|
data-option={option}
|
|
@@ -94,8 +108,18 @@ export function Select({
|
|
|
94
108
|
</ul>
|
|
95
109
|
)}
|
|
96
110
|
</div>
|
|
97
|
-
{helpText &&
|
|
98
|
-
|
|
111
|
+
{helpText && !errors && (
|
|
112
|
+
<span className="select-help-text">{helpText}</span>
|
|
113
|
+
)}
|
|
114
|
+
{errors &&
|
|
115
|
+
errors?.map((error, index) => {
|
|
116
|
+
return (
|
|
117
|
+
<span key={`error-${index}`} className="select-help-text">
|
|
118
|
+
{error}
|
|
119
|
+
</span>
|
|
120
|
+
)
|
|
121
|
+
})}
|
|
122
|
+
<input type="hidden" name={name} value={selectedOption.id} {...props} />
|
|
99
123
|
</div>
|
|
100
124
|
)
|
|
101
125
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M3.143 17.778C3.143 19 4.17 20 5.429 20h9.142c1.258 0 2.286-1 2.286-2.222V4.444H3.143v13.334ZM5.429 6.667h9.142v11.11H5.43V6.668ZM14 1.11 12.857 0H7.143L6 1.111H2v2.222h16V1.111h-4Z" fill="#444"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><g clip-path="url(#edit__a)"><path d="M12.287 6.689 13.31 7.71 3.244 17.778H2.222v-1.022L12.287 6.689Zm4-6.689c-.278 0-.567.111-.778.322l-2.033 2.034 4.166 4.166 2.033-2.033a1.107 1.107 0 0 0 0-1.567l-2.6-2.6A1.09 1.09 0 0 0 16.287 0Zm-4 3.544L0 15.834V20h4.166L16.453 7.711l-4.166-4.167Z" fill="#444"/></g><defs><clipPath id="edit__a"><path fill="#fff" d="M0 0h20v20H0z"/></clipPath></defs></svg>
|
package/src/icons/index.tsx
CHANGED
|
@@ -7,7 +7,9 @@ import Check from './check.svg'
|
|
|
7
7
|
import CheckboxActive from './checkbox-active.svg'
|
|
8
8
|
import CheckboxInactive from './checkbox-inactive.svg'
|
|
9
9
|
import Close from './close.svg'
|
|
10
|
+
import Delete from './delete.svg'
|
|
10
11
|
import Done from './done.svg'
|
|
12
|
+
import Edit from './edit.svg'
|
|
11
13
|
import EditColumns from './edit-columns.svg'
|
|
12
14
|
import EmptyState from './empty-customize.svg'
|
|
13
15
|
import Error from './error.svg'
|
|
@@ -30,7 +32,9 @@ export {
|
|
|
30
32
|
CheckboxActive,
|
|
31
33
|
CheckboxInactive,
|
|
32
34
|
Close,
|
|
35
|
+
Delete,
|
|
33
36
|
Done,
|
|
37
|
+
Edit,
|
|
34
38
|
EditColumns,
|
|
35
39
|
EmptyState,
|
|
36
40
|
Error,
|
|
@@ -12,6 +12,10 @@ const meta = {
|
|
|
12
12
|
label: {
|
|
13
13
|
description: 'Button text content',
|
|
14
14
|
},
|
|
15
|
+
accessibilityLabel: {
|
|
16
|
+
description:
|
|
17
|
+
'Describes the button action. If empty, label content will be used',
|
|
18
|
+
},
|
|
15
19
|
variant: {
|
|
16
20
|
description: 'Button variant used from a list of values',
|
|
17
21
|
},
|
|
@@ -96,6 +100,7 @@ export const Primary: Story = {
|
|
|
96
100
|
variant: 'primary',
|
|
97
101
|
disabled: false,
|
|
98
102
|
loading: false,
|
|
103
|
+
accessibilityLabel: 'Triggers a custom action',
|
|
99
104
|
},
|
|
100
105
|
parameters: figmaPrimaryDesign,
|
|
101
106
|
}
|
|
@@ -3,6 +3,19 @@ import { Meta } from "@storybook/addon-docs";
|
|
|
3
3
|
<Meta title="Changelog" />
|
|
4
4
|
# Changelog
|
|
5
5
|
|
|
6
|
+
## 0.5.0
|
|
7
|
+
|
|
8
|
+
- IconButton component is added to Storybook.
|
|
9
|
+
- Edit and Delete icons have been added.
|
|
10
|
+
- Button outlined disabled styles have been changed to make them look the same as the filled disabled styles.
|
|
11
|
+
- `accessibilityLabel` specific prop has been added as optional to Button, Input, Select and Multiselect components.
|
|
12
|
+
- Select and Multiselect can be closed too when losing focus.
|
|
13
|
+
- Select and Multiselect accept native props.
|
|
14
|
+
- Select component triggers `onChange` event prop.
|
|
15
|
+
|
|
16
|
+
BREAKING CHANGES
|
|
17
|
+
- Input, Select and Multiselect components accept an `errors` array prop. Previous `invalid` prop is deprecated.
|
|
18
|
+
|
|
6
19
|
## 0.4.0
|
|
7
20
|
|
|
8
21
|
- CardsTable component suitable to create custom tables is added to Storybook.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { StoryObj } from '@storybook/react'
|
|
2
|
+
import { IconButton } from '../atoms/IconButton'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Design System/Atoms/IconButton',
|
|
6
|
+
component: IconButton,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
accessibilityLabel: {
|
|
10
|
+
description: 'Accessible name & description of the element',
|
|
11
|
+
},
|
|
12
|
+
variant: {
|
|
13
|
+
description: 'IconButton variant used from a list of values',
|
|
14
|
+
},
|
|
15
|
+
disabled: {
|
|
16
|
+
description: 'Is the button in disabled state?',
|
|
17
|
+
},
|
|
18
|
+
icon: {
|
|
19
|
+
description: 'Icon from a list of values',
|
|
20
|
+
control: { type: 'select' },
|
|
21
|
+
},
|
|
22
|
+
href: {
|
|
23
|
+
description:
|
|
24
|
+
'If a link is provided, the component will be rendered as NextLink, otherwise as button',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const figmaPrimaryDesign = {
|
|
30
|
+
design: {
|
|
31
|
+
type: 'figma',
|
|
32
|
+
url: 'https://www.figma.com/file/DN2ova21vWqCRvPspBXgI1/Design-System?type=design&node-id=1873-922&mode=dev',
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default meta
|
|
37
|
+
type Story = StoryObj<typeof meta>
|
|
38
|
+
|
|
39
|
+
export const Link: Story = {
|
|
40
|
+
args: {
|
|
41
|
+
icon: 'Edit',
|
|
42
|
+
variant: 'primary',
|
|
43
|
+
accessibilityLabel: 'Edit game',
|
|
44
|
+
href: 'link.com',
|
|
45
|
+
disabled: false,
|
|
46
|
+
},
|
|
47
|
+
parameters: figmaPrimaryDesign,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const Primary: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
icon: 'Delete',
|
|
53
|
+
variant: 'primary',
|
|
54
|
+
accessibilityLabel: 'Delete game',
|
|
55
|
+
disabled: false,
|
|
56
|
+
},
|
|
57
|
+
parameters: figmaPrimaryDesign,
|
|
58
|
+
}
|
|
@@ -9,6 +9,10 @@ const meta = {
|
|
|
9
9
|
label: {
|
|
10
10
|
description: 'Label for the input',
|
|
11
11
|
},
|
|
12
|
+
accessibilityLabel: {
|
|
13
|
+
description:
|
|
14
|
+
'Describes the input purpose. If empty, label content will be used',
|
|
15
|
+
},
|
|
12
16
|
placeholder: {
|
|
13
17
|
description: 'Optional input placeholder text',
|
|
14
18
|
},
|
|
@@ -33,6 +37,10 @@ const meta = {
|
|
|
33
37
|
id: {
|
|
34
38
|
description: 'Value needed for the label relation',
|
|
35
39
|
},
|
|
40
|
+
errors: {
|
|
41
|
+
description:
|
|
42
|
+
'Optional array of errors. If passed, the errors are listed and invalid style is applied.',
|
|
43
|
+
},
|
|
36
44
|
},
|
|
37
45
|
}
|
|
38
46
|
|
|
@@ -49,6 +57,7 @@ type Story = StoryObj<typeof meta>
|
|
|
49
57
|
export const Primary: Story = {
|
|
50
58
|
args: {
|
|
51
59
|
label: 'Email:',
|
|
60
|
+
accessibilityLabel: 'Fill the form email',
|
|
52
61
|
placeholder: 'Email...',
|
|
53
62
|
variant: 'primary',
|
|
54
63
|
disabled: false,
|
|
@@ -73,3 +82,19 @@ export const Password: Story = {
|
|
|
73
82
|
},
|
|
74
83
|
parameters: figmaPrimaryDesign,
|
|
75
84
|
}
|
|
85
|
+
|
|
86
|
+
export const WithErrors: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
label: 'Email:',
|
|
89
|
+
accessibilityLabel: 'Fill the form email',
|
|
90
|
+
placeholder: 'Email...',
|
|
91
|
+
variant: 'primary',
|
|
92
|
+
disabled: false,
|
|
93
|
+
helpText: 'This text can help you',
|
|
94
|
+
name: 'email',
|
|
95
|
+
type: 'email',
|
|
96
|
+
id: 'email_input',
|
|
97
|
+
errors: ['error1', 'error2'],
|
|
98
|
+
},
|
|
99
|
+
parameters: figmaPrimaryDesign,
|
|
100
|
+
}
|
|
@@ -9,6 +9,10 @@ const meta = {
|
|
|
9
9
|
label: {
|
|
10
10
|
description: 'Label for the select',
|
|
11
11
|
},
|
|
12
|
+
accessibilityLabel: {
|
|
13
|
+
description:
|
|
14
|
+
'Describes the select purpose. If empty, label content will be used',
|
|
15
|
+
},
|
|
12
16
|
selectedLabel: {
|
|
13
17
|
description: 'Label used when having selected values',
|
|
14
18
|
},
|
|
@@ -18,9 +22,6 @@ const meta = {
|
|
|
18
22
|
disabled: {
|
|
19
23
|
description: 'Is the select in disabled state?',
|
|
20
24
|
},
|
|
21
|
-
invalid: {
|
|
22
|
-
description: 'Is the select in disabled state?',
|
|
23
|
-
},
|
|
24
25
|
helpText: {
|
|
25
26
|
description: 'Optional help text',
|
|
26
27
|
},
|
|
@@ -36,6 +37,10 @@ const meta = {
|
|
|
36
37
|
selected: {
|
|
37
38
|
description: 'Array of values to be displayed as selected',
|
|
38
39
|
},
|
|
40
|
+
errors: {
|
|
41
|
+
description:
|
|
42
|
+
'Optional array of errors. If passed, the errors are listed and invalid style is applied.',
|
|
43
|
+
},
|
|
39
44
|
},
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -53,11 +58,11 @@ export const Primary: Story = {
|
|
|
53
58
|
args: {
|
|
54
59
|
variant: 'primary',
|
|
55
60
|
disabled: false,
|
|
56
|
-
invalid: false,
|
|
57
61
|
hideLabel: false,
|
|
58
62
|
helpText: 'This text can help you',
|
|
59
63
|
name: 'example',
|
|
60
64
|
label: 'Videogames',
|
|
65
|
+
accessibilityLabel: 'Select your favourite videogames options',
|
|
61
66
|
selectedLabel: 'videogames selected',
|
|
62
67
|
placeholder: 'Select your favourite videogames...',
|
|
63
68
|
options: [
|
|
@@ -68,6 +73,7 @@ export const Primary: Story = {
|
|
|
68
73
|
{ id: '5', label: 'Super Mario Bros' },
|
|
69
74
|
{ id: '6', label: 'Red Dead Redemption' },
|
|
70
75
|
],
|
|
76
|
+
id: 'multiselect-videogames',
|
|
71
77
|
},
|
|
72
78
|
parameters: figmaPrimaryDesign,
|
|
73
79
|
}
|
|
@@ -76,7 +82,6 @@ export const PrimaryWithSelectedOptions: Story = {
|
|
|
76
82
|
args: {
|
|
77
83
|
variant: 'primary',
|
|
78
84
|
disabled: false,
|
|
79
|
-
invalid: false,
|
|
80
85
|
hideLabel: false,
|
|
81
86
|
helpText: 'This text can help you',
|
|
82
87
|
name: 'example',
|
|
@@ -95,6 +100,32 @@ export const PrimaryWithSelectedOptions: Story = {
|
|
|
95
100
|
{ id: '2', label: 'Spyro the Dragon' },
|
|
96
101
|
{ id: '1', label: 'The Legend of Zelda: Ocarina of Time' },
|
|
97
102
|
],
|
|
103
|
+
id: 'multiselect-videogames',
|
|
104
|
+
},
|
|
105
|
+
parameters: figmaPrimaryDesign,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const PrimaryWithErrors: Story = {
|
|
109
|
+
args: {
|
|
110
|
+
variant: 'primary',
|
|
111
|
+
disabled: false,
|
|
112
|
+
hideLabel: false,
|
|
113
|
+
helpText: 'This text can help you',
|
|
114
|
+
name: 'example',
|
|
115
|
+
label: 'Videogames',
|
|
116
|
+
accessibilityLabel: 'Select your favourite videogames options',
|
|
117
|
+
selectedLabel: 'videogames selected',
|
|
118
|
+
placeholder: 'Select your favourite videogames...',
|
|
119
|
+
options: [
|
|
120
|
+
{ id: '1', label: 'The Legend of Zelda: Ocarina of Time' },
|
|
121
|
+
{ id: '2', label: 'Spyro the Dragon' },
|
|
122
|
+
{ id: '3', label: 'Halo' },
|
|
123
|
+
{ id: '4', label: 'Tetris' },
|
|
124
|
+
{ id: '5', label: 'Super Mario Bros' },
|
|
125
|
+
{ id: '6', label: 'Red Dead Redemption' },
|
|
126
|
+
],
|
|
127
|
+
id: 'multiselect-videogames',
|
|
128
|
+
errors: ['error1', 'error2'],
|
|
98
129
|
},
|
|
99
130
|
parameters: figmaPrimaryDesign,
|
|
100
131
|
}
|
|
@@ -9,6 +9,10 @@ const meta = {
|
|
|
9
9
|
label: {
|
|
10
10
|
description: 'Label for the select',
|
|
11
11
|
},
|
|
12
|
+
accessibilityLabel: {
|
|
13
|
+
description:
|
|
14
|
+
'Describes the select purpose. If empty, label content will be used',
|
|
15
|
+
},
|
|
12
16
|
variant: {
|
|
13
17
|
description: 'Select variant used',
|
|
14
18
|
},
|
|
@@ -33,6 +37,10 @@ const meta = {
|
|
|
33
37
|
selected: {
|
|
34
38
|
description: 'Value to be displayed as selected',
|
|
35
39
|
},
|
|
40
|
+
errors: {
|
|
41
|
+
description:
|
|
42
|
+
'Optional array of errors. If passed, the errors are listed and invalid style is applied.',
|
|
43
|
+
},
|
|
36
44
|
},
|
|
37
45
|
}
|
|
38
46
|
|
|
@@ -50,10 +58,10 @@ export const Primary: Story = {
|
|
|
50
58
|
args: {
|
|
51
59
|
variant: 'primary',
|
|
52
60
|
disabled: false,
|
|
53
|
-
invalid: false,
|
|
54
61
|
helpText: 'This text can help you',
|
|
55
62
|
name: 'example',
|
|
56
63
|
label: 'Videogames',
|
|
64
|
+
accessibilityLabel: 'Select your favourite gaming system options',
|
|
57
65
|
hideLabel: false,
|
|
58
66
|
placeholder: 'Select your favourite gaming system...',
|
|
59
67
|
options: [
|
|
@@ -61,6 +69,8 @@ export const Primary: Story = {
|
|
|
61
69
|
{ id: '2', label: 'PlayStation 5' },
|
|
62
70
|
{ id: '3', label: 'Xbox Series S/X' },
|
|
63
71
|
],
|
|
72
|
+
id: 'select-videogames',
|
|
73
|
+
onChange: (optionId) => console.log('onChange optionId:', optionId),
|
|
64
74
|
},
|
|
65
75
|
parameters: figmaPrimaryDesign,
|
|
66
76
|
}
|
|
@@ -69,7 +79,6 @@ export const PrimaryWithSelectedOptions: Story = {
|
|
|
69
79
|
args: {
|
|
70
80
|
variant: 'primary',
|
|
71
81
|
disabled: false,
|
|
72
|
-
invalid: false,
|
|
73
82
|
helpText: 'This text can help you',
|
|
74
83
|
name: 'example',
|
|
75
84
|
label: 'Videogames',
|
|
@@ -81,6 +90,29 @@ export const PrimaryWithSelectedOptions: Story = {
|
|
|
81
90
|
{ id: '3', label: 'Xbox Series S/X' },
|
|
82
91
|
],
|
|
83
92
|
selected: { id: '2', label: 'PlayStation 5' },
|
|
93
|
+
id: 'select-videogames',
|
|
94
|
+
},
|
|
95
|
+
parameters: figmaPrimaryDesign,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const PrimaryWithErrors: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
variant: 'primary',
|
|
101
|
+
disabled: false,
|
|
102
|
+
helpText: 'This text can help you',
|
|
103
|
+
name: 'example',
|
|
104
|
+
label: 'Videogames',
|
|
105
|
+
accessibilityLabel: 'Select your favourite gaming system options',
|
|
106
|
+
hideLabel: false,
|
|
107
|
+
placeholder: 'Select your favourite gaming system...',
|
|
108
|
+
options: [
|
|
109
|
+
{ id: '1', label: 'Nintendo Switch' },
|
|
110
|
+
{ id: '2', label: 'PlayStation 5' },
|
|
111
|
+
{ id: '3', label: 'Xbox Series S/X' },
|
|
112
|
+
],
|
|
113
|
+
id: 'select-videogames',
|
|
114
|
+
errors: ['error1', 'error2'],
|
|
115
|
+
onChange: (optionId) => console.log('onChange optionId:', optionId),
|
|
84
116
|
},
|
|
85
117
|
parameters: figmaPrimaryDesign,
|
|
86
118
|
}
|