feat(home): replace horizontal carousel with vertical scroll-snap stack
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
For multi-frame setups, switch from side-swipe carousel + dot indicators to a vertical scroll-snap stack of full-size cards. Each frame gets its own page-height slide; flicking up/down moves between frames with the same snap-stop feel as the horizontal version. Removes ~30 lines of carousel scroll-tracking JS and the dot navigation. Single-frame layout unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -106,8 +106,8 @@ describe('HomeView', () => {
|
||||
})
|
||||
}
|
||||
|
||||
// HV-01: N devices renders a carousel of N large FrameCard stubs + N dots
|
||||
it('renders one FrameCard per device in a carousel when multiple devices present', async () => {
|
||||
// HV-01: N devices renders a vertical stack of N large FrameCard stubs
|
||||
it('renders one FrameCard per device in a vertical stack when multiple devices present', async () => {
|
||||
const devicesStore = useDevicesStore()
|
||||
devicesStore.devices = [
|
||||
makeDevice({ id: 1, name: 'Frame A' }),
|
||||
@@ -119,58 +119,27 @@ describe('HomeView', () => {
|
||||
const wrapper = mountView()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.find('.home-view__carousel').exists()).toBe(true)
|
||||
expect(wrapper.find('.home-view__stack').exists()).toBe(true)
|
||||
expect(wrapper.findAll('.home-view__slide')).toHaveLength(3)
|
||||
expect(wrapper.findAll('.frame-card-stub')).toHaveLength(3)
|
||||
// All cards should be the large variant (no more compact stack)
|
||||
// All cards should be the large variant (no compact / no carousel)
|
||||
const cards = wrapper.findAllComponents({ name: 'FrameCard' })
|
||||
for (const c of cards) expect(c.props('size')).toBe('large')
|
||||
// One navigation dot per device
|
||||
expect(wrapper.findAll('.home-view__dot')).toHaveLength(3)
|
||||
})
|
||||
|
||||
it('marks the first dot active by default and updates active dot on scroll', async () => {
|
||||
it('labels each slide with the device name for accessibility', async () => {
|
||||
const devicesStore = useDevicesStore()
|
||||
devicesStore.devices = [
|
||||
makeDevice({ id: 1, name: 'A' }),
|
||||
makeDevice({ id: 2, name: 'B' }),
|
||||
makeDevice({ id: 1, name: 'Living Room' }),
|
||||
makeDevice({ id: 2, name: 'Bedroom' }),
|
||||
]
|
||||
vi.spyOn(devicesStore, 'fetchDevices').mockResolvedValue()
|
||||
|
||||
const wrapper = mountView()
|
||||
await wrapper.vm.$nextTick()
|
||||
let dots = wrapper.findAll('.home-view__dot')
|
||||
expect(dots[0].classes()).toContain('home-view__dot--active')
|
||||
expect(dots[1].classes()).not.toContain('home-view__dot--active')
|
||||
|
||||
// Simulate the carousel having scrolled to the second slide
|
||||
const carousel = wrapper.find('.home-view__carousel').element as HTMLElement
|
||||
Object.defineProperty(carousel, 'clientWidth', { configurable: true, value: 360 })
|
||||
Object.defineProperty(carousel, 'scrollLeft', { configurable: true, value: 360 })
|
||||
await wrapper.find('.home-view__carousel').trigger('scroll')
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
dots = wrapper.findAll('.home-view__dot')
|
||||
expect(dots[1].classes()).toContain('home-view__dot--active')
|
||||
expect(dots[0].classes()).not.toContain('home-view__dot--active')
|
||||
})
|
||||
|
||||
it('clicking a dot scrolls the carousel to that slide', async () => {
|
||||
const devicesStore = useDevicesStore()
|
||||
devicesStore.devices = [makeDevice({ id: 1 }), makeDevice({ id: 2 }), makeDevice({ id: 3 })]
|
||||
vi.spyOn(devicesStore, 'fetchDevices').mockResolvedValue()
|
||||
|
||||
const wrapper = mountView()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
const carousel = wrapper.find('.home-view__carousel').element as HTMLElement
|
||||
Object.defineProperty(carousel, 'clientWidth', { configurable: true, value: 360 })
|
||||
const scrollToSpy = vi.fn()
|
||||
;(carousel as any).scrollTo = scrollToSpy
|
||||
|
||||
await wrapper.findAll('.home-view__dot')[2].trigger('click')
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(scrollToSpy).toHaveBeenCalledWith(expect.objectContaining({ left: 720, behavior: 'smooth' }))
|
||||
const slides = wrapper.findAll('.home-view__slide')
|
||||
expect(slides[0].attributes('aria-label')).toBe('Living Room')
|
||||
expect(slides[1].attributes('aria-label')).toBe('Bedroom')
|
||||
})
|
||||
|
||||
// HV-01b: single device still renders one FrameCard (large variant branch)
|
||||
|
||||
Reference in New Issue
Block a user