Skip to content

Commit 810f06c

Browse files
committed
feat(DateField): add hidden input for native form submissition
1 parent cad9a7e commit 810f06c

File tree

6 files changed

+71
-17
lines changed

6 files changed

+71
-17
lines changed

src/components/DateField/DateField.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ export function DateField({className, ...props}: DateFieldProps) {
3030
{...inputProps}
3131
value={state.isEmpty && !isActive && props.placeholder ? '' : inputProps.value}
3232
/>
33+
<input
34+
type="text"
35+
hidden
36+
name={props.name}
37+
value={state.value ? state.value.toISOString() : ''}
38+
// Ignore React warning
39+
onChange={() => {}}
40+
/>
3341
</div>
3442
);
3543
}

src/components/DatePicker/DatePicker.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
DateFieldBase,
1212
DomProps,
1313
FocusableProps,
14+
InputDOMProps,
1415
KeyboardEvents,
1516
StyleProps,
1617
TextInputProps,
@@ -29,20 +30,16 @@ export interface DatePickerProps
2930
FocusableProps,
3031
KeyboardEvents,
3132
DomProps,
33+
InputDOMProps,
3234
StyleProps,
3335
AccessibilityProps {
3436
children?: (props: CalendarProps) => React.ReactNode;
3537
}
3638

37-
export function DatePicker({value, defaultValue, onUpdate, className, ...props}: DatePickerProps) {
39+
export function DatePicker({className, ...props}: DatePickerProps) {
3840
const anchorRef = React.useRef<HTMLDivElement>(null);
3941

40-
const state = useDatePickerState({
41-
...props,
42-
value,
43-
defaultValue,
44-
onUpdate,
45-
});
42+
const state = useDatePickerState(props);
4643

4744
const {groupProps, fieldProps, calendarButtonProps, popupProps, calendarProps, timeInputProps} =
4845
useDatePickerProps(state, props);
@@ -85,6 +82,14 @@ export function DatePicker({value, defaultValue, onUpdate, className, ...props}:
8582
)
8683
}
8784
/>
85+
<input
86+
type="text"
87+
hidden
88+
name={props.name}
89+
value={state.value ? state.value.toISOString() : ''}
90+
// Ignore React warning
91+
onChange={() => {}}
92+
/>
8893
</div>
8994
);
9095
}

src/components/RelativeDateField/RelativeDateField.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
AccessibilityProps,
1010
DomProps,
1111
FocusableProps,
12+
InputDOMProps,
1213
KeyboardEvents,
1314
StyleProps,
1415
TextInputExtendProps,
@@ -28,6 +29,7 @@ export interface RelativeDateFieldProps
2829
TextInputProps,
2930
TextInputExtendProps,
3031
DomProps,
32+
InputDOMProps,
3133
StyleProps,
3234
AccessibilityProps,
3335
FocusableProps,
@@ -74,6 +76,14 @@ export function RelativeDateField(props: RelativeDateFieldProps) {
7476
}}
7577
onBlur={props.onBlur}
7678
/>
79+
<input
80+
type="text"
81+
hidden
82+
name={props.name}
83+
value={state.value ?? ''}
84+
// Ignore React warning
85+
onChange={() => {}}
86+
/>
7787
{!isMobile && (
7888
<Popup anchorRef={anchorRef} open={isOpen}>
7989
<div className={b('popup-content')}>

src/components/RelativeDatePicker/RelativeDatePicker.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ import type {
1212
AccessibilityProps,
1313
DomProps,
1414
FocusableProps,
15+
InputDOMProps,
1516
KeyboardEvents,
1617
StyleProps,
1718
TextInputProps,
1819
} from '../types';
1920

2021
import {useRelativeDatePickerProps} from './hooks/useRelativeDatePickerProps';
2122
import {useRelativeDatePickerState} from './hooks/useRelativeDatePickerState';
22-
import type {RelativeDatePickerStateOptions} from './hooks/useRelativeDatePickerState';
23+
import type {RelativeDatePickerStateOptions, Value} from './hooks/useRelativeDatePickerState';
2324

2425
import './RelativeDatePicker.scss';
2526

@@ -31,6 +32,7 @@ export interface RelativeDatePickerProps
3132
FocusableProps,
3233
KeyboardEvents,
3334
DomProps,
35+
InputDOMProps,
3436
StyleProps,
3537
AccessibilityProps {
3638
children?: (props: CalendarProps) => React.ReactNode;
@@ -69,6 +71,12 @@ export function RelativeDatePicker(props: RelativeDatePickerProps) {
6971
)}
7072
<TextInput
7173
{...fieldProps}
74+
controlProps={{
75+
...fieldProps.controlProps,
76+
disabled: isMobile && state.mode === 'absolute',
77+
className: b('input', {mobile: isMobile && state.mode === 'absolute'}),
78+
}}
79+
hasClear={props.hasClear && !(isMobile && state.mode === 'absolute')}
7280
startContent={
7381
<Button {...modeSwitcherProps}>
7482
<Icon data={FunctionIcon} />
@@ -90,6 +98,14 @@ export function RelativeDatePicker(props: RelativeDatePickerProps) {
9098
</React.Fragment>
9199
}
92100
/>
101+
<input
102+
type="text"
103+
hidden
104+
name={props.name}
105+
value={getNativeValue(state.value)}
106+
// Ignore React warning
107+
onChange={() => {}}
108+
/>
93109
{!isMobile && (
94110
<Popup {...popupProps} anchorRef={anchorRef}>
95111
<div className={b('popup-content')}>
@@ -109,3 +125,13 @@ export function RelativeDatePicker(props: RelativeDatePickerProps) {
109125
</div>
110126
);
111127
}
128+
129+
function getNativeValue(value: Value | null) {
130+
if (!value) {
131+
return '';
132+
}
133+
if (value.type === 'relative') {
134+
return value.value;
135+
}
136+
return value.value.toISOString();
137+
}

src/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {ButtonProps, PopupProps, TextInputProps} from '@gravity-ui/uikit';
66
import type {Calendar, CalendarInstance} from '../../Calendar';
77
import {useDateFieldProps} from '../../DateField';
88
import type {DateFieldProps} from '../../DateField';
9+
import {useRelativeDateFieldProps} from '../../RelativeDateField';
910
import {getButtonSizeForInput} from '../../utils/getButtonSizeForInput';
1011
import {mergeProps} from '../../utils/mergeProps';
1112
import type {RelativeDatePickerProps} from '../RelativeDatePicker';
@@ -96,14 +97,12 @@ export function useRelativeDatePickerProps(
9697
onUpdate: undefined,
9798
});
9899

99-
const relativeDateProps: TextInputProps = {
100-
disabled: relativeDateState.disabled,
101-
value: relativeDateState.text,
102-
onUpdate: relativeDateState.setText,
103-
hasClear: props.hasClear && !relativeDateState.readOnly,
104-
placeholder: props.placeholder,
105-
size: props.size,
106-
};
100+
const {inputProps: relativeDateProps} = useRelativeDateFieldProps(relativeDateState, {
101+
...props,
102+
value: undefined,
103+
defaultValue: undefined,
104+
onUpdate: undefined,
105+
});
107106

108107
let validationState = props.validationState;
109108
if (!validationState) {
@@ -163,7 +162,7 @@ export function useRelativeDatePickerProps(
163162
size: getButtonSizeForInput(props.size),
164163
disabled: state.readOnly || state.disabled,
165164
view: 'flat-secondary',
166-
style: {zIndex: 2},
165+
style: {zIndex: 2, marginInlineEnd: 2},
167166
selected: mode === 'relative',
168167
extraProps: {
169168
'aria-label': i18n('Formula input mode'),

src/components/types/inputs.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ export interface DomProps {
3535
id?: string;
3636
}
3737

38+
export interface InputDOMProps {
39+
/**
40+
* The name of the input element, used when submitting an HTML form.
41+
*/
42+
name?: string;
43+
}
3844
export interface InputBase {
3945
/**
4046
* Whether the field is disabled.

0 commit comments

Comments
 (0)