Add mobile accessibility and UI/UX improvements

- Add mobile card view for packages (replaces table on small screens)
- Implement design tokens system for consistent styling
- Add dark/light theme toggle with system preference support
- Create reusable StatusBadge and EmptyState components
- Add accessible form labels to package filters
- Add compact mobile stats display in navigation
- Add safe area insets for notched devices
- Add reduced motion support for accessibility
- Improve touch targets for WCAG compliance
- Add unit tests for composables
- Update Vuetify configuration and styling
This commit is contained in:
2025-11-26 16:46:02 +01:00
parent e384635da5
commit 5fac66a38c
34 changed files with 5635 additions and 607 deletions

View File

@@ -0,0 +1,105 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { useAutoRefresh, useNowTimer } from '@/composables/useAutoRefresh'
// Mock Vue's onUnmounted
vi.mock('vue', async () => {
const actual = await vi.importActual('vue')
return {
...actual,
onUnmounted: vi.fn(),
}
})
describe('useAutoRefresh', () => {
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
})
describe('useAutoRefresh', () => {
it('should call callback at specified interval', () => {
const callback = vi.fn()
const { start } = useAutoRefresh(callback, 1000)
start()
expect(callback).not.toHaveBeenCalled()
vi.advanceTimersByTime(1000)
expect(callback).toHaveBeenCalledTimes(1)
vi.advanceTimersByTime(1000)
expect(callback).toHaveBeenCalledTimes(2)
})
it('should stop calling callback after stop()', () => {
const callback = vi.fn()
const { start, stop } = useAutoRefresh(callback, 1000)
start()
vi.advanceTimersByTime(1000)
expect(callback).toHaveBeenCalledTimes(1)
stop()
vi.advanceTimersByTime(2000)
expect(callback).toHaveBeenCalledTimes(1) // Still 1
})
it('should track running state', () => {
const callback = vi.fn()
const { start, stop, isRunning } = useAutoRefresh(callback, 1000)
expect(isRunning.value).toBe(false)
start()
expect(isRunning.value).toBe(true)
stop()
expect(isRunning.value).toBe(false)
})
it('should restart interval on restart()', () => {
const callback = vi.fn()
const { start, restart } = useAutoRefresh(callback, 1000)
start()
vi.advanceTimersByTime(500)
restart()
vi.advanceTimersByTime(500)
expect(callback).not.toHaveBeenCalled() // Timer was reset
vi.advanceTimersByTime(500)
expect(callback).toHaveBeenCalledTimes(1)
})
})
describe('useNowTimer', () => {
it('should update now value every second', () => {
const { now, start } = useNowTimer()
const initialValue = now.value
start()
vi.advanceTimersByTime(1000)
expect(now.value).toBeGreaterThan(initialValue)
vi.advanceTimersByTime(1000)
expect(now.value).toBeGreaterThan(initialValue + 1)
})
it('should stop updating after stop()', () => {
const { now, start, stop } = useNowTimer()
start()
vi.advanceTimersByTime(1000)
const valueAfterStart = now.value
stop()
vi.advanceTimersByTime(2000)
expect(now.value).toBe(valueAfterStart)
})
})
})