All files / src/components/DatePicker DatePicker.tsx

100% Statements 10/10
72.72% Branches 16/22
100% Functions 3/3
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117    9x                                                                                             22x   22x     22x   22x 22x   22x     9x                                                                                       14x   1x                
'use client';
 
import React from 'react';
 
import type {DateTime} from '@gravity-ui/date-utils';
import {Calendar as CalendarIcon, Clock as ClockIcon} from '@gravity-ui/icons';
import {Button, Icon, Popup, TextInput, useMobile} from '@gravity-ui/uikit';
 
import {Calendar} from '../Calendar';
import type {CalendarProps} from '../Calendar';
import {DateField} from '../DateField';
import {HiddenInput} from '../HiddenInput/HiddenInput';
import type {
    AccessibilityProps,
    DomProps,
    FocusableProps,
    InputDOMProps,
    KeyboardEvents,
    PopupStyleProps,
    StyleProps,
    TextInputProps,
} from '../types';
 
import {MobileCalendar} from './MobileCalendar';
import {StubButton} from './StubButton';
import type {DatePickerStateOptions} from './hooks/datePickerStateFactory';
import {useDatePickerProps} from './hooks/useDatePickerProps';
import {useDatePickerState} from './hooks/useDatePickerState';
import {b} from './utils';
 
import './DatePicker.scss';
 
export interface DatePickerProps<T = DateTime>
    extends
        DatePickerStateOptions<T>,
        TextInputProps,
        FocusableProps,
        KeyboardEvents,
        DomProps,
        InputDOMProps,
        StyleProps,
        AccessibilityProps,
        PopupStyleProps {
    children?: (props: CalendarProps<T>) => React.ReactNode;
    disablePortal?: boolean;
    disableFocusTrap?: boolean;
}
 
export function DatePicker({className, ...props}: DatePickerProps) {
    const [anchor, setAnchor] = React.useState<HTMLDivElement | null>(null);
 
    const state = useDatePickerState(props);
 
    const {groupProps, fieldProps, calendarButtonProps, popupProps, calendarProps, timeInputProps} =
        useDatePickerProps(state, props);
 
    const isMobile = useMobile();
    const isOnlyTime = state.formatInfo.hasTime && !state.formatInfo.hasDate;
 
    return (
        <div className={b(null, className)} {...groupProps}>
            {isMobile ? (
                <MobileCalendar props={props} state={state} />
            ) : (
                !isOnlyTime && (
                    <div ref={setAnchor} className={b('popup-anchor')}>
                        <Popup anchorElement={anchor} {...popupProps}>
                            <div className={b('popup-content')}>
                                {typeof props.children === 'function' ? (
                                    props.children(calendarProps)
                                ) : (
                                    <Calendar {...calendarProps} />
                                )}
                                {state.formatInfo.hasTime && (
                                    <div className={b('time-field-wrapper')}>
                                        <DateField {...timeInputProps} />
                                    </div>
                                )}
                            </div>
                        </Popup>
                    </div>
                )
            )}
            <TextInput
                {...fieldProps}
                className={b('field', {mobile: isMobile})}
                hasClear={!isMobile && fieldProps.hasClear}
                endContent={
                    <React.Fragment>
                        {!isMobile && !isOnlyTime && (
                            <Button {...calendarButtonProps}>
                                <Icon data={CalendarIcon} />
                            </Button>
                        )}
                        {(isMobile || isOnlyTime) && (
                            <StubButton
                                size={calendarButtonProps.size}
                                icon={isOnlyTime ? ClockIcon : CalendarIcon}
                            />
                        )}
                    </React.Fragment>
                }
            />
            <HiddenInput
                name={props.name}
                value={state.value}
                toStringValue={(value) => value?.toISOString() ?? ''}
                onReset={(value) => {
                    state.setValue(value);
                }}
                disabled={state.disabled}
                form={props.form}
            />
        </div>
    );
}