Skip to content

Commit

Permalink
feat: support timezone setting (#2103)
Browse files Browse the repository at this point in the history
Co-authored-by: YanHui <yanhui@growingio.com>
  • Loading branch information
hiker90 and YanHui committed Oct 17, 2022
1 parent da29156 commit 38a48f6
Show file tree
Hide file tree
Showing 18 changed files with 155 additions and 102 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@
"@types/react-resizable": "^1.7.3",
"classnames": "^2.2.6",
"date-fns": "^2.21.3",
"date-fns-tz": "^1.3.7",
"immutability-helper": "^3.1.1",
"lodash": "^4.17.14",
"moment": "^2.26.0",
"moment-timezone": "^0.5.38",
"pinyin-match": "^1.2.2",
"raf": "^3.4.1",
"rc-animate": "^3.1.0",
Expand Down
4 changes: 2 additions & 2 deletions src/date-picker/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import classnames from 'classnames';
import { usePrefixCls, useControlledState } from '@gio-design/utils';
import { format } from 'date-fns/fp';
import { CalendarOutlined } from '@gio-design/icons';
import { parseFnsTimeZone } from '../utils/timeHelper';
import Popover from '../popover';
import { InputButton } from '../input';
import StaticDatePicker from '../static-date-picker';
Expand Down Expand Up @@ -40,7 +40,7 @@ export const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) =>

const [controlledValue, setControlledValue] = useControlledState(value, defaultValue);

const formatDate = (date: Date) => format(formatString, date);
const formatDate = (date: Date) => parseFnsTimeZone(date, formatString);

const handleVisibleChange = (current: boolean) => {
setVisible(current);
Expand Down
4 changes: 2 additions & 2 deletions src/date-picker/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import { format } from 'date-fns';
import { parseFnsTimeZone } from '../../utils/timeHelper';
import Button from '../../button';
import DatePicker from '../DatePicker';

Expand All @@ -13,7 +13,7 @@ describe('Testing DatePicker ', () => {
it('render has trigger params', () => {
const { container } = render(
<DatePicker
trigger={<Button type="secondary">{format(new Date(), '您的所选时间为 yyyy-MM-dd HH:mm:ss')}</Button>}
trigger={<Button type="secondary">{parseFnsTimeZone(new Date(), '您的所选时间为 yyyy-MM-dd HH:mm:ss')}</Button>}
/>
);
expect(container.querySelectorAll('您的所选时间为')).toBeTruthy();
Expand Down
31 changes: 12 additions & 19 deletions src/date-picker/demos/DatePicker.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { useState } from 'react';
import { Meta, Story } from '@storybook/react/types-6-0';
import { withDesign } from 'storybook-addon-designs';
import { addMonths, format, isBefore, startOfToday } from 'date-fns';
import { addMonths, isBefore, startOfToday } from 'date-fns';
import { action } from '@storybook/addon-actions';
import { ClockOutlined, ArrowDownOutlined } from '@gio-design/icons';
import { parseFnsTimeZone } from '../../utils/timeHelper';
import Docs from './DatePickerPage';
import Button from '../../button';
import Toast from '../../toast';
Expand Down Expand Up @@ -35,28 +36,24 @@ Default.args = {
defaultValue: new Date(),
placeholder: '选择具体某一天',
onChange: () => {
action('onChange')
action('onChange');
},
};

export const Format: Story<DatePickerProps> = (args) => {
const [value, setValue] = useState(new Date());

return <DatePicker
value={value}
onSelect={setValue}
{...args}
/>
}
return <DatePicker value={value} onSelect={setValue} {...args} />;
};
Format.args = {
format: 'yyyy/dd-mm'
}
format: 'yyyy/dd-mm',
};

export const PrefixAndSuffix = Template.bind({});
PrefixAndSuffix.args = {
defaultValue: new Date(),
prefix: <ClockOutlined />,
suffix: <ArrowDownOutlined />
suffix: <ArrowDownOutlined />,
};

export const AllowClear = Template;
Expand All @@ -67,7 +64,7 @@ AllowClear.args = {
export const Size = Template.bind({});
Size.args = {
defaultValue: new Date(),
size: 'small'
size: 'small',
};

export const Disabled = Template.bind({});
Expand All @@ -92,17 +89,13 @@ OnSelectAndOnClose.args = {
const CustomizeTriggerTemplate: Story<DatePickerProps> = (args) => {
const [value, setValue] = useState(new Date());
const onSelect = (date: Date) => {
setValue(date)
action('onSelect:')
setValue(date);
action('onSelect:');
};

return (
<DatePicker
trigger={
<Button type="secondary">
{format(value, '您的所选时间为 yyyy-MM-dd HH:mm:ss')}
</Button>
}
trigger={<Button type="secondary">{parseFnsTimeZone(value, '您的所选时间为 yyyy-MM-dd HH:mm:ss')}</Button>}
onSelect={onSelect}
{...args}
/>
Expand Down
6 changes: 3 additions & 3 deletions src/date-range-picker/DateRangePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react';
import classnames from 'classnames';
import { usePrefixCls, useControlledState } from '@gio-design/utils';
import format from 'date-fns/format';
import { CalendarOutlined } from '@gio-design/icons';
import { parseFnsTimeZone } from '../utils/timeHelper';
import Popover from '../popover';
import { InputButton } from '../input';
import StaticDateRangePicker from '../static-date-range-picker';
import { DateRangePickerProps, NullableDate, NullableString } from './interfaces';

export const formatDates = (dates: [NullableDate, NullableDate], formatString = 'yyyy/MM/dd'): NullableString => {
const strongFormat = (date: NullableDate) => (date ? format(date, formatString) : undefined);
const strongFormat = (date: NullableDate) => (date ? parseFnsTimeZone(date, formatString) : undefined);
return `${strongFormat(dates[0]) || ''} - ${strongFormat(dates[1]) || ''}`;
};

Expand Down Expand Up @@ -88,7 +88,7 @@ export const DateRangePicker: React.FC<DateRangePickerProps> = (props) => {
placeholder={placeholder}
disabled={disabled}
allowClear={allowClear}
value={validValue(controlledValue) ? formatDates(controlledValue,formatString) : undefined}
value={validValue(controlledValue) ? formatDates(controlledValue, formatString) : undefined}
size={size}
suffix={suffix}
className={className}
Expand Down
44 changes: 21 additions & 23 deletions src/date-range-picker/demos/DateRangePicker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import React, { useState } from 'react';
import { Meta, Story } from '@storybook/react/types-6-0';
import { action } from '@storybook/addon-actions';
import { ArrowDownOutlined, ClockOutlined } from '@gio-design/icons';
import { format, isEqual, startOfDay, startOfToday, startOfWeek, subMonths } from 'date-fns';
import { isEqual, startOfDay, startOfToday, startOfWeek, subMonths } from 'date-fns';
import { parseFnsTimeZone } from '../../utils/timeHelper';
import Docs from './DateRangePickerPage';
import DateRangePicker, { DateRangePickerProps, StaticDateRangePickerProps } from '../index';
import '../style';
Expand All @@ -25,9 +26,7 @@ export default {
},
} as Meta;

const Template: Story<DateRangePickerProps> = (args) => (
<DateRangePicker {...args} />
);
const Template: Story<DateRangePickerProps> = (args) => <DateRangePicker {...args} />;

const ControlledTemplate: Story<DateRangePickerProps> = (args) => {
const [TimeRange, setTimeRange] = useState([new Date(), new Date()] as [Date, Date]);
Expand All @@ -36,13 +35,15 @@ const ControlledTemplate: Story<DateRangePickerProps> = (args) => {
};
return (
<div style={{ width: 280 }}>
<DateRangePicker value={TimeRange} onSelect={onSelect} trigger={
<Button type='secondary'>
{TimeRange[0] && format(TimeRange[0], 'yyyy/MM/dd')}
-
{TimeRange[1] && format(TimeRange[1], 'yyyy/MM/dd')}
</Button>
}
<DateRangePicker
value={TimeRange}
onSelect={onSelect}
trigger={
<Button type="secondary">
{TimeRange[0] && parseFnsTimeZone(TimeRange[0], 'yyyy/MM/dd')}-
{TimeRange[1] && parseFnsTimeZone(TimeRange[1], 'yyyy/MM/dd')}
</Button>
}
{...args}
/>
</div>
Expand All @@ -55,22 +56,22 @@ export const Default = ControlledTemplate.bind({});
Default.args = {
placeholder: '选择日期范围',
trigger: null,
format: "yyyy/MM/dd",
format: 'yyyy/MM/dd',
disabled: false,
size: 'normal',
};

export const Format = Template.bind({})
export const Format = Template.bind({});
Format.args = {
defaultValue: [new Date(), new Date()],
format: 'yyyy--MM--dd'
}
format: 'yyyy--MM--dd',
};

export const PrefixAndSuffix = Template.bind({});
PrefixAndSuffix.args = {
defaultValue: [new Date(), new Date()],
prefix: <ClockOutlined />,
suffix: <ArrowDownOutlined />
suffix: <ArrowDownOutlined />,
};

export const OnSelectAndOnClose = Template.bind({});
Expand All @@ -85,14 +86,14 @@ export const AllowClear = Template.bind({});
AllowClear.args = {
allowClear: true,
onClear: () => {
action('onClear')
action('onClear');
},
};

export const Size = Template.bind({});
Size.args = {
defaultValue: [new Date(), new Date()],
size: 'small'
size: 'small',
};

export const Disabled = Template.bind({});
Expand All @@ -111,13 +112,10 @@ export const StaticBasic = StaticTemplate.bind({});
StaticBasic.args = {};

export const StaticDisabledDate: Story<StaticDateRangePickerProps> = () => {
const disabledDate = (date: Date) => isEqual(startOfWeek(date), startOfDay(date))
return <DateRangePicker.Static
disabledDate={disabledDate}
/>
const disabledDate = (date: Date) => isEqual(startOfWeek(date), startOfDay(date));
return <DateRangePicker.Static disabledDate={disabledDate} />;
};


export const StaticViewDates = StaticTemplate.bind({});
StaticViewDates.args = {
defaultViewDates: [subMonths(startOfToday(), 1), startOfToday()],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import moment, { Moment } from 'moment';
import { Moment } from 'moment';
import { parseTimeZone } from '../../../../../../../../utils/timeHelper';
import RelativeCurrent from './components/RelativeCurrent';
import RelativeBetween from './components/RelativeBetween';
import IncludeToday from './components/IncludeToday';
Expand All @@ -16,30 +17,32 @@ interface DateAttrSelectProps {
function DateAttrSelect(props: DateAttrSelectProps) {
const { attrSelect, attrChange, values, style } = props;
const [time, setTime] = useState<Moment>(
values?.[0] && parseFloat(values?.[0]).toString() !== 'NaN' ? moment(parseInt(values?.[0], 10)) : moment(Date.now())
values?.[0] && parseFloat(values?.[0]).toString() !== 'NaN'
? parseTimeZone(parseInt(values?.[0], 10))
: parseTimeZone(Date.now())
);
const [timeRange, setTimeRange] = useState<Moment[]>(
values.length && values?.[0]?.includes?.('abs')
? [
moment(parseInt(values?.[0].split(':')[1].split(',')[0], 10)),
moment(parseInt(values?.[0].split(':')[1].split(',')[1], 10)),
parseTimeZone(parseInt(values?.[0].split(':')[1].split(',')[0], 10)),
parseTimeZone(parseInt(values?.[0].split(':')[1].split(',')[1], 10)),
]
: [moment(Date.now()), moment(Date.now())]
: [parseTimeZone(Date.now()), parseTimeZone(Date.now())]
);

useEffect(() => {
setTime(
values?.[0] && parseFloat(values?.[0]).toString() !== 'NaN'
? moment(parseInt(values?.[0], 10))
: moment(Date.now())
? parseTimeZone(parseInt(values?.[0], 10))
: parseTimeZone(Date.now())
);
setTimeRange(
values.length && values?.[0]?.includes?.('abs')
? [
moment(parseInt(values?.[0].split(':')[1].split(',')[0], 10)),
moment(parseInt(values?.[0].split(':')[1].split(',')[1], 10)),
parseTimeZone(parseInt(values?.[0].split(':')[1].split(',')[0], 10)),
parseTimeZone(parseInt(values?.[0].split(':')[1].split(',')[1], 10)),
]
: [moment(Date.now()), moment(Date.now())]
: [parseTimeZone(Date.now()), parseTimeZone(Date.now())]
);
}, [values]);

Expand Down Expand Up @@ -75,32 +78,38 @@ function DateAttrSelect(props: DateAttrSelectProps) {
} else if (attrSelect === 'between' || attrSelect === 'not between') {
// 在。。。与。。。之间,值的初始化
attrChange([
`abs:${moment(timeRange?.[0], 'YYYY-MM-DD').startOf('day').valueOf()},${moment(timeRange?.[1], 'YYYY-MM-DD')
`abs:${parseTimeZone(timeRange?.[0], 'YYYY-MM-DD').startOf('day').valueOf()},${parseTimeZone(
timeRange?.[1],
'YYYY-MM-DD'
)
.endOf('day')
.valueOf()}`,
]);
} else {
attrChange([`${moment(Date.now()).valueOf()}`]);
attrChange([`${parseTimeZone(Date.now()).valueOf()}`]);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [attrSelect]);

const changeDate = (value: Date | null) => {
const date = moment(value);
const date = parseTimeZone(value);
date && setTime(date);
attrChange([`${moment(date, 'YYYY-MM-DD').startOf('day').valueOf()}`]);
attrChange([`${parseTimeZone(date, 'YYYY-MM-DD').startOf('day').valueOf()}`]);
};
const relativeDateChange = (v: string) => {
attrChange([v]);
};
const dateRangeChange = (value?: [NullableDate, NullableDate]) => {
if (!value || value.some((item) => !moment(item).isValid())) return;
const dateRange = [moment(value[0]), moment(value[1])];
if (!value || value.some((item) => !parseTimeZone(item).isValid())) return;
const dateRange = [parseTimeZone(value[0]), parseTimeZone(value[1])];
dateRange && setTimeRange(dateRange);
dateRange &&
attrChange([
`abs:${moment(dateRange?.[0], 'YYYY-MM-DD').startOf('day').valueOf()},${moment(dateRange?.[1], 'YYYY-MM-DD')
`abs:${parseTimeZone(dateRange?.[0], 'YYYY-MM-DD').startOf('day').valueOf()},${parseTimeZone(
dateRange?.[1],
'YYYY-MM-DD'
)
.endOf('day')
.valueOf()}`,
]);
Expand Down

1 comment on commit 38a48f6

@vercel
Copy link

@vercel vercel bot commented on 38a48f6 Oct 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

gio-design – ./

gio-design.vercel.app
gio-design-growingio.vercel.app
gio-design-git-master-growingio.vercel.app

Please sign in to comment.