From d0d1da6e7bab8185e0bdd0b60e8282e95dbafbec Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Fri, 2 Oct 2026 20:37:43 +0300 Subject: [PATCH] Stop submit bubbling out of every Dialog --- app/components/elements/Dialog.tsx | 2 + .../routes/to.$id.admin.registration.$tid.tsx | 25 +++++------ .../2026-10-02-friend-code-edit-profile.md | 5 +++ e2e/pages/user/user-edit-profile-page.ts | 11 +++++ e2e/sidebar-streams.spec.ts | 42 ++++++++++--------- e2e/user-page.spec.ts | 11 +++++ 6 files changed, 61 insertions(+), 35 deletions(-) create mode 100644 changelog/2026-10-02-friend-code-edit-profile.md diff --git a/app/components/elements/Dialog.tsx b/app/components/elements/Dialog.tsx index ee0d67afa..0fa3ff365 100644 --- a/app/components/elements/Dialog.tsx +++ b/app/components/elements/Dialog.tsx @@ -91,6 +91,8 @@ function DialogElement({ tabIndex={-1} closedby="closerequest" onClose={onClose} + // a portal doesn't stop React events from bubbling to an outer form + onSubmit={(e) => e.stopPropagation()} {...backdropPressHandlers} > {children} diff --git a/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.tsx b/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.tsx index ec7aa9542..39c8f87ff 100644 --- a/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.tsx +++ b/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.tsx @@ -381,21 +381,16 @@ function ImportTeamSection({ heading={t("forms:regImportTeam")} onClose={() => setIsOpen(false)} > - {/* The modal is portaled out of the registration
in the DOM, - but its submit event still bubbles through the React tree to the - outer form. Stop it here so importing doesn't submit registration. */} -
e.stopPropagation()}> - - - -
+ + + ) : null} diff --git a/changelog/2026-10-02-friend-code-edit-profile.md b/changelog/2026-10-02-friend-code-edit-profile.md new file mode 100644 index 000000000..1728cff47 --- /dev/null +++ b/changelog/2026-10-02-friend-code-edit-profile.md @@ -0,0 +1,5 @@ +--- +navItem: u +type: bug +--- +Fix setting a friend code from the edit profile page diff --git a/e2e/pages/user/user-edit-profile-page.ts b/e2e/pages/user/user-edit-profile-page.ts index 08c366055..798f0f994 100644 --- a/e2e/pages/user/user-edit-profile-page.ts +++ b/e2e/pages/user/user-edit-profile-page.ts @@ -37,6 +37,17 @@ export class UserEditProfilePage { await search.press("Enter"); } + friendCodeButton(friendCode: string) { + return this.page.getByRole("button", { name: `SW-${friendCode}` }); + } + + async setFriendCode(friendCode: string) { + await this.page.getByRole("button", { name: "Set friend code" }).click(); + const dialog = this.page.getByRole("dialog", { name: "Friend code" }); + await dialog.getByLabel("Friend code").fill(friendCode); + await submit(this.page, dialog.getByTestId("submit-button")); + } + async deleteWeapon(name: string | RegExp) { await this.page .getByRole("button", { name }) diff --git a/e2e/sidebar-streams.spec.ts b/e2e/sidebar-streams.spec.ts index bfced0610..a6ef4cb86 100644 --- a/e2e/sidebar-streams.spec.ts +++ b/e2e/sidebar-streams.spec.ts @@ -1,6 +1,9 @@ import { NZAP_TEST_ID } from "~/db/seed/constants"; import { ADMIN_ID } from "~/features/admin/admin-constants"; +import { MATCHES_COUNT_NEEDED_FOR_LEADERBOARD } from "~/features/leaderboards/leaderboards-constants"; +import * as Seasons from "~/features/mmr/core/Seasons"; import { tournamentStreamsPage, twitchUrl } from "~/utils/urls"; +import type { Factories } from "./helpers/factories"; import { expect, expectNoErrorPage, @@ -57,16 +60,7 @@ test.describe("Sidebar streams", () => { page, factories, }) => { - const [topPlayer] = await createNamedUsers(factories, ["TopPlayer"], { - twitch: XRANK_TWITCH, - }); - await factories.XRankPlacementFactory.create( - { playerUserId: topPlayer.id, power: TOP_PLAYER_XP }, - { refreshPeakXp: true }, - ); - await factories.LiveStreamFactory.replaceAll([ - { userId: topPlayer.id, twitch: XRANK_TWITCH }, - ]); + await createLiveXRankStreamer(factories); await impersonate(page, NZAP_TEST_ID); await new FrontPage(page).goto(); @@ -85,16 +79,7 @@ test.describe("Sidebar streams", () => { page, factories, }) => { - const [topPlayer] = await createNamedUsers(factories, ["TopPlayer"], { - twitch: XRANK_TWITCH, - }); - await factories.XRankPlacementFactory.create( - { playerUserId: topPlayer.id, power: TOP_PLAYER_XP }, - { refreshPeakXp: true }, - ); - await factories.LiveStreamFactory.replaceAll([ - { userId: topPlayer.id, twitch: XRANK_TWITCH }, - ]); + await createLiveXRankStreamer(factories); await factories.TournamentFactory.create( { @@ -210,3 +195,20 @@ test.describe("Sidebar streams", () => { await expectNoErrorPage(page); }); }); + +async function createLiveXRankStreamer(factories: Factories) { + const [topPlayer] = await createNamedUsers(factories, ["TopPlayer"], { + twitch: XRANK_TWITCH, + }); + await factories.XRankPlacementFactory.create( + { playerUserId: topPlayer.id, power: TOP_PLAYER_XP }, + { refreshPeakXp: true }, + ); + await factories.SkillFactory.create( + { userId: topPlayer.id, season: Seasons.currentOrPrevious()!.nth }, + { matchesCount: MATCHES_COUNT_NEEDED_FOR_LEADERBOARD }, + ); + await factories.LiveStreamFactory.replaceAll([ + { userId: topPlayer.id, twitch: XRANK_TWITCH }, + ]); +} diff --git a/e2e/user-page.spec.ts b/e2e/user-page.spec.ts index 4baadb06b..76ca46be2 100644 --- a/e2e/user-page.spec.ts +++ b/e2e/user-page.spec.ts @@ -11,6 +11,7 @@ import type { MainWeaponId, StageId } from "~/modules/in-game-lists/types"; import type { Factories } from "./helpers/factories"; import { expect, impersonate, isNotVisible, test } from "./helpers/playwright"; import { SettingsPage } from "./pages/settings/settings-page"; +import { UserEditProfilePage } from "./pages/user/user-edit-profile-page"; import { UserEditWidgetsPage } from "./pages/user/user-edit-widgets-page"; import { UserPage } from "./pages/user/user-page"; import { UserSeasonsPage } from "./pages/user/user-seasons-page"; @@ -132,6 +133,16 @@ test.describe("User page", () => { await expect(userPage.text("Lean#1234")).toBeVisible(); }); + test("sets friend code from the edit profile page", async ({ page }) => { + await impersonate(page, NZAP_TEST_ID); + + const editProfile = new UserEditProfilePage(page); + await editProfile.goto(NZAP_TEST_DISCORD_ID); + await editProfile.setFriendCode("0123-4567-8901"); + + await expect(editProfile.friendCodeButton("0123-4567-8901")).toBeVisible(); + }); + test("customizes theme colors and resets them", async ({ page, factories,