cronofy-elements 1.39.2 → 1.41.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/build/CronofyElements.v1.41.0.js +2 -0
- package/build/{CronofyElements.v1.39.2.js.LICENSE.txt → CronofyElements.v1.41.0.js.LICENSE.txt} +0 -0
- package/build/npm/CronofyElements.js +2 -2
- package/demo/availability-viewer.ejs +8 -0
- package/demo/date-time-picker.ejs +2 -0
- package/package.json +1 -1
- package/src/js/components/AvailabilityViewer/AvailabilityViewer.js +14 -15
- package/src/js/components/AvailabilityViewer/Navigation.js +23 -37
- package/src/js/components/AvailabilityViewer/WeekWrapper.js +3 -8
- package/src/js/components/AvailabilityViewer/contexts/page-context.js +17 -0
- package/src/js/components/AvailabilityViewer/contexts/page-reducer.js +28 -0
- package/src/js/components/DateTimePicker/Calendar.js +8 -6
- package/src/js/components/DateTimePicker/CalendarHeader.js +27 -14
- package/src/js/components/DateTimePicker/Confirm.js +8 -6
- package/src/js/components/DateTimePicker/DateTimePicker.js +49 -29
- package/src/js/components/DateTimePicker/DayButton.js +3 -1
- package/src/js/components/DateTimePicker/Details.js +5 -8
- package/src/js/components/DateTimePicker/SlotButton.js +6 -6
- package/src/js/components/DateTimePicker/TimeZoneSelector.js +25 -29
- package/src/js/components/DateTimePicker/Wrapper.js +89 -39
- package/src/js/components/DateTimePicker/contexts/status-context.js +3 -3
- package/src/js/components/DateTimePicker/contexts/status-reducer.js +90 -74
- package/src/js/components/DateTimePicker/contexts/tz-context.js +18 -0
- package/src/js/components/DateTimePicker/utils/slots.js +11 -4
- package/src/js/helpers/init.DateTimePicker.js +61 -2
- package/src/js/helpers/utils.AvailabilityViewer.js +7 -2
- package/src/js/helpers/utils.js +7 -1
- package/src/js/main.js +17 -1
- package/tests/AvailabilityViewer/Navigation.test.js +130 -0
- package/tests/AvailabilityViewer/contexts/page-reducer.test.js +87 -0
- package/tests/CalendarSync/Active.test.js +1 -1
- package/tests/CalendarSync/AddToggle.test.js +1 -1
- package/tests/CalendarSync/EditToggle.test.js +1 -1
- package/tests/CalendarSync/Inactive.test.js +1 -1
- package/tests/CalendarSync/Pending.test.js +1 -1
- package/tests/DateTimePicker/SlotButton.test.js +16 -5
- package/tests/DateTimePicker/contexts/status-reducer.test.js +277 -322
- package/tests/components/main.test.js +8 -1
- package/tests/{CalendarSync/mocks → mocks}/i18n.js +0 -0
- package/tests/mocks/theme.js +3 -0
- package/tests/utils.AvailabilityViewer.test.js +8 -0
- package/build/CronofyElements.v1.39.2.js +0 -2
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState } from "react";
|
|
2
|
+
|
|
3
|
+
const TzContext = createContext();
|
|
4
|
+
|
|
5
|
+
export const TzProvider = ({ children, options }) => {
|
|
6
|
+
const [tz, setTz] = useState({
|
|
7
|
+
selectedTzid: options.selectedTzid,
|
|
8
|
+
list: options.list,
|
|
9
|
+
});
|
|
10
|
+
return <TzContext.Provider value={[tz, setTz]}>{children}</TzContext.Provider>;
|
|
11
|
+
};
|
|
12
|
+
export const useTz = () => {
|
|
13
|
+
const context = useContext(TzContext);
|
|
14
|
+
if (context === undefined) {
|
|
15
|
+
throw new Error("useTz must be used within a TzProvider");
|
|
16
|
+
}
|
|
17
|
+
return context;
|
|
18
|
+
};
|
|
@@ -21,7 +21,7 @@ export const getMonthsCoveredByPeriod = (period, tzid) => {
|
|
|
21
21
|
return months;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export const getMonthsFromQuery = (periods, tzid) => {
|
|
24
|
+
export const getMonthsFromQuery = (periods = [], tzid) => {
|
|
25
25
|
const months = periods
|
|
26
26
|
.map(period => getMonthsCoveredByPeriod(period, tzid))
|
|
27
27
|
// getMonthsCoveredByPeriod returns an array, so flatten them...
|
|
@@ -42,9 +42,17 @@ export const getMonthObjectsFromQuery = (query, tzid, current = false) => {
|
|
|
42
42
|
if (!query.query_periods || !query.query_periods.length) {
|
|
43
43
|
return [getCurrentMonth()];
|
|
44
44
|
}
|
|
45
|
+
|
|
45
46
|
const monthStrings = getMonthsFromQuery(query.query_periods, tzid);
|
|
46
|
-
const
|
|
47
|
-
const
|
|
47
|
+
const startMonth = moment(monthStrings[0], "YYYY-MM");
|
|
48
|
+
const endMonth = moment(monthStrings[monthStrings.length - 1], "YYYY-MM");
|
|
49
|
+
|
|
50
|
+
const currentMonth =
|
|
51
|
+
current && moment(current, "YYYY-MM").isBetween(startMonth, endMonth)
|
|
52
|
+
? current
|
|
53
|
+
: monthStrings[0];
|
|
54
|
+
|
|
55
|
+
return monthStrings.map(month => {
|
|
48
56
|
const croppedQueryPeriods = cropPeriodsByMonth(query.query_periods, month, tzid);
|
|
49
57
|
return {
|
|
50
58
|
month,
|
|
@@ -56,7 +64,6 @@ export const getMonthObjectsFromQuery = (query, tzid, current = false) => {
|
|
|
56
64
|
},
|
|
57
65
|
};
|
|
58
66
|
});
|
|
59
|
-
return monthObjects;
|
|
60
67
|
};
|
|
61
68
|
|
|
62
69
|
export const getSlots = ({ query, auth, tzid, slots = [] }) =>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import moment from "moment-timezone";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
parseConnectionDomains,
|
|
3
5
|
parseQuery,
|
|
@@ -8,6 +10,12 @@ import {
|
|
|
8
10
|
validateLocaleModifiers,
|
|
9
11
|
} from "./init";
|
|
10
12
|
import { logConstructor } from "./logging";
|
|
13
|
+
import { queryForDateTimePicker } from "./mocks";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
getMonthsFromQuery,
|
|
17
|
+
parseQuery as parseWithOverlappingSlots,
|
|
18
|
+
} from "../components/DateTimePicker/utils/slots";
|
|
11
19
|
|
|
12
20
|
export const parseDateTimePickerOptions = (options = {}) => {
|
|
13
21
|
const config = typeof options.config === "undefined" ? {} : options.config;
|
|
@@ -66,10 +74,14 @@ export const parseDateTimePickerOptions = (options = {}) => {
|
|
|
66
74
|
const isBookableEventsQuery = options.availability_query.bookable_events ? true : false;
|
|
67
75
|
|
|
68
76
|
let query;
|
|
69
|
-
if (
|
|
77
|
+
if (options.demo) {
|
|
78
|
+
query = queryForDateTimePicker;
|
|
79
|
+
} else if (isBookableEventsQuery) {
|
|
70
80
|
query = options.availability_query;
|
|
81
|
+
query = parseWithOverlappingSlots(query);
|
|
71
82
|
} else {
|
|
72
83
|
query = parseQuery({ options, elementSlug: "date-time-picker", log });
|
|
84
|
+
query = parseWithOverlappingSlots(query);
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
const tzid = parseTimezone(options.tzid, "date-time-picker", log);
|
|
@@ -93,6 +105,44 @@ export const parseDateTimePickerOptions = (options = {}) => {
|
|
|
93
105
|
});
|
|
94
106
|
}
|
|
95
107
|
|
|
108
|
+
let selectedDate = config.selected_date;
|
|
109
|
+
if (typeof selectedDate !== "undefined") {
|
|
110
|
+
const validDate = moment(selectedDate, "YYYY-MM-DD", true).isValid();
|
|
111
|
+
|
|
112
|
+
if (!validDate) {
|
|
113
|
+
log.warn(
|
|
114
|
+
`The provided date ${selectedDate} is not valid. Please ensure it's formatted like "YYYY-MM-DD". Picking the first available date as starting date.`,
|
|
115
|
+
{
|
|
116
|
+
docsSlug: "#config.start_date",
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
selectedDate = undefined;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const months = getMonthsFromQuery(query?.query_periods, tzid.tzid);
|
|
125
|
+
const selectedDateMoment = selectedDate && moment(selectedDate, "YYYY-MM-DD");
|
|
126
|
+
|
|
127
|
+
let startDateMoment = months.length
|
|
128
|
+
? moment(months[0], "YYYY-MM").startOf("month")
|
|
129
|
+
: moment().startOf("month");
|
|
130
|
+
|
|
131
|
+
if (selectedDateMoment?.isBefore(startDate)) {
|
|
132
|
+
startDateMoment = selectedDateMoment;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let endDateMoment = months.length
|
|
136
|
+
? moment(months[months.length - 1], "YYYY-MM").endOf("month")
|
|
137
|
+
: moment().endOf("month");
|
|
138
|
+
|
|
139
|
+
if (selectedDateMoment?.isAfter(endDateMoment)) {
|
|
140
|
+
endDateMoment = selectedDateMoment;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const startDate = startDateMoment.format("YYYY-MM-DD");
|
|
144
|
+
const endDate = endDateMoment.format("YYYY-MM-DD");
|
|
145
|
+
|
|
96
146
|
delete options.availability_query;
|
|
97
147
|
delete options.element_token;
|
|
98
148
|
delete options.target_id;
|
|
@@ -107,7 +157,16 @@ export const parseDateTimePickerOptions = (options = {}) => {
|
|
|
107
157
|
token,
|
|
108
158
|
domains,
|
|
109
159
|
query,
|
|
110
|
-
config: {
|
|
160
|
+
config: {
|
|
161
|
+
...config,
|
|
162
|
+
mode,
|
|
163
|
+
logs,
|
|
164
|
+
startDay,
|
|
165
|
+
selectedDate,
|
|
166
|
+
startDate,
|
|
167
|
+
endDate,
|
|
168
|
+
tzList,
|
|
169
|
+
},
|
|
111
170
|
translations,
|
|
112
171
|
log,
|
|
113
172
|
};
|
|
@@ -145,11 +145,16 @@ export const getAllWeekDays = ({ startDate, endDate, startDay = "sunday", tzid }
|
|
|
145
145
|
};
|
|
146
146
|
|
|
147
147
|
export const getWeeksInfo = weekdays => {
|
|
148
|
+
const current = 1;
|
|
149
|
+
const total = Math.ceil(weekdays.length / 7);
|
|
150
|
+
|
|
148
151
|
return {
|
|
149
152
|
set: true,
|
|
150
|
-
current: 1,
|
|
151
153
|
days: weekdays,
|
|
152
|
-
total
|
|
154
|
+
total,
|
|
155
|
+
current,
|
|
156
|
+
hasNext: current < total,
|
|
157
|
+
hasPrev: false,
|
|
153
158
|
};
|
|
154
159
|
};
|
|
155
160
|
|
package/src/js/helpers/utils.js
CHANGED
|
@@ -16,7 +16,13 @@ export const objectToArray = (object, keepKey = true) => {
|
|
|
16
16
|
export const truncateString = (str, length = 240) =>
|
|
17
17
|
str.length <= length - 2 ? str : `${str.slice(0, length)}...`;
|
|
18
18
|
|
|
19
|
-
export const uniqueItems = array =>
|
|
19
|
+
export const uniqueItems = array => {
|
|
20
|
+
if (!Array.isArray(array)) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return [...new Set(array)];
|
|
25
|
+
};
|
|
20
26
|
|
|
21
27
|
export const objectIsEmpty = obj => {
|
|
22
28
|
for (let key in obj) {
|
package/src/js/main.js
CHANGED
|
@@ -50,23 +50,39 @@ export const SlotPicker = options => {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
export const AvailabilityViewer = options => {
|
|
53
|
+
let cb;
|
|
54
|
+
|
|
53
55
|
// Make sure the correct props are passed to the component
|
|
54
56
|
const renderElement = (key, options) => (
|
|
55
57
|
<AvailabilityViewerApp
|
|
56
58
|
key={key}
|
|
57
59
|
options={{ ...globalOptionFallbacks, ...options }}
|
|
58
60
|
error={options.error}
|
|
61
|
+
eventCallback={ecb => (cb = ecb)}
|
|
59
62
|
/>
|
|
60
63
|
);
|
|
61
64
|
// Generate and render the component. The `return` is important
|
|
62
65
|
// here, as it exposes the `update` method (which can be used
|
|
63
66
|
// after the inital page-load).
|
|
64
|
-
|
|
67
|
+
|
|
68
|
+
const elementApi = generateElementAPI(
|
|
65
69
|
renderElement,
|
|
66
70
|
options,
|
|
67
71
|
parseAvailabilityViewerOptions,
|
|
68
72
|
"Availability Viewer"
|
|
69
73
|
);
|
|
74
|
+
|
|
75
|
+
if (!elementApi) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
...elementApi,
|
|
81
|
+
navigate: {
|
|
82
|
+
next: () => cb("SET_NEXT_PAGE"),
|
|
83
|
+
prev: () => cb("SET_PREV_PAGE"),
|
|
84
|
+
},
|
|
85
|
+
};
|
|
70
86
|
};
|
|
71
87
|
|
|
72
88
|
export const AvailabilityRules = options => {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, fireEvent } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import { i18n } from "../mocks/i18n";
|
|
5
|
+
import { theme } from "../mocks/theme";
|
|
6
|
+
|
|
7
|
+
import { PagesProvider } from "../../src/js/components/AvailabilityViewer/contexts/page-context";
|
|
8
|
+
import {
|
|
9
|
+
I18nContext,
|
|
10
|
+
LoggingContext,
|
|
11
|
+
StatusContext,
|
|
12
|
+
ThemeContext,
|
|
13
|
+
} from "../../src/js/components/AvailabilityViewer/AvailabilityViewer";
|
|
14
|
+
|
|
15
|
+
import Navigation from "../../src/js/components/AvailabilityViewer/Navigation";
|
|
16
|
+
|
|
17
|
+
const wrapper = ({ children, log, status, pages }) => (
|
|
18
|
+
<LoggingContext.Provider value={log ?? { warn: () => undefined }}>
|
|
19
|
+
<I18nContext.Provider value={i18n}>
|
|
20
|
+
<ThemeContext.Provider value={[theme("AvailabilityViewer")]}>
|
|
21
|
+
<StatusContext.Provider
|
|
22
|
+
value={
|
|
23
|
+
status ?? [
|
|
24
|
+
{ pagesLoaded: [], notificationCallback: () => undefined },
|
|
25
|
+
() => undefined,
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
>
|
|
29
|
+
<PagesProvider
|
|
30
|
+
{...(pages
|
|
31
|
+
? { value: pages[0], dispatch: pages[1] }
|
|
32
|
+
: { value: {}, dispatch: () => undefined })}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</PagesProvider>
|
|
36
|
+
</StatusContext.Provider>
|
|
37
|
+
</ThemeContext.Provider>
|
|
38
|
+
</I18nContext.Provider>
|
|
39
|
+
</LoggingContext.Provider>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
describe("Navigation", () => {
|
|
43
|
+
it("displays nothing if pages.total is empty", () => {
|
|
44
|
+
const pages = [{ total: 0 }];
|
|
45
|
+
|
|
46
|
+
const { container } = render(<Navigation />, { wrapper: e => wrapper({ ...e, pages }) });
|
|
47
|
+
|
|
48
|
+
expect(container).toBeEmptyDOMElement();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("displays navigation if pages.total > 0", () => {
|
|
52
|
+
const pages = [{ total: 1 }];
|
|
53
|
+
|
|
54
|
+
const { container } = render(<Navigation />, { wrapper: e => wrapper({ ...e, pages }) });
|
|
55
|
+
|
|
56
|
+
const buttonWrapper = container.querySelector(".AvailabilityViewer__navigation");
|
|
57
|
+
|
|
58
|
+
expect(buttonWrapper).toBeInTheDocument();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("disables button is hasNext is false", () => {
|
|
62
|
+
const pages = [{ total: 10, hasNext: false }];
|
|
63
|
+
|
|
64
|
+
const { container } = render(<Navigation />, { wrapper: e => wrapper({ ...e, pages }) });
|
|
65
|
+
|
|
66
|
+
const nextButton = container.querySelector(".AvailabilityViewer__button--next");
|
|
67
|
+
|
|
68
|
+
expect(nextButton).toBeDisabled();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("enables next button if hasNext is true", () => {
|
|
72
|
+
const pages = [{ total: 10, hasNext: true }];
|
|
73
|
+
|
|
74
|
+
const { container } = render(<Navigation />, { wrapper: e => wrapper({ ...e, pages }) });
|
|
75
|
+
|
|
76
|
+
const nextButton = container.querySelector(".AvailabilityViewer__button--next");
|
|
77
|
+
|
|
78
|
+
expect(nextButton).toBeEnabled();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("calls dispatch to set the next page", () => {
|
|
82
|
+
const pagesDispatch = jest.fn();
|
|
83
|
+
const pages = [{ total: 10, hasNext: true }, pagesDispatch];
|
|
84
|
+
|
|
85
|
+
const { container } = render(<Navigation />, { wrapper: e => wrapper({ ...e, pages }) });
|
|
86
|
+
|
|
87
|
+
const nextButton = container.querySelector(".AvailabilityViewer__button--next");
|
|
88
|
+
|
|
89
|
+
fireEvent.click(nextButton);
|
|
90
|
+
|
|
91
|
+
expect(pagesDispatch).toHaveBeenCalledWith({
|
|
92
|
+
type: "SET_NEXT_PAGE",
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("disables button if hasPrev is false", () => {
|
|
97
|
+
const pages = [{ total: 10, hasPrev: false }];
|
|
98
|
+
|
|
99
|
+
const { container } = render(<Navigation />, { wrapper: e => wrapper({ ...e, pages }) });
|
|
100
|
+
|
|
101
|
+
const prevButton = container.querySelector(".AvailabilityViewer__button--prev");
|
|
102
|
+
|
|
103
|
+
expect(prevButton).toBeDisabled();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("enables button if hasPrev is true", () => {
|
|
107
|
+
const pages = [{ total: 10, hasPrev: true }];
|
|
108
|
+
|
|
109
|
+
const { container } = render(<Navigation />, { wrapper: e => wrapper({ ...e, pages }) });
|
|
110
|
+
|
|
111
|
+
const prevButton = container.querySelector(".AvailabilityViewer__button--prev");
|
|
112
|
+
|
|
113
|
+
expect(prevButton).toBeEnabled();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("calls dispatch to set the prev page", () => {
|
|
117
|
+
const pagesDispatch = jest.fn();
|
|
118
|
+
const pages = [{ total: 10, hasPrev: true }, pagesDispatch];
|
|
119
|
+
|
|
120
|
+
const { container } = render(<Navigation />, { wrapper: e => wrapper({ ...e, pages }) });
|
|
121
|
+
|
|
122
|
+
const prevButton = container.querySelector(".AvailabilityViewer__button--prev");
|
|
123
|
+
|
|
124
|
+
fireEvent.click(prevButton);
|
|
125
|
+
|
|
126
|
+
expect(pagesDispatch).toHaveBeenCalledWith({
|
|
127
|
+
type: "SET_PREV_PAGE",
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { pageReducer } from "../../../src/js/components/AvailabilityViewer/contexts/page-reducer";
|
|
2
|
+
|
|
3
|
+
describe("pageReducer", () => {
|
|
4
|
+
describe("SET_PREV_PAGE", () => {
|
|
5
|
+
it("sets prev page", () => {
|
|
6
|
+
const initialState = {
|
|
7
|
+
current: 5,
|
|
8
|
+
total: 10,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const action = {
|
|
12
|
+
type: "SET_PREV_PAGE",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const actual = pageReducer(initialState, action);
|
|
16
|
+
|
|
17
|
+
expect(actual).toEqual({
|
|
18
|
+
current: 4,
|
|
19
|
+
total: 10,
|
|
20
|
+
hasNext: true,
|
|
21
|
+
hasPrev: true,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("sets page to 1 if calculated page is less than it", () => {
|
|
26
|
+
const initialState = {
|
|
27
|
+
current: 1,
|
|
28
|
+
total: 10,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const action = {
|
|
32
|
+
type: "SET_PREV_PAGE",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const actual = pageReducer(initialState, action);
|
|
36
|
+
|
|
37
|
+
expect(actual).toEqual({
|
|
38
|
+
current: 1,
|
|
39
|
+
total: 10,
|
|
40
|
+
hasNext: true,
|
|
41
|
+
hasPrev: false,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("SET_NEXT_PAGE", () => {
|
|
47
|
+
it("sets next page", () => {
|
|
48
|
+
const initialState = {
|
|
49
|
+
current: 3,
|
|
50
|
+
total: 15,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const action = {
|
|
54
|
+
type: "SET_NEXT_PAGE",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const actual = pageReducer(initialState, action);
|
|
58
|
+
|
|
59
|
+
expect(actual).toEqual({
|
|
60
|
+
current: 4,
|
|
61
|
+
total: 15,
|
|
62
|
+
hasNext: true,
|
|
63
|
+
hasPrev: true,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("sets page to the total if calculated page is greater than it", () => {
|
|
68
|
+
const initialState = {
|
|
69
|
+
current: 15,
|
|
70
|
+
total: 15,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const action = {
|
|
74
|
+
type: "SET_NEXT_PAGE",
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const actual = pageReducer(initialState, action);
|
|
78
|
+
|
|
79
|
+
expect(actual).toEqual({
|
|
80
|
+
current: 15,
|
|
81
|
+
total: 15,
|
|
82
|
+
hasNext: false,
|
|
83
|
+
hasPrev: true,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { render } from "@testing-library/react";
|
|
3
3
|
import "@testing-library/jest-dom";
|
|
4
4
|
|
|
5
|
-
import { i18n } from "
|
|
5
|
+
import { i18n } from "../mocks/i18n";
|
|
6
6
|
import { theme } from "./mocks/theme";
|
|
7
7
|
import { I18nContext, ThemeContext } from "../../src/js/components/CalendarSync/CalendarSync";
|
|
8
8
|
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
ThemeContext,
|
|
6
6
|
} from "../../src/js/components/CalendarSync/CalendarSync";
|
|
7
7
|
|
|
8
|
-
import { i18n } from "
|
|
8
|
+
import { i18n } from "../mocks/i18n";
|
|
9
9
|
import { theme } from "./mocks/theme";
|
|
10
10
|
|
|
11
11
|
import AddToggle from "../../src/js/components/CalendarSync/AddToggle";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
ThemeContext,
|
|
6
6
|
} from "../../src/js/components/CalendarSync/CalendarSync";
|
|
7
7
|
|
|
8
|
-
import { i18n } from "
|
|
8
|
+
import { i18n } from "../mocks/i18n";
|
|
9
9
|
import { theme } from "./mocks/theme";
|
|
10
10
|
|
|
11
11
|
import EditToggle from "../../src/js/components/CalendarSync/EditToggle";
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { render } from "@testing-library/react";
|
|
3
3
|
import "@testing-library/jest-dom";
|
|
4
4
|
|
|
5
|
-
import { i18n } from "
|
|
5
|
+
import { i18n } from "../mocks/i18n";
|
|
6
6
|
import { theme } from "./mocks/theme";
|
|
7
7
|
import { I18nContext, ThemeContext } from "../../src/js/components/CalendarSync/CalendarSync";
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { render } from "@testing-library/react";
|
|
3
3
|
import "@testing-library/jest-dom";
|
|
4
4
|
|
|
5
|
-
import { i18n } from "
|
|
5
|
+
import { i18n } from "../mocks/i18n";
|
|
6
6
|
import { theme } from "./mocks/theme";
|
|
7
7
|
import { I18nContext, ThemeContext } from "../../src/js/components/CalendarSync/CalendarSync";
|
|
8
8
|
|
|
@@ -7,6 +7,7 @@ import SlotButton from "../../src/js/components/DateTimePicker/SlotButton";
|
|
|
7
7
|
import { I18nProvider } from "../../src/js/contexts/i18n-context";
|
|
8
8
|
import { ThemeProvider } from "../../src/js/components/DateTimePicker/contexts/theme-context";
|
|
9
9
|
import { StatusProvider } from "../../src/js/components/DateTimePicker/contexts/status-context";
|
|
10
|
+
import { TzProvider } from "../../src/js/components/DateTimePicker/contexts/tz-context";
|
|
10
11
|
|
|
11
12
|
const wrapper = ({ children, status }) => (
|
|
12
13
|
<ThemeProvider options={{ name: "DTP" }}>
|
|
@@ -17,17 +18,27 @@ const wrapper = ({ children, status }) => (
|
|
|
17
18
|
tzid: "Europe/London",
|
|
18
19
|
}}
|
|
19
20
|
>
|
|
20
|
-
<
|
|
21
|
+
<TzProvider
|
|
21
22
|
options={{
|
|
22
|
-
selected: false,
|
|
23
23
|
selectedTzid: {
|
|
24
24
|
tzid: "Europe/London",
|
|
25
|
+
offset: "+01:00",
|
|
26
|
+
offsetMins: 60,
|
|
27
|
+
name: "London",
|
|
28
|
+
abbr: "BST",
|
|
25
29
|
},
|
|
26
|
-
|
|
30
|
+
list: [],
|
|
27
31
|
}}
|
|
28
32
|
>
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
<StatusProvider
|
|
34
|
+
options={{
|
|
35
|
+
selected: false,
|
|
36
|
+
...status,
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</StatusProvider>
|
|
41
|
+
</TzProvider>
|
|
31
42
|
</I18nProvider>
|
|
32
43
|
</ThemeProvider>
|
|
33
44
|
);
|