agroptima-design-system 0.28.2 → 0.28.4-beta.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/Alert/Alert.tsx +1 -1
- package/src/atoms/Button/IconButton.scss +22 -0
- package/src/atoms/Button/IconButton.tsx +1 -1
- package/src/atoms/{DatePicker.scss → DatePicker/DateRangePicker.scss} +5 -5
- package/src/atoms/DatePicker/DateRangePicker.tsx +96 -0
- package/src/atoms/DatePicker/translations.ts +16 -0
- package/src/stories/Changelog.mdx +9 -0
- package/src/stories/DatePicker.stories.ts +4 -6
- package/src/stories/Drawer.stories.js +2 -3
- package/src/stories/IconButton.stories.ts +10 -0
- package/src/utils/dateHelpers.ts +19 -0
- package/tests/{DatePicker.spec.tsx → DateRangePicker.spec.tsx} +3 -4
- package/src/atoms/DatePicker.tsx +0 -66
package/package.json
CHANGED
|
@@ -54,6 +54,28 @@
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
&.tertiary {
|
|
58
|
+
> .icon {
|
|
59
|
+
> svg {
|
|
60
|
+
fill: color_alias.$neutral-color-400;
|
|
61
|
+
path {
|
|
62
|
+
fill: color_alias.$neutral-color-400;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&:hover {
|
|
68
|
+
> .icon {
|
|
69
|
+
> svg {
|
|
70
|
+
fill: color_alias.$neutral-color-400;
|
|
71
|
+
path {
|
|
72
|
+
fill: color_alias.$neutral-color-400;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
57
79
|
&.primary,
|
|
58
80
|
&.secondary {
|
|
59
81
|
&:disabled {
|
|
@@ -5,7 +5,7 @@ import { Icon } from '../Icon'
|
|
|
5
5
|
import type { BaseButtonProps } from './BaseButton'
|
|
6
6
|
import { BaseButton } from './BaseButton'
|
|
7
7
|
|
|
8
|
-
export type Variant = 'primary' | 'secondary'
|
|
8
|
+
export type Variant = 'primary' | 'secondary' | 'tertiary'
|
|
9
9
|
|
|
10
10
|
interface CustomProps {
|
|
11
11
|
icon: IconType
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
@use '
|
|
2
|
-
@use '
|
|
3
|
-
@use '
|
|
4
|
-
@use '
|
|
5
|
-
@use '
|
|
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,96 @@
|
|
|
1
|
+
import 'react-day-picker/style.css'
|
|
2
|
+
import './DateRangePicker.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
|
+
import {
|
|
8
|
+
formatDatePickerFooterDate,
|
|
9
|
+
formatDatePickerParamsDate,
|
|
10
|
+
} from '../../utils/dateHelpers'
|
|
11
|
+
import { translations } from './translations'
|
|
12
|
+
|
|
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
|
+
const availableLocales: AvailableLocale = {
|
|
25
|
+
es: es,
|
|
26
|
+
en: enGB,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface DateRangePickerProps extends DivPropsWithoutOnSelect {
|
|
30
|
+
variant?: Variant
|
|
31
|
+
onSelect: (dateRange: DateRange | undefined) => void
|
|
32
|
+
selected?: DateRange
|
|
33
|
+
lng: keyof typeof availableLocales
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function DateRangePicker({
|
|
37
|
+
className,
|
|
38
|
+
variant = 'primary',
|
|
39
|
+
onSelect = () => {},
|
|
40
|
+
selected: preselected,
|
|
41
|
+
lng,
|
|
42
|
+
}: DateRangePickerProps): React.JSX.Element {
|
|
43
|
+
const manageFooterText = (): string => {
|
|
44
|
+
const hasDatesFilter = selected && selected.from && selected.to
|
|
45
|
+
const isExactDate =
|
|
46
|
+
formatDatePickerParamsDate(selected?.from) ===
|
|
47
|
+
formatDatePickerParamsDate(selected?.to)
|
|
48
|
+
|
|
49
|
+
if (!hasDatesFilter) return translations[lng].pickDate
|
|
50
|
+
|
|
51
|
+
if (isExactDate) {
|
|
52
|
+
return translations[lng].selectedDate.replace(
|
|
53
|
+
'${date}',
|
|
54
|
+
formatDatePickerFooterDate(selected.from, lng as string),
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return translations[lng].selectedRangeOfDates
|
|
59
|
+
.replace(
|
|
60
|
+
'${from}',
|
|
61
|
+
formatDatePickerFooterDate(selected.from, lng as string),
|
|
62
|
+
)
|
|
63
|
+
.replace('${to}', formatDatePickerFooterDate(selected.to, lng as string))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const [selected, setSelected] = useState<DateRange | undefined>(preselected)
|
|
67
|
+
const [footer, setFooter] = useState<string>(() => {
|
|
68
|
+
return manageFooterText()
|
|
69
|
+
})
|
|
70
|
+
const cssClasses = classNames('date-picker', variant, className)
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
setSelected(selected)
|
|
74
|
+
setFooter(manageFooterText())
|
|
75
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
76
|
+
}, [selected])
|
|
77
|
+
|
|
78
|
+
function selectDate(dateRange: DateRange | undefined) {
|
|
79
|
+
setSelected(dateRange)
|
|
80
|
+
onSelect(dateRange)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div className={cssClasses}>
|
|
85
|
+
<DayPicker
|
|
86
|
+
locale={availableLocales[lng]}
|
|
87
|
+
mode="range"
|
|
88
|
+
min={1}
|
|
89
|
+
selected={selected}
|
|
90
|
+
onSelect={(dateRange) => selectDate(dateRange)}
|
|
91
|
+
footer={footer}
|
|
92
|
+
defaultMonth={selected?.from}
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface Translation {
|
|
2
|
+
[index: string]: { [index: string]: string }
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const translations: Translation = {
|
|
6
|
+
en: {
|
|
7
|
+
pickDate: 'Pick a day or a range of dates',
|
|
8
|
+
selectedDate: 'Selected date: ${date}',
|
|
9
|
+
selectedRangeOfDates: 'Selected dates range: from ${from} to ${to}',
|
|
10
|
+
},
|
|
11
|
+
es: {
|
|
12
|
+
pickDate: 'Selecciona un día o un rango de fechas',
|
|
13
|
+
selectedDate: 'Fecha seleccionada: ${date}',
|
|
14
|
+
selectedRangeOfDates: 'Rango de fechas seleccionado: desde ${from} a ${to}',
|
|
15
|
+
},
|
|
16
|
+
}
|
|
@@ -4,6 +4,15 @@ import { Meta } from "@storybook/blocks";
|
|
|
4
4
|
|
|
5
5
|
# Changelog
|
|
6
6
|
|
|
7
|
+
## 0.28.4
|
|
8
|
+
|
|
9
|
+
* WIP DateRangePicker refactor
|
|
10
|
+
|
|
11
|
+
## 0.28.3
|
|
12
|
+
|
|
13
|
+
* Add tertiary variant to IconButton component
|
|
14
|
+
* Update exit button variant on Alert component
|
|
15
|
+
|
|
7
16
|
## 0.28.1
|
|
8
17
|
|
|
9
18
|
* Align items to top in CardTable component
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { StoryObj } from '@storybook/react'
|
|
2
|
-
import {
|
|
2
|
+
import { DateRangePicker } from '../atoms/DatePicker/DateRangePicker'
|
|
3
3
|
|
|
4
4
|
const meta = {
|
|
5
|
-
title: 'Design System/Atoms/
|
|
6
|
-
component:
|
|
5
|
+
title: 'Design System/Atoms/DateRangePicker',
|
|
6
|
+
component: DateRangePicker,
|
|
7
7
|
tags: ['autodocs'],
|
|
8
8
|
argTypes: {
|
|
9
9
|
footer: {
|
|
@@ -34,10 +34,9 @@ const figmaPrimaryDesign = {
|
|
|
34
34
|
export default meta
|
|
35
35
|
type Story = StoryObj<typeof meta>
|
|
36
36
|
|
|
37
|
-
export const
|
|
37
|
+
export const Primary: Story = {
|
|
38
38
|
args: {
|
|
39
39
|
variant: 'primary',
|
|
40
|
-
footer: 'Pick a day.',
|
|
41
40
|
onSelect: (date) => console.log('onSelect date:', date),
|
|
42
41
|
lng: 'en',
|
|
43
42
|
},
|
|
@@ -47,7 +46,6 @@ export const PrimaryDateRange: Story = {
|
|
|
47
46
|
export const WithDateRangeSelected: Story = {
|
|
48
47
|
args: {
|
|
49
48
|
variant: 'primary',
|
|
50
|
-
footer: 'From 2024-02-02 to 2024-02-15',
|
|
51
49
|
onSelect: (date) => console.log('onSelect date:', date),
|
|
52
50
|
selected: { from: new Date(2024, 1, 2), to: new Date(2024, 1, 15) },
|
|
53
51
|
lng: 'en',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { CheckableTag, CheckableTagGroup } from '../atoms/CheckableTag'
|
|
3
3
|
import { Collapsible } from '../atoms/Collapsible'
|
|
4
|
-
import {
|
|
4
|
+
import { DateRangePicker } from '../atoms/DatePicker/DateRangePicker'
|
|
5
5
|
import { Drawer } from '../atoms/Drawer'
|
|
6
6
|
|
|
7
7
|
const figmaPrimaryDesign = {
|
|
@@ -122,8 +122,7 @@ export const Filters = {
|
|
|
122
122
|
</Collapsible>
|
|
123
123
|
<Collapsible noHorizontalPadding open title="Date">
|
|
124
124
|
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
|
125
|
-
<
|
|
126
|
-
footer="From 2024-01-01 to 2024-01-15"
|
|
125
|
+
<DateRangePicker
|
|
127
126
|
selected={{ from: new Date(2025, 0, 1), to: new Date(2025, 0, 15) }}
|
|
128
127
|
onSelect={(date) => console.log('date: ', date)}
|
|
129
128
|
/>
|
|
@@ -70,3 +70,13 @@ export const Secondary: Story = {
|
|
|
70
70
|
},
|
|
71
71
|
parameters: figmaPrimaryDesign,
|
|
72
72
|
}
|
|
73
|
+
|
|
74
|
+
export const Tertiary: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
icon: 'Delete',
|
|
77
|
+
variant: 'tertiary',
|
|
78
|
+
accessibilityLabel: 'Delete game',
|
|
79
|
+
disabled: false,
|
|
80
|
+
},
|
|
81
|
+
parameters: figmaPrimaryDesign,
|
|
82
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function formatDate(date: string, lng: string) {
|
|
2
|
+
if (!date) return ''
|
|
3
|
+
return new Date(date).toLocaleDateString(lng)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function formatDatePickerParamsDate(date: Date | undefined) {
|
|
7
|
+
if (!date) return ''
|
|
8
|
+
|
|
9
|
+
return date.toLocaleDateString('sv-SE')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function formatDatePickerFooterDate(
|
|
13
|
+
date: Date | undefined,
|
|
14
|
+
lng: string,
|
|
15
|
+
) {
|
|
16
|
+
if (!date) return ''
|
|
17
|
+
|
|
18
|
+
return formatDate(formatDatePickerParamsDate(date), lng)
|
|
19
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { render } from '@testing-library/react'
|
|
2
2
|
import React from 'react'
|
|
3
|
-
import {
|
|
3
|
+
import { DateRangePicker } from '../src/atoms/DatePicker/DateRangePicker'
|
|
4
4
|
|
|
5
|
-
describe('
|
|
5
|
+
describe('DateRangePicker', () => {
|
|
6
6
|
it('renders with expected footer, language and month', () => {
|
|
7
7
|
const { getByText } = render(
|
|
8
|
-
<
|
|
9
|
-
footer="From 2024-01-01 to 2024-01-15"
|
|
8
|
+
<DateRangePicker
|
|
10
9
|
selected={{ from: new Date(2025, 0, 1), to: new Date(2025, 0, 15) }}
|
|
11
10
|
onSelect={() => jest.fn()}
|
|
12
11
|
lng={'es'}
|
package/src/atoms/DatePicker.tsx
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
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
|
-
}
|