@hero-design/rn 7.12.1 → 7.14.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/.eslintrc.json +3 -1
- package/.turbo/turbo-build.log +3 -2
- package/assets/fonts/hero-icons.ttf +0 -0
- package/babel.config.js +16 -0
- package/es/index.js +35840 -16325
- package/lib/assets/fonts/hero-icons.ttf +0 -0
- package/lib/index.js +35847 -16327
- package/package.json +9 -4
- package/rollup.config.js +1 -0
- package/src/components/Accordion/AccordionItem.tsx +50 -0
- package/src/components/Accordion/StyledAccordion.tsx +29 -0
- package/src/components/Accordion/__tests__/AccordionItem.spec.tsx +56 -0
- package/src/components/Accordion/__tests__/StyledAccordion.spec.tsx +17 -0
- package/src/components/Accordion/__tests__/__snapshots__/AccordionItem.spec.tsx.snap +529 -0
- package/src/components/Accordion/__tests__/__snapshots__/StyledAccordion.spec.tsx.snap +33 -0
- package/src/components/Accordion/__tests__/__snapshots__/index.spec.tsx.snap +822 -0
- package/src/components/Accordion/__tests__/index.spec.tsx +54 -0
- package/src/components/Accordion/index.tsx +82 -0
- package/src/components/Accordion/utils.tsx +11 -0
- package/src/components/Button/Button.tsx +64 -60
- package/src/components/Button/IconButton.tsx +1 -1
- package/src/components/Button/StyledButton.tsx +4 -6
- package/src/components/Button/__tests__/StyledButton.spec.tsx +11 -4
- package/src/components/Button/__tests__/__snapshots__/StyledButton.spec.tsx.snap +312 -78
- package/src/components/Calendar/CalendarRowItem.tsx +54 -0
- package/src/components/Calendar/StyledCalendar.tsx +76 -0
- package/src/components/Calendar/__tests__/CalendarRowItem.spec.tsx +76 -0
- package/src/components/Calendar/__tests__/__snapshots__/CalendarRowItem.spec.tsx.snap +411 -0
- package/src/components/Calendar/__tests__/helper.spec.ts +50 -0
- package/src/components/Calendar/__tests__/index.spec.tsx +99 -0
- package/src/components/Calendar/helpers.ts +29 -0
- package/src/components/Calendar/index.tsx +217 -0
- package/src/components/Collapse/index.tsx +13 -15
- package/src/components/ContentNavigator/index.tsx +6 -0
- package/src/components/DatePicker/DatePickerAndroid.tsx +59 -0
- package/src/components/DatePicker/DatePickerIOS.tsx +87 -0
- package/src/components/DatePicker/StyledDatePicker.tsx +8 -0
- package/src/components/DatePicker/__tests__/DatePicker.spec.tsx +34 -0
- package/src/components/DatePicker/__tests__/DatePickerAndroid.spec.tsx +39 -0
- package/src/components/DatePicker/__tests__/DatePickerIOS.spec.tsx +46 -0
- package/src/components/DatePicker/__tests__/__snapshots__/DatePickerAndroid.spec.tsx.snap +199 -0
- package/src/components/DatePicker/__tests__/__snapshots__/DatePickerIOS.spec.tsx.snap +513 -0
- package/src/components/DatePicker/index.tsx +15 -0
- package/src/components/DatePicker/types.ts +49 -0
- package/src/components/Empty/StyledEmpty.tsx +47 -0
- package/src/components/Empty/__tests__/__snapshots__/index.spec.tsx.snap +66 -0
- package/src/components/Empty/__tests__/index.spec.tsx +17 -0
- package/src/components/Empty/index.tsx +53 -0
- package/src/components/FAB/ActionGroup/ActionItem.tsx +6 -2
- package/src/components/FAB/ActionGroup/StyledActionGroup.tsx +1 -0
- package/src/components/FAB/ActionGroup/StyledActionItem.tsx +7 -1
- package/src/components/FAB/ActionGroup/__tests__/__snapshots__/index.spec.tsx.snap +84 -22
- package/src/components/FAB/ActionGroup/index.tsx +8 -1
- package/src/components/Icon/HeroIcon/selection.json +1 -1
- package/src/components/Icon/IconList.ts +13 -0
- package/src/components/List/BasicListItem.tsx +44 -34
- package/src/components/List/ListItem.tsx +67 -58
- package/src/components/List/StyledBasicListItem.tsx +2 -3
- package/src/components/List/StyledListItem.tsx +2 -2
- package/src/components/List/__tests__/StyledBasicListItem.spec.tsx +5 -2
- package/src/components/List/__tests__/StyledListItem.spec.tsx +4 -1
- package/src/components/List/__tests__/__snapshots__/BasicListItem.spec.tsx.snap +15 -10
- package/src/components/List/__tests__/__snapshots__/ListItem.spec.tsx.snap +52 -32
- package/src/components/List/__tests__/__snapshots__/StyledBasicListItem.spec.tsx.snap +128 -48
- package/src/components/List/__tests__/__snapshots__/StyledListItem.spec.tsx.snap +132 -52
- package/src/components/RichTextEditor/EditorEvent.ts +7 -0
- package/src/components/RichTextEditor/EditorToolbar.tsx +220 -0
- package/src/components/RichTextEditor/MentionList.tsx +69 -0
- package/src/components/RichTextEditor/RichTextEditor.tsx +396 -0
- package/src/components/RichTextEditor/StyledRichTextEditor.ts +20 -0
- package/src/components/RichTextEditor/StyledToolbar.ts +32 -0
- package/src/components/RichTextEditor/__tests__/EditorToolbar.spec.tsx +130 -0
- package/src/components/RichTextEditor/__tests__/MentionList.spec.tsx +109 -0
- package/src/components/RichTextEditor/__tests__/RichTextEditor.spec.tsx +245 -0
- package/src/components/RichTextEditor/__tests__/__snapshots__/EditorToolbar.spec.tsx.snap +324 -0
- package/src/components/RichTextEditor/__tests__/__snapshots__/MentionList.spec.tsx.snap +45 -0
- package/src/components/RichTextEditor/__tests__/__snapshots__/RichTextEditor.spec.tsx.snap +526 -0
- package/src/components/RichTextEditor/constants.ts +20 -0
- package/src/components/RichTextEditor/hero-editor.d.ts +8 -0
- package/src/components/RichTextEditor/index.tsx +8 -0
- package/src/components/RichTextEditor/utils/events.ts +31 -0
- package/src/components/RichTextEditor/utils/rnWebView.ts +19 -0
- package/src/components/SectionHeading/__tests__/__snapshots__/index.spec.tsx.snap +77 -0
- package/src/components/SectionHeading/__tests__/index.spec.tsx +14 -0
- package/src/components/SectionHeading/index.tsx +16 -9
- package/src/components/Tag/StyledTag.tsx +12 -2
- package/src/components/Tag/__tests__/Tag.spec.tsx +35 -8
- package/src/components/Tag/__tests__/__snapshots__/Tag.spec.tsx.snap +118 -4
- package/src/components/Tag/index.tsx +9 -2
- package/src/components/TextInput/__tests__/__snapshots__/StyledTextInput.spec.tsx.snap +1 -0
- package/src/components/TimePicker/TimePickerIOS.tsx +1 -1
- package/src/components/TimePicker/types.ts +1 -1
- package/src/components/Typography/Text/StyledText.tsx +2 -1
- package/src/components/Typography/Text/__tests__/StyledText.spec.tsx +1 -0
- package/src/components/Typography/Text/__tests__/__snapshots__/StyledText.spec.tsx.snap +22 -0
- package/src/components/Typography/Text/index.tsx +2 -1
- package/src/index.ts +10 -0
- package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +119 -4
- package/src/theme/components/accordion.ts +19 -0
- package/src/theme/components/button.ts +12 -0
- package/src/theme/components/calendar.ts +34 -0
- package/src/theme/components/card.ts +1 -1
- package/src/theme/components/datePicker.ts +11 -0
- package/src/theme/components/empty.ts +38 -0
- package/src/theme/components/fab.ts +4 -3
- package/src/theme/components/list.ts +1 -0
- package/src/theme/components/pinInput.ts +1 -1
- package/src/theme/components/richTextEditor.ts +34 -0
- package/src/theme/components/tag.ts +8 -2
- package/src/theme/components/typography.ts +1 -0
- package/src/theme/global/borders.ts +6 -6
- package/src/theme/global/colors.ts +5 -1
- package/src/theme/index.ts +15 -0
- package/testUtils/setup.tsx +17 -0
- package/types/components/Accordion/AccordionItem.d.ts +14 -0
- package/types/components/Accordion/StyledAccordion.d.ts +32 -0
- package/types/components/Accordion/__tests__/AccordionItem.spec.d.ts +1 -0
- package/types/components/Accordion/__tests__/StyledAccordion.spec.d.ts +1 -0
- package/types/components/Accordion/__tests__/index.spec.d.ts +1 -0
- package/types/components/Accordion/index.d.ts +38 -0
- package/types/components/Accordion/utils.d.ts +1 -0
- package/types/components/Button/IconButton.d.ts +1 -1
- package/types/components/Button/StyledButton.d.ts +3 -3
- package/types/components/Calendar/CalendarRowItem.d.ts +10 -0
- package/types/components/Calendar/StyledCalendar.d.ts +54 -0
- package/types/components/Calendar/__tests__/CalendarRowItem.spec.d.ts +1 -0
- package/types/components/Calendar/__tests__/helper.spec.d.ts +1 -0
- package/types/components/Calendar/__tests__/index.spec.d.ts +1 -0
- package/types/components/Calendar/helpers.d.ts +3 -0
- package/types/components/Calendar/index.d.ts +40 -0
- package/types/components/Collapse/index.d.ts +1 -1
- package/types/components/ContentNavigator/index.d.ts +5 -1
- package/types/components/DatePicker/DatePickerAndroid.d.ts +3 -0
- package/types/components/DatePicker/DatePickerIOS.d.ts +3 -0
- package/types/components/DatePicker/StyledDatePicker.d.ts +8 -0
- package/types/components/DatePicker/__tests__/DatePicker.spec.d.ts +1 -0
- package/types/components/DatePicker/__tests__/DatePickerAndroid.spec.d.ts +1 -0
- package/types/components/DatePicker/__tests__/DatePickerIOS.spec.d.ts +1 -0
- package/types/components/DatePicker/index.d.ts +3 -0
- package/types/components/DatePicker/types.d.ts +48 -0
- package/types/components/Empty/StyledEmpty.d.ts +31 -0
- package/types/components/Empty/__tests__/index.spec.d.ts +1 -0
- package/types/components/Empty/index.d.ts +26 -0
- package/types/components/FAB/ActionGroup/StyledActionItem.d.ts +6 -1
- package/types/components/FAB/ActionGroup/index.d.ts +6 -1
- package/types/components/FAB/index.d.ts +1 -1
- package/types/components/Icon/IconList.d.ts +1 -1
- package/types/components/Icon/utils.d.ts +1 -1
- package/types/components/List/StyledBasicListItem.d.ts +3 -3
- package/types/components/List/StyledListItem.d.ts +3 -3
- package/types/components/RichTextEditor/EditorEvent.d.ts +3 -0
- package/types/components/RichTextEditor/EditorToolbar.d.ts +17 -0
- package/types/components/RichTextEditor/MentionList.d.ts +12 -0
- package/types/components/RichTextEditor/RichTextEditor.d.ts +65 -0
- package/types/components/RichTextEditor/StyledRichTextEditor.d.ts +16 -0
- package/types/components/RichTextEditor/StyledToolbar.d.ts +21 -0
- package/types/components/RichTextEditor/__tests__/EditorToolbar.spec.d.ts +1 -0
- package/types/components/RichTextEditor/__tests__/MentionList.spec.d.ts +1 -0
- package/types/components/RichTextEditor/__tests__/RichTextEditor.spec.d.ts +1 -0
- package/types/components/RichTextEditor/constants.d.ts +19 -0
- package/types/components/RichTextEditor/index.d.ts +5 -0
- package/types/components/RichTextEditor/utils/events.d.ts +8 -0
- package/types/components/RichTextEditor/utils/rnWebView.d.ts +7 -0
- package/types/components/SectionHeading/index.d.ts +2 -2
- package/types/components/Select/MultiSelect/OptionList.d.ts +1 -1
- package/types/components/Select/SingleSelect/OptionList.d.ts +1 -1
- package/types/components/Tag/StyledTag.d.ts +1 -1
- package/types/components/Tag/index.d.ts +1 -1
- package/types/components/TimePicker/types.d.ts +1 -1
- package/types/components/Typography/Text/StyledText.d.ts +1 -1
- package/types/components/Typography/Text/index.d.ts +1 -1
- package/types/index.d.ts +6 -1
- package/types/theme/components/accordion.d.ts +13 -0
- package/types/theme/components/button.d.ts +12 -0
- package/types/theme/components/calendar.d.ts +26 -0
- package/types/theme/components/datePicker.d.ts +6 -0
- package/types/theme/components/empty.d.ts +28 -0
- package/types/theme/components/fab.d.ts +1 -0
- package/types/theme/components/list.d.ts +1 -0
- package/types/theme/components/richTextEditor.d.ts +26 -0
- package/types/theme/components/tag.d.ts +8 -2
- package/types/theme/components/typography.d.ts +1 -0
- package/types/theme/global/colors.d.ts +5 -1
- package/types/theme/global/index.d.ts +5 -1
- package/types/theme/index.d.ts +10 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { fireEvent } from '@testing-library/react-native';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import Calendar from '..';
|
|
4
|
+
import renderWithTheme from '../../../testHelpers/renderWithTheme';
|
|
5
|
+
|
|
6
|
+
Date.now = jest.fn(() => new Date(2022, 10, 2).valueOf());
|
|
7
|
+
|
|
8
|
+
describe('Calendar', () => {
|
|
9
|
+
it('renders correctly', () => {
|
|
10
|
+
const onChange = jest.fn();
|
|
11
|
+
const onTitlePress = jest.fn();
|
|
12
|
+
const onPreviousPress = jest.fn();
|
|
13
|
+
const onNextPress = jest.fn();
|
|
14
|
+
const { getByText, queryAllByTestId, queryByTestId } = renderWithTheme(
|
|
15
|
+
<Calendar
|
|
16
|
+
value={new Date(2022, 10, 5)}
|
|
17
|
+
visibleDate={new Date(2022, 10, 5)}
|
|
18
|
+
onChange={onChange}
|
|
19
|
+
onTitlePress={onTitlePress}
|
|
20
|
+
onPreviousPress={onPreviousPress}
|
|
21
|
+
onNextPress={onNextPress}
|
|
22
|
+
markedDates={[
|
|
23
|
+
new Date(2022, 10, 5),
|
|
24
|
+
new Date(2022, 10, 6),
|
|
25
|
+
new Date(2022, 10, 7),
|
|
26
|
+
new Date(2022, 10, 8),
|
|
27
|
+
new Date(2022, 10, 9),
|
|
28
|
+
]}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(getByText('November 2022')).toBeTruthy();
|
|
33
|
+
expect(getByText('Mo')).toBeTruthy();
|
|
34
|
+
expect(getByText('Tu')).toBeTruthy();
|
|
35
|
+
expect(getByText('We')).toBeTruthy();
|
|
36
|
+
expect(getByText('Th')).toBeTruthy();
|
|
37
|
+
expect(getByText('Fr')).toBeTruthy();
|
|
38
|
+
expect(getByText('Sa')).toBeTruthy();
|
|
39
|
+
expect(getByText('Su')).toBeTruthy();
|
|
40
|
+
|
|
41
|
+
expect(queryAllByTestId('calendar-date-cell')).toHaveLength(42);
|
|
42
|
+
expect(queryAllByTestId('calendar-disabled-cell')).toHaveLength(0);
|
|
43
|
+
expect(queryAllByTestId('calendar-date-mark')).toHaveLength(5);
|
|
44
|
+
|
|
45
|
+
fireEvent.press(getByText('28'));
|
|
46
|
+
expect(onChange).toBeCalledWith(new Date(2022, 10, 28));
|
|
47
|
+
|
|
48
|
+
fireEvent.press(queryByTestId('previous-icon-button'));
|
|
49
|
+
expect(onPreviousPress).toBeCalled();
|
|
50
|
+
|
|
51
|
+
fireEvent.press(queryByTestId('next-icon-button'));
|
|
52
|
+
expect(onNextPress).toBeCalled();
|
|
53
|
+
|
|
54
|
+
fireEvent.press(getByText('November 2022'));
|
|
55
|
+
expect(onTitlePress).toBeCalled();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('renders correctly with minDate and maxDate', () => {
|
|
59
|
+
const onChange = jest.fn();
|
|
60
|
+
const onTitlePress = jest.fn();
|
|
61
|
+
const onPreviousPress = jest.fn();
|
|
62
|
+
const onNextPress = jest.fn();
|
|
63
|
+
const {
|
|
64
|
+
getByText,
|
|
65
|
+
queryByText,
|
|
66
|
+
queryAllByTestId,
|
|
67
|
+
queryByTestId,
|
|
68
|
+
} = renderWithTheme(
|
|
69
|
+
<Calendar
|
|
70
|
+
value={new Date(2022, 10, 5)}
|
|
71
|
+
visibleDate={new Date(2022, 10, 5)}
|
|
72
|
+
onChange={onChange}
|
|
73
|
+
onTitlePress={onTitlePress}
|
|
74
|
+
onPreviousPress={onPreviousPress}
|
|
75
|
+
onNextPress={onNextPress}
|
|
76
|
+
minDate={new Date(2022, 10, 3)}
|
|
77
|
+
maxDate={new Date(2022, 10, 27)}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(queryAllByTestId('calendar-date-cell')).toHaveLength(25);
|
|
82
|
+
expect(queryAllByTestId('calendar-disabled-cell')).toHaveLength(17);
|
|
83
|
+
|
|
84
|
+
expect(queryByText('2')).toBeNull();
|
|
85
|
+
expect(queryByText('28')).toBeNull();
|
|
86
|
+
|
|
87
|
+
fireEvent.press(queryByTestId('previous-icon-button'));
|
|
88
|
+
expect(onPreviousPress).not.toBeCalled();
|
|
89
|
+
|
|
90
|
+
fireEvent.press(queryByTestId('next-icon-button'));
|
|
91
|
+
expect(onNextPress).not.toBeCalled();
|
|
92
|
+
|
|
93
|
+
fireEvent.press(queryAllByTestId('calendar-disabled-cell')[0]);
|
|
94
|
+
expect(onChange).not.toBeCalled();
|
|
95
|
+
|
|
96
|
+
fireEvent.press(getByText('26'));
|
|
97
|
+
expect(onChange).toBeCalledWith(new Date(2022, 10, 26));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const initArray = <T>(length: number, func: (value: number) => T): T[] =>
|
|
2
|
+
Array.from(Array(length)).map((_, index) => func(index));
|
|
3
|
+
|
|
4
|
+
export const isEqDate = (dateA?: Date, dateB?: Date) =>
|
|
5
|
+
dateA?.toDateString() === dateB?.toDateString();
|
|
6
|
+
|
|
7
|
+
export const getValidDate = (
|
|
8
|
+
date: Date,
|
|
9
|
+
minDate?: Date,
|
|
10
|
+
maxDate?: Date
|
|
11
|
+
): Date | undefined => {
|
|
12
|
+
if (minDate === undefined && maxDate === undefined) {
|
|
13
|
+
return date;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (minDate !== undefined && maxDate === undefined) {
|
|
17
|
+
return date >= minDate ? date : undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (minDate === undefined && maxDate !== undefined) {
|
|
21
|
+
return date <= maxDate ? date : undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (minDate !== undefined && maxDate !== undefined) {
|
|
25
|
+
return date >= minDate && date <= maxDate ? date : undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return undefined;
|
|
29
|
+
};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import format from 'date-fns/fp/format';
|
|
3
|
+
import ContentNavigator from '../ContentNavigator';
|
|
4
|
+
import Typography from '../Typography';
|
|
5
|
+
import {
|
|
6
|
+
StyledCalendarDayNameCell,
|
|
7
|
+
StyledCalendarHeader,
|
|
8
|
+
StyledCalendarRow,
|
|
9
|
+
StyledCalendarRowItem,
|
|
10
|
+
StyledContainer,
|
|
11
|
+
StyledDisabledCalendarRowItem,
|
|
12
|
+
} from './StyledCalendar';
|
|
13
|
+
import CalendarRowItem from './CalendarRowItem';
|
|
14
|
+
import { getValidDate, initArray, isEqDate } from './helpers';
|
|
15
|
+
|
|
16
|
+
const DAYS_OF_WEEK = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];
|
|
17
|
+
// Sunday first column => 0
|
|
18
|
+
// Sunday last column => 1
|
|
19
|
+
const WEEK_INDEX_OFFSET = 1;
|
|
20
|
+
const SUNDAY_INDEX = 6;
|
|
21
|
+
|
|
22
|
+
// Always render 7 rows x 6 items for consistent layout
|
|
23
|
+
const TOTAL_DATES_ITEMS = 7 * 6;
|
|
24
|
+
|
|
25
|
+
type ParsedMaskedDate = {
|
|
26
|
+
[key: string]: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface CalendarProps {
|
|
30
|
+
/**
|
|
31
|
+
* Selected date value.
|
|
32
|
+
*/
|
|
33
|
+
value?: Date;
|
|
34
|
+
/**
|
|
35
|
+
* Visible date of month that calendar opens to display. Default is now.
|
|
36
|
+
*/
|
|
37
|
+
visibleDate: Date;
|
|
38
|
+
/**
|
|
39
|
+
* Mininum date. Restrict the range of possible date values.
|
|
40
|
+
*/
|
|
41
|
+
minDate?: Date;
|
|
42
|
+
/**
|
|
43
|
+
* Maximum date. Restrict the range of possible date values.
|
|
44
|
+
*/
|
|
45
|
+
maxDate?: Date;
|
|
46
|
+
/**
|
|
47
|
+
* Callback is called when selected date value is changed.
|
|
48
|
+
*/
|
|
49
|
+
onChange?: (date: Date) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Handler to be called when the user taps the previous button.
|
|
52
|
+
*/
|
|
53
|
+
onPreviousPress?: () => void;
|
|
54
|
+
/**
|
|
55
|
+
* Handler to be called when the user taps the next button.
|
|
56
|
+
*/
|
|
57
|
+
onNextPress?: () => void;
|
|
58
|
+
/**
|
|
59
|
+
* Handler to be called when the user taps the title.
|
|
60
|
+
*/
|
|
61
|
+
onTitlePress?: () => void;
|
|
62
|
+
/**
|
|
63
|
+
* Mark dates to display on calendar..
|
|
64
|
+
*/
|
|
65
|
+
markedDates?: Date[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const Calendar = ({
|
|
69
|
+
value,
|
|
70
|
+
visibleDate,
|
|
71
|
+
onChange,
|
|
72
|
+
onPreviousPress = () => {},
|
|
73
|
+
onNextPress = () => {},
|
|
74
|
+
onTitlePress = () => {},
|
|
75
|
+
minDate,
|
|
76
|
+
maxDate,
|
|
77
|
+
markedDates = [],
|
|
78
|
+
}: CalendarProps) => {
|
|
79
|
+
const currentMonth = visibleDate.getMonth();
|
|
80
|
+
const currentYear = visibleDate.getFullYear();
|
|
81
|
+
const now = new Date();
|
|
82
|
+
const parsedMaskedDate: ParsedMaskedDate = markedDates.reduce(
|
|
83
|
+
(current, markedDate) => ({
|
|
84
|
+
...current,
|
|
85
|
+
[markedDate.toDateString()]: true,
|
|
86
|
+
}),
|
|
87
|
+
{}
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const firstDateOfMonth = new Date(currentYear, currentMonth, 1);
|
|
91
|
+
const lastDateOfMonth = new Date(currentYear, currentMonth + 1, 0);
|
|
92
|
+
|
|
93
|
+
const lastDateOfPreviousMonth = new Date(currentYear, currentMonth, 0);
|
|
94
|
+
|
|
95
|
+
// Index of day in week is shifted by 1 due to Sunday is the last column
|
|
96
|
+
const firstDayWeekIndexOfMonth =
|
|
97
|
+
firstDateOfMonth.getDay() === 0
|
|
98
|
+
? SUNDAY_INDEX
|
|
99
|
+
: firstDateOfMonth.getDay() - WEEK_INDEX_OFFSET;
|
|
100
|
+
|
|
101
|
+
const lastDayIndexOfCurrentMonth = lastDateOfMonth.getDate();
|
|
102
|
+
const lastDayIndexOfPreviousMonth = lastDateOfPreviousMonth.getDate();
|
|
103
|
+
|
|
104
|
+
const daysOfPreviousMonth = initArray(firstDayWeekIndexOfMonth, index => {
|
|
105
|
+
const reversedIndex = firstDayWeekIndexOfMonth - index - 1;
|
|
106
|
+
const count = lastDayIndexOfPreviousMonth - reversedIndex;
|
|
107
|
+
return getValidDate(
|
|
108
|
+
new Date(currentYear, currentMonth - 1, count),
|
|
109
|
+
minDate,
|
|
110
|
+
maxDate
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const daysOfCurrentMonth = initArray(lastDayIndexOfCurrentMonth, index =>
|
|
115
|
+
getValidDate(
|
|
116
|
+
new Date(currentYear, currentMonth, index + 1),
|
|
117
|
+
minDate,
|
|
118
|
+
maxDate
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const daysOfNextMonth = initArray(
|
|
123
|
+
TOTAL_DATES_ITEMS -
|
|
124
|
+
(daysOfPreviousMonth.length + daysOfCurrentMonth.length),
|
|
125
|
+
index =>
|
|
126
|
+
getValidDate(
|
|
127
|
+
new Date(currentYear, currentMonth + 1, index + 1),
|
|
128
|
+
minDate,
|
|
129
|
+
maxDate
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const disablePrevButton =
|
|
134
|
+
minDate === undefined
|
|
135
|
+
? false
|
|
136
|
+
: !daysOfPreviousMonth.some(date => date !== undefined) &&
|
|
137
|
+
minDate >= firstDateOfMonth;
|
|
138
|
+
|
|
139
|
+
const disableNextButton =
|
|
140
|
+
maxDate === undefined
|
|
141
|
+
? false
|
|
142
|
+
: !daysOfNextMonth.some(date => date !== undefined) ||
|
|
143
|
+
maxDate <= lastDateOfMonth;
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<StyledContainer>
|
|
147
|
+
<StyledCalendarHeader>
|
|
148
|
+
<ContentNavigator
|
|
149
|
+
value={format('MMMM yyyy', visibleDate)}
|
|
150
|
+
onPreviousPress={onPreviousPress}
|
|
151
|
+
onNextPress={onNextPress}
|
|
152
|
+
onPress={onTitlePress}
|
|
153
|
+
previousDisabled={disablePrevButton}
|
|
154
|
+
nextDisabled={disableNextButton}
|
|
155
|
+
fontSize="large"
|
|
156
|
+
/>
|
|
157
|
+
</StyledCalendarHeader>
|
|
158
|
+
<StyledCalendarRow>
|
|
159
|
+
{DAYS_OF_WEEK.map(day => (
|
|
160
|
+
<StyledCalendarRowItem key={day}>
|
|
161
|
+
<StyledCalendarDayNameCell>
|
|
162
|
+
<Typography.Text>{day}</Typography.Text>
|
|
163
|
+
</StyledCalendarDayNameCell>
|
|
164
|
+
</StyledCalendarRowItem>
|
|
165
|
+
))}
|
|
166
|
+
</StyledCalendarRow>
|
|
167
|
+
<StyledCalendarRow>
|
|
168
|
+
{daysOfPreviousMonth.map(date =>
|
|
169
|
+
date ? (
|
|
170
|
+
<CalendarRowItem
|
|
171
|
+
key={date.toDateString()}
|
|
172
|
+
date={date}
|
|
173
|
+
isCurrent={isEqDate(now, date)}
|
|
174
|
+
isSelected={isEqDate(value, date)}
|
|
175
|
+
onPress={() => onChange?.(date)}
|
|
176
|
+
textIntent="subdued"
|
|
177
|
+
marked={parsedMaskedDate[date.toDateString()]}
|
|
178
|
+
/>
|
|
179
|
+
) : (
|
|
180
|
+
<StyledDisabledCalendarRowItem testID="calendar-disabled-cell" />
|
|
181
|
+
)
|
|
182
|
+
)}
|
|
183
|
+
{daysOfCurrentMonth.map(date =>
|
|
184
|
+
date ? (
|
|
185
|
+
<CalendarRowItem
|
|
186
|
+
key={date.toDateString()}
|
|
187
|
+
date={date}
|
|
188
|
+
isCurrent={isEqDate(now, date)}
|
|
189
|
+
isSelected={isEqDate(value, date)}
|
|
190
|
+
onPress={() => onChange?.(date)}
|
|
191
|
+
marked={parsedMaskedDate[date.toDateString()]}
|
|
192
|
+
/>
|
|
193
|
+
) : (
|
|
194
|
+
<StyledDisabledCalendarRowItem testID="calendar-disabled-cell" />
|
|
195
|
+
)
|
|
196
|
+
)}
|
|
197
|
+
{daysOfNextMonth.map(date =>
|
|
198
|
+
date ? (
|
|
199
|
+
<CalendarRowItem
|
|
200
|
+
key={date.toDateString()}
|
|
201
|
+
date={date}
|
|
202
|
+
isCurrent={isEqDate(now, date)}
|
|
203
|
+
isSelected={isEqDate(value, date)}
|
|
204
|
+
onPress={() => onChange?.(date)}
|
|
205
|
+
textIntent="subdued"
|
|
206
|
+
marked={parsedMaskedDate[date.toDateString()]}
|
|
207
|
+
/>
|
|
208
|
+
) : (
|
|
209
|
+
<StyledDisabledCalendarRowItem testID="calendar-disabled-cell" />
|
|
210
|
+
)
|
|
211
|
+
)}
|
|
212
|
+
</StyledCalendarRow>
|
|
213
|
+
</StyledContainer>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export default Calendar;
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from './StyledCollapse';
|
|
14
14
|
import { usePrevious } from '../../utils/hooks';
|
|
15
15
|
|
|
16
|
-
interface CollapseProps extends ViewProps {
|
|
16
|
+
export interface CollapseProps extends ViewProps {
|
|
17
17
|
/*
|
|
18
18
|
* The collapse's content.
|
|
19
19
|
*/
|
|
@@ -39,35 +39,33 @@ const Collapse = ({ open = false, children, testID, style }: CollapseProps) => {
|
|
|
39
39
|
const previousOpenState = usePrevious(open);
|
|
40
40
|
|
|
41
41
|
useEffect(() => {
|
|
42
|
-
if (
|
|
42
|
+
if (
|
|
43
|
+
(open !== previousOpenState && previousOpenState !== undefined) ||
|
|
44
|
+
open
|
|
45
|
+
) {
|
|
43
46
|
Animated.timing(collapseAnim, {
|
|
44
47
|
toValue: open ? contentHeight : 0,
|
|
45
|
-
easing: Easing.inOut(Easing.
|
|
48
|
+
easing: Easing.inOut(Easing.cubic),
|
|
46
49
|
useNativeDriver: false,
|
|
47
50
|
}).start();
|
|
48
51
|
}
|
|
49
52
|
}, [open, previousOpenState, contentHeight]);
|
|
50
53
|
|
|
51
|
-
const fetchMaxHeight = useCallback(
|
|
52
|
-
(
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
[contentHeight]
|
|
56
|
-
);
|
|
54
|
+
const fetchMaxHeight = useCallback(({ height }) => {
|
|
55
|
+
setContentHeight(height);
|
|
56
|
+
}, []);
|
|
57
57
|
|
|
58
58
|
return (
|
|
59
59
|
<StyledWrapper
|
|
60
|
-
style={
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
height: collapseAnim,
|
|
64
|
-
},
|
|
65
|
-
]}
|
|
60
|
+
style={{
|
|
61
|
+
height: collapseAnim,
|
|
62
|
+
}}
|
|
66
63
|
testID={testID}
|
|
67
64
|
>
|
|
68
65
|
<StyledHiddenWrapper>
|
|
69
66
|
<StyledChildWrapper
|
|
70
67
|
onLayout={event => fetchMaxHeight(event.nativeEvent.layout)}
|
|
68
|
+
style={style}
|
|
71
69
|
>
|
|
72
70
|
{children}
|
|
73
71
|
</StyledChildWrapper>
|
|
@@ -28,6 +28,10 @@ interface ContentNavigatorProps {
|
|
|
28
28
|
* Whether the next icon is disabled.
|
|
29
29
|
*/
|
|
30
30
|
nextDisabled?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Content font size.
|
|
33
|
+
*/
|
|
34
|
+
fontSize?: 'medium' | 'large';
|
|
31
35
|
/**
|
|
32
36
|
* Additional style.
|
|
33
37
|
*/
|
|
@@ -45,6 +49,7 @@ function ContentNavigator({
|
|
|
45
49
|
value,
|
|
46
50
|
previousDisabled = false,
|
|
47
51
|
nextDisabled = false,
|
|
52
|
+
fontSize = 'medium',
|
|
48
53
|
testID,
|
|
49
54
|
style,
|
|
50
55
|
}: ContentNavigatorProps) {
|
|
@@ -58,6 +63,7 @@ function ContentNavigator({
|
|
|
58
63
|
/>
|
|
59
64
|
<Value
|
|
60
65
|
fontWeight="semi-bold"
|
|
66
|
+
fontSize={fontSize}
|
|
61
67
|
onPress={onPress}
|
|
62
68
|
testID="content-navigator-value"
|
|
63
69
|
>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import DateTimePicker from '@react-native-community/datetimepicker';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { TouchableOpacity, View } from 'react-native';
|
|
4
|
+
import formatDate from 'date-fns/fp/format';
|
|
5
|
+
|
|
6
|
+
import TextInput from '../TextInput';
|
|
7
|
+
import { DatePickerProps } from './types';
|
|
8
|
+
|
|
9
|
+
const DatePickerAndroid = ({
|
|
10
|
+
value,
|
|
11
|
+
label,
|
|
12
|
+
placeholder,
|
|
13
|
+
onChange,
|
|
14
|
+
displayFormat = 'dd/MM/yyyy',
|
|
15
|
+
disabled = false,
|
|
16
|
+
required,
|
|
17
|
+
error,
|
|
18
|
+
style,
|
|
19
|
+
testID,
|
|
20
|
+
}: DatePickerProps) => {
|
|
21
|
+
const [open, setOpen] = useState(false);
|
|
22
|
+
|
|
23
|
+
const displayValue = value ? formatDate(displayFormat, value) : '';
|
|
24
|
+
const pickerInitValue = value || new Date();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<TouchableOpacity onPress={() => setOpen(true)} disabled={disabled}>
|
|
28
|
+
<View pointerEvents="none" testID="datePickerInputAndroid">
|
|
29
|
+
<TextInput
|
|
30
|
+
label={label}
|
|
31
|
+
value={displayValue}
|
|
32
|
+
suffix="calendar-dates-outlined"
|
|
33
|
+
placeholder={placeholder || displayFormat}
|
|
34
|
+
disabled={disabled}
|
|
35
|
+
error={error}
|
|
36
|
+
required={required}
|
|
37
|
+
style={style}
|
|
38
|
+
testID={testID}
|
|
39
|
+
/>
|
|
40
|
+
</View>
|
|
41
|
+
{open ? (
|
|
42
|
+
<DateTimePicker
|
|
43
|
+
testID="datePickerAndroid"
|
|
44
|
+
mode="date"
|
|
45
|
+
value={pickerInitValue}
|
|
46
|
+
display="default"
|
|
47
|
+
onChange={(_: any, date: Date | undefined) => {
|
|
48
|
+
setOpen(false);
|
|
49
|
+
if (date) {
|
|
50
|
+
onChange(date);
|
|
51
|
+
}
|
|
52
|
+
}}
|
|
53
|
+
/>
|
|
54
|
+
) : null}
|
|
55
|
+
</TouchableOpacity>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default DatePickerAndroid;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import DateTimePicker from '@react-native-community/datetimepicker';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { TouchableOpacity, View } from 'react-native';
|
|
4
|
+
import formatDate from 'date-fns/fp/format';
|
|
5
|
+
|
|
6
|
+
import BottomSheet from '../BottomSheet';
|
|
7
|
+
import TextInput from '../TextInput';
|
|
8
|
+
import Typography from '../Typography';
|
|
9
|
+
import { StyledPickerWrapper } from './StyledDatePicker';
|
|
10
|
+
import { DatePickerProps } from './types';
|
|
11
|
+
|
|
12
|
+
const DatePickerIOS = ({
|
|
13
|
+
value,
|
|
14
|
+
label,
|
|
15
|
+
placeholder,
|
|
16
|
+
onChange,
|
|
17
|
+
confirmLabel,
|
|
18
|
+
displayFormat = 'dd/MM/yyyy',
|
|
19
|
+
disabled = false,
|
|
20
|
+
required,
|
|
21
|
+
error,
|
|
22
|
+
style,
|
|
23
|
+
testID,
|
|
24
|
+
}: DatePickerProps) => {
|
|
25
|
+
const [selectingDate, setSelectingDate] = useState<Date | undefined>(value);
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
|
|
28
|
+
const displayValue = value ? formatDate(displayFormat, value) : '';
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<TouchableOpacity onPress={() => setOpen(true)} disabled={disabled}>
|
|
32
|
+
<View pointerEvents="none" testID="datePickerInputIOS">
|
|
33
|
+
<TextInput
|
|
34
|
+
label={label}
|
|
35
|
+
value={displayValue}
|
|
36
|
+
suffix="calendar-dates-outlined"
|
|
37
|
+
placeholder={placeholder || displayFormat}
|
|
38
|
+
disabled={disabled}
|
|
39
|
+
error={error}
|
|
40
|
+
required={required}
|
|
41
|
+
testID={testID}
|
|
42
|
+
style={style}
|
|
43
|
+
/>
|
|
44
|
+
</View>
|
|
45
|
+
<BottomSheet
|
|
46
|
+
open={open}
|
|
47
|
+
onRequestClose={() => setOpen(false)}
|
|
48
|
+
header={label}
|
|
49
|
+
footer={
|
|
50
|
+
<TouchableOpacity
|
|
51
|
+
onPress={() => {
|
|
52
|
+
if (selectingDate) {
|
|
53
|
+
onChange(selectingDate);
|
|
54
|
+
}
|
|
55
|
+
setOpen(false);
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
<Typography.Text
|
|
59
|
+
fontSize="large"
|
|
60
|
+
fontWeight="semi-bold"
|
|
61
|
+
intent="primary"
|
|
62
|
+
>
|
|
63
|
+
{confirmLabel}
|
|
64
|
+
</Typography.Text>
|
|
65
|
+
</TouchableOpacity>
|
|
66
|
+
}
|
|
67
|
+
>
|
|
68
|
+
<StyledPickerWrapper>
|
|
69
|
+
<DateTimePicker
|
|
70
|
+
testID="datePickerIOS"
|
|
71
|
+
value={selectingDate || new Date()}
|
|
72
|
+
mode="date"
|
|
73
|
+
onChange={(_: any, date: Date | undefined) => {
|
|
74
|
+
if (date) {
|
|
75
|
+
setSelectingDate(date);
|
|
76
|
+
}
|
|
77
|
+
}}
|
|
78
|
+
display="spinner"
|
|
79
|
+
style={{ flex: 1 }}
|
|
80
|
+
/>
|
|
81
|
+
</StyledPickerWrapper>
|
|
82
|
+
</BottomSheet>
|
|
83
|
+
</TouchableOpacity>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default DatePickerIOS;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import DatePicker from '..';
|
|
4
|
+
import renderWithTheme from '../../../testHelpers/renderWithTheme';
|
|
5
|
+
|
|
6
|
+
describe('DatePicker', () => {
|
|
7
|
+
it('renders DatePickerIOS when OS is iOS', () => {
|
|
8
|
+
Platform.OS = 'ios';
|
|
9
|
+
const { getByTestId } = renderWithTheme(
|
|
10
|
+
<DatePicker
|
|
11
|
+
label="Start date"
|
|
12
|
+
value={new Date('December 17, 1995')}
|
|
13
|
+
confirmLabel="Confirm"
|
|
14
|
+
onChange={jest.fn()}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(getByTestId('datePickerInputIOS')).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('renders DatePickerAndroid when OS is android', () => {
|
|
22
|
+
Platform.OS = 'android';
|
|
23
|
+
const { getByTestId } = renderWithTheme(
|
|
24
|
+
<DatePicker
|
|
25
|
+
label="Start date"
|
|
26
|
+
value={new Date('December 17, 1995')}
|
|
27
|
+
confirmLabel="Confirm"
|
|
28
|
+
onChange={jest.fn()}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(getByTestId('datePickerInputAndroid')).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { fireEvent } from '@testing-library/react-native';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import renderWithTheme from '../../../testHelpers/renderWithTheme';
|
|
4
|
+
import DatePickerAndroid from '../DatePickerAndroid';
|
|
5
|
+
|
|
6
|
+
describe('DatePickerAndroid', () => {
|
|
7
|
+
it('renders correctly', () => {
|
|
8
|
+
const onChange = jest.fn();
|
|
9
|
+
|
|
10
|
+
const { getByText, queryByTestId, toJSON } = renderWithTheme(
|
|
11
|
+
<DatePickerAndroid
|
|
12
|
+
value={new Date('December 21, 1995')}
|
|
13
|
+
label="Start date"
|
|
14
|
+
confirmLabel="Confirm"
|
|
15
|
+
onChange={onChange}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
expect(getByText('Start date')).toBeDefined();
|
|
20
|
+
expect(queryByTestId('text-input').props.value).toBe('21/12/1995');
|
|
21
|
+
expect(queryByTestId('datePickerAndroid')).toBeNull();
|
|
22
|
+
|
|
23
|
+
// Open date picker
|
|
24
|
+
fireEvent.press(getByText('Start date'));
|
|
25
|
+
expect(queryByTestId('datePickerAndroid')).toBeTruthy();
|
|
26
|
+
|
|
27
|
+
expect(toJSON()).toMatchSnapshot();
|
|
28
|
+
|
|
29
|
+
// Change date
|
|
30
|
+
fireEvent(
|
|
31
|
+
queryByTestId('datePickerAndroid'),
|
|
32
|
+
'onChange',
|
|
33
|
+
null,
|
|
34
|
+
new Date('December 17, 1995')
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(onChange).toBeCalledWith(new Date('December 17, 1995'));
|
|
38
|
+
});
|
|
39
|
+
});
|