agroptima-design-system 0.30.3 → 0.31.0
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 +1 -1
- package/src/atoms/DatePicker/DatePicker.tsx +58 -0
- package/src/atoms/DatePicker/DateRangePicker.tsx +5 -40
- package/src/atoms/DatePicker/DateSinglePicker.tsx +67 -0
- package/src/atoms/DatePicker/translations.ts +2 -0
- package/src/stories/Changelog.mdx +7 -0
- package/src/stories/DatePicker.stories.ts +23 -11
- package/tests/DatePicker.spec.tsx +48 -0
- package/tests/DateRangePicker.spec.tsx +0 -26
- /package/src/atoms/DatePicker/{DateRangePicker.scss → DatePicker.scss} +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import 'react-day-picker/style.css'
|
|
2
|
+
import './DatePicker.scss'
|
|
3
|
+
import { type DateRange, type Locale } from 'react-day-picker'
|
|
4
|
+
import { classNames } from '../../utils/classNames'
|
|
5
|
+
import { DateRangePicker } from './DateRangePicker'
|
|
6
|
+
import { DateSinglePicker } from './DateSinglePicker'
|
|
7
|
+
|
|
8
|
+
export type Variant = 'primary'
|
|
9
|
+
export type DatePickerType = 'single' | 'range'
|
|
10
|
+
|
|
11
|
+
type DivPropsWithoutOnSelect = Omit<
|
|
12
|
+
React.ComponentPropsWithoutRef<'div'>,
|
|
13
|
+
'onSelect'
|
|
14
|
+
>
|
|
15
|
+
|
|
16
|
+
export interface AvailableLocale {
|
|
17
|
+
[index: string]: Locale
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DatePickerBaseProps extends DivPropsWithoutOnSelect {
|
|
21
|
+
variant?: Variant
|
|
22
|
+
lng: keyof AvailableLocale
|
|
23
|
+
type: DatePickerType
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface DateSinglePickerProps {
|
|
27
|
+
type: 'single'
|
|
28
|
+
selected?: Date | undefined
|
|
29
|
+
onSelect: (date: Date | undefined) => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DateRangePickerProps {
|
|
33
|
+
type: 'range'
|
|
34
|
+
selected?: DateRange | undefined
|
|
35
|
+
onSelect: (date: DateRange | undefined) => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type DatePickerProps = (DateSinglePickerProps | DateRangePickerProps) &
|
|
39
|
+
DatePickerBaseProps
|
|
40
|
+
|
|
41
|
+
export function DatePicker(props: DatePickerProps): React.JSX.Element {
|
|
42
|
+
const { variant, className, type, lng, onSelect, selected } = props
|
|
43
|
+
|
|
44
|
+
const cssClasses = classNames('date-picker', variant, className)
|
|
45
|
+
|
|
46
|
+
if (type === 'single') {
|
|
47
|
+
return (
|
|
48
|
+
<div className={cssClasses}>
|
|
49
|
+
<DateSinglePicker lng={lng} selected={selected} onSelect={onSelect} />
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
return (
|
|
54
|
+
<div className={cssClasses}>
|
|
55
|
+
<DateRangePicker lng={lng} selected={selected} onSelect={onSelect} />
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
65
|
+
</>
|
|
101
66
|
)
|
|
102
67
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import 'react-day-picker/style.css'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
3
|
+
import { DayPicker } 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 [month, setMonth] = useState<Date | undefined>(preselected)
|
|
36
|
+
const [footer, setFooter] = useState<string>(() => {
|
|
37
|
+
return manageFooterText()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
setSelected(preselected)
|
|
42
|
+
setMonth(preselected)
|
|
43
|
+
setFooter(manageFooterText())
|
|
44
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
45
|
+
}, [preselected])
|
|
46
|
+
|
|
47
|
+
function selectDate(date: Date | undefined) {
|
|
48
|
+
setSelected(date)
|
|
49
|
+
if (date) setMonth(new Date(date.getFullYear(), date.getMonth()))
|
|
50
|
+
onSelect(date)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
<DayPicker
|
|
56
|
+
locale={availableLocales[lng]}
|
|
57
|
+
mode="single"
|
|
58
|
+
selected={selected}
|
|
59
|
+
onSelect={(date) => selectDate(date)}
|
|
60
|
+
footer={footer}
|
|
61
|
+
required
|
|
62
|
+
month={month}
|
|
63
|
+
onMonthChange={(date) => setMonth(date)}
|
|
64
|
+
/>
|
|
65
|
+
</>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
@@ -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
|
},
|
|
@@ -4,6 +4,13 @@ import { Meta } from "@storybook/blocks";
|
|
|
4
4
|
|
|
5
5
|
# Changelog
|
|
6
6
|
|
|
7
|
+
## 0.31.0
|
|
8
|
+
|
|
9
|
+
* DatePicker option for a single day created.
|
|
10
|
+
* Updated the footer message for a single day date picker, and add month selection.
|
|
11
|
+
* DatePicker component now is a wrapper for the single and range options to select.
|
|
12
|
+
* DateRangePicker component refactored to DatePicker.
|
|
13
|
+
|
|
7
14
|
## 0.30.3
|
|
8
15
|
|
|
9
16
|
* Autofocus on search input in the Select and MultiSelect components.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { StoryObj } from '@storybook/react'
|
|
2
|
-
import {
|
|
2
|
+
import { DatePicker } from '../atoms/DatePicker/DatePicker'
|
|
3
3
|
|
|
4
4
|
const meta = {
|
|
5
|
-
title: 'Design System/Atoms/
|
|
6
|
-
component:
|
|
5
|
+
title: 'Design System/Atoms/DatePicker',
|
|
6
|
+
component: DatePicker,
|
|
7
7
|
parameters: {
|
|
8
8
|
docs: {
|
|
9
9
|
description: {
|
|
@@ -14,9 +14,6 @@ const meta = {
|
|
|
14
14
|
},
|
|
15
15
|
tags: ['autodocs'],
|
|
16
16
|
argTypes: {
|
|
17
|
-
footer: {
|
|
18
|
-
description: 'Text for the footer',
|
|
19
|
-
},
|
|
20
17
|
variant: {
|
|
21
18
|
description: 'Component variant used',
|
|
22
19
|
},
|
|
@@ -29,6 +26,9 @@ const meta = {
|
|
|
29
26
|
lng: {
|
|
30
27
|
description: 'String with the locale to be used on the translations',
|
|
31
28
|
},
|
|
29
|
+
type: {
|
|
30
|
+
description: 'Type of date that could be range or single',
|
|
31
|
+
},
|
|
32
32
|
},
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -42,21 +42,21 @@ const figmaPrimaryDesign = {
|
|
|
42
42
|
export default meta
|
|
43
43
|
type Story = StoryObj<typeof meta>
|
|
44
44
|
|
|
45
|
-
export const
|
|
45
|
+
export const SingleDatePicker: Story = {
|
|
46
46
|
args: {
|
|
47
47
|
variant: 'primary',
|
|
48
48
|
onSelect: (date) => console.log('onSelect date:', date),
|
|
49
49
|
lng: 'en',
|
|
50
|
+
type: 'single',
|
|
50
51
|
},
|
|
51
|
-
parameters: figmaPrimaryDesign,
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
export const
|
|
54
|
+
export const RangeDatePicker: Story = {
|
|
55
55
|
args: {
|
|
56
56
|
variant: 'primary',
|
|
57
57
|
onSelect: (date) => console.log('onSelect date:', date),
|
|
58
|
-
selected: { from: new Date(2024, 1, 2), to: new Date(2024, 1, 15) },
|
|
59
58
|
lng: 'en',
|
|
59
|
+
type: 'range',
|
|
60
60
|
},
|
|
61
61
|
parameters: figmaPrimaryDesign,
|
|
62
62
|
}
|
|
@@ -65,8 +65,20 @@ export const WithSingleDaySelected: Story = {
|
|
|
65
65
|
args: {
|
|
66
66
|
variant: 'primary',
|
|
67
67
|
onSelect: (date) => console.log('onSelect date:', date),
|
|
68
|
-
selected:
|
|
68
|
+
selected: new Date(2024, 1, 2),
|
|
69
|
+
lng: 'en',
|
|
70
|
+
type: 'single',
|
|
71
|
+
},
|
|
72
|
+
parameters: figmaPrimaryDesign,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const WithRangeDateSelected: Story = {
|
|
76
|
+
args: {
|
|
77
|
+
variant: 'primary',
|
|
78
|
+
onSelect: (date) => console.log('onSelect date:', date),
|
|
79
|
+
selected: { from: new Date(2024, 1, 2), to: new Date(2024, 1, 12) },
|
|
69
80
|
lng: 'en',
|
|
81
|
+
type: 'range',
|
|
70
82
|
},
|
|
71
83
|
parameters: figmaPrimaryDesign,
|
|
72
84
|
}
|
|
@@ -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
|
-
})
|
|
File without changes
|