agroptima-design-system 0.37.1-beta.9 → 0.37.3

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.
@@ -8,7 +8,7 @@ on:
8
8
  - synchronize
9
9
 
10
10
  jobs:
11
- build:
11
+ run-linters:
12
12
  runs-on: ubuntu-latest
13
13
 
14
14
  steps:
@@ -8,7 +8,7 @@ on:
8
8
  - synchronize
9
9
 
10
10
  jobs:
11
- build:
11
+ run-tests:
12
12
  runs-on: ubuntu-latest
13
13
 
14
14
  steps:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroptima-design-system",
3
- "version": "0.37.1-beta.9",
3
+ "version": "0.37.3",
4
4
  "scripts": {
5
5
  "dev": "npm run storybook",
6
6
  "storybook": "storybook dev -p 6006 --ci",
@@ -9,6 +9,7 @@
9
9
  > .icon {
10
10
  > svg {
11
11
  fill: $secondary-color;
12
+
12
13
  path {
13
14
  fill: $secondary-color;
14
15
  }
@@ -36,38 +37,33 @@
36
37
  border: 1px solid transparent;
37
38
 
38
39
  > .icon {
39
- margin-top: 2px;
40
- width: config.$icon-size-3x;
41
- height: config.$icon-size-3x;
42
- > svg {
43
- width: 100%;
44
- height: 100%;
45
- }
40
+ margin: 6px 2px;
46
41
  }
47
42
 
48
43
  &.primary {
49
44
  @include button-style(
50
- color_alias.$primary-color-600,
51
- color_alias.$neutral-white,
52
- color_alias.$primary-color-800
45
+ color_alias.$primary-color-600,
46
+ color_alias.$neutral-white,
47
+ color_alias.$primary-color-800
53
48
  );
54
49
  }
55
50
 
56
51
  &.primary-outlined {
57
52
  border: 1px solid color_alias.$primary-color-600;
58
53
  @include button-style(
59
- color_alias.$neutral-white,
60
- color_alias.$primary-color-600,
61
- color_alias.$primary-color-50
54
+ color_alias.$neutral-white,
55
+ color_alias.$primary-color-600,
56
+ color_alias.$primary-color-50
62
57
  );
63
58
  }
64
59
 
65
60
  &.primary-ghost {
66
61
  @include button-style(
67
- color_alias.$neutral-white,
68
- color_alias.$primary-color-600,
69
- transparent
62
+ color_alias.$neutral-white,
63
+ color_alias.$primary-color-600,
64
+ transparent
70
65
  );
66
+
71
67
  &:not(:disabled):hover,
72
68
  &:not(:disabled):active {
73
69
  border: 1px solid color_alias.$primary-color-600;
@@ -76,16 +72,19 @@
76
72
 
77
73
  &.error {
78
74
  @include button-style(
79
- color_alias.$error-color-1000,
80
- color_alias.$neutral-color-1000,
81
- color_alias.$error-color-700
75
+ color_alias.$error-color-1000,
76
+ color_alias.$neutral-color-1000,
77
+ color_alias.$error-color-700
82
78
  );
79
+
83
80
  &:not(:disabled):hover,
84
81
  &:not(:disabled):active {
85
82
  color: color_alias.$neutral-white;
83
+
86
84
  > .icon {
87
85
  > svg {
88
86
  fill: color_alias.$neutral-white;
87
+
89
88
  path {
90
89
  fill: color_alias.$neutral-white;
91
90
  }
@@ -97,18 +96,19 @@
97
96
  &.error-outlined {
98
97
  border: 1px solid color_alias.$error-color-600;
99
98
  @include button-style(
100
- color_alias.$neutral-white,
101
- color_alias.$error-color-600,
102
- color_alias.$error-color-50
99
+ color_alias.$neutral-white,
100
+ color_alias.$error-color-600,
101
+ color_alias.$error-color-50
103
102
  );
104
103
  }
105
104
 
106
105
  &.error-ghost {
107
106
  @include button-style(
108
- color_alias.$neutral-white,
109
- color_alias.$error-color-600,
110
- transparent
107
+ color_alias.$neutral-white,
108
+ color_alias.$error-color-600,
109
+ transparent
111
110
  );
111
+
112
112
  &:not(:disabled):hover,
113
113
  &:not(:disabled):active {
114
114
  border: 1px solid color_alias.$error-color-600;
@@ -117,16 +117,19 @@
117
117
 
118
118
  &.success {
119
119
  @include button-style(
120
- color_alias.$success-color-1000,
121
- color_alias.$neutral-color-900,
122
- color_alias.$success-color-700
120
+ color_alias.$success-color-1000,
121
+ color_alias.$neutral-color-900,
122
+ color_alias.$success-color-700
123
123
  );
124
+
124
125
  &:not(:disabled):hover,
125
126
  &:not(:disabled):active {
126
127
  color: color_alias.$neutral-white;
128
+
127
129
  > .icon {
128
130
  > svg {
129
131
  fill: color_alias.$neutral-white;
132
+
130
133
  path {
131
134
  fill: color_alias.$neutral-white;
132
135
  }
@@ -139,18 +142,19 @@
139
142
  border: 1px solid color_alias.$success-color-1000;
140
143
 
141
144
  @include button-style(
142
- color_alias.$neutral-white,
143
- color_alias.$neutral-color-900,
144
- color_alias.$success-color-50
145
+ color_alias.$neutral-white,
146
+ color_alias.$neutral-color-900,
147
+ color_alias.$success-color-50
145
148
  );
146
149
  }
147
150
 
148
151
  &.success-ghost {
149
152
  @include button-style(
150
- color_alias.$neutral-white,
151
- color_alias.$success-color-1000,
152
- transparent
153
+ color_alias.$neutral-white,
154
+ color_alias.$success-color-1000,
155
+ transparent
153
156
  );
157
+
154
158
  &:not(:disabled):hover,
155
159
  &:not(:disabled):active {
156
160
  border: 1px solid color_alias.$success-color-1000;
@@ -159,16 +163,19 @@
159
163
 
160
164
  &.warning {
161
165
  @include button-style(
162
- color_alias.$warning-color-1000,
163
- color_alias.$neutral-color-900,
164
- color_alias.$warning-color-700
166
+ color_alias.$warning-color-1000,
167
+ color_alias.$neutral-color-900,
168
+ color_alias.$warning-color-700
165
169
  );
170
+
166
171
  &:not(:disabled):hover,
167
172
  &:not(:disabled):active {
168
173
  color: color_alias.$neutral-white;
174
+
169
175
  > .icon {
170
176
  > svg {
171
177
  fill: color_alias.$neutral-white;
178
+
172
179
  path {
173
180
  fill: color_alias.$neutral-white;
174
181
  }
@@ -180,18 +187,19 @@
180
187
  &.warning-outlined {
181
188
  border: 1px solid color_alias.$warning-color-1000;
182
189
  @include button-style(
183
- color_alias.$neutral-white,
184
- color_alias.$neutral-color-900,
185
- color_alias.$warning-color-50
190
+ color_alias.$neutral-white,
191
+ color_alias.$neutral-color-900,
192
+ color_alias.$warning-color-50
186
193
  );
187
194
  }
188
195
 
189
196
  &.warning-ghost {
190
197
  @include button-style(
191
- color_alias.$neutral-white,
192
- color_alias.$warning-color-1000,
193
- transparent
198
+ color_alias.$neutral-white,
199
+ color_alias.$warning-color-1000,
200
+ transparent
194
201
  );
202
+
195
203
  &:not(:disabled):hover,
196
204
  &:not(:disabled):active {
197
205
  border: 1px solid color_alias.$warning-color-1000;
@@ -200,27 +208,28 @@
200
208
 
201
209
  &.info {
202
210
  @include button-style(
203
- color_alias.$info-color-600,
204
- color_alias.$neutral-white,
205
- color_alias.$info-color-900
211
+ color_alias.$info-color-600,
212
+ color_alias.$neutral-white,
213
+ color_alias.$info-color-900
206
214
  );
207
215
  }
208
216
 
209
217
  &.info-outlined {
210
218
  border: 1px solid color_alias.$info-color-600;
211
219
  @include button-style(
212
- color_alias.$neutral-white,
213
- color_alias.$info-color-600,
214
- color_alias.$info-color-50
220
+ color_alias.$neutral-white,
221
+ color_alias.$info-color-600,
222
+ color_alias.$info-color-50
215
223
  );
216
224
  }
217
225
 
218
226
  &.info-ghost {
219
227
  @include button-style(
220
- color_alias.$neutral-white,
221
- color_alias.$info-color-600,
222
- transparent
228
+ color_alias.$neutral-white,
229
+ color_alias.$info-color-600,
230
+ transparent
223
231
  );
232
+
224
233
  &:not(:disabled):hover,
225
234
  &:not(:disabled):active {
226
235
  border: 1px solid color_alias.$info-color-600;
@@ -229,27 +238,28 @@
229
238
 
230
239
  &.neutral {
231
240
  @include button-style(
232
- color_alias.$neutral-color-100,
233
- color_alias.$neutral-color-1000,
234
- color_alias.$neutral-color-300
241
+ color_alias.$neutral-color-100,
242
+ color_alias.$neutral-color-1000,
243
+ color_alias.$neutral-color-300
235
244
  );
236
245
  }
237
246
 
238
247
  &.neutral-outlined {
239
248
  border: 1px solid color_alias.$neutral-color-100;
240
249
  @include button-style(
241
- color_alias.$neutral-white,
242
- color_alias.$neutral-color-1000,
243
- color_alias.$neutral-color-50
250
+ color_alias.$neutral-white,
251
+ color_alias.$neutral-color-1000,
252
+ color_alias.$neutral-color-50
244
253
  );
245
254
  }
246
255
 
247
256
  &.neutral-ghost {
248
257
  @include button-style(
249
- color_alias.$neutral-white,
250
- color_alias.$neutral-color-1000,
251
- transparent
258
+ color_alias.$neutral-white,
259
+ color_alias.$neutral-color-1000,
260
+ transparent
252
261
  );
262
+
253
263
  &:not(:disabled):hover,
254
264
  &:not(:disabled):active {
255
265
  border: 1px solid color_alias.$neutral-color-100;
@@ -258,9 +268,11 @@
258
268
 
259
269
  &:disabled {
260
270
  color: color_alias.$neutral-color-400;
271
+
261
272
  > .icon {
262
273
  > svg {
263
274
  fill: color_alias.$neutral-color-400;
275
+
264
276
  path {
265
277
  fill: color_alias.$neutral-color-400;
266
278
  }
@@ -7,6 +7,7 @@ import { BaseButton } from './BaseButton'
7
7
 
8
8
  interface CustomProps {
9
9
  label: string
10
+ icon?: IconType
10
11
  accessibilityLabel?: string
11
12
  leftIcon?: IconType
12
13
  rightIcon?: IconType
@@ -40,6 +41,7 @@ export type ButtonVariant =
40
41
 
41
42
  export function Button({
42
43
  label,
44
+ icon,
43
45
  accessibilityLabel,
44
46
  leftIcon,
45
47
  rightIcon,
@@ -63,7 +65,7 @@ export function Button({
63
65
  {...props}
64
66
  >
65
67
  {leftIconName && <Icon name={leftIconName} size="3" />}
66
- {label}
68
+ {icon ? <Icon name={icon} size="3" /> : label}
67
69
  {rightIcon && <Icon name={rightIcon} size="3" />}
68
70
  </BaseButton>
69
71
  )
@@ -0,0 +1,20 @@
1
+ .button-group {
2
+ display: flex;
3
+ flex-direction: row;
4
+ align-items: center;
5
+ gap: 1px;
6
+
7
+ > .button:first-child, > .popover-container:first-child > .button {
8
+ border-top-right-radius: 0;
9
+ border-bottom-right-radius: 0;
10
+ }
11
+
12
+ > .button:last-child, > .popover-container:last-child > .button {
13
+ border-top-left-radius: 0;
14
+ border-bottom-left-radius: 0;
15
+ }
16
+
17
+ > .button:not(:first-child):not(:last-child), > .popover-container:not(:first-child):not(:last-child) {
18
+ border-radius: 0;
19
+ }
20
+ }
@@ -0,0 +1,10 @@
1
+ import './ButtonGroup.scss'
2
+ import React from 'react'
3
+ import { classNames } from '../../utils/classNames'
4
+
5
+ export interface ButtonGroupProps
6
+ extends React.HTMLAttributes<HTMLDivElement> {}
7
+
8
+ export function ButtonGroup({ className, children }: ButtonGroupProps) {
9
+ return <div className={classNames('button-group', className)}>{children}</div>
10
+ }
@@ -0,0 +1,3 @@
1
+ import { ButtonGroup } from './ButtonGroup'
2
+
3
+ export { ButtonGroup }
@@ -46,7 +46,7 @@ export function Checkbox({
46
46
  type="checkbox"
47
47
  className="checkbox"
48
48
  disabled={disabled}
49
- aria-label={accessibilityLabel}
49
+ aria-label={accessibilityLabel || `${name} checkbox`}
50
50
  {...props}
51
51
  />
52
52
  <Label
@@ -54,8 +54,9 @@ export function Checkbox({
54
54
  required={props.required}
55
55
  disabled={disabled}
56
56
  className={classNames({ 'visually-hidden': hideLabel })}
57
+ aria-labelledby={identifier}
57
58
  >
58
- {children || accessibilityLabel}
59
+ {children}
59
60
  </Label>
60
61
  </div>
61
62
  )
@@ -7,7 +7,7 @@ import { Icon } from './Icon'
7
7
  type DividerIconTypes = IconType | 'Line'
8
8
 
9
9
  export interface DividerProps extends ComponentPropsWithoutRef<'div'> {
10
- title: string
10
+ title?: string
11
11
  variant?: string
12
12
  icon?: DividerIconTypes
13
13
  iconButton?: IconType
@@ -28,12 +28,14 @@ export function Divider({
28
28
 
29
29
  return (
30
30
  <div role="separator" className={cssClasses}>
31
- <div className="divider-title">
32
- <DividerIcon icon={icon} />
33
- <span onClick={onClick} className={classNames({ link: hasAction })}>
34
- {title}
35
- </span>
36
- </div>
31
+ {title && (
32
+ <div className="divider-title">
33
+ <DividerIcon icon={icon} />
34
+ <span onClick={onClick} className={classNames({ link: hasAction })}>
35
+ {title}
36
+ </span>
37
+ </div>
38
+ )}
37
39
  <div className="long line"></div>
38
40
  {children}
39
41
  </div>
@@ -3,26 +3,67 @@
3
3
  @use '../../settings/config';
4
4
  @use '../../settings/depth';
5
5
 
6
+
7
+ %top {
8
+ bottom: 42px;
9
+ margin-top: auto;
10
+ margin-bottom: config.$space-2x;
11
+ }
12
+
13
+ %left {
14
+ left: 0;
15
+ }
16
+
17
+ %right {
18
+ right: 0;
19
+ }
20
+
21
+ %center {
22
+ left: 50%;
23
+ transform: translateX(-50%);
24
+ }
25
+
6
26
  .popover-container {
7
27
  pointer-events: none;
8
28
  position: relative;
29
+
9
30
  > * {
10
31
  pointer-events: auto;
11
32
  }
33
+
12
34
  .popover-menu-container {
13
35
  position: absolute;
14
36
  white-space: nowrap;
15
37
  margin-top: config.$space-2x;
16
38
  z-index: depth.$z-popover;
39
+
17
40
  &.left {
18
- left: 0;
41
+ @extend %left;
19
42
  }
43
+
20
44
  &.right {
21
- right: 0;
45
+ @extend %right;
22
46
  }
47
+
23
48
  &.center {
24
- left: 50%;
25
- transform: translateX(-50%);
49
+ @extend %center;
50
+ }
51
+
52
+ &.top {
53
+ &-left {
54
+ @extend %top;
55
+ @extend %left;
56
+ }
57
+
58
+ &-right {
59
+ @extend %top;
60
+ @extend %right;
61
+ }
62
+
63
+ &-center {
64
+ @extend %top;
65
+ @extend %center;
66
+ }
26
67
  }
27
68
  }
28
69
  }
@@ -31,10 +72,9 @@
31
72
  display: flex;
32
73
  flex-direction: column;
33
74
  justify-content: flex-start;
34
- box-shadow:
35
- 0px 3px 6px -4px rgba(0, 0, 0, 0.12),
36
- 0px 6px 16px 0px rgba(0, 0, 0, 0.08),
37
- 0px 9px 28px 8px rgba(0, 0, 0, 0.05);
75
+ box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
76
+ 0px 6px 16px 0px rgba(0, 0, 0, 0.08),
77
+ 0px 9px 28px 8px rgba(0, 0, 0, 0.05);
38
78
 
39
79
  .popover-menu-option {
40
80
  @include typography.body-regular-primary;
@@ -44,6 +84,7 @@
44
84
  border-radius: config.$corner-radius-xxs;
45
85
  text-decoration: none;
46
86
  cursor: default;
87
+ border: none;
47
88
 
48
89
  &:hover {
49
90
  text-decoration: none;
@@ -5,34 +5,50 @@ import { useOpen } from '../../hooks/useOpen'
5
5
  import { useOutsideClick } from '../../hooks/useOutsideClick'
6
6
  import { classNames } from '../../utils/classNames'
7
7
 
8
+ type Actions = {
9
+ isOpen: boolean
10
+ close: () => void
11
+ open: () => void
12
+ toggle: () => void
13
+ }
14
+
15
+ type Horizontal = 'left' | 'right' | 'center'
16
+
17
+ type Position = Horizontal | `top-${Horizontal}`
18
+
8
19
  export interface PopoverProps {
9
- renderButton: (props: {
10
- isOpen: boolean
11
- close: () => void
12
- open: () => void
13
- toggle: () => void
14
- }) => React.ReactNode
15
- position?: 'left' | 'right' | 'center'
20
+ renderButton: (props: Actions) => React.ReactNode
21
+ position?: Position
22
+ className?: string
16
23
  children: React.ReactNode
17
24
  }
18
25
 
19
- function Popover({ renderButton, position = 'left', children }: PopoverProps) {
26
+ function Popover({
27
+ renderButton,
28
+ position = 'left',
29
+ className,
30
+ children,
31
+ }: PopoverProps) {
20
32
  const { isOpen, close, open, toggle } = useOpen()
21
33
 
22
34
  const popoverRef = useRef(null)
23
35
  useOutsideClick(popoverRef, close)
24
36
 
25
37
  return (
26
- <div className="popover-container" ref={popoverRef}>
38
+ <div
39
+ className={classNames('popover-container', className)}
40
+ ref={popoverRef}
41
+ >
27
42
  {renderButton({ isOpen, close, open, toggle })}
28
- {isOpen && (
29
- <div
30
- className={classNames('popover-menu-container', position)}
31
- onClick={close}
32
- >
33
- {children}
34
- </div>
35
- )}
43
+ <div
44
+ className={classNames('popover-menu-container', position, {
45
+ hidden: !isOpen,
46
+ })}
47
+ onClick={close}
48
+ aria-hidden={!isOpen}
49
+ >
50
+ {children}
51
+ </div>
36
52
  </div>
37
53
  )
38
54
  }
@@ -1,4 +1,5 @@
1
1
  import './Popover.scss'
2
+ import React from 'react'
2
3
  import { classNames } from '../../utils/classNames'
3
4
 
4
5
  export interface PopoverMenuProps
@@ -1,26 +1,24 @@
1
1
  import './Popover.scss'
2
- import type { LinkProps as NextLinkProps } from 'next/link'
3
- import Link from 'next/link'
4
- import React, { type AnchorHTMLAttributes } from 'react'
2
+ import React from 'react'
5
3
  import { classNames } from '../../utils/classNames'
4
+ import { BaseButton, type BaseButtonProps } from '../Button'
6
5
 
7
6
  export type Variant = 'primary'
8
7
 
9
- type LinkProps = NextLinkProps & AnchorHTMLAttributes<HTMLAnchorElement>
10
- export interface PopoverMenuOptionProps extends LinkProps {
8
+ export interface CustomProps {
11
9
  variant?: Variant
12
10
  title: string
13
11
  disabled?: boolean
14
- href: string
15
12
  active?: boolean
16
13
  }
17
14
 
15
+ export type PopoverMenuOptionProps = CustomProps & BaseButtonProps
16
+
18
17
  export function PopoverMenuOption({
19
18
  variant = 'primary',
20
19
  className,
21
20
  title,
22
21
  disabled,
23
- href,
24
22
  active,
25
23
  prefetch = false,
26
24
  ...props
@@ -29,17 +27,14 @@ export function PopoverMenuOption({
29
27
  disabled,
30
28
  active,
31
29
  })
32
-
33
30
  return (
34
- <Link
31
+ <BaseButton
35
32
  role="menuitem"
36
33
  className={cssClasses}
37
- href={disabled ? '#' : href}
38
34
  aria-disabled={disabled}
39
- prefetch={prefetch}
40
35
  {...props}
41
36
  >
42
- <span className="title">{title}</span>
43
- </Link>
37
+ {title}
38
+ </BaseButton>
44
39
  )
45
40
  }
@@ -141,11 +141,7 @@ $elements-space: config.$space-2x;
141
141
  margin: 0;
142
142
  padding: 0;
143
143
  }
144
- .loader {
145
- height: 50px;
146
- opacity: 0.5;
147
- background-color: green;
148
- }
144
+
149
145
  .option {
150
146
  display: flex;
151
147
  align-items: center;
@@ -30,10 +30,6 @@ export interface SelectProps extends InputPropsWithoutOnChange {
30
30
  isSearchable?: boolean
31
31
  searchLabel?: string
32
32
  fullWidth?: boolean
33
- infiniteScroll?: boolean
34
- onLoadMore?: () => void
35
- hasMore?: boolean
36
- loading?: boolean
37
33
  }
38
34
 
39
35
  const EMPTY_OPTION = { id: '', label: '' }
@@ -56,10 +52,6 @@ export function Select({
56
52
  isSearchable = false,
57
53
  searchLabel = 'Search',
58
54
  fullWidth = false,
59
- infiniteScroll = false,
60
- onLoadMore,
61
- hasMore,
62
- loading,
63
55
  ...props
64
56
  }: SelectProps): React.JSX.Element {
65
57
  const { isOpen, close, toggle } = useOpen()
@@ -129,10 +121,6 @@ export function Select({
129
121
  isSearchable={isSearchable}
130
122
  searchLabel={searchLabel}
131
123
  onClose={handleClose}
132
- infiniteScroll={infiniteScroll}
133
- onLoadMore={onLoadMore}
134
- hasMore={hasMore}
135
- loading={loading}
136
124
  />
137
125
  )}
138
126
  <HelpText helpText={helpText} errors={errors} />
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react'
1
+ import React, { useEffect } from 'react'
2
2
  import { useSearch } from '../../hooks/useSearch'
3
3
  import { Input } from '../Input'
4
4
  import type { Option } from './Select'
@@ -14,10 +14,6 @@ interface OptionListProps {
14
14
  isSearchable: boolean
15
15
  searchLabel: string
16
16
  onClose: () => void
17
- infiniteScroll?: boolean
18
- onLoadMore?: () => void
19
- hasMore?: boolean
20
- loading?: boolean
21
17
  }
22
18
 
23
19
  const ESCAPE_KEY = 'Escape'
@@ -32,41 +28,8 @@ export function SelectItems({
32
28
  isSearchable,
33
29
  searchLabel,
34
30
  onClose,
35
- infiniteScroll = false,
36
- onLoadMore,
37
- hasMore,
38
- loading,
39
31
  }: OptionListProps) {
40
- const scrollRef = React.useRef<HTMLDivElement>(null)
41
- const loaderRef = React.useRef<HTMLDivElement>(null)
42
- const [items, setItems] = useState<Option[]>(options)
43
- const { findItems, search } = useSearch(items, 'label')
44
-
45
- useEffect(() => {
46
- if (!infiniteScroll || !hasMore || !loaderRef.current) return
47
-
48
- const observer = new IntersectionObserver(
49
- ([entry]) => {
50
- if (entry.isIntersecting && !loading) {
51
- onLoadMore?.()
52
- }
53
- },
54
- { root: scrollRef.current, threshold: 0.1 },
55
- )
56
-
57
- const current = loaderRef.current
58
- observer.observe(current)
59
-
60
- return () => {
61
- if (current) observer.unobserve(current)
62
- }
63
- }, [infiniteScroll, hasMore, loading, onLoadMore])
64
-
65
- useEffect(() => {
66
- console.log('Options updated:', options)
67
- setItems(options)
68
- }, [options])
69
-
32
+ const { findItems, search } = useSearch(options, 'label')
70
33
  useEffect(() => {
71
34
  const handleKeyDown = (event: KeyboardEvent) => {
72
35
  if (event.key === ESCAPE_KEY) {
@@ -81,7 +44,7 @@ export function SelectItems({
81
44
 
82
45
  return (
83
46
  <div className="select-options-container">
84
- <div className="select-options" id={id} ref={scrollRef}>
47
+ <div className="select-options" id={id}>
85
48
  {isSearchable && (
86
49
  <Input
87
50
  autoFocus
@@ -105,19 +68,7 @@ export function SelectItems({
105
68
  />
106
69
  ))}
107
70
  </ul>
108
- <LoaderSentinel loaderRef={loaderRef} />
109
71
  </div>
110
72
  </div>
111
73
  )
112
74
  }
113
-
114
- function LoaderSentinel({
115
- loaderRef,
116
- }: {
117
- loaderRef?: React.RefObject<HTMLDivElement | null>
118
- }) {
119
- if (!loaderRef) {
120
- return null
121
- }
122
- return <div ref={loaderRef} className="loader" />
123
- }
@@ -38,9 +38,11 @@ import Invoice from './invoice.svg'
38
38
  import Loading from './loading.svg'
39
39
  import Logout from './logout.svg'
40
40
  import Minus from './minus.svg'
41
+ import More from './more.svg'
41
42
  import NewView from './new-view.svg'
42
43
  import Orders from './orders.svg'
43
44
  import PDF from './pdf.svg'
45
+ import Picture from './picture.svg'
44
46
  import Product from './product.svg'
45
47
  import Reason from './reason.svg'
46
48
  import Reports from './reports.svg'
@@ -97,9 +99,11 @@ export {
97
99
  Loading,
98
100
  Logout,
99
101
  Minus,
102
+ More,
100
103
  NewView,
101
104
  Orders,
102
105
  PDF,
106
+ Picture,
103
107
  Product,
104
108
  Reason,
105
109
  Reports,
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Capa_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
5
+ <g>
6
+ <g id="keyboard-control">
7
+ <path d="M20,4c0-2.2-1.8-4-4-4s-4,1.8-4,4s1.8,4,4,4S20,6.2,20,4z M20,28c0-2.2-1.8-4-4-4s-4,1.8-4,4s1.8,4,4,4S20,30.2,20,28z
8
+ M20,16c0-2.2-1.8-4-4-4s-4,1.8-4,4s1.8,4,4,4S20,18.2,20,16z"/>
9
+ </g>
10
+ </g>
11
+ </svg>
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#picture__a)"><path d="M2.222 20a2.14 2.14 0 0 1-1.57-.653A2.14 2.14 0 0 1 0 17.777V2.223c0-.61.218-1.134.653-1.57A2.14 2.14 0 0 1 2.223 0h15.555a2.14 2.14 0 0 1 1.57.653c.434.435.652.958.652 1.57v15.555a2.14 2.14 0 0 1-.653 1.57 2.14 2.14 0 0 1-1.57.652H2.223Zm0-2.222h15.556V2.222H2.222v15.556Zm1.111-2.222h13.334L12.5 10l-3.333 4.444-2.5-3.333-3.334 4.445Z"/></g><defs><clipPath id="picture__a"><path fill="#fff" d="M0 0h20v20H0z"/></clipPath></defs></svg>
@@ -49,6 +49,10 @@ const meta = {
49
49
  description: 'Button right icon from a list of values',
50
50
  control: { type: 'select' },
51
51
  },
52
+ icon: {
53
+ description: 'Replaces the label for a icon',
54
+ control: { type: 'select' },
55
+ },
52
56
  href: {
53
57
  description:
54
58
  'If a link is provided, the component will be rendered as NextLink, otherwise as button',
@@ -0,0 +1,115 @@
1
+ import type { StoryObj } from '@storybook/react'
2
+ import { Button } from '../atoms/Button'
3
+ import { ButtonGroup } from '../atoms/ButtonGroup'
4
+ import { Popover, PopoverMenu, PopoverMenuOption } from '../atoms/Popover'
5
+
6
+ const figmaDesign = {
7
+ design: {
8
+ type: 'figma',
9
+ url: 'https://www.figma.com/design/DN2ova21vWqCRvPspBXgI1/Design-System?node-id=4279-942&t=06AFHQVmeQz1JClF-0',
10
+ },
11
+ }
12
+
13
+ const meta = {
14
+ title: 'Design System/Atoms/ButtonGroup',
15
+ component: ButtonGroup,
16
+ parameters: {
17
+ docs: {
18
+ description: {
19
+ component:
20
+ '<h2>Usage guidelines</h2>' +
21
+ '<p>The <code>ButtonGroup</code> component is used to visually group two or more buttons on a single horizontal line. This helps indicate related actions and maintain consistent UI alignment.</p>' +
22
+ '<ul>' +
23
+ ' <li>Use to group related actions that should be presented together</li>' +
24
+ ' <li>Use for things like filter options, segmented controls, or inline actions</li>' +
25
+ ' <li>Ensure all buttons in the group are accessible and clearly labeled</li>' +
26
+ ' <li>Pass buttons through the <code>children</code> prop as an array or fragment</li>' +
27
+ ' <li>Do not include unrelated controls like inputs or toggles in the same group</li>' +
28
+ '</ul>',
29
+ },
30
+ },
31
+ figmaDesign,
32
+ },
33
+ tags: ['autodocs'],
34
+ argTypes: {
35
+ children: {
36
+ description: 'prop is used to pass buttons components.',
37
+ },
38
+ },
39
+ }
40
+
41
+ export default meta
42
+ type Story = StoryObj<typeof meta>
43
+
44
+ export const TwoButtons: Story = {
45
+ args: {
46
+ children: (
47
+ <>
48
+ <Button type="button" label="Button" className="button" />
49
+ <Button
50
+ icon="AngleDown"
51
+ type="button"
52
+ label="Button"
53
+ className="button"
54
+ />
55
+ </>
56
+ ),
57
+ },
58
+ }
59
+
60
+ export const ThreeButtons: Story = {
61
+ args: {
62
+ children: (
63
+ <>
64
+ <Button
65
+ icon="AngleLeft"
66
+ type="button"
67
+ label="Button"
68
+ className="button"
69
+ />
70
+ <Button type="button" label="Button" className="button" />
71
+ <Button
72
+ icon="AngleRight"
73
+ type="button"
74
+ label="Button"
75
+ className="button"
76
+ />
77
+ </>
78
+ ),
79
+ },
80
+ }
81
+
82
+ export const withPopover: Story = {
83
+ args: {
84
+ children: (
85
+ <>
86
+ <Button type="button" label="Button" className="button" />
87
+ <Popover
88
+ renderButton={({ toggle }) => (
89
+ <Button label="More options" icon="More" onClick={toggle} />
90
+ )}
91
+ >
92
+ <PopoverMenu>
93
+ <PopoverMenuOption
94
+ active
95
+ href="#"
96
+ title="Profile"
97
+ variant="primary"
98
+ />
99
+ <PopoverMenuOption
100
+ href="#"
101
+ title="Change password"
102
+ variant="primary"
103
+ />
104
+ <PopoverMenuOption
105
+ disabled
106
+ href="#"
107
+ title="Logout"
108
+ variant="primary"
109
+ />
110
+ </PopoverMenu>
111
+ </Popover>
112
+ </>
113
+ ),
114
+ },
115
+ }
@@ -4,9 +4,25 @@ import { Meta } from "@storybook/blocks";
4
4
 
5
5
  # Changelog
6
6
 
7
- ## 0.37.1
7
+ ## 0.37.3
8
8
 
9
- * Add inifinite scroll options to Select component
9
+ * Fix Checkbox accessibilityLabel wrong use
10
+
11
+ ## 0.37.2
12
+
13
+ * Add Divider version without title
14
+
15
+ ## 0.37.0
16
+
17
+ * Add ButtonGroup component
18
+ * Add `icon` property to Button component
19
+ * Add More icon
20
+ * Add Button to PopoverMenuOption component
21
+ * Add `top` position to Popover component
22
+
23
+ ## 0.36.3
24
+
25
+ * Add Picture icon
10
26
 
11
27
  ## 0.36.2
12
28
 
@@ -89,3 +89,7 @@ export const NoIcon: Story = {
89
89
  },
90
90
  parameters: figmaPrimaryDesign,
91
91
  }
92
+
93
+ export const NoTitle: Story = {
94
+ parameters: figmaPrimaryDesign,
95
+ }
@@ -2,6 +2,7 @@ import type { StoryObj } from '@storybook/react'
2
2
  import React from 'react'
3
3
  import { Button } from '../atoms/Button'
4
4
  import { Popover, PopoverMenu, PopoverMenuOption } from '../atoms/Popover'
5
+ import type { PopoverProps } from '../atoms/Popover/Popover'
5
6
 
6
7
  const figmaPrimaryDesign = {
7
8
  design: {
@@ -20,9 +21,12 @@ const meta = {
20
21
  },
21
22
  position: {
22
23
  description: 'Position of the popover',
23
- options: ['right', 'left', 'center'],
24
24
  control: { type: 'select' },
25
25
  },
26
+ top: {
27
+ description: 'Show menu on top of the popover',
28
+ control: { type: 'boolean' },
29
+ },
26
30
  },
27
31
  parameters: {
28
32
  docs: {
@@ -42,15 +46,11 @@ const meta = {
42
46
  export default meta
43
47
  type Story = StoryObj<typeof meta>
44
48
 
45
- interface Position {
46
- position?: 'left' | 'right' | 'center'
47
- }
48
-
49
49
  export const Menu: Story = {
50
- render: ({ position }: Position) => (
50
+ render: (props: PopoverProps) => (
51
51
  <div style={{ display: 'flex', justifyContent: 'center' }}>
52
52
  <Popover
53
- position={position}
53
+ {...props}
54
54
  renderButton={({ toggle }) => (
55
55
  <Button
56
56
  variant="primary-outlined"
@@ -82,3 +82,36 @@ export const Menu: Story = {
82
82
  </div>
83
83
  ),
84
84
  } as unknown as Story
85
+
86
+ export const MenuWithButtons: Story = {
87
+ args: {
88
+ renderButton: ({ toggle }) => (
89
+ <Button
90
+ variant="primary-outlined"
91
+ label="Open popover"
92
+ onClick={toggle}
93
+ />
94
+ ),
95
+ children: (
96
+ <PopoverMenu>
97
+ <PopoverMenuOption
98
+ active
99
+ onClick={() => alert('Profile')}
100
+ variant="primary"
101
+ title="Profile"
102
+ />
103
+ <PopoverMenuOption
104
+ onClick={() => alert('Change password')}
105
+ variant="primary"
106
+ title="Change password"
107
+ />
108
+ <PopoverMenuOption
109
+ disabled
110
+ onClick={() => alert('Logout')}
111
+ variant="primary"
112
+ title="Logout"
113
+ />
114
+ </PopoverMenu>
115
+ ),
116
+ },
117
+ }
@@ -4,7 +4,7 @@ import { IconButton } from '../src/atoms/Button'
4
4
  import { Divider } from '../src/atoms/Divider'
5
5
 
6
6
  describe('Divider', () => {
7
- it('renders', () => {
7
+ it('renders with title', () => {
8
8
  const { getByRole, getByText, container } = render(
9
9
  <Divider title="A title divider" icon="Line" />,
10
10
  )
@@ -44,4 +44,10 @@ describe('Divider', () => {
44
44
  expect(getByText('A title divider with button')).toBeInTheDocument()
45
45
  expect(handleClick).toHaveBeenCalledTimes(1)
46
46
  })
47
+
48
+ it('renders without title nor icon', () => {
49
+ const { getByRole, container } = render(<Divider />)
50
+ expect(getByRole('separator')).toHaveClass('divider primary')
51
+ expect(container.querySelector('.long.line')).toBeInTheDocument()
52
+ })
47
53
  })
@@ -52,6 +52,10 @@ describe('PopoverMenu', () => {
52
52
  })
53
53
 
54
54
  it('hides when click outside of the menu', async () => {
55
+ document.head.insertAdjacentHTML(
56
+ 'beforeend',
57
+ `<style>.hidden { display: none !important; }</style>`,
58
+ )
55
59
  const user = userEvent.setup()
56
60
  const { getByText, queryByText } = render(
57
61
  <>
@@ -77,6 +81,6 @@ describe('PopoverMenu', () => {
77
81
  expect(getByText(/Profile/i)).toBeInTheDocument()
78
82
  await user.click(getByText(/Outside/i))
79
83
 
80
- expect(queryByText(/Profile/i)).not.toBeInTheDocument()
84
+ expect(queryByText(/Profile/i)).not.toBeVisible()
81
85
  })
82
86
  })