Skip to content

Commit

Permalink
Only show language switcher for schools with multiple languages #166
Browse files Browse the repository at this point in the history
  • Loading branch information
caebr authored May 1, 2024
1 parent 8c08bd9 commit a778f7d
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 102 deletions.
168 changes: 83 additions & 85 deletions cypress/e2e/serviceNavigation.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,11 @@ describe("Service Navigation", () => {
describe("desktop", () => {
beforeEach(() => {
cy.resizeToDesktop();
cy.visit("/index.html");
});

it("open/closes user settings", () => {
// User settings menu initially closed
cy.get("button[aria-label='Menü Benutzereinstellungen']")
.as("toggle")
.should("be.visible")
.ariaExpanded(false);
cy.get("bkd-user-settings")
.find('ul[role="menu"]')
.as("serviceNav")
.should("not.be.visible");

// Open user settings menu
cy.get("@toggle").click();
cy.get("@toggle").ariaExpanded(true);
cy.get("@serviceNav").should("be.visible");
cy.visit("/index.html");
openServiceNavigation();

// Close user settings menu
cy.get("@toggle").click();
Expand All @@ -30,20 +17,10 @@ describe("Service Navigation", () => {
});

it("renders user settings in the service navigation", () => {
// User settings menu initially closed
cy.get("button[aria-label='Menü Benutzereinstellungen']")
.as("toggle")
.should("be.visible")
.ariaExpanded(false);
cy.get("bkd-user-settings")
.find('ul[role="menu"]')
.as("serviceNav")
.should("not.be.visible");
cy.visit("/index.html");
openServiceNavigation();

// Open user settings menu
cy.get("@toggle").click();
cy.get("@serviceNav")
.should("be.visible")
.find("a")
.should((links) =>
expect(
Expand All @@ -60,20 +37,8 @@ describe("Service Navigation", () => {
// Apparently the triggering of the 'keydown' event does not work
// when run headless
it.skip("closes user settings on escape", () => {
// User settings menu initially closed
cy.get("button[aria-label='Menü Benutzereinstellungen']")
.as("toggle")
.should("be.visible")
.ariaExpanded(false);
cy.get("bkd-user-settings")
.find('ul[role="menu"]')
.as("serviceNav")
.should("not.be.visible");

// Open user settings menu
cy.get("@toggle").click();
cy.get("@toggle").ariaExpanded(true);
cy.get("@serviceNav").should("be.visible");
cy.visit("/index.html");
openServiceNavigation();

// Close user settings menu
cy.document().trigger("keydown", { key: "Escape" });
Expand All @@ -82,20 +47,8 @@ describe("Service Navigation", () => {
});

it("closes user settings on select", () => {
// User settings menu initially closed
cy.get("button[aria-label='Menü Benutzereinstellungen']")
.as("toggle")
.should("be.visible")
.ariaExpanded(false);
cy.get("bkd-user-settings")
.find('ul[role="menu"]')
.as("serviceNav")
.should("not.be.visible");

// Open user settings menu
cy.get("@toggle").click();
cy.get("@toggle").ariaExpanded(true);
cy.get("@serviceNav").should("be.visible");
cy.visit("/index.html");
openServiceNavigation();

// Close user settings menu
cy.get("@serviceNav")
Expand All @@ -107,28 +60,19 @@ describe("Service Navigation", () => {
});

it("closes user settings on click away", () => {
// User settings menu initially closed
cy.get("button[aria-label='Menü Benutzereinstellungen']")
.as("toggle")
.should("be.visible")
.ariaExpanded(false);
cy.get("bkd-user-settings")
.find('ul[role="menu"]')
.as("serviceNav")
.should("not.be.visible");

// Open user settings menu
cy.get("@toggle").click();
cy.get("@toggle").ariaExpanded(true);
cy.get("@serviceNav").should("be.visible");
cy.visit("/index.html");
openServiceNavigation();

// Close user settings menu
cy.get("a.logo").click();
cy.get("@toggle").ariaExpanded(false);
cy.get("@serviceNav").should("not.be.visible");
});

it("renders language switcher in the service navigation", () => {
it("renders language switcher in the service navigation for multilingual schools", () => {
interceptGuiLanguagesRequest(["de-CH", "fr-CH"]);

cy.visit("/index.html");
cy.get("bkd-language-switcher")
.find("a")
.should((links) =>
Expand All @@ -137,28 +81,24 @@ describe("Service Navigation", () => {
).to.deep.eq(["de", "fr"]),
);
});

it("does not render language switcher in the service navigation for monolingual schools", () => {
interceptGuiLanguagesRequest(["de-CH"]);

cy.visit("/index.html");
cy.get("bkd-language-switcher").should("not.exist");
});
});

describe("mobile", () => {
beforeEach(() => {
cy.resizeToMobile();
cy.visit("/index.html");
});

it("renders user settings and language switcher in the mobile navigation", () => {
// Mobile navigation initially closed
cy.get("button[aria-label='Menü Benutzereinstellungen']")
.should("not.be.visible")
.ariaExpanded(false);
cy.get("bkd-user-settings")
.find('ul[role="menu"]')
.should("not.be.visible");

// Open mobile navigation
cy.get("button[aria-label='Menü']").as("toggle");
cy.get("@toggle").click();
cy.get("@toggle").ariaExpanded(true);
cy.get(".service-nav").as("serviceNav").should("be.visible");
it("renders user settings and language switcher in the mobile navigation for multilingual schools", () => {
interceptGuiLanguagesRequest(["de-CH", "fr-CH"]);
cy.visit("/index.html");
openMobileNavigation();

cy.get("@serviceNav")
.find("a")
Expand All @@ -175,5 +115,63 @@ describe("Service Navigation", () => {
]),
);
});

it("renders user settings in the mobile navigation for monolingual schools", () => {
interceptGuiLanguagesRequest(["de-CH"]);
cy.visit("/index.html");
openMobileNavigation();

cy.get("@serviceNav")
.find("a")
.should((links) =>
expect(
links.toArray().map((link) => link.textContent?.trim()),
).to.deep.eq([
"Mein Profil",
"Einstellungen",
"Video-Tutorials",
"Logout",
]),
);
});
});
});

function interceptGuiLanguagesRequest(guiLanguage: string[]) {
cy.intercept(
"GET",
"https://eventotest.api/restApi/Configurations/SchoolAppNavigation",
{ instanceName: "Test", guiLanguage },
);
}

function openServiceNavigation() {
// User settings menu initially closed
cy.get("button[aria-label='Menü Benutzereinstellungen']")
.as("toggle")
.should("be.visible")
.ariaExpanded(false);
cy.get("bkd-user-settings")
.find('ul[role="menu"]')
.as("serviceNav")
.should("not.be.visible");

// Open user settings menu
cy.get("@toggle").click();
cy.get("@toggle").ariaExpanded(true);
cy.get("@serviceNav").should("be.visible");
}

function openMobileNavigation() {
// Mobile navigation initially closed
cy.get("button[aria-label='Menü Benutzereinstellungen']")
.should("not.be.visible")
.ariaExpanded(false);
cy.get("bkd-user-settings").find('ul[role="menu"]').should("not.be.visible");

// Open mobile navigation
cy.get("button[aria-label='Menü']").as("toggle");
cy.get("@toggle").click();
cy.get("@toggle").ariaExpanded(true);
cy.get(".service-nav").as("serviceNav").should("be.visible");
}
2 changes: 1 addition & 1 deletion cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Cypress.Commands.add(
cy.intercept(
"GET",
"https://eventotest.api/restApi/Configurations/SchoolAppNavigation",
{ instanceName: "Test" },
{ instanceName: "Test", guiLanguage: ["de-CH", "fr-CH"] },
);

cy.intercept(
Expand Down
6 changes: 5 additions & 1 deletion src/components/Header/MobileNav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { customElement, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { map } from "lit/directives/map.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { when } from "lit/directives/when.js";
import { localized, msg } from "@lit/localize";
import { StateController } from "@lit-app/state";
import arrowDownIcon from "../../assets/icons/arrow-down.svg?raw";
Expand Down Expand Up @@ -316,7 +317,10 @@ export class MobileNav extends LitElement {
this.renderSettingsItem.bind(this),
)}
</ul>
<bkd-language-switcher></bkd-language-switcher>
${when(
portalState.allowedLocales.length > 1,
() => html`<bkd-language-switcher></bkd-language-switcher>`,
)}
</div>
</nav>
`;
Expand Down
13 changes: 12 additions & 1 deletion src/components/Header/ServiceNav.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { when } from "lit/directives/when.js";
import { localized, msg } from "@lit/localize";
import { StateController } from "@lit-app/state";
import { portalState } from "../../state/portal-state.ts";
import { theme } from "../../utils/theme";

@customElement("bkd-service-nav")
Expand Down Expand Up @@ -54,13 +57,21 @@ export class ServiceNav extends LitElement {
`,
];

constructor() {
super();
new StateController(this, portalState);
}

render() {
return html`
<nav role="navigation" aria-label=${msg("Servicenavigation")}>
<bkd-substitutions-toggle></bkd-substitutions-toggle>
<bkd-notifications-toggle></bkd-notifications-toggle>
<bkd-user-settings></bkd-user-settings>
<bkd-language-switcher></bkd-language-switcher>
${when(
portalState.allowedLocales.length > 1,
() => html`<bkd-language-switcher></bkd-language-switcher>`,
)}
<bkd-hamburger .open=${this.mobileNavOpen}></bkd-hamburger>
</nav>
`;
Expand Down
28 changes: 18 additions & 10 deletions src/state/portal-state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { msg } from "@lit/localize";
import { State, property, query } from "@lit-app/state";
import {
App,
Expand All @@ -7,7 +6,7 @@ import {
NavigationItem,
settings,
} from "../settings";
import { fetchInstanceName, fetchUserAccessInfo } from "../utils/fetch";
import { fetchInstanceInfo, fetchUserAccessInfo } from "../utils/fetch";
import { getInitialLocale, getLocale, updateLocale } from "../utils/locale";
import { filterAllowed, getApp, getNavigationItem } from "../utils/navigation";
import { cleanupQueryParams, updateQueryParam } from "../utils/routing";
Expand All @@ -29,6 +28,9 @@ class PortalState extends State {
@property({ value: "" })
instanceName!: string;

@property({ value: [] })
allowedLocales!: ReadonlyArray<string>;

@property({ value: [] })
navigation!: Navigation;

Expand Down Expand Up @@ -68,6 +70,8 @@ class PortalState extends State {
);

async init() {
await this.loadInstanceInfo();

// Update initially
await this.handleStateChange("locale", this.locale);

Expand Down Expand Up @@ -160,7 +164,6 @@ class PortalState extends State {
private async handleStateChange(key: string, value: any): Promise<void> {
if (key === "locale") {
await this.updateLocale(value);
await this.loadInstanceName();
}

if (key === "locale" || key === "navigationItemKey") {
Expand All @@ -180,9 +183,15 @@ class PortalState extends State {
}

private async updateLocale(locale: PortalState["locale"]): Promise<void> {
updateQueryParam(LOCALE_QUERY_PARAM, locale);
// Fall back to allowed language (i.e. for instances that only support one
// language)
this.locale = this.allowedLocales.includes(locale)
? locale
: this.allowedLocales[0];

updateQueryParam(LOCALE_QUERY_PARAM, this.locale);
try {
await updateLocale(locale);
await updateLocale(this.locale);
} catch (error) {
console.warn(
"Unable to fetch/update locale (this may happen when interrupted by a redirect):",
Expand Down Expand Up @@ -257,14 +266,13 @@ class PortalState extends State {
this.rolesAndPermissions = [...roles, ...permissions];
}

private async loadInstanceName(): Promise<void> {
private async loadInstanceInfo(): Promise<void> {
if (!tokenState.authenticated) return;

try {
const instanceName = await fetchInstanceName();
this.instanceName = [msg("Evento"), instanceName]
.filter(Boolean)
.join(" | ");
const { instanceName, allowedLocales } = await fetchInstanceInfo();
this.allowedLocales = allowedLocales;
this.instanceName = ["Evento", instanceName].filter(Boolean).join(" | ");
} catch (error) {
console.warn(
"Unable to fetch/update instance name (this may happen when interrupted by a redirect):",
Expand Down
15 changes: 11 additions & 4 deletions src/utils/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@ export async function fetchUserAccessInfo(): Promise<UserAccessInfo> {
};
}

export async function fetchInstanceName(): Promise<string | null> {
const url = `${envSettings.apiServer}/Configurations/SchoolAppNavigation`;
const result = await fetchApi<{ instanceName: string }>(url);
type InstanceInfo = Readonly<{
instanceName: string;
allowedLocales: ReadonlyArray<string>;
}>;

return result?.instanceName || null;
export async function fetchInstanceInfo(): Promise<InstanceInfo> {
const url = `${envSettings.apiServer}/Configurations/SchoolAppNavigation`;
const { instanceName, guiLanguage: allowedLocales } = await fetchApi<{
instanceName: string;
guiLanguage: ReadonlyArray<string>;
}>(url);
return { instanceName, allowedLocales };
}

export type Substitution = Readonly<{
Expand Down

0 comments on commit a778f7d

Please sign in to comment.