Skip to content

Commit 8188550

Browse files
authored
Merge branch 'main' into export-preset-title
2 parents 6cec32f + 3ea92ad commit 8188550

File tree

11 files changed

+141
-61
lines changed

11 files changed

+141
-61
lines changed

src/components/RelativeRangeDatePicker/RelativeRangeDatePicker.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export function RelativeRangeDatePicker(props: RelativeRangeDatePickerProps) {
154154
docs={props.docs}
155155
withApplyButton={props.withApplyButton}
156156
withZonesList={props.withZonesList}
157+
withHeader={props.withHeader}
157158
/>
158159
</div>
159160
);

src/components/RelativeRangeDatePicker/__tests__/form.test.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,42 @@ describe('RelativeRangeDatePicker: form', () => {
9797
]);
9898
});
9999

100+
it('should submit docs preset after selection', async () => {
101+
let value;
102+
const onSubmit = jest.fn((e) => {
103+
e.preventDefault();
104+
const formData = new FormData(e.currentTarget);
105+
value = [...formData.entries()];
106+
});
107+
render(
108+
<form data-qa="form" onSubmit={onSubmit}>
109+
<RelativeRangeDatePicker name="date-field" withHeader />
110+
<button type="submit" data-qa="submit">
111+
submit
112+
</button>
113+
</form>,
114+
);
115+
await userEvent.tab();
116+
await userEvent.tab();
117+
await userEvent.keyboard('{Enter}');
118+
119+
await userEvent.click(screen.getByRole('button', {name: 'now - 5m'}));
120+
await userEvent.click(screen.getAllByRole('button', {name: 'now'})[0]);
121+
122+
expect(screen.getByText('Last 5 minutes')).toBeVisible();
123+
124+
await userEvent.click(screen.getByTestId('submit'));
125+
126+
expect(onSubmit).toHaveBeenCalledTimes(1);
127+
expect(value).toEqual([
128+
['date-field', 'relative'],
129+
['date-field', 'now - 5m'],
130+
['date-field', 'relative'],
131+
['date-field', 'now'],
132+
['date-field', 'default'],
133+
]);
134+
});
135+
100136
it('supports form reset', async () => {
101137
function Test() {
102138
const [value, setValue] = React.useState<RangeValue<Value | null> | null>({

src/components/RelativeRangeDatePicker/components/Presets/PresetsDoc.scss renamed to src/components/RelativeRangeDatePicker/components/PickerDialog/PickerDoc.scss

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
@use '@gravity-ui/uikit/styles/mixins';
22
@use '../../../variables';
33

4-
$block: '.#{variables.$ns}relative-range-date-picker-presets-doc';
4+
$block: '.#{variables.$ns}relative-range-date-picker-doc';
55

66
#{$block} {
7-
padding: var(--g-spacing-4);
8-
97
&__button {
108
--g-button-background-color-hover: transparent;
119
}

src/components/RelativeRangeDatePicker/components/Presets/PresetsDoc.tsx renamed to src/components/RelativeRangeDatePicker/components/PickerDialog/PickerDoc.tsx

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,12 @@ import type {TableColumnConfig} from '@gravity-ui/uikit';
88

99
import {block} from '../../../../utils/cn';
1010
import {getButtonSizeForInput} from '../../../utils/getButtonSizeForInput';
11+
import type {Preset} from '../Presets/defaultPresets';
12+
import {i18n} from '../Presets/i18n';
1113

12-
import type {Preset} from './defaultPresets';
13-
import {i18n} from './i18n';
14+
import './PickerDoc.scss';
1415

15-
import './PresetsDoc.scss';
16-
17-
const b = block('relative-range-date-picker-presets-doc');
18-
19-
const columns: TableColumnConfig<Preset>[] = [
20-
{
21-
id: 'title',
22-
name: () => {
23-
return i18n('Range');
24-
},
25-
},
26-
{
27-
id: 'from',
28-
name: () => {
29-
return i18n('From');
30-
},
31-
},
32-
{
33-
id: 'to',
34-
name: () => {
35-
return i18n('To');
36-
},
37-
},
38-
];
16+
const b = block('relative-range-date-picker-doc');
3917

4018
const data: Preset[] = [
4119
{
@@ -75,25 +53,67 @@ const data: Preset[] = [
7553
},
7654
];
7755

78-
interface PresetsExamplesProps {
79-
size?: 's' | 'm' | 'l' | 'xl';
56+
interface DocContentProps extends Omit<PresetsDocProps, 'docs' | 'className'> {
8057
docs: Preset[];
8158
}
82-
function PresetsExamples({size, docs}: PresetsExamplesProps) {
83-
return <Table columns={columns} data={docs} className={b('table', {size})} />;
59+
60+
function DocContent({size, docs, onStartUpdate, onEndUpdate}: DocContentProps) {
61+
const isMobile = useMobile();
62+
63+
const columns: TableColumnConfig<Preset>[] = React.useMemo(
64+
() => [
65+
{
66+
id: 'title',
67+
name: () => {
68+
return i18n('Range');
69+
},
70+
},
71+
{
72+
id: 'from',
73+
name: () => {
74+
return i18n('From');
75+
},
76+
template: (item) => (
77+
<Button
78+
size={isMobile ? 'l' : getButtonSizeForInput(size)}
79+
onClick={() => onStartUpdate(item.from)}
80+
>
81+
{item.from}
82+
</Button>
83+
),
84+
},
85+
{
86+
id: 'to',
87+
name: () => {
88+
return i18n('To');
89+
},
90+
template: (item) => (
91+
<Button
92+
size={isMobile ? 'l' : getButtonSizeForInput(size)}
93+
onClick={() => onEndUpdate(item.to)}
94+
>
95+
{item.to}
96+
</Button>
97+
),
98+
},
99+
],
100+
[isMobile, onEndUpdate, onStartUpdate, size],
101+
);
102+
103+
return <Table columns={columns} data={docs} className={b('table', {size})} wordWrap />;
84104
}
85105

86-
interface DesktopDocProps {
87-
className?: string;
88-
size?: 's' | 'm' | 'l' | 'xl';
106+
interface DesktopDocProps extends Omit<PresetsDocProps, 'docs'> {
89107
docs: Preset[];
90108
}
91-
function DesktopDoc({className, size, docs}: DesktopDocProps) {
109+
110+
function DesktopDoc({className, size, ...props}: DesktopDocProps) {
92111
return (
93112
<Popover
94113
className={b(null)}
95114
hasArrow={false}
96-
content={<PresetsExamples size={size} docs={docs} />}
115+
placement={['right-start', 'left-start']}
116+
content={<DocContent size={size} {...props} />}
97117
>
98118
<Button
99119
className={b('button', className)}
@@ -106,12 +126,11 @@ function DesktopDoc({className, size, docs}: DesktopDocProps) {
106126
);
107127
}
108128

109-
interface MobileDocProps {
110-
className?: string;
111-
size?: 's' | 'm' | 'l' | 'xl';
129+
interface MobileDocProps extends Omit<PresetsDocProps, 'docs'> {
112130
docs: Preset[];
113131
}
114-
function MobileDoc({className, size, docs}: MobileDocProps) {
132+
133+
function MobileDoc({className, ...props}: MobileDocProps) {
115134
const [open, setOpen] = React.useState(false);
116135
return (
117136
<div className={b(null, className)}>
@@ -126,7 +145,7 @@ function MobileDoc({className, size, docs}: MobileDocProps) {
126145
<Icon data={CircleQuestion} />
127146
</Button>
128147
<Sheet visible={open} onClose={() => setOpen(false)}>
129-
<PresetsExamples size={size} docs={docs} />
148+
<DocContent {...props} />
130149
</Sheet>
131150
</div>
132151
);
@@ -136,18 +155,20 @@ interface PresetsDocProps {
136155
className?: string;
137156
size?: 's' | 'm' | 'l' | 'xl';
138157
docs?: Preset[];
158+
onStartUpdate: (start: string) => void;
159+
onEndUpdate: (end: string) => void;
139160
}
140161

141-
export function PresetsDoc({className, size, docs = data}: PresetsDocProps) {
162+
export function PickerDoc({docs = data, ...props}: PresetsDocProps) {
142163
const isMobile = useMobile();
143164

144165
if (!Array.isArray(docs) || docs.length === 0) {
145166
return null;
146167
}
147168

148169
if (isMobile) {
149-
return <MobileDoc className={className} size={size} docs={docs} />;
170+
return <MobileDoc {...props} docs={docs} />;
150171
}
151172

152-
return <DesktopDoc className={className} size={size} docs={docs} />;
173+
return <DesktopDoc {...props} docs={docs} />;
153174
}

src/components/RelativeRangeDatePicker/components/PickerDialog/PickerForm.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ $block: '.#{variables.$ns}relative-range-date-picker-form';
1717
}
1818
}
1919

20+
&__header {
21+
display: flex;
22+
align-items: center;
23+
gap: var(--g-spacing-1);
24+
25+
margin-block-end: var(--g-spacing-2);
26+
}
27+
2028
&__apply {
2129
margin-block-start: var(--g-spacing-2);
2230
}

src/components/RelativeRangeDatePicker/components/PickerDialog/PickerForm.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import type {DateTime} from '@gravity-ui/date-utils';
4-
import {Button} from '@gravity-ui/uikit';
4+
import {Button, Text} from '@gravity-ui/uikit';
55
import type {TextInputSize} from '@gravity-ui/uikit';
66

77
import {block} from '../../../../utils/cn';
@@ -14,6 +14,7 @@ import type {Preset} from '../Presets/defaultPresets';
1414
import type {PresetTab} from '../Presets/utils';
1515
import {Zones} from '../Zones/Zones';
1616

17+
import {PickerDoc} from './PickerDoc';
1718
import {i18n} from './i18n';
1819
import {useRelativeRangeDatePickerDialogState} from './useRelativeRangeDatePickerDialogState';
1920

@@ -39,9 +40,11 @@ export interface PickerFormProps extends RelativeRangeDatePickerStateOptions, Do
3940
withZonesList?: boolean;
4041
/** Show relative range presets */
4142
withPresets?: boolean;
43+
/** Show header with docs tooltip */
44+
withHeader?: boolean;
4245
/** Custom preset tabs */
4346
presetTabs?: PresetTab[];
44-
/** Custom docs for presets, if empty array docs will be hidden */
47+
/** Custom docs for picker, if empty array docs will be hidden */
4548
docs?: Preset[];
4649
}
4750

@@ -62,9 +65,27 @@ export function PickerForm(
6265
size: props.size,
6366
errorPlacement: 'inside',
6467
};
65-
const {isDateUnavailable} = props;
68+
const {isDateUnavailable, withHeader = false} = props;
6669
return (
6770
<div className={b({size: props.size}, props.className)} style={props.style}>
71+
{withHeader && (
72+
<div className={b('header')}>
73+
<Text variant={props.size === 'xl' ? 'subheader-3' : 'subheader-2'}>
74+
{i18n('Select the interval')}
75+
</Text>
76+
<PickerDoc
77+
size={props.size}
78+
docs={props.docs}
79+
onStartUpdate={(start) => {
80+
state.setStart({type: 'relative', value: start});
81+
}}
82+
onEndUpdate={(end) => {
83+
state.setEnd({type: 'relative', value: end});
84+
}}
85+
/>
86+
</div>
87+
)}
88+
6889
<div className={b('pickers')}>
6990
<RelativeDatePicker
7091
{...fieldProps}
@@ -124,7 +145,6 @@ export function PickerForm(
124145
}
125146
}}
126147
minValue={props.minValue}
127-
docs={props.docs}
128148
className={b('presets')}
129149
/>
130150
) : null}

src/components/RelativeRangeDatePicker/components/PickerDialog/i18n/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"\"From\" can't be after \"To\".": "\"From\" can't be after \"To\".",
55
"From": "From",
66
"To": "To",
7-
"Apply": "Apply"
7+
"Apply": "Apply",
8+
"Select the interval": "Select the interval"
89
}

src/components/RelativeRangeDatePicker/components/PickerDialog/i18n/ru.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"\"From\" can't be after \"To\".": "Значение «От» не может быть позже чем «До».",
55
"From": "От",
66
"To": "Дo",
7-
"Apply": "Применить"
7+
"Apply": "Применить",
8+
"Select the interval": "Выберите интервал"
89
}

src/components/RelativeRangeDatePicker/components/Presets/Presets.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ $block: '.#{variables.$ns}relative-range-date-picker-presets';
2222
outline: none;
2323
}
2424

25-
&__doc {
26-
margin-inline-start: auto;
27-
}
28-
2925
&__content {
3026
overflow: auto;
3127

src/components/RelativeRangeDatePicker/components/Presets/Presets.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {List, Tab, TabList, TabPanel, TabProvider} from '@gravity-ui/uikit';
77

88
import {block} from '../../../../utils/cn';
99

10-
import {PresetsDoc} from './PresetsDoc';
1110
import type {Preset} from './defaultPresets';
1211
import {filterPresetTabs, getDefaultPresetTabs} from './utils';
1312
import type {PresetTab} from './utils';
@@ -23,7 +22,6 @@ export interface PresetProps {
2322
minValue?: DateTime;
2423
size?: 's' | 'm' | 'l' | 'xl';
2524
presetTabs?: PresetTab[];
26-
docs?: Preset[];
2725
}
2826
export function Presets({
2927
className,
@@ -32,7 +30,6 @@ export function Presets({
3230
withTime,
3331
onChoosePreset,
3432
presetTabs,
35-
docs,
3633
}: PresetProps) {
3734
const tabs = React.useMemo(() => {
3835
return filterPresetTabs(presetTabs ?? getDefaultPresetTabs({withTime}), {minValue});
@@ -64,7 +61,6 @@ export function Presets({
6461
</Tab>
6562
))}
6663
</TabList>
67-
<PresetsDoc className={b('doc')} size={size} docs={docs} />
6864
</div>
6965
<TabPanel className={b('content')} value={activeTabId}>
7066
<PresetsList

0 commit comments

Comments
 (0)