orc-shared 5.10.0-dev.20 → 5.10.0-dev.21
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/dist/components/Culture.js +33 -14
- package/dist/components/MaterialUI/Inputs/DatePicker.js +14 -14
- package/dist/components/MaterialUI/Inputs/TimePicker.js +14 -21
- package/dist/components/Provision.js +1 -1
- package/dist/hooks/useDaysAndMonthsLocalization.js +77 -0
- package/dist/sharedMessages.js +180 -0
- package/dist/utils/timezoneHelper.js +18 -31
- package/package.json +4 -3
- package/src/components/Culture.js +20 -10
- package/src/components/Culture.test.js +27 -16
- package/src/components/MaterialUI/Inputs/DatePicker.js +19 -20
- package/src/components/MaterialUI/Inputs/DatePicker.test.js +11 -6
- package/src/components/MaterialUI/Inputs/TimePicker.js +10 -19
- package/src/components/MaterialUI/Inputs/TimePicker.test.js +278 -117
- package/src/components/Provision.js +1 -1
- package/src/hooks/useDaysAndMonthsLocalization.js +79 -0
- package/src/hooks/useDaysAndMonthsLocalization.test.js +107 -0
- package/src/sharedMessages.js +180 -0
- package/src/timezones.json +883 -0
- package/src/translations/en-US.json +45 -0
- package/src/translations/fr-CA.json +45 -0
- package/src/utils/timezoneHelper.js +10 -135
- package/src/utils/timezoneHelper.test.js +7 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orc-shared",
|
|
3
|
-
"version": "5.10.0-dev.
|
|
3
|
+
"version": "5.10.0-dev.21",
|
|
4
4
|
"description": "Shared code for Orckestra applications",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"prepublishOnly": "npm run build:clean --",
|
|
26
26
|
"icons": "orc-scripts buildIconsSheet",
|
|
27
27
|
"extract": "orc-scripts extract-messages",
|
|
28
|
-
"generateApi": "orc-scripts generateApi --outputFile src/actions/requestsApi.js --requestsFile ./src/requests"
|
|
28
|
+
"generateApi": "orc-scripts generateApi --outputFile src/actions/requestsApi.js --requestsFile ./src/requests",
|
|
29
|
+
"generateWindowsZone": "orc-scripts generateWindowsZone --outputFile src/timezones.json"
|
|
29
30
|
},
|
|
30
31
|
"files": [
|
|
31
32
|
"src",
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
"@testing-library/react": "^10.4.9"
|
|
56
57
|
},
|
|
57
58
|
"dependencies": {
|
|
58
|
-
"orc-scripts": "4.0.
|
|
59
|
+
"orc-scripts": "4.0.7",
|
|
59
60
|
"react-number-format": "^5.3.0"
|
|
60
61
|
},
|
|
61
62
|
"sideEffects": false,
|
|
@@ -1,23 +1,33 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
import { useSelector } from "react-redux";
|
|
4
|
-
import { currentLocaleOrDefault } from "../selectors/locale";
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { useIntl } from "react-intl";
|
|
5
3
|
import { registerLocale, setDefaultLocale } from "react-datepicker";
|
|
6
4
|
import * as date_fns_locale from "date-fns/locale";
|
|
7
5
|
import { findCorrespondingLocale } from "../utils/localizationHelper";
|
|
6
|
+
import useDaysAndMonthsLocalization from "../hooks/useDaysAndMonthsLocalization";
|
|
7
|
+
|
|
8
|
+
export let customFnsLocale = null;
|
|
8
9
|
|
|
9
10
|
const Culture = () => {
|
|
10
|
-
const locale =
|
|
11
|
-
const
|
|
11
|
+
const { locale } = useIntl();
|
|
12
|
+
const daysAndMonthsLocalization = useDaysAndMonthsLocalization();
|
|
12
13
|
|
|
13
14
|
useMemo(() => {
|
|
14
|
-
const fnsLocale = findCorrespondingLocale(date_fns_locale,
|
|
15
|
+
const fnsLocale = findCorrespondingLocale(date_fns_locale, locale);
|
|
15
16
|
|
|
16
17
|
if (fnsLocale != null) {
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
customFnsLocale = {
|
|
19
|
+
...fnsLocale,
|
|
20
|
+
localize: {
|
|
21
|
+
...fnsLocale.localize,
|
|
22
|
+
day: n => daysAndMonthsLocalization.weekdaysMin[n],
|
|
23
|
+
month: n => daysAndMonthsLocalization.months[n],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
registerLocale(locale, customFnsLocale);
|
|
28
|
+
setDefaultLocale(locale);
|
|
19
29
|
}
|
|
20
|
-
}, [
|
|
30
|
+
}, [locale, daysAndMonthsLocalization]);
|
|
21
31
|
|
|
22
32
|
return <React.Fragment />;
|
|
23
33
|
};
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import Immutable from "immutable";
|
|
3
|
-
import { Provider } from "react-redux";
|
|
4
3
|
import { getDefaultLocale } from "react-datepicker";
|
|
5
|
-
import Culture from "./Culture";
|
|
4
|
+
import Culture, { customFnsLocale } from "./Culture";
|
|
5
|
+
import { extractMessages, TestWrapper } from "../utils/testUtils";
|
|
6
|
+
import sharedMessages from "../sharedMessages";
|
|
7
|
+
import { mount } from "enzyme";
|
|
8
|
+
|
|
9
|
+
const messages = extractMessages(sharedMessages);
|
|
6
10
|
|
|
7
11
|
describe("Culture", () => {
|
|
8
|
-
let state, store
|
|
12
|
+
let state, store;
|
|
9
13
|
|
|
10
14
|
beforeEach(() => {
|
|
11
|
-
languageGetter = jest.spyOn(window.navigator, "language", "get");
|
|
12
|
-
|
|
13
15
|
state = Immutable.fromJS({
|
|
14
16
|
requests: {},
|
|
15
17
|
settings: {
|
|
16
18
|
defaultScope: "aDefaultScope",
|
|
17
19
|
},
|
|
18
20
|
locale: {
|
|
21
|
+
locale: "en-US",
|
|
19
22
|
supportedLocales: [
|
|
20
23
|
{ language: "English", cultureIso: "en" },
|
|
21
24
|
{ language: "Français", cultureIso: "fr" },
|
|
@@ -30,17 +33,14 @@ describe("Culture", () => {
|
|
|
30
33
|
});
|
|
31
34
|
|
|
32
35
|
afterEach(() => {
|
|
33
|
-
languageGetter.mockReset();
|
|
34
36
|
jest.clearAllMocks();
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
it("shows the wrapped component if authenticated and default scope is known", () => {
|
|
38
|
-
languageGetter.mockReturnValue(null);
|
|
39
|
-
|
|
40
40
|
const component = (
|
|
41
|
-
<
|
|
41
|
+
<TestWrapper provider={{ store }} intlProvider={{ messages, locale: "zz-ZZ" }}>
|
|
42
42
|
<Culture />
|
|
43
|
-
</
|
|
43
|
+
</TestWrapper>
|
|
44
44
|
);
|
|
45
45
|
|
|
46
46
|
expect(component, "when mounted", "to satisfy", null);
|
|
@@ -50,12 +50,11 @@ describe("Culture", () => {
|
|
|
50
50
|
|
|
51
51
|
it("shows the wrapped component if authenticated and default scope is known 222", () => {
|
|
52
52
|
state = state.setIn(["locale", "supportedLocales"], [{ language: "English", cultureIso: "en" }]);
|
|
53
|
-
languageGetter.mockReturnValue("en-GB");
|
|
54
53
|
|
|
55
54
|
const component = (
|
|
56
|
-
<
|
|
55
|
+
<TestWrapper provider={{ store }} intlProvider={{ messages, locale: "en-GB" }}>
|
|
57
56
|
<Culture />
|
|
58
|
-
</
|
|
57
|
+
</TestWrapper>
|
|
59
58
|
);
|
|
60
59
|
|
|
61
60
|
expect(component, "when mounted", "to satisfy", null);
|
|
@@ -71,16 +70,28 @@ describe("Culture", () => {
|
|
|
71
70
|
{ language: "EnglishMy", cultureIso: "enMy" },
|
|
72
71
|
],
|
|
73
72
|
);
|
|
74
|
-
languageGetter.mockReturnValue("fr-FR");
|
|
75
73
|
|
|
76
74
|
const component = (
|
|
77
|
-
<
|
|
75
|
+
<TestWrapper provider={{ store }} intlProvider={{ messages, locale: "fr-FR" }}>
|
|
78
76
|
<Culture />
|
|
79
|
-
</
|
|
77
|
+
</TestWrapper>
|
|
80
78
|
);
|
|
81
79
|
|
|
82
80
|
expect(component, "when mounted", "to satisfy", null);
|
|
83
81
|
|
|
84
82
|
expect(getDefaultLocale(), "to equal", "fr-FR");
|
|
85
83
|
});
|
|
84
|
+
|
|
85
|
+
it("computes right localized day and month with Italian culture ", () => {
|
|
86
|
+
const component = (
|
|
87
|
+
<TestWrapper provider={{ store }} intlProvider={{ messages, locale: "it-IT" }}>
|
|
88
|
+
<Culture />
|
|
89
|
+
</TestWrapper>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
mount(component);
|
|
93
|
+
|
|
94
|
+
expect(customFnsLocale.localize.day(1), "to equal", "Mo");
|
|
95
|
+
expect(customFnsLocale.localize.month(6), "to equal", "July");
|
|
96
|
+
});
|
|
86
97
|
});
|
|
@@ -6,12 +6,10 @@ import "react-datepicker/dist/react-datepicker.css";
|
|
|
6
6
|
import TimePicker from "./TimePicker";
|
|
7
7
|
import { makeStyles } from "@material-ui/core/styles";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
convertTimeToOtherTimeZone,
|
|
9
|
+
getIanaTimeZoneFromWindowsName,
|
|
11
10
|
convertTimeToLocalTimeZone,
|
|
11
|
+
getWindowsTimeZone,
|
|
12
12
|
} from "../../../utils/timezoneHelper";
|
|
13
|
-
import { namedLookupLocalizedSelector } from "../../../selectors/metadata";
|
|
14
|
-
import { useSelector } from "react-redux";
|
|
15
13
|
|
|
16
14
|
const useStyles = makeStyles(theme => ({
|
|
17
15
|
container: {
|
|
@@ -139,24 +137,31 @@ const WrappedDatePicker = ({
|
|
|
139
137
|
readOnly,
|
|
140
138
|
showTimeSelectOnly,
|
|
141
139
|
metadata,
|
|
142
|
-
|
|
140
|
+
timePickerWindowsTimeZone,
|
|
143
141
|
error,
|
|
144
142
|
timeOption,
|
|
145
143
|
...props
|
|
146
144
|
}) => {
|
|
147
145
|
const classes = useStyles({ readOnly });
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
?
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
146
|
+
|
|
147
|
+
const ianaTimeZone =
|
|
148
|
+
useTimeZone && timePickerWindowsTimeZone ? getIanaTimeZoneFromWindowsName(timePickerWindowsTimeZone) : null;
|
|
149
|
+
|
|
150
|
+
const computeDateWithTimezone = React.useCallback(
|
|
151
|
+
date => {
|
|
152
|
+
return date ? (ianaTimeZone ? convertTimeToLocalTimeZone(new Date(date), ianaTimeZone) : new Date(date)) : null;
|
|
153
|
+
},
|
|
154
|
+
[ianaTimeZone],
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const startDate = computeDateWithTimezone(value);
|
|
154
158
|
const disabledCls = classNames({ [classes.disabled]: props.disabled });
|
|
155
|
-
|
|
159
|
+
|
|
160
|
+
const windowsTimeZoneName = useTime && showTimeZone ? (timePickerWindowsTimeZone ?? getWindowsTimeZone()) : null;
|
|
156
161
|
|
|
157
162
|
const updateDate = (date, metadata) => {
|
|
158
163
|
if (onChange) {
|
|
159
|
-
onChange(
|
|
164
|
+
onChange(computeDateWithTimezone(date), metadata);
|
|
160
165
|
}
|
|
161
166
|
};
|
|
162
167
|
|
|
@@ -179,13 +184,7 @@ const WrappedDatePicker = ({
|
|
|
179
184
|
showTimeInput={useTime ?? false}
|
|
180
185
|
useTime={useTime ?? false}
|
|
181
186
|
customTimeInput={
|
|
182
|
-
useTime ?
|
|
183
|
-
<TimePicker
|
|
184
|
-
showTimeZone={showTimeZone}
|
|
185
|
-
requestedTimeZone={localizedTimeZoneName}
|
|
186
|
-
timeOption={timeOption}
|
|
187
|
-
/>
|
|
188
|
-
) : null
|
|
187
|
+
useTime ? <TimePicker windowsTimeZone={windowsTimeZoneName} timeOption={timeOption} /> : null
|
|
189
188
|
}
|
|
190
189
|
timeInputLabel={timeInputLabel ?? ""}
|
|
191
190
|
readOnly={readOnly}
|
|
@@ -269,7 +269,7 @@ describe("DatePicker", () => {
|
|
|
269
269
|
const expectedDate = "06/30/2020 12:00 AM";
|
|
270
270
|
expect(
|
|
271
271
|
<TestWrapper provider={{ store }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
|
|
272
|
-
<DatePicker useTime={true} readOnly={true} onChange={updater} value={date} />
|
|
272
|
+
<DatePicker useTime={true} readOnly={true} onChange={updater} value={date} showTimeZone={true} />
|
|
273
273
|
</TestWrapper>,
|
|
274
274
|
"when mounted",
|
|
275
275
|
"to satisfy",
|
|
@@ -348,7 +348,7 @@ describe("DatePicker", () => {
|
|
|
348
348
|
useTime={true}
|
|
349
349
|
useDate={false}
|
|
350
350
|
showTimeSelectOnly={true}
|
|
351
|
-
|
|
351
|
+
timePickerWindowsTimeZone={requestTimeZone}
|
|
352
352
|
/>
|
|
353
353
|
</TestWrapper>
|
|
354
354
|
);
|
|
@@ -375,7 +375,7 @@ describe("DatePicker", () => {
|
|
|
375
375
|
useTime={true}
|
|
376
376
|
useDate={false}
|
|
377
377
|
showTimeSelectOnly={true}
|
|
378
|
-
|
|
378
|
+
timePickerWindowsTimeZone={null}
|
|
379
379
|
/>
|
|
380
380
|
</TestWrapper>
|
|
381
381
|
);
|
|
@@ -400,7 +400,7 @@ describe("DatePicker", () => {
|
|
|
400
400
|
useTime={true}
|
|
401
401
|
useDate={false}
|
|
402
402
|
showTimeSelectOnly={true}
|
|
403
|
-
|
|
403
|
+
timePickerWindowsTimeZone={requestTimeZone}
|
|
404
404
|
/>
|
|
405
405
|
</TestWrapper>
|
|
406
406
|
);
|
|
@@ -419,7 +419,12 @@ describe("DatePicker", () => {
|
|
|
419
419
|
const requestTimeZone = "Eastern Standard Time";
|
|
420
420
|
const component = (
|
|
421
421
|
<TestWrapper provider={{ store }} intlProvider>
|
|
422
|
-
<DatePicker
|
|
422
|
+
<DatePicker
|
|
423
|
+
value={date}
|
|
424
|
+
useDate={false}
|
|
425
|
+
showTimeSelectOnly={true}
|
|
426
|
+
timePickerWindowsTimeZone={requestTimeZone}
|
|
427
|
+
/>
|
|
423
428
|
</TestWrapper>
|
|
424
429
|
);
|
|
425
430
|
const mountedComponent = mount(component);
|
|
@@ -477,7 +482,7 @@ describe("DatePicker", () => {
|
|
|
477
482
|
useDate={false}
|
|
478
483
|
useTimeZone={true}
|
|
479
484
|
showTimeSelectOnly={true}
|
|
480
|
-
|
|
485
|
+
timePickerWindowsTimeZone={requestTimeZone}
|
|
481
486
|
/>
|
|
482
487
|
</TestWrapper>
|
|
483
488
|
);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { useIntl } from "react-intl";
|
|
2
3
|
import { makeStyles } from "@material-ui/core/styles";
|
|
3
4
|
import Select from "./Select";
|
|
4
5
|
import SelectProps from "./SelectProps";
|
|
5
|
-
import {
|
|
6
|
+
import { getWindowsTimeZone } from "../../../utils/timezoneHelper";
|
|
6
7
|
import { namedLookupLocalizedSelector } from "../../../selectors/metadata";
|
|
7
8
|
import { useSelector } from "react-redux";
|
|
8
9
|
|
|
@@ -65,8 +66,8 @@ const ampmOptions = [
|
|
|
65
66
|
{ value: "PM", label: "PM" },
|
|
66
67
|
];
|
|
67
68
|
|
|
68
|
-
const isBrowserUsingAMPM =
|
|
69
|
-
!!new Date(Date.UTC(2020, 7, 30, 3, 0, 0)).toLocaleTimeString().match(/am|a.m|pm|p.m/i);
|
|
69
|
+
const isBrowserUsingAMPM = locale =>
|
|
70
|
+
!!new Date(Date.UTC(2020, 7, 30, 3, 0, 0)).toLocaleTimeString(locale).match(/am|a.m|pm|p.m/i);
|
|
70
71
|
|
|
71
72
|
export const parseTime = timeStr => {
|
|
72
73
|
var time = timeStr.match(/(\d+)(?::(\d\d))?\s*(p?)/i);
|
|
@@ -184,17 +185,18 @@ export const MinsSelect = ({ updateTimeOptions, time, values = minOptions }) =>
|
|
|
184
185
|
);
|
|
185
186
|
};
|
|
186
187
|
|
|
187
|
-
const TimePicker = ({ value, onChange, showTimeZone,
|
|
188
|
+
const TimePicker = ({ value, onChange, showTimeZone, windowsTimeZone, timeOption }) => {
|
|
188
189
|
const classes = useStyles();
|
|
189
|
-
|
|
190
|
+
const { locale } = useIntl();
|
|
191
|
+
const showAMPM = isBrowserUsingAMPM(locale);
|
|
190
192
|
const [time, setTime] = useState(parseTime(value || "00:00"));
|
|
191
193
|
|
|
192
194
|
useEffect(() => {
|
|
193
195
|
setTime(parseTime(value || "00:00"));
|
|
194
196
|
}, [value, setTime]);
|
|
195
197
|
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
+
const timeZone = windowsTimeZone ?? getWindowsTimeZone();
|
|
199
|
+
const localizedTimeZone = useSelector(namedLookupLocalizedSelector("customer", "TimeZone", timeZone));
|
|
198
200
|
|
|
199
201
|
const onTimeChange = datetime => {
|
|
200
202
|
if (onChange) {
|
|
@@ -227,15 +229,6 @@ const TimePicker = ({ value, onChange, showTimeZone, showAMPM, requestedTimeZone
|
|
|
227
229
|
onTimeChange(time);
|
|
228
230
|
};
|
|
229
231
|
|
|
230
|
-
const getTimeZone = requestedTimeZone => {
|
|
231
|
-
if (requestedTimeZone) return requestedTimeZone;
|
|
232
|
-
if (!localizedTimeZoneName) {
|
|
233
|
-
var timezone = new Date().toString().match(/GMT(\S+) \(([^)]+)\)/i);
|
|
234
|
-
return `${timezone[2]} (GMT${timezone[1]})`;
|
|
235
|
-
}
|
|
236
|
-
return localizedTimeZoneName;
|
|
237
|
-
};
|
|
238
|
-
|
|
239
232
|
return (
|
|
240
233
|
<div className={classes.timeWrapper}>
|
|
241
234
|
<span className={classes.timePickerWrapper}>
|
|
@@ -244,9 +237,7 @@ const TimePicker = ({ value, onChange, showTimeZone, showAMPM, requestedTimeZone
|
|
|
244
237
|
<MinsSelect updateTimeOptions={updateTimeOptions} time={time} values={timeOption?.minutes} />
|
|
245
238
|
<AMPMSelect showAMPM={showAMPM} updateTimeOptions={updateTimeOptions} time={time} />
|
|
246
239
|
</span>
|
|
247
|
-
{showTimeZone &&
|
|
248
|
-
<label className={classes.timeZoneWrapper}>{showTimeZone && getTimeZone(requestedTimeZone)}</label>
|
|
249
|
-
)}
|
|
240
|
+
{showTimeZone && <label className={classes.timeZoneWrapper}>{localizedTimeZone}</label>}
|
|
250
241
|
</div>
|
|
251
242
|
);
|
|
252
243
|
};
|