diff --git a/package-lock.json b/package-lock.json index 77ce5d9c6cb..f4e15f1df69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35199,7 +35199,7 @@ "interactjs": "1.10.27", "lodash-es": "4.17.21", "sortablejs": "1.15.3", - "timezone-groups": "0.9.1", + "timezone-groups": "0.10.2", "type-fest": "4.18.2" }, "devDependencies": { @@ -39260,11 +39260,12 @@ "license": "MIT" }, "packages/calcite-components/node_modules/timezone-groups": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/timezone-groups/-/timezone-groups-0.9.0.tgz", - "integrity": "sha512-6Os7VhGet5ZU5q7Sx5hxzyzyym5+IfVax5m6LBz1LqZGshlcDRykYyyD4j7yhN1yn/jjw4eJpd8KpzyPWKU+nQ==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/timezone-groups/-/timezone-groups-0.10.2.tgz", + "integrity": "sha512-01G9JdlIybA9Njp0wJcGenXKWAw+woWbv6W/oMexWyPs7Nr/S2p2n1NRrMHbHaFzdf+PNNStQp1WILdnAGjYXQ==", + "license": "MIT", "engines": { - "node": ">=20.0.0" + "node": ">=18.12.0" } }, "packages/calcite-components/node_modules/type-fest": { diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 4bbb2ab53de..35e4b356bb5 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -72,7 +72,7 @@ "interactjs": "1.10.27", "lodash-es": "4.17.21", "sortablejs": "1.15.3", - "timezone-groups": "0.9.1", + "timezone-groups": "0.10.2", "type-fest": "4.18.2" }, "devDependencies": { diff --git a/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages.json b/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages.json index 26928e42517..34e09cd6deb 100644 --- a/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages.json +++ b/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages.json @@ -62,7 +62,12 @@ "America/Anguilla": "Anguilla", "America/Antigua": "Antigua", "America/Araguaina": "Araguaina", + "America/Argentina/Buenos_Aires": "Buenos Aires", + "America/Argentina/Catamarca": "Catamarca", + "America/Argentina/Cordoba": "Cordoba", + "America/Argentina/Jujuy": "Jujuy", "America/Argentina/La_Rioja": "La Rioja", + "America/Argentina/Mendoza": "Mendoza", "America/Argentina/Rio_Gallegos": "Rio Gallegos", "America/Argentina/Salta": "Salta", "America/Argentina/San_Juan": "San Juan", @@ -120,6 +125,7 @@ "America/Halifax": "Halifax", "America/Havana": "Havana", "America/Hermosillo": "Hermosillo", + "America/Indiana/Indianapolis": "Indianapolis", "America/Indiana/Knox": "Knox", "America/Indiana/Marengo": "Marengo", "America/Indiana/Petersburg": "Petersburg", @@ -133,6 +139,7 @@ "America/Jamaica": "Jamaica", "America/Jujuy": "Jujuy", "America/Juneau": "Juneau", + "America/Kentucky/Louisville": "Louisville", "America/Kentucky/Monticello": "Monticello", "America/Kralendijk": "Kralendijk", "America/La_Paz": "La Paz", @@ -166,6 +173,7 @@ "America/North_Dakota/Beulah": "Beulah", "America/North_Dakota/Center": "Center", "America/North_Dakota/New_Salem": "New Salem", + "America/Nuuk": "Nuuk", "America/Ojinaga": "Ojinaga", "America/Panama": "Panama", "America/Pangnirtung": "Pangnirtung", @@ -247,6 +255,7 @@ "Asia/Famagusta": "Famagusta", "Asia/Gaza": "Gaza", "Asia/Hebron": "Hebron", + "Asia/Ho_Chi_Minh": "Ho Chi Minh", "Asia/Hong_Kong": "Hong Kong", "Asia/Hovd": "Hovd", "Asia/Irkutsk": "Irkutsk", @@ -257,7 +266,9 @@ "Asia/Kamchatka": "Kamchatka", "Asia/Karachi": "Karachi", "Asia/Katmandu": "Katmandu", + "Asia/Kathmandu": "Kathmandu", "Asia/Khandyga": "Khandyga", + "Asia/Kolkata": "Kolkata", "Asia/Krasnoyarsk": "Krasnoyarsk", "Asia/Kuala_Lumpur": "Kuala Lumpur", "Asia/Kuching": "Kuching", @@ -300,6 +311,7 @@ "Asia/Vientiane": "Vientiane", "Asia/Vladivostok": "Vladivostok", "Asia/Yakutsk": "Yakutsk", + "Asia/Yangon": "Yangon", "Asia/Yekaterinburg": "Yekaterinburg", "Asia/Yerevan": "Yerevan", "Atlantic/Azores": "Azores", @@ -307,6 +319,7 @@ "Atlantic/Canary": "Canary", "Atlantic/Cape_Verde": "Cape Verde", "Atlantic/Faeroe": "Faeroe", + "Atlantic/Faroe": "Faroe Islands", "Atlantic/Madeira": "Madeira", "Atlantic/Reykjavik": "Reykjavik", "Atlantic/South_Georgia": "South Georgia", @@ -325,6 +338,34 @@ "Australia/Perth": "Perth", "Australia/Sydney": "Sydney", "Europe/Amsterdam": "Amsterdam", + "Etc/GMT": "GMT", + "Etc/GMT+1": "GMT-1", + "Etc/GMT+10": "GMT-10", + "Etc/GMT+11": "GMT-11", + "Etc/GMT+12": "GMT-12", + "Etc/GMT+2": "GMT-2", + "Etc/GMT+3": "GMT-3", + "Etc/GMT+4": "GMT-4", + "Etc/GMT+5": "GMT-5", + "Etc/GMT+6": "GMT-6", + "Etc/GMT+7": "GMT-7", + "Etc/GMT+8": "GMT-8", + "Etc/GMT+9": "GMT-9", + "Etc/GMT-1": "GMT+1", + "Etc/GMT-10": "GMT+10", + "Etc/GMT-11": "GMT+11", + "Etc/GMT-12": "GMT+12", + "Etc/GMT-13": "GMT+13", + "Etc/GMT-14": "GMT+14", + "Etc/GMT-2": "GMT+2", + "Etc/GMT-3": "GMT+3", + "Etc/GMT-4": "GMT+4", + "Etc/GMT-5": "GMT+5", + "Etc/GMT-6": "GMT+6", + "Etc/GMT-7": "GMT+7", + "Etc/GMT-8": "GMT+8", + "Etc/GMT-9": "GMT+9", + "Etc/UTC": "Coordinated Universal Time (UTC)", "Europe/Andorra": "Andorra", "Europe/Astrakhan": "Astrakhan", "Europe/Athens": "Athens", @@ -347,6 +388,7 @@ "Europe/Kaliningrad": "Kaliningrad", "Europe/Kiev": "Kiev", "Europe/Kirov": "Kirov", + "Europe/Kyiv": "Kyiv", "Europe/Lisbon": "Lisbon", "Europe/Ljubljana": "Ljubljana", "Europe/London": "London", @@ -385,6 +427,7 @@ "Europe/Zaporozhye": "Zaporozhye", "Europe/Zurich": "Zurich", "Indian/Antananarivo": "Antananarivo", + "Factory": "System Default Time", "Indian/Chagos": "Chagos", "Indian/Christmas": "Christmas Island", "Indian/Cocos": "Cocos", @@ -411,6 +454,7 @@ "Pacific/Guam": "Guam", "Pacific/Honolulu": "Honolulu", "Pacific/Johnston": "Johnston", + "Pacific/Kanton": "Kanton", "Pacific/Kiritimati": "Kiritimati", "Pacific/Kosrae": "Kosrae", "Pacific/Kwajalein": "Kwajalein", @@ -419,7 +463,7 @@ "Pacific/Midway": "Midway", "Pacific/Nauru": "Nauru", "Pacific/Niue": "Niue", - "Pacific/Norfolk": "Norfolk", + "Pacific/Norfolk": "Norfolk Island", "Pacific/Noumea": "Noumea", "Pacific/Pago_Pago": "Pago Pago", "Pacific/Palau": "Palau", @@ -442,6 +486,7 @@ "Atlantic": "Atlantic", "Australia": "Australia", "Europe": "Europe", + "Global": "Global", "Indian": "Indian", "Pacific": "Pacific", "AD": "Andorra", diff --git a/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages_en.json b/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages_en.json index 26928e42517..34e09cd6deb 100644 --- a/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages_en.json +++ b/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages_en.json @@ -62,7 +62,12 @@ "America/Anguilla": "Anguilla", "America/Antigua": "Antigua", "America/Araguaina": "Araguaina", + "America/Argentina/Buenos_Aires": "Buenos Aires", + "America/Argentina/Catamarca": "Catamarca", + "America/Argentina/Cordoba": "Cordoba", + "America/Argentina/Jujuy": "Jujuy", "America/Argentina/La_Rioja": "La Rioja", + "America/Argentina/Mendoza": "Mendoza", "America/Argentina/Rio_Gallegos": "Rio Gallegos", "America/Argentina/Salta": "Salta", "America/Argentina/San_Juan": "San Juan", @@ -120,6 +125,7 @@ "America/Halifax": "Halifax", "America/Havana": "Havana", "America/Hermosillo": "Hermosillo", + "America/Indiana/Indianapolis": "Indianapolis", "America/Indiana/Knox": "Knox", "America/Indiana/Marengo": "Marengo", "America/Indiana/Petersburg": "Petersburg", @@ -133,6 +139,7 @@ "America/Jamaica": "Jamaica", "America/Jujuy": "Jujuy", "America/Juneau": "Juneau", + "America/Kentucky/Louisville": "Louisville", "America/Kentucky/Monticello": "Monticello", "America/Kralendijk": "Kralendijk", "America/La_Paz": "La Paz", @@ -166,6 +173,7 @@ "America/North_Dakota/Beulah": "Beulah", "America/North_Dakota/Center": "Center", "America/North_Dakota/New_Salem": "New Salem", + "America/Nuuk": "Nuuk", "America/Ojinaga": "Ojinaga", "America/Panama": "Panama", "America/Pangnirtung": "Pangnirtung", @@ -247,6 +255,7 @@ "Asia/Famagusta": "Famagusta", "Asia/Gaza": "Gaza", "Asia/Hebron": "Hebron", + "Asia/Ho_Chi_Minh": "Ho Chi Minh", "Asia/Hong_Kong": "Hong Kong", "Asia/Hovd": "Hovd", "Asia/Irkutsk": "Irkutsk", @@ -257,7 +266,9 @@ "Asia/Kamchatka": "Kamchatka", "Asia/Karachi": "Karachi", "Asia/Katmandu": "Katmandu", + "Asia/Kathmandu": "Kathmandu", "Asia/Khandyga": "Khandyga", + "Asia/Kolkata": "Kolkata", "Asia/Krasnoyarsk": "Krasnoyarsk", "Asia/Kuala_Lumpur": "Kuala Lumpur", "Asia/Kuching": "Kuching", @@ -300,6 +311,7 @@ "Asia/Vientiane": "Vientiane", "Asia/Vladivostok": "Vladivostok", "Asia/Yakutsk": "Yakutsk", + "Asia/Yangon": "Yangon", "Asia/Yekaterinburg": "Yekaterinburg", "Asia/Yerevan": "Yerevan", "Atlantic/Azores": "Azores", @@ -307,6 +319,7 @@ "Atlantic/Canary": "Canary", "Atlantic/Cape_Verde": "Cape Verde", "Atlantic/Faeroe": "Faeroe", + "Atlantic/Faroe": "Faroe Islands", "Atlantic/Madeira": "Madeira", "Atlantic/Reykjavik": "Reykjavik", "Atlantic/South_Georgia": "South Georgia", @@ -325,6 +338,34 @@ "Australia/Perth": "Perth", "Australia/Sydney": "Sydney", "Europe/Amsterdam": "Amsterdam", + "Etc/GMT": "GMT", + "Etc/GMT+1": "GMT-1", + "Etc/GMT+10": "GMT-10", + "Etc/GMT+11": "GMT-11", + "Etc/GMT+12": "GMT-12", + "Etc/GMT+2": "GMT-2", + "Etc/GMT+3": "GMT-3", + "Etc/GMT+4": "GMT-4", + "Etc/GMT+5": "GMT-5", + "Etc/GMT+6": "GMT-6", + "Etc/GMT+7": "GMT-7", + "Etc/GMT+8": "GMT-8", + "Etc/GMT+9": "GMT-9", + "Etc/GMT-1": "GMT+1", + "Etc/GMT-10": "GMT+10", + "Etc/GMT-11": "GMT+11", + "Etc/GMT-12": "GMT+12", + "Etc/GMT-13": "GMT+13", + "Etc/GMT-14": "GMT+14", + "Etc/GMT-2": "GMT+2", + "Etc/GMT-3": "GMT+3", + "Etc/GMT-4": "GMT+4", + "Etc/GMT-5": "GMT+5", + "Etc/GMT-6": "GMT+6", + "Etc/GMT-7": "GMT+7", + "Etc/GMT-8": "GMT+8", + "Etc/GMT-9": "GMT+9", + "Etc/UTC": "Coordinated Universal Time (UTC)", "Europe/Andorra": "Andorra", "Europe/Astrakhan": "Astrakhan", "Europe/Athens": "Athens", @@ -347,6 +388,7 @@ "Europe/Kaliningrad": "Kaliningrad", "Europe/Kiev": "Kiev", "Europe/Kirov": "Kirov", + "Europe/Kyiv": "Kyiv", "Europe/Lisbon": "Lisbon", "Europe/Ljubljana": "Ljubljana", "Europe/London": "London", @@ -385,6 +427,7 @@ "Europe/Zaporozhye": "Zaporozhye", "Europe/Zurich": "Zurich", "Indian/Antananarivo": "Antananarivo", + "Factory": "System Default Time", "Indian/Chagos": "Chagos", "Indian/Christmas": "Christmas Island", "Indian/Cocos": "Cocos", @@ -411,6 +454,7 @@ "Pacific/Guam": "Guam", "Pacific/Honolulu": "Honolulu", "Pacific/Johnston": "Johnston", + "Pacific/Kanton": "Kanton", "Pacific/Kiritimati": "Kiritimati", "Pacific/Kosrae": "Kosrae", "Pacific/Kwajalein": "Kwajalein", @@ -419,7 +463,7 @@ "Pacific/Midway": "Midway", "Pacific/Nauru": "Nauru", "Pacific/Niue": "Niue", - "Pacific/Norfolk": "Norfolk", + "Pacific/Norfolk": "Norfolk Island", "Pacific/Noumea": "Noumea", "Pacific/Pago_Pago": "Pago Pago", "Pacific/Palau": "Palau", @@ -442,6 +486,7 @@ "Atlantic": "Atlantic", "Australia": "Australia", "Europe": "Europe", + "Global": "Global", "Indian": "Indian", "Pacific": "Pacific", "AD": "Andorra", diff --git a/packages/calcite-components/src/components/input-time-zone/input-time-zone.e2e.ts b/packages/calcite-components/src/components/input-time-zone/input-time-zone.e2e.ts index 1571d349924..0bcfd22545e 100644 --- a/packages/calcite-components/src/components/input-time-zone/input-time-zone.e2e.ts +++ b/packages/calcite-components/src/components/input-time-zone/input-time-zone.e2e.ts @@ -22,7 +22,6 @@ import { getCity, toUserFriendlyName } from "./utils"; * * - tests need to have an emulated time zone * - test time zones should preferably be unaffected by daylight savings time, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for more info - * - not all time zones are supported in Puppeteer's bundled Chromium, so we patch the Intl API with test-specific values/logic */ describe("calcite-input-time-zone", () => { @@ -43,7 +42,7 @@ describe("calcite-input-time-zone", () => { async function simpleTestProvider(): Promise { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent(overrideSupportedTimeZones(html``)); + await page.setContent(html``); return { page, @@ -62,7 +61,7 @@ describe("calcite-input-time-zone", () => { describe("formAssociated", () => { formAssociated( { - tagOrHTML: overrideSupportedTimeZones(html``), + tagOrHTML: html``, beforeContent: async (page) => { await page.emulateTimezone(testTimeZoneItems[0].name); }, @@ -84,7 +83,7 @@ describe("calcite-input-time-zone", () => { describe("labelable", () => { labelable({ - tagOrHTML: overrideSupportedTimeZones(html``), + tagOrHTML: html``, beforeContent: async (page) => { await page.emulateTimezone(testTimeZoneItems[0].name); }, @@ -136,14 +135,6 @@ describe("calcite-input-time-zone", () => { openClose.initial("calcite-input-time-zone", { beforeContent: async (page) => { await page.emulateTimezone(testTimeZoneItems[0].name); - - // we add the override script this way because `setContent` was already used before this hook, and calling it again will result in an error. - await page.evaluate( - (supportedTimeZoneOverrideHtml) => - document.body.insertAdjacentHTML("beforeend", supportedTimeZoneOverrideHtml), - overrideSupportedTimeZones(""), - ); - await page.waitForChanges(); }, }); @@ -157,9 +148,7 @@ describe("calcite-input-time-zone", () => { it(`selects default time zone for "${name}"`, async () => { const page = await newE2EPage(); await page.emulateTimezone(name); - await page.setContent( - overrideSupportedTimeZones(html``), - ); + await page.setContent(html``); await page.waitForChanges(); const input = await page.find("calcite-input-time-zone"); @@ -176,9 +165,7 @@ describe("calcite-input-time-zone", () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); await page.setContent( - overrideSupportedTimeZones( - html``, - ), + html``, ); const input = await page.find("calcite-input-time-zone"); @@ -193,9 +180,7 @@ describe("calcite-input-time-zone", () => { it("ignores invalid values", async () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent( - overrideSupportedTimeZones(html``), - ); + await page.setContent(html``); const input = await page.find("calcite-input-time-zone"); @@ -209,9 +194,7 @@ describe("calcite-input-time-zone", () => { it("omits filtered or non-localized time zones (incoming to browser)", async () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent( - await overrideSupportedTimeZones(html``), - ); + await page.setContent(await html``); const input = await page.find("calcite-input-time-zone"); @@ -224,15 +207,13 @@ describe("calcite-input-time-zone", () => { it("looks up in label and time zone groups (not displayed)", async () => { const displayLabelSearchTerm = "Guam"; - const groupedTimeZoneSearchTerm = "Chuuk"; + const groupedTimeZoneSearchTerm = "Moresby"; const gmtSearchTerm = "GMT-12"; const searchTerms = [displayLabelSearchTerm, groupedTimeZoneSearchTerm, gmtSearchTerm]; const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent( - await overrideSupportedTimeZones(html``), - ); + await page.setContent(html``); const input = await page.find("calcite-input-time-zone"); @@ -290,9 +271,7 @@ describe("calcite-input-time-zone", () => { it(`selects default time zone for "${name}"`, async () => { const page = await newE2EPage(); await page.emulateTimezone(name); - await page.setContent( - await overrideSupportedTimeZones(html``), - ); + await page.setContent(html``); await page.waitForChanges(); const input = await page.find("calcite-input-time-zone"); @@ -309,9 +288,7 @@ describe("calcite-input-time-zone", () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); await page.setContent( - await overrideSupportedTimeZones( - html``, - ), + html``, ); const input = await page.find("calcite-input-time-zone"); @@ -327,9 +304,7 @@ describe("calcite-input-time-zone", () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); await page.setContent( - await overrideSupportedTimeZones( - html``, - ), + html``, ); const input = await page.find("calcite-input-time-zone"); @@ -348,9 +323,7 @@ describe("calcite-input-time-zone", () => { it(`selects default time zone for "${name}"`, async () => { const page = await newE2EPage(); await page.emulateTimezone(name); - await page.setContent( - await overrideSupportedTimeZones(html``), - ); + await page.setContent(html``); await page.waitForChanges(); const input = await page.find("calcite-input-time-zone"); @@ -367,12 +340,7 @@ describe("calcite-input-time-zone", () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); await page.setContent( - await overrideSupportedTimeZones( - html``, - ), + html``, ); const input = await page.find("calcite-input-time-zone"); @@ -388,11 +356,10 @@ describe("calcite-input-time-zone", () => { it("ignores invalid values", async () => { const page = await newE2EPage(); + await page.emulateTimezone(testTimeZoneItems[0].name); await page.setContent( - await overrideSupportedTimeZones( - html``, - ), + html``, ); const input = await page.find("calcite-input-time-zone"); @@ -405,21 +372,63 @@ describe("calcite-input-time-zone", () => { toUserFriendlyName(getCity(testTimeZoneItems[0].name)), ); }); + + it("properly sets region label when setting value programmatically", async () => { + const page = await newE2EPage(); + + await page.emulateTimezone(testTimeZoneItems[0].name); + await page.setContent(html``); + + const input = await page.find("calcite-input-time-zone"); + const region = "America/New_York"; + + input.setProperty("value", region); + await page.waitForChanges(); + + expect(await input.getProperty("value")).toBe(region); + + const timeZoneItem = await page.find("calcite-input-time-zone >>> calcite-combobox-item[selected]"); + + expect(await timeZoneItem.getProperty("textLabel")).toMatch(toUserFriendlyName(getCity(region))); + }); + + it("maps deprecated time zones to aliases", async () => { + const deprecatedTimeZone1 = "Asia/Calcutta"; + const aliasTimeZone1 = "Asia/Kolkata"; + + const page = await newE2EPage(); + + await page.emulateTimezone(testTimeZoneItems[0].name); + await page.setContent( + html``, + ); + + const input = await page.find("calcite-input-time-zone"); + + expect(await input.getProperty("value")).toBe(aliasTimeZone1); + + const deprecatedTimeZone2 = "Asia/Istanbul"; + const aliasTimeZone2 = "Europe/Istanbul"; + + input.setProperty("value", deprecatedTimeZone2); + await page.waitForChanges(); + + expect(await input.getProperty("value")).toBe(aliasTimeZone2); + }); }); }); describe("clearable", () => { it("does not allow users to deselect a time zone value by default", async () => { const page = await newE2EPage(); - await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent( - overrideSupportedTimeZones(html` - - `), - ); + await page.emulateTimezone(testTimeZoneItems[1].name); + await page.setContent(html` + + `); await page.waitForChanges(); let selectedTimeZoneItem = await page.find("calcite-input-time-zone >>> calcite-combobox-item[selected]"); + await selectedTimeZoneItem.click(); await page.waitForChanges(); @@ -445,9 +454,7 @@ describe("calcite-input-time-zone", () => { page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); await page.setContent( - overrideSupportedTimeZones( - html` `, - ), + html` `, ); input = await page.find("calcite-input-time-zone"); }); @@ -471,9 +478,7 @@ describe("calcite-input-time-zone", () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); await page.setContent( - overrideSupportedTimeZones( - html``, - ), + html``, ); const input = await page.find("calcite-input-time-zone"); @@ -490,9 +495,7 @@ describe("calcite-input-time-zone", () => { it("can be cleared on initialization when clearable is enabled", async () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent( - overrideSupportedTimeZones(html``), - ); + await page.setContent(html``); const input = await page.find("calcite-input-time-zone"); expect(await input.getProperty("value")).toBe(""); @@ -501,9 +504,7 @@ describe("calcite-input-time-zone", () => { it("selects user time zone value when value is not set and clearable is enabled", async () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent( - overrideSupportedTimeZones(html``), - ); + await page.setContent(html``); const input = await page.find("calcite-input-time-zone"); expect(await input.getProperty("value")).toBe(`${testTimeZoneItems[0].offset}`); @@ -530,9 +531,7 @@ describe("calcite-input-time-zone", () => { const page = await newE2EPage(); await page.emulateTimezone(initialTimeZoneItem.name); await page.setContent( - overrideSupportedTimeZones( - html` `, - ), + html` `, ); const input = await page.find("calcite-input-time-zone"); @@ -563,9 +562,7 @@ describe("calcite-input-time-zone", () => { it("supports setting maxItems to display", async () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent( - overrideSupportedTimeZones(html``), - ); + await page.setContent(html``); const internalCombobox = await page.find("calcite-input-time-zone >>> calcite-combobox"); // we assume maxItems works properly on combobox @@ -575,7 +572,8 @@ describe("calcite-input-time-zone", () => { it("recreates time zone items when item-dependent props change", async () => { const page = await newE2EPage(); await page.emulateTimezone(testTimeZoneItems[0].name); - await page.setContent(overrideSupportedTimeZones(html``)); + await page.setContent(html``); + await page.waitForChanges(); const inputTimeZone = await page.find("calcite-input-time-zone"); let prevComboboxItem = await page.find("calcite-input-time-zone >>> calcite-combobox-item"); @@ -593,7 +591,7 @@ describe("calcite-input-time-zone", () => { expect(currComboboxItem).not.toBe(prevComboboxItem); prevComboboxItem = currComboboxItem; - inputTimeZone.setProperty("mode", "list"); + inputTimeZone.setProperty("mode", "name"); await page.waitForChanges(); currComboboxItem = await page.find("calcite-input-time-zone >>> calcite-combobox-item"); @@ -620,21 +618,13 @@ describe("calcite-input-time-zone", () => { describe("displays UTC or GMT based on user's locale (default)", () => { it("displays GMT for GMT-preferred locale", async () => { - await page.setContent( - overrideSupportedTimeZones( - html``, - ), - ); + await page.setContent(html``); await assertItemLabelMatches(page, "GMT"); }); it("displays UTC for UTC-preferred locale", async () => { - await page.setContent( - overrideSupportedTimeZones( - html``, - ), - ); + await page.setContent(html``); await assertItemLabelMatches(page, "UTC"); }); @@ -642,9 +632,7 @@ describe("calcite-input-time-zone", () => { it("supports GMT as a style", async () => { await page.setContent( - overrideSupportedTimeZones( - html``, - ), + html``, ); await assertItemLabelMatches(page, "GMT"); @@ -652,62 +640,10 @@ describe("calcite-input-time-zone", () => { it("supports UTC as a style", async () => { await page.setContent( - overrideSupportedTimeZones( - html``, - ), + html``, ); await assertItemLabelMatches(page, "UTC"); }); }); }); - -/** - * This helper overrides supported time zones for testing purposes - * - * @param testHtml - */ -function overrideSupportedTimeZones(testHtml: string): string { - return html` - ${testHtml}`; -} diff --git a/packages/calcite-components/src/components/input-time-zone/input-time-zone.tsx b/packages/calcite-components/src/components/input-time-zone/input-time-zone.tsx index d27f07ef92f..f0ff733dcb6 100644 --- a/packages/calcite-components/src/components/input-time-zone/input-time-zone.tsx +++ b/packages/calcite-components/src/components/input-time-zone/input-time-zone.tsx @@ -51,13 +51,13 @@ import { CSS } from "./resources"; import { createTimeZoneItems, findTimeZoneItemByProp, - getMessageOrKeyFallback, + getNormalizer, getSelectedRegionTimeZoneLabel, getUserTimeZoneName, getUserTimeZoneOffset, } from "./utils"; import { InputTimeZoneMessages } from "./assets/input-time-zone/t9n"; -import { OffsetStyle, TimeZoneItem, TimeZoneItemGroup, TimeZoneMode } from "./interfaces"; +import { OffsetStyle, TimeZone, TimeZoneItem, TimeZoneItemGroup, TimeZoneMode } from "./interfaces"; @Component({ tag: "calcite-input-time-zone", @@ -138,7 +138,12 @@ export class InputTimeZone @Watch("mode") @Watch("referenceDate") handleTimeZoneItemPropsChange(): void { - this.updateTimeZoneItemsAndSelection(); + if (!this.timeZoneItems) { + return; + } + + this.updateTimeZoneItems(); + this.updateTimeZoneSelection(); } /** @@ -242,9 +247,15 @@ export class InputTimeZone handleValueChange(value: string, oldValue: string): void { value = this.normalizeValue(value); - if (!value && this.clearable) { - this.value = value; - this.selectedTimeZoneItem = null; + if (!value) { + if (this.clearable) { + this.value = value; + this.selectedTimeZoneItem = null; + return; + } + + this.value = oldValue; + this.selectedTimeZoneItem = this.findTimeZoneItem(oldValue); return; } @@ -256,6 +267,9 @@ export class InputTimeZone } this.selectedTimeZoneItem = timeZoneItem; + requestAnimationFrame(() => { + this.overrideSelectedLabelForRegion(this.open); + }); } /** @@ -356,14 +370,19 @@ export class InputTimeZone * @private */ private overrideSelectedLabelForRegion(open: boolean): void { - if (this.mode !== "region" || !this.selectedTimeZoneItem) { + if (this.mode !== "region" || !this.selectedTimeZoneItem || !this.comboboxEl?.selectedItems) { return; } const { label, metadata } = this.selectedTimeZoneItem; - this.comboboxEl.selectedItems[0].textLabel = open - ? label - : getSelectedRegionTimeZoneLabel(label, metadata.country, this.messages); + + requestAnimationFrame(() => { + const itemLabel = + !metadata.country || open + ? label + : getSelectedRegionTimeZoneLabel(label, metadata.country, this.messages); + this.comboboxEl.selectedItems[0].textLabel = itemLabel; + }); } private onComboboxBeforeClose = (event: CustomEvent): void => { @@ -391,7 +410,6 @@ export class InputTimeZone } const selected = this.findTimeZoneItemByLabel(selectedItem.textLabel); - const selectedValue = `${selected.value}`; if (this.value === selectedValue && selected.label === this.selectedTimeZoneItem.label) { @@ -423,9 +441,11 @@ export class InputTimeZone return findTimeZoneItemByProp(this.timeZoneItems, "label", label); } - private async updateTimeZoneItemsAndSelection(): Promise { + private async updateTimeZoneItems(): Promise { this.timeZoneItems = await this.createTimeZoneItems(); + } + private async updateTimeZoneSelection(): Promise { if (this.value === "" && this.clearable) { this.selectedTimeZoneItem = null; return; @@ -475,15 +495,22 @@ export class InputTimeZone } private normalizeValue(value: string | null): string { - return value === null ? "" : value; + value = value === null ? "" : value; + + return value ? this.normalizer(value) : value; } + private normalizer: (timeZone: TimeZone) => TimeZone; + async componentWillLoad(): Promise { setUpLoadableComponent(this); - await setUpMessages(this); + const [, normalizer] = await Promise.all([setUpMessages(this), getNormalizer(this.mode)]); + + this.normalizer = normalizer; + await this.updateTimeZoneItems(); this.value = this.normalizeValue(this.value); - await this.updateTimeZoneItemsAndSelection(); + await this.updateTimeZoneSelection(); const selectedValue = this.selectedTimeZoneItem ? `${this.selectedTimeZoneItem.value}` : null; afterConnectDefaultValueSet(this, selectedValue); @@ -563,10 +590,7 @@ export class InputTimeZone private renderRegionItems(): VNode[] { return (this.timeZoneItems as TimeZoneItemGroup[]).flatMap(({ label, items }) => ( - + {items.map((item) => { const selected = this.selectedTimeZoneItem === item; const { label, value } = item; @@ -574,7 +598,7 @@ export class InputTimeZone return ( TimeZone> { + if (mode === "offset") { + return (timeZone: TimeZone) => timeZone; + } + + const { normalize } = await import("timezone-groups/dist/utils/time-zones.mjs"); + return normalize; +} + export async function createTimeZoneItems( locale: SupportedLocale, messages: InputTimeZoneMessages, @@ -92,34 +85,45 @@ export async function createTimeZoneItems( const referenceDateInMs: number = referenceDate.getTime(); if (mode === "region") { - const [{ groupByRegion }, { getCountry }] = await Promise.all([ + const [{ groupByRegion }, { getCountry, global: globalLabel }] = await Promise.all([ import("timezone-groups/dist/groupByRegion/index.mjs"), - import("timezone-groups/dist/utils/country.mjs"), + import("timezone-groups/dist/utils/region.mjs"), ]); const groups = await groupByRegion(); return groups .map(({ label: region, tzs }) => { return { - label: region, + label: getMessageOrKeyFallback(messages, region), items: tzs.map((timeZone) => { const decimalOffset = timeZoneOffsetToDecimal( getTimeZoneShortOffset(timeZone, effectiveLocale, referenceDateInMs), ); + const filterValue = + toUserFriendlyName(timeZone) + + (region === globalLabel + ? // we add the global label as global group items do not have a unifying region/name + getTimeZoneLabel(globalLabel, messages) + : ""); + const label = getTimeZoneLabel(timeZone, messages); + const countryCode = getCountry(timeZone); + const country = getMessageOrKeyFallback(messages, countryCode); return { - label: getTimeZoneLabel(timeZone, messages), + label, value: timeZone, - filterValue: toUserFriendlyName(timeZone), + filterValue, metadata: { offset: decimalOffset, - country: getCountry(timeZone), + country: country === label ? undefined : country, }, }; }), }; }) - .sort((groupA, groupB) => groupA.label.localeCompare(groupB.label)); + .sort((groupA, groupB) => + groupA.label === globalLabel ? -1 : groupB.label === globalLabel ? 1 : groupA.label.localeCompare(groupB.label), + ); } const [{ groupByOffset }, { DateEngine }] = await Promise.all([ @@ -134,6 +138,7 @@ export async function createTimeZoneItems( }); const listFormatter = new Intl.ListFormat(locale, { style: "long", type: "conjunction" }); + const offsetTimeZoneNameBlockList = ["Factory", "Etc/UTC"]; // we remove blocked entries from tzs and adjust label indices accordingly groups.forEach((group) => { @@ -141,13 +146,13 @@ export async function createTimeZoneItems( let removedSoFar = 0; group.tzs.forEach((tz, index) => { - if (timeZoneNameBlockList.includes(tz)) { + if (offsetTimeZoneNameBlockList.includes(tz)) { removedSoFar++; } indexOffsets[index] = removedSoFar; }); - group.tzs = group.tzs.filter((tz) => !timeZoneNameBlockList.includes(tz)); + group.tzs = group.tzs.filter((tz) => !offsetTimeZoneNameBlockList.includes(tz)); group.labelTzIdx = group.labelTzIdx .map((index) => index - indexOffsets[index])