cronofy-elements 1.46.1 → 1.48.2
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.48.2.js +2 -0
- package/build/{CronofyElements.v1.46.1.js.LICENSE.txt → CronofyElements.v1.48.2.js.LICENSE.txt} +0 -0
- package/build/npm/CronofyElements.js +2 -2
- package/demo/availability-viewer.ejs +6 -0
- package/demo/date-time-picker.ejs +4 -25
- package/package.json +1 -1
- package/src/js/components/AvailabilityRules/Wrapper.js +14 -5
- package/src/js/components/AvailabilityRules/utils/tz-utils.js +17 -0
- package/src/js/components/AvailabilityViewer/AvailabilityViewer.js +4 -0
- package/src/js/components/DateTimePicker/Confirm.js +37 -1
- package/src/js/components/DateTimePicker/DateTimePicker.js +1 -0
- package/src/js/components/DateTimePicker/SequencedSlotButton.js +37 -11
- package/src/js/components/DateTimePicker/Wrapper.js +1 -1
- package/src/js/components/DateTimePicker/contexts/status-reducer.js +2 -2
- package/src/js/components/DateTimePicker/scss/_components.confirm.scss +10 -0
- package/src/js/components/DateTimePicker/scss/_components.slotslist.scss +10 -0
- package/src/js/components/DateTimePicker/utils/slots.js +15 -2
- package/src/js/helpers/connections.js +1 -15
- package/src/js/helpers/init.DateTimePicker.js +44 -2
- package/src/js/helpers/mocks.js +97 -0
- package/tests/DateTimePicker/SequencedSlotButton.test.js +28 -1
- package/tests/DateTimePicker/contexts/status-reducer.test.js +5 -3
- package/tests/DateTimePicker/dummy-data.js +186 -1
- package/tests/DateTimePicker/utils.test.js +24 -3
- package/build/CronofyElements.v1.46.1.js +0 -2
|
@@ -115,6 +115,12 @@
|
|
|
115
115
|
// { start: offsetTime(1, "09:00"), end: offsetTime(90, "17:30") }
|
|
116
116
|
// ];
|
|
117
117
|
|
|
118
|
+
// Query period with at least one missing week in the middle
|
|
119
|
+
// availablePeriods = [
|
|
120
|
+
// { start: offsetTime(1, "09:00"), end: offsetTime(7, "17:30") },
|
|
121
|
+
// { start: offsetTime(25, "09:00"), end: offsetTime(50, "17:30") }
|
|
122
|
+
// ];
|
|
123
|
+
|
|
118
124
|
if (display.one) {
|
|
119
125
|
|
|
120
126
|
const originalQuery = {
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
sequence: [
|
|
50
50
|
{
|
|
51
51
|
sequence_id: "test",
|
|
52
|
+
sequence_title: "Test Event One",
|
|
52
53
|
ordinal: 1,
|
|
53
54
|
participants: [
|
|
54
55
|
{
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
},
|
|
67
68
|
{
|
|
68
69
|
sequence_id: "test-1",
|
|
70
|
+
sequence_title: "Test Event Two",
|
|
69
71
|
ordinal: 2,
|
|
70
72
|
participants: [
|
|
71
73
|
{
|
|
@@ -86,31 +88,7 @@
|
|
|
86
88
|
minimum: { minutes: 15 }
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
sequence_id: "test-2",
|
|
92
|
-
ordinal: 3,
|
|
93
|
-
participants: [
|
|
94
|
-
{
|
|
95
|
-
required: "all",
|
|
96
|
-
members: [
|
|
97
|
-
{
|
|
98
|
-
sub: "<%= sub %>",
|
|
99
|
-
// managed_availability: true,
|
|
100
|
-
// availability_rule_ids: ["weekly_work_hours"]
|
|
101
|
-
}
|
|
102
|
-
]
|
|
103
|
-
}
|
|
104
|
-
],
|
|
105
|
-
required_duration: { minutes: 30 },
|
|
106
|
-
start_interval: { minutes: 10 },
|
|
107
|
-
buffer: {
|
|
108
|
-
before: {
|
|
109
|
-
minimum: { minutes: 15 }
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
|
|
91
|
+
},
|
|
114
92
|
],
|
|
115
93
|
query_periods: [
|
|
116
94
|
{ start: slotTimes(01,"08:00"), end: slotTimes(31,"17:00") }
|
|
@@ -151,6 +129,7 @@
|
|
|
151
129
|
},
|
|
152
130
|
config: {
|
|
153
131
|
logs: 'info',
|
|
132
|
+
//slot_button_mode: 'detailed' //summary | detailed
|
|
154
133
|
//selected_date: "2022-05-15"
|
|
155
134
|
//mode: 'no_confirm'
|
|
156
135
|
// week_start_day: "tuesday"
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
parseAccountOptions,
|
|
15
15
|
buildRuleTemplate,
|
|
16
16
|
} from "../../helpers/utils.AvailabilityRules";
|
|
17
|
-
import { getInitialSelectedTzid } from "./utils/tz-utils";
|
|
17
|
+
import { getInitialSelectedTzid, addTzidToTzList } from "./utils/tz-utils";
|
|
18
18
|
import { parseStyleOptions, classBuilder } from "../../helpers/theming";
|
|
19
19
|
import { globals } from "../../styles/utils";
|
|
20
20
|
import { useI18n } from "../../contexts/i18n-context";
|
|
@@ -198,10 +198,19 @@ const Wrapper = ({ options }) => {
|
|
|
198
198
|
tz.list,
|
|
199
199
|
rulesResponse.availability_rule.tzid
|
|
200
200
|
);
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
if (selectedTzid) {
|
|
202
|
+
setTz({
|
|
203
|
+
list: tz.list,
|
|
204
|
+
selectedTzid: selectedTzid,
|
|
205
|
+
});
|
|
206
|
+
} else {
|
|
207
|
+
//If we get here the tzid from the availbility rule is not in the tz.list so we need to add it.
|
|
208
|
+
const result = addTzidToTzList(tz.list, rulesResponse.availability_rule.tzid);
|
|
209
|
+
setTz({
|
|
210
|
+
list: result.tzList,
|
|
211
|
+
selectedTzid: result.selectedTzid,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
205
214
|
|
|
206
215
|
setSlots(slots => {
|
|
207
216
|
const hydratedSlots = checkSlotAvailability(
|
|
@@ -42,3 +42,20 @@ export const getInitialSelectedTzid = (tzList, tzid) => {
|
|
|
42
42
|
const result = tzList.find(tz => tzid === tz.tzid);
|
|
43
43
|
return result;
|
|
44
44
|
};
|
|
45
|
+
|
|
46
|
+
export const addTzidToTzList = (tzList, tzid) => {
|
|
47
|
+
const isValidTimeZone = !!moment.tz.zone(tzid);
|
|
48
|
+
if (isValidTimeZone) {
|
|
49
|
+
const newTzList = [...tzList];
|
|
50
|
+
const item = createTzObject(tzid);
|
|
51
|
+
newTzList.push(item);
|
|
52
|
+
newTzList.sort((tzA, tzB) => tzA.offsetMins - tzB.offsetMins);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
tzList: newTzList,
|
|
56
|
+
selectedTzid: item,
|
|
57
|
+
};
|
|
58
|
+
} else {
|
|
59
|
+
throw `There was an error setting the selected tzid as ${tzid}.`;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -336,6 +336,10 @@ const AvailabilityViewer = ({ options, error, eventCallback }) => {
|
|
|
336
336
|
// If we get here, the query_periods are not within the visible window.
|
|
337
337
|
// Empty queries will have triggered a `log.warn` earlier.
|
|
338
338
|
log.info("The provided query_periods are not within the visible window");
|
|
339
|
+
// Passing an empty array to `handleNormalAvailability()`
|
|
340
|
+
// to trigger the page being updated when there is an empty week
|
|
341
|
+
// in the middle of a query period.
|
|
342
|
+
handleNormalAvailability([]);
|
|
339
343
|
}
|
|
340
344
|
if (status.slotSelection === "unrestricted" && emptyQuery) {
|
|
341
345
|
setRawData([]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import moment from "moment-timezone";
|
|
3
3
|
|
|
4
4
|
import { useI18n } from "../../contexts/i18n-context";
|
|
@@ -12,6 +12,12 @@ const Confirm = ({ confirmButtonRef }) => {
|
|
|
12
12
|
const theme = useTheme();
|
|
13
13
|
const [tz] = useTz();
|
|
14
14
|
|
|
15
|
+
const [sequenceTitlePresent, setSequenceTitlePresent] = useState(
|
|
16
|
+
() =>
|
|
17
|
+
status.selected.sequence &&
|
|
18
|
+
status.selected.sequence.every(obj => Object.keys(obj).includes("sequence_title"))
|
|
19
|
+
);
|
|
20
|
+
|
|
15
21
|
const handleCancel = () => {
|
|
16
22
|
dispatchStatus({ type: "CANCEL_SLOT_SELECTION" });
|
|
17
23
|
};
|
|
@@ -52,6 +58,36 @@ const Confirm = ({ confirmButtonRef }) => {
|
|
|
52
58
|
)}{" "}
|
|
53
59
|
{`(${moment.tz(tz.selectedTzid.tzid).format("z")})`}
|
|
54
60
|
</p>
|
|
61
|
+
{status.selected.sequence && sequenceTitlePresent && (
|
|
62
|
+
<dl className={theme.classBuilder("confirm-details--sequence")}>
|
|
63
|
+
{status.selected.sequence.map((slot, k) => (
|
|
64
|
+
<div key={k}>
|
|
65
|
+
<dt
|
|
66
|
+
className={theme.classBuilder(
|
|
67
|
+
"confirm-details--sequence-title"
|
|
68
|
+
)}
|
|
69
|
+
>
|
|
70
|
+
{slot.sequence_title}
|
|
71
|
+
</dt>
|
|
72
|
+
<dd
|
|
73
|
+
className={theme.classBuilder("confirm-details--sequence-time")}
|
|
74
|
+
>
|
|
75
|
+
{i18n.customFormatedTimeZone(
|
|
76
|
+
moment(slot.start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
77
|
+
tz.selectedTzid.tzid,
|
|
78
|
+
"LT"
|
|
79
|
+
)}
|
|
80
|
+
{" - "}
|
|
81
|
+
{i18n.customFormatedTimeZone(
|
|
82
|
+
moment(slot.end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
83
|
+
tz.selectedTzid.tzid,
|
|
84
|
+
"LT"
|
|
85
|
+
)}
|
|
86
|
+
</dd>
|
|
87
|
+
</div>
|
|
88
|
+
))}
|
|
89
|
+
</dl>
|
|
90
|
+
)}
|
|
55
91
|
</div>
|
|
56
92
|
<button
|
|
57
93
|
className={theme.classBuilder("confirm-button")}
|
|
@@ -78,6 +78,7 @@ const DateTimePicker = ({ options }) => {
|
|
|
78
78
|
availableDays: [],
|
|
79
79
|
sequenced_availability: options.query.sequence ? true : false,
|
|
80
80
|
slots: {},
|
|
81
|
+
slotButtonMode: options.config.slotButtonMode,
|
|
81
82
|
slotFetchCount: 0,
|
|
82
83
|
slotInjectionPoint: undefined,
|
|
83
84
|
tzid: options.tzid,
|
|
@@ -41,29 +41,55 @@ const SequencedSlotButton = ({ slot }) => {
|
|
|
41
41
|
}, [status.focusedSlot]);
|
|
42
42
|
|
|
43
43
|
let classStub = "time-slot";
|
|
44
|
+
if (status.slotButtonMode === "detailed") classStub = classStub + " time-slot--detailed";
|
|
44
45
|
if (status.selected.start === start) classStub = classStub + " time-slot--selected";
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
<
|
|
48
|
-
className={theme.classBuilder(classStub)}
|
|
49
|
-
onClick={() => handleSlotSelection(slot)}
|
|
50
|
-
ref={slotButtonRef}
|
|
51
|
-
>
|
|
52
|
-
<span className={theme.classBuilder("visually-hidden")}>
|
|
53
|
-
{i18n.t("select_time_slot")}
|
|
54
|
-
</span>
|
|
47
|
+
const detailedSlot = slot.map((s, i) => (
|
|
48
|
+
<li key={i} className={theme.classBuilder("detailed-slot-list--item")}>
|
|
55
49
|
{i18n.customFormatedTimeZone(
|
|
56
|
-
moment(start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
50
|
+
moment(s.start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
57
51
|
tz.selectedTzid.tzid,
|
|
58
52
|
"LT"
|
|
59
53
|
)}
|
|
60
54
|
{" - "}
|
|
61
55
|
{i18n.customFormatedTimeZone(
|
|
62
|
-
moment(end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
56
|
+
moment(s.end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
63
57
|
tz.selectedTzid.tzid,
|
|
64
58
|
"LT"
|
|
65
59
|
)}{" "}
|
|
66
60
|
{`(${moment(start, "YYYY-MM-DDTHH:mm:00Z").tz(tz.selectedTzid.tzid).format("z")})`}
|
|
61
|
+
</li>
|
|
62
|
+
));
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<button
|
|
66
|
+
className={theme.classBuilder(classStub)}
|
|
67
|
+
onClick={() => handleSlotSelection(slot)}
|
|
68
|
+
ref={slotButtonRef}
|
|
69
|
+
>
|
|
70
|
+
<span className={theme.classBuilder("visually-hidden")}>
|
|
71
|
+
{i18n.t("select_time_slot")}
|
|
72
|
+
</span>
|
|
73
|
+
{status.slotButtonMode === "detailed" ? (
|
|
74
|
+
<ul className={theme.classBuilder("detailed-slot-list")}>{detailedSlot}</ul>
|
|
75
|
+
) : (
|
|
76
|
+
<>
|
|
77
|
+
{i18n.customFormatedTimeZone(
|
|
78
|
+
moment(start, "YYYY-MM-DDTHH:mm:00Z"),
|
|
79
|
+
tz.selectedTzid.tzid,
|
|
80
|
+
"LT"
|
|
81
|
+
)}
|
|
82
|
+
{" - "}
|
|
83
|
+
{i18n.customFormatedTimeZone(
|
|
84
|
+
moment(end, "YYYY-MM-DDTHH:mm:00Z"),
|
|
85
|
+
tz.selectedTzid.tzid,
|
|
86
|
+
"LT"
|
|
87
|
+
)}{" "}
|
|
88
|
+
{`(${moment(start, "YYYY-MM-DDTHH:mm:00Z")
|
|
89
|
+
.tz(tz.selectedTzid.tzid)
|
|
90
|
+
.format("z")})`}
|
|
91
|
+
</>
|
|
92
|
+
)}
|
|
67
93
|
</button>
|
|
68
94
|
);
|
|
69
95
|
};
|
|
@@ -186,7 +186,7 @@ const Wrapper = () => {
|
|
|
186
186
|
|
|
187
187
|
// For allowing only this to be called once slots have been fetched
|
|
188
188
|
// Since we know the first month that is being fetched will be for the initial selected day
|
|
189
|
-
if (status.selectedDay && fetchCount >= 1) {
|
|
189
|
+
if (status.selectedDay && fetchCount >= 1 && !status.selected) {
|
|
190
190
|
const currentMonth = moment(status.selectedDay, "YYYY-MM-DD").format("YYYY-MM");
|
|
191
191
|
const month = status.months.filter(el => {
|
|
192
192
|
return el.month === currentMonth;
|
|
@@ -110,7 +110,7 @@ export const statusReducer = (state, action) => {
|
|
|
110
110
|
|
|
111
111
|
// Add action slots to state slots
|
|
112
112
|
const slotsObject = state.sequenced_availability
|
|
113
|
-
? addSequencedSlotsToObject(state.slots, action.slots)
|
|
113
|
+
? addSequencedSlotsToObject(state.slots, action.slots, state.query.sequence)
|
|
114
114
|
: addSlotsToObject(state.slots, action.slots);
|
|
115
115
|
|
|
116
116
|
// since we already know the available days for state slots
|
|
@@ -118,7 +118,7 @@ export const statusReducer = (state, action) => {
|
|
|
118
118
|
// this way we don't have to loop through the entirety of the state slotsObject
|
|
119
119
|
// every time new slots are rendered
|
|
120
120
|
const actionSlotsObject = state.sequenced_availability
|
|
121
|
-
? addSequencedSlotsToObject({}, action.slots)
|
|
121
|
+
? addSequencedSlotsToObject({}, action.slots, [])
|
|
122
122
|
: addSlotsToObject({}, action.slots);
|
|
123
123
|
|
|
124
124
|
const availableDays = uniqueItems([
|
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
font-size: 1.5em;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
.DTP__confirm-details--sequence-title {
|
|
14
|
+
display: inline-block;
|
|
15
|
+
font-weight: bold;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.DTP__confirm-details--sequence-time {
|
|
19
|
+
display: inline-block;
|
|
20
|
+
margin-left: 0.5rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
.DTP__confirm-button {
|
|
14
24
|
@extend .DTP__button;
|
|
15
25
|
background-color: var(--buttonConfirm);
|
|
@@ -25,6 +25,16 @@
|
|
|
25
25
|
border-radius: 0.4em;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
.DTP__time-slot--detailed {
|
|
29
|
+
height: unset;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.DTP__detailed-slot-list,
|
|
33
|
+
.DTP__detailed-slot-list--item {
|
|
34
|
+
@extend %unset;
|
|
35
|
+
list-style: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
.DTP__time-slot--selected {
|
|
29
39
|
@extend .DTP__button;
|
|
30
40
|
background-color: var(--buttonActive);
|
|
@@ -196,13 +196,26 @@ export const addSlotsToObject = (slotsObject, newSlotsArray) => {
|
|
|
196
196
|
return slotsObject;
|
|
197
197
|
};
|
|
198
198
|
|
|
199
|
-
export const
|
|
199
|
+
export const addSequenceTitle = (slot, sequence) => {
|
|
200
|
+
if (sequence.length > 0) {
|
|
201
|
+
const title = sequence.find(s => {
|
|
202
|
+
return s.sequence_id === slot.sequence_id;
|
|
203
|
+
}).sequence_title;
|
|
204
|
+
return title;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const addSequencedSlotsToObject = (slotsObject, newSlotsArray, sequence) => {
|
|
200
209
|
newSlotsArray.forEach(slot => {
|
|
210
|
+
const formattedSlot = slot.sequence.map(s => {
|
|
211
|
+
const title = addSequenceTitle(s, sequence);
|
|
212
|
+
return title ? { ...s, sequence_title: title } : s;
|
|
213
|
+
});
|
|
201
214
|
const startArray = slot.sequence.map(a => a.start);
|
|
202
215
|
const start = startArray.reduce((prev, current) => {
|
|
203
216
|
return prev < current ? prev : current;
|
|
204
217
|
});
|
|
205
|
-
slotsObject[start] =
|
|
218
|
+
slotsObject[start] = formattedSlot;
|
|
206
219
|
});
|
|
207
220
|
return slotsObject;
|
|
208
221
|
};
|
|
@@ -276,21 +276,7 @@ export const getSequencedAvailability = (
|
|
|
276
276
|
tzid = "Etc/UTC",
|
|
277
277
|
mock = false
|
|
278
278
|
) => {
|
|
279
|
-
if (
|
|
280
|
-
(mock && params.response_format === "slots") ||
|
|
281
|
-
(mock && typeof params.start_interval === "undefined")
|
|
282
|
-
) {
|
|
283
|
-
return mocks.availabilitySlots;
|
|
284
|
-
}
|
|
285
|
-
if (mock && params.response_format === "overlapping_slots") {
|
|
286
|
-
return mocks.availabilityOverlappingSlots(
|
|
287
|
-
params.start_interval.minutes,
|
|
288
|
-
params.required_duration.minutes,
|
|
289
|
-
params.query_periods,
|
|
290
|
-
tzid
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
if (mock) return mocks.availability;
|
|
279
|
+
if (mock) return mocks.sequencedAvailabilitySlots;
|
|
294
280
|
|
|
295
281
|
return fetch(`${api_domain}/v1/sequenced_availability?et=${token}`, {
|
|
296
282
|
method: "POST",
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
validateLocaleModifiers,
|
|
11
11
|
} from "./init";
|
|
12
12
|
import { logConstructor } from "./logging";
|
|
13
|
-
import { queryForDateTimePicker } from "./mocks";
|
|
13
|
+
import { queryForDateTimePicker, sequencedQueryForDateTimePicker } from "./mocks";
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
getMonthsFromQuery,
|
|
@@ -75,8 +75,10 @@ export const parseDateTimePickerOptions = (options = {}) => {
|
|
|
75
75
|
const isSequencedAvailabilityQuery = options.availability_query.sequence ? true : false;
|
|
76
76
|
|
|
77
77
|
let query;
|
|
78
|
-
if (options.demo) {
|
|
78
|
+
if (options.demo && !isSequencedAvailabilityQuery) {
|
|
79
79
|
query = queryForDateTimePicker;
|
|
80
|
+
} else if (options.demo && isSequencedAvailabilityQuery) {
|
|
81
|
+
query = sequencedQueryForDateTimePicker;
|
|
80
82
|
} else if (isBookableEventsQuery) {
|
|
81
83
|
query = options.availability_query;
|
|
82
84
|
query = parseWithOverlappingSlots(query);
|
|
@@ -88,6 +90,45 @@ export const parseDateTimePickerOptions = (options = {}) => {
|
|
|
88
90
|
query = parseWithOverlappingSlots(query);
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
if (isSequencedAvailabilityQuery) {
|
|
94
|
+
const isOneTitlePresent = query.sequence.some(obj =>
|
|
95
|
+
Object.keys(obj).includes("sequence_title")
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (isOneTitlePresent) {
|
|
99
|
+
const isEveryTitlePresent = query.sequence.every(obj =>
|
|
100
|
+
Object.keys(obj).includes("sequence_title")
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (!isEveryTitlePresent) {
|
|
104
|
+
log.error("There is a missing `sequence_title` from at least one sequence event.");
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (typeof config.slot_button_mode !== "undefined" && !isSequencedAvailabilityQuery) {
|
|
111
|
+
log.warn(
|
|
112
|
+
`\`config.slot_button_mode\`. can only be used with a sequenced availability query. Setting this option on a regular availability query will have no effect.`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const slotButtonMode =
|
|
117
|
+
typeof config.slot_button_mode === "undefined" ? "summary" : config.slot_button_mode;
|
|
118
|
+
|
|
119
|
+
const validSlotButtonModes = ["summary", "detailed"];
|
|
120
|
+
const validSlotButtonMode = validSlotButtonModes.includes(slotButtonMode);
|
|
121
|
+
|
|
122
|
+
if (isSequencedAvailabilityQuery && !validSlotButtonMode) {
|
|
123
|
+
log.error(
|
|
124
|
+
`Please provide a valid \`config.slot_button_mode\`. \`${slotButtonMode}\` is not a supported value.`,
|
|
125
|
+
{
|
|
126
|
+
docsSlug: "date-time-picker/#config.slot_button_mode",
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
91
132
|
const tzid = parseTimezone(options.tzid, "date-time-picker", log);
|
|
92
133
|
|
|
93
134
|
const tzList = typeof config.tz_list === "undefined" ? false : config.tz_list;
|
|
@@ -170,6 +211,7 @@ export const parseDateTimePickerOptions = (options = {}) => {
|
|
|
170
211
|
startDate,
|
|
171
212
|
endDate,
|
|
172
213
|
tzList,
|
|
214
|
+
slotButtonMode,
|
|
173
215
|
},
|
|
174
216
|
translations,
|
|
175
217
|
log,
|
package/src/js/helpers/mocks.js
CHANGED
|
@@ -562,6 +562,67 @@ export const availabilitySlots = new Promise(function (resolve, reject) {
|
|
|
562
562
|
});
|
|
563
563
|
});
|
|
564
564
|
|
|
565
|
+
export const sequencedAvailabilitySlots = new Promise(function (resolve, reject) {
|
|
566
|
+
const createMockSequenceSlot = (day, startOne, endOne, startTwo, endTwo) => ({
|
|
567
|
+
sequence: [
|
|
568
|
+
{
|
|
569
|
+
sequence_id: "demo-one",
|
|
570
|
+
start: offsetTime(day, startOne),
|
|
571
|
+
end: offsetTime(day, endOne),
|
|
572
|
+
participants: [
|
|
573
|
+
{
|
|
574
|
+
sub: "acc_5b97a52a5c92eb0cc0400ffe",
|
|
575
|
+
},
|
|
576
|
+
],
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
sequence_id: "demo-two",
|
|
580
|
+
start: offsetTime(day, startTwo),
|
|
581
|
+
end: offsetTime(day, endTwo),
|
|
582
|
+
participants: [
|
|
583
|
+
{
|
|
584
|
+
sub: "acc_5b97a52a5c92eb0cc0400ffe",
|
|
585
|
+
},
|
|
586
|
+
],
|
|
587
|
+
},
|
|
588
|
+
],
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
resolve({
|
|
592
|
+
available_slots: [
|
|
593
|
+
createMockSequenceSlot(1, "09:00", "09:30", "09:30", "10:00"),
|
|
594
|
+
createMockSequenceSlot(1, "10:00", "10:30", "10:30", "11:00"),
|
|
595
|
+
createMockSequenceSlot(1, "11:00", "11:30", "11:30", "12:00"),
|
|
596
|
+
createMockSequenceSlot(1, "12:00", "12:30", "12:30", "13:00"),
|
|
597
|
+
createMockSequenceSlot(1, "14:00", "14:30", "14:30", "15:00"),
|
|
598
|
+
createMockSequenceSlot(2, "14:30", "15:00", "15:00", "15:30"),
|
|
599
|
+
createMockSequenceSlot(2, "16:30", "17:00", "17:00", "17:30"),
|
|
600
|
+
createMockSequenceSlot(3, "08:00", "08:30", "08:30", "09:00"),
|
|
601
|
+
createMockSequenceSlot(3, "09:00", "09:30", "09:30", "10:00"),
|
|
602
|
+
createMockSequenceSlot(3, "10:00", "10:30", "10:30", "11:00"),
|
|
603
|
+
createMockSequenceSlot(3, "11:00", "11:30", "11:30", "12:00"),
|
|
604
|
+
createMockSequenceSlot(3, "12:00", "12:30", "12:30", "13:00"),
|
|
605
|
+
createMockSequenceSlot(3, "14:00", "14:30", "14:30", "15:00"),
|
|
606
|
+
createMockSequenceSlot(4, "09:00", "09:30", "09:30", "10:00"),
|
|
607
|
+
createMockSequenceSlot(4, "10:00", "10:30", "10:30", "11:00"),
|
|
608
|
+
createMockSequenceSlot(4, "12:00", "12:30", "12:30", "13:00"),
|
|
609
|
+
createMockSequenceSlot(4, "16:00", "16:30", "16:30", "17:00"),
|
|
610
|
+
createMockSequenceSlot(5, "09:00", "09:30", "09:30", "10:00"),
|
|
611
|
+
createMockSequenceSlot(5, "10:00", "10:30", "10:30", "11:00"),
|
|
612
|
+
createMockSequenceSlot(5, "11:00", "11:30", "11:30", "12:00"),
|
|
613
|
+
createMockSequenceSlot(5, "12:00", "12:30", "12:30", "13:00"),
|
|
614
|
+
createMockSequenceSlot(5, "13:30", "14:00", "14:00", "14:30"),
|
|
615
|
+
createMockSequenceSlot(8, "09:00", "09:30", "09:30", "10:00"),
|
|
616
|
+
createMockSequenceSlot(8, "10:00", "10:30", "10:30", "11:00"),
|
|
617
|
+
createMockSequenceSlot(8, "11:00", "11:30", "11:30", "12:00"),
|
|
618
|
+
createMockSequenceSlot(8, "14:00", "14:30", "14:30", "15:00"),
|
|
619
|
+
createMockSequenceSlot(9, "09:00", "09:30", "09:30", "10:00"),
|
|
620
|
+
createMockSequenceSlot(9, "10:00", "10:30", "10:30", "11:00"),
|
|
621
|
+
createMockSequenceSlot(9, "11:00", "11:30", "11:30", "12:00"),
|
|
622
|
+
],
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
|
|
565
626
|
export const availabilityOverlappingSlots = (
|
|
566
627
|
interval = 30,
|
|
567
628
|
duration = 60,
|
|
@@ -772,3 +833,39 @@ export const queryForDateTimePicker = {
|
|
|
772
833
|
query_periods: [{ start: offsetTime(1, "09:00"), end: offsetTime(9, "11:30") }],
|
|
773
834
|
required_duration: { minutes: 30 },
|
|
774
835
|
};
|
|
836
|
+
|
|
837
|
+
export const sequencedQueryForDateTimePicker = {
|
|
838
|
+
sequence: [
|
|
839
|
+
{
|
|
840
|
+
sequence_id: "demo-one",
|
|
841
|
+
sequence_title: "Demo Event One",
|
|
842
|
+
ordinal: 1,
|
|
843
|
+
participants: [
|
|
844
|
+
{
|
|
845
|
+
members: [
|
|
846
|
+
{
|
|
847
|
+
sub: "acc_5b97a52a5c92eb0cc0400ffe",
|
|
848
|
+
},
|
|
849
|
+
],
|
|
850
|
+
},
|
|
851
|
+
],
|
|
852
|
+
required_duration: { minutes: 30 },
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
sequence_id: "demo-two",
|
|
856
|
+
sequence_title: "Demo Event Two",
|
|
857
|
+
ordinal: 2,
|
|
858
|
+
participants: [
|
|
859
|
+
{
|
|
860
|
+
members: [
|
|
861
|
+
{
|
|
862
|
+
sub: "acc_5b97a52a5c92eb0cc0400ffe",
|
|
863
|
+
},
|
|
864
|
+
],
|
|
865
|
+
},
|
|
866
|
+
],
|
|
867
|
+
required_duration: { minutes: 30 },
|
|
868
|
+
},
|
|
869
|
+
],
|
|
870
|
+
query_periods: [{ start: offsetTime(1, "09:00"), end: offsetTime(9, "11:30") }],
|
|
871
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { render, fireEvent } from "@testing-library/react";
|
|
2
|
+
import { render, fireEvent, within } from "@testing-library/react";
|
|
3
3
|
import "@testing-library/jest-dom";
|
|
4
4
|
|
|
5
5
|
import SequencedSlotButton from "../../src/js/components/DateTimePicker/SequencedSlotButton";
|
|
@@ -67,6 +67,33 @@ describe("SequencedSlotButton", () => {
|
|
|
67
67
|
expect(button.innerHTML).toEqual(expect.stringContaining("10:30 AM - 12:00 PM (BST)"));
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
+
it("displays a detailed view when slot button mode is set to detailed", () => {
|
|
71
|
+
const status = {
|
|
72
|
+
slotButtonMode: "detailed",
|
|
73
|
+
};
|
|
74
|
+
const { container } = render(<SequencedSlotButton slot={slot} />, {
|
|
75
|
+
wrapper: e => wrapper({ ...e, status }),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const button = container.querySelector(".DTP__time-slot");
|
|
79
|
+
const detailedList = container.querySelector(".DTP__detailed-slot-list");
|
|
80
|
+
|
|
81
|
+
expect(button).toBeInTheDocument();
|
|
82
|
+
expect(detailedList).toBeInTheDocument();
|
|
83
|
+
|
|
84
|
+
const { getAllByRole } = within(detailedList);
|
|
85
|
+
const items = getAllByRole("listitem");
|
|
86
|
+
expect(items.length).toBe(2);
|
|
87
|
+
|
|
88
|
+
const listText = items.map(item => item.textContent);
|
|
89
|
+
expect(listText).toMatchInlineSnapshot(`
|
|
90
|
+
Array [
|
|
91
|
+
"10:30 AM - 11:00 AM (BST)",
|
|
92
|
+
"11:30 AM - 12:00 PM (BST)",
|
|
93
|
+
]
|
|
94
|
+
`);
|
|
95
|
+
});
|
|
96
|
+
|
|
70
97
|
it("displays slot button with time in GMT", () => {
|
|
71
98
|
const slot = [
|
|
72
99
|
{ sequence_id: "test-1", start: "2022-11-05T09:30:00Z", end: "2022-11-05T10:00:00Z" },
|
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
testSequencedSlotsArray,
|
|
10
10
|
testDaySlots,
|
|
11
11
|
testDaySequencedSlots,
|
|
12
|
-
|
|
12
|
+
testSequencedSlotsObject,
|
|
13
13
|
testQuery,
|
|
14
|
+
testSequencedQuery,
|
|
14
15
|
} from "../dummy-data";
|
|
15
16
|
|
|
16
17
|
import {
|
|
@@ -584,6 +585,7 @@ describe("Date Time Picker status reducer", () => {
|
|
|
584
585
|
it("sets sequenced slots", () => {
|
|
585
586
|
const oldState = {
|
|
586
587
|
...startingState,
|
|
588
|
+
query: testSequencedQuery,
|
|
587
589
|
slots: {},
|
|
588
590
|
availableDays: [],
|
|
589
591
|
slotFetchCount: 0,
|
|
@@ -649,7 +651,7 @@ describe("Date Time Picker status reducer", () => {
|
|
|
649
651
|
} = result;
|
|
650
652
|
|
|
651
653
|
// These values have been updated
|
|
652
|
-
expect(slots).toStrictEqual(
|
|
654
|
+
expect(slots).toStrictEqual(testSequencedSlotsObject);
|
|
653
655
|
expect(availableDays).toStrictEqual([
|
|
654
656
|
"2021-09-29",
|
|
655
657
|
"2021-09-30",
|
|
@@ -748,7 +750,7 @@ describe("Date Time Picker status reducer", () => {
|
|
|
748
750
|
it("SELECT_DAY for sequenced slots", async () => {
|
|
749
751
|
const oldState = {
|
|
750
752
|
...startingState,
|
|
751
|
-
slots:
|
|
753
|
+
slots: testSequencedSlotsObject,
|
|
752
754
|
sequenced_availability: true,
|
|
753
755
|
};
|
|
754
756
|
const date = "2021-09-29";
|