diff --git a/src/frontend/apps/drive/src/features/drivers/types.ts b/src/frontend/apps/drive/src/features/drivers/types.ts
index 0d1782c1..55000b24 100644
--- a/src/frontend/apps/drive/src/features/drivers/types.ts
+++ b/src/frontend/apps/drive/src/features/drivers/types.ts
@@ -222,6 +222,7 @@ export type ApiConfig = {
legalNoticeUrl?: string;
};
supportEmail?: string;
+ supportMessagesWidget?: boolean;
};
FRONTEND_THEME?: string;
FRONTEND_HIDE_GAUFRE?: boolean;
diff --git a/src/frontend/apps/drive/src/features/layouts/components/explorer/ExplorerLayout.tsx b/src/frontend/apps/drive/src/features/layouts/components/explorer/ExplorerLayout.tsx
index ad4b07b4..53bdd189 100644
--- a/src/frontend/apps/drive/src/features/layouts/components/explorer/ExplorerLayout.tsx
+++ b/src/frontend/apps/drive/src/features/layouts/components/explorer/ExplorerLayout.tsx
@@ -43,6 +43,7 @@ import i18n from "@/features/i18n/initI18n";
import { useMemo } from "react";
import { UserProfile } from "@/features/ui/components/user/UserProfile";
import { Gaufre } from "@/features/ui/components/gaufre/Gaufre";
+import { useMessagesWidget } from "@/features/feedback/useMessagesWidget";
export const getGlobalExplorerLayout = (page: React.ReactElement) => {
return {page};
@@ -199,20 +200,26 @@ const HelpMenuButton = () => {
const helpMenuConfig = config?.FRONTEND_HELP_MENU_CONFIG;
const hasHelpMenu =
!!helpMenuConfig && Object.keys(helpMenuConfig).length > 0;
+ const { showWidget } = useMessagesWidget();
if (!hasHelpMenu) {
return null;
}
+ const getOnContactUs = () => {
+ if (helpMenuConfig.supportMessagesWidget) {
+ return () => showWidget();
+ }
+ return helpMenuConfig.supportEmail
+ ? () => window.open(helpMenuConfig.supportEmail)
+ : undefined;
+ };
+
return (
window.open(helpMenuConfig.supportEmail)
- : undefined
- }
+ onContactUs={getOnContactUs()}
/>
);
};
diff --git a/src/frontend/apps/e2e/__tests__/app-drive/help-menu.spec.ts b/src/frontend/apps/e2e/__tests__/app-drive/help-menu.spec.ts
index 66a7bead..1bee8c2b 100644
--- a/src/frontend/apps/e2e/__tests__/app-drive/help-menu.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-drive/help-menu.spec.ts
@@ -12,6 +12,7 @@ const HELP_MENU_CONFIG = {
const overrideHelpMenuConfig = async (
page: import("@playwright/test").Page,
helpMenuConfig: unknown,
+ extraConfig: Record = {},
) => {
await page.route("**/api/v1.0/config/", async (route) => {
const response = await route.fetch();
@@ -21,10 +22,25 @@ const overrideHelpMenuConfig = async (
} else {
json.FRONTEND_HELP_MENU_CONFIG = helpMenuConfig;
}
+ Object.assign(json, extraConfig);
await route.fulfill({ response, json });
});
};
+// Records window.open calls so that mailto links can be asserted without
+// relying on the browser actually opening them.
+const spyOnWindowOpen = async (page: import("@playwright/test").Page) => {
+ await page.addInitScript(() => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (window as any).__openedUrls = [];
+ window.open = (url?: string | URL) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (window as any).__openedUrls.push(String(url));
+ return null;
+ };
+ });
+};
+
test.describe("Help menu", () => {
test("renders the help menu with the configured options", async ({
page,
@@ -60,6 +76,69 @@ test.describe("Help menu", () => {
).toHaveCount(0);
});
+ test("opens the support email when clicking on contact us", async ({
+ page,
+ }) => {
+ await overrideHelpMenuConfig(page, HELP_MENU_CONFIG);
+ await spyOnWindowOpen(page);
+ await login(page, "drive@example.com");
+ await page.goto("/");
+
+ const footer = page.locator(".c__left-panel__footer__drive");
+ await footer.getByRole("button", { name: "Help" }).click();
+ await page.getByRole("menuitem", { name: "Contact us" }).click();
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const openedUrls = await page.evaluate(() => (window as any).__openedUrls);
+ expect(openedUrls).toEqual([HELP_MENU_CONFIG.supportEmail]);
+ });
+
+ test("opens the messages widget when clicking on contact us", async ({
+ page,
+ }) => {
+ const widgetPath = "https://widget.example.com/";
+ await overrideHelpMenuConfig(
+ page,
+ {
+ ...HELP_MENU_CONFIG,
+ supportMessagesWidget: true,
+ },
+ {
+ FRONTEND_FEEDBACK_MESSAGES_WIDGET_API_URL: "https://widget.example.com/api/",
+ FRONTEND_FEEDBACK_MESSAGES_WIDGET_PATH: widgetPath,
+ FRONTEND_FEEDBACK_MESSAGES_WIDGET_CHANNEL: "drive",
+ },
+ );
+ // Serve an empty widget script so the injected loader does not hit the
+ // network for real.
+ await page.route(`${widgetPath}feedback.js`, (route) =>
+ route.fulfill({ contentType: "application/javascript", body: "" }),
+ );
+ await spyOnWindowOpen(page);
+ await login(page, "drive@example.com");
+ await page.goto("/");
+
+ const footer = page.locator(".c__left-panel__footer__drive");
+ await footer.getByRole("button", { name: "Help" }).click();
+ await page.getByRole("menuitem", { name: "Contact us" }).click();
+
+ // The widget queues an init call and injects its loader script.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const widgetCalls = await page.evaluate(() => (window as any)._stmsg_widget);
+ expect(widgetCalls).toHaveLength(1);
+ expect(widgetCalls[0][0]).toBe("feedback");
+ expect(widgetCalls[0][1]).toBe("init");
+ expect(widgetCalls[0][2].channel).toBe("drive");
+ await expect(
+ page.locator(`script[src="${widgetPath}feedback.js"]`),
+ ).toHaveCount(1);
+
+ // The widget takes precedence over the support email.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const openedUrls = await page.evaluate(() => (window as any).__openedUrls);
+ expect(openedUrls).toEqual([]);
+ });
+
test("does not render the help menu when the config is empty", async ({
page,
}) => {