diff --git a/src/components/views/auth/CountryDropdown.tsx b/src/components/views/auth/CountryDropdown.tsx index fe20730a08b..83f6eca71a4 100644 --- a/src/components/views/auth/CountryDropdown.tsx +++ b/src/components/views/auth/CountryDropdown.tsx @@ -57,16 +57,30 @@ export default class CountryDropdown extends React.Component { public constructor(props: IProps) { super(props); - let defaultCountry: PhoneNumberCountryDefinition = COUNTRIES[0]; + let defaultCountry: PhoneNumberCountryDefinition | undefined; const defaultCountryCode = SdkConfig.get("default_country_code"); if (defaultCountryCode) { const country = COUNTRIES.find((c) => c.iso2 === defaultCountryCode.toUpperCase()); if (country) defaultCountry = country; } + if (!defaultCountry) { + try { + const locale = new Intl.Locale(navigator.language ?? navigator.languages[0]); + const code = locale.region ?? locale.language ?? locale.baseName; + const displayNames = new Intl.DisplayNames(["en"], { type: "region" }); + const displayName = displayNames.of(code).toUpperCase(); + defaultCountry = COUNTRIES.find( + (c) => c.iso2 === code.toUpperCase() || c.name.toUpperCase() === displayName, + ); + } catch (e) { + console.warn("Failed to detect default locale", e); + } + } + this.state = { searchQuery: "", - defaultCountry, + defaultCountry: defaultCountry ?? COUNTRIES[0], }; } diff --git a/test/components/views/auth/CountryDropdown-test.tsx b/test/components/views/auth/CountryDropdown-test.tsx new file mode 100644 index 00000000000..a6beeda233d --- /dev/null +++ b/test/components/views/auth/CountryDropdown-test.tsx @@ -0,0 +1,68 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; +import { render } from "@testing-library/react"; + +import CountryDropdown from "../../../../src/components/views/auth/CountryDropdown"; +import SdkConfig from "../../../../src/SdkConfig"; + +describe("CountryDropdown", () => { + describe("default_country_code", () => { + afterEach(() => { + SdkConfig.unset(); + }); + + it.each([ + ["GB", 44], + ["IE", 353], + ["ES", 34], + ["FR", 33], + ["PL", 48], + ["DE", 49], + ])("should respect configured default country code for %s", (config, defaultCountryCode) => { + SdkConfig.add({ + default_country_code: config, + }); + + const fn = jest.fn(); + render(); + expect(fn).toHaveBeenCalledWith(expect.objectContaining({ prefix: defaultCountryCode.toString() })); + }); + }); + + describe("defaultCountry", () => { + it.each([ + ["en-GB", 44], + ["en-ie", 353], + ["es-ES", 34], + ["fr", 33], + ["pl", 48], + ["de-DE", 49], + ])("should pick appropriate default country for %s", (language, defaultCountryCode) => { + Object.defineProperty(navigator, "language", { + configurable: true, + get() { + return language; + }, + }); + + const fn = jest.fn(); + render(); + expect(fn).toHaveBeenCalledWith(expect.objectContaining({ prefix: defaultCountryCode.toString() })); + }); + }); +});