Skip to content

Commit c0e7749

Browse files
authored
Merge pull request #41 from ErnieAtLYD/refactor/remove-over-engineered-focus-system
Refactor/remove over engineered focus system
2 parents 4070fd8 + 606d449 commit c0e7749

4 files changed

Lines changed: 153 additions & 747 deletions

File tree

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,7 @@ src/
157157
├── app/
158158
│ ├── api/
159159
│ │ └── reflect/ # AI processing endpoint
160-
│ ├── components-demo/ # Component showcase page
161-
│ └── page.tsx # Main application page
160+
│ │ └── page.tsx # Main application page
162161
├── components/
163162
│ ├── ui/ # UI components (buttons, inputs, etc.)
164163
│ └── __tests__/ # Component unit tests

e2e/responsive-validation.spec.ts

Lines changed: 84 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -1,277 +1,127 @@
11
import { test, expect } from '@playwright/test'
22

33
const breakpoints = {
4-
mobile: { width: 375, height: 667, name: 'Mobile' },
5-
xs: { width: 480, height: 800, name: 'XS' },
6-
sm: { width: 640, height: 800, name: 'SM' },
7-
md: { width: 768, height: 1024, name: 'MD' },
8-
lg: { width: 1024, height: 768, name: 'LG' },
9-
xl: { width: 1280, height: 1024, name: 'XL' },
10-
'2xl': { width: 1536, height: 1024, name: '2XL' },
4+
mobile: 375,
5+
xs: 480,
6+
sm: 640,
7+
md: 768,
8+
lg: 1024,
9+
xl: 1280,
10+
'2xl': 1536,
1111
}
1212

13-
test.describe('Responsive Layout Validation', () => {
14-
test.beforeEach(async ({ page }) => {
15-
await page.goto('/')
16-
})
17-
18-
for (const [key, viewport] of Object.entries(breakpoints)) {
19-
test(`should render correctly at ${viewport.name} (${viewport.width}x${viewport.height})`, async ({
13+
test.describe('Journal App Responsive Validation', () => {
14+
const testBreakpoints = [
15+
{ name: 'Mobile', width: breakpoints.mobile, height: 667 },
16+
{ name: 'XS', width: breakpoints.xs, height: 800 },
17+
{ name: 'SM', width: breakpoints.sm, height: 800 },
18+
{ name: 'MD', width: breakpoints.md, height: 1024 },
19+
{ name: 'LG', width: breakpoints.lg, height: 768 },
20+
{ name: 'XL', width: breakpoints.xl, height: 1024 },
21+
{ name: '2XL', width: breakpoints['2xl'], height: 1024 },
22+
]
23+
24+
testBreakpoints.forEach(({ name, width, height }) => {
25+
test(`should render correctly at ${name} (${width}x${height})`, async ({
2026
page,
2127
}) => {
22-
// Set viewport size
23-
await page.setViewportSize({
24-
width: viewport.width,
25-
height: viewport.height,
26-
})
27-
28-
// Wait for layout to stabilize
29-
await page.waitForLoadState('networkidle')
30-
31-
// Check that main content is visible
32-
await expect(page.locator('main')).toBeVisible()
33-
await expect(
34-
page.getByRole('heading', { name: 'Reflect App' })
35-
).toBeVisible()
36-
37-
// Verify no horizontal overflow
38-
const bodyScrollWidth = await page.evaluate(
39-
() => document.body.scrollWidth
28+
await page.goto('/')
29+
await page.setViewportSize({ width, height })
30+
31+
// Core app elements should be visible and functional
32+
await expect(page.locator('h1')).toBeVisible()
33+
await expect(page.getByTestId('journal-entry-input')).toBeVisible()
34+
await expect(page.getByTestId('reflect-now-button')).toBeVisible()
35+
36+
// Test actual user interaction
37+
const input = page.getByTestId('journal-entry-input')
38+
await input.fill(
39+
'This is a test entry to verify the app works at this screen size.'
4040
)
41-
const windowInnerWidth = await page.evaluate(() => window.innerWidth)
42-
expect(bodyScrollWidth).toBeLessThanOrEqual(windowInnerWidth + 1) // Allow 1px tolerance
4341

44-
// Check that current breakpoint indicator is showing correctly
45-
if (key === 'mobile') {
46-
const mobileIndicator = page
47-
.locator('.bg-destructive')
48-
.filter({ hasText: /Mobile/ })
49-
await expect(mobileIndicator).toBeVisible()
50-
} else if (key === 'xs') {
51-
const xsIndicator = page
52-
.locator('.bg-chart-1')
53-
.filter({ hasText: /XS \(480px\+\)/ })
54-
await expect(xsIndicator).toBeVisible()
55-
} else if (key === 'sm') {
56-
const smIndicator = page
57-
.locator('.bg-chart-2')
58-
.filter({ hasText: /SM \(640px\+\)/ })
59-
await expect(smIndicator).toBeVisible()
60-
} else if (key === 'md') {
61-
const mdIndicator = page
62-
.locator('.bg-chart-3')
63-
.filter({ hasText: /MD \(768px\+\)/ })
64-
await expect(mdIndicator).toBeVisible()
65-
} else if (key === 'lg') {
66-
const lgIndicator = page
67-
.locator('.bg-chart-4')
68-
.filter({ hasText: /LG \(1024px\+\)/ })
69-
await expect(lgIndicator).toBeVisible()
70-
} else if (key === 'xl') {
71-
const xlIndicator = page
72-
.locator('.bg-chart-5')
73-
.filter({ hasText: /XL \(1280px\+\)/ })
74-
await expect(xlIndicator).toBeVisible()
75-
} else if (key === '2xl') {
76-
const x2xlIndicator = page
77-
.locator('.bg-primary')
78-
.filter({ hasText: /2XL \(1536px\+\)/ })
79-
await expect(x2xlIndicator).toBeVisible()
80-
}
42+
await expect(page.getByTestId('reflect-now-button')).toBeEnabled()
8143

82-
// Verify grid layout adapts correctly
83-
const breakpointGrid = page.locator('[data-testid="breakpoint-grid"]')
84-
await expect(breakpointGrid).toBeVisible()
44+
// Clear input should work
45+
await input.clear()
46+
await expect(page.getByTestId('reflect-now-button')).toBeDisabled()
8547

86-
// Take screenshot for visual validation
87-
await page.screenshot({
88-
path: `e2e/screenshots/responsive-${key}-${viewport.width}x${viewport.height}.png`,
89-
fullPage: true,
48+
// Theme toggle should be accessible
49+
const themeToggle = page.getByTestId('theme-toggle-advanced')
50+
await expect(themeToggle).toBeVisible()
51+
52+
// No horizontal overflow
53+
const hasHorizontalOverflow = await page.evaluate(() => {
54+
return document.body.scrollWidth > document.body.clientWidth
9055
})
56+
expect(hasHorizontalOverflow).toBe(false)
9157
})
92-
}
93-
94-
test('should handle smooth transitions between breakpoints', async ({
95-
page,
96-
}) => {
97-
// Start at mobile size
98-
await page.setViewportSize({ width: 375, height: 667 })
99-
await page.waitForLoadState('networkidle')
100-
101-
// Gradually increase width to test transitions
102-
const testWidths = [480, 640, 768, 1024, 1280, 1536]
103-
104-
for (const width of testWidths) {
105-
await page.setViewportSize({ width, height: 800 })
106-
107-
// Wait for layout to stabilize after viewport change
108-
await page.waitForLoadState('networkidle')
109-
110-
// Wait for the appropriate breakpoint indicator to be visible
111-
// This ensures the CSS transitions have completed
112-
if (width >= 1536) {
113-
await expect(
114-
page.locator('.bg-primary').filter({ hasText: /2XL \(1536px\+\)/ })
115-
).toBeVisible()
116-
} else if (width >= 1280) {
117-
await expect(
118-
page.locator('.bg-chart-5').filter({ hasText: /XL \(1280px\+\)/ })
119-
).toBeVisible()
120-
} else if (width >= 1024) {
121-
await expect(
122-
page.locator('.bg-chart-4').filter({ hasText: /LG \(1024px\+\)/ })
123-
).toBeVisible()
124-
} else if (width >= 768) {
125-
await expect(
126-
page.locator('.bg-chart-3').filter({ hasText: /MD \(768px\+\)/ })
127-
).toBeVisible()
128-
} else if (width >= 640) {
129-
await expect(
130-
page.locator('.bg-chart-2').filter({ hasText: /SM \(640px\+\)/ })
131-
).toBeVisible()
132-
} else if (width >= 480) {
133-
await expect(
134-
page.locator('.bg-chart-1').filter({ hasText: /XS \(480px\+\)/ })
135-
).toBeVisible()
136-
}
137-
138-
// Verify no layout breaks
139-
const bodyScrollWidth = await page.evaluate(
140-
() => document.body.scrollWidth
141-
)
142-
const windowInnerWidth = await page.evaluate(() => window.innerWidth)
143-
expect(bodyScrollWidth).toBeLessThanOrEqual(windowInnerWidth + 1)
144-
145-
// Verify content is still visible
146-
await expect(
147-
page.getByRole('heading', { name: 'Reflect App' })
148-
).toBeVisible()
149-
}
15058
})
15159

15260
test('should maintain proper spacing and alignment at all breakpoints', async ({
15361
page,
15462
}) => {
155-
for (const [, viewport] of Object.entries(breakpoints)) {
156-
await page.setViewportSize({
157-
width: viewport.width,
158-
height: viewport.height,
159-
})
160-
await page.waitForLoadState('networkidle')
63+
await page.goto('/')
16164

162-
// Check header layout
163-
const header = page.locator('header')
164-
await expect(header).toBeVisible()
65+
for (const { width, height } of testBreakpoints) {
66+
await page.setViewportSize({ width, height })
16567

166-
// Check that cards maintain proper spacing
167-
const cards = page.locator('[data-testid="card"], .group')
168-
const cardCount = await cards.count()
68+
// Main container should be centered
69+
const main = page.locator('main').first()
70+
await expect(main).toBeVisible()
16971

170-
if (cardCount > 0) {
171-
for (let i = 0; i < cardCount; i++) {
172-
await expect(cards.nth(i)).toBeVisible()
173-
}
174-
}
72+
// Elements should have proper spacing
73+
const input = page.getByTestId('journal-entry-input')
74+
const button = page.getByTestId('reflect-now-button')
75+
76+
await expect(input).toBeVisible()
77+
await expect(button).toBeVisible()
17578

176-
// Verify form elements are properly sized
177-
const buttons = page.locator('button')
178-
const buttonCount = await buttons.count()
79+
// Button should be below input with proper spacing
80+
const inputBox = await input.boundingBox()
81+
const buttonBox = await button.boundingBox()
17982

180-
for (let i = 0; i < buttonCount; i++) {
181-
const button = buttons.nth(i)
182-
if (await button.isVisible()) {
183-
const boundingBox = await button.boundingBox()
184-
if (boundingBox) {
185-
// Buttons should have reasonable touch target size on mobile (< 480px)
186-
// Note: Some buttons might have borders/padding that affect rendered size
187-
if (viewport.width < 480) {
188-
expect(boundingBox.height).toBeGreaterThanOrEqual(36)
189-
} else {
190-
// At xs+ breakpoints, can be smaller
191-
expect(boundingBox.height).toBeGreaterThanOrEqual(28)
192-
}
193-
}
194-
}
83+
if (inputBox && buttonBox) {
84+
// Button should be below input (allowing for some margin)
85+
expect(buttonBox.y).toBeGreaterThan(inputBox.y + inputBox.height - 10)
19586
}
19687
}
19788
})
19889

19990
test('should handle container behavior correctly', async ({ page }) => {
200-
for (const [, viewport] of Object.entries(breakpoints)) {
201-
await page.setViewportSize({
202-
width: viewport.width,
203-
height: viewport.height,
204-
})
205-
await page.waitForLoadState('networkidle')
206-
207-
// Check container max-width behavior
208-
const container = page.locator('.container').first()
209-
const containerBoundingBox = await container.boundingBox()
210-
211-
if (containerBoundingBox) {
212-
// Container should never exceed viewport width
213-
expect(containerBoundingBox.width).toBeLessThanOrEqual(viewport.width)
91+
await page.goto('/')
21492

215-
// Container should be properly centered
216-
const containerLeft = containerBoundingBox.x
217-
const containerRight = containerLeft + containerBoundingBox.width
218-
const viewportCenter = viewport.width / 2
219-
const containerCenter = (containerLeft + containerRight) / 2
93+
for (const { width, height } of testBreakpoints.slice(0, 4)) {
94+
// Test smaller breakpoints
95+
await page.setViewportSize({ width, height })
22096

221-
// Allow some tolerance for centering
222-
expect(Math.abs(containerCenter - viewportCenter)).toBeLessThanOrEqual(
223-
20
224-
)
225-
}
97+
// Container should not exceed viewport width
98+
const bodyWidth = await page.evaluate(() => document.body.scrollWidth)
99+
expect(bodyWidth).toBeLessThanOrEqual(width + 1) // +1 for rounding
226100
}
227101
})
228102

229103
test('should handle theme toggle interactions across breakpoints', async ({
230104
page,
231105
}) => {
232-
for (const [, viewport] of Object.entries(breakpoints)) {
233-
await page.setViewportSize({
234-
width: viewport.width,
235-
height: viewport.height,
236-
})
237-
await page.waitForLoadState('networkidle')
238-
239-
// Find theme toggle buttons
240-
const themeToggle = page.locator('[data-testid="theme-toggle"]').first()
241-
const themeToggleAdvanced = page
242-
.locator('[data-testid="theme-toggle-advanced"]')
243-
.first()
244-
245-
// Test basic theme toggle
246-
if (await themeToggle.isVisible()) {
247-
await themeToggle.click()
248-
249-
// Wait for theme change by checking that toggle button is still responsive
250-
// and that the page has finished any theme transitions
251-
await page.waitForLoadState('networkidle')
252-
253-
// Simple verification that the theme system is working
254-
await expect(themeToggle).toBeVisible()
255-
256-
// Verify the toggle button has theme-aware icons (Sun/Moon) visible
257-
const sunIcon = themeToggle.locator('svg').first()
258-
await expect(sunIcon).toBeVisible()
259-
}
106+
await page.goto('/')
260107

261-
// Test advanced theme toggle
262-
if (await themeToggleAdvanced.isVisible()) {
263-
await themeToggleAdvanced.click()
108+
for (const { width, height } of [
109+
testBreakpoints[0], // Mobile
110+
testBreakpoints[3], // MD
111+
testBreakpoints[5], // XL
112+
]) {
113+
await page.setViewportSize({ width, height })
264114

265-
// Wait for any network activity to settle after theme change
266-
await page.waitForLoadState('networkidle')
115+
const themeToggle = page.getByTestId('theme-toggle-advanced')
116+
await expect(themeToggle).toBeVisible()
267117

268-
// Verify button is still visible and functional
269-
await expect(themeToggleAdvanced).toBeVisible()
118+
// Should be clickable at all screen sizes
119+
await themeToggle.click()
120+
await expect(themeToggle).toBeVisible()
270121

271-
// Verify button contains valid theme text (Light, Dark, or System)
272-
const buttonText = await themeToggleAdvanced.textContent()
273-
expect(buttonText).toMatch(/\b(Light|Dark|System)\b/)
274-
}
122+
// App should remain functional after theme change
123+
await expect(page.getByTestId('journal-entry-input')).toBeVisible()
124+
await expect(page.getByTestId('reflect-now-button')).toBeVisible()
275125
}
276126
})
277127
})

0 commit comments

Comments
 (0)