feat: custom date time value (#4367)

* feat: custom date time value

* fix: update custom date picker

* fix: old placeholder value flicker

* fix: html semantics and move styles to css

* fix: remove console logs

---------

Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
This commit is contained in:
Yunus M 2024-01-16 01:13:52 +05:30 committed by GitHub
parent cbf150ef7b
commit 739b1bf387
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 316 additions and 19 deletions

View File

@ -0,0 +1,88 @@
.time-options-container {
.time-options-item {
margin: 2px 0;
padding: 8px;
border-radius: 2px;
&.active {
background-color: rgba($color: #000000, $alpha: 0.2);
&:hover {
cursor: pointer;
background-color: rgba($color: #000000, $alpha: 0.3);
}
}
&:hover {
cursor: pointer;
background-color: rgba($color: #000000, $alpha: 0.3);
}
}
}
.time-selection-dropdown-content {
min-width: 172px;
width: 100%;
}
.timeSelection-input {
display: flex;
gap: 8px;
align-items: center;
padding: 4px 8px;
padding-left: 0px !important;
input::placeholder {
color: white;
}
input:focus::placeholder {
color: rgba($color: #ffffff, $alpha: 0.4);
}
}
.valid-format-error {
margin-top: 4px;
color: var(--bg-cherry-400, #ea6d71);
}
.lightMode {
.time-options-container {
.time-options-item {
&.active {
background-color: rgba($color: #ffffff, $alpha: 0.2);
&:hover {
cursor: pointer;
background-color: rgba($color: #ffffff, $alpha: 0.3);
}
}
&:hover {
cursor: pointer;
background-color: rgba($color: #ffffff, $alpha: 0.3);
}
}
}
.timeSelection-input {
display: flex;
gap: 8px;
align-items: center;
padding: 4px 8px;
padding-left: 0px !important;
input::placeholder {
color: var(---bg-ink-300);
}
input:focus::placeholder {
color: rgba($color: #000000, $alpha: 0.4);
}
}
.valid-format-error {
margin-top: 4px;
color: var(--bg-cherry-400, #ea6d71);
}
}

View File

@ -0,0 +1,208 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import './CustomTimePicker.styles.scss';
import { Input, Popover, Tooltip } from 'antd';
import cx from 'classnames';
import { Options } from 'container/TopNav/DateTimeSelection/config';
import dayjs from 'dayjs';
import debounce from 'lodash-es/debounce';
import { CheckCircle, ChevronDown, Clock } from 'lucide-react';
import { ChangeEvent, useEffect, useState } from 'react';
import { popupContainer } from 'utils/selectPopupContainer';
interface CustomTimePickerProps {
onSelect: (value: string) => void;
items: any[];
selectedValue: string;
selectedTime: string;
onValidCustomDateChange: ([t1, t2]: any[]) => void;
}
function CustomTimePicker({
onSelect,
items,
selectedValue,
selectedTime,
onValidCustomDateChange,
}: CustomTimePickerProps): JSX.Element {
const [open, setOpen] = useState(false);
const [
selectedTimePlaceholderValue,
setSelectedTimePlaceholderValue,
] = useState('Select / Enter Time Range');
const [inputValue, setInputValue] = useState('');
const [inputStatus, setInputStatus] = useState<'' | 'error' | 'success'>('');
const [isInputFocused, setIsInputFocused] = useState(false);
const getSelectedTimeRangeLabel = (
selectedTime: string,
selectedTimeValue: string,
): string => {
if (selectedTime === 'custom') {
return selectedTimeValue;
}
for (let index = 0; index < Options.length; index++) {
if (Options[index].value === selectedTime) {
return Options[index].label;
}
}
return '';
};
useEffect(() => {
const value = getSelectedTimeRangeLabel(selectedTime, selectedValue);
setSelectedTimePlaceholderValue(value);
}, [selectedTime, selectedValue]);
const hide = (): void => {
setOpen(false);
};
const handleOpenChange = (newOpen: boolean): void => {
setOpen(newOpen);
};
const debouncedHandleInputChange = debounce((inputValue): void => {
const isValidFormat = /^(\d+)([mhdw])$/.test(inputValue);
if (isValidFormat) {
setInputStatus('success');
const match = inputValue.match(/^(\d+)([mhdw])$/);
const value = parseInt(match[1], 10);
const unit = match[2];
const currentTime = dayjs();
let minTime = null;
switch (unit) {
case 'm':
minTime = currentTime.subtract(value, 'minute');
break;
case 'h':
minTime = currentTime.subtract(value, 'hour');
break;
case 'd':
minTime = currentTime.subtract(value, 'day');
break;
case 'w':
minTime = currentTime.subtract(value, 'week');
break;
default:
break;
}
onValidCustomDateChange([minTime, currentTime]);
} else {
setInputStatus('error');
}
}, 300);
const handleInputChange = (event: ChangeEvent<HTMLInputElement>): void => {
const inputValue = event.target.value;
if (inputValue.length > 0) {
setOpen(false);
} else {
setOpen(true);
}
setInputValue(inputValue);
// Call the debounced function with the input value
debouncedHandleInputChange(inputValue);
};
const content = (
<div className="time-selection-dropdown-content">
<div className="time-options-container">
{items.map(({ value, label }) => (
<div
onClick={(): void => {
onSelect(value);
setSelectedTimePlaceholderValue(label);
setInputStatus('');
setInputValue('');
hide();
}}
key={value}
className={cx(
'time-options-item',
selectedValue === value ? 'active' : '',
)}
>
{label}
</div>
))}
</div>
</div>
);
const handleFocus = (): void => {
setIsInputFocused(true);
};
const handleBlur = (): void => {
setIsInputFocused(false);
};
return (
<Popover
placement="bottomRight"
getPopupContainer={popupContainer}
content={content}
arrow={false}
open={open}
onOpenChange={handleOpenChange}
trigger={['click']}
style={{
padding: 0,
}}
>
<Input
className="timeSelection-input"
type="text"
style={{
minWidth: '120px',
width: '100%',
}}
status={inputValue && inputStatus === 'error' ? 'error' : ''}
allowClear={!isInputFocused && selectedTime === 'custom'}
placeholder={
isInputFocused
? 'Time Format (1m or 2h or 3d or 4w)'
: selectedTimePlaceholderValue
}
value={inputValue}
onFocus={handleFocus}
onBlur={handleBlur}
onChange={handleInputChange}
prefix={
inputValue && inputStatus === 'success' ? (
<CheckCircle size={14} color="#51E7A8" />
) : (
<Tooltip title="Enter time in format (e.g., 1m, 2h, 2d, 4w)">
<Clock size={14} />
</Tooltip>
)
}
suffix={
<ChevronDown
size={14}
onClick={(): void => {
setOpen(!open);
}}
/>
}
/>
</Popover>
);
}
export default CustomTimePicker;

View File

@ -1,7 +1,8 @@
import { SyncOutlined } from '@ant-design/icons'; import { SyncOutlined } from '@ant-design/icons';
import { Button, Select as DefaultSelect } from 'antd'; import { Button } from 'antd';
import getLocalStorageKey from 'api/browser/localstorage/get'; import getLocalStorageKey from 'api/browser/localstorage/get';
import setLocalStorageKey from 'api/browser/localstorage/set'; import setLocalStorageKey from 'api/browser/localstorage/set';
import CustomTimePicker from 'components/CustomTimePicker/CustomTimePicker';
import { LOCALSTORAGE } from 'constants/localStorage'; import { LOCALSTORAGE } from 'constants/localStorage';
import { QueryParams } from 'constants/query'; import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
@ -21,7 +22,6 @@ import { GlobalTimeLoading, UpdateTimeInterval } from 'store/actions';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import AppActions from 'types/actions'; import AppActions from 'types/actions';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { popupContainer } from 'utils/selectPopupContainer';
import AutoRefresh from '../AutoRefresh'; import AutoRefresh from '../AutoRefresh';
import CustomDateTimeModal, { DateTimeRangeType } from '../CustomDateTimeModal'; import CustomDateTimeModal, { DateTimeRangeType } from '../CustomDateTimeModal';
@ -29,8 +29,6 @@ import { getDefaultOption, getOptions, Time } from './config';
import RefreshText from './Refresh'; import RefreshText from './Refresh';
import { Form, FormContainer, FormItem } from './styles'; import { Form, FormContainer, FormItem } from './styles';
const { Option } = DefaultSelect;
function DateTimeSelection({ function DateTimeSelection({
location, location,
updateTimeInterval, updateTimeInterval,
@ -211,6 +209,7 @@ function DateTimeSelection({
}; };
const onCustomDateHandler = (dateTimeRange: DateTimeRangeType): void => { const onCustomDateHandler = (dateTimeRange: DateTimeRangeType): void => {
console.log('dateTimeRange', dateTimeRange);
if (dateTimeRange !== null) { if (dateTimeRange !== null) {
const [startTimeMoment, endTimeMoment] = dateTimeRange; const [startTimeMoment, endTimeMoment] = dateTimeRange;
if (startTimeMoment && endTimeMoment) { if (startTimeMoment && endTimeMoment) {
@ -289,26 +288,22 @@ function DateTimeSelection({
initialValues={{ interval: selectedTime }} initialValues={{ interval: selectedTime }}
> >
<FormContainer> <FormContainer>
<DefaultSelect <CustomTimePicker
getPopupContainer={popupContainer} onSelect={(value: unknown): void => {
onSelect={(value: unknown): void => onSelectHandler(value as Time)} onSelectHandler(value as Time);
value={getInputLabel( }}
selectedTime={selectedTime}
onValidCustomDateChange={(dateTime): void =>
onCustomDateHandler(dateTime as DateTimeRangeType)
}
selectedValue={getInputLabel(
dayjs(minTime / 1000000), dayjs(minTime / 1000000),
dayjs(maxTime / 1000000), dayjs(maxTime / 1000000),
selectedTime, selectedTime,
)} )}
data-testid="dropDown" data-testid="dropDown"
style={{ items={options}
minWidth: 120, />
}}
listHeight={400}
>
{options.map(({ value, label }) => (
<Option key={value + label} value={value}>
{label}
</Option>
))}
</DefaultSelect>
<FormItem hidden={refreshButtonHidden}> <FormItem hidden={refreshButtonHidden}>
<Button <Button

View File

@ -130,3 +130,9 @@ body {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgba(236, 236, 241, var(--tw-bg-opacity)); background-color: rgba(236, 236, 241, var(--tw-bg-opacity));
} }
.flexBtn {
display: flex;
align-items: center;
gap: 8px;
}