diff --git a/cypress/component/contextLayout.cy.js b/cypress/component/contextLayout.cy.js new file mode 100644 index 0000000000..40d5d07d0a --- /dev/null +++ b/cypress/component/contextLayout.cy.js @@ -0,0 +1,39 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { getGridCellStyle, loadContextLayout, saveContextLayout } from '../../src/shared/utils/contextLayout.js' + +describe('application layout', () => { + beforeEach(() => { + window.localStorage.clear() + }) + + it('keeps a saved layout per application', () => { + saveContextLayout(7, { 4: 6, 'view-9': 4 }) + + expect(loadContextLayout(7)).to.deep.equal({ 4: 6, 'view-9': 4 }) + expect(loadContextLayout(8)).to.deep.equal({}) + }) + + it('resets to the default when an empty layout is saved', () => { + saveContextLayout(7, { 4: 6 }) + saveContextLayout(7, {}) + + expect(loadContextLayout(7)).to.deep.equal({}) + }) + + it('drops widths it does not know and survives a broken entry', () => { + saveContextLayout(7, { 4: 6, 5: 5, 6: '12' }) + expect(loadContextLayout(7)).to.deep.equal({ 4: 6 }) + + const key = Object.keys(window.localStorage).find(name => name.endsWith(':7')) + window.localStorage.setItem(key, '{not json') + expect(loadContextLayout(7)).to.deep.equal({}) + }) + + it('narrows a wide item to the medium grid', () => { + expect(getGridCellStyle(8)).to.deep.equal({ '--context-grid-span': 8, '--context-grid-span-medium': 6 }) + expect(getGridCellStyle(4)).to.deep.equal({ '--context-grid-span': 4, '--context-grid-span-medium': 4 }) + }) +}) diff --git a/playwright/e2e/application.spec.ts b/playwright/e2e/application.spec.ts index e9c20012ad..ededcfa964 100644 --- a/playwright/e2e/application.spec.ts +++ b/playwright/e2e/application.spec.ts @@ -108,6 +108,35 @@ test.describe('Applications', () => { await expect(page.locator('[data-cy="context-title"]')).toContainText('listed application') }) + test('A width chosen for a table in an application opened on its own is kept after a reload', async ({ userPage: { page, user } }) => { + const tableResponse = await ocsRequest(page.request, user, { + method: 'POST', + url: '/ocs/v2.php/apps/tables/api/2/tables?format=json', + data: { title: 'Layout table', template: 'custom' }, + }) + const table = (await tableResponse.json()).ocs.data + const contextResponse = await ocsRequest(page.request, user, { + method: 'POST', + url: '/ocs/v2.php/apps/tables/api/2/contexts?format=json', + data: { name: 'layout application', iconName: 'star', nodes: [{ id: table.id, type: 0, permissions: 1 }] }, + }) + const context = (await contextResponse.json()).ocs.data + + await page.goto(`/index.php/apps/tables/app/${context.id}`) + const item = page.locator('[data-cy="context-grid-item"]') + await expect(item).toHaveCount(1, { timeout: 10000 }) + + await page.locator('[data-cy="context-layout-edit"]').click() + // The width menu is the only menu in the card's toolbar while the layout is edited + await item.locator('.context__grid-item-toolbar').getByRole('button').first().click() + await page.getByRole('menuitemradio', { name: 'Half' }).or(page.getByRole('radio', { name: 'Half' })).first().click() + await page.locator('[data-cy="context-layout-save"]').click() + await expect(item).toHaveAttribute('style', /--context-grid-span: 6/) + + await page.reload() + await expect(page.locator('[data-cy="context-grid-item"]')).toHaveAttribute('style', /--context-grid-span: 6/, { timeout: 10000 }) + }) + test('A page with widgets added on the overview gets a menu entry and opens', async ({ userPage: { page } }) => { await page.goto('/index.php/apps/tables') await createContext(page, 'overview application') diff --git a/playwright/e2e/context.spec.ts b/playwright/e2e/context.spec.ts index 8afce5a67d..dccece8da4 100644 --- a/playwright/e2e/context.spec.ts +++ b/playwright/e2e/context.spec.ts @@ -247,6 +247,7 @@ test.describe('Manage a context', () => { await openApplication(page, contextTitle) // The application has loaded once its loading icon is gone; only then does a missing table mean anything await expect(page.locator('.main-context-view > .icon-loading')).toHaveCount(0, { timeout: 10000 }) + await expect(page.locator('[data-cy="context-grid-item"]')).toHaveCount(0) await expect(page.locator('h1', { hasText: tableTitle })).toBeHidden() }) diff --git a/src/pages/Context.vue b/src/pages/Context.vue index ec5fa14146..4474bdcec1 100644 --- a/src/pages/Context.vue +++ b/src/pages/Context.vue @@ -369,8 +369,52 @@ -