Files
klz-cables.com/lib/responsive-test.ts
2025-12-29 18:18:48 +01:00

326 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Responsive Testing Utilities for KLZ Cables
* Tools for testing and validating responsive design
*/
import { BREAKPOINTS, getViewport, Viewport } from './responsive';
// Test viewport configurations
export const TEST_VIEWPORTS = {
mobile: {
width: 375,
height: 667,
name: 'Mobile (iPhone SE)',
breakpoint: 'xs',
},
mobileLarge: {
width: 414,
height: 896,
name: 'Mobile Large (iPhone 11)',
breakpoint: 'sm',
},
tablet: {
width: 768,
height: 1024,
name: 'Tablet (iPad)',
breakpoint: 'md',
},
tabletLandscape: {
width: 1024,
height: 768,
name: 'Tablet Landscape',
breakpoint: 'lg',
},
desktop: {
width: 1280,
height: 800,
name: 'Desktop (Laptop)',
breakpoint: 'xl',
},
desktopLarge: {
width: 1440,
height: 900,
name: 'Desktop Large',
breakpoint: '2xl',
},
desktopWide: {
width: 1920,
height: 1080,
name: 'Desktop Wide (Full HD)',
breakpoint: '3xl',
},
};
/**
* Responsive Design Checklist
* Comprehensive checklist for validating responsive design
*/
export const RESPONSIVE_CHECKLIST = {
layout: [
'Content stacks properly on mobile (1 column)',
'Grid layouts adapt to screen size (2-4 columns)',
'No horizontal scrolling at any breakpoint',
'Content remains within safe areas',
'Padding and margins scale appropriately',
],
typography: [
'Text remains readable at all sizes',
'Line height is optimized for mobile',
'Headings scale appropriately',
'No text overflow or clipping',
'Font size meets WCAG guidelines (16px minimum)',
],
navigation: [
'Mobile menu is accessible (44px touch targets)',
'Desktop navigation hides on mobile',
'Menu items are properly spaced',
'Active states are visible',
'Back/forward navigation works',
],
images: [
'Images load with appropriate sizes',
'Aspect ratios are maintained',
'No layout shift during loading',
'Lazy loading works correctly',
'Placeholder blur is applied',
],
forms: [
'Input fields are 44px minimum touch target',
'Labels remain visible',
'Error messages are readable',
'Form submits on mobile',
'Keyboard navigation works',
],
performance: [
'Images are properly sized for viewport',
'No unnecessary large assets on mobile',
'Critical CSS is loaded',
'Touch interactions are smooth',
'No layout thrashing',
],
accessibility: [
'Touch targets are 44px minimum',
'Focus indicators are visible',
'Screen readers work correctly',
'Color contrast meets WCAG AA',
'Zoom is not restricted',
],
};
/**
* Generate responsive design report
*/
export function generateResponsiveReport(): string {
const viewport = getViewport();
const report = `
Responsive Design Report - KLZ Cables
=====================================
Current Viewport:
- Width: ${viewport.width}px
- Height: ${viewport.height}px
- Breakpoint: ${viewport.breakpoint}
- Device Type: ${viewport.isMobile ? 'Mobile' : viewport.isTablet ? 'Tablet' : 'Desktop'}
Breakpoint Configuration:
- xs: ${BREAKPOINTS.xs}px
- sm: ${BREAKPOINTS.sm}px
- md: ${BREAKPOINTS.md}px
- lg: ${BREAKPOINTS.lg}px
- xl: ${BREAKPOINTS.xl}px
- 2xl: ${BREAKPOINTS['2xl']}px
- 3xl: ${BREAKPOINTS['3xl']}px
Touch Target Verification:
- Minimum: 44px × 44px
- Recommended: 48px × 48px
- Large: 56px × 56px
Image Optimization:
- Mobile Quality: 75%
- Tablet Quality: 85%
- Desktop Quality: 90%
Typography Scale:
- Fluid typography using CSS clamp()
- Mobile: 16px base
- Desktop: 18px base
- Line height: 1.4-1.6
Generated: ${new Date().toISOString()}
`.trim();
return report;
}
/**
* Validate responsive design rules
*/
export function validateResponsiveDesign(): {
passed: boolean;
warnings: string[];
errors: string[];
} {
const warnings: string[] = [];
const errors: string[] = [];
// Check viewport
if (typeof window === 'undefined') {
warnings.push('Server-side rendering detected - some checks skipped');
}
// Check minimum touch target size
const buttons = document.querySelectorAll('button, a');
buttons.forEach((el) => {
const rect = el.getBoundingClientRect();
if (rect.width < 44 || rect.height < 44) {
warnings.push(`Element ${el.tagName} has touch target < 44px`);
}
});
// Check for horizontal scroll
if (document.body.scrollWidth > window.innerWidth) {
errors.push('Horizontal scrolling detected');
}
// Check text size
const textElements = document.querySelectorAll('p, span, div');
textElements.forEach((el) => {
const computed = window.getComputedStyle(el);
const fontSize = parseFloat(computed.fontSize);
if (fontSize < 16 && el.textContent && el.textContent.length > 50) {
warnings.push(`Text element ${el.tagName} has font-size < 16px`);
}
});
return {
passed: errors.length === 0,
warnings,
errors,
};
}
/**
* Responsive design utilities for testing
*/
export const ResponsiveTestUtils = {
// Set viewport for testing
setViewport: (width: number, height: number) => {
if (typeof window !== 'undefined') {
window.innerWidth = width;
window.innerHeight = height;
window.dispatchEvent(new Event('resize'));
}
},
// Simulate mobile viewport
simulateMobile: () => {
ResponsiveTestUtils.setViewport(375, 667);
},
// Simulate tablet viewport
simulateTablet: () => {
ResponsiveTestUtils.setViewport(768, 1024);
},
// Simulate desktop viewport
simulateDesktop: () => {
ResponsiveTestUtils.setViewport(1280, 800);
},
// Check if element is in viewport
isElementInViewport: (element: HTMLElement, offset = 0): boolean => {
const rect = element.getBoundingClientRect();
return (
rect.top >= -offset &&
rect.left >= -offset &&
rect.bottom <= (window.innerHeight + offset) &&
rect.right <= (window.innerWidth + offset)
);
},
// Measure touch target size
measureTouchTarget: (element: HTMLElement): { width: number; height: number; valid: boolean } => {
const rect = element.getBoundingClientRect();
return {
width: rect.width,
height: rect.height,
valid: rect.width >= 44 && rect.height >= 44,
};
},
// Check text readability
checkTextReadability: (element: HTMLElement): { fontSize: number; lineHeight: number; valid: boolean } => {
const computed = window.getComputedStyle(element);
const fontSize = parseFloat(computed.fontSize);
const lineHeight = parseFloat(computed.lineHeight);
return {
fontSize,
lineHeight,
valid: fontSize >= 16 && lineHeight >= 1.4,
};
},
// Generate responsive test report
generateTestReport: () => {
const viewport = getViewport();
const validation = validateResponsiveDesign();
return {
viewport,
validation,
timestamp: new Date().toISOString(),
};
},
};
/**
* Responsive design patterns for common scenarios
*/
export const RESPONSIVE_PATTERNS = {
// Mobile-first card grid
cardGrid: {
mobile: { columns: 1, gap: '1rem' },
tablet: { columns: 2, gap: '1.5rem' },
desktop: { columns: 3, gap: '2rem' },
},
// Hero section
hero: {
mobile: { layout: 'stacked', padding: '2rem 1rem' },
tablet: { layout: 'split', padding: '3rem 2rem' },
desktop: { layout: 'split', padding: '4rem 3rem' },
},
// Form layout
form: {
mobile: { columns: 1, fieldWidth: '100%' },
tablet: { columns: 2, fieldWidth: '48%' },
desktop: { columns: 2, fieldWidth: '48%' },
},
// Navigation
navigation: {
mobile: { type: 'hamburger', itemsPerScreen: 6 },
tablet: { type: 'hybrid', itemsPerScreen: 8 },
desktop: { type: 'full', itemsPerScreen: 12 },
},
// Image gallery
gallery: {
mobile: { columns: 1, aspectRatio: '4:3' },
tablet: { columns: 2, aspectRatio: '1:1' },
desktop: { columns: 3, aspectRatio: '16:9' },
},
};
export default {
TEST_VIEWPORTS,
RESPONSIVE_CHECKLIST,
generateResponsiveReport,
validateResponsiveDesign,
ResponsiveTestUtils,
RESPONSIVE_PATTERNS,
};