Skip to content

Commit 61128b4

Browse files
authored
feat(overview): create overview page / 创建总览页面 (#611)
1 parent 8070120 commit 61128b4

32 files changed

Lines changed: 3292 additions & 64 deletions

packages/app/cypress/component/header.cy.tsx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,22 @@ import { PathnameContext } from 'next/dist/shared/lib/hooks-client-context.share
55
import { Header } from '@/components/header/header';
66
import { ThemeProvider } from '@/components/ui/theme-provider';
77

8+
// Mounted outside the Next app shell; next-style-loader inserts the global
9+
// stylesheet before this anchor, so it must exist before the import below.
10+
const cssAnchor = document.createElement('noscript');
11+
cssAnchor.id = '__next_css__DO_NOT_USE__';
12+
document.head.append(cssAnchor);
13+
require('@/app/globals.css');
14+
815
const queryClient = new QueryClient({
916
defaultOptions: { queries: { retry: false } },
1017
});
1118

19+
/** Minimum touch target, per WCAG 2.5.8 and the `size-11` / `min-h-11` utilities. */
20+
const MIN_TOUCH_PX = 44;
21+
/** Tolerance for sub-pixel layout rounding. */
22+
const EPSILON = 0.5;
23+
1224
function createMockRouter() {
1325
return {
1426
push: cy.stub(),
@@ -20,6 +32,10 @@ function createMockRouter() {
2032
};
2133
}
2234

35+
function rectOf(selector: string) {
36+
return cy.get(selector).then(($el) => $el[0].getBoundingClientRect());
37+
}
38+
2339
describe('Header', () => {
2440
beforeEach(() => {
2541
const mockRouter = createMockRouter();
@@ -84,4 +100,87 @@ describe('Header', () => {
84100
cy.contains('a', 'Supporters').should('be.visible').and('have.attr', 'href', '/quotes');
85101
});
86102
});
103+
104+
describe('at 320x700', () => {
105+
beforeEach(() => {
106+
cy.viewport(320, 700);
107+
});
108+
109+
it('hides the GitHub star control', () => {
110+
cy.get('[data-testid="header-star-button"]').should('not.be.visible');
111+
});
112+
113+
it('keeps the remaining controls inside the header bounds', () => {
114+
cy.get('[data-testid="header"]').then(($header) => {
115+
const bounds = $header[0].getBoundingClientRect();
116+
const selectors = [
117+
'[data-testid="header-brand"]',
118+
'[data-testid="language-toggle"]',
119+
'[data-testid="theme-toggle"]',
120+
'[data-testid="mobile-menu-toggle"]',
121+
];
122+
selectors.forEach((selector) => {
123+
rectOf(selector).then((rect) => {
124+
expect(rect.left, `${selector} left edge`).to.be.at.least(bounds.left - EPSILON);
125+
expect(rect.right, `${selector} right edge`).to.be.at.most(bounds.right + EPSILON);
126+
});
127+
});
128+
});
129+
});
130+
131+
it('gives the brand and language controls a 44px touch height', () => {
132+
['[data-testid="header-brand"]', '[data-testid="language-toggle"]'].forEach((selector) => {
133+
rectOf(selector).then((rect) => {
134+
expect(rect.height, `${selector} height`).to.be.at.least(MIN_TOUCH_PX - EPSILON);
135+
});
136+
});
137+
});
138+
139+
it('gives the icon controls a 44px touch target in both dimensions', () => {
140+
['[data-testid="theme-toggle"]', '[data-testid="mobile-menu-toggle"]'].forEach((selector) => {
141+
rectOf(selector).then((rect) => {
142+
expect(rect.width, `${selector} width`).to.be.at.least(MIN_TOUCH_PX - EPSILON);
143+
expect(rect.height, `${selector} height`).to.be.at.least(MIN_TOUCH_PX - EPSILON);
144+
});
145+
});
146+
});
147+
148+
it('does not overflow horizontally', () => {
149+
cy.get('[data-testid="header"]').then(($header) => {
150+
const header = $header[0];
151+
expect(header.scrollWidth, 'header scrollWidth').to.be.at.most(header.clientWidth);
152+
});
153+
});
154+
155+
it('still opens the menu and exposes its links', () => {
156+
cy.get('[data-testid="mobile-menu-toggle"]').click();
157+
cy.get('[data-testid="mobile-menu"]').should('be.visible');
158+
cy.get('[data-testid="mobile-menu"]').within(() => {
159+
['Home', 'Dashboard', 'Comparisons', 'Supporters', 'Datasets', 'Articles', 'About'].forEach(
160+
(label) => {
161+
cy.contains('a', label).should('be.visible');
162+
},
163+
);
164+
});
165+
cy.get('[data-testid="mobile-menu"] a').each(($link) => {
166+
const rect = $link[0].getBoundingClientRect();
167+
expect(rect.height, `${$link.text()} link height`).to.be.at.least(MIN_TOUCH_PX - EPSILON);
168+
});
169+
});
170+
171+
it('exposes the minecraft audio toggles in the mobile menu without overflowing', () => {
172+
cy.get('[data-testid="theme-toggle"]').click();
173+
cy.get('[data-testid="theme-toggle"]').click();
174+
cy.get('html').should('have.class', 'minecraft');
175+
cy.get('[data-testid="mobile-menu-toggle"]').click();
176+
cy.get('[data-testid="mobile-menu"]').within(() => {
177+
cy.get('button[aria-label="Mute music"]').should('be.visible');
178+
cy.get('button[aria-label="Mute click sounds"]').should('be.visible');
179+
});
180+
cy.get('[data-testid="header"]').then(($header) => {
181+
const header = $header[0];
182+
expect(header.scrollWidth, 'header scrollWidth').to.be.at.most(header.clientWidth);
183+
});
184+
});
185+
});
87186
});

packages/app/cypress/component/tab-nav.cy.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,19 @@ describe('TabNav — unofficialrun URL preservation (issue #319)', () => {
4848

4949
it('renders bare hrefs when the URL has no unofficialrun param', () => {
5050
mountTabNav({});
51+
cy.get('[data-testid="tab-trigger-overview"]').should('have.attr', 'href', '/overview');
5152
cy.get('[data-testid="tab-trigger-evaluation"]').should('have.attr', 'href', '/evaluation');
5253
cy.get('[data-testid="tab-trigger-historical"]').should('have.attr', 'href', '/historical');
5354
cy.get('[data-testid="tab-trigger-calculator"]').should('have.attr', 'href', '/calculator');
5455
});
5556

5657
it('appends unofficialruns to every tab href when the URL has the param', () => {
5758
mountTabNav({ search: '?unofficialruns=12345' });
59+
cy.get('[data-testid="tab-trigger-overview"]').should(
60+
'have.attr',
61+
'href',
62+
'/overview?unofficialruns=12345',
63+
);
5864
cy.get('[data-testid="tab-trigger-evaluation"]').should(
5965
'have.attr',
6066
'href',

packages/app/cypress/e2e/navigation.cy.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ describe('Chart Section Tabs — E2E', () => {
1010
});
1111

1212
it('updates the URL path when switching tabs', () => {
13+
cy.get('[data-testid="tab-trigger-overview"]').click();
14+
cy.url().should('include', '/overview');
15+
1316
cy.get('[data-testid="tab-trigger-evaluation"]').click();
1417
cy.url().should('include', '/evaluation');
1518

0 commit comments

Comments
 (0)