mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-11 05:36:10 -05:00
Filter/search call to action to log in
This commit is contained in:
17
app/components/LogInPopover.module.css
Normal file
17
app/components/LogInPopover.module.css
Normal file
@@ -0,0 +1,17 @@
|
||||
.popover {
|
||||
max-width: 16rem;
|
||||
|
||||
/* a display on the class itself would show the closed popovers too */
|
||||
&:popover-open {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--s-3);
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: var(--font-xs);
|
||||
color: var(--color-text-high);
|
||||
text-align: center;
|
||||
text-wrap: balance;
|
||||
}
|
||||
31
app/components/LogInPopover.tsx
Normal file
31
app/components/LogInPopover.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { LogIn } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SendouButton } from "./elements/Button";
|
||||
import { SendouPopover } from "./elements/Popover";
|
||||
import styles from "./LogInPopover.module.css";
|
||||
import { LogInButtonContainer } from "./layout/LogInButtonContainer";
|
||||
|
||||
/** Wraps a trigger a logged out user can't use, prompting them to log in instead. */
|
||||
export function LogInPopover({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactElement<Record<string, unknown>>;
|
||||
}) {
|
||||
const { t } = useTranslation(["common"]);
|
||||
|
||||
return (
|
||||
<SendouPopover trigger={children} popoverClassName={styles.popover}>
|
||||
<div className={styles.text}>{t("common:logInPrompt")}</div>
|
||||
<LogInButtonContainer>
|
||||
<SendouButton
|
||||
type="submit"
|
||||
size="small"
|
||||
icon={<LogIn />}
|
||||
data-testid="log-in-popover-button"
|
||||
>
|
||||
{t("common:header.login.discord")}
|
||||
</SendouButton>
|
||||
</LogInButtonContainer>
|
||||
</SendouPopover>
|
||||
);
|
||||
}
|
||||
@@ -87,6 +87,29 @@ function TestFilterBar(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function TestHighlightsFilterBar() {
|
||||
const [highlights, setHighlights] = useState(true);
|
||||
|
||||
return (
|
||||
<FilterBar
|
||||
pills={[
|
||||
{
|
||||
key: "highlights",
|
||||
name: "Highlights",
|
||||
formattedValue: highlights ? "Only" : null,
|
||||
usableLoggedOut: true,
|
||||
onRemove: () => setHighlights(false),
|
||||
popover: (
|
||||
<button type="button" onClick={() => setHighlights(!highlights)}>
|
||||
Toggle highlights
|
||||
</button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
describe("FilterBar", () => {
|
||||
test("renders a set pill with its name and formatted value", async () => {
|
||||
const screen = await render(<TestFilterBar initialMode="SZ" />);
|
||||
@@ -189,16 +212,50 @@ describe("FilterBar", () => {
|
||||
.not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders nothing for a logged out user", async () => {
|
||||
test("prompts a logged out user to log in instead of opening a pill", async () => {
|
||||
useUser.mockReturnValue(null);
|
||||
|
||||
const screen = await render(<TestFilterBar initialMode="SZ" />);
|
||||
|
||||
await screen.getByRole("button", { name: "Mode SZ" }).click();
|
||||
|
||||
await expect
|
||||
.element(screen.getByRole("button", { name: /Mode/ }))
|
||||
.element(screen.getByText("Log in to use this"))
|
||||
.toBeInTheDocument();
|
||||
await expect
|
||||
.element(screen.getByRole("button", { name: "Set SZ" }))
|
||||
.not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("prompts a logged out user to log in instead of opening the add filter menu", async () => {
|
||||
useUser.mockReturnValue(null);
|
||||
|
||||
const screen = await render(<TestFilterBar />);
|
||||
|
||||
await screen.getByRole("button", { name: "Filter" }).click();
|
||||
|
||||
await expect
|
||||
.element(screen.getByRole("button", { name: "Filter" }))
|
||||
.element(screen.getByText("Log in to use this"))
|
||||
.toBeInTheDocument();
|
||||
await expect
|
||||
.element(screen.getByRole("menuitem", { name: "Mode" }))
|
||||
.not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("keeps a pill marked usable logged out on the bar and out of the log in prompt", async () => {
|
||||
useUser.mockReturnValue(null);
|
||||
|
||||
const screen = await render(<TestHighlightsFilterBar />);
|
||||
|
||||
await screen.getByRole("button", { name: "Highlights Only" }).click();
|
||||
await screen.getByRole("button", { name: "Toggle highlights" }).click();
|
||||
|
||||
// still there to be turned back on, without a remove button now that it is off
|
||||
await expect
|
||||
.element(screen.getByRole("button", { name: "Highlights", exact: true }))
|
||||
.toBeInTheDocument();
|
||||
await expect
|
||||
.element(screen.getByRole("button", { name: "Remove Highlights filter" }))
|
||||
.not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useUser } from "~/features/auth/core/user";
|
||||
import { SendouButton } from "../elements/Button";
|
||||
import { SendouMenu, SendouMenuItem } from "../elements/Menu";
|
||||
import { SendouPopover } from "../elements/Popover";
|
||||
import { LogInPopover } from "../LogInPopover";
|
||||
import styles from "./FilterBar.module.css";
|
||||
|
||||
export interface FilterBarPill {
|
||||
@@ -20,6 +21,8 @@ export interface FilterBarPill {
|
||||
onRemove?: () => void;
|
||||
/** Writes a starting value when the pill is added from the menu. */
|
||||
onAdd?: () => void;
|
||||
/** Usable logged out, where every other pill prompts to log in instead. */
|
||||
usableLoggedOut?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
popoverClassName?: string;
|
||||
testId?: string;
|
||||
@@ -35,17 +38,24 @@ export function FilterBar({
|
||||
onReset?: () => void;
|
||||
actions?: React.ReactNode;
|
||||
}) {
|
||||
const user = useUser();
|
||||
const isLoggedIn = Boolean(useUser());
|
||||
const { t } = useTranslation();
|
||||
const [justAddedKeys, setJustAddedKeys] = React.useState<ReadonlySet<string>>(
|
||||
new Set(),
|
||||
);
|
||||
const [openPillKey, setOpenPillKey] = React.useState<string | null>(null);
|
||||
|
||||
if (!user) return null;
|
||||
/** A logged out visitor has no add filter menu to bring a pill back with. */
|
||||
const isPinned = (pill: FilterBarPill) =>
|
||||
!isLoggedIn && Boolean(pill.usableLoggedOut);
|
||||
|
||||
const isVisible = (pill: FilterBarPill) =>
|
||||
pill.formattedValue !== null || justAddedKeys.has(pill.key);
|
||||
pill.formattedValue !== null ||
|
||||
justAddedKeys.has(pill.key) ||
|
||||
isPinned(pill);
|
||||
|
||||
const isRemovable = (pill: FilterBarPill) =>
|
||||
Boolean(pill.onRemove) && (pill.formattedValue !== null || !isPinned(pill));
|
||||
|
||||
const hiddenPills = pills.filter((pill) => !isVisible(pill));
|
||||
|
||||
@@ -79,13 +89,18 @@ export function FilterBar({
|
||||
<FilterPill
|
||||
key={pill.key}
|
||||
pill={pill}
|
||||
showLogInPrompt={!isLoggedIn && !pill.usableLoggedOut}
|
||||
isOpen={openPillKey === pill.key}
|
||||
onOpenChange={(isOpen) => setOpenPillKey(isOpen ? pill.key : null)}
|
||||
onRemove={pill.onRemove ? () => removePill(pill) : undefined}
|
||||
onRemove={isRemovable(pill) ? () => removePill(pill) : undefined}
|
||||
/>
|
||||
))}
|
||||
{hiddenPills.length > 0 ? (
|
||||
<AddFilterMenu pills={hiddenPills} onAdd={addPill} />
|
||||
<AddFilterMenu
|
||||
pills={hiddenPills}
|
||||
isLoggedIn={isLoggedIn}
|
||||
onAdd={addPill}
|
||||
/>
|
||||
) : null}
|
||||
{onReset || actions ? (
|
||||
<div className={styles.actions}>
|
||||
@@ -103,41 +118,47 @@ export function FilterBar({
|
||||
|
||||
function FilterPill({
|
||||
pill,
|
||||
showLogInPrompt,
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
onRemove,
|
||||
}: {
|
||||
pill: FilterBarPill;
|
||||
showLogInPrompt: boolean;
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
onRemove?: () => void;
|
||||
}) {
|
||||
const trigger = (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.trigger}
|
||||
data-active={pill.formattedValue !== null}
|
||||
data-testid={pill.testId}
|
||||
>
|
||||
{pill.icon ? <span className={styles.icon}>{pill.icon}</span> : null}
|
||||
<span>{pill.name}</span>
|
||||
{pill.formattedValue !== null ? (
|
||||
<span className={styles.value}>{pill.formattedValue}</span>
|
||||
) : null}
|
||||
<ChevronDown className={styles.chevron} />
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.pill}>
|
||||
<SendouPopover
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
popoverClassName={clsx(styles.popover, pill.popoverClassName)}
|
||||
trigger={
|
||||
<button
|
||||
type="button"
|
||||
className={styles.trigger}
|
||||
data-active={pill.formattedValue !== null}
|
||||
data-testid={pill.testId}
|
||||
>
|
||||
{pill.icon ? (
|
||||
<span className={styles.icon}>{pill.icon}</span>
|
||||
) : null}
|
||||
<span>{pill.name}</span>
|
||||
{pill.formattedValue !== null ? (
|
||||
<span className={styles.value}>{pill.formattedValue}</span>
|
||||
) : null}
|
||||
<ChevronDown className={styles.chevron} />
|
||||
</button>
|
||||
}
|
||||
>
|
||||
{pill.popover}
|
||||
</SendouPopover>
|
||||
{showLogInPrompt ? (
|
||||
<LogInPopover>{trigger}</LogInPopover>
|
||||
) : (
|
||||
<SendouPopover
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
popoverClassName={clsx(styles.popover, pill.popoverClassName)}
|
||||
trigger={trigger}
|
||||
>
|
||||
{pill.popover}
|
||||
</SendouPopover>
|
||||
)}
|
||||
{onRemove ? (
|
||||
<button
|
||||
type="button"
|
||||
@@ -155,27 +176,37 @@ function FilterPill({
|
||||
|
||||
function AddFilterMenu({
|
||||
pills,
|
||||
isLoggedIn,
|
||||
onAdd,
|
||||
}: {
|
||||
pills: FilterBarPill[];
|
||||
isLoggedIn: boolean;
|
||||
onAdd: (pill: FilterBarPill) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const trigger = (
|
||||
<button
|
||||
type="button"
|
||||
className={styles.trigger}
|
||||
data-testid="add-filter-button"
|
||||
>
|
||||
<Plus className={styles.plus} />
|
||||
<span>{t("filterBar.addFilter")}</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return (
|
||||
<div className={styles.pill}>
|
||||
<LogInPopover>{trigger}</LogInPopover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.pill}>
|
||||
<SendouMenu
|
||||
trigger={
|
||||
<button
|
||||
type="button"
|
||||
className={styles.trigger}
|
||||
data-testid="add-filter-button"
|
||||
>
|
||||
<Plus className={styles.plus} />
|
||||
<span>{t("filterBar.addFilter")}</span>
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<SendouMenu trigger={trigger}>
|
||||
{pills.map((pill) => (
|
||||
<SendouMenuItem
|
||||
key={pill.key}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { SendouRadio, SendouRadioGroup } from "~/components/elements/Radio";
|
||||
import { Image } from "~/components/Image";
|
||||
import { Input } from "~/components/Input";
|
||||
import { LocaleTime } from "~/components/LocaleTime";
|
||||
import { LogInPopover } from "~/components/LogInPopover";
|
||||
import type { SearchLoaderData } from "~/features/search/routes/search";
|
||||
import { searchSearchParams } from "~/features/search/search-search-params";
|
||||
import { tournamentOrganizationPage } from "~/features/tournament-organization/tournament-organization-urls";
|
||||
@@ -144,6 +145,20 @@ export function GlobalSearch() {
|
||||
);
|
||||
}
|
||||
|
||||
/** Search is logged in only, so a logged out visitor gets a log in prompt instead. */
|
||||
export function LoggedOutGlobalSearch() {
|
||||
const { t } = useTranslation(["common"]);
|
||||
|
||||
return (
|
||||
<LogInPopover>
|
||||
<button type="button" className={styles.searchButton}>
|
||||
<Search className={styles.searchIcon} />
|
||||
<span className={styles.searchPlaceholder}>{t("common:search")}</span>
|
||||
</button>
|
||||
</LogInPopover>
|
||||
);
|
||||
}
|
||||
|
||||
function resolveInitialWeapon(
|
||||
weaponId: MainWeaponId | null,
|
||||
t: TFunction<["common", "weapons"]>,
|
||||
|
||||
@@ -3,20 +3,18 @@ import { useTranslation } from "react-i18next";
|
||||
import { SUPPORT_PAGE } from "~/utils/urls";
|
||||
import { LinkButton, SendouButton } from "../elements/Button";
|
||||
import { AnythingAdder } from "./AnythingAdder";
|
||||
import { GlobalSearch } from "./GlobalSearch";
|
||||
import { GlobalSearch, LoggedOutGlobalSearch } from "./GlobalSearch";
|
||||
import { LogInButtonContainer } from "./LogInButtonContainer";
|
||||
import styles from "./TopRightButtons.module.css";
|
||||
|
||||
export function TopRightButtons({
|
||||
showSupport,
|
||||
showSearch,
|
||||
isLoggedIn,
|
||||
onChatToggle,
|
||||
onChatModalToggle,
|
||||
chatUnreadCount,
|
||||
}: {
|
||||
showSupport: boolean;
|
||||
showSearch: boolean;
|
||||
isLoggedIn: boolean;
|
||||
onChatToggle?: () => void;
|
||||
onChatModalToggle?: () => void;
|
||||
@@ -49,16 +47,14 @@ export function TopRightButtons({
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div className={styles.searchAndAddContainer}>
|
||||
<div className={styles.searchWrapper}>
|
||||
{isLoggedIn ? <GlobalSearch /> : <LoggedOutGlobalSearch />}
|
||||
</div>
|
||||
{isLoggedIn ? <AnythingAdder /> : null}
|
||||
</div>
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
<div className={styles.searchAndAddContainer}>
|
||||
{showSearch ? (
|
||||
<div className={styles.searchWrapper}>
|
||||
<GlobalSearch />
|
||||
</div>
|
||||
) : null}
|
||||
<AnythingAdder />
|
||||
</div>
|
||||
{onChatToggle ? (
|
||||
<div className={styles.chatButtonWrapperPersistent}>
|
||||
<ChatButton
|
||||
|
||||
@@ -462,7 +462,6 @@ export function Layout({
|
||||
showSupport={Boolean(
|
||||
data && !data?.user?.roles.includes("MINOR_SUPPORT"),
|
||||
)}
|
||||
showSearch={Boolean(data?.user)}
|
||||
isLoggedIn={Boolean(data?.user)}
|
||||
onChatToggle={
|
||||
data?.user && !chatSidebarOpen
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
width: 100%;
|
||||
max-width: var(--columns-width);
|
||||
margin-inline: auto;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonsContainer {
|
||||
|
||||
@@ -95,14 +95,14 @@ export default function ScrimsPage() {
|
||||
|
||||
return (
|
||||
<Main className="stack lg">
|
||||
{user ? (
|
||||
<div className="stack horizontal sm items-center flex-wrap">
|
||||
<div className="stack horizontal sm items-center flex-wrap">
|
||||
{user ? (
|
||||
<LinkButton size="small" to={associationsPage()} variant="outlined">
|
||||
{t("scrims:associations.title")}
|
||||
</LinkButton>
|
||||
<Filters />
|
||||
</div>
|
||||
) : null}
|
||||
) : null}
|
||||
<Filters />
|
||||
</div>
|
||||
<SendouTabs
|
||||
key={pendingRequestPostId}
|
||||
defaultSelectedKey={
|
||||
|
||||
@@ -80,6 +80,7 @@ export function ResultsFiltersBar() {
|
||||
formattedValue: filters.highlightsOnly ? t("results.filter.only") : null,
|
||||
onRemove: () => setFilters({ highlightsOnly: false }),
|
||||
onAdd: () => setFilters({ highlightsOnly: true }),
|
||||
usableLoggedOut: true,
|
||||
testId: "highlights-filter",
|
||||
popover: (
|
||||
<SendouSwitch
|
||||
|
||||
@@ -35,16 +35,9 @@ export const loader = async ({ request, url }: LoaderFunctionArgs) => {
|
||||
const isChoosingHighlights = url.pathname.includes("/results/highlights");
|
||||
const canFilter = !isChoosingHighlights && Boolean(getUser());
|
||||
|
||||
/** Logged out visitors are locked to the highlights, if there are any. */
|
||||
let showHighlightsOnly = hasHighlightedResults;
|
||||
|
||||
if (canFilter && !highlightsOnly) {
|
||||
showHighlightsOnly = false;
|
||||
}
|
||||
|
||||
if (isChoosingHighlights) {
|
||||
showHighlightsOnly = false;
|
||||
}
|
||||
/** Turning the highlights off is the one filter a logged out visitor gets. */
|
||||
const showHighlightsOnly =
|
||||
hasHighlightedResults && highlightsOnly && !isChoosingHighlights;
|
||||
|
||||
const filters = canFilter
|
||||
? {
|
||||
|
||||
8
changelog/2026-09-09-logged-out-search-and-filters.md
Normal file
8
changelog/2026-09-09-logged-out-search-and-filters.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
type: feature
|
||||
---
|
||||
Search and filters are no longer hidden when you are logged out
|
||||
|
||||
- Both are shown, and using one prompts you to log in
|
||||
- Clearing a filter someone shared with you in a link works without an account
|
||||
- On a user's results page you can turn off the highlights only filter without logging in
|
||||
12
e2e/pages/layout/log-in-popover.ts
Normal file
12
e2e/pages/layout/log-in-popover.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
|
||||
/** Shown when a logged out visitor uses a control that requires an account. */
|
||||
export class LogInPopover {
|
||||
readonly locators;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.locators = {
|
||||
logInButton: page.getByTestId("log-in-popover-button"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,10 @@ export class TopRightButtons {
|
||||
supportLink: page
|
||||
.getByRole("banner")
|
||||
.getByRole("link", { name: "Support" }),
|
||||
// logged out only: logged in the search opener is a link, not a button
|
||||
searchButton: page
|
||||
.getByRole("banner")
|
||||
.getByRole("button", { name: "Search" }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,21 @@ export class UserResultsPage {
|
||||
chooseHighlightsButton: page.getByRole("link", {
|
||||
name: "Choose highlights",
|
||||
}),
|
||||
highlightsFilter: page.getByTestId("highlights-filter"),
|
||||
highlightsOnlySwitch: page.getByRole("switch", {
|
||||
name: "Only highlighted results",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/** Flips the highlights only filter, which a logged out visitor may use too. */
|
||||
async toggleHighlightsOnly() {
|
||||
await this.locators.highlightsFilter.click();
|
||||
// the switch indicator covers its input, like everywhere else this one is clicked
|
||||
await this.locators.highlightsOnlySwitch.click({ force: true });
|
||||
await this.page.keyboard.press("Escape");
|
||||
}
|
||||
|
||||
async goto(discordId: string) {
|
||||
await navigate({ page: this.page, url: userResultsPage({ discordId }) });
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import { FaqPage } from "./pages/info/faq-page";
|
||||
import { LinksPage } from "./pages/info/links-page";
|
||||
import { SupportPage } from "./pages/info/support-page";
|
||||
import { ErrorPage } from "./pages/layout/error-page";
|
||||
import { LogInPopover } from "./pages/layout/log-in-popover";
|
||||
import { TopRightButtons } from "./pages/layout/top-right-buttons";
|
||||
import { ScannerPage } from "./pages/scanner/scanner-page";
|
||||
import { TournamentPage } from "./pages/tournament/tournament-page";
|
||||
import { UserPage } from "./pages/user/user-page";
|
||||
@@ -20,6 +22,7 @@ const PUBLIC_USER = {
|
||||
discordName: "Chirpy",
|
||||
};
|
||||
const BUILD_WEAPON_ID = 40;
|
||||
const BUILD_WEAPON_SLUG = "splattershot";
|
||||
const EVENT_NAME = "Ink Clash Open";
|
||||
const TOURNAMENT_NAME = "Public Pages Cup";
|
||||
const ICS_EVENT_NAME = "ICS Feed Cup";
|
||||
@@ -114,6 +117,23 @@ test.describe("Public pages", () => {
|
||||
await expect(page).toHaveURL("/");
|
||||
});
|
||||
|
||||
test("prompts a logged out visitor to log in when using search or a filter", async ({
|
||||
page,
|
||||
}) => {
|
||||
const weaponBuilds = new WeaponBuildsPage(page);
|
||||
await weaponBuilds.goto(BUILD_WEAPON_SLUG);
|
||||
|
||||
const logInPopover = new LogInPopover(page);
|
||||
|
||||
await weaponBuilds.locators.addFilterButton.click();
|
||||
await expect(logInPopover.locators.logInButton).toBeVisible();
|
||||
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
await new TopRightButtons(page).locators.searchButton.click();
|
||||
await expect(logInPopover.locators.logInButton).toBeVisible();
|
||||
});
|
||||
|
||||
test("lists articles and renders one by slug", async ({ page }) => {
|
||||
const articles = new ArticlesPage(page);
|
||||
await articles.goto();
|
||||
|
||||
@@ -264,6 +264,16 @@ test.describe("User page", () => {
|
||||
|
||||
await expect(resultsPage.eventName("In The Zone 30")).toBeVisible();
|
||||
await isNotVisible(resultsPage.eventName("Paddling Pool 253"));
|
||||
|
||||
await page.context().clearCookies();
|
||||
await resultsPage.goto(ADMIN_DISCORD_ID);
|
||||
await isNotVisible(resultsPage.eventName("Paddling Pool 253"));
|
||||
|
||||
await resultsPage.toggleHighlightsOnly();
|
||||
await expect(resultsPage.eventName("Paddling Pool 253")).toBeVisible();
|
||||
|
||||
await resultsPage.toggleHighlightsOnly();
|
||||
await isNotVisible(resultsPage.eventName("Paddling Pool 253"));
|
||||
});
|
||||
|
||||
test("edits profile widgets, lists vods and shows season stats", async ({
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Profil",
|
||||
"header.logout": "Log ud",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Sprog",
|
||||
"header.loggedInAs": "Du er logget ind som {{userName}}",
|
||||
"header.theme": "Tema",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Profil",
|
||||
"header.logout": "Ausloggen",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Sprache",
|
||||
"header.loggedInAs": "Eingeloggt als {{userName}}",
|
||||
"header.theme": "Theme",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Profile",
|
||||
"header.logout": "Log out",
|
||||
"header.login.discord": "Log in via Discord",
|
||||
"logInPrompt": "Log in to use this",
|
||||
"header.language": "Language",
|
||||
"header.loggedInAs": "Logged in as {{userName}}",
|
||||
"header.theme": "Theme",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Perfil",
|
||||
"header.logout": "Cerrar sesión",
|
||||
"header.login.discord": "Iniciar sesión con Discord",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Idioma",
|
||||
"header.loggedInAs": "Conectado como {{userName}}",
|
||||
"header.theme": "Tema",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Perfil",
|
||||
"header.logout": "Cerrar sesión",
|
||||
"header.login.discord": "Iniciar sesión con Discord",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Idioma",
|
||||
"header.loggedInAs": "Ingresado como {{userName}}",
|
||||
"header.theme": "Tema",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Profil",
|
||||
"header.logout": "Déconnexion",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Langue",
|
||||
"header.loggedInAs": "Connecté en tant que {{userName}}",
|
||||
"header.theme": "Thème",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Profil",
|
||||
"header.logout": "Déconnexion",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Langue",
|
||||
"header.loggedInAs": "Connecté en tant que {{userName}}",
|
||||
"header.theme": "Thème",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "פרופיל",
|
||||
"header.logout": "התנתקות",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "שפה",
|
||||
"header.loggedInAs": "הנך מחובר בתור {{userName}}",
|
||||
"header.theme": "נושא",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Profilo",
|
||||
"header.logout": "Esci",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Lingua",
|
||||
"header.loggedInAs": "Autenticato come {{userName}}",
|
||||
"header.theme": "Tema",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "プロファイル",
|
||||
"header.logout": "ログアウト",
|
||||
"header.login.discord": "Discord",
|
||||
"logInPrompt": "",
|
||||
"header.language": "言語",
|
||||
"header.loggedInAs": "{{userName}} でログインしています",
|
||||
"header.theme": "テーマ",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "프로필",
|
||||
"header.logout": "로그아웃",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "언어",
|
||||
"header.loggedInAs": "{{userName}}로 로그인됨",
|
||||
"header.theme": "테마",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Profiel",
|
||||
"header.logout": "Log uit",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "",
|
||||
"header.loggedInAs": "",
|
||||
"header.theme": "",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Profil",
|
||||
"header.logout": "Wyloguj się",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Język",
|
||||
"header.loggedInAs": "Zalogowany/a jako {{userName}}",
|
||||
"header.theme": "Motyw",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Perfil",
|
||||
"header.logout": "Sair",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Idioma",
|
||||
"header.loggedInAs": "Logado como {{userName}}",
|
||||
"header.theme": "Tema",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "Профиль",
|
||||
"header.logout": "Выйти",
|
||||
"header.login.discord": "",
|
||||
"logInPrompt": "",
|
||||
"header.language": "Язык",
|
||||
"header.loggedInAs": "Вы вошли как {{userName}}",
|
||||
"header.theme": "Тема",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"header.profile": "个人资料",
|
||||
"header.logout": "退出登录",
|
||||
"header.login.discord": "通过 Discord 登录",
|
||||
"logInPrompt": "",
|
||||
"header.language": "语言",
|
||||
"header.loggedInAs": "已登录: {{userName}}",
|
||||
"header.theme": "主题",
|
||||
|
||||
Reference in New Issue
Block a user