@hero-design/rn 8.96.0 → 8.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/rn",
3
- "version": "8.96.0",
3
+ "version": "8.97.0",
4
4
  "license": "MIT",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -1,10 +1,8 @@
1
- import DateTimePicker from '@react-native-community/datetimepicker';
2
1
  import React, { useState } from 'react';
3
2
  import { TouchableOpacity, View } from 'react-native';
4
3
 
5
- import { MonthYearPickerDialogueAndroid } from '@hero-design/react-native-month-year-picker';
6
- import { useTheme } from '../../theme';
7
4
  import TextInput from '../TextInput';
5
+ import AndroidDatePickerDialog from './Dialog/AndroidDialog';
8
6
  import useCalculateDate from './hooks/useCalculateDate';
9
7
  import useFormatDate from './hooks/useFormatDate';
10
8
  import type { DatePickerProps } from './types';
@@ -43,9 +41,6 @@ const DatePickerAndroid = ({
43
41
 
44
42
  useCalculateDate({ minDate, maxDate, onChange, value });
45
43
 
46
- const pickerInitValue = value || new Date();
47
- const theme = useTheme();
48
-
49
44
  return (
50
45
  <TouchableOpacity onPress={() => setOpen(true)} disabled={disabled}>
51
46
  <View pointerEvents="none" testID="datePickerInputAndroid">
@@ -74,36 +69,15 @@ const DatePickerAndroid = ({
74
69
  }
75
70
  />
76
71
  </View>
77
- {open && variant === 'month-year' ? (
78
- <MonthYearPickerDialogueAndroid
79
- themeVariant={theme.themeMode === 'dark' ? 'dark' : 'light'}
80
- value={value}
81
- minimumDate={minDate}
82
- maximumDate={maxDate}
83
- onChange={(action, date) => {
84
- setOpen(false);
85
- if (action === 'dateSetAction' && !!date) {
86
- onChange(date);
87
- }
88
- }}
89
- />
90
- ) : null}
91
- {open && variant === 'default' ? (
92
- <DateTimePicker
93
- testID="datePickerAndroid"
94
- mode="date"
95
- value={pickerInitValue}
96
- minimumDate={minDate}
97
- maximumDate={maxDate}
98
- display="default"
99
- onChange={(_: any, date: Date | undefined) => {
100
- setOpen(false);
101
- if (date) {
102
- onChange(date);
103
- }
104
- }}
105
- />
106
- ) : null}
72
+ <AndroidDatePickerDialog
73
+ open={open}
74
+ onClose={() => setOpen(false)}
75
+ value={value}
76
+ minDate={minDate}
77
+ maxDate={maxDate}
78
+ onChange={onChange}
79
+ variant={variant}
80
+ />
107
81
  </TouchableOpacity>
108
82
  );
109
83
  };
@@ -1,16 +1,11 @@
1
- import DateTimePicker from '@react-native-community/datetimepicker';
2
1
  import React, { useState } from 'react';
3
2
  import { TouchableOpacity, View } from 'react-native';
4
3
 
5
- import { MonthYearPickerViewIOS } from '@hero-design/react-native-month-year-picker';
6
- import { useTheme } from '../../theme';
7
- import BottomSheet from '../BottomSheet';
8
- import Button from '../Button';
9
4
  import { useLocale } from '../LocaleProvider/hooks';
10
5
  import TextInput from '../TextInput';
11
- import useCalculateDate, { getDateValue } from './hooks/useCalculateDate';
6
+ import IOSDatePickerDialog from './Dialog/IOSDialog';
7
+ import useCalculateDate from './hooks/useCalculateDate';
12
8
  import useFormatDate from './hooks/useFormatDate';
13
- import { StyledPickerWrapper } from './StyledDatePicker';
14
9
  import type { DatePickerProps } from './types';
15
10
 
16
11
  type DatePickerIOSProps = Omit<
@@ -39,10 +34,6 @@ const DatePickerIOS = ({
39
34
  locale,
40
35
  renderSelectedValue,
41
36
  }: DatePickerIOSProps) => {
42
- const theme = useTheme();
43
- const [selectingDate, setSelectingDate] = useState<Date>(
44
- getDateValue(value || new Date(), minDate, maxDate)
45
- );
46
37
  const [open, setOpen] = useState(false);
47
38
  const { lang: defaultLocale } = useLocale();
48
39
  const { displayValue, format } = useFormatDate({
@@ -81,60 +72,19 @@ const DatePickerIOS = ({
81
72
  }
82
73
  />
83
74
  </View>
84
- <BottomSheet
75
+ <IOSDatePickerDialog
76
+ value={value}
77
+ onChange={onChange}
85
78
  open={open}
86
- onRequestClose={() => setOpen(false)}
87
- header={label}
88
- footer={
89
- <Button
90
- variant="text"
91
- text={confirmLabel}
92
- onPress={() => {
93
- if (selectingDate) {
94
- onChange(selectingDate);
95
- }
96
- setOpen(false);
97
- }}
98
- />
99
- }
79
+ onClose={() => setOpen(false)}
80
+ confirmLabel={confirmLabel}
81
+ locale={locale || defaultLocale}
100
82
  supportedOrientations={supportedOrientations}
101
- >
102
- <StyledPickerWrapper>
103
- {variant === 'month-year' ? (
104
- <MonthYearPickerViewIOS
105
- value={value}
106
- locale={locale || defaultLocale}
107
- minimumDate={minDate}
108
- textColor={theme.colors.onDefaultGlobalSurface}
109
- maximumDate={maxDate}
110
- onChange={(date: Date | undefined) => {
111
- if (date) {
112
- setSelectingDate(date);
113
- }
114
- }}
115
- style={{ flex: 1 }}
116
- />
117
- ) : null}
118
- {variant === 'default' ? (
119
- <DateTimePicker
120
- locale={locale || defaultLocale}
121
- testID="datePickerIOS"
122
- value={selectingDate}
123
- minimumDate={minDate}
124
- maximumDate={maxDate}
125
- mode="date"
126
- onChange={(_: any, date: Date | undefined) => {
127
- if (date) {
128
- setSelectingDate(date);
129
- }
130
- }}
131
- display="spinner"
132
- style={{ flex: 1 }}
133
- textColor={theme.colors.onDefaultGlobalSurface}
134
- />
135
- ) : null}
136
- </StyledPickerWrapper>
137
- </BottomSheet>
83
+ variant={variant}
84
+ label={label}
85
+ minDate={minDate}
86
+ maxDate={maxDate}
87
+ />
138
88
  </TouchableOpacity>
139
89
  );
140
90
  };
@@ -0,0 +1,68 @@
1
+ import React from 'react';
2
+ import DateTimePicker, {
3
+ DateTimePickerEvent,
4
+ } from '@react-native-community/datetimepicker';
5
+ import { MonthYearPickerDialogueAndroid } from '@hero-design/react-native-month-year-picker';
6
+ import { DatePickerDialogProps } from './type';
7
+ import { useTheme } from '../../../theme';
8
+ import Box from '../../Box';
9
+
10
+ export type AndroidDatePickerDialogProps = Omit<
11
+ DatePickerDialogProps,
12
+ 'supportedOrientations' | 'confirmLabel' | 'label' | 'locale'
13
+ >;
14
+
15
+ const AndroidDatePickerDialog = ({
16
+ open,
17
+ onClose,
18
+ value,
19
+ minDate,
20
+ maxDate,
21
+ onChange,
22
+ testID,
23
+ variant = 'default',
24
+ }: AndroidDatePickerDialogProps) => {
25
+ const theme = useTheme();
26
+ if (!open) return null;
27
+
28
+ const pickerInitValue = value || new Date();
29
+
30
+ return (
31
+ <Box testID={testID}>
32
+ {open && variant === 'month-year' ? (
33
+ <Box testID={testID}>
34
+ <MonthYearPickerDialogueAndroid
35
+ themeVariant={theme.themeMode === 'dark' ? 'dark' : 'light'}
36
+ value={value}
37
+ minimumDate={minDate}
38
+ maximumDate={maxDate}
39
+ onChange={(action, date) => {
40
+ onClose();
41
+ if (action === 'dateSetAction' && !!date) {
42
+ onChange(date);
43
+ }
44
+ }}
45
+ />
46
+ </Box>
47
+ ) : null}
48
+ {open && variant === 'default' ? (
49
+ <DateTimePicker
50
+ testID="datePickerAndroid"
51
+ mode="date"
52
+ value={pickerInitValue}
53
+ minimumDate={minDate}
54
+ maximumDate={maxDate}
55
+ display="default"
56
+ onChange={(_: DateTimePickerEvent, date: Date | undefined) => {
57
+ onClose();
58
+ if (date) {
59
+ onChange(date);
60
+ }
61
+ }}
62
+ />
63
+ ) : null}
64
+ </Box>
65
+ );
66
+ };
67
+
68
+ export default AndroidDatePickerDialog;
@@ -0,0 +1,91 @@
1
+ import { MonthYearPickerViewIOS } from '@hero-design/react-native-month-year-picker';
2
+ import DateTimePicker, {
3
+ DateTimePickerEvent,
4
+ } from '@react-native-community/datetimepicker';
5
+ import React, { useState } from 'react';
6
+ import BottomSheet from '../../BottomSheet';
7
+ import Button from '../../Button';
8
+ import { useTheme } from '../../../theme';
9
+ import { StyledPickerWrapper } from '../StyledDatePicker';
10
+ import { DatePickerDialogProps } from './type';
11
+ import { getDateValue } from '../hooks/useCalculateDate';
12
+
13
+ const IOSDatePickerDialog = ({
14
+ label,
15
+ open,
16
+ onClose,
17
+ confirmLabel,
18
+ locale,
19
+ value,
20
+ minDate,
21
+ maxDate,
22
+ onChange,
23
+ testID,
24
+ variant = 'default',
25
+ supportedOrientations = ['portrait'],
26
+ }: DatePickerDialogProps) => {
27
+ const theme = useTheme();
28
+ const [selectingDate, setSelectingDate] = useState<Date>(
29
+ getDateValue(value || new Date(), minDate, maxDate)
30
+ );
31
+
32
+ return (
33
+ <BottomSheet
34
+ testID={testID}
35
+ open={open}
36
+ onRequestClose={onClose}
37
+ header={label}
38
+ footer={
39
+ <Button
40
+ variant="text"
41
+ text={confirmLabel}
42
+ onPress={() => {
43
+ if (selectingDate) {
44
+ onChange(selectingDate);
45
+ }
46
+ onClose();
47
+ }}
48
+ />
49
+ }
50
+ supportedOrientations={supportedOrientations}
51
+ >
52
+ <StyledPickerWrapper>
53
+ {variant === 'month-year' ? (
54
+ <MonthYearPickerViewIOS
55
+ value={selectingDate}
56
+ locale={locale}
57
+ minimumDate={minDate}
58
+ textColor={theme.colors.onDefaultGlobalSurface}
59
+ maximumDate={maxDate}
60
+ onChange={(date: Date | undefined) => {
61
+ if (date) {
62
+ setSelectingDate(date);
63
+ }
64
+ }}
65
+ style={{ flex: 1 }}
66
+ />
67
+ ) : null}
68
+ {variant === 'default' ? (
69
+ <DateTimePicker
70
+ locale={locale}
71
+ testID="datePickerIOS"
72
+ value={selectingDate}
73
+ minimumDate={minDate}
74
+ maximumDate={maxDate}
75
+ mode="date"
76
+ onChange={(_: DateTimePickerEvent, date: Date | undefined) => {
77
+ if (date) {
78
+ setSelectingDate(date);
79
+ }
80
+ }}
81
+ display="spinner"
82
+ style={{ flex: 1 }}
83
+ textColor={theme.colors.onDefaultGlobalSurface}
84
+ />
85
+ ) : null}
86
+ </StyledPickerWrapper>
87
+ </BottomSheet>
88
+ );
89
+ };
90
+
91
+ export default IOSDatePickerDialog;
@@ -0,0 +1,70 @@
1
+ import { fireEvent } from '@testing-library/react-native';
2
+ import React, { useState } from 'react';
3
+ import { Button } from '../../../..';
4
+ import renderWithTheme from '../../../../testHelpers/renderWithTheme';
5
+ import AndroidDialog, { AndroidDatePickerDialogProps } from '../AndroidDialog';
6
+
7
+ const AndroidDialogExample = (
8
+ props: Omit<AndroidDatePickerDialogProps, 'open' | 'onClose'>
9
+ ) => {
10
+ const [open, setOpen] = useState(false);
11
+
12
+ return (
13
+ <>
14
+ <Button onPress={() => setOpen(true)} text="Open" />
15
+ <AndroidDialog open={open} onClose={() => setOpen(false)} {...props} />
16
+ </>
17
+ );
18
+ };
19
+
20
+ describe('AndroidDialog', () => {
21
+ it('renders correctly', () => {
22
+ const onChange = jest.fn();
23
+
24
+ const { getByText, queryByTestId } = renderWithTheme(
25
+ <AndroidDialogExample
26
+ value={new Date('December 21, 1995')}
27
+ minDate={new Date('December 17, 1994')}
28
+ maxDate={new Date('December 17, 1996')}
29
+ onChange={onChange}
30
+ testID="android-dialog"
31
+ />
32
+ );
33
+
34
+ expect(queryByTestId('android-dialog')).toBeNull();
35
+
36
+ // Open date picker
37
+ fireEvent.press(getByText('Open'));
38
+ expect(queryByTestId('datePickerAndroid')).toBeVisible();
39
+
40
+ // Change date
41
+ fireEvent(
42
+ queryByTestId('datePickerAndroid'),
43
+ 'onChange',
44
+ null,
45
+ new Date('December 17, 1995')
46
+ );
47
+
48
+ expect(onChange).toBeCalledWith(new Date('December 17, 1995'));
49
+ });
50
+
51
+ it('renders month-year picker', () => {
52
+ const onChangeSpy = jest.fn();
53
+ const { queryByText, getByText } = renderWithTheme(
54
+ <AndroidDialogExample
55
+ value={new Date('December 21, 1995')}
56
+ variant="month-year"
57
+ onChange={onChangeSpy}
58
+ testID="android-dialog"
59
+ />
60
+ );
61
+
62
+ fireEvent.press(getByText('Open'));
63
+ expect(queryByText('Android month year picker')).toBeDefined();
64
+
65
+ // mock change date
66
+ fireEvent.press(getByText('Android month year picker'));
67
+ expect(queryByText('Android month year picker')).toBeNull();
68
+ expect(onChangeSpy).toBeCalledWith(new Date('2019-09-01'));
69
+ });
70
+ });
@@ -0,0 +1,114 @@
1
+ import { fireEvent } from '@testing-library/react-native';
2
+ import React, { useState } from 'react';
3
+ import type { ModalProps } from 'react-native';
4
+ import renderWithTheme from '../../../../testHelpers/renderWithTheme';
5
+ import IOSDialog from '../IOSDialog';
6
+ import { setOrientation } from '../../../../testHelpers/utils';
7
+ import { Button } from '../../../..';
8
+ import { DatePickerDialogProps } from '../type';
9
+
10
+ jest.mock('react-native/Libraries/Modal/Modal', () => {
11
+ const Modal = jest.requireActual('react-native/Libraries/Modal/Modal');
12
+ return (props: ModalProps) => <Modal {...props} />;
13
+ });
14
+
15
+ const IOSDialogExample = (
16
+ props: Omit<DatePickerDialogProps, 'open' | 'onClose'>
17
+ ) => {
18
+ const [open, setOpen] = useState(false);
19
+
20
+ return (
21
+ <>
22
+ <Button onPress={() => setOpen(true)} text="Open" />
23
+ <IOSDialog open={open} onClose={() => setOpen(false)} {...props} />
24
+ </>
25
+ );
26
+ };
27
+
28
+ describe('IOSDialog', () => {
29
+ it('renders correctly', () => {
30
+ const onChange = jest.fn();
31
+ const { getByText, queryByTestId } = renderWithTheme(
32
+ <IOSDialogExample
33
+ value={new Date('December 21, 1995')}
34
+ label="Start date"
35
+ confirmLabel="Confirm"
36
+ onChange={onChange}
37
+ testID="ios-dialog"
38
+ />
39
+ );
40
+
41
+ expect(queryByTestId('ios-dialog')).toBeNull();
42
+
43
+ // Open date picker
44
+ fireEvent.press(getByText('Open'));
45
+ expect(queryByTestId('datePickerIOS')).toBeTruthy();
46
+
47
+ // Change date
48
+ fireEvent(
49
+ queryByTestId('datePickerIOS'),
50
+ 'onChange',
51
+ null,
52
+ new Date('December 17, 1995')
53
+ );
54
+ fireEvent.press(getByText('Confirm'));
55
+
56
+ expect(onChange).toBeCalledWith(new Date('December 17, 1995'));
57
+ });
58
+
59
+ it('renders correctly in landscape mode', () => {
60
+ const onChange = jest.fn();
61
+ setOrientation('landscape');
62
+ const { getByText, queryByTestId } = renderWithTheme(
63
+ <IOSDialogExample
64
+ value={new Date('December 21, 1995')}
65
+ label="Start date"
66
+ confirmLabel="Confirm"
67
+ onChange={onChange}
68
+ supportedOrientations={['landscape']}
69
+ testID="ios-dialog"
70
+ />
71
+ );
72
+
73
+ expect(queryByTestId('ios-dialog')).toBeNull();
74
+
75
+ // Open date picker
76
+ fireEvent.press(getByText('Open'));
77
+ expect(queryByTestId('datePickerIOS')).toBeTruthy();
78
+
79
+ // Change date
80
+ fireEvent(
81
+ queryByTestId('datePickerIOS'),
82
+ 'onChange',
83
+ null,
84
+ new Date('December 17, 1995')
85
+ );
86
+ fireEvent.press(getByText('Confirm'));
87
+
88
+ expect(onChange).toBeCalledWith(new Date('December 17, 1995'));
89
+ });
90
+
91
+ it('renders month-year picker', () => {
92
+ const onChangeSpy = jest.fn();
93
+ const { queryByText, getByText } = renderWithTheme(
94
+ <IOSDialogExample
95
+ value={new Date('December 21, 1995')}
96
+ variant="month-year"
97
+ label="Start date"
98
+ confirmLabel="Confirm"
99
+ onChange={onChangeSpy}
100
+ testID="ios-dialog"
101
+ />
102
+ );
103
+
104
+ fireEvent.press(getByText('Open'));
105
+ expect(getByText('Confirm')).toBeDefined();
106
+ expect(getByText('IOS month year picker')).toBeDefined();
107
+
108
+ // mock change date
109
+ fireEvent.press(getByText('IOS month year picker'));
110
+ fireEvent.press(getByText('Confirm'));
111
+ expect(queryByText('iOS month year picker')).toBeNull();
112
+ expect(onChangeSpy).toBeCalledWith(new Date('2019-09-01'));
113
+ });
114
+ });
@@ -0,0 +1,50 @@
1
+ export interface DatePickerDialogProps {
2
+ /**
3
+ * The value of the date picker.
4
+ */
5
+ value?: Date;
6
+ /**
7
+ * The minimum date of the date picker.
8
+ */
9
+ minDate?: Date;
10
+ /**
11
+ * The maximum date of the date picker.
12
+ */
13
+ maxDate?: Date;
14
+ /**
15
+ * The function to call when the date is changed.
16
+ */
17
+ onChange: (date: Date) => void;
18
+ /**
19
+ * The test ID of the date picker.
20
+ */
21
+ testID?: string;
22
+ /**
23
+ * Whether the dialog is open
24
+ */
25
+ open: boolean;
26
+ /**
27
+ * The function to call when the dialog is closed.
28
+ */
29
+ onClose: () => void;
30
+ /**
31
+ * The variant of the date picker.
32
+ */
33
+ variant?: 'default' | 'month-year';
34
+ /**
35
+ * [iOS only] The label of the DatePicker bottom sheet.
36
+ */
37
+ label: string;
38
+ /**
39
+ * [iOS only] The confirm label of the date picker bottom sheet.
40
+ */
41
+ confirmLabel: string;
42
+ /**
43
+ * [iOS only] The supported orientations of the date picker.
44
+ */
45
+ supportedOrientations?: ('portrait' | 'landscape')[];
46
+ /**
47
+ * [iOS only] The locale of the date picker.
48
+ */
49
+ locale?: string;
50
+ }
@@ -278,15 +278,24 @@ exports[`DatePickerAndroid renders correctly 1`] = `
278
278
  </View>
279
279
  </View>
280
280
  </View>
281
- <Picker
282
- display="default"
283
- maximumDate={1996-12-17T00:00:00.000Z}
284
- minimumDate={1994-12-17T00:00:00.000Z}
285
- mode="date"
286
- onChange={[Function]}
287
- testID="datePickerAndroid"
288
- value={1995-12-21T00:00:00.000Z}
289
- />
281
+ <View
282
+ style={
283
+ [
284
+ {},
285
+ undefined,
286
+ ]
287
+ }
288
+ >
289
+ <Picker
290
+ display="default"
291
+ maximumDate={1996-12-17T00:00:00.000Z}
292
+ minimumDate={1994-12-17T00:00:00.000Z}
293
+ mode="date"
294
+ onChange={[Function]}
295
+ testID="datePickerAndroid"
296
+ value={1995-12-21T00:00:00.000Z}
297
+ />
298
+ </View>
290
299
  </View>
291
300
  <View
292
301
  pointerEvents="box-none"
@@ -4,6 +4,9 @@ import DatePickerAndroid from './DatePickerAndroid';
4
4
  import DatePickerCalendar from './DatePickerCalendar';
5
5
  import DatePickerIOS from './DatePickerIOS';
6
6
  import type { DatePickerProps } from './types';
7
+ import IOSDatePickerDialog from './Dialog/IOSDialog';
8
+ import AndroidDatePickerDialog from './Dialog/AndroidDialog';
9
+ import { DatePickerDialogProps } from './Dialog/type';
7
10
 
8
11
  const DatePicker = ({ variant = 'default', ...props }: DatePickerProps) => {
9
12
  if (variant === 'calendar') {
@@ -16,4 +19,12 @@ const DatePicker = ({ variant = 'default', ...props }: DatePickerProps) => {
16
19
  return <DatePickerAndroid {...props} variant={variant} />;
17
20
  };
18
21
 
19
- export default DatePicker;
22
+ const Dialog = ({ ...props }: DatePickerDialogProps) => {
23
+ if (Platform.OS === 'ios') {
24
+ return <IOSDatePickerDialog {...props} />;
25
+ }
26
+
27
+ return <AndroidDatePickerDialog {...props} />;
28
+ };
29
+
30
+ export default Object.assign(DatePicker, { Dialog });