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 @@
-->
-
-
+
@@ -101,7 +102,44 @@
-
+
+
+
+
+
+ {{ t('deck', 'Add card') }}
+
+
+
+
+
+
-