메인 턴 당기기와 미루기를 1턴 기본 분할 버튼으로 변경
This commit is contained in:
@@ -987,6 +987,20 @@ const install = async (
|
|||||||
if (name === 'messages.getContacts') return response({ nation: [] });
|
if (name === 'messages.getContacts') return response({ nation: [] });
|
||||||
if (name === 'board.getAccess') return response({ canMeeting: false, canSecret: false });
|
if (name === 'board.getAccess') return response({ canMeeting: false, canSecret: false });
|
||||||
if (name === 'tournament.getState') return response({ stage: 0 });
|
if (name === 'tournament.getState') return response({ stage: 0 });
|
||||||
|
if (name === 'turns.reserved.shiftGeneral') {
|
||||||
|
const input = (body as Record<string, { generalId: number; amount: number; expectedRevision: number }>)[
|
||||||
|
String(names.indexOf(name))
|
||||||
|
];
|
||||||
|
expect(route.request().method()).toBe('POST');
|
||||||
|
expect(input.generalId).toBe(generalId);
|
||||||
|
expect(input.expectedRevision).toBe(generalRevision);
|
||||||
|
expect(
|
||||||
|
Number.isInteger(input.amount) && Math.abs(input.amount) >= 1 && Math.abs(input.amount) <= 6
|
||||||
|
).toBe(true);
|
||||||
|
requests.push(input);
|
||||||
|
generalRevision += 1;
|
||||||
|
return response({ ok: true, revision: generalRevision, turns: generalTurns, autorunLimit: 2403 });
|
||||||
|
}
|
||||||
if (name === 'turns.reserved.setGeneralBulk') {
|
if (name === 'turns.reserved.setGeneralBulk') {
|
||||||
requests.push(body);
|
requests.push(body);
|
||||||
if (rejectGeneral) return errorResponse(name, '대상 도시를 선택할 수 없습니다.');
|
if (rejectGeneral) return errorResponse(name, '대상 도시를 선택할 수 없습니다.');
|
||||||
@@ -1019,6 +1033,80 @@ const install = async (
|
|||||||
return requests;
|
return requests;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (const width of [1200, 500]) {
|
||||||
|
test(`split turn shift applies one turn or the chosen count in both modes at ${width}px`, async ({
|
||||||
|
page,
|
||||||
|
}, testInfo) => {
|
||||||
|
const requests = await install(page);
|
||||||
|
const shifts = () =>
|
||||||
|
requests.filter((entry) => typeof entry === 'object' && entry !== null && 'amount' in entry);
|
||||||
|
await page.setViewportSize({ width, height: 900 });
|
||||||
|
await page.goto('/');
|
||||||
|
const editor = page.locator('[data-command-scope="general"]:visible');
|
||||||
|
const evidence: unknown[] = [];
|
||||||
|
for (const advanced of [false, true]) {
|
||||||
|
if (advanced) await editor.getByRole('button', { name: '고급 모드', exact: true }).click();
|
||||||
|
for (const [index, label] of ['당기기', '미루기'].entries()) {
|
||||||
|
const direction = index === 0 ? -1 : 1;
|
||||||
|
const control = editor.locator('.bottom-shift-control').nth(index);
|
||||||
|
const main = control.getByRole('button', { name: label, exact: true });
|
||||||
|
const menu = control.locator('details');
|
||||||
|
const toggle = menu.locator('summary');
|
||||||
|
const before = shifts().length;
|
||||||
|
await main.click();
|
||||||
|
await expect.poll(() => shifts().length).toBe(before + 1);
|
||||||
|
expect(shifts().at(-1)).toMatchObject({ amount: direction });
|
||||||
|
await expect(menu).not.toHaveAttribute('open');
|
||||||
|
await toggle.focus();
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
await expect(menu).toHaveAttribute('open');
|
||||||
|
expect(shifts().length).toBe(before + 1);
|
||||||
|
await expect(menu.locator('.menu-items > button')).toHaveText([
|
||||||
|
'1턴',
|
||||||
|
'2턴',
|
||||||
|
'3턴',
|
||||||
|
'4턴',
|
||||||
|
'5턴',
|
||||||
|
'6턴',
|
||||||
|
]);
|
||||||
|
const geometry = await control.evaluate((element) => {
|
||||||
|
const main = element.querySelector('button')!;
|
||||||
|
const toggle = element.querySelector('summary')!;
|
||||||
|
const menu = element.querySelector('.menu-items')!;
|
||||||
|
return {
|
||||||
|
main: main.getBoundingClientRect().toJSON(),
|
||||||
|
toggle: toggle.getBoundingClientRect().toJSON(),
|
||||||
|
menu: menu.getBoundingClientRect().toJSON(),
|
||||||
|
style: { radius: getComputedStyle(main).borderRadius, font: getComputedStyle(main).fontFamily },
|
||||||
|
overflow: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
expect(geometry.main.right).toBeCloseTo(geometry.toggle.left, 1);
|
||||||
|
expect(geometry.menu.right).toBeLessThanOrEqual(width);
|
||||||
|
expect(geometry.menu.left).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(geometry.overflow).toBeLessThanOrEqual(0);
|
||||||
|
evidence.push({ advanced, label, geometry });
|
||||||
|
await page.screenshot({
|
||||||
|
path: testInfo.outputPath(`split-${advanced}-${index}-${width}.png`),
|
||||||
|
fullPage: true,
|
||||||
|
});
|
||||||
|
await menu.getByRole('button', { name: '6턴', exact: true }).click();
|
||||||
|
await expect.poll(() => shifts().length).toBe(before + 2);
|
||||||
|
expect(shifts().at(-1)).toMatchObject({ amount: direction * 6 });
|
||||||
|
await expect(menu).not.toHaveAttribute('open');
|
||||||
|
await main.click();
|
||||||
|
await expect.poll(() => shifts().length).toBe(before + 3);
|
||||||
|
expect(shifts().at(-1)).toMatchObject({ amount: direction });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await writeFile(testInfo.outputPath('split-evidence.json'), JSON.stringify(evidence, null, 2));
|
||||||
|
await writeFile(
|
||||||
|
testInfo.outputPath('split-editor.html'),
|
||||||
|
await editor.evaluate((element) => element.outerHTML)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
test('offers 12 repeat turns and six shift turns for general turns while keeping the chief range', async ({ page }) => {
|
test('offers 12 repeat turns and six shift turns for general turns while keeping the chief range', async ({ page }) => {
|
||||||
await install(page);
|
await install(page);
|
||||||
await page.setViewportSize({ width: 1200, height: 900 });
|
await page.setViewportSize({ width: 1200, height: 900 });
|
||||||
|
|||||||
@@ -4393,10 +4393,12 @@ test('all main Lumen button families share the rounded pressed geometry', async
|
|||||||
[
|
[
|
||||||
'당기기',
|
'당기기',
|
||||||
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '당기기' }),
|
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '당기기' }),
|
||||||
|
{ radius: '5.25px 0px 0px 5.25px' },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'미루기',
|
'미루기',
|
||||||
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '미루기' }),
|
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '미루기' }),
|
||||||
|
{ radius: '5.25px 0px 0px 5.25px' },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'펼치기',
|
'펼치기',
|
||||||
@@ -4457,7 +4459,7 @@ test('all main Lumen button families share the rounded pressed geometry', async
|
|||||||
filter: 'none',
|
filter: 'none',
|
||||||
});
|
});
|
||||||
if (label === '당기기' || label === '미루기') {
|
if (label === '당기기' || label === '미루기') {
|
||||||
expect(base.width, `${label} fills its menu column`).toBeCloseTo(base.parentWidth, 2);
|
expect(base.width, `${label} leaves room for the split button`).toBeCloseTo(base.parentWidth - 28, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
await control.focus();
|
await control.focus();
|
||||||
@@ -4702,7 +4704,7 @@ test('mobile main Lumen button families keep the same state geometry without ove
|
|||||||
await expect(control).toHaveClass(/legacy-button/u);
|
await expect(control).toHaveClass(/legacy-button/u);
|
||||||
await expect(control).toHaveCSS(
|
await expect(control).toHaveCSS(
|
||||||
'border-radius',
|
'border-radius',
|
||||||
index === 7 ? '0px 5.25px 5.25px 0px' : index === 8 ? '5.25px 0px 0px 5.25px' : '5.25px'
|
index === 7 ? '0px 5.25px 5.25px 0px' : [4, 5, 8].includes(index) ? '5.25px 0px 0px 5.25px' : '5.25px'
|
||||||
);
|
);
|
||||||
await expect(control).toHaveCSS('border-bottom-width', '4px');
|
await expect(control).toHaveCSS('border-bottom-width', '4px');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -701,38 +701,38 @@ const clickOutsideMenu = (event: Event) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!props.compact" class="bottom-actions">
|
<div v-if="!props.compact" class="bottom-actions">
|
||||||
<details class="legacy-menu bottom-shift-menu">
|
<div v-for="direction in [-1, 1]" :key="direction" class="bottom-shift-control">
|
||||||
<summary class="legacy-button legacy-button--secondary" role="button">당기기</summary>
|
<button
|
||||||
<div class="menu-items">
|
class="legacy-button legacy-button--secondary shift-main"
|
||||||
<button
|
type="button"
|
||||||
v-for="amount in props.maxShiftTurn"
|
:title="direction < 0 ? '1턴 당기기' : '1턴 미루기'"
|
||||||
:key="amount"
|
@click="emit('shift', direction)"
|
||||||
type="button"
|
>
|
||||||
@click="
|
{{ direction < 0 ? '당기기' : '미루기' }}
|
||||||
emit('shift', -amount);
|
</button>
|
||||||
clickOutsideMenu($event);
|
<details class="legacy-menu bottom-shift-menu">
|
||||||
"
|
<summary
|
||||||
|
class="legacy-button legacy-button--secondary"
|
||||||
|
role="button"
|
||||||
|
:aria-label="direction < 0 ? '당길 턴 수 선택' : '미룰 턴 수 선택'"
|
||||||
>
|
>
|
||||||
{{ amount }}턴
|
<span aria-hidden="true">▾</span>
|
||||||
</button>
|
</summary>
|
||||||
</div>
|
<div class="menu-items">
|
||||||
</details>
|
<button
|
||||||
<details class="legacy-menu bottom-shift-menu">
|
v-for="amount in props.maxShiftTurn"
|
||||||
<summary class="legacy-button legacy-button--secondary" role="button">미루기</summary>
|
:key="amount"
|
||||||
<div class="menu-items">
|
type="button"
|
||||||
<button
|
@click="
|
||||||
v-for="amount in props.maxShiftTurn"
|
emit('shift', direction * amount);
|
||||||
:key="amount"
|
clickOutsideMenu($event);
|
||||||
type="button"
|
"
|
||||||
@click="
|
>
|
||||||
emit('shift', amount);
|
{{ amount }}턴
|
||||||
clickOutsideMenu($event);
|
</button>
|
||||||
"
|
</div>
|
||||||
>
|
</details>
|
||||||
{{ amount }}턴
|
</div>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
<button class="legacy-button legacy-button--secondary" type="button" @click="expanded = !expanded">
|
<button class="legacy-button legacy-button--secondary" type="button" @click="expanded = !expanded">
|
||||||
{{ expanded ? '접기' : '펼치기' }}
|
{{ expanded ? '접기' : '펼치기' }}
|
||||||
</button>
|
</button>
|
||||||
@@ -1098,8 +1098,29 @@ const clickOutsideMenu = (event: Event) => {
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
}
|
}
|
||||||
|
.bottom-shift-control {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.shift-main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
.bottom-shift-menu {
|
||||||
|
flex: 0 0 28px;
|
||||||
|
}
|
||||||
.bottom-shift-menu > summary {
|
.bottom-shift-menu > summary {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding-inline: 0;
|
||||||
|
border-left: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
.bottom-shift-menu .menu-items {
|
||||||
|
left: auto;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
||||||
.command-picker {
|
.command-picker {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
Reference in New Issue
Block a user