@pushwoosh/dumb-components 1.1.237 → 2.0.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.
@@ -1,4 +1,4 @@
1
- import { formatTimezoneShort } from '../helpers-tz';
1
+ import { formatTimezoneShort } from '../../helpers/timezones';
2
2
  export function formatDate(val, withTime = true) {
3
3
  const date = new Date(val.year, val.month - 1, val.day, 'hour' in val ? val.hour : 0, 'minute' in val ? val.minute : 0);
4
4
  if (withTime) {
@@ -5,8 +5,8 @@ import { Columns, Horizontal, Vertical } from '@pushwoosh/kit-helpers';
5
5
  import { TimeIcon } from '@pushwoosh/kit-icons';
6
6
  import { H5, Text } from '@pushwoosh/kit-typography';
7
7
  import { Button } from '../../../../Button';
8
+ import { formatTimezoneShort } from '../../../../helpers/timezones';
8
9
  import { extendPeriodStruct, formatTwoDigitNumber } from '../../../helpers';
9
- import { formatTimezoneShort } from '../../../helpers-tz';
10
10
  import { Lang } from '../../../polyglot';
11
11
  import { CalendarTimeInput } from '../CalendarTimeInput';
12
12
  import { CalendarTimezoneSelect } from '../CalendarTimezoneSelect';
@@ -1,23 +1,38 @@
1
- import { canonicalizeTimezone, findTimezoneOption, formatTimezoneLabel, formatTimezoneOffset, getTimezoneOffsetSafe, listTimezones, resolveTimezoneOption } from '../timezones';
1
+ import { canonicalizeTimezone, findTimezoneOption, formatTimezoneLabel, formatTimezoneOffset, formatTimezoneShort, getTimezoneOffsetSafe, listTimezones, resolveTimezoneOption } from '../timezones';
2
2
  const winter = new Date('2026-01-15T12:00:00Z');
3
3
  const melbourneDst = new Date('2026-10-10T12:00:00Z');
4
+ const melbourneStandard = new Date('2026-06-15T12:00:00Z');
4
5
  describe('formatTimezoneOffset', () => {
5
6
  it('renders sign, hours and minutes', () => {
6
- expect(formatTimezoneOffset(300)).toBe('GMT+05:00');
7
- expect(formatTimezoneOffset(330)).toBe('GMT+05:30');
8
- expect(formatTimezoneOffset(-210)).toBe('GMT-03:30');
9
- expect(formatTimezoneOffset(0)).toBe('GMT+00:00');
7
+ expect(formatTimezoneOffset(300)).toBe('UTC+05:00');
8
+ expect(formatTimezoneOffset(330)).toBe('UTC+05:30');
9
+ expect(formatTimezoneOffset(-210)).toBe('UTC-03:30');
10
+ expect(formatTimezoneOffset(0)).toBe('UTC+00:00');
11
+ });
12
+ });
13
+ describe('formatTimezoneShort', () => {
14
+ it('renders the bare offset the calendar badge shows', () => {
15
+ expect(formatTimezoneShort('Asia/Almaty', winter)).toBe('UTC+05:00');
16
+ expect(formatTimezoneShort('Asia/Kolkata', winter)).toBe('UTC+05:30');
17
+ expect(formatTimezoneShort('UTC', winter)).toBe('UTC+00:00');
18
+ });
19
+ it('follows DST at the given instant', () => {
20
+ expect(formatTimezoneShort('Australia/Melbourne', melbourneStandard)).toBe('UTC+10:00');
21
+ expect(formatTimezoneShort('Australia/Melbourne', melbourneDst)).toBe('UTC+11:00');
22
+ });
23
+ it('keeps an unknown zone readable', () => {
24
+ expect(formatTimezoneShort('Not/AZone', winter)).toBe('Not/AZone');
10
25
  });
11
26
  });
12
27
  describe('formatTimezoneLabel', () => {
13
28
  it('uses the current tzdata: Kazakhstan is UTC+5 since 2024', () => {
14
- expect(formatTimezoneLabel('Asia/Almaty', winter)).toBe('(GMT+05:00) Asia/Almaty');
15
- expect(formatTimezoneLabel('Asia/Qostanay', winter)).toBe('(GMT+05:00) Asia/Qostanay');
29
+ expect(formatTimezoneLabel('Asia/Almaty', winter)).toBe('(UTC+05:00) Asia/Almaty');
30
+ expect(formatTimezoneLabel('Asia/Qostanay', winter)).toBe('(UTC+05:00) Asia/Qostanay');
16
31
  });
17
32
  it('keeps half-hour zones east of UTC exact', () => {
18
- expect(formatTimezoneLabel('Asia/Kolkata', winter)).toBe('(GMT+05:30) Asia/Kolkata');
19
- expect(formatTimezoneLabel('Asia/Kathmandu', winter)).toBe('(GMT+05:45) Asia/Kathmandu');
20
- expect(formatTimezoneLabel('America/St_Johns', winter)).toBe('(GMT-03:30) America/St_Johns');
33
+ expect(formatTimezoneLabel('Asia/Kolkata', winter)).toBe('(UTC+05:30) Asia/Kolkata');
34
+ expect(formatTimezoneLabel('Asia/Kathmandu', winter)).toBe('(UTC+05:45) Asia/Kathmandu');
35
+ expect(formatTimezoneLabel('America/St_Johns', winter)).toBe('(UTC-03:30) America/St_Johns');
21
36
  });
22
37
  it('resolves the offset at the given instant, not today', () => {
23
38
  expect(getTimezoneOffsetSafe('Australia/Melbourne', winter)).toBe(660);
@@ -25,9 +40,9 @@ describe('formatTimezoneLabel', () => {
25
40
  expect(getTimezoneOffsetSafe('Australia/Melbourne', melbourneDst)).toBe(660);
26
41
  });
27
42
  it('labels Etc and POSIX names the backend accepts but the picker does not list', () => {
28
- expect(formatTimezoneLabel('Etc/GMT+3', winter)).toBe('(GMT-03:00) Etc/GMT+3');
29
- expect(formatTimezoneLabel('Etc/GMT-0', winter)).toBe('(GMT+00:00) Etc/GMT-0');
30
- expect(formatTimezoneLabel('EST', winter)).toBe('(GMT-05:00) EST');
43
+ expect(formatTimezoneLabel('Etc/GMT+3', winter)).toBe('(UTC-03:00) Etc/GMT+3');
44
+ expect(formatTimezoneLabel('Etc/GMT-0', winter)).toBe('(UTC+00:00) Etc/GMT-0');
45
+ expect(formatTimezoneLabel('EST', winter)).toBe('(UTC-05:00) EST');
31
46
  });
32
47
  it('falls back to the bare name for a zone the engine does not know', () => {
33
48
  expect(getTimezoneOffsetSafe('Not/A_Zone')).toBeNull();
@@ -115,7 +130,7 @@ describe('resolveTimezoneOption', () => {
115
130
  it('never rewrites the stored value', () => {
116
131
  expect(resolveTimezoneOption('Europe/Kiev', winter)).toEqual({
117
132
  value: 'Europe/Kiev',
118
- label: '(GMT+02:00) Europe/Kiev'
133
+ label: '(UTC+02:00) Europe/Kiev'
119
134
  });
120
135
  expect(resolveTimezoneOption('Not/A_Zone', winter)).toEqual({
121
136
  value: 'Not/A_Zone',
@@ -9,10 +9,12 @@ export declare function getBrowserTimezone(): string;
9
9
  export declare function canonicalizeTimezone(timezone: string): string | null;
10
10
  /** Offset of `timezone` in minutes east of UTC at instant `at`, or `null` for a zone the engine doesn't know. */
11
11
  export declare function getTimezoneOffsetSafe(timezone: string, at?: Date): number | null;
12
- /** Minutes east of UTC rendered as `GMT+05:00` / `GMT-03:30`. */
12
+ /** Minutes east of UTC rendered as `UTC+05:00` / `UTC-03:30`. */
13
13
  export declare function formatTimezoneOffset(minutes: number): string;
14
- /** The shared picker label, `(GMT+05:00) Asia/Almaty` at instant `at`; a zone the engine doesn't know is labelled by its bare name. */
14
+ /** The shared picker label, `(UTC+05:00) Asia/Almaty` at instant `at`; a zone the engine doesn't know is labelled by its bare name. */
15
15
  export declare function formatTimezoneLabel(timezone: string, at?: Date): string;
16
+ /** Compact badge for a zone, `UTC+05:00`; a zone the engine doesn't know is shown by its bare name. */
17
+ export declare function formatTimezoneShort(timezone: string, at?: Date): string;
16
18
  /** Option for any stored zone name — aliases, `Etc/GMT±N`, POSIX abbreviations — kept verbatim as the value. */
17
19
  export declare function resolveTimezoneOption(timezone: string, at?: Date): TimezoneOption;
18
20
  /** Zones the browser knows plus `UTC` and every non-empty `include` value, sorted by offset at `at` then by name. Pass the stored value as `include` so an old alias stays selectable and is re-saved unchanged. */
@@ -22,19 +22,24 @@ export function getTimezoneOffsetSafe(timezone, at = new Date()) {
22
22
  return null;
23
23
  }
24
24
  }
25
- /** Minutes east of UTC rendered as `GMT+05:00` / `GMT-03:30`. */
25
+ /** Minutes east of UTC rendered as `UTC+05:00` / `UTC-03:30`. */
26
26
  export function formatTimezoneOffset(minutes) {
27
27
  const sign = minutes < 0 ? '-' : '+';
28
28
  const absolute = Math.abs(minutes);
29
29
  const hours = String(Math.floor(absolute / 60)).padStart(2, '0');
30
30
  const mins = String(absolute % 60).padStart(2, '0');
31
- return `GMT${sign}${hours}:${mins}`;
31
+ return `UTC${sign}${hours}:${mins}`;
32
32
  }
33
- /** The shared picker label, `(GMT+05:00) Asia/Almaty` at instant `at`; a zone the engine doesn't know is labelled by its bare name. */
33
+ /** The shared picker label, `(UTC+05:00) Asia/Almaty` at instant `at`; a zone the engine doesn't know is labelled by its bare name. */
34
34
  export function formatTimezoneLabel(timezone, at = new Date()) {
35
35
  const offset = getTimezoneOffsetSafe(timezone, at);
36
36
  return offset === null ? timezone : `(${formatTimezoneOffset(offset)}) ${timezone}`;
37
37
  }
38
+ /** Compact badge for a zone, `UTC+05:00`; a zone the engine doesn't know is shown by its bare name. */
39
+ export function formatTimezoneShort(timezone, at = new Date()) {
40
+ const offset = getTimezoneOffsetSafe(timezone, at);
41
+ return offset === null ? timezone : formatTimezoneOffset(offset);
42
+ }
38
43
  /** Option for any stored zone name — aliases, `Etc/GMT±N`, POSIX abbreviations — kept verbatim as the value. */
39
44
  export function resolveTimezoneOption(timezone, at = new Date()) {
40
45
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushwoosh/dumb-components",
3
- "version": "1.1.237",
3
+ "version": "2.0.0",
4
4
  "description": "React components to build Pushwoosh products",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -65,7 +65,6 @@
65
65
  "styled-components": "^5.3.3 || ^6.0.0"
66
66
  },
67
67
  "scripts": {
68
- "start": "pnpm --dir storybook start",
69
68
  "build": "pushwoosh-engine lib:build",
70
69
  "check": "pnpm run check:lint && pnpm run check:types",
71
70
  "check:lint": "echo \"Run check:lint\" && eslint src",
@@ -1,2 +0,0 @@
1
- export declare function groupTimezonesByOffset(at?: Date): [number, string[]][];
2
- export declare function formatTimezoneShort(timezone: string): string;
@@ -1,47 +0,0 @@
1
- import { formatTwoDigitNumber, getTimezoneOffset } from './helpers';
2
- function getAllTimeZones() {
3
- return Intl.supportedValuesOf('timeZone');
4
- }
5
- function formatTimezoneOffset(minutes) {
6
- const minutesAbs = Math.abs(minutes);
7
- const hours = Math.floor(minutesAbs / 60);
8
- const mins = minutesAbs % 60;
9
- const sign = minutes < 0 ? '-' : '+';
10
- if (mins === 0) {
11
- return `UTC${sign}${hours}`;
12
- } else {
13
- return `UTC${sign}${hours}:${formatTwoDigitNumber(mins)}`;
14
- }
15
- }
16
- export function groupTimezonesByOffset(at = new Date()) {
17
- const zones = getAllTimeZones();
18
- const groups = new Map();
19
- for (const tz of zones) {
20
- try {
21
- const diff = getTimezoneOffset(tz, at);
22
- const arr = groups.get(diff);
23
- if (arr) {
24
- arr.push(tz);
25
- } else {
26
- groups.set(diff, [tz]);
27
- }
28
- } catch (e) {
29
- console.warn(`Cannot get timezone info for "${tz}"`, e);
30
- }
31
- }
32
- const result = [];
33
- for (const [diff, list] of groups) {
34
- list.sort((a, b) => a.localeCompare(b));
35
- result.push([diff, list]);
36
- }
37
- result.sort((a, b) => a[0] - b[0]);
38
- return result;
39
- }
40
- export function formatTimezoneShort(timezone) {
41
- const offsetMinutes = getTimezoneOffset(timezone, new Date());
42
- const formattedOffset = formatTimezoneOffset(offsetMinutes);
43
- if (formattedOffset === 'UTC+0' || formattedOffset === 'UTC-0') {
44
- return 'UTC';
45
- }
46
- return formattedOffset;
47
- }