agroptima-design-system 0.24.5 → 0.25.0-beta.2

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.24.5",
3
+ "version": "0.25.0-beta.2",
4
4
  "scripts": {
5
5
  "dev": "npm run storybook",
6
6
  "storybook": "storybook dev -p 6006 --ci",
@@ -11,6 +11,7 @@
11
11
  "types": "tsc --noEmit",
12
12
  "chromatic": "npx chromatic --exit-zero-on-changes",
13
13
  "test": "jest",
14
+ "test-coverage": "jest --coverage",
14
15
  "publish:beta": "npm publish --tag beta"
15
16
  },
16
17
  "dependencies": {
@@ -35,6 +36,7 @@
35
36
  "@svgr/webpack": "^8.1.0",
36
37
  "@testing-library/jest-dom": "^6.4.2",
37
38
  "@testing-library/react": "^16.0.0",
39
+ "@testing-library/user-event": "^14.5.2",
38
40
  "@types/jest": "^29.5.12",
39
41
  "@types/jest-axe": "^3.5.9",
40
42
  "@types/node": "^22.1.0",
@@ -1,6 +1,7 @@
1
1
  import type { IconType } from './Icon'
2
2
  import React, { useState } from 'react'
3
3
  import { Icon } from './Icon'
4
+ import { IconButton } from './Button/IconButton'
4
5
  import { classNames } from '../utils/classNames'
5
6
  import { buildHelpText } from '../utils/buildHelpText'
6
7
  import './Input.scss'
@@ -76,9 +77,10 @@ export function Input({
76
77
  />
77
78
  {suffix && <span className="input-suffix">{suffix}</span>}
78
79
  {type === 'password' && (
79
- <Icon
80
+ <IconButton
81
+ accessibilityLabel={handlePasswordIcon()}
80
82
  className="password-icon"
81
- name={handlePasswordIcon()}
83
+ icon={handlePasswordIcon()}
82
84
  onClick={handlePasswordVisibility}
83
85
  />
84
86
  )}
@@ -0,0 +1,46 @@
1
+ @use '../../settings/color_alias';
2
+ @use '../../settings/typography/content' as typography;
3
+ @use '../../settings/config';
4
+ @use '../../settings/depth';
5
+
6
+ .popover-menu {
7
+ box-shadow:
8
+ 0px 3px 6px -4px rgba(0, 0, 0, 0.12),
9
+ 0px 6px 16px 0px rgba(0, 0, 0, 0.08),
10
+ 0px 9px 28px 8px rgba(0, 0, 0, 0.05);
11
+
12
+ .popover-menu-option {
13
+ @include typography.body-regular-primary;
14
+ display: flex;
15
+ flex-direction: row;
16
+ align-items: flex-start;
17
+ justify-content: space-between;
18
+ width: 100%;
19
+ padding: config.$space-2x config.$space-3x config.$space-2x config.$space-3x;
20
+ gap: config.$space-3x;
21
+ border-radius: config.$corner-radius-xxs;
22
+ text-decoration: none;
23
+ cursor: default;
24
+
25
+ &:hover {
26
+ text-decoration: none;
27
+ }
28
+
29
+ &.primary {
30
+ background: color_alias.$neutral-white;
31
+
32
+ &.active {
33
+ background-color: transparent;
34
+ }
35
+
36
+ &:not(.disabled):hover {
37
+ background: color_alias.$primary-color-50;
38
+ }
39
+
40
+ &.disabled {
41
+ background: color_alias.$neutral-color-50;
42
+ @include typography.body-regular-disabled;
43
+ }
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,13 @@
1
+ export interface PopoverMenuProps
2
+ extends React.ComponentPropsWithoutRef<'div'> {}
3
+
4
+ export function PopoverMenu({
5
+ children,
6
+ ...props
7
+ }: PopoverMenuProps): React.JSX.Element {
8
+ return (
9
+ <div role="menu" className="popover-menu" {...props}>
10
+ {children}
11
+ </div>
12
+ )
13
+ }
@@ -0,0 +1,42 @@
1
+ import type { LinkProps as NextLinkProps } from 'next/link'
2
+ import { classNames } from '../../utils/classNames'
3
+ import Link from 'next/link'
4
+ import './PopoverMenu.scss'
5
+
6
+ export type Variant = 'primary'
7
+
8
+ type LinkProps = NextLinkProps & React.AnchorHTMLAttributes<HTMLAnchorElement>
9
+ export interface PopoverMenuOptionProps extends LinkProps {
10
+ variant?: Variant
11
+ title: string
12
+ disabled?: boolean
13
+ href: string
14
+ active?: boolean
15
+ }
16
+
17
+ export function PopoverMenuOption({
18
+ variant = 'primary',
19
+ className,
20
+ title,
21
+ disabled,
22
+ href,
23
+ active,
24
+ ...props
25
+ }: PopoverMenuOptionProps): React.JSX.Element {
26
+ const cssClasses = classNames('popover-menu-option', variant, className, {
27
+ disabled,
28
+ active,
29
+ })
30
+
31
+ return (
32
+ <Link
33
+ role="menuitem"
34
+ className={cssClasses}
35
+ href={disabled ? '#' : href}
36
+ aria-disabled={disabled}
37
+ {...props}
38
+ >
39
+ <span className="title">{title}</span>
40
+ </Link>
41
+ )
42
+ }
@@ -0,0 +1,4 @@
1
+ import { PopoverMenu } from './PopoverMenu'
2
+ import { PopoverMenuOption } from './PopoverMenuOption'
3
+
4
+ export { PopoverMenu, PopoverMenuOption }
@@ -36,6 +36,7 @@ import Settings from './settings.svg'
36
36
  import Show from './show.svg'
37
37
  import ShowOff from './show-off.svg'
38
38
  import Sorter from './sorter.svg'
39
+ import UserMenu from './user-menu.svg'
39
40
  import Warning from './warning.svg'
40
41
  import DeliveryNote from './delivery-note.svg'
41
42
  import PDF from './pdf.svg'
@@ -81,5 +82,6 @@ export {
81
82
  Show,
82
83
  ShowOff,
83
84
  Sorter,
85
+ UserMenu,
84
86
  Warning,
85
87
  }
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#user-menu__a)"><path d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0Zm0 4c1.93 0 3.5 1.57 3.5 3.5S11.93 11 10 11 6.5 9.43 6.5 7.5 8.07 4 10 4Zm0 14c-2.03 0-4.43-.82-6.14-2.88a9.947 9.947 0 0 1 12.28 0C14.43 17.18 12.03 18 10 18Z" fill="#161C26"/></g><defs><clipPath id="user-menu__a"><path fill="#fff" d="M0 0h20v20H0z"/></clipPath></defs></svg>
@@ -4,6 +4,15 @@ import { Meta } from "@storybook/blocks";
4
4
 
5
5
  # Changelog
6
6
 
7
+ # 0.25.0
8
+
9
+ * Add PopoverMenu and PopoverMenuOption components.
10
+ * Add UserMenu icon.
11
+
12
+ # 0.24.5
13
+
14
+ * Add tests to all components.
15
+
7
16
  # 0.24.4
8
17
 
9
18
  * Remove margin from Alert component.
@@ -0,0 +1,43 @@
1
+ import React from 'react'
2
+
3
+ import { PopoverMenu, PopoverMenuOption } from '../atoms/PopoverMenu'
4
+
5
+ const figmaPrimaryDesign = {
6
+ design: {
7
+ type: 'figma',
8
+ url: 'https://www.figma.com/design/DN2ova21vWqCRvPspBXgI1/Design-System?node-id=3200-3749&m=dev',
9
+ },
10
+ }
11
+
12
+ const meta = {
13
+ title: 'Design System/Atoms/PopoverMenu',
14
+ component: PopoverMenuOption,
15
+ tags: ['autodocs'],
16
+ argTypes: {
17
+ variant: {
18
+ description: 'Component variant used',
19
+ },
20
+ title: {
21
+ description: 'Component title text',
22
+ },
23
+ disabled: {
24
+ description: 'Is the component disabled?',
25
+ },
26
+ active: {
27
+ description: 'Is the component active?',
28
+ },
29
+ },
30
+ parameters: figmaPrimaryDesign,
31
+ }
32
+
33
+ export default meta
34
+
35
+ export const Menu = {
36
+ render: () => (
37
+ <PopoverMenu>
38
+ <PopoverMenuOption active href="#" variant="primary" title="Profile" />
39
+ <PopoverMenuOption href="#" variant="primary" title="Change password" />
40
+ <PopoverMenuOption disabled href="#" variant="primary" title="Logout" />
41
+ </PopoverMenu>
42
+ ),
43
+ }
@@ -0,0 +1,113 @@
1
+ import type { ButtonVariant } from '@/atoms/Button/Button'
2
+ import React from 'react'
3
+ import { screen, render } from '@testing-library/react'
4
+ import userEvent from '@testing-library/user-event'
5
+ import { Button } from '@/atoms/Button/Button'
6
+
7
+ describe('Button', () => {
8
+ const variants = [
9
+ 'primary',
10
+ 'primary-ghost',
11
+ 'primary-outlined',
12
+ 'neutral',
13
+ 'neutral-ghost',
14
+ 'neutral-outlined',
15
+ 'error',
16
+ 'error-ghost',
17
+ 'error-outlined',
18
+ 'success',
19
+ 'success-ghost',
20
+ 'success-outlined',
21
+ 'info',
22
+ 'info-ghost',
23
+ 'info-outlined',
24
+ 'warning',
25
+ 'warning-ghost',
26
+ 'warning-outlined',
27
+ ]
28
+
29
+ it.each(variants)(
30
+ 'renders the Link version %s variant with text and expected styles',
31
+ (variant) => {
32
+ const label = `${variant} Button`
33
+ const { getByRole, getByText } = render(
34
+ <Button
35
+ id={`${variant}-button`}
36
+ label={label}
37
+ variant={variant as ButtonVariant}
38
+ href="link.com"
39
+ />,
40
+ )
41
+ expect(getByRole('link')).toHaveClass(`button ${variant}`)
42
+ expect(getByText(label)).toBeInTheDocument()
43
+ expect(getByRole('link')).toBeInTheDocument()
44
+ },
45
+ )
46
+
47
+ it.each(variants)(
48
+ 'renders the Button version %s variant with text and expected styles',
49
+ (variant) => {
50
+ const label = `${variant} Button`
51
+ const { getByRole, getByText } = render(
52
+ <Button
53
+ id={`${variant}-button`}
54
+ label={label}
55
+ variant={variant as ButtonVariant}
56
+ onClick={() => alert('click')}
57
+ />,
58
+ )
59
+ expect(getByRole('button')).toHaveClass(`button ${variant}`)
60
+ expect(getByText(label)).toBeInTheDocument()
61
+ expect(getByRole('button')).toBeInTheDocument()
62
+ },
63
+ )
64
+
65
+ it('renders the icon when leftIcon prop is passed', async () => {
66
+ const { getByRole } = render(
67
+ <Button
68
+ id="button-with-icon"
69
+ label="Button with icon"
70
+ variant="info"
71
+ leftIcon="AngleLeft"
72
+ href="link.com"
73
+ />,
74
+ )
75
+
76
+ expect(getByRole('img').title).toBe('AngleLeft')
77
+ })
78
+
79
+ it('triggers event on onClick', async () => {
80
+ const user = userEvent.setup()
81
+ const onClickEvent = jest.fn()
82
+ render(
83
+ <Button
84
+ id="enabled-button"
85
+ label="Enabled button"
86
+ variant="info"
87
+ onClick={onClickEvent}
88
+ />,
89
+ )
90
+
91
+ await user.click(screen.getByRole('button'))
92
+
93
+ expect(onClickEvent).toHaveBeenCalled()
94
+ })
95
+
96
+ it('does not trigger event on onClick if Button is disabled', async () => {
97
+ const user = userEvent.setup()
98
+ const onClickEvent = jest.fn()
99
+ render(
100
+ <Button
101
+ id="disabled-button"
102
+ label="Disabled button"
103
+ variant="info"
104
+ disabled
105
+ onClick={onClickEvent}
106
+ />,
107
+ )
108
+
109
+ await user.click(screen.getByRole('button'))
110
+
111
+ expect(onClickEvent).not.toHaveBeenCalled()
112
+ })
113
+ })
@@ -0,0 +1,21 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { Checkbox } from '@/atoms/Checkbox'
4
+
5
+ describe('Checkbox', () => {
6
+ const variants = ['primary']
7
+
8
+ it.each(variants)('renders with label and expected styles', (variant) => {
9
+ const { getByRole, getAllByRole, getByText } = render(
10
+ <Checkbox
11
+ accessibilityLabel="Marks if the user likes videogames"
12
+ id="checkbox-videogames-preference"
13
+ label="Do you like videogames?"
14
+ variant="primary"
15
+ />,
16
+ )
17
+ expect(getAllByRole('generic')[1]).toHaveClass(`checkbox-group ${variant}`)
18
+ expect(getByRole('checkbox')).toHaveClass(`checkbox ${variant}`)
19
+ expect(getByText(/Do you like videogames/i)).toBeInTheDocument()
20
+ })
21
+ })
@@ -1,11 +1,11 @@
1
1
  import React from 'react'
2
- import { screen, render } from '@testing-library/react'
2
+ import { render } from '@testing-library/react'
3
3
  import { Collapsible } from '@/atoms/Collapsible'
4
4
  import { Input } from '@/atoms/Input'
5
5
 
6
6
  describe('Collapsible', () => {
7
7
  it('renders', () => {
8
- const { getByRole, getByText, getAllByRole } = render(
8
+ const { getByRole, getByText } = render(
9
9
  <Collapsible title="My personal data" name="personal-data" open>
10
10
  <Input
11
11
  accessibilityLabel="Fill the form name"
@@ -0,0 +1,44 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import type { Variant } from '@/atoms/EmptyState'
4
+ import { EmptyState } from '@/atoms/EmptyState'
5
+ import { Button } from '@/atoms/Button/Button'
6
+
7
+ describe('EmptyState', () => {
8
+ const variants = ['primary']
9
+ it.each(variants)(
10
+ 'renders the Basic %s variant with the expected image and text',
11
+ (variant) => {
12
+ const content = `${variant} empty state content`
13
+ const { getAllByRole, getByRole, getByText } = render(
14
+ <EmptyState variant={variant as Variant}>
15
+ <p>{content}</p>
16
+ </EmptyState>,
17
+ )
18
+ expect(getAllByRole('generic')[1]).toHaveClass(`empty-state ${variant}`)
19
+ expect(getByRole('img').title).toBe('EmptyState')
20
+ expect(getByText(content)).toBeInTheDocument()
21
+ },
22
+ )
23
+
24
+ it('renders the Custom version with the expected image and content', () => {
25
+ const { getAllByRole, getByRole, getByText } = render(
26
+ <EmptyState>
27
+ <p>
28
+ There are no videogames yet. You can import videogames to your{' '}
29
+ <a href="#">list</a>.
30
+ </p>
31
+ <Button
32
+ label="Import videogames"
33
+ onClick={() => alert('click')}
34
+ ></Button>
35
+ </EmptyState>,
36
+ )
37
+ expect(getAllByRole('generic')[1]).toHaveClass(`empty-state primary`)
38
+ expect(getByRole('img').title).toBe('EmptyState')
39
+ expect(getByText(/There are no videogames yet/i)).toBeInTheDocument()
40
+ expect(getByText('Import videogames')).toBeInTheDocument()
41
+ expect(getByRole('link')).toBeInTheDocument()
42
+ expect(getByRole('button')).toBeInTheDocument()
43
+ })
44
+ })
@@ -0,0 +1,86 @@
1
+ import type { Variant } from '@/atoms/Button/FloatingButton'
2
+ import React from 'react'
3
+ import { screen, render } from '@testing-library/react'
4
+ import userEvent from '@testing-library/user-event'
5
+ import { FloatingButton } from '@/atoms/Button/FloatingButton'
6
+
7
+ describe('Floating Button', () => {
8
+ const variants = ['primary']
9
+
10
+ it.each(variants)(
11
+ 'renders the Link version %s variant with text and expected styles',
12
+ (variant) => {
13
+ const { getByRole } = render(
14
+ <FloatingButton
15
+ id={`${variant}-floating-button`}
16
+ icon="Add"
17
+ shape="circle"
18
+ accessibilityLabel="Add game"
19
+ variant={variant as Variant}
20
+ href="link.com"
21
+ />,
22
+ )
23
+ expect(getByRole('link')).toHaveClass(`floating-button ${variant} circle`)
24
+ expect(getByRole('img').title).toBe('Add')
25
+ expect(getByRole('link')).toBeInTheDocument()
26
+ },
27
+ )
28
+
29
+ it.each(variants)(
30
+ 'renders the Button version %s variant with text and expected styles',
31
+ (variant) => {
32
+ const { getByRole } = render(
33
+ <FloatingButton
34
+ id={`${variant}-floating-button`}
35
+ icon="Add"
36
+ shape="rounded-square"
37
+ accessibilityLabel="Add game"
38
+ variant={variant as Variant}
39
+ onClick={() => alert('click')}
40
+ />,
41
+ )
42
+ expect(getByRole('button')).toHaveClass(
43
+ `floating-button ${variant} rounded-square`,
44
+ )
45
+ expect(getByRole('img').title).toBe('Add')
46
+ expect(getByRole('button')).toBeInTheDocument()
47
+ },
48
+ )
49
+
50
+ it('triggers event on onClick', async () => {
51
+ const user = userEvent.setup()
52
+ const onClickEvent = jest.fn()
53
+ render(
54
+ <FloatingButton
55
+ id="enabled-floating-button"
56
+ icon="Add"
57
+ shape="rounded-square"
58
+ accessibilityLabel="Add game"
59
+ onClick={onClickEvent}
60
+ />,
61
+ )
62
+
63
+ await user.click(screen.getByRole('button'))
64
+
65
+ expect(onClickEvent).toHaveBeenCalled()
66
+ })
67
+
68
+ it('does not trigger event on onClick if Button is disabled', async () => {
69
+ const user = userEvent.setup()
70
+ const onClickEvent = jest.fn()
71
+ render(
72
+ <FloatingButton
73
+ id="disabled-floating-button"
74
+ icon="Add"
75
+ shape="rounded-square"
76
+ accessibilityLabel="Add game"
77
+ disabled
78
+ onClick={onClickEvent}
79
+ />,
80
+ )
81
+
82
+ await user.click(screen.getByRole('button'))
83
+
84
+ expect(onClickEvent).not.toHaveBeenCalled()
85
+ })
86
+ })
@@ -0,0 +1,19 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { Icon } from '@/atoms/Icon'
4
+
5
+ describe('Icon', () => {
6
+ it('renders with the expected graphic and styles', () => {
7
+ const { getByRole } = render(<Icon name="AngleLeft" className="info" />)
8
+
9
+ expect(getByRole('img')).toHaveClass('icon info')
10
+ expect(getByRole('img').title).toBe('AngleLeft')
11
+ })
12
+
13
+ it('has a rotating animation', () => {
14
+ const { getByRole } = render(<Icon name="Loading" className="info" />)
15
+
16
+ expect(getByRole('img')).toHaveClass('icon info rotate')
17
+ expect(getByRole('img').title).toBe('Loading')
18
+ })
19
+ })
@@ -0,0 +1,80 @@
1
+ import type { Variant } from '@/atoms/Button/IconButton'
2
+ import React from 'react'
3
+ import { screen, render } from '@testing-library/react'
4
+ import userEvent from '@testing-library/user-event'
5
+ import { IconButton } from '@/atoms/Button/IconButton'
6
+
7
+ describe('Icon Button', () => {
8
+ const variants = ['primary', 'secondary']
9
+
10
+ it.each(variants)(
11
+ 'renders the Link version %s variant with text and expected styles',
12
+ (variant) => {
13
+ const { getByRole } = render(
14
+ <IconButton
15
+ id={`${variant}-icon-button`}
16
+ icon="Edit"
17
+ accessibilityLabel="Edit game"
18
+ variant={variant as Variant}
19
+ href="link.com"
20
+ />,
21
+ )
22
+ expect(getByRole('link')).toHaveClass(`icon-button ${variant}`)
23
+ expect(getByRole('img').title).toBe('Edit game')
24
+ expect(getByRole('link')).toBeInTheDocument()
25
+ },
26
+ )
27
+
28
+ it.each(variants)(
29
+ 'renders the Button version %s variant with text and expected styles',
30
+ (variant) => {
31
+ const { getByRole } = render(
32
+ <IconButton
33
+ id={`${variant}-icon-button`}
34
+ icon="Edit"
35
+ accessibilityLabel="Edit game"
36
+ variant={variant as Variant}
37
+ onClick={() => alert('click')}
38
+ />,
39
+ )
40
+ expect(getByRole('button')).toHaveClass(`icon-button ${variant}`)
41
+ expect(getByRole('img').title).toBe('Edit game')
42
+ expect(getByRole('button')).toBeInTheDocument()
43
+ },
44
+ )
45
+
46
+ it('triggers event on onClick', async () => {
47
+ const user = userEvent.setup()
48
+ const onClickEvent = jest.fn()
49
+ render(
50
+ <IconButton
51
+ id="enabled-icon-button"
52
+ icon="Edit"
53
+ accessibilityLabel="Edit game"
54
+ onClick={onClickEvent}
55
+ />,
56
+ )
57
+
58
+ await user.click(screen.getByRole('button'))
59
+
60
+ expect(onClickEvent).toHaveBeenCalled()
61
+ })
62
+
63
+ it('does not trigger event on onClick if Button is disabled', async () => {
64
+ const user = userEvent.setup()
65
+ const onClickEvent = jest.fn()
66
+ render(
67
+ <IconButton
68
+ id="disabled-icon-button"
69
+ icon="Edit"
70
+ accessibilityLabel="Edit game"
71
+ disabled
72
+ onClick={onClickEvent}
73
+ />,
74
+ )
75
+
76
+ await user.click(screen.getByRole('button'))
77
+
78
+ expect(onClickEvent).not.toHaveBeenCalled()
79
+ })
80
+ })
@@ -0,0 +1,90 @@
1
+ import React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import userEvent from '@testing-library/user-event'
4
+ import { Input } from '@/atoms/Input'
5
+
6
+ describe('Input', () => {
7
+ it('renders the Text type', () => {
8
+ const { getByPlaceholderText, getAllByRole, getByRole, getByText } = render(
9
+ <Input
10
+ accessibilityLabel="Fill the form email"
11
+ helpText="This text can help you"
12
+ id="email_input"
13
+ label="Email"
14
+ name="email"
15
+ placeholder="Write your email..."
16
+ type="email"
17
+ variant="primary"
18
+ />,
19
+ )
20
+
21
+ expect(getByRole('textbox')).toHaveAttribute('type', 'email')
22
+ expect(getAllByRole('generic')[1]).toHaveClass('input-group primary')
23
+ expect(getByText('Email')).toBeInTheDocument()
24
+ expect(getByPlaceholderText(/Write your email.../)).toBeInTheDocument()
25
+ expect(getByText(/This text can help you/i)).toBeInTheDocument()
26
+ })
27
+
28
+ it('renders the Password type', async () => {
29
+ const user = userEvent.setup()
30
+ const { getByPlaceholderText, getAllByRole, getByRole, getByText } = render(
31
+ <Input
32
+ helpText="This text can help you"
33
+ id="password_input"
34
+ label="Password"
35
+ name="login_password"
36
+ placeholder="Write your password..."
37
+ type="password"
38
+ variant="primary"
39
+ />,
40
+ )
41
+
42
+ expect(getByPlaceholderText(/Write your password.../)).toHaveAttribute(
43
+ 'type',
44
+ 'password',
45
+ )
46
+ expect(getAllByRole('generic')[1]).toHaveClass('input-group primary')
47
+ expect(getByText('Password')).toBeInTheDocument()
48
+ expect(getByRole('img').title).toBe('Show')
49
+ expect(getByText(/This text can help you/i)).toBeInTheDocument()
50
+
51
+ await user.click(screen.getByRole('button'))
52
+
53
+ expect(getByRole('img').title).toBe('ShowOff')
54
+ })
55
+
56
+ it('renders input with errors', () => {
57
+ const { getAllByRole, getByText } = render(
58
+ <Input
59
+ accessibilityLabel="Fill the form email"
60
+ errors={['error1', 'error2']}
61
+ helpText="This text can help you"
62
+ id="email_input"
63
+ label="Email"
64
+ name="email"
65
+ placeholder="Email..."
66
+ type="email"
67
+ variant="primary"
68
+ />,
69
+ )
70
+
71
+ expect(getAllByRole('generic')[1]).toHaveClass('input-group invalid')
72
+ expect(getByText(/error1/i)).toBeInTheDocument()
73
+ expect(getByText(/error2/i)).toBeInTheDocument()
74
+ })
75
+
76
+ it('renders input with suffix', () => {
77
+ const { getByRole, getByText } = render(
78
+ <Input
79
+ helpText="This text can help you"
80
+ label="Input with suffix"
81
+ name="price"
82
+ suffix="€/Bottle"
83
+ type="number"
84
+ />,
85
+ )
86
+
87
+ expect(getByRole('spinbutton')).toBeInTheDocument()
88
+ expect(getByText('€/Bottle')).toBeInTheDocument()
89
+ })
90
+ })
@@ -0,0 +1,126 @@
1
+ import React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import userEvent from '@testing-library/user-event'
4
+ import { Multiselect } from '@/atoms/Multiselect'
5
+
6
+ describe('Multiselect', () => {
7
+ it('renders', async () => {
8
+ const user = userEvent.setup()
9
+ const { getAllByRole, getByText } = render(
10
+ <Multiselect
11
+ accessibilityLabel="Select your favourite videogames options"
12
+ helpText="This text can help you"
13
+ id="multiselect-videogames"
14
+ label="Videogames"
15
+ name="example"
16
+ options={[
17
+ {
18
+ id: '1',
19
+ label: 'The Legend of Zelda: Ocarina of Time',
20
+ },
21
+ {
22
+ id: '2',
23
+ label: 'Spyro the Dragon',
24
+ },
25
+ {
26
+ id: '3',
27
+ label: 'Halo',
28
+ },
29
+ ]}
30
+ placeholder="Select your favourite videogames..."
31
+ selectedLabel="videogames selected"
32
+ variant="primary"
33
+ />,
34
+ )
35
+
36
+ expect(getAllByRole('generic')[1]).toHaveClass('multiselect-group primary')
37
+ expect(getByText('Videogames')).toBeInTheDocument()
38
+ expect(getByText(/Select your favourite videogames.../)).toBeInTheDocument()
39
+ expect(getByText(/This text can help you/i)).toBeInTheDocument()
40
+
41
+ await user.click(screen.getByRole('alert'))
42
+
43
+ expect(getByText(/Zelda/i)).toBeInTheDocument()
44
+ expect(getByText(/Spyro/i)).toBeInTheDocument()
45
+ expect(getByText(/Halo/i)).toBeInTheDocument()
46
+ })
47
+
48
+ it('renders the number of options selected', async () => {
49
+ const user = userEvent.setup()
50
+ const { getByText } = render(
51
+ <Multiselect
52
+ helpText="This text can help you"
53
+ id="multiselect-videogames"
54
+ label="Videogames"
55
+ name="example"
56
+ options={[
57
+ {
58
+ id: '1',
59
+ label: 'The Legend of Zelda: Ocarina of Time',
60
+ },
61
+ {
62
+ id: '2',
63
+ label: 'Spyro the Dragon',
64
+ },
65
+ {
66
+ id: '3',
67
+ label: 'Halo',
68
+ },
69
+ ]}
70
+ placeholder="Select your favourite videogames..."
71
+ selected={[
72
+ {
73
+ id: '2',
74
+ label: 'Spyro the Dragon',
75
+ },
76
+ {
77
+ id: '1',
78
+ label: 'The Legend of Zelda: Ocarina of Time',
79
+ },
80
+ ]}
81
+ selectedLabel="videogames selected"
82
+ variant="primary"
83
+ />,
84
+ )
85
+
86
+ expect(getByText(/2 videogames selected/i)).toBeInTheDocument()
87
+
88
+ await user.click(screen.getByRole('alert'))
89
+ await user.click(screen.getAllByRole('option')[0])
90
+
91
+ expect(getByText(/1 videogames selected/i)).toBeInTheDocument()
92
+ })
93
+
94
+ it('renders with errors', () => {
95
+ const { getByText } = render(
96
+ <Multiselect
97
+ accessibilityLabel="Select your favourite videogames options"
98
+ errors={['error1', 'error2']}
99
+ helpText="This text can help you"
100
+ id="multiselect-videogames"
101
+ label="Videogames"
102
+ name="example"
103
+ options={[
104
+ {
105
+ id: '1',
106
+ label: 'The Legend of Zelda: Ocarina of Time',
107
+ },
108
+ {
109
+ id: '2',
110
+ label: 'Spyro the Dragon',
111
+ },
112
+ {
113
+ id: '3',
114
+ label: 'Halo',
115
+ },
116
+ ]}
117
+ placeholder="Select your favourite videogames..."
118
+ selectedLabel="videogames selected"
119
+ variant="primary"
120
+ />,
121
+ )
122
+
123
+ expect(getByText(/error1/i)).toBeInTheDocument()
124
+ expect(getByText(/error2/i)).toBeInTheDocument()
125
+ })
126
+ })
@@ -0,0 +1,28 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { PopoverMenu, PopoverMenuOption } from '@/atoms/PopoverMenu'
4
+
5
+ describe('PopoverMenu', () => {
6
+ it('renders', () => {
7
+ const { getByText, getAllByRole } = render(
8
+ <PopoverMenu>
9
+ <PopoverMenuOption active href="#" variant="primary" title="Profile" />
10
+ <PopoverMenuOption href="#" variant="primary" title="Change password" />
11
+ <PopoverMenuOption disabled href="#" variant="primary" title="Logout" />
12
+ </PopoverMenu>,
13
+ )
14
+
15
+ expect(getAllByRole('menuitem')[0]).toHaveClass(
16
+ `popover-menu-option primary active`,
17
+ )
18
+ expect(getAllByRole('menuitem')[1]).toHaveClass(
19
+ `popover-menu-option primary`,
20
+ )
21
+ expect(getAllByRole('menuitem')[2]).toHaveClass(
22
+ `popover-menu-option primary disabled`,
23
+ )
24
+ expect(getByText(/Profile/i)).toBeInTheDocument()
25
+ expect(getByText(/Change password/i)).toBeInTheDocument()
26
+ expect(getByText(/Logout/i)).toBeInTheDocument()
27
+ })
28
+ })
@@ -0,0 +1,121 @@
1
+ import React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import userEvent from '@testing-library/user-event'
4
+ import { Select } from '@/atoms/Select'
5
+
6
+ describe('Select', () => {
7
+ it('renders', async () => {
8
+ const user = userEvent.setup()
9
+ const { getAllByRole, getByText } = render(
10
+ <Select
11
+ accessibilityLabel="Select your favourite gaming system options"
12
+ helpText="This text can help you"
13
+ id="select-videogames"
14
+ label="Videogames"
15
+ name="example"
16
+ options={[
17
+ {
18
+ id: '1',
19
+ label: 'Nintendo Switch',
20
+ },
21
+ {
22
+ id: '2',
23
+ label: 'PlayStation 5',
24
+ },
25
+ {
26
+ id: '3',
27
+ label: 'Xbox Series S/X',
28
+ },
29
+ ]}
30
+ placeholder="Select your favourite gaming system..."
31
+ variant="primary"
32
+ />,
33
+ )
34
+
35
+ expect(getAllByRole('generic')[1]).toHaveClass('select-group primary')
36
+ expect(getByText('Videogames')).toBeInTheDocument()
37
+ expect(
38
+ getByText(/Select your favourite gaming system.../),
39
+ ).toBeInTheDocument()
40
+ expect(getByText(/This text can help you/i)).toBeInTheDocument()
41
+
42
+ await user.click(screen.getByRole('alert'))
43
+
44
+ expect(getByText(/Switch/i)).toBeInTheDocument()
45
+ expect(getByText(/PlayStation/i)).toBeInTheDocument()
46
+ expect(getByText(/Xbox/i)).toBeInTheDocument()
47
+ })
48
+
49
+ it('renders the selected option', async () => {
50
+ const user = userEvent.setup()
51
+ const { getByText, queryByText } = render(
52
+ <Select
53
+ defaultValue="2"
54
+ helpText="This text can help you"
55
+ id="select-videogames"
56
+ label="Videogames"
57
+ name="example"
58
+ options={[
59
+ {
60
+ id: '1',
61
+ label: 'Nintendo Switch',
62
+ },
63
+ {
64
+ id: '2',
65
+ label: 'PlayStation 5',
66
+ },
67
+ {
68
+ id: '3',
69
+ label: 'Xbox Series S/X',
70
+ },
71
+ ]}
72
+ placeholder="Select your favourite gaming system..."
73
+ variant="primary"
74
+ />,
75
+ )
76
+
77
+ expect(getByText(/PlayStation/i)).toBeInTheDocument()
78
+ expect(queryByText(/Switch/i)).not.toBeInTheDocument()
79
+ expect(queryByText(/Xbox/i)).not.toBeInTheDocument()
80
+
81
+ await user.click(screen.getByRole('alert'))
82
+ await user.click(screen.getAllByRole('option')[0])
83
+
84
+ expect(getByText(/Switch/i)).toBeInTheDocument()
85
+ expect(queryByText(/PlayStation/i)).not.toBeInTheDocument()
86
+ expect(queryByText(/Xbox/i)).not.toBeInTheDocument()
87
+ })
88
+
89
+ it('renders with errors', () => {
90
+ const { getByText } = render(
91
+ <Select
92
+ accessibilityLabel="Select your favourite gaming system options"
93
+ errors={['error1', 'error2']}
94
+ helpText="This text can help you"
95
+ id="select-videogames"
96
+ label="Videogames"
97
+ name="example"
98
+ onChange={() => {}}
99
+ options={[
100
+ {
101
+ id: '1',
102
+ label: 'Nintendo Switch',
103
+ },
104
+ {
105
+ id: '2',
106
+ label: 'PlayStation 5',
107
+ },
108
+ {
109
+ id: '3',
110
+ label: 'Xbox Series S/X',
111
+ },
112
+ ]}
113
+ placeholder="Select your favourite gaming system..."
114
+ variant="primary"
115
+ />,
116
+ )
117
+
118
+ expect(getByText(/error1/i)).toBeInTheDocument()
119
+ expect(getByText(/error2/i)).toBeInTheDocument()
120
+ })
121
+ })
@@ -0,0 +1,43 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { TextArea } from '@/atoms/TextArea'
4
+
5
+ describe('TextArea', () => {
6
+ it('renders', () => {
7
+ const { getByPlaceholderText, getAllByRole, getByRole, getByText } = render(
8
+ <TextArea
9
+ accessibilityLabel="Fill the textarea"
10
+ helpText="This text can help you"
11
+ id="textarea"
12
+ label="Description"
13
+ name="textarea"
14
+ placeholder="Write here..."
15
+ variant="primary"
16
+ />,
17
+ )
18
+
19
+ expect(getByRole('textbox')).toBeInTheDocument()
20
+ expect(getAllByRole('generic')[1]).toHaveClass('input-group primary')
21
+ expect(getByText('Description')).toBeInTheDocument()
22
+ expect(getByPlaceholderText(/Write here.../)).toBeInTheDocument()
23
+ })
24
+
25
+ it('renders with errors', () => {
26
+ const { getAllByRole, getByText } = render(
27
+ <TextArea
28
+ accessibilityLabel="Fill the form textarea"
29
+ errors={['Che che che', 'Another error']}
30
+ helpText="This text can help you"
31
+ id="textarea"
32
+ label="Description"
33
+ name="textarea"
34
+ placeholder="Write here..."
35
+ variant="primary"
36
+ />,
37
+ )
38
+
39
+ expect(getAllByRole('generic')[1]).toHaveClass('input-group invalid')
40
+ expect(getByText(/Che che che/i)).toBeInTheDocument()
41
+ expect(getByText(/Another error/i)).toBeInTheDocument()
42
+ })
43
+ })
@@ -1,4 +1,4 @@
1
- import { classNames } from '/utils/classNames'
1
+ import { classNames } from '@/utils/classNames'
2
2
 
3
3
  describe('classNames', () => {
4
4
  it('returns a string of classes', () => {