agroptima-design-system 0.28.4-beta.2 → 0.28.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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/src/atoms/Alert/Alert.scss +0 -41
  3. package/src/atoms/Alert/Alert.tsx +1 -1
  4. package/src/atoms/{DatePicker/DateRangePicker.scss → DatePicker.scss} +5 -5
  5. package/src/atoms/DatePicker.tsx +66 -0
  6. package/src/atoms/Form/{Form.scss → Actions.scss} +0 -27
  7. package/src/atoms/Form/Actions.tsx +2 -2
  8. package/src/atoms/Form/Form.tsx +5 -16
  9. package/src/atoms/Form/FormContainer.scss +35 -0
  10. package/src/atoms/Form/FormContainer.tsx +25 -0
  11. package/src/atoms/Form/index.ts +4 -3
  12. package/src/atoms/Icon.scss +22 -16
  13. package/src/atoms/Icon.tsx +4 -1
  14. package/src/atoms/Modal/Modal.scss +139 -0
  15. package/src/atoms/Modal/Modal.tsx +74 -0
  16. package/src/atoms/Modal/ModalBody.tsx +12 -0
  17. package/src/atoms/Modal/ModalCloseButton.tsx +23 -0
  18. package/src/atoms/Modal/ModalDialog.tsx +57 -0
  19. package/src/atoms/Modal/ModalFooter.tsx +17 -0
  20. package/src/atoms/Modal/ModalHeader.tsx +17 -0
  21. package/src/atoms/Modal/ModalTitle.tsx +12 -0
  22. package/src/atoms/Modal/index.tsx +24 -0
  23. package/src/settings/_mixins.scss +14 -0
  24. package/src/stories/Changelog.mdx +6 -1
  25. package/src/stories/Collapsible.stories.js +55 -66
  26. package/src/stories/DatePicker.stories.ts +6 -14
  27. package/src/stories/Drawer.stories.js +3 -2
  28. package/src/stories/Modal.stories.js +169 -73
  29. package/src/utils/LoremIpsum.tsx +15 -0
  30. package/tests/{DateRangePicker.spec.tsx → DatePicker.spec.tsx} +4 -3
  31. package/tests/Modal.spec.tsx +4 -3
  32. package/src/atoms/DatePicker/DateRangePicker.tsx +0 -102
  33. package/src/atoms/DatePicker/translations.ts +0 -16
  34. package/src/atoms/Modal.scss +0 -118
  35. package/src/atoms/Modal.tsx +0 -73
  36. package/src/utils/dateHelpers.ts +0 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroptima-design-system",
3
- "version": "0.28.4-beta.2",
3
+ "version": "0.28.4",
4
4
  "scripts": {
5
5
  "dev": "npm run storybook",
6
6
  "storybook": "storybook dev -p 6006 --ci",
@@ -46,7 +46,6 @@
46
46
  .icon {
47
47
  width: config.$icon-size-3x;
48
48
  height: config.$icon-size-3x;
49
-
50
49
  > svg {
51
50
  fill: color_alias.$neutral-color-400;
52
51
  path {
@@ -73,61 +72,21 @@
73
72
  &.info {
74
73
  border: 1px solid color_alias.$info-color-600;
75
74
  background: color_alias.$info-color-50;
76
- .information-container {
77
- .icon {
78
- > svg {
79
- fill: color_alias.$info-color-1000;
80
- path {
81
- fill: color_alias.$info-color-1000;
82
- }
83
- }
84
- }
85
- }
86
75
  }
87
76
 
88
77
  &.success {
89
78
  border: 1px solid color_alias.$success-color-300;
90
79
  background: color_alias.$success-color-50;
91
- .information-container {
92
- .icon {
93
- > svg {
94
- fill: color_alias.$success-color-1000;
95
- path {
96
- fill: color_alias.$success-color-1000;
97
- }
98
- }
99
- }
100
- }
101
80
  }
102
81
 
103
82
  &.warning {
104
83
  border: 1px solid color_alias.$warning-color-300;
105
84
  background: color_alias.$warning-color-50;
106
- .information-container {
107
- .icon {
108
- > svg {
109
- fill: color_alias.$warning-color-1000;
110
- path {
111
- fill: color_alias.$warning-color-1000;
112
- }
113
- }
114
- }
115
- }
116
85
  }
117
86
 
118
87
  &.error {
119
88
  border: 1px solid color_alias.$error-color-1000;
120
89
  background: color_alias.$error-color-50;
121
- .information-container {
122
- .icon {
123
- > svg {
124
- fill: color_alias.$error-color-1000;
125
- path {
126
- fill: color_alias.$error-color-1000;
127
- }
128
- }
129
- }
130
- }
131
90
  }
132
91
 
133
92
  @keyframes fadeOut {
@@ -46,7 +46,7 @@ export function Alert({
46
46
  {...props}
47
47
  >
48
48
  <div className="information-container">
49
- <Icon name={IconVariant[variant]} className={variant} />
49
+ <Icon name={IconVariant[variant]} variant={variant} />
50
50
  <span id={`${id}-text`} className="text">
51
51
  {text}
52
52
  </span>
@@ -1,8 +1,8 @@
1
- @use '../../settings/color_alias';
2
- @use '../../settings/typography/content' as typography;
3
- @use '../../settings/config';
4
- @use '../../settings/depth';
5
- @use '../../settings/breakpoints';
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
6
 
7
7
  // Interpolation applied: https://sass-lang.com/documentation/breaking-changes/css-vars/
8
8
 
@@ -0,0 +1,66 @@
1
+ import 'react-day-picker/style.css'
2
+ import './DatePicker.scss'
3
+ import { useEffect, useState } from 'react'
4
+ import { type DateRange, DayPicker, type Locale } from 'react-day-picker'
5
+ import { enGB, es } from 'react-day-picker/locale'
6
+ import { classNames } from '../utils/classNames'
7
+
8
+ export type Variant = 'primary'
9
+
10
+ type DivPropsWithoutOnSelect = Omit<
11
+ React.ComponentPropsWithoutRef<'div'>,
12
+ 'onSelect'
13
+ >
14
+
15
+ interface AvailableLocale {
16
+ [index: string]: Locale
17
+ }
18
+
19
+ const availableLocales: AvailableLocale = {
20
+ es: es,
21
+ en: enGB,
22
+ }
23
+
24
+ export interface DatePickerProps extends DivPropsWithoutOnSelect {
25
+ variant?: Variant
26
+ onSelect: (dateRange: DateRange | undefined) => void
27
+ footer: string
28
+ selected?: DateRange
29
+ lng: keyof typeof availableLocales
30
+ }
31
+
32
+ export function DatePicker({
33
+ className,
34
+ variant = 'primary',
35
+ onSelect = () => {},
36
+ footer = 'Pick a day',
37
+ selected: preselected,
38
+ lng,
39
+ }: DatePickerProps): React.JSX.Element {
40
+ useEffect(() => {
41
+ setSelected(preselected)
42
+ }, [preselected])
43
+
44
+ const cssClasses = classNames('date-picker', variant, className)
45
+
46
+ const [selected, setSelected] = useState<DateRange | undefined>(preselected)
47
+
48
+ function selectDate(dateRange: DateRange | undefined) {
49
+ setSelected(dateRange)
50
+ onSelect(dateRange)
51
+ }
52
+
53
+ return (
54
+ <div className={cssClasses}>
55
+ <DayPicker
56
+ locale={availableLocales[lng]}
57
+ mode="range"
58
+ min={1}
59
+ selected={selected}
60
+ onSelect={(dateRange) => selectDate(dateRange)}
61
+ footer={footer}
62
+ defaultMonth={selected?.from}
63
+ />
64
+ </div>
65
+ )
66
+ }
@@ -4,26 +4,6 @@
4
4
 
5
5
  $gap: config.$space-4x;
6
6
 
7
- .form {
8
- display: flex;
9
- flex-direction: column;
10
- margin: 0 auto;
11
- gap: $gap;
12
- padding: 0;
13
-
14
- width: breakpoints.$medium;
15
- max-width: breakpoints.$medium;
16
-
17
- &.full-width {
18
- width: 100%;
19
- max-width: 100%;
20
- }
21
-
22
- &:has(.footer-actions) {
23
- padding-bottom: 6.25rem;
24
- }
25
- }
26
-
27
7
  .footer-actions {
28
8
  display: flex;
29
9
  justify-content: flex-end;
@@ -39,13 +19,6 @@ $gap: config.$space-4x;
39
19
  }
40
20
 
41
21
  @media only screen and (max-width: breakpoints.$large) {
42
- .form {
43
- max-width: 100%;
44
- width: 100%;
45
- &:has(.footer-actions) {
46
- padding-bottom: 9rem;
47
- }
48
- }
49
22
  .footer-actions {
50
23
  background-color: color_alias.$neutral-white;
51
24
  border-top: 1px solid color_alias.$neutral-color-200;
@@ -1,11 +1,11 @@
1
- import './Form.scss'
1
+ import './Actions.scss'
2
2
  import { classNames } from '../../utils/classNames'
3
3
 
4
4
  export interface ActionsProps extends React.ComponentPropsWithoutRef<'div'> {
5
5
  children: React.ReactNode
6
6
  }
7
7
 
8
- export default function Actions({ className, children }: ActionsProps) {
8
+ export function Actions({ className, children }: ActionsProps) {
9
9
  return (
10
10
  <div className={classNames('footer-actions', className)}>{children}</div>
11
11
  )
@@ -1,25 +1,14 @@
1
- import './Form.scss'
2
- import { classNames } from '../../utils/classNames'
1
+ import { FormContainer } from './FormContainer'
3
2
 
4
3
  export interface FormProps extends React.ComponentPropsWithoutRef<'form'> {
5
- fullWidth?: boolean
4
+ fluid?: boolean
6
5
  children: React.ReactNode
7
6
  }
8
7
 
9
- export default function Form({
10
- className,
11
- fullWidth,
12
- children,
13
- ...props
14
- }: FormProps) {
8
+ export function Form({ fluid, children, ...props }: FormProps) {
15
9
  return (
16
- <form
17
- className={classNames(className, 'form', {
18
- 'full-width': fullWidth,
19
- })}
20
- {...props}
21
- >
22
- {children}
10
+ <form {...props}>
11
+ <FormContainer fluid={fluid}>{children}</FormContainer>
23
12
  </form>
24
13
  )
25
14
  }
@@ -0,0 +1,35 @@
1
+ @use '../../settings/config';
2
+ @use '../../settings/breakpoints';
3
+ @use '../../settings/color_alias';
4
+
5
+ $gap: config.$space-4x;
6
+
7
+ .form-container {
8
+ display: flex;
9
+ flex-direction: column;
10
+ margin: 0 auto;
11
+ gap: $gap;
12
+ padding: 0;
13
+
14
+ width: breakpoints.$medium;
15
+ max-width: breakpoints.$medium;
16
+
17
+ &.fluid {
18
+ width: 100%;
19
+ max-width: 100%;
20
+ }
21
+
22
+ &:has(.footer-actions) {
23
+ padding-bottom: 6.25rem;
24
+ }
25
+ }
26
+
27
+ @media only screen and (max-width: breakpoints.$large) {
28
+ .form-container {
29
+ max-width: 100%;
30
+ width: 100%;
31
+ &:has(.footer-actions) {
32
+ padding-bottom: 9rem;
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,25 @@
1
+ import './FormContainer.scss'
2
+ import { classNames } from '../../utils/classNames'
3
+
4
+ export interface FormContainerProps
5
+ extends React.ComponentPropsWithoutRef<'div'> {
6
+ fluid?: boolean
7
+ }
8
+
9
+ export function FormContainer({
10
+ fluid,
11
+ className,
12
+ children,
13
+ ...props
14
+ }: FormContainerProps) {
15
+ return (
16
+ <div
17
+ className={classNames(className, 'form-container', {
18
+ fluid,
19
+ })}
20
+ {...props}
21
+ >
22
+ {children}
23
+ </div>
24
+ )
25
+ }
@@ -1,4 +1,5 @@
1
- import Actions from './Actions'
2
- import Form from './Form'
1
+ import { Actions } from './Actions'
2
+ import { Form } from './Form'
3
+ import { FormContainer } from './FormContainer'
3
4
 
4
- export { Actions, Form }
5
+ export { Actions, Form, FormContainer }
@@ -1,5 +1,6 @@
1
1
  @use '../settings/color_alias';
2
2
  @use '../settings/config';
3
+ @use '../settings/mixins';
3
4
 
4
5
  .icon {
5
6
  display: inline-flex;
@@ -12,36 +13,41 @@
12
13
  }
13
14
 
14
15
  &.size-1 {
15
- width: config.$icon-size-1x;
16
- height: config.$icon-size-1x;
16
+ @include mixins.size(config.$icon-size-1x);
17
17
  }
18
18
  &.size-2 {
19
- width: config.$icon-size-2x;
20
- height: config.$icon-size-2x;
19
+ @include mixins.size(config.$icon-size-2x);
21
20
  }
22
21
  &.size-3 {
23
- width: config.$icon-size-3x;
24
- height: config.$icon-size-3x;
22
+ @include mixins.size(config.$icon-size-3x);
25
23
  }
26
24
  &.size-4 {
27
- width: config.$icon-size-4x;
28
- height: config.$icon-size-4x;
25
+ @include mixins.size(config.$icon-size-4x);
29
26
  }
30
27
  &.size-5 {
31
- width: config.$icon-size-5x;
32
- height: config.$icon-size-5x;
28
+ @include mixins.size(config.$icon-size-5x);
33
29
  }
34
30
  &.size-6 {
35
- width: config.$icon-size-6x;
36
- height: config.$icon-size-6x;
31
+ @include mixins.size(config.$icon-size-6x);
37
32
  }
38
33
  &.size-7 {
39
- width: config.$icon-size-7x;
40
- height: config.$icon-size-7x;
34
+ @include mixins.size(config.$icon-size-7x);
41
35
  }
42
36
  &.size-8 {
43
- width: config.$icon-size-8x;
44
- height: config.$icon-size-8x;
37
+ @include mixins.size(config.$icon-size-8x);
38
+ }
39
+
40
+ &.info {
41
+ @include mixins.svg-color(color_alias.$info-color-1000);
42
+ }
43
+ &.success {
44
+ @include mixins.svg-color(color_alias.$success-color-1000);
45
+ }
46
+ &.warning {
47
+ @include mixins.svg-color(color_alias.$warning-color-1000);
48
+ }
49
+ &.error {
50
+ @include mixins.svg-color(color_alias.$error-color-1000);
45
51
  }
46
52
 
47
53
  @keyframes rotate {
@@ -5,6 +5,7 @@ import { classNames } from '../utils/classNames'
5
5
  export type IconType = keyof typeof icons
6
6
 
7
7
  export type IconSize = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'
8
+ export type Variant = 'info' | 'success' | 'warning' | 'error'
8
9
 
9
10
  export interface IconProps extends React.SVGAttributes<HTMLOrSVGElement> {
10
11
  name: IconType
@@ -12,18 +13,20 @@ export interface IconProps extends React.SVGAttributes<HTMLOrSVGElement> {
12
13
  title?: string
13
14
  visible?: boolean
14
15
  size?: IconSize
16
+ variant?: Variant
15
17
  }
16
18
 
17
19
  export const Icon: React.FC<IconProps> = ({
18
20
  name,
19
21
  className,
22
+ variant,
20
23
  size = '5',
21
24
  visible = true,
22
25
  ...props
23
26
  }) => {
24
27
  if (!visible) return null
25
28
 
26
- const cssClasses = classNames('icon', `size-${size}`, className, {
29
+ const cssClasses = classNames('icon', `size-${size}`, variant, className, {
27
30
  rotate: name === 'Loading',
28
31
  })
29
32
  return (
@@ -0,0 +1,139 @@
1
+ @use '../../settings/mixins';
2
+ @use '../../settings/typography/content' as typography;
3
+ @use '../../settings/color_alias';
4
+ @use '../../settings/breakpoints';
5
+ @use '../../settings/depth';
6
+ @use '../../settings/config';
7
+
8
+ $modal-margin: 3rem;
9
+ $modal-width: 34.5rem;
10
+ $modal-detail-width: 50rem;
11
+ $modal-background-color: color_alias.$neutral-white;
12
+ $modal-boder-color: color_alias.$neutral-color-200;
13
+ $backdrop-opacity: 0.4;
14
+ $backdrop-background-color: color_alias.$neutral-color-900;
15
+
16
+ .modal {
17
+ position: fixed;
18
+ top: 0;
19
+ left: 0;
20
+ z-index: depth.$z-modal;
21
+ width: 100%;
22
+ height: 100%;
23
+ overflow-x: hidden;
24
+ overflow-y: auto;
25
+ outline: 0;
26
+ }
27
+
28
+ .modal-dialog {
29
+ transition: transform 0.3s ease-out;
30
+ max-width: $modal-width;
31
+ position: relative;
32
+ width: auto;
33
+ margin-block: $modal-margin;
34
+ margin-inline: auto;
35
+ padding-inline: config.$space-4x;
36
+ pointer-events: none;
37
+ }
38
+
39
+ .modal-content {
40
+ position: relative;
41
+ display: flex;
42
+ flex-direction: column;
43
+ width: 100%;
44
+ pointer-events: auto;
45
+ background-color: $modal-background-color;
46
+ background-clip: padding-box;
47
+ outline: 0;
48
+ border-radius: config.$corner-radius-xxs;
49
+ padding: config.$space-4x;
50
+ box-shadow:
51
+ 0px 3px 6px -4px rgba(0, 0, 0, 0.12),
52
+ 0px 6px 16px 0px rgba(0, 0, 0, 0.08),
53
+ 0px 9px 28px 8px rgba(0, 0, 0, 0.05);
54
+ }
55
+
56
+ .modal-header {
57
+ gap: config.$space-2x;
58
+ padding-bottom: config.$space-1x;
59
+ display: flex;
60
+ flex-shrink: 0;
61
+ align-items: center;
62
+ button {
63
+ margin-inline-start: auto;
64
+ }
65
+ }
66
+
67
+ .modal-title {
68
+ @include typography.h4;
69
+ }
70
+
71
+ .modal-body {
72
+ padding-top: config.$space-1x;
73
+ position: relative;
74
+ flex: 1 1 auto;
75
+ }
76
+
77
+ .modal-footer {
78
+ padding-top: config.$space-2x;
79
+ display: flex;
80
+ flex-shrink: 0;
81
+ flex-wrap: wrap;
82
+ align-items: center;
83
+ justify-content: flex-end;
84
+ gap: config.$space-2x;
85
+ }
86
+
87
+ .modal-backdrop {
88
+ opacity: $backdrop-opacity;
89
+ position: fixed;
90
+ top: 0;
91
+ left: 0;
92
+ z-index: depth.$z-modal;
93
+ width: 100vw;
94
+ height: 100vh;
95
+ background-color: $backdrop-background-color;
96
+ transition: opacity 0.15s linear;
97
+ }
98
+
99
+ .modal-details {
100
+ .modal-dialog {
101
+ max-width: 50rem;
102
+ }
103
+
104
+ .modal-header {
105
+ border-bottom: 1px solid $modal-boder-color;
106
+ padding-bottom: config.$space-3x;
107
+ }
108
+
109
+ .modal-body {
110
+ padding-top: config.$space-3x;
111
+ }
112
+
113
+ .modal-footer {
114
+ padding-top: config.$space-3x;
115
+ }
116
+ }
117
+
118
+ .modal-dialog-scrollable {
119
+ height: calc(100vh - $modal-margin * 2);
120
+ .modal-content {
121
+ max-height: 100%;
122
+ overflow: hidden;
123
+ }
124
+ .modal-body {
125
+ overflow-y: auto;
126
+ margin-inline: config.$space-4x * -1;
127
+ padding-inline: config.$space-4x;
128
+ }
129
+ }
130
+
131
+ .modal-icon {
132
+ margin-top: config.$space-1x;
133
+ align-self: flex-start;
134
+ }
135
+
136
+ body:has(.modal-backdrop) {
137
+ overflow: hidden;
138
+ padding-right: 0px;
139
+ }
@@ -0,0 +1,74 @@
1
+ import './Modal.scss'
2
+ import type { ReactNode } from 'react'
3
+ import type { ButtonProps } from '../Button'
4
+ import { Button } from '../Button'
5
+ import { Icon } from '../Icon'
6
+ import {
7
+ ModalBody,
8
+ ModalCloseButton,
9
+ ModalDialog,
10
+ ModalFooter,
11
+ ModalHeader,
12
+ ModalTitle,
13
+ } from '.'
14
+
15
+ export type Variant =
16
+ | 'info'
17
+ | 'success'
18
+ | 'warning'
19
+ | 'error'
20
+ | 'discard'
21
+ | 'details'
22
+
23
+ export interface ModalProps {
24
+ id: string
25
+ title: string
26
+ variant?: Variant
27
+ closeButton?: boolean
28
+ scrollable?: boolean
29
+ className?: string
30
+ onClose?: () => void
31
+ buttons: ButtonProps[]
32
+ children: ReactNode
33
+ }
34
+
35
+ const ICONS: { [key: string]: ReactNode } = {
36
+ info: <Icon className="modal-icon" name="Info" variant="info" />,
37
+ success: <Icon className="modal-icon" name="Check" variant="success" />,
38
+ warning: <Icon className="modal-icon" name="Warning" variant="warning" />,
39
+ error: <Icon className="modal-icon" name="Error" variant="error" />,
40
+ discard: <Icon className="modal-icon" name="Warning" variant="warning" />,
41
+ }
42
+
43
+ export function Modal({
44
+ id,
45
+ title,
46
+ buttons,
47
+ onClose,
48
+ children,
49
+ closeButton = false,
50
+ variant = 'details',
51
+ ...props
52
+ }: ModalProps) {
53
+ return (
54
+ <ModalDialog
55
+ aria-labelledby={`${id}-title`}
56
+ aria-describedby={`${id}-body`}
57
+ onClose={onClose}
58
+ details={variant === 'details'}
59
+ {...props}
60
+ >
61
+ <ModalHeader>
62
+ {ICONS[variant]}
63
+ <ModalTitle id={`${id}-title`}>{title}</ModalTitle>
64
+ {closeButton && <ModalCloseButton onClick={onClose} />}
65
+ </ModalHeader>
66
+ <ModalBody id={`${id}-body`}>{children}</ModalBody>
67
+ <ModalFooter>
68
+ {buttons.map(({ ...button }) => (
69
+ <Button key={button.label} {...button} />
70
+ ))}
71
+ </ModalFooter>
72
+ </ModalDialog>
73
+ )
74
+ }
@@ -0,0 +1,12 @@
1
+ import './Modal.scss'
2
+ import { classNames } from '../../utils/classNames'
3
+
4
+ export interface ModalBodyProps extends React.HTMLAttributes<HTMLDivElement> {}
5
+
6
+ export function ModalBody({ className, children, ...props }: ModalBodyProps) {
7
+ return (
8
+ <div className={classNames('modal-body', className)} {...props}>
9
+ {children}
10
+ </div>
11
+ )
12
+ }
@@ -0,0 +1,23 @@
1
+ import './Modal.scss'
2
+ import { IconButton } from '../Button'
3
+
4
+ export interface ModalCloseButtonProps {
5
+ label?: string
6
+ onClick?: () => void
7
+ }
8
+
9
+ export function ModalCloseButton({
10
+ label = 'Close',
11
+ onClick,
12
+ }: ModalCloseButtonProps) {
13
+ return (
14
+ <IconButton
15
+ type="button"
16
+ variant="tertiary"
17
+ accessibilityLabel={label}
18
+ icon="Close"
19
+ size="4"
20
+ onClick={onClick}
21
+ />
22
+ )
23
+ }