diff --git a/cypress/e2e/cardColor.js b/cypress/e2e/cardColor.js index 569e43ff19..6c053bbdb1 100644 --- a/cypress/e2e/cardColor.js +++ b/cypress/e2e/cardColor.js @@ -46,7 +46,7 @@ describe('Card color', function () { const newCardTitle = 'Card with color' - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .first().click() cy.get('.stack__card-add form input#new-stack-input-main') .type(newCardTitle) diff --git a/cypress/e2e/cardFeatures.js b/cypress/e2e/cardFeatures.js index a62e5bedc3..82d72a7aaa 100644 --- a/cypress/e2e/cardFeatures.js +++ b/cypress/e2e/cardFeatures.js @@ -52,7 +52,7 @@ describe('Card', function () { cy.get('.board .stack').eq(0).within(() => { cy.get('.card:contains("Hello world")').should('be.visible') - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .first().click() cy.get('.stack__card-add form input#new-stack-input-main') @@ -69,7 +69,7 @@ describe('Card', function () { cy.intercept({ method: 'POST', url: '**/ocs/v2.php/apps/deck/api/v1.0/cards' }).as('save') cy.intercept({ method: 'GET', url: '**/apps/deck/boards/*' }).as('getBoard') - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .first().click() // Somehow this avoids the electron crash @@ -98,7 +98,7 @@ describe('Card', function () { cy.visit(`/apps/deck/#/board/${boardId}`) const absoluteUrl = `https://example.com` cy.get('.board .stack').eq(0).within(() => { - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .first().click() cy.get('.stack__card-add form input#new-stack-input-main') @@ -120,7 +120,7 @@ describe('Card', function () { const absoluteUrl = `https://example.com` const plainTitle = 'New title' cy.get('.board .stack').eq(0).within(() => { - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .first().click() cy.get('.stack__card-add form input#new-stack-input-main') @@ -260,7 +260,7 @@ describe('Card', function () { it('Set a due date', function () { const newCardTitle = 'Card with a due date' - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .first().click() cy.get('.stack__card-add form input#new-stack-input-main') .type(newCardTitle) @@ -296,13 +296,20 @@ describe('Card', function () { it('Add a label', function () { const newCardTitle = 'Card with labels' - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .first().click() cy.get('.stack__card-add form input#new-stack-input-main') .type(newCardTitle) cy.get('.stack__card-add form input[type=submit]') .first().click() cy.get(`.card:contains("${newCardTitle}")`).should('be.visible').click() + cy.get('body').then(($body) => { + const addCardInput = $body.find('.stack__card-add form input#new-stack-input-main') + if (addCardInput.length) { + cy.wrap(addCardInput.first()).type('{esc}') + } + }) + cy.get('.stack__card-add form').should('not.exist') // Add delay to ensure the events are bound cy.wait(1000) @@ -316,8 +323,10 @@ describe('Card', function () { cy.get('.vs__selected .tag:contains("Action needed")') .parent().find('button').click() - cy.get(`.card:contains("${newCardTitle}")`).find('.labels li:contains("Later")') - .should('be.visible') + cy.get(`.card:contains("${newCardTitle}")`) + .scrollIntoView({ block: 'center' }) + .find('.labels li:contains("Later")') + .should('exist') cy.get(`.card:contains("${newCardTitle}")`).find('.labels li:contains("Action needed")') .should('not.exist') }) diff --git a/cypress/e2e/sharingFeatures.js b/cypress/e2e/sharingFeatures.js index abe1ed3f8f..d90844c677 100644 --- a/cypress/e2e/sharingFeatures.js +++ b/cypress/e2e/sharingFeatures.js @@ -32,7 +32,7 @@ describe('Board', function() { cy.login(recipient) cy.visit(`/apps/deck/#/board/${boardId}`) cy.get('.board-title').contains(board.title) - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .should('not.exist') }) }) @@ -50,7 +50,7 @@ describe('Board', function() { cy.login(recipient) cy.visit(`/apps/deck/#/board/${boardId}`) cy.get('.board-title').contains(board.title) - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .should('not.exist') }) }) @@ -71,7 +71,7 @@ describe('Board', function() { cy.login(recipient) cy.visit(`/apps/deck/#/board/${boardId}`) cy.get('.board-title').contains(board.title) - cy.get('.button-vue[aria-label*="Add card"]') + cy.get('[data-cy="action:add-card"]') .first().click() }) }) diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php index b4afb8fc45..7378b09f41 100644 --- a/lib/Service/ConfigService.php +++ b/lib/Service/ConfigService.php @@ -52,7 +52,8 @@ public function getAll(): array { $data = [ 'calendar' => $this->isCalendarEnabled(), 'cardDetailsInModal' => $this->isCardDetailsInModal(), - 'cardIdBadge' => $this->isCardIdBadgeEnabled() + 'cardIdBadge' => $this->isCardIdBadgeEnabled(), + 'stackAddCardAtTop' => $this->isStackAddCardAtTopEnabled() ]; if ($this->groupManager->isAdmin($userId)) { $data['groupLimit'] = $this->get('groupLimit'); @@ -90,6 +91,8 @@ public function get(string $key) { return false; } return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', false); + case 'stackAddCardAtTop': + return $this->isStackAddCardAtTopEnabled(); } return false; } @@ -134,6 +137,15 @@ public function isCardIdBadgeEnabled(): bool { return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardIdBadge', $defaultState); } + public function isStackAddCardAtTopEnabled(): bool { + $userId = $this->getUserId(); + if ($userId === null) { + return false; + } + + return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'stackAddCardAtTop', false); + } + public function ensureFederationEnabled() { if (!$this->get('federationEnabled')) { throw new FederationDisabledException(); @@ -181,6 +193,10 @@ public function set($key, $value) { $this->config->setUserValue($userId, Application::APP_ID, 'cardIdBadge', (string)$value); $result = $value; break; + case 'stackAddCardAtTop': + $this->config->setUserValue($userId, Application::APP_ID, 'stackAddCardAtTop', (string)$value); + $result = $value; + break; case 'board': // extra check that user only send one of the allowed board settings and not something random $parts = explode(':', $key, 3); diff --git a/src/components/Controls.vue b/src/components/Controls.vue index c84b126a0e..5001ab9f99 100644 --- a/src/components/Controls.vue +++ b/src/components/Controls.vue @@ -14,7 +14,7 @@ {{ overviewName }} - + {{ t('deck', 'Add card') }} diff --git a/src/components/DeckAppSettings.vue b/src/components/DeckAppSettings.vue index 3935dbff5d..4d095b3448 100644 --- a/src/components/DeckAppSettings.vue +++ b/src/components/DeckAppSettings.vue @@ -12,6 +12,8 @@ + @@ -119,6 +121,14 @@ export default { this.$store.dispatch('setConfig', { cardDetailsInModal: newValue }) }, }, + stackAddCardAtTop: { + get() { + return this.$store.getters.config('stackAddCardAtTop') === true + }, + set(newValue) { + this.$store.dispatch('setConfig', { stackAddCardAtTop: newValue }) + }, + }, cardIdBadge: { get() { return this.$store.getters.config('cardIdBadge') diff --git a/src/components/board/Stack.vue b/src/components/board/Stack.vue index 7f81349f57..6b567f2515 100644 --- a/src/components/board/Stack.vue +++ b/src/components/board/Stack.vue @@ -4,8 +4,17 @@ -->