From 0cf6b17f7be943830631964e58593275210341f2 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 2 Oct 2026 00:31:11 +0200 Subject: [PATCH] feat(PageList): add a "collapse all pages" action Signed-off-by: Jonas Assisted-by: ClaudeCode:claude-opus-5.5 --- cypress/e2e/page-list.spec.js | 8 ++--- playwright/e2e/page-list.spec.ts | 22 ++++++++++++ .../support/sections/PageListSection.ts | 5 +++ src/components/PageList.vue | 36 ++++++++++++++----- src/stores/pages.js | 10 ++++++ 5 files changed, 69 insertions(+), 12 deletions(-) diff --git a/cypress/e2e/page-list.spec.js b/cypress/e2e/page-list.spec.js index 2919f9512..5c4215bbe 100644 --- a/cypress/e2e/page-list.spec.js +++ b/cypress/e2e/page-list.spec.js @@ -43,7 +43,7 @@ describe('Page list', function() { }) it('can sort pages by title/timestamp and sort order is persistent', function() { // Select sorting by title - cy.get('span.sort-ascending-icon').click() + cy.get('button[aria-label="Page list options"]').click() cy.get('.sort-alphabetical-ascending-icon').click() cy.get('.page-list-item').last() .should('contain', 'Target') @@ -56,15 +56,15 @@ describe('Page list', function() { .should('contain', 'Target') // Select sorting by timestamp - cy.get('span.sort-alphabetical-ascending-icon').click() + cy.get('button[aria-label="Page list options"]').click() cy.get('button.action-button > span.sort-clock-ascending-outline-icon').click() cy.get('.page-list-item').last() .should('contain', 'Target') // Remove alternative sort order cy.get('div.sort-order-chip > button').click() - cy.get('span.sort-ascending-icon') - .should('be.visible') + cy.get('div.sort-order-chip') + .should('not.exist') cy.get('.page-list-item').eq(0) .should('contain', '#% special chars') cy.get('.page-list-item').last() diff --git a/playwright/e2e/page-list.spec.ts b/playwright/e2e/page-list.spec.ts index 8aac07978..09ace6768 100644 --- a/playwright/e2e/page-list.spec.ts +++ b/playwright/e2e/page-list.spec.ts @@ -140,3 +140,25 @@ test.describe('Page list drag and drop', () => { await editor.hasCollectiveLink(page1.data.title) }) }) + +test.describe('Page list collapse all', () => { + test('Collapse all pages but keep current page visible', async ({ page, user, collective, pageList }) => { + const parent = await collective.createPage({ title: 'Parent', user, page }) + const child = await collective.createPage({ title: 'Child', parentId: parent.data.id, user, page }) + await collective.createPage({ title: 'Grandchild', parentId: child.data.id, user, page }) + const other = await collective.createPage({ title: 'Other', user, page }) + await collective.createPage({ title: 'Other child', parentId: other.data.id, user, page }) + + await child.open() + await expect(pageList.getPageItem('Grandchild')).toBeVisible() + await pageList.toggleExpandPage('Other') + await expect(pageList.getPageItem('Other child')).toBeVisible() + + await pageList.collapseAll() + + // Parent of current page stays expanded, everything else collapses + await expect(pageList.getPageItem('Child')).toBeVisible() + await expect(pageList.getPageItem('Grandchild')).toHaveCount(0) + await expect(pageList.getPageItem('Other child')).toHaveCount(0) + }) +}) diff --git a/playwright/support/sections/PageListSection.ts b/playwright/support/sections/PageListSection.ts index 3d830dcd6..d6e0348ba 100644 --- a/playwright/support/sections/PageListSection.ts +++ b/playwright/support/sections/PageListSection.ts @@ -39,6 +39,11 @@ export class PageListSection { .click() } + public async collapseAll(): Promise { + await this.el.getByRole('button', { name: 'Page list options' }).click() + await this.page.getByRole('menuitem', { name: 'Collapse all pages' }).click() + } + public async addPage(parentTitle: string): Promise { const item = this.getPageItem(parentTitle) // The add-subpage button is only shown on hover diff --git a/src/components/PageList.vue b/src/components/PageList.vue index 1f282c634..8bd61bc58 100644 --- a/src/components/PageList.vue +++ b/src/components/PageList.vue @@ -47,13 +47,9 @@ + :aria-label="t('collectives', 'Page list options')"> {{ sortedBy('byTitleAsc') ? t('collectives', 'Sort descending by title') : t('collectives', 'Sort ascending by title') }} + { + scrollToPage(this.currentPageId) + }) + }, + updateScrollerHeights() { const [filteredPagesScrollerMaxHeight, contentFilteredPagesScrollerMaxHeight] = this.scrollerMaxHeights this.$nextTick(() => { diff --git a/src/stores/pages.js b/src/stores/pages.js index d60068f9b..3b36d651a 100644 --- a/src/stores/pages.js +++ b/src/stores/pages.js @@ -550,6 +550,16 @@ export const usePagesStore = defineStore('pages', { } }, + // Collapse all pages of current collective except parents of current page + collapseAll() { + for (const page of this.pages) { + delete this.collapsed[page.id] + } + if (this.currentPage) { + this.expandParents(this.currentPage.parentId) + } + }, + setNewPageParentId(id) { this.newPageParentId = id },