@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.
Files changed (185) hide show
  1. package/.eslintrc.json +3 -1
  2. package/.turbo/turbo-build.log +3 -2
  3. package/assets/fonts/hero-icons.ttf +0 -0
  4. package/babel.config.js +16 -0
  5. package/es/index.js +35840 -16325
  6. package/lib/assets/fonts/hero-icons.ttf +0 -0
  7. package/lib/index.js +35847 -16327
  8. package/package.json +9 -4
  9. package/rollup.config.js +1 -0
  10. package/src/components/Accordion/AccordionItem.tsx +50 -0
  11. package/src/components/Accordion/StyledAccordion.tsx +29 -0
  12. package/src/components/Accordion/__tests__/AccordionItem.spec.tsx +56 -0
  13. package/src/components/Accordion/__tests__/StyledAccordion.spec.tsx +17 -0
  14. package/src/components/Accordion/__tests__/__snapshots__/AccordionItem.spec.tsx.snap +529 -0
  15. package/src/components/Accordion/__tests__/__snapshots__/StyledAccordion.spec.tsx.snap +33 -0
  16. package/src/components/Accordion/__tests__/__snapshots__/index.spec.tsx.snap +822 -0
  17. package/src/components/Accordion/__tests__/index.spec.tsx +54 -0
  18. package/src/components/Accordion/index.tsx +82 -0
  19. package/src/components/Accordion/utils.tsx +11 -0
  20. package/src/components/Button/Button.tsx +64 -60
  21. package/src/components/Button/IconButton.tsx +1 -1
  22. package/src/components/Button/StyledButton.tsx +4 -6
  23. package/src/components/Button/__tests__/StyledButton.spec.tsx +11 -4
  24. package/src/components/Button/__tests__/__snapshots__/StyledButton.spec.tsx.snap +312 -78
  25. package/src/components/Calendar/CalendarRowItem.tsx +54 -0
  26. package/src/components/Calendar/StyledCalendar.tsx +76 -0
  27. package/src/components/Calendar/__tests__/CalendarRowItem.spec.tsx +76 -0
  28. package/src/components/Calendar/__tests__/__snapshots__/CalendarRowItem.spec.tsx.snap +411 -0
  29. package/src/components/Calendar/__tests__/helper.spec.ts +50 -0
  30. package/src/components/Calendar/__tests__/index.spec.tsx +99 -0
  31. package/src/components/Calendar/helpers.ts +29 -0
  32. package/src/components/Calendar/index.tsx +217 -0
  33. package/src/components/Collapse/index.tsx +13 -15
  34. package/src/components/ContentNavigator/index.tsx +6 -0
  35. package/src/components/DatePicker/DatePickerAndroid.tsx +59 -0
  36. package/src/components/DatePicker/DatePickerIOS.tsx +87 -0
  37. package/src/components/DatePicker/StyledDatePicker.tsx +8 -0
  38. package/src/components/DatePicker/__tests__/DatePicker.spec.tsx +34 -0
  39. package/src/components/DatePicker/__tests__/DatePickerAndroid.spec.tsx +39 -0
  40. package/src/components/DatePicker/__tests__/DatePickerIOS.spec.tsx +46 -0
  41. package/src/components/DatePicker/__tests__/__snapshots__/DatePickerAndroid.spec.tsx.snap +199 -0
  42. package/src/components/DatePicker/__tests__/__snapshots__/DatePickerIOS.spec.tsx.snap +513 -0
  43. package/src/components/DatePicker/index.tsx +15 -0
  44. package/src/components/DatePicker/types.ts +49 -0
  45. package/src/components/Empty/StyledEmpty.tsx +47 -0
  46. package/src/components/Empty/__tests__/__snapshots__/index.spec.tsx.snap +66 -0
  47. package/src/components/Empty/__tests__/index.spec.tsx +17 -0
  48. package/src/components/Empty/index.tsx +53 -0
  49. package/src/components/FAB/ActionGroup/ActionItem.tsx +6 -2
  50. package/src/components/FAB/ActionGroup/StyledActionGroup.tsx +1 -0
  51. package/src/components/FAB/ActionGroup/StyledActionItem.tsx +7 -1
  52. package/src/components/FAB/ActionGroup/__tests__/__snapshots__/index.spec.tsx.snap +84 -22
  53. package/src/components/FAB/ActionGroup/index.tsx +8 -1
  54. package/src/components/Icon/HeroIcon/selection.json +1 -1
  55. package/src/components/Icon/IconList.ts +13 -0
  56. package/src/components/List/BasicListItem.tsx +44 -34
  57. package/src/components/List/ListItem.tsx +67 -58
  58. package/src/components/List/StyledBasicListItem.tsx +2 -3
  59. package/src/components/List/StyledListItem.tsx +2 -2
  60. package/src/components/List/__tests__/StyledBasicListItem.spec.tsx +5 -2
  61. package/src/components/List/__tests__/StyledListItem.spec.tsx +4 -1
  62. package/src/components/List/__tests__/__snapshots__/BasicListItem.spec.tsx.snap +15 -10
  63. package/src/components/List/__tests__/__snapshots__/ListItem.spec.tsx.snap +52 -32
  64. package/src/components/List/__tests__/__snapshots__/StyledBasicListItem.spec.tsx.snap +128 -48
  65. package/src/components/List/__tests__/__snapshots__/StyledListItem.spec.tsx.snap +132 -52
  66. package/src/components/RichTextEditor/EditorEvent.ts +7 -0
  67. package/src/components/RichTextEditor/EditorToolbar.tsx +220 -0
  68. package/src/components/RichTextEditor/MentionList.tsx +69 -0
  69. package/src/components/RichTextEditor/RichTextEditor.tsx +396 -0
  70. package/src/components/RichTextEditor/StyledRichTextEditor.ts +20 -0
  71. package/src/components/RichTextEditor/StyledToolbar.ts +32 -0
  72. package/src/components/RichTextEditor/__tests__/EditorToolbar.spec.tsx +130 -0
  73. package/src/components/RichTextEditor/__tests__/MentionList.spec.tsx +109 -0
  74. package/src/components/RichTextEditor/__tests__/RichTextEditor.spec.tsx +245 -0
  75. package/src/components/RichTextEditor/__tests__/__snapshots__/EditorToolbar.spec.tsx.snap +324 -0
  76. package/src/components/RichTextEditor/__tests__/__snapshots__/MentionList.spec.tsx.snap +45 -0
  77. package/src/components/RichTextEditor/__tests__/__snapshots__/RichTextEditor.spec.tsx.snap +526 -0
  78. package/src/components/RichTextEditor/constants.ts +20 -0
  79. package/src/components/RichTextEditor/hero-editor.d.ts +8 -0
  80. package/src/components/RichTextEditor/index.tsx +8 -0
  81. package/src/components/RichTextEditor/utils/events.ts +31 -0
  82. package/src/components/RichTextEditor/utils/rnWebView.ts +19 -0
  83. package/src/components/SectionHeading/__tests__/__snapshots__/index.spec.tsx.snap +77 -0
  84. package/src/components/SectionHeading/__tests__/index.spec.tsx +14 -0
  85. package/src/components/SectionHeading/index.tsx +16 -9
  86. package/src/components/Tag/StyledTag.tsx +12 -2
  87. package/src/components/Tag/__tests__/Tag.spec.tsx +35 -8
  88. package/src/components/Tag/__tests__/__snapshots__/Tag.spec.tsx.snap +118 -4
  89. package/src/components/Tag/index.tsx +9 -2
  90. package/src/components/TextInput/__tests__/__snapshots__/StyledTextInput.spec.tsx.snap +1 -0
  91. package/src/components/TimePicker/TimePickerIOS.tsx +1 -1
  92. package/src/components/TimePicker/types.ts +1 -1
  93. package/src/components/Typography/Text/StyledText.tsx +2 -1
  94. package/src/components/Typography/Text/__tests__/StyledText.spec.tsx +1 -0
  95. package/src/components/Typography/Text/__tests__/__snapshots__/StyledText.spec.tsx.snap +22 -0
  96. package/src/components/Typography/Text/index.tsx +2 -1
  97. package/src/index.ts +10 -0
  98. package/src/theme/__tests__/__snapshots__/index.spec.ts.snap +119 -4
  99. package/src/theme/components/accordion.ts +19 -0
  100. package/src/theme/components/button.ts +12 -0
  101. package/src/theme/components/calendar.ts +34 -0
  102. package/src/theme/components/card.ts +1 -1
  103. package/src/theme/components/datePicker.ts +11 -0
  104. package/src/theme/components/empty.ts +38 -0
  105. package/src/theme/components/fab.ts +4 -3
  106. package/src/theme/components/list.ts +1 -0
  107. package/src/theme/components/pinInput.ts +1 -1
  108. package/src/theme/components/richTextEditor.ts +34 -0
  109. package/src/theme/components/tag.ts +8 -2
  110. package/src/theme/components/typography.ts +1 -0
  111. package/src/theme/global/borders.ts +6 -6
  112. package/src/theme/global/colors.ts +5 -1
  113. package/src/theme/index.ts +15 -0
  114. package/testUtils/setup.tsx +17 -0
  115. package/types/components/Accordion/AccordionItem.d.ts +14 -0
  116. package/types/components/Accordion/StyledAccordion.d.ts +32 -0
  117. package/types/components/Accordion/__tests__/AccordionItem.spec.d.ts +1 -0
  118. package/types/components/Accordion/__tests__/StyledAccordion.spec.d.ts +1 -0
  119. package/types/components/Accordion/__tests__/index.spec.d.ts +1 -0
  120. package/types/components/Accordion/index.d.ts +38 -0
  121. package/types/components/Accordion/utils.d.ts +1 -0
  122. package/types/components/Button/IconButton.d.ts +1 -1
  123. package/types/components/Button/StyledButton.d.ts +3 -3
  124. package/types/components/Calendar/CalendarRowItem.d.ts +10 -0
  125. package/types/components/Calendar/StyledCalendar.d.ts +54 -0
  126. package/types/components/Calendar/__tests__/CalendarRowItem.spec.d.ts +1 -0
  127. package/types/components/Calendar/__tests__/helper.spec.d.ts +1 -0
  128. package/types/components/Calendar/__tests__/index.spec.d.ts +1 -0
  129. package/types/components/Calendar/helpers.d.ts +3 -0
  130. package/types/components/Calendar/index.d.ts +40 -0
  131. package/types/components/Collapse/index.d.ts +1 -1
  132. package/types/components/ContentNavigator/index.d.ts +5 -1
  133. package/types/components/DatePicker/DatePickerAndroid.d.ts +3 -0
  134. package/types/components/DatePicker/DatePickerIOS.d.ts +3 -0
  135. package/types/components/DatePicker/StyledDatePicker.d.ts +8 -0
  136. package/types/components/DatePicker/__tests__/DatePicker.spec.d.ts +1 -0
  137. package/types/components/DatePicker/__tests__/DatePickerAndroid.spec.d.ts +1 -0
  138. package/types/components/DatePicker/__tests__/DatePickerIOS.spec.d.ts +1 -0
  139. package/types/components/DatePicker/index.d.ts +3 -0
  140. package/types/components/DatePicker/types.d.ts +48 -0
  141. package/types/components/Empty/StyledEmpty.d.ts +31 -0
  142. package/types/components/Empty/__tests__/index.spec.d.ts +1 -0
  143. package/types/components/Empty/index.d.ts +26 -0
  144. package/types/components/FAB/ActionGroup/StyledActionItem.d.ts +6 -1
  145. package/types/components/FAB/ActionGroup/index.d.ts +6 -1
  146. package/types/components/FAB/index.d.ts +1 -1
  147. package/types/components/Icon/IconList.d.ts +1 -1
  148. package/types/components/Icon/utils.d.ts +1 -1
  149. package/types/components/List/StyledBasicListItem.d.ts +3 -3
  150. package/types/components/List/StyledListItem.d.ts +3 -3
  151. package/types/components/RichTextEditor/EditorEvent.d.ts +3 -0
  152. package/types/components/RichTextEditor/EditorToolbar.d.ts +17 -0
  153. package/types/components/RichTextEditor/MentionList.d.ts +12 -0
  154. package/types/components/RichTextEditor/RichTextEditor.d.ts +65 -0
  155. package/types/components/RichTextEditor/StyledRichTextEditor.d.ts +16 -0
  156. package/types/components/RichTextEditor/StyledToolbar.d.ts +21 -0
  157. package/types/components/RichTextEditor/__tests__/EditorToolbar.spec.d.ts +1 -0
  158. package/types/components/RichTextEditor/__tests__/MentionList.spec.d.ts +1 -0
  159. package/types/components/RichTextEditor/__tests__/RichTextEditor.spec.d.ts +1 -0
  160. package/types/components/RichTextEditor/constants.d.ts +19 -0
  161. package/types/components/RichTextEditor/index.d.ts +5 -0
  162. package/types/components/RichTextEditor/utils/events.d.ts +8 -0
  163. package/types/components/RichTextEditor/utils/rnWebView.d.ts +7 -0
  164. package/types/components/SectionHeading/index.d.ts +2 -2
  165. package/types/components/Select/MultiSelect/OptionList.d.ts +1 -1
  166. package/types/components/Select/SingleSelect/OptionList.d.ts +1 -1
  167. package/types/components/Tag/StyledTag.d.ts +1 -1
  168. package/types/components/Tag/index.d.ts +1 -1
  169. package/types/components/TimePicker/types.d.ts +1 -1
  170. package/types/components/Typography/Text/StyledText.d.ts +1 -1
  171. package/types/components/Typography/Text/index.d.ts +1 -1
  172. package/types/index.d.ts +6 -1
  173. package/types/theme/components/accordion.d.ts +13 -0
  174. package/types/theme/components/button.d.ts +12 -0
  175. package/types/theme/components/calendar.d.ts +26 -0
  176. package/types/theme/components/datePicker.d.ts +6 -0
  177. package/types/theme/components/empty.d.ts +28 -0
  178. package/types/theme/components/fab.d.ts +1 -0
  179. package/types/theme/components/list.d.ts +1 -0
  180. package/types/theme/components/richTextEditor.d.ts +26 -0
  181. package/types/theme/components/tag.d.ts +8 -2
  182. package/types/theme/components/typography.d.ts +1 -0
  183. package/types/theme/global/colors.d.ts +5 -1
  184. package/types/theme/global/index.d.ts +5 -1
  185. 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 (open !== previousOpenState && previousOpenState !== undefined) {
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.ease),
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
- ({ height }) => {
53
- setContentHeight(height);
54
- },
55
- [contentHeight]
56
- );
54
+ const fetchMaxHeight = useCallback(({ height }) => {
55
+ setContentHeight(height);
56
+ }, []);
57
57
 
58
58
  return (
59
59
  <StyledWrapper
60
- style={[
61
- style,
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,8 @@
1
+ import styled from '@emotion/native';
2
+ import { View, ViewProps } from 'react-native';
3
+
4
+ const StyledPickerWrapper = styled(View)<ViewProps>(({ theme }) => ({
5
+ height: theme.__hd__.datePicker.sizes.height,
6
+ }));
7
+
8
+ export { StyledPickerWrapper };
@@ -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
+ });