mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-27 21:58:08 -05:00
Draggable tiers
This commit is contained in:
@@ -5,6 +5,12 @@
|
||||
min-height: 68px;
|
||||
}
|
||||
|
||||
.containerReordering {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.tierLabel {
|
||||
grid-column: 1;
|
||||
display: flex;
|
||||
@@ -93,14 +99,8 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.arrowControls {
|
||||
.dragHandle {
|
||||
grid-column: 3;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
gap: var(--s-1);
|
||||
}
|
||||
|
||||
.arrowButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -109,31 +109,24 @@
|
||||
padding: var(--s-1);
|
||||
background: var(--color-bg-high);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s;
|
||||
border-radius: 0 var(--radius-field) var(--radius-field) 0;
|
||||
color: var(--color-text-high);
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
transition: color 0.2s;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
opacity: 0.8;
|
||||
&:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
.arrowButtonUpper {
|
||||
border-radius: 0 var(--radius-field) 0 0;
|
||||
}
|
||||
|
||||
.arrowButtonLower {
|
||||
border-radius: 0 0 var(--radius-field) 0;
|
||||
}
|
||||
|
||||
.arrowIcon {
|
||||
.dragHandleIcon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.colorGrid {
|
||||
|
||||
@@ -2,9 +2,11 @@ import { useDroppable } from "@dnd-kit/core";
|
||||
import {
|
||||
horizontalListSortingStrategy,
|
||||
SortableContext,
|
||||
useSortable,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import clsx from "clsx";
|
||||
import { ChevronDown, ChevronUp, Plus, Trash } from "lucide-react";
|
||||
import { GripVertical, Plus, Trash } from "lucide-react";
|
||||
import type { KeyboardEvent } from "react";
|
||||
import { useLayoutEffect, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -20,6 +22,7 @@ import {
|
||||
isLightColor,
|
||||
tierListItemId,
|
||||
tierNameFontSize,
|
||||
tierSortableId,
|
||||
tierTextColor,
|
||||
} from "../tier-list-maker-utils";
|
||||
import { DraggableItem } from "./DraggableItem";
|
||||
@@ -31,14 +34,11 @@ interface TierRowProps {
|
||||
|
||||
export function TierRow({ tier }: TierRowProps) {
|
||||
const {
|
||||
state,
|
||||
activeItem,
|
||||
getItemsInTier,
|
||||
handleRemoveTier,
|
||||
handleRenameTier,
|
||||
handleChangeTierColor,
|
||||
handleMoveTierUp,
|
||||
handleMoveTierDown,
|
||||
showTierHeaders,
|
||||
placementMode,
|
||||
selectedTierId,
|
||||
@@ -56,11 +56,15 @@ export function TierRow({ tier }: TierRowProps) {
|
||||
isDragging: activeItem !== null,
|
||||
});
|
||||
|
||||
const tierIndex = state.tiers.findIndex(
|
||||
(candidate) => candidate.id === tier.id,
|
||||
);
|
||||
const isFirstTier = tierIndex === 0;
|
||||
const isLastTier = tierIndex === state.tiers.length - 1;
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef: setSortableNodeRef,
|
||||
setActivatorNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging: isReordering,
|
||||
} = useSortable({ id: tierSortableId(tier.id) });
|
||||
|
||||
const isClickMode = placementMode === "click";
|
||||
const isSelected = isClickMode && selectedTierId === tier.id;
|
||||
@@ -82,7 +86,14 @@ export function TierRow({ tier }: TierRowProps) {
|
||||
: {};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div
|
||||
ref={setSortableNodeRef}
|
||||
data-tier-id={tier.id}
|
||||
className={clsx(styles.container, {
|
||||
[styles.containerReordering]: isReordering,
|
||||
})}
|
||||
style={{ transform: CSS.Translate.toString(transform), transition }}
|
||||
>
|
||||
{showTierHeaders ? (
|
||||
<SendouPopover
|
||||
trigger={
|
||||
@@ -193,26 +204,16 @@ export function TierRow({ tier }: TierRowProps) {
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className={styles.arrowControls}>
|
||||
<button
|
||||
className={clsx(styles.arrowButton, styles.arrowButtonUpper)}
|
||||
onClick={() => handleMoveTierUp(tier.id)}
|
||||
disabled={isFirstTier}
|
||||
type="button"
|
||||
aria-label="Move tier up"
|
||||
>
|
||||
<ChevronUp className={styles.arrowIcon} />
|
||||
</button>
|
||||
<button
|
||||
className={clsx(styles.arrowButton, styles.arrowButtonLower)}
|
||||
onClick={() => handleMoveTierDown(tier.id)}
|
||||
disabled={isLastTier}
|
||||
type="button"
|
||||
aria-label="Move tier down"
|
||||
>
|
||||
<ChevronDown className={styles.arrowIcon} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
ref={setActivatorNodeRef}
|
||||
className={styles.dragHandle}
|
||||
type="button"
|
||||
aria-label="Reorder tier"
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
>
|
||||
<GripVertical className={styles.dragHandleIcon} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,11 @@ import type {
|
||||
TierListState,
|
||||
} from "../tier-list-maker-schemas";
|
||||
import { tierListMakerSearchParams } from "../tier-list-maker-search-params";
|
||||
import { addItemToTier, getNextNthForItem } from "../tier-list-maker-utils";
|
||||
import {
|
||||
addItemToTier,
|
||||
getNextNthForItem,
|
||||
tierIdFromSortableId,
|
||||
} from "../tier-list-maker-utils";
|
||||
|
||||
export type TierListPlacementMode = "track" | "click";
|
||||
|
||||
@@ -46,6 +50,7 @@ export function useTierList() {
|
||||
const { tiers, setTiers, persistTiersStateToParams } =
|
||||
useSearchParamTiersState();
|
||||
const [activeItem, setActiveItem] = React.useState<TierListItem | null>(null);
|
||||
const [isReorderingTiers, setIsReorderingTiers] = React.useState(false);
|
||||
|
||||
const [placementMode, setPlacementMode] = usePersistedState(
|
||||
placementModePersisted,
|
||||
@@ -119,6 +124,10 @@ export function useTierList() {
|
||||
};
|
||||
|
||||
const handleDragStart = (event: DragStartEvent) => {
|
||||
const isTierDrag = tierIdFromSortableId(String(event.active.id)) !== null;
|
||||
setIsReorderingTiers(isTierDrag);
|
||||
if (isTierDrag) return;
|
||||
|
||||
const item = parseItemFromId(String(event.active.id));
|
||||
if (item) {
|
||||
setActiveItem(item);
|
||||
@@ -128,7 +137,7 @@ export function useTierList() {
|
||||
const handleDragOver = (event: DragOverEvent) => {
|
||||
const { active, over } = event;
|
||||
|
||||
if (!over) {
|
||||
if (!over || tierIdFromSortableId(String(active.id))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -196,6 +205,13 @@ export function useTierList() {
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
setActiveItem(null);
|
||||
setIsReorderingTiers(false);
|
||||
|
||||
const draggedTierId = tierIdFromSortableId(String(active.id));
|
||||
if (draggedTierId) {
|
||||
handleReorderTiers(draggedTierId, over?.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!over) {
|
||||
persistTiersStateToParams(tiers);
|
||||
@@ -275,6 +291,30 @@ export function useTierList() {
|
||||
persistTiersStateToParams(tiers);
|
||||
};
|
||||
|
||||
const handleDragCancel = () => {
|
||||
setActiveItem(null);
|
||||
setIsReorderingTiers(false);
|
||||
};
|
||||
|
||||
const handleReorderTiers = (
|
||||
draggedTierId: string,
|
||||
overId: string | number | undefined,
|
||||
) => {
|
||||
const overTierId = overId ? tierIdFromSortableId(String(overId)) : null;
|
||||
if (!overTierId || overTierId === draggedTierId) return;
|
||||
|
||||
const oldIndex = tiers.tiers.findIndex((tier) => tier.id === draggedTierId);
|
||||
const newIndex = tiers.tiers.findIndex((tier) => tier.id === overTierId);
|
||||
if (oldIndex === -1 || newIndex === -1) return;
|
||||
|
||||
const newState = {
|
||||
...tiers,
|
||||
tiers: arrayMove(tiers.tiers, oldIndex, newIndex),
|
||||
};
|
||||
setTiers(newState);
|
||||
persistTiersStateToParams(newState);
|
||||
};
|
||||
|
||||
const handleAddItemToTier = (item: TierListItem, tierId: string) => {
|
||||
const newState = addItemToTier(tiers, tierId, item);
|
||||
if (newState === tiers) return;
|
||||
@@ -408,44 +448,6 @@ export function useTierList() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleMoveTierUp = (tierId: string) => {
|
||||
const currentIndex = tiers.tiers.findIndex((tier) => tier.id === tierId);
|
||||
if (currentIndex <= 0) return;
|
||||
|
||||
const newTiers = [...tiers.tiers];
|
||||
[newTiers[currentIndex - 1], newTiers[currentIndex]] = [
|
||||
newTiers[currentIndex],
|
||||
newTiers[currentIndex - 1],
|
||||
];
|
||||
|
||||
const newState = {
|
||||
...tiers,
|
||||
tiers: newTiers,
|
||||
};
|
||||
setTiers(newState);
|
||||
persistTiersStateToParams(newState);
|
||||
};
|
||||
|
||||
const handleMoveTierDown = (tierId: string) => {
|
||||
const currentIndex = tiers.tiers.findIndex((tier) => tier.id === tierId);
|
||||
if (currentIndex === -1 || currentIndex >= tiers.tiers.length - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newTiers = [...tiers.tiers];
|
||||
[newTiers[currentIndex], newTiers[currentIndex + 1]] = [
|
||||
newTiers[currentIndex + 1],
|
||||
newTiers[currentIndex],
|
||||
];
|
||||
|
||||
const newState = {
|
||||
...tiers,
|
||||
tiers: newTiers,
|
||||
};
|
||||
setTiers(newState);
|
||||
persistTiersStateToParams(newState);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
const newState = {
|
||||
tiers: DEFAULT_TIERS,
|
||||
@@ -460,16 +462,16 @@ export function useTierList() {
|
||||
setItemType,
|
||||
state: tiers,
|
||||
activeItem,
|
||||
isReorderingTiers,
|
||||
handleDragStart,
|
||||
handleDragOver,
|
||||
handleDragEnd,
|
||||
handleDragCancel,
|
||||
handleAddTier,
|
||||
handleAddItemToTier,
|
||||
handleRemoveTier,
|
||||
handleRenameTier,
|
||||
handleChangeTierColor,
|
||||
handleMoveTierUp,
|
||||
handleMoveTierDown,
|
||||
handleReset,
|
||||
getItemsInTier,
|
||||
availableItems: getAvailableItems(),
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { CollisionDetection, Modifier } from "@dnd-kit/core";
|
||||
import {
|
||||
closestCenter,
|
||||
DndContext,
|
||||
DragOverlay,
|
||||
KeyboardSensor,
|
||||
@@ -8,7 +10,12 @@ import {
|
||||
useSensor,
|
||||
useSensors,
|
||||
} from "@dnd-kit/core";
|
||||
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
||||
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
||||
import {
|
||||
SortableContext,
|
||||
sortableKeyboardCoordinates,
|
||||
verticalListSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
import clsx from "clsx";
|
||||
import { HardDriveDownload, Plus, RefreshCcw } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
@@ -47,11 +54,33 @@ import {
|
||||
} from "../contexts/TierListContext";
|
||||
import type { TierListPlacementMode } from "../hooks/useTierList";
|
||||
import type { TierListItem } from "../tier-list-maker-schemas";
|
||||
import { tierListMakerPathWithState } from "../tier-list-maker-utils";
|
||||
import {
|
||||
tierIdFromSortableId,
|
||||
tierListMakerPathWithState,
|
||||
tierSortableId,
|
||||
} from "../tier-list-maker-utils";
|
||||
import styles from "./tier-list-maker.module.css";
|
||||
|
||||
const PLACEMENT_MODES: TierListPlacementMode[] = ["track", "click"];
|
||||
|
||||
/** Tier rows and items share one context, so each drag only collides with its own kind of target. */
|
||||
const tierAwareCollisionDetection: CollisionDetection = (args) => {
|
||||
const isTierDrag = tierIdFromSortableId(String(args.active.id)) !== null;
|
||||
const droppableContainers = args.droppableContainers.filter(
|
||||
(container) =>
|
||||
(tierIdFromSortableId(String(container.id)) !== null) === isTierDrag,
|
||||
);
|
||||
|
||||
return isTierDrag
|
||||
? closestCenter({ ...args, droppableContainers })
|
||||
: pointerWithin({ ...args, droppableContainers });
|
||||
};
|
||||
|
||||
const restrictTierDragToVerticalAxis: Modifier = (args) =>
|
||||
args.active && tierIdFromSortableId(String(args.active.id)) !== null
|
||||
? restrictToVerticalAxis(args)
|
||||
: args.transform;
|
||||
|
||||
export const meta: MetaFunction = (args) => {
|
||||
return metaTags({
|
||||
title: "Tier List Maker",
|
||||
@@ -97,9 +126,11 @@ function TierListMakerContent() {
|
||||
setItemType,
|
||||
state,
|
||||
activeItem,
|
||||
isReorderingTiers,
|
||||
handleDragStart,
|
||||
handleDragOver,
|
||||
handleDragEnd,
|
||||
handleDragCancel,
|
||||
handleAddTier,
|
||||
handleReset,
|
||||
hideAltKits,
|
||||
@@ -144,16 +175,23 @@ function TierListMakerContent() {
|
||||
<DndContext
|
||||
key={itemType}
|
||||
sensors={sensors}
|
||||
collisionDetection={pointerWithin}
|
||||
collisionDetection={tierAwareCollisionDetection}
|
||||
modifiers={[restrictTierDragToVerticalAxis]}
|
||||
onDragStart={handleDragStart}
|
||||
onDragOver={handleDragOver}
|
||||
onDragEnd={handleDragEnd}
|
||||
onDragCancel={handleDragCancel}
|
||||
>
|
||||
<div className="stack">
|
||||
<div className={styles.tierList}>
|
||||
{state.tiers.map((tier) => (
|
||||
<TierRow key={tier.id} tier={tier} />
|
||||
))}
|
||||
<SortableContext
|
||||
items={state.tiers.map((tier) => tierSortableId(tier.id))}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
{state.tiers.map((tier) => (
|
||||
<TierRow key={tier.id} tier={tier} />
|
||||
))}
|
||||
</SortableContext>
|
||||
</div>
|
||||
|
||||
<div className="stack horizontal md flex-wrap items-center">
|
||||
@@ -280,9 +318,12 @@ function TierListMakerContent() {
|
||||
</SendouTabPanel>
|
||||
</SendouTabs>
|
||||
|
||||
<DragOverlay>
|
||||
{activeItem ? <ItemDragPreview item={activeItem} /> : null}
|
||||
</DragOverlay>
|
||||
{/* an empty overlay would hide the dropped tier row during its drop animation */}
|
||||
{isReorderingTiers ? null : (
|
||||
<DragOverlay>
|
||||
{activeItem ? <ItemDragPreview item={activeItem} /> : null}
|
||||
</DragOverlay>
|
||||
)}
|
||||
</DndContext>
|
||||
</Main>
|
||||
);
|
||||
|
||||
@@ -10,6 +10,20 @@ export function tierListItemId(item: TierListItem) {
|
||||
return `${item.type}:${item.id}${item.nth ? `:${item.nth}` : ""}`;
|
||||
}
|
||||
|
||||
const TIER_SORTABLE_ID_PREFIX = "tier-sortable:";
|
||||
|
||||
/** Id of the tier row as a sortable, kept apart from the tier's own item drop zone id. */
|
||||
export function tierSortableId(tierId: string) {
|
||||
return `${TIER_SORTABLE_ID_PREFIX}${tierId}`;
|
||||
}
|
||||
|
||||
/** Tier id behind a sortable id, or `null` if the id belongs to something else being dragged. */
|
||||
export function tierIdFromSortableId(id: string) {
|
||||
return id.startsWith(TIER_SORTABLE_ID_PREFIX)
|
||||
? id.slice(TIER_SORTABLE_ID_PREFIX.length)
|
||||
: null;
|
||||
}
|
||||
|
||||
/** Path that reopens the given tier list, used by the exported image's QR code. */
|
||||
export function tierListMakerPathWithState({
|
||||
state,
|
||||
|
||||
5
changelog/2026-09-22-tier-list-maker-tier-reorder.md
Normal file
5
changelog/2026-09-22-tier-list-maker-tier-reorder.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
navItem: tier-list-maker
|
||||
type: feature
|
||||
---
|
||||
Tiers can be reordered by dragging the handle on the right side of the row
|
||||
@@ -504,6 +504,29 @@ export async function waitForDropToSettle(page: Page) {
|
||||
await page.waitForTimeout(2 * DND_KIT_CLICK_SUPPRESSION_MS);
|
||||
}
|
||||
|
||||
/** Drags `from` onto the center of `to` with a dnd-kit compatible stepped pointer move, then waits for the drop to settle. */
|
||||
export async function dragAndDrop(
|
||||
page: Page,
|
||||
{ from, to }: { from: Locator; to: Locator },
|
||||
) {
|
||||
await from.hover();
|
||||
await page.mouse.down();
|
||||
|
||||
const targetBox = await to.boundingBox();
|
||||
if (!targetBox) {
|
||||
throw new Error("The drop target has no bounding box");
|
||||
}
|
||||
// the drag & drop library only registers the drop when moved in steps
|
||||
await page.mouse.move(
|
||||
targetBox.x + targetBox.width / 2,
|
||||
targetBox.y + targetBox.height / 2,
|
||||
{ steps: 10 },
|
||||
);
|
||||
await page.mouse.up();
|
||||
|
||||
await waitForDropToSettle(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* The scroll position the page was at when it was last pressed, for asserting that
|
||||
* an action did not move the viewer. Playwright scrolls a click target into view
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
import { invariant } from "~/utils/invariant";
|
||||
import { TIER_LIST_MAKER_URL } from "~/utils/urls";
|
||||
import {
|
||||
dragAndDrop,
|
||||
expect,
|
||||
expectIsHydrated,
|
||||
navigate,
|
||||
waitForDropToSettle,
|
||||
} from "../../helpers/playwright";
|
||||
|
||||
type ItemType =
|
||||
@@ -49,6 +48,7 @@ export class TierListMakerPage {
|
||||
this.locators = {
|
||||
emptyTiersDragMode: page.getByText("Drop items here"),
|
||||
emptyTiersClickMode: page.getByText("Click items to add here"),
|
||||
tierDragHandles: page.getByRole("button", { name: "Reorder tier" }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -88,20 +88,30 @@ export class TierListMakerPage {
|
||||
const emptyTiers = this.locators.emptyTiersDragMode;
|
||||
const emptyCountBefore = await emptyTiers.count();
|
||||
|
||||
await this.poolItems(type).first().hover();
|
||||
await this.page.mouse.down();
|
||||
|
||||
const tierBox = await emptyTiers.last().boundingBox();
|
||||
invariant(tierBox, "The tier dropped on has no bounding box");
|
||||
await this.page.mouse.move(
|
||||
tierBox.x + tierBox.width / 2,
|
||||
tierBox.y + tierBox.height / 2,
|
||||
{ steps: 10 },
|
||||
);
|
||||
await this.page.mouse.up();
|
||||
await dragAndDrop(this.page, {
|
||||
from: this.poolItems(type).first(),
|
||||
to: emptyTiers.last(),
|
||||
});
|
||||
|
||||
await expect(emptyTiers).toHaveCount(emptyCountBefore - 1);
|
||||
await waitForDropToSettle(this.page);
|
||||
}
|
||||
|
||||
/** Ids of the tier rows, top to bottom. */
|
||||
async tierIds() {
|
||||
return this.page
|
||||
.locator("[data-tier-id]")
|
||||
.evaluateAll((rows) =>
|
||||
rows.map((row) => (row as HTMLElement).dataset.tierId),
|
||||
);
|
||||
}
|
||||
|
||||
async dragTier({ from, to }: { from: number; to: number }) {
|
||||
const handles = this.locators.tierDragHandles;
|
||||
|
||||
await dragAndDrop(this.page, {
|
||||
from: handles.nth(from),
|
||||
to: handles.nth(to),
|
||||
});
|
||||
}
|
||||
|
||||
async clickFirstItem(type: ItemType) {
|
||||
|
||||
@@ -39,7 +39,7 @@ test.describe("Tier List Maker", () => {
|
||||
const tierList = new TierListMakerPage(page);
|
||||
await tierList.goto();
|
||||
|
||||
// click to place is the default mode
|
||||
await tierList.setPlacementMode("click");
|
||||
await expect(tierList.locators.emptyTiersClickMode).toHaveCount(5);
|
||||
|
||||
// the first tier is selected by default
|
||||
@@ -50,4 +50,37 @@ test.describe("Tier List Maker", () => {
|
||||
await tierList.clickFirstItem("main-weapon");
|
||||
await expect(tierList.locators.emptyTiersClickMode).toHaveCount(3);
|
||||
});
|
||||
|
||||
test("tiers are reordered by dragging their handle", async ({ page }) => {
|
||||
const tierList = new TierListMakerPage(page);
|
||||
await tierList.goto();
|
||||
|
||||
expect(await tierList.tierIds()).toEqual([
|
||||
"tier-x",
|
||||
"tier-s",
|
||||
"tier-a",
|
||||
"tier-b",
|
||||
"tier-c",
|
||||
]);
|
||||
|
||||
await tierList.dragTier({ from: 0, to: 2 });
|
||||
|
||||
expect(await tierList.tierIds()).toEqual([
|
||||
"tier-s",
|
||||
"tier-a",
|
||||
"tier-x",
|
||||
"tier-b",
|
||||
"tier-c",
|
||||
]);
|
||||
|
||||
await tierList.reload();
|
||||
|
||||
expect(await tierList.tierIds()).toEqual([
|
||||
"tier-s",
|
||||
"tier-a",
|
||||
"tier-x",
|
||||
"tier-b",
|
||||
"tier-c",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user