agroptima-design-system 0.29.2 → 0.29.3-beta.1

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.29.2",
3
+ "version": "0.29.3-beta.1",
4
4
  "scripts": {
5
5
  "dev": "npm run storybook",
6
6
  "storybook": "storybook dev -p 6006 --ci",
@@ -45,20 +45,21 @@ export function Button({
45
45
  disabled,
46
46
  variant = 'primary',
47
47
  loading = false,
48
- className,
49
48
  ...props
50
49
  }: ButtonProps) {
51
- const leftIconName = loading ? 'Loading' : leftIcon
52
- const cssClasses = classNames(className, 'button', variant)
50
+ if (loading) {
51
+ leftIcon = 'Loading'
52
+ }
53
+ const cssClasses = classNames(props.className, 'button', variant)
53
54
 
54
55
  return (
55
56
  <BaseButton
56
57
  disabled={loading || disabled}
57
58
  aria-label={accessibilityLabel || label}
58
- className={cssClasses}
59
59
  {...props}
60
+ className={cssClasses}
60
61
  >
61
- {leftIconName && <Icon name={leftIconName} size="3" />}
62
+ {leftIcon && <Icon name={leftIcon} size="3" />}
62
63
  {label}
63
64
  {rightIcon && <Icon name={rightIcon} size="3" />}
64
65
  </BaseButton>
@@ -15,7 +15,6 @@ interface CustomProps {
15
15
  disabled?: boolean
16
16
  accessibilityLabel: string
17
17
  shape?: Shape
18
- loading?: boolean
19
18
  }
20
19
 
21
20
  export type FloatingButtonProps = CustomProps & BaseButtonProps
@@ -26,21 +25,23 @@ export function FloatingButton({
26
25
  disabled,
27
26
  variant = 'primary',
28
27
  shape = 'rounded-square',
29
- loading = false,
30
- className,
31
28
  ...props
32
29
  }: FloatingButtonProps) {
33
- const cssClasses = classNames('floating-button', variant, className, shape)
34
- const iconName = loading ? 'Loading' : icon
30
+ const cssClasses = classNames(
31
+ 'floating-button',
32
+ variant,
33
+ props.className,
34
+ shape,
35
+ )
35
36
 
36
37
  return (
37
38
  <BaseButton
38
- disabled={loading || disabled}
39
+ disabled={disabled}
39
40
  aria-label={accessibilityLabel}
40
- className={cssClasses}
41
41
  {...props}
42
+ className={cssClasses}
42
43
  >
43
- <Icon name={iconName} />
44
+ <Icon name={icon} />
44
45
  </BaseButton>
45
46
  )
46
47
  }
@@ -12,7 +12,6 @@ interface CustomProps {
12
12
  variant?: Variant
13
13
  disabled?: boolean
14
14
  accessibilityLabel: string
15
- loading?: boolean
16
15
  size?: IconSize
17
16
  }
18
17
 
@@ -23,20 +22,17 @@ export function IconButton({
23
22
  icon,
24
23
  disabled,
25
24
  variant = 'primary',
26
- loading = false,
27
25
  size,
28
- className,
29
26
  ...props
30
27
  }: IconButtonProps) {
31
- const iconName = loading ? 'Loading' : icon
32
28
  return (
33
29
  <BaseButton
34
- disabled={loading || disabled}
30
+ disabled={disabled}
35
31
  aria-label={accessibilityLabel}
36
- className={classNames(className, 'icon-button', variant)}
37
32
  {...props}
33
+ className={classNames(props.className, 'icon-button', variant)}
38
34
  >
39
- <Icon title={accessibilityLabel} name={iconName} size={size} />
35
+ <Icon title={accessibilityLabel} name={icon} size={size} />
40
36
  </BaseButton>
41
37
  )
42
38
  }
@@ -0,0 +1,59 @@
1
+ import 'react-day-picker/style.css'
2
+ import './DatePicker.scss'
3
+ import { type DateRange, type Locale } from 'react-day-picker'
4
+ import { enGB, es } from 'react-day-picker/locale'
5
+ import { classNames } from '../../utils/classNames'
6
+ import { DateRangePicker } from './DateRangePicker'
7
+ import { DateSinglePicker } from './DateSinglePicker'
8
+
9
+ export type Variant = 'primary'
10
+ export type DatePickerType = 'single' | 'range'
11
+
12
+ type DivPropsWithoutOnSelect = Omit<
13
+ React.ComponentPropsWithoutRef<'div'>,
14
+ 'onSelect'
15
+ >
16
+
17
+ export interface AvailableLocale {
18
+ [index: string]: Locale
19
+ }
20
+
21
+ export interface DatePickerBaseProps extends DivPropsWithoutOnSelect {
22
+ variant?: Variant
23
+ lng: keyof AvailableLocale
24
+ type: DatePickerType
25
+ }
26
+
27
+ export interface DateSinglePickerProps {
28
+ type: 'single'
29
+ selected?: Date | undefined
30
+ onSelect: (date: Date | undefined) => void
31
+ }
32
+
33
+ export interface DateRangePickerProps {
34
+ type: 'range'
35
+ selected?: DateRange | undefined
36
+ onSelect: (date: DateRange | undefined) => void
37
+ }
38
+
39
+ type DatePickerProps = (DateSinglePickerProps | DateRangePickerProps) &
40
+ DatePickerBaseProps
41
+
42
+ export function DatePicker(props: DatePickerProps): React.JSX.Element {
43
+ const { variant, className, type, lng, onSelect, selected } = props
44
+
45
+ const cssClasses = classNames('date-picker', variant, className)
46
+
47
+ if (type === 'single') {
48
+ return (
49
+ <div className={cssClasses}>
50
+ <DateSinglePicker lng={lng} selected={selected} onSelect={onSelect} />
51
+ </div>
52
+ )
53
+ }
54
+ return (
55
+ <div className={cssClasses}>
56
+ <DateRangePicker lng={lng} selected={selected} onSelect={onSelect} />
57
+ </div>
58
+ )
59
+ }
@@ -1,60 +1,32 @@
1
1
  import 'react-day-picker/style.css'
2
- import './DateRangePicker.scss'
3
2
  import { useEffect, useState } from 'react'
4
3
  import { type DateRange, DayPicker, type Locale } from 'react-day-picker'
5
4
  import { enGB, es } from 'react-day-picker/locale'
6
- import { classNames } from '../../utils/classNames'
7
- import {
8
- formatDatePickerFooterDate,
9
- formatDatePickerParamsDate,
10
- } from '../../utils/dateHelpers'
5
+ import { formatDatePickerFooterDate } from '../../utils/dateHelpers'
6
+ import type { AvailableLocale } from './DatePicker'
11
7
  import { translations } from './translations'
12
8
 
13
- export type Variant = 'primary'
14
-
15
- type DivPropsWithoutOnSelect = Omit<
16
- React.ComponentPropsWithoutRef<'div'>,
17
- 'onSelect'
18
- >
19
-
20
- interface AvailableLocale {
21
- [index: string]: Locale
22
- }
23
-
24
9
  const availableLocales: AvailableLocale = {
25
10
  es: es,
26
11
  en: enGB,
27
12
  }
28
13
 
29
- export interface DateRangePickerProps extends DivPropsWithoutOnSelect {
30
- variant?: Variant
14
+ export interface DateRangePickerProps {
31
15
  onSelect: (dateRange: DateRange | undefined) => void
32
16
  selected?: DateRange
33
17
  lng: keyof typeof availableLocales
34
18
  }
35
19
 
36
20
  export function DateRangePicker({
37
- className,
38
- variant = 'primary',
39
21
  onSelect = () => {},
40
22
  selected: preselected,
41
23
  lng,
42
24
  }: DateRangePickerProps): React.JSX.Element {
43
25
  const manageFooterText = (): string => {
44
26
  const hasDatesFilter = selected && selected.from && selected.to
45
- const isExactDate =
46
- formatDatePickerParamsDate(selected?.from) ===
47
- formatDatePickerParamsDate(selected?.to)
48
27
 
49
28
  if (!hasDatesFilter) return translations[lng].pickDate
50
29
 
51
- if (isExactDate) {
52
- return translations[lng].selectedDate.replace(
53
- '${date}',
54
- formatDatePickerFooterDate(selected.from, lng as string),
55
- )
56
- }
57
-
58
30
  return translations[lng].selectedRangeOfDates
59
31
  .replace(
60
32
  '${from}',
@@ -67,7 +39,6 @@ export function DateRangePicker({
67
39
  const [footer, setFooter] = useState<string>(() => {
68
40
  return manageFooterText()
69
41
  })
70
- const cssClasses = classNames('date-picker', variant, className)
71
42
 
72
43
  useEffect(() => {
73
44
  setSelected(preselected)
@@ -76,18 +47,12 @@ export function DateRangePicker({
76
47
  }, [preselected])
77
48
 
78
49
  function selectDate(dateRange: DateRange | undefined) {
79
- const isSingleDaySelection = dateRange && !dateRange.to
80
-
81
- if (isSingleDaySelection) {
82
- dateRange.to = dateRange.from
83
- }
84
-
85
50
  setSelected(dateRange)
86
51
  onSelect(dateRange)
87
52
  }
88
53
 
89
54
  return (
90
- <div className={cssClasses}>
55
+ <>
91
56
  <DayPicker
92
57
  locale={availableLocales[lng]}
93
58
  mode="range"
@@ -97,6 +62,6 @@ export function DateRangePicker({
97
62
  footer={footer}
98
63
  defaultMonth={selected?.from}
99
64
  />
100
- </div>
65
+ </>
101
66
  )
102
67
  }
@@ -0,0 +1,63 @@
1
+ import 'react-day-picker/style.css'
2
+ import { useEffect, useState } from 'react'
3
+ import { DayPicker, type Locale } from 'react-day-picker'
4
+ import { enGB, es } from 'react-day-picker/locale'
5
+ import { formatDatePickerFooterDate } from '../../utils/dateHelpers'
6
+ import type { AvailableLocale } from './DatePicker'
7
+ import { translations } from './translations'
8
+
9
+ const availableLocales: AvailableLocale = {
10
+ es: es,
11
+ en: enGB,
12
+ }
13
+
14
+ export interface DateSinglePickerProps {
15
+ onSelect: (date: Date | undefined) => void
16
+ selected?: Date
17
+ lng: keyof typeof availableLocales
18
+ }
19
+
20
+ export function DateSinglePicker({
21
+ onSelect = () => {},
22
+ selected: preselected,
23
+ lng,
24
+ }: DateSinglePickerProps): React.JSX.Element {
25
+ const manageFooterText = (): string => {
26
+ if (!selected) return translations[lng].pickSingleDate
27
+
28
+ return translations[lng].selectedDate.replace(
29
+ '${date}',
30
+ formatDatePickerFooterDate(selected, lng as string),
31
+ )
32
+ }
33
+
34
+ const [selected, setSelected] = useState<Date | undefined>(preselected)
35
+ const [footer, setFooter] = useState<string>(() => {
36
+ return manageFooterText()
37
+ })
38
+
39
+ useEffect(() => {
40
+ setSelected(preselected)
41
+ setFooter(manageFooterText())
42
+ // eslint-disable-next-line react-hooks/exhaustive-deps
43
+ }, [preselected])
44
+
45
+ function selectDate(date: Date | undefined) {
46
+ setSelected(date)
47
+ onSelect(date)
48
+ }
49
+
50
+ return (
51
+ <>
52
+ <DayPicker
53
+ locale={availableLocales[lng]}
54
+ mode="single"
55
+ selected={selected}
56
+ onSelect={(date) => selectDate(date)}
57
+ footer={footer}
58
+ required
59
+ defaultMonth={selected}
60
+ />
61
+ </>
62
+ )
63
+ }
@@ -5,11 +5,13 @@ interface Translation {
5
5
  export const translations: Translation = {
6
6
  en: {
7
7
  pickDate: 'Pick a day or a range of dates',
8
+ pickSingleDate: 'Pick a date',
8
9
  selectedDate: 'Selected date: ${date}',
9
10
  selectedRangeOfDates: 'Selected dates range: from ${from} to ${to}',
10
11
  },
11
12
  es: {
12
13
  pickDate: 'Selecciona un día o un rango de fechas',
14
+ pickSingleDate: 'Selecciona una fecha',
13
15
  selectedDate: 'Fecha seleccionada: ${date}',
14
16
  selectedRangeOfDates: 'Rango de fechas seleccionado: desde ${from} a ${to}',
15
17
  },
@@ -1,3 +1,4 @@
1
+ 'use client'
1
2
  import './Select.scss'
2
3
  import React, { useRef, useState } from 'react'
3
4
  import { useOpen } from '../hooks/useOpen'
@@ -160,6 +161,7 @@ function OptionList({
160
161
  searchLabel,
161
162
  }: OptionListProps) {
162
163
  const { findItems, search } = useSearch(options, 'label')
164
+
163
165
  return (
164
166
  <div className="select-options">
165
167
  {isSearchable && (
@@ -24,7 +24,6 @@ const meta = {
24
24
  },
25
25
  loading: {
26
26
  description: 'Is the button in loading state?',
27
- control: { type: 'boolean', default: false },
28
27
  },
29
28
  leftIcon: {
30
29
  description: 'Button left icon from a list of values',
@@ -40,7 +39,7 @@ const meta = {
40
39
  },
41
40
  visible: {
42
41
  description: 'Is the button visible?',
43
- control: { type: 'boolean' },
42
+ control: { type: 'boolean', default: true },
44
43
  },
45
44
  },
46
45
  }
@@ -4,10 +4,6 @@ import { Meta } from "@storybook/blocks";
4
4
 
5
5
  # Changelog
6
6
 
7
- ## 0.29.2
8
-
9
- * Add loading property to IconButton and FloatingButton components.
10
-
11
7
  ## 0.29.1
12
8
 
13
9
  * Update modal component to respect the scroll width.
@@ -1,14 +1,11 @@
1
1
  import type { StoryObj } from '@storybook/react'
2
- import { DateRangePicker } from '../atoms/DatePicker/DateRangePicker'
2
+ import { DatePicker } from '../atoms/DatePicker/DatePicker'
3
3
 
4
4
  const meta = {
5
- title: 'Design System/Atoms/DateRangePicker',
6
- component: DateRangePicker,
5
+ title: 'Design System/Atoms/DatePicker',
6
+ component: DatePicker,
7
7
  tags: ['autodocs'],
8
8
  argTypes: {
9
- footer: {
10
- description: 'Text for the footer',
11
- },
12
9
  variant: {
13
10
  description: 'Component variant used',
14
11
  },
@@ -21,6 +18,9 @@ const meta = {
21
18
  lng: {
22
19
  description: 'String with the locale to be used on the translations',
23
20
  },
21
+ type: {
22
+ description: 'Type of date that could be range or single',
23
+ },
24
24
  },
25
25
  }
26
26
 
@@ -34,21 +34,21 @@ const figmaPrimaryDesign = {
34
34
  export default meta
35
35
  type Story = StoryObj<typeof meta>
36
36
 
37
- export const Primary: Story = {
37
+ export const SingleDatePicker: Story = {
38
38
  args: {
39
39
  variant: 'primary',
40
40
  onSelect: (date) => console.log('onSelect date:', date),
41
41
  lng: 'en',
42
+ type: 'single',
42
43
  },
43
- parameters: figmaPrimaryDesign,
44
44
  }
45
45
 
46
- export const WithDateRangeSelected: Story = {
46
+ export const RangeDatePicker: Story = {
47
47
  args: {
48
48
  variant: 'primary',
49
49
  onSelect: (date) => console.log('onSelect date:', date),
50
- selected: { from: new Date(2024, 1, 2), to: new Date(2024, 1, 15) },
51
50
  lng: 'en',
51
+ type: 'range',
52
52
  },
53
53
  parameters: figmaPrimaryDesign,
54
54
  }
@@ -57,8 +57,20 @@ export const WithSingleDaySelected: Story = {
57
57
  args: {
58
58
  variant: 'primary',
59
59
  onSelect: (date) => console.log('onSelect date:', date),
60
- selected: { from: new Date(2024, 1, 2), to: new Date(2024, 1, 2) },
60
+ selected: new Date(2024, 1, 2),
61
+ lng: 'en',
62
+ type: 'single',
63
+ },
64
+ parameters: figmaPrimaryDesign,
65
+ }
66
+
67
+ export const WithRangeDateSelected: Story = {
68
+ args: {
69
+ variant: 'primary',
70
+ onSelect: (date) => console.log('onSelect date:', date),
71
+ selected: { from: new Date(2024, 1, 2), to: new Date(2024, 1, 12) },
61
72
  lng: 'en',
73
+ type: 'range',
62
74
  },
63
75
  parameters: figmaPrimaryDesign,
64
76
  }
@@ -23,10 +23,6 @@ const meta = {
23
23
  description:
24
24
  'If a link is provided, the component will be rendered as NextLink, otherwise as button',
25
25
  },
26
- loading: {
27
- description: 'Is the button in loading state?',
28
- control: { type: 'boolean' },
29
- },
30
26
  },
31
27
  }
32
28
 
@@ -27,10 +27,6 @@ const meta = {
27
27
  description:
28
28
  'If a link is provided, the component will be rendered as NextLink, otherwise as button',
29
29
  },
30
- loading: {
31
- description: 'Is the button in loading state?',
32
- control: { type: 'boolean' },
33
- },
34
30
  },
35
31
  }
36
32
 
@@ -0,0 +1,48 @@
1
+ import { render } from '@testing-library/react'
2
+ import React from 'react'
3
+ import { DatePicker } from '../src/atoms/DatePicker/DatePicker'
4
+
5
+ describe('DatePicker', () => {
6
+ it('renders with expected footer, language and month for a range type', () => {
7
+ const { getByText } = render(
8
+ <DatePicker
9
+ selected={{ from: new Date(2025, 0, 1), to: new Date(2025, 0, 15) }}
10
+ onSelect={() => jest.fn()}
11
+ lng={'es'}
12
+ type="range"
13
+ />,
14
+ )
15
+
16
+ expect(getByText('enero 2025')).toBeInTheDocument()
17
+ expect(getByText('lu')).toBeInTheDocument()
18
+ expect(getByText('ma')).toBeInTheDocument()
19
+ expect(getByText('mi')).toBeInTheDocument()
20
+ expect(getByText('ju')).toBeInTheDocument()
21
+ expect(getByText('vi')).toBeInTheDocument()
22
+ expect(getByText('sá')).toBeInTheDocument()
23
+ expect(getByText('do')).toBeInTheDocument()
24
+ expect(
25
+ getByText('Rango de fechas seleccionado: desde 1/1/2025 a 15/1/2025'),
26
+ ).toBeInTheDocument()
27
+ })
28
+ it('renders with expected footer, language and month for a single type', () => {
29
+ const { getByText } = render(
30
+ <DatePicker
31
+ selected={new Date(2025, 1, 15)}
32
+ onSelect={() => jest.fn()}
33
+ lng={'es'}
34
+ type="single"
35
+ />,
36
+ )
37
+
38
+ expect(getByText('febrero 2025')).toBeInTheDocument()
39
+ expect(getByText('lu')).toBeInTheDocument()
40
+ expect(getByText('ma')).toBeInTheDocument()
41
+ expect(getByText('mi')).toBeInTheDocument()
42
+ expect(getByText('ju')).toBeInTheDocument()
43
+ expect(getByText('vi')).toBeInTheDocument()
44
+ expect(getByText('sá')).toBeInTheDocument()
45
+ expect(getByText('do')).toBeInTheDocument()
46
+ expect(getByText('Fecha seleccionada: 15/2/2025')).toBeInTheDocument()
47
+ })
48
+ })
@@ -1,26 +0,0 @@
1
- import { getAllByRole, render } from '@testing-library/react'
2
- import React from 'react'
3
- import { DateRangePicker } from '../src/atoms/DatePicker/DateRangePicker'
4
-
5
- describe('DateRangePicker', () => {
6
- it('renders with expected footer, language and month', () => {
7
- const { getByText } = render(
8
- <DateRangePicker
9
- selected={{ from: new Date(2025, 0, 1), to: new Date(2025, 0, 15) }}
10
- onSelect={() => jest.fn()}
11
- lng={'es'}
12
- />,
13
- )
14
- expect(getByText('enero 2025')).toBeInTheDocument()
15
- expect(getByText('lu')).toBeInTheDocument()
16
- expect(getByText('ma')).toBeInTheDocument()
17
- expect(getByText('mi')).toBeInTheDocument()
18
- expect(getByText('ju')).toBeInTheDocument()
19
- expect(getByText('vi')).toBeInTheDocument()
20
- expect(getByText('sá')).toBeInTheDocument()
21
- expect(getByText('do')).toBeInTheDocument()
22
- expect(
23
- getByText('Rango de fechas seleccionado: desde 1/1/2025 a 15/1/2025'),
24
- ).toBeInTheDocument()
25
- })
26
- })