agroptima-design-system 0.4.0 → 0.6.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroptima-design-system",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "scripts": {
5
5
  "dev": "npm run storybook",
6
6
  "storybook": "storybook dev -p 6006 --ci",
@@ -146,7 +146,7 @@
146
146
  }
147
147
  &:disabled {
148
148
  background: color_alias.$neutral-color-50;
149
- border: 1px solid color_alias.$neutral-color-400;
149
+ border: none;
150
150
  color: color_alias.$neutral-color-400;
151
151
  svg,
152
152
  svg path {
@@ -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 href={props.href || ''} className={cssClasses} {...props}>
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 className={cssClasses} disabled={loading || disabled} {...props}>
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,71 @@
1
+ @use '../settings/color_alias';
2
+ @use '../settings/typography';
3
+ @use '../settings/config';
4
+
5
+ .checkbox-group {
6
+ .checkbox-label-container {
7
+ display: flex;
8
+ justify-content: flex-start;
9
+ align-items: center;
10
+ gap: config.$space-1x;
11
+
12
+ .background-icon {
13
+ width: config.$icon-size-4x;
14
+ height: config.$icon-size-4x;
15
+ }
16
+
17
+ .label {
18
+ @include typography.checkbox-label;
19
+ }
20
+ }
21
+
22
+ &.primary {
23
+ input[type='checkbox'] {
24
+ display: none;
25
+ }
26
+
27
+ input[type='checkbox'] + .checkbox-label-container .background-icon {
28
+ background: url('../icons/checkbox-primary-inactive.svg') left top
29
+ no-repeat;
30
+ }
31
+
32
+ input[type='checkbox']:checked
33
+ + .checkbox-label-container
34
+ .background-icon {
35
+ background: url('../icons/checkbox-primary-active.svg') left top no-repeat;
36
+ }
37
+
38
+ // Disabled
39
+ input[type='checkbox']
40
+ + .checkbox-label-container.disabled
41
+ .background-icon {
42
+ background: url('../icons/checkbox-disabled-inactive.svg') left top
43
+ no-repeat;
44
+ }
45
+
46
+ input[type='checkbox']:checked
47
+ + .checkbox-label-container.disabled
48
+ .background-icon {
49
+ background: url('../icons/checkbox-disabled-active.svg') left top
50
+ no-repeat;
51
+ }
52
+
53
+ .checkbox-label-container {
54
+ &.disabled {
55
+ .label {
56
+ @include typography.checkbox-disabled-label;
57
+ }
58
+ }
59
+ }
60
+
61
+ // Hover
62
+ &:hover {
63
+ input[type='checkbox']:not(:checked)
64
+ + .checkbox-label-container:not(.disabled)
65
+ .background-icon {
66
+ background: url('../icons/checkbox-primary-hover-inactive.svg') left top
67
+ no-repeat;
68
+ }
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,45 @@
1
+ import './Checkbox.scss'
2
+
3
+ export type Variant = 'primary'
4
+
5
+ export interface CheckboxProps extends React.ComponentPropsWithoutRef<'input'> {
6
+ label?: string
7
+ hideLabel?: boolean
8
+ variant?: Variant
9
+ id: string
10
+ accessibilityLabel: string
11
+ }
12
+
13
+ export function Checkbox({
14
+ accessibilityLabel,
15
+ label,
16
+ hideLabel,
17
+ disabled,
18
+ variant = 'primary',
19
+ id,
20
+ ...props
21
+ }: CheckboxProps) {
22
+ const disabledClass = disabled ? 'disabled' : ''
23
+ const cssClasses = ['checkbox', variant].join(' ')
24
+
25
+ return (
26
+ <div className={`checkbox-group ${variant}`}>
27
+ <input
28
+ id={id}
29
+ type="checkbox"
30
+ className={cssClasses}
31
+ disabled={disabled}
32
+ aria-label={accessibilityLabel}
33
+ {...props}
34
+ />
35
+
36
+ <label
37
+ className={`checkbox-label-container ${disabledClass}`}
38
+ htmlFor={id}
39
+ >
40
+ <span className="background-icon"></span>
41
+ {!hideLabel && <span className="label">{label}</span>}
42
+ </label>
43
+ </div>
44
+ )
45
+ }
@@ -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
+ }
@@ -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
- invalid?: boolean
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
- invalid,
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 = invalid ? 'invalid' : ''
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 && <span className="input-help-text">{helpText}</span>}
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<'select'> {
9
+ extends React.ComponentPropsWithoutRef<'input'> {
10
10
  placeholder?: string
11
11
  helpText?: string
12
12
  variant?: Variant
13
13
  options: Option[]
14
- invalid?: boolean
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
- invalid,
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 = invalid ? 'invalid' : ''
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
- <input type="hidden" name={name} value={selectedOptionsIds.toString()} />
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
  }
@@ -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<'select'> {
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
- invalid?: boolean
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
- invalid,
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 = invalid ? 'invalid' : ''
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 && <span className="select-help-text">{helpText}</span>}
98
- <input type="hidden" name={name} value={selectedOption.id} />
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 class="border" d="M0 2a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Z" fill="#F3F3F3"/><path class="background" fill-rule="evenodd" clip-rule="evenodd" d="M18 1H2a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1ZM2 0a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" fill="#9B9B9B"/><path class="check" d="m7.085 13.699-3.793-3.98L2 11.063 7.085 16.4 18 4.946 16.718 3.6 7.085 13.699Z" fill="#9B9B9B"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path class="background" d="M0 2a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Z" fill="#F3F3F3"/><path class="border" fill-rule="evenodd" clip-rule="evenodd" d="M18 1H2a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1ZM2 0a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" fill="#9B9B9B"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path class="border" d="M0 2a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Z" fill="#A95782"/><path class="background" fill-rule="evenodd" clip-rule="evenodd" d="M18 1H2a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1ZM2 0a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" fill="#A95782"/><path class="check" d="m7.085 13.699-3.793-3.98L2 11.063 7.085 16.4 18 4.946 16.718 3.6 7.085 13.699Z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path class="background" d="M0 2a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Z" fill="#fff"/><path class="border" fill-rule="evenodd" clip-rule="evenodd" d="M18 1H2a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1ZM2 0a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" fill="#A95782"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path class="background" d="M0 2a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Z" fill="#fff"/><path class="border" fill-rule="evenodd" clip-rule="evenodd" d="M18 1H2a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1ZM2 0a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" fill="#727272"/></svg>
@@ -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>
@@ -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,
@@ -68,6 +68,24 @@ $font-primary: $font-base-stretch $text-base-style $font-base-weight #{$text-bas
68
68
  line-height: 0.775rem;
69
69
  }
70
70
 
71
+ @mixin checkbox-label {
72
+ font-style: $text-base-style;
73
+ font-variant: $text-base-style;
74
+ font-weight: 400;
75
+ font-family: $font-base-family;
76
+ font-size: 1rem;
77
+ color: color_alias.$neutral-color-1000;
78
+ }
79
+
80
+ @mixin checkbox-disabled-label {
81
+ font-style: $text-base-style;
82
+ font-variant: $text-base-style;
83
+ font-weight: 400;
84
+ font-family: $font-base-family;
85
+ font-size: 1rem;
86
+ color: color_alias.$neutral-color-400;
87
+ }
88
+
71
89
  @mixin cards-table-list-header {
72
90
  font-style: $text-base-style;
73
91
  font-variant: $text-base-style;
@@ -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,23 @@ import { Meta } from "@storybook/addon-docs";
3
3
  <Meta title="Changelog" />
4
4
  # Changelog
5
5
 
6
+ ## 0.6.0
7
+
8
+ Checkbox component is added to Storybook.
9
+
10
+ ## 0.5.0
11
+
12
+ - IconButton component is added to Storybook.
13
+ - Edit and Delete icons have been added.
14
+ - Button outlined disabled styles have been changed to make them look the same as the filled disabled styles.
15
+ - `accessibilityLabel` specific prop has been added as optional to Button, Input, Select and Multiselect components.
16
+ - Select and Multiselect can be closed too when losing focus.
17
+ - Select and Multiselect accept native props.
18
+ - Select component triggers `onChange` event prop.
19
+
20
+ BREAKING CHANGES
21
+ - Input, Select and Multiselect components accept an `errors` array prop. Previous `invalid` prop is deprecated.
22
+
6
23
  ## 0.4.0
7
24
 
8
25
  - CardsTable component suitable to create custom tables is added to Storybook.
@@ -0,0 +1,58 @@
1
+ import { StoryObj } from '@storybook/react'
2
+ import { Checkbox } from '../atoms/Checkbox'
3
+
4
+ const meta = {
5
+ title: 'Design System/Atoms/Checkbox',
6
+ component: Checkbox,
7
+ tags: ['autodocs'],
8
+ argTypes: {
9
+ accessibilityLabel: {
10
+ description: 'Accessible name & description of the element',
11
+ },
12
+ variant: {
13
+ description: 'Variant used from a list of values',
14
+ },
15
+ disabled: {
16
+ description: 'Is the component in disabled state?',
17
+ },
18
+ label: {
19
+ description: 'Label for the component',
20
+ },
21
+ id: {
22
+ description: 'Value needed for the label relation',
23
+ },
24
+ },
25
+ }
26
+
27
+ const figmaPrimaryDesign = {
28
+ design: {
29
+ type: 'figma',
30
+ url: 'https://www.figma.com/file/DN2ova21vWqCRvPspBXgI1/Design-System?type=design&node-id=521-104&mode=dev',
31
+ },
32
+ }
33
+
34
+ export default meta
35
+ type Story = StoryObj<typeof meta>
36
+
37
+ export const Primary: Story = {
38
+ args: {
39
+ accessibilityLabel: 'Marks if the user likes videogames',
40
+ variant: 'primary',
41
+ disabled: false,
42
+ label: 'Do you like videogames?',
43
+ id: 'checkbox-videogames-preference',
44
+ },
45
+ parameters: figmaPrimaryDesign,
46
+ }
47
+
48
+ export const PrimaryWithoutLabel: Story = {
49
+ args: {
50
+ accessibilityLabel: 'Marks if the user likes videogames',
51
+ variant: 'primary',
52
+ disabled: false,
53
+ label: 'Do you like videogames?',
54
+ id: 'checkbox-videogames-preference',
55
+ hideLabel: true,
56
+ },
57
+ parameters: figmaPrimaryDesign,
58
+ }
@@ -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
  }