genesys-spark 4.0.0-beta.58 → 4.0.0-beta.60

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/index.js CHANGED
@@ -152,7 +152,96 @@ function checkAndLoadFonts(fonts) {
152
152
  })).then(function () { }); // flatten the promise array
153
153
  }
154
154
 
155
- var ASSET_PREFIX = '/spark-components/build-assets/4.0.0-beta.58-90/genesys-webcomponents/';
155
+ /**
156
+ * Provides an [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)
157
+ * object for formatting dates and times. Unlike the native version, `locale` is
158
+ * an optional argument. If not provided, the function will try to determine the
159
+ * locale from the DOM, where it should be set for a11y reasons.
160
+ * @param locale optional locale to use for formatting
161
+ * @param options options to pass to the Intl.DateTimeFormat constructor
162
+ * @returns a new DateTimeFormat
163
+ */
164
+ function dateTimeFormat(localeOrOptions, options) {
165
+ var locale = undefined;
166
+ if (typeof localeOrOptions === 'string') {
167
+ locale = localeOrOptions;
168
+ }
169
+ else {
170
+ options = localeOrOptions;
171
+ }
172
+ if (locale != undefined) {
173
+ return new Intl.DateTimeFormat(locale, options);
174
+ }
175
+ else {
176
+ var userLocale = determineDisplayLocale();
177
+ return new Intl.DateTimeFormat(userLocale, options);
178
+ }
179
+ }
180
+ /**
181
+ * Provides an [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat)
182
+ * object for formatting dates and times. Unlike the native version, `locale` is
183
+ * an optional argument. If not provided, the function will try to determine the
184
+ * locale from the DOM, where it should be set for a11y reasons.
185
+ * @param locale optional locale to use for formatting
186
+ * @param options options to pass to the Intl.RelativeTimeFormat constructor
187
+ * @returns a new RelativeTimeFormat
188
+ */
189
+ function relativeTimeFormat(localeOrOptions, options) {
190
+ var locale = undefined;
191
+ if (typeof localeOrOptions === 'string') {
192
+ locale = localeOrOptions;
193
+ }
194
+ else {
195
+ options = localeOrOptions;
196
+ }
197
+ if (locale != undefined) {
198
+ return new Intl.RelativeTimeFormat(locale, options);
199
+ }
200
+ else {
201
+ var userLocale = determineDisplayLocale();
202
+ return new Intl.RelativeTimeFormat(userLocale, options);
203
+ }
204
+ }
205
+ /**
206
+ * Makes a best effort to return the locale that should be used for a given element
207
+ * by checking language tags on ancestors. If no element is provided, it will
208
+ * start with the document's <body> tag. If no locale can be found, it will use
209
+ * the browser's locale preference. It will also try to add a region to regionless
210
+ * locales when there is a partial match with the browser's locale.
211
+ * @returns a locale string (e.g. 'en-US', 'en', 'de-DE', etc)
212
+ */
213
+ function determineDisplayLocale(element) {
214
+ var _a;
215
+ if (element === void 0) { element = document.body; }
216
+ var domLocale = (_a = element.closest('[lang]')) === null || _a === void 0 ? void 0 : _a.lang;
217
+ if (!domLocale || browserHasRegionData(domLocale)) {
218
+ // If we can't find a locale in the DOM, or we find a locale without a region that matches the
219
+ // users's browser locale, use the browser locale.
220
+ return navigator.language;
221
+ }
222
+ else {
223
+ return domLocale;
224
+ }
225
+ }
226
+ /**
227
+ * Returns true if the provided locale only has a language, but the user's
228
+ * browser settings have the same language with a locale.
229
+ * @param localeString The locale to check
230
+ * @returns true if the region can be guessed from the browser settings.
231
+ */
232
+ function browserHasRegionData(localeString) {
233
+ return (localeString.length == 2 &&
234
+ navigator.language.startsWith("".concat(localeString, "-")));
235
+ }
236
+
237
+ var intl = /*#__PURE__*/Object.freeze({
238
+ __proto__: null,
239
+ dateTimeFormat: dateTimeFormat,
240
+ determineDisplayLocale: determineDisplayLocale,
241
+ relativeTimeFormat: relativeTimeFormat
242
+ });
243
+
244
+ var ASSET_PREFIX = '/spark-components/build-assets/4.0.0-beta.60-92/genesys-webcomponents/';
156
245
  var SCRIPT_PATH = 'genesys-webcomponents.esm.js';
157
246
  var STYLE_PATH = 'genesys-webcomponents.css';
158
247
  var assetsOrigin = getAssetsOrigin();
@@ -179,7 +268,5 @@ function registerSparkComponents() {
179
268
  checkAndLoadFonts(FONTS)
180
269
  ]).then();
181
270
  }
182
- // TODO: Build out utility functions where components aren't the right solution
183
- // export function formatDate(...)
184
271
 
185
- export { registerSparkComponents };
272
+ export { intl as Intl, registerSparkComponents };
@@ -8,3 +8,4 @@
8
8
  * unexpected failures.
9
9
  */
10
10
  export declare function registerSparkComponents(): Promise<void>;
11
+ export * as Intl from './intl';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Provides an [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)
3
+ * object for formatting dates and times. Unlike the native version, `locale` is
4
+ * an optional argument. If not provided, the function will try to determine the
5
+ * locale from the DOM, where it should be set for a11y reasons.
6
+ * @param locale optional locale to use for formatting
7
+ * @param options options to pass to the Intl.DateTimeFormat constructor
8
+ * @returns a new DateTimeFormat
9
+ */
10
+ export declare function dateTimeFormat(localeOrOptions: string | Intl.DateTimeFormatOptions, options?: Intl.DateTimeFormatOptions): Intl.DateTimeFormat;
11
+ /**
12
+ * Provides an [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat)
13
+ * object for formatting dates and times. Unlike the native version, `locale` is
14
+ * an optional argument. If not provided, the function will try to determine the
15
+ * locale from the DOM, where it should be set for a11y reasons.
16
+ * @param locale optional locale to use for formatting
17
+ * @param options options to pass to the Intl.RelativeTimeFormat constructor
18
+ * @returns a new RelativeTimeFormat
19
+ */
20
+ export declare function relativeTimeFormat(localeOrOptions: string | Intl.RelativeTimeFormatOptions, options?: Intl.RelativeTimeFormatOptions): Intl.RelativeTimeFormat;
21
+ /**
22
+ * Makes a best effort to return the locale that should be used for a given element
23
+ * by checking language tags on ancestors. If no element is provided, it will
24
+ * start with the document's <body> tag. If no locale can be found, it will use
25
+ * the browser's locale preference. It will also try to add a region to regionless
26
+ * locales when there is a partial match with the browser's locale.
27
+ * @returns a locale string (e.g. 'en-US', 'en', 'de-DE', etc)
28
+ */
29
+ export declare function determineDisplayLocale(element?: HTMLElement): string;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genesys-spark",
3
- "version": "4.0.0-beta.58",
3
+ "version": "4.0.0-beta.60",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
package/src/index.ts CHANGED
@@ -35,5 +35,5 @@ export function registerSparkComponents(): Promise<void> {
35
35
  ]).then();
36
36
  }
37
37
 
38
- // TODO: Build out utility functions where components aren't the right solution
39
- // export function formatDate(...)
38
+ // Re-export of utility modules
39
+ export * as Intl from './intl';
package/src/intl.ts ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Provides an [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)
3
+ * object for formatting dates and times. Unlike the native version, `locale` is
4
+ * an optional argument. If not provided, the function will try to determine the
5
+ * locale from the DOM, where it should be set for a11y reasons.
6
+ * @param locale optional locale to use for formatting
7
+ * @param options options to pass to the Intl.DateTimeFormat constructor
8
+ * @returns a new DateTimeFormat
9
+ */
10
+ export function dateTimeFormat(
11
+ localeOrOptions: string | Intl.DateTimeFormatOptions,
12
+ options?: Intl.DateTimeFormatOptions
13
+ ): Intl.DateTimeFormat {
14
+ let locale = undefined;
15
+ if (typeof localeOrOptions === 'string') {
16
+ locale = localeOrOptions;
17
+ } else {
18
+ options = localeOrOptions;
19
+ }
20
+
21
+ if (locale != undefined) {
22
+ return new Intl.DateTimeFormat(locale, options);
23
+ } else {
24
+ const userLocale = determineDisplayLocale();
25
+ return new Intl.DateTimeFormat(userLocale, options);
26
+ }
27
+ }
28
+ /**
29
+ * Provides an [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat)
30
+ * object for formatting dates and times. Unlike the native version, `locale` is
31
+ * an optional argument. If not provided, the function will try to determine the
32
+ * locale from the DOM, where it should be set for a11y reasons.
33
+ * @param locale optional locale to use for formatting
34
+ * @param options options to pass to the Intl.RelativeTimeFormat constructor
35
+ * @returns a new RelativeTimeFormat
36
+ */
37
+ export function relativeTimeFormat(
38
+ localeOrOptions: string | Intl.RelativeTimeFormatOptions,
39
+ options?: Intl.RelativeTimeFormatOptions
40
+ ): Intl.RelativeTimeFormat {
41
+ let locale = undefined;
42
+ if (typeof localeOrOptions === 'string') {
43
+ locale = localeOrOptions;
44
+ } else {
45
+ options = localeOrOptions;
46
+ }
47
+
48
+ if (locale != undefined) {
49
+ return new Intl.RelativeTimeFormat(locale, options);
50
+ } else {
51
+ const userLocale = determineDisplayLocale();
52
+ return new Intl.RelativeTimeFormat(userLocale, options);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Makes a best effort to return the locale that should be used for a given element
58
+ * by checking language tags on ancestors. If no element is provided, it will
59
+ * start with the document's <body> tag. If no locale can be found, it will use
60
+ * the browser's locale preference. It will also try to add a region to regionless
61
+ * locales when there is a partial match with the browser's locale.
62
+ * @returns a locale string (e.g. 'en-US', 'en', 'de-DE', etc)
63
+ */
64
+ export function determineDisplayLocale(
65
+ element: HTMLElement = document.body
66
+ ): string {
67
+ const domLocale = element.closest<HTMLElement>('[lang]')?.lang;
68
+ if (!domLocale || browserHasRegionData(domLocale)) {
69
+ // If we can't find a locale in the DOM, or we find a locale without a region that matches the
70
+ // users's browser locale, use the browser locale.
71
+ return navigator.language;
72
+ } else {
73
+ return domLocale;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Returns true if the provided locale only has a language, but the user's
79
+ * browser settings have the same language with a locale.
80
+ * @param localeString The locale to check
81
+ * @returns true if the region can be guessed from the browser settings.
82
+ */
83
+ function browserHasRegionData(localeString: string): boolean {
84
+ return (
85
+ localeString.length == 2 &&
86
+ navigator.language.startsWith(`${localeString}-`)
87
+ );
88
+ }
@@ -0,0 +1,104 @@
1
+ import {
2
+ determineDisplayLocale,
3
+ dateTimeFormat,
4
+ relativeTimeFormat
5
+ } from '../src/intl';
6
+
7
+ describe('The intl module', () => {
8
+ beforeEach(() => {
9
+ // Reset the Dom
10
+ document.documentElement.innerHTML = '<head></head><body></body>';
11
+ // Remove language attribute from <html>
12
+ document.documentElement.removeAttribute('lang');
13
+ // Reset/set navigator.language to a known value
14
+ Object.defineProperty(window.navigator, 'language', {
15
+ value: 'yy-YY',
16
+ configurable: true
17
+ });
18
+ });
19
+
20
+ describe('When determining what locale to use', () => {
21
+ test('It will determine the display locale from <body> if no locale is provided', () => {
22
+ document.body.setAttribute('lang', 'xx-XX');
23
+ expect(determineDisplayLocale()).toBe('xx-XX');
24
+ });
25
+ test('It will determine the display locale from <html> if no locale is provided', () => {
26
+ document.documentElement.setAttribute('lang', 'xx-XX');
27
+ expect(determineDisplayLocale()).toBe('xx-XX');
28
+ });
29
+ test('Given an element with a language attribute, it will determine the locale from that tag', () => {
30
+ const target = document.createElement('div');
31
+ target.setAttribute('lang', 'xx-XX');
32
+ expect(determineDisplayLocale(target)).toBe('xx-XX');
33
+ });
34
+ test("Given an element without a language attribute, it will check the element's ancestors for a language attribute", () => {
35
+ const localeOwner = document.createElement('div');
36
+ localeOwner.setAttribute('lang', 'xx-XX');
37
+ localeOwner.innerHTML =
38
+ '<div><div><span class="target"></span></div></div>';
39
+ const target =
40
+ localeOwner.querySelector<HTMLElement>('.target') || undefined;
41
+ expect(target).not.toBeUndefined();
42
+ expect(determineDisplayLocale(target)).toBe('xx-XX');
43
+ });
44
+ test("If no language tag is found, uses the browser's language", () => {
45
+ expect(determineDisplayLocale()).toBe('yy-YY');
46
+ });
47
+ test('If given a partial match to the browser language, it will pull the region from the browser', () => {
48
+ document.body.setAttribute('lang', 'yy');
49
+ expect(determineDisplayLocale()).toBe('yy-YY');
50
+ });
51
+ });
52
+
53
+ describe('When creating a DateTimeFormat', () => {
54
+ const formatOptions: Intl.DateTimeFormatOptions = {};
55
+ beforeEach(() => {
56
+ jest.resetAllMocks();
57
+ jest.spyOn(Intl, 'DateTimeFormat');
58
+ });
59
+ it('Calls through to the browser implementation', () => {
60
+ dateTimeFormat('xx-XX', formatOptions);
61
+ expect(Intl.DateTimeFormat).toHaveBeenCalledWith('xx-XX', formatOptions);
62
+ });
63
+ it('The locale is optional and will defer to `determineDisplayLocale`', () => {
64
+ document.body.setAttribute('lang', 'xx-XX');
65
+ dateTimeFormat(formatOptions);
66
+ expect(Intl.DateTimeFormat).toHaveBeenCalledWith('xx-XX', formatOptions);
67
+ });
68
+ it('Maintains optional format options', () => {
69
+ dateTimeFormat('en-US');
70
+ expect(Intl.DateTimeFormat).toHaveBeenCalledWith('en-US', undefined);
71
+ });
72
+ });
73
+
74
+ describe('When creating a RelativeTimeFormat', () => {
75
+ const formatOptions: Intl.RelativeTimeFormatOptions = {};
76
+ beforeEach(() => {
77
+ jest.resetAllMocks();
78
+ // This has to be different than the DateTimeFormat mock - probably because of some ES6 class
79
+ // thing that I can't be bothered to figure out.
80
+ Object.defineProperty(Intl, 'RelativeTimeFormat', {
81
+ value: jest.fn()
82
+ });
83
+ });
84
+ it('Calls through to the browser implementation', () => {
85
+ relativeTimeFormat('xx-XX', formatOptions);
86
+ expect(Intl.RelativeTimeFormat).toHaveBeenCalledWith(
87
+ 'xx-XX',
88
+ formatOptions
89
+ );
90
+ });
91
+ it('The locale is optional and will defer to `determineDisplayLocale`', () => {
92
+ document.body.setAttribute('lang', 'xx-XX');
93
+ relativeTimeFormat(formatOptions);
94
+ expect(Intl.RelativeTimeFormat).toHaveBeenCalledWith(
95
+ 'xx-XX',
96
+ formatOptions
97
+ );
98
+ });
99
+ it('Maintains optional format options', () => {
100
+ relativeTimeFormat('en-US');
101
+ expect(Intl.RelativeTimeFormat).toHaveBeenCalledWith('en-US', undefined);
102
+ });
103
+ });
104
+ });
package/tsconfig.json CHANGED
@@ -3,7 +3,8 @@
3
3
  "compilerOptions": {
4
4
  "noEmitOnError": true,
5
5
  "outDir": "dist",
6
- "declaration": true
6
+ "declaration": true,
7
+ "lib": ["ESNext.Intl", "DOM"]
7
8
  },
8
9
  "include": ["src", "test"]
9
10
  }