Skip to content

Commit

Permalink
[web] Adapt l10n/LocaleSelector to core/Selector
Browse files Browse the repository at this point in the history
  • Loading branch information
dgdavid committed Jan 24, 2024
1 parent 737ec2e commit 3c8687b
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 32 deletions.
47 changes: 15 additions & 32 deletions web/src/components/l10n/LocaleSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,13 @@
import React, { useState } from "react";

import { _ } from "~/i18n";
import { ListSearch } from "~/components/core";
import { ListSearch, Selector } from "~/components/core";
import { noop } from "~/utils";

/**
* @typedef {import ("~/client/l10n").Locale} Locale
*/

const ListBox = ({ children, ...props }) => {
return (
<ul data-type="agama/list" data-of="agama/locales" {...props}>{children}</ul>
);
};

const ListBoxItem = ({ isSelected, children, onClick, ...props }) => {
if (isSelected) props['aria-selected'] = true;

return (
<li
role="option"
onClick={onClick}
{ ...props }
>
{children}
</li>
);
};

/**
* Content for a locale item.
* @component
Expand All @@ -58,11 +38,11 @@ const ListBoxItem = ({ isSelected, children, onClick, ...props }) => {
*/
const LocaleItem = ({ locale }) => {
return (
<>
<div data-items-type="agama/locales">
<div>{locale.name}</div>
<div>{locale.territory}</div>
<div>{locale.id}</div>
</>
</div>
);
};

Expand All @@ -80,23 +60,26 @@ export default function LocaleSelector({ value, locales = [], onChange = noop })
const [filteredLocales, setFilteredLocales] = useState(locales);

const searchHelp = _("Filter by language, territory or locale code");
const onSelectionChange = (selection) => onChange(selection[0]);

return (
<>
<div className="sticky-top-0">
<ListSearch placeholder={searchHelp} elements={locales} onChange={setFilteredLocales} />
</div>
<ListBox aria-label={_("Available locales")} role="listbox">
{ filteredLocales.map((locale, index) => (
<ListBoxItem
key={`locale-${index}`}
onClick={() => onChange(locale.id)}
isSelected={locale.id === value}
>
<Selector
// FIXME: when filtering, these are not the available locales but the
// filtered ones.
aria-label={_("Available locales")}
selectedIds={value}
onSelectionChange={onSelectionChange}
>
{ filteredLocales.map((locale) => (
<Selector.Option id={locale.id} key={locale.id}>
<LocaleItem locale={locale} />
</ListBoxItem>
</Selector.Option>
))}
</ListBox>
</Selector>
</>
);
}
77 changes: 77 additions & 0 deletions web/src/components/l10n/LocaleSelector.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (c) [2024] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import React from "react";
import { act, screen, waitFor, within } from "@testing-library/react";
import { plainRender } from "~/test-utils";
import { LocaleSelector } from "~/components/l10n";

const locales = [
{ id: "es_ES", name: "Spanish", territory: "Spain" },
{ id: "en_US", name: "English", territory: "United States" }
];

const onChange = jest.fn();

describe("LocaleSelector", () => {
it("renders a selector for given locales displaying their name, territory, and id", () => {
plainRender(
<LocaleSelector locales={locales} aria-label="Available locales" />
);

const selector = screen.getByRole("grid", { name: "Available locales" });

const options = within(selector).getAllByRole("row");
expect(options.length).toEqual(locales.length);

within(selector).getByRole("row", { name: "Spanish Spain es_ES" });
within(selector).getByRole("row", { name: "English United States en_US" });
});

it("renders an input for filtering locales", async () => {
const { user } = plainRender(
<LocaleSelector locales={locales} aria-label="Available locales" />
);

const filterInput = screen.getByRole("search");
screen.getByRole("row", { name: "English United States en_US" });

await act(async () => await user.type(filterInput, "Span"));
await waitFor(() => {
const englishOption = screen.queryByRole("row", { name: "English United States en_US" });
expect(englishOption).not.toBeInTheDocument();
});
});

describe("when user clicks an option", () => {
it("calls the #onChange callback with the locale id", async () => {
const { user } = plainRender(
<LocaleSelector locales={locales} onChange={onChange} aria-label="Available locales" />
);

const selector = screen.getByRole("grid", { name: "Available locales" });
const english = within(selector).getByRole("row", { name: "English United States en_US" });
await user.click(english);

expect(onChange).toHaveBeenCalledWith("en_US");
});
});
});

0 comments on commit 3c8687b

Please sign in to comment.