mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
fix(DisplayCarousel): use back button in grid view and remove hover icons (#10655)
## Summary - Grid view top-left icon changed from square to back arrow (`arrow-left`) per Figma spec - Back button is always visible in grid view (no longer hover-dependent), uses sticky positioning - Removed hover opacity effect on grid thumbnails ## Related - Figma: https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=6008-83034&m=dev - Figma: https://www.figma.com/design/vALUV83vIdBzEsTJAhQgXq/Comfy-Design-System?node-id=6008-83069&m=dev ## Test plan - [x] All 31 existing DisplayCarousel tests pass - [ ] Visual check: grid view shows back arrow icon (top-left, always visible) - [ ] Visual check: hovering grid thumbnails shows no overlay icons - [ ] Verify back button stays visible when scrolling through many grid items ## Screenshot ### Before <img width="492" height="364" alt="스크린샷 2026-03-28 오후 4 31 54" src="https://github.com/user-attachments/assets/f9f36521-e993-45de-b692-59fba22a026d" /> <img width="457" height="400" alt="스크린샷 2026-03-28 오후 4 32 03" src="https://github.com/user-attachments/assets/004f6380-8ad7-4167-b1f4-ebc4bdb559cc" /> ### After <img width="596" height="388" alt="스크린샷 2026-03-28 오후 4 31 43" src="https://github.com/user-attachments/assets/e5585887-ad36-42e3-a6c0-e6eacb90dad7" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10655-fix-DisplayCarousel-use-back-button-in-grid-view-and-remove-hover-icons-3316d73d365081c5826afd63c50994ba) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -341,7 +341,7 @@ describe('DisplayCarousel Grid Mode', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('switches back to single mode via toggle button', async () => {
|
||||
it('grid mode has no overlay icons', async () => {
|
||||
const wrapper = createGalleriaWrapper([...TEST_IMAGES_SMALL])
|
||||
|
||||
// Switch to grid via focus on image container
|
||||
@@ -350,19 +350,69 @@ describe('DisplayCarousel Grid Mode', () => {
|
||||
await wrapper.find('[aria-label="Switch to grid view"]').trigger('click')
|
||||
await nextTick()
|
||||
|
||||
// Focus the grid container to reveal toggle
|
||||
// Grid mode should have no toggle/back button
|
||||
expect(wrapper.find('[aria-label="Switch to single view"]').exists()).toBe(
|
||||
false
|
||||
)
|
||||
expect(wrapper.find('[aria-label="Switch to grid view"]').exists()).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('always uses undo-2 icon for grid toggle button', async () => {
|
||||
const wrapper = createGalleriaWrapper([...TEST_IMAGES_SMALL])
|
||||
|
||||
// Show controls
|
||||
await findImageContainer(wrapper).trigger('focusin')
|
||||
await nextTick()
|
||||
|
||||
// Switch back to single
|
||||
const singleToggle = wrapper.find('[aria-label="Switch to single view"]')
|
||||
expect(singleToggle.exists()).toBe(true)
|
||||
const toggleBtn = wrapper.find('[aria-label="Switch to grid view"]')
|
||||
expect(toggleBtn.find('i').classes()).toContain('icon-[lucide--undo-2]')
|
||||
|
||||
await singleToggle.trigger('click')
|
||||
// Switch to grid and back
|
||||
await toggleBtn.trigger('click')
|
||||
await nextTick()
|
||||
|
||||
// Should be back in single mode with main image
|
||||
expect(wrapper.find('[aria-label="Previous image"]').exists()).toBe(true)
|
||||
const gridButtons = wrapper
|
||||
.findAll('button')
|
||||
.filter((btn) => btn.find('img').exists())
|
||||
await gridButtons[0].trigger('click')
|
||||
await nextTick()
|
||||
|
||||
await findImageContainer(wrapper).trigger('focusin')
|
||||
await nextTick()
|
||||
|
||||
// Icon should still be undo-2
|
||||
const toggleBtnAfter = wrapper.find('[aria-label="Switch to grid view"]')
|
||||
expect(toggleBtnAfter.find('i').classes()).toContain(
|
||||
'icon-[lucide--undo-2]'
|
||||
)
|
||||
})
|
||||
|
||||
it('shows grid button in single mode after selecting from grid', async () => {
|
||||
const wrapper = createGalleriaWrapper([...TEST_IMAGES_SMALL])
|
||||
|
||||
// Switch to grid
|
||||
await findImageContainer(wrapper).trigger('focusin')
|
||||
await nextTick()
|
||||
await wrapper.find('[aria-label="Switch to grid view"]').trigger('click')
|
||||
await nextTick()
|
||||
|
||||
// Click first grid image to go back to single mode
|
||||
const gridButtons = wrapper
|
||||
.findAll('button')
|
||||
.filter((btn) => btn.find('img').exists())
|
||||
await gridButtons[0].trigger('click')
|
||||
await nextTick()
|
||||
|
||||
// Hover to reveal controls
|
||||
await findImageContainer(wrapper).trigger('focusin')
|
||||
await nextTick()
|
||||
|
||||
// Should still show grid view button (same icon always)
|
||||
expect(wrapper.find('[aria-label="Switch to grid view"]').exists()).toBe(
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('clicking grid image switches to single mode focused on that image', async () => {
|
||||
@@ -404,8 +454,8 @@ describe('DisplayCarousel Grid Mode', () => {
|
||||
await wrapper.setProps({ modelValue: [TEST_IMAGES_SMALL[0]] })
|
||||
await nextTick()
|
||||
|
||||
// Should revert to single mode (no grid toggle visible)
|
||||
expect(wrapper.find('[aria-label="Switch to single view"]').exists()).toBe(
|
||||
// Should revert to single mode (single image, no grid button)
|
||||
expect(wrapper.find('[aria-label="Switch to grid view"]').exists()).toBe(
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
:aria-label="t('g.switchToGridView')"
|
||||
@click="switchToGrid"
|
||||
>
|
||||
<i class="icon-[lucide--layout-grid] size-4" />
|
||||
<i class="icon-[lucide--undo-2] size-4" />
|
||||
</button>
|
||||
|
||||
<!-- Action Buttons (hover, top-right) -->
|
||||
@@ -142,41 +142,19 @@
|
||||
ref="gridContainerEl"
|
||||
class="relative h-72 overflow-x-hidden overflow-y-auto rounded-sm bg-component-node-background"
|
||||
tabindex="0"
|
||||
@mouseenter="isHovered = true"
|
||||
@mouseleave="isHovered = false"
|
||||
@focusin="isFocused = true"
|
||||
@focusout="handleFocusOut"
|
||||
>
|
||||
<!-- Toggle to Single (hover, top-left) -->
|
||||
<button
|
||||
v-if="showControls"
|
||||
:class="toggleButtonClass"
|
||||
class="absolute top-2 left-2 z-10"
|
||||
:aria-label="t('g.switchToSingleView')"
|
||||
@click="switchToSingle"
|
||||
>
|
||||
<i class="icon-[lucide--square] size-4" />
|
||||
</button>
|
||||
|
||||
<div class="flex flex-wrap content-start gap-1">
|
||||
<button
|
||||
v-for="(item, index) in galleryImages"
|
||||
:key="getItemSrc(item)"
|
||||
class="size-14 shrink-0 cursor-pointer overflow-hidden border-0 p-0"
|
||||
:aria-label="getItemAlt(item, index)"
|
||||
@mouseenter="hoveredGridIndex = index"
|
||||
@mouseleave="hoveredGridIndex = -1"
|
||||
@click="selectFromGrid(index)"
|
||||
>
|
||||
<img
|
||||
:src="getItemThumbnail(item)"
|
||||
:alt="getItemAlt(item, index)"
|
||||
:class="
|
||||
cn(
|
||||
'size-full object-cover transition-opacity',
|
||||
hoveredGridIndex === index && 'opacity-50'
|
||||
)
|
||||
"
|
||||
class="size-full object-cover"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
@@ -229,7 +207,6 @@ const activeIndex = ref(0)
|
||||
const displayMode = ref<DisplayMode>('single')
|
||||
const isHovered = ref(false)
|
||||
const isFocused = ref(false)
|
||||
const hoveredGridIndex = ref(-1)
|
||||
const imageDimensions = ref<string | null>(null)
|
||||
const thumbnailRefs = ref<(HTMLElement | null)[]>([])
|
||||
const imageContainerEl = ref<HTMLDivElement>()
|
||||
@@ -359,11 +336,6 @@ function switchToGrid() {
|
||||
displayMode.value = 'grid'
|
||||
}
|
||||
|
||||
function switchToSingle() {
|
||||
isHovered.value = false
|
||||
displayMode.value = 'single'
|
||||
}
|
||||
|
||||
function selectFromGrid(index: number) {
|
||||
activeIndex.value = index
|
||||
imageDimensions.value = null
|
||||
|
||||
Reference in New Issue
Block a user