agroptima-design-system 0.26.0-beta.2 → 0.26.0-beta.4

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.26.0-beta.2",
3
+ "version": "0.26.0-beta.4",
4
4
  "scripts": {
5
5
  "dev": "npm run storybook",
6
6
  "storybook": "storybook dev -p 6006 --ci",
@@ -16,23 +16,24 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@storybook/addon-designs": "^8.0.3",
19
- "@storybook/addon-viewport": "^8.2.9",
19
+ "@storybook/addon-viewport": "^8.3.2",
20
+ "agroptima-design-system": "^0.26.0-beta.3",
20
21
  "next": "^14.0.3",
21
22
  "react": "^18.2.0",
22
23
  "react-dom": "^18.2.0",
23
24
  "sass": "^1.69.4"
24
25
  },
25
26
  "devDependencies": {
26
- "@chromatic-com/storybook": "^1.8.0",
27
- "@storybook/addon-a11y": "^8.2.9",
28
- "@storybook/addon-essentials": "^8.2.9",
29
- "@storybook/addon-interactions": "^8.2.9",
30
- "@storybook/addon-links": "^8.2.9",
31
- "@storybook/addon-mdx-gfm": "^8.2.9",
32
- "@storybook/blocks": "^8.2.9",
33
- "@storybook/nextjs": "^8.2.9",
34
- "@storybook/react": "^8.2.9",
35
- "@storybook/test": "^8.2.9",
27
+ "@chromatic-com/storybook": "^2.0.2",
28
+ "@storybook/addon-a11y": "^8.3.2",
29
+ "@storybook/addon-essentials": "^8.3.2",
30
+ "@storybook/addon-interactions": "^8.3.2",
31
+ "@storybook/addon-links": "^8.3.2",
32
+ "@storybook/addon-mdx-gfm": "^8.3.2",
33
+ "@storybook/blocks": "^8.3.2",
34
+ "@storybook/nextjs": "^8.3.2",
35
+ "@storybook/react": "^8.3.2",
36
+ "@storybook/test": "^8.3.2",
36
37
  "@svgr/webpack": "^8.1.0",
37
38
  "@testing-library/jest-dom": "^6.4.2",
38
39
  "@testing-library/react": "^16.0.0",
@@ -50,7 +51,7 @@
50
51
  "jest": "^29.7.0",
51
52
  "jest-axe": "^9.0.0",
52
53
  "jest-environment-jsdom": "^29.7.0",
53
- "storybook": "^8.2.9",
54
+ "storybook": "^8.3.2",
54
55
  "ts-node": "^10.9.2",
55
56
  "typescript": "^5"
56
57
  },
@@ -16,8 +16,8 @@
16
16
  @include typography.footnote-primary;
17
17
 
18
18
  &.dot {
19
- width: 6px;
20
- height: 6px;
19
+ width: 8px;
20
+ height: 8px;
21
21
  padding: 0;
22
22
  }
23
23
 
@@ -0,0 +1,86 @@
1
+ @use '../settings/color_alias';
2
+ @use '../settings/typography/content' as typography;
3
+ @use '../settings/config';
4
+ @use '../settings/depth';
5
+ @use '../settings/breakpoints';
6
+
7
+ .drawer-container {
8
+ display: flex;
9
+ justify-content: flex-end;
10
+ z-index: depth.$z-modal;
11
+
12
+ .backdrop {
13
+ position: fixed;
14
+ height: 100%;
15
+ opacity: 0.4;
16
+ background: color_alias.$neutral-color-900;
17
+ inset: 0;
18
+ }
19
+
20
+ .drawer {
21
+ display: flex;
22
+ position: absolute;
23
+ top: 0;
24
+ right: 0;
25
+ padding: config.$space-5x;
26
+ flex-direction: column;
27
+ align-items: flex-start;
28
+ justify-content: flex-start;
29
+ gap: config.$space-2x;
30
+ flex-grow: 1;
31
+ width: 26rem;
32
+ height: 100vh;
33
+ border-radius: config.$corner-radius-xxs;
34
+ background: color_alias.$neutral-white;
35
+ box-shadow:
36
+ 0px 3px 6px -4px rgba(0, 0, 0, 0.12),
37
+ 0px 6px 16px 0px rgba(0, 0, 0, 0.08),
38
+ 0px 9px 28px 8px rgba(0, 0, 0, 0.05);
39
+
40
+ .close-container {
41
+ align-self: flex-end;
42
+ }
43
+
44
+ .scroll-area {
45
+ width: 100%;
46
+ padding-left: config.$space-halfx;
47
+ padding-right: config.$space-halfx;
48
+ overflow-y: auto;
49
+ overflow-anchor: none;
50
+
51
+ .header {
52
+ display: flex;
53
+ gap: config.$space-2x;
54
+ align-items: flex-start;
55
+
56
+ .title {
57
+ @include typography.h4;
58
+ }
59
+ }
60
+
61
+ .body {
62
+ @include typography.body-regular-primary;
63
+ width: 100%;
64
+ }
65
+ }
66
+
67
+ .footer {
68
+ display: flex;
69
+ align-self: flex-end;
70
+ gap: config.$space-3x;
71
+ margin-top: config.$space-2x;
72
+
73
+ .actions {
74
+ position: absolute;
75
+ }
76
+ }
77
+ }
78
+
79
+ // Media queries
80
+ // Mobile & tablet cases
81
+ @media only screen and (max-width: breakpoints.$medium) {
82
+ .drawer {
83
+ width: 100%;
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,66 @@
1
+ import type { ButtonProps } from './Button'
2
+ import { classNames } from '../utils/classNames'
3
+ import { Button, IconButton } from './Button'
4
+ import './Drawer.scss'
5
+ import { Actions } from './Form'
6
+
7
+ export type Variant = 'primary'
8
+
9
+ export interface DrawerProps extends React.ComponentPropsWithoutRef<'div'> {
10
+ id: string
11
+ variant?: Variant
12
+ title: string
13
+ buttons: ButtonProps[]
14
+ onCloseDrawer: () => void
15
+ }
16
+
17
+ export function Drawer({
18
+ id,
19
+ className,
20
+ variant = 'primary',
21
+ title,
22
+ buttons,
23
+ children,
24
+ onCloseDrawer,
25
+ ...props
26
+ }: DrawerProps): React.JSX.Element {
27
+ const cssClasses = classNames('drawer', variant, className)
28
+
29
+ return (
30
+ <div className="drawer-container">
31
+ <div className="backdrop"></div>
32
+ <div
33
+ role="dialog"
34
+ aria-labelledby={`${id}-title`}
35
+ aria-describedby={`${id}-body`}
36
+ className={cssClasses}
37
+ {...props}
38
+ >
39
+ <div className="close-container">
40
+ <IconButton
41
+ icon="Close"
42
+ accessibilityLabel="Close"
43
+ onClick={onCloseDrawer}
44
+ />
45
+ </div>
46
+ <div className="scroll-area">
47
+ <div className="header">
48
+ <h4 id={`${id}-title`} className="title">
49
+ {title}
50
+ </h4>
51
+ </div>
52
+ <div id={`${id}-body`} className="body">
53
+ {children}
54
+ </div>
55
+ </div>
56
+ <div className="footer">
57
+ <Actions>
58
+ {buttons.map(({ ...button }) => (
59
+ <Button key={button.label} {...button} />
60
+ ))}
61
+ </Actions>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ )
66
+ }
@@ -20,20 +20,20 @@
20
20
  &:has(.actions) {
21
21
  padding-bottom: 6.25rem;
22
22
  }
23
+ }
23
24
 
24
- .actions {
25
- display: flex;
26
- justify-content: flex-end;
27
- gap: config.$space-4x;
25
+ .actions {
26
+ display: flex;
27
+ justify-content: flex-end;
28
+ gap: config.$space-4x;
28
29
 
29
- flex-direction: row;
30
- padding: config.$space-3x config.$space-5x;
31
- background-color: color_alias.$neutral-white;
32
- box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.3);
30
+ flex-direction: row;
31
+ padding: config.$space-3x config.$space-5x;
32
+ background-color: color_alias.$neutral-white;
33
+ box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.3);
33
34
 
34
- position: fixed;
35
- inset: auto 0 0 0;
36
- }
35
+ position: fixed;
36
+ inset: auto 0 0 0;
37
37
  }
38
38
 
39
39
  @media only screen and (max-width: breakpoints.$large) {
@@ -43,12 +43,18 @@
43
43
  &:has(.actions) {
44
44
  padding-bottom: 9rem;
45
45
  }
46
- .actions {
47
- padding: 0;
48
- flex-direction: column;
49
- box-shadow: none;
50
- background-color: transparent;
51
- inset: auto config.$space-4x config.$space-4x config.$space-4x;
46
+ }
47
+ .actions {
48
+ background-color: color_alias.$neutral-white;
49
+ border-top: 1px solid color_alias.$neutral-color-200;
50
+ flex-direction: row;
51
+ justify-content: space-between;
52
+ gap: config.$space-2x;
53
+ box-shadow: none;
54
+ padding: config.$space-4x auto config.$space-4x auto;
55
+
56
+ .button {
57
+ width: 50%;
52
58
  }
53
59
  }
54
60
  }
@@ -1,6 +1,7 @@
1
1
  @use '../settings/color_alias';
2
2
  @use '../settings/typography/form' as typography;
3
3
  @use '../settings/config';
4
+ @import 'button/Button.scss';
4
5
 
5
6
  .input-group {
6
7
  @include typography.input-text;
@@ -124,4 +125,22 @@
124
125
  }
125
126
  }
126
127
  }
128
+ &.file .input-container {
129
+ padding: 0;
130
+ border: none;
131
+ input {
132
+ color: color_alias.$neutral-color-600;
133
+ &::before {
134
+ content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='12' fill='none'%3E%3Cpath d='M2.943 8.718h4.114V4.694H9.8L5 0 .2 4.694h2.743v4.024ZM5 1.898l1.488 1.455h-.802v4.023H4.314V3.353h-.802L5 1.898Zm-4.8 8.16h9.6V11.4H.2v-1.341Z' fill='%23EB004D'/%3E%3C/svg%3E");
135
+ position: absolute;
136
+ left: config.$space-3x;
137
+ bottom: config.$space-2x;
138
+ }
139
+ &::file-selector-button {
140
+ margin-right: config.$space-2x;
141
+ padding-left: config.$space-3x + config.$icon-size-2x + config.$space-1x;
142
+ @extend .button;
143
+ }
144
+ }
145
+ }
127
146
  }
@@ -57,6 +57,7 @@ export function Input({
57
57
  return (
58
58
  <div
59
59
  className={classNames('input-group', variant, className, {
60
+ file: type === 'file',
60
61
  invalid: errors?.length,
61
62
  })}
62
63
  >
@@ -73,6 +74,7 @@ export function Input({
73
74
  type={handleInputType()}
74
75
  name={name}
75
76
  aria-label={accessibilityLabel || label}
77
+ className={classNames({ 'primary-outlined': type === 'file' })}
76
78
  {...props}
77
79
  />
78
80
  {suffix && <span className="input-suffix">{suffix}</span>}
@@ -154,12 +154,5 @@
154
154
  }
155
155
  }
156
156
  }
157
-
158
- // Media queries
159
- // Mobile & tablet cases
160
- @media only screen and (max-width: breakpoints.$large) {
161
- &.details {
162
- }
163
- }
164
157
  }
165
158
  }
@@ -9,6 +9,15 @@ import { Meta } from "@storybook/blocks";
9
9
  * Add CheckableTag & CheckableTagGroup components.
10
10
  * Add Filter icon.
11
11
  * Add Dot option to Badge component.
12
+ * Add Drawer component.
13
+
14
+ # 0.25.7
15
+
16
+ * Add styles for file Input component
17
+
18
+ # 0.25.6
19
+
20
+ * Align Form Action buttons horizontally
12
21
 
13
22
  # 0.25.5
14
23
 
@@ -0,0 +1,125 @@
1
+ import React from 'react'
2
+
3
+ import { Drawer } from '../atoms/Drawer'
4
+ import { CheckableTagGroup, CheckableTag } from '../atoms/CheckableTag'
5
+ import { Collapsible } from '@/atoms/Collapsible'
6
+
7
+ const figmaPrimaryDesign = {
8
+ design: {
9
+ type: 'figma',
10
+ url: 'https://www.figma.com/file/DN2ova21vWqCRvPspBXgI1/Design-System?type=design&node-id=1992-142&mode=dev',
11
+ },
12
+ }
13
+
14
+ const meta = {
15
+ title: 'Design System/Atoms/Drawer',
16
+ component: Drawer,
17
+ tags: ['autodocs'],
18
+ argTypes: {
19
+ id: {
20
+ description: 'Id for aria purposes',
21
+ },
22
+ variant: {
23
+ description: 'Component variant used',
24
+ },
25
+ title: {
26
+ description: 'Component title text',
27
+ },
28
+ buttons: {
29
+ description: 'Array of button to be shown on the footer',
30
+ },
31
+ },
32
+ parameters: figmaPrimaryDesign,
33
+ }
34
+
35
+ export default meta
36
+
37
+ export const Filters = {
38
+ render: () => (
39
+ <Drawer
40
+ id="videogames-filters"
41
+ title="Filters"
42
+ buttons={[
43
+ {
44
+ label: 'Clean',
45
+ variant: 'primary-outlined',
46
+ onClick: () => alert('click'),
47
+ },
48
+ {
49
+ label: 'Apply',
50
+ onClick: () => alert('click'),
51
+ },
52
+ ]}
53
+ >
54
+ <Collapsible noHorizontalPadding open title="Genres">
55
+ <CheckableTagGroup>
56
+ <CheckableTag
57
+ variant="primary"
58
+ label="RPG"
59
+ aria-label="RPG games"
60
+ onSelect={() => {}}
61
+ onChange={() => {}}
62
+ isChecked={false}
63
+ />
64
+ <CheckableTag
65
+ variant="primary"
66
+ label="Sports"
67
+ aria-label="Sport games"
68
+ onSelect={() => {}}
69
+ onChange={() => {}}
70
+ isChecked={false}
71
+ />
72
+ <CheckableTag
73
+ variant="primary"
74
+ label="Party"
75
+ aria-label="Party games"
76
+ onSelect={() => {}}
77
+ onChange={() => {}}
78
+ isChecked={false}
79
+ isDisabled={true}
80
+ />
81
+ <CheckableTag
82
+ variant="primary"
83
+ label="Survival horror"
84
+ aria-label="Survival horror games"
85
+ onSelect={() => {}}
86
+ onChange={() => {}}
87
+ isChecked={true}
88
+ />
89
+ <CheckableTag
90
+ variant="primary"
91
+ label="Action"
92
+ aria-label="Action games"
93
+ onSelect={() => {}}
94
+ onChange={() => {}}
95
+ isChecked={false}
96
+ />
97
+ <CheckableTag
98
+ variant="primary"
99
+ label="Platform"
100
+ aria-label="Platform games"
101
+ onSelect={() => {}}
102
+ onChange={() => {}}
103
+ isChecked={false}
104
+ />
105
+ <CheckableTag
106
+ variant="primary"
107
+ label="Graphic adventure"
108
+ aria-label="Graphic adventure games"
109
+ onSelect={() => {}}
110
+ onChange={() => {}}
111
+ isChecked={false}
112
+ />
113
+ <CheckableTag
114
+ variant="primary"
115
+ label="FPS"
116
+ aria-label="First Person Shooter games"
117
+ onSelect={() => {}}
118
+ onChange={() => {}}
119
+ isChecked={false}
120
+ />
121
+ </CheckableTagGroup>
122
+ </Collapsible>
123
+ </Drawer>
124
+ ),
125
+ }
@@ -86,6 +86,18 @@ export const Password: Story = {
86
86
  parameters: figmaPrimaryDesign,
87
87
  }
88
88
 
89
+ export const File: Story = {
90
+ args: {
91
+ label: 'Label for input file',
92
+ variant: 'primary',
93
+ disabled: false,
94
+ helpText: 'This text can help you',
95
+ name: 'file',
96
+ type: 'file',
97
+ },
98
+ parameters: figmaPrimaryDesign,
99
+ }
100
+
89
101
  export const WithErrors: Story = {
90
102
  args: {
91
103
  label: 'Email',
@@ -0,0 +1,63 @@
1
+ import React from 'react'
2
+ import { screen, render, queryByRole } from '@testing-library/react'
3
+ import { Modal } from '@/atoms/Modal'
4
+
5
+ describe('Modal', () => {
6
+ const variants = ['info', 'success', 'warning', 'error']
7
+ it.each(variants)(
8
+ 'renders the %s variant with title and the expected icon and button',
9
+ (variant) => {
10
+ const title = `${variant} modal`
11
+ const content = `${variant} modal content`
12
+ const { getByRole, getByText } = render(
13
+ <Modal
14
+ id={`${variant}-modal`}
15
+ title={title}
16
+ buttons={[
17
+ {
18
+ label: 'Done',
19
+ },
20
+ ]}
21
+ >
22
+ {content}
23
+ </Modal>,
24
+ )
25
+ expect(getByRole('img')).toHaveClass('info')
26
+ expect(getByText(title)).toBeInTheDocument()
27
+ expect(getByText(content)).toBeInTheDocument()
28
+ expect(getByRole('button')).toHaveTextContent('Done')
29
+ expect(getByRole('button')).toHaveClass('primary')
30
+ },
31
+ )
32
+
33
+ it('renders the Delete/Discard variant with title and the expected icon and buttons', () => {
34
+ const title = 'Delete modal'
35
+ const content = 'Delete modal content'
36
+ const { getByRole, getByText } = render(
37
+ <Modal
38
+ id="discard-modal"
39
+ title={title}
40
+ variant="discard"
41
+ buttons={[
42
+ {
43
+ label: 'Cancel',
44
+ variant: 'neutral',
45
+ },
46
+ {
47
+ label: 'Delete',
48
+ variant: 'error',
49
+ },
50
+ ]}
51
+ >
52
+ {content}
53
+ </Modal>,
54
+ )
55
+ expect(getByRole('img')).toHaveClass('discard')
56
+ expect(getByText(title)).toBeInTheDocument()
57
+ expect(getByText(content)).toBeInTheDocument()
58
+ expect(screen.getAllByRole('button')[0]).toHaveTextContent('Cancel')
59
+ expect(screen.getAllByRole('button')[0]).toHaveClass('neutral')
60
+ expect(screen.getAllByRole('button')[1]).toHaveTextContent('Delete')
61
+ expect(screen.getAllByRole('button')[1]).toHaveClass('error')
62
+ })
63
+ })