/** * Simple accessibility validation script for asset management components * This script checks for common accessibility patterns in our components */ import fs from 'fs'; import path from 'path'; interface AccessibilityCheck { name: string; pattern: RegExp; description: string; required: boolean; } const accessibilityChecks: AccessibilityCheck[] = [ { name: 'ARIA Labels', pattern: /aria-label(ledby)?=/g, description: 'Components should have proper ARIA labels', required: true }, { name: 'Screen Reader Text', pattern: /sr-only|screen-reader/g, description: 'Important information should be available to screen readers', required: true }, { name: 'Keyboard Navigation', pattern: /onKeyDown|tabIndex|role="button"/g, description: 'Interactive elements should support keyboard navigation', required: true }, { name: 'Live Regions', pattern: /aria-live|aria-atomic/g, description: 'Dynamic content should announce changes to screen readers', required: true }, { name: 'Focus Management', pattern: /focus\(\)|useRef.*focus|Focus/g, description: 'Components should manage focus appropriately', required: false }, { name: 'Semantic HTML', pattern: /role="(navigation|dialog|button|searchbox|tablist|tab|tabpanel)"/g, description: 'Components should use semantic HTML and ARIA roles', required: true } ]; const responsiveChecks: AccessibilityCheck[] = [ { name: 'Mobile Breakpoints', pattern: /(sm:|md:|lg:|xl:)/g, description: 'Components should use responsive breakpoints', required: true }, { name: 'Flexible Layouts', pattern: /(flex|grid|w-full|min-w-0)/g, description: 'Components should use flexible layout patterns', required: true }, { name: 'Touch Targets', pattern: /(h-8|w-8|h-10|w-10|h-12|w-12|min-h-|min-w-)/g, description: 'Interactive elements should have adequate touch target sizes', required: true }, { name: 'Text Wrapping', pattern: /(break-words|truncate|text-wrap)/g, description: 'Text should wrap appropriately on small screens', required: false } ]; function analyzeFile(filePath: string): { accessibility: number; responsive: number; issues: string[] } { const content = fs.readFileSync(filePath, 'utf-8'); const issues: string[] = []; let accessibilityScore = 0; let responsiveScore = 0; console.log(`\nšŸ“ Analyzing: ${path.relative(process.cwd(), filePath)}`); // Check accessibility patterns console.log('\nšŸ” Accessibility Checks:'); accessibilityChecks.forEach(check => { const matches = content.match(check.pattern); const count = matches ? matches.length : 0; if (count > 0) { console.log(` āœ… ${check.name}: ${count} instances found`); accessibilityScore += check.required ? 2 : 1; } else { const severity = check.required ? 'āŒ' : 'āš ļø'; console.log(` ${severity} ${check.name}: Not found`); if (check.required) { issues.push(`Missing ${check.name}: ${check.description}`); } } }); // Check responsive patterns console.log('\nšŸ“± Responsive Design Checks:'); responsiveChecks.forEach(check => { const matches = content.match(check.pattern); const count = matches ? matches.length : 0; if (count > 0) { console.log(` āœ… ${check.name}: ${count} instances found`); responsiveScore += check.required ? 2 : 1; } else { const severity = check.required ? 'āŒ' : 'āš ļø'; console.log(` ${severity} ${check.name}: Not found`); if (check.required) { issues.push(`Missing ${check.name}: ${check.description}`); } } }); return { accessibility: accessibilityScore, responsive: responsiveScore, issues }; } function validateAccessibility() { console.log('šŸš€ Asset Management Accessibility Validation\n'); console.log('=' .repeat(60)); const componentDir = path.join(process.cwd(), 'src/components/asset-management'); const files = [ 'AssetList.tsx', 'AssetForm.tsx', 'AssetDetail.tsx', 'shared/ConfirmDialog.tsx', 'shared/Pagination.tsx' ]; let totalAccessibilityScore = 0; let totalResponsiveScore = 0; let totalIssues: string[] = []; let filesAnalyzed = 0; files.forEach(file => { const filePath = path.join(componentDir, file); if (fs.existsSync(filePath)) { const result = analyzeFile(filePath); totalAccessibilityScore += result.accessibility; totalResponsiveScore += result.responsive; totalIssues.push(...result.issues); filesAnalyzed++; } else { console.log(`āš ļø File not found: ${file}`); } }); // Calculate scores const maxAccessibilityScore = filesAnalyzed * accessibilityChecks.filter(c => c.required).length * 2; const maxResponsiveScore = filesAnalyzed * responsiveChecks.filter(c => c.required).length * 2; const accessibilityPercentage = Math.round((totalAccessibilityScore / maxAccessibilityScore) * 100); const responsivePercentage = Math.round((totalResponsiveScore / maxResponsiveScore) * 100); // Print summary console.log('\n' + '=' .repeat(60)); console.log('šŸ“Š SUMMARY REPORT'); console.log('=' .repeat(60)); console.log(`\nšŸ“ˆ Accessibility Score: ${accessibilityPercentage}% (${totalAccessibilityScore}/${maxAccessibilityScore})`); console.log(`šŸ“± Responsive Score: ${responsivePercentage}% (${totalResponsiveScore}/${maxResponsiveScore})`); console.log(`šŸ“ Files Analyzed: ${filesAnalyzed}`); console.log(`āš ļø Total Issues: ${totalIssues.length}`); if (totalIssues.length > 0) { console.log('\nšŸ”§ Issues to Address:'); totalIssues.forEach((issue, index) => { console.log(` ${index + 1}. ${issue}`); }); } // Grade the implementation const overallScore = (accessibilityPercentage + responsivePercentage) / 2; let grade = 'F'; let emoji = 'āŒ'; if (overallScore >= 90) { grade = 'A'; emoji = 'šŸŽ‰'; } else if (overallScore >= 80) { grade = 'B'; emoji = 'āœ…'; } else if (overallScore >= 70) { grade = 'C'; emoji = 'āš ļø'; } else if (overallScore >= 60) { grade = 'D'; emoji = 'šŸ”¶'; } console.log(`\n${emoji} Overall Grade: ${grade} (${Math.round(overallScore)}%)`); if (overallScore >= 80) { console.log('\nšŸŽŠ Great job! Your components have excellent accessibility and responsive design.'); } else if (overallScore >= 60) { console.log('\nšŸ‘ Good progress! Consider addressing the issues above to improve accessibility.'); } else { console.log('\nšŸ”§ More work needed. Focus on the required accessibility patterns.'); } console.log('\n' + '=' .repeat(60)); } // Run the validation validateAccessibility();