Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 22 additions & 23 deletions components/matchmaking/MatchmakingSettingsPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import {
PopoverTrigger,
} from "~/components/ui/popover";
import {
Sheet,
SheetContent,
SheetDescription,
SheetTitle,
} from "~/components/ui/sheet";
Drawer,
DrawerContent,
DrawerDescription,
DrawerTitle,
} from "~/components/ui/drawer";
import MatchmakingSettings from "~/components/matchmaking/MatchmakingSettings.vue";

// Every way into the matchmaking settings (the top-bar globe, the /play gear)
// opens this: the same sections everywhere, a popover on desktop and a bottom
// sheet on phones, where a popover this tall flips up and clips.
// opens this: the same sections everywhere, a popover on desktop and a
// swipe-down drawer on phones, where a popover this tall flips up and clips.
withDefaults(defineProps<{ align?: "start" | "center" | "end" }>(), {
align: "start",
});
Expand Down Expand Up @@ -44,23 +44,22 @@ const isPhone = useMediaQuery("(max-width: 767px)");
</PopoverContent>
</Popover>

<Sheet
<Drawer
:open="open && isPhone"
@update:open="(value: boolean) => (open = value)"
>
<SheetContent
side="bottom"
class="max-h-[85dvh] overflow-y-auto rounded-t-xl pb-8"
>
<SheetTitle class="mb-4 text-base">{{
$t("pages.play.matchmaking.settings")
}}</SheetTitle>
<SheetDescription class="sr-only">{{
$t("pages.play.matchmaking.settings")
}}</SheetDescription>
<MatchmakingSettings>
<slot name="extra" />
</MatchmakingSettings>
</SheetContent>
</Sheet>
<DrawerContent class="max-h-[85dvh]">
<div class="overflow-y-auto px-4 pb-8 pt-4">
<DrawerTitle class="mb-4 text-base">{{
$t("pages.play.matchmaking.settings")
}}</DrawerTitle>
<DrawerDescription class="sr-only">{{
$t("pages.play.matchmaking.settings")
}}</DrawerDescription>
<MatchmakingSettings>
<slot name="extra" />
</MatchmakingSettings>
</div>
</DrawerContent>
</Drawer>
</template>
41 changes: 22 additions & 19 deletions components/matchmaking/PlayWherePrompt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@ import {
DialogTitle,
} from "~/components/ui/dialog";
import {
Sheet,
SheetContent,
SheetDescription,
SheetTitle,
} from "~/components/ui/sheet";
Drawer,
DrawerContent,
DrawerDescription,
DrawerTitle,
} from "~/components/ui/drawer";
import PlayWhereChoices from "~/components/matchmaking/PlayWhereChoices.vue";
import { useMatchmakingStore } from "~/stores/MatchmakingStore";
import { roundedPing } from "~/components/play/matchmakingHero";

// Asks a player on a LAN whether to play there or online, the first time they
// queue or host this session. Anchored to the button that asked on desktop; a
// bottom sheet on phones; a small dialog when nothing anchors it (a menu item).
// swipe-down drawer on phones; a small dialog when nothing anchors it (a menu
// item).
const { t } = useI18n();
const matchmaking = useMatchmakingStore();
const isMobile = useMediaQuery("(max-width: 767px)");
Expand Down Expand Up @@ -99,19 +100,21 @@ function onOpenChange(open: boolean) {
</PopoverContent>
</Popover>

<Sheet v-else-if="isMobile" :open="!!prompt" @update:open="onOpenChange">
<SheetContent side="bottom" class="grid gap-3 rounded-t-xl pb-7">
<SheetTitle class="text-base">{{ title }}</SheetTitle>
<SheetDescription class="sr-only">{{ title }}</SheetDescription>
<PlayWhereChoices
v-if="prompt"
:kind="prompt.kind"
:lan-ping="lanPing"
:online-summary="onlineSummary"
@choose="choose"
/>
</SheetContent>
</Sheet>
<Drawer v-else-if="isMobile" :open="!!prompt" @update:open="onOpenChange">
<DrawerContent>
<div class="grid gap-3 px-4 pb-7 pt-4">
<DrawerTitle class="text-base">{{ title }}</DrawerTitle>
<DrawerDescription class="sr-only">{{ title }}</DrawerDescription>
<PlayWhereChoices
v-if="prompt"
:kind="prompt.kind"
:lan-ping="lanPing"
:online-summary="onlineSummary"
@choose="choose"
/>
</div>
</DrawerContent>
</Drawer>

<Dialog v-else :open="!!prompt" @update:open="onOpenChange">
<DialogContent class="grid max-w-sm gap-3">
Expand Down
1 change: 1 addition & 0 deletions components/matchmaking/RegionLatencySettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ const wordClasses =
step="5"
:max="sliderMax"
:value="maxLatency"
data-vaul-no-drag
class="min-w-0 flex-1 cursor-pointer accent-[hsl(var(--tac-amber))]"
@input="onLatencyInput"
/>
Expand Down
61 changes: 61 additions & 0 deletions tests/components/matchmaking/MatchmakingSettingsPanel.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { flushPromises } from "@vue/test-utils";
import { mountSuspended } from "@nuxt/test-utils/runtime";
import MatchmakingSettingsPanel from "~/components/matchmaking/MatchmakingSettingsPanel.vue";

function screen({ phone }: { phone: boolean }) {
vi.spyOn(window, "matchMedia").mockImplementation(
(query: string) =>
({
matches: phone && query === "(max-width: 767px)",
media: query,
onchange: null,
addEventListener: () => {},
removeEventListener: () => {},
addListener: () => {},
removeListener: () => {},
dispatchEvent: () => false,
}) as MediaQueryList,
);
}

async function mountPanel(open: boolean) {
const wrapper = await mountSuspended(MatchmakingSettingsPanel, {
props: { open },
slots: { trigger: '<button type="button">Settings</button>' },
global: {
stubs: { MatchmakingSettings: { template: "<div>settings body</div>" } },
},
attachTo: document.body,
});
await flushPromises();
return wrapper;
}

afterEach(() => {
vi.restoreAllMocks();
document.body.innerHTML = "";
});

describe("MatchmakingSettingsPanel", () => {
it("opens as a drawer you can swipe down on a phone", async () => {
screen({ phone: true });
const wrapper = await mountPanel(true);

expect(document.querySelector("[data-vaul-drawer]")).not.toBeNull();
expect(document.body.textContent).toContain("settings body");
wrapper.unmount();
});

it("opens as a popover on desktop", async () => {
screen({ phone: false });
const wrapper = await mountPanel(false);
await wrapper.find("button").trigger("click");
await wrapper.setProps({ open: true });
await flushPromises();

expect(document.querySelector("[data-vaul-drawer]")).toBeNull();
expect(document.body.textContent).toContain("settings body");
wrapper.unmount();
});
});
Loading