From 6d201a3468c391c7ddbc06bbbb09d1b223d791ba Mon Sep 17 00:00:00 2001 From: Conduction Release Bot Date: Sun, 30 Aug 2026 17:50:38 +0200 Subject: [PATCH] fix(ui): the select dropdown must paint above what follows it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adapts to @nextcloud/vue 9.11 rather than pinning away from it, and takes the pin off. The eleven E2E failures were never an unstable element. The Playwright trace from CI says so directly: - locator resolved to
  • - attempting click action - element is visible, enabled and stable <- stable - scrolling into view if needed -
    ... intercepts The option was VISIBLE, ENABLED AND STABLE. The click was intercepted by the element painted on top of it. Both failing areas are the same shape. A column of blocks; a select in one of them; its dropdown opens downward over the LATER siblings; those siblings have no stacking context, so they paint last and win: conditional-visibility
    dashboard-sharing

    9.10's floating-label rework (#8570) is what made it reachable: the control got taller, so the dropdown now extends far enough to reach them. That is why 9.9.0 was green and 9.11 is not, without either version being broken. The fix is a stacking context on the block that holds the select, in both places. Nothing else changes, and the dropdown is only raised where a select actually lives. ⚠️ This could not be verified locally. The same specs PASS against a seeded launchpad-demo on :8605 built with 9.11 -- the layout there does not put the dropdown over those siblings. The trace from CI is the evidence this rests on, and CI is where it has to be confirmed. --- package-lock.json | 18 +++---- package.json | 2 +- src/components/Widgets/VisibilityRuleRow.vue | 26 ++++++++++ src/modals/DashboardConfigModal.vue | 53 +++++++++++++++++++- 4 files changed, 88 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 33eca57c8..9b2e2851e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@nextcloud/initial-state": "^2.2.0", "@nextcloud/l10n": "^3.4.1", "@nextcloud/router": "^3.1.0", - "@nextcloud/vue": "~9.9.0", + "@nextcloud/vue": "^9.11.0", "dexie": "^4.4.5", "dompurify": "^3.4.14", "gridstack": "^12.2.1", @@ -4251,9 +4251,9 @@ } }, "node_modules/@nextcloud/vue": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-9.9.0.tgz", - "integrity": "sha512-TmKnBWp6Aiw+cm+WamwX5cMkhkpJqLSUKrTUB5I5Y9RDt2S0TUlwRaTe9vltBIV3qmKParhpZQ3/ewWHgEQlrQ==", + "version": "9.11.0", + "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-9.11.0.tgz", + "integrity": "sha512-LRsyU9Mxs0b2xWqbxygps8O7/i1z4QhNQD9/QDni+VqFY3V0N95mAgmKJm6vfoFuxTAMXERp9bjqFn3UV542gw==", "license": "AGPL-3.0-or-later", "dependencies": { "@ckpack/vue-color": "^1.6.0", @@ -4270,19 +4270,19 @@ "@nextcloud/sharing": "^0.4.0", "@nextcloud/vue-select": "^4.1.0", "@vuepic/vue-datepicker": "^11.0.3", - "@vueuse/components": "^14.3.0", - "@vueuse/core": "^14.3.0", + "@vueuse/components": "^14.4.0", + "@vueuse/core": "^14.4.0", "blurhash": "^2.0.5", "clone": "^2.1.2", "debounce": "^3.0.0", - "dompurify": "^3.4.12", + "dompurify": "^3.4.14", "emoji-mart-vue-fast": "^15.0.5", "escape-html": "^1.0.3", "floating-vue": "^5.2.2", "focus-trap": "^8.2.2", "linkifyjs": "^4.3.3", "mdast-util-to-string": "^4.0.0", - "p-queue": "^9.3.1", + "p-queue": "^9.3.3", "rehype-external-links": "^3.0.0", "rehype-highlight": "^7.0.2", "rehype-react": "^8.0.0", @@ -4299,7 +4299,7 @@ "unist-builder": "^4.0.0", "unist-util-visit-parents": "^6.0.2", "vue": "^3.5.18", - "vue-router": "^5.1.0" + "vue-router": "^5.2.0" }, "engines": { "node": "^20.11.0 || ^22 || ^24" diff --git a/package.json b/package.json index 03fca29a0..2238ec08e 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@nextcloud/initial-state": "^2.2.0", "@nextcloud/l10n": "^3.4.1", "@nextcloud/router": "^3.1.0", - "@nextcloud/vue": "~9.9.0", + "@nextcloud/vue": "^9.11.0", "dexie": "^4.4.5", "dompurify": "^3.4.14", "gridstack": "^12.2.1", diff --git a/src/components/Widgets/VisibilityRuleRow.vue b/src/components/Widgets/VisibilityRuleRow.vue index 246c2c50b..b93ad9dd8 100644 --- a/src/components/Widgets/VisibilityRuleRow.vue +++ b/src/components/Widgets/VisibilityRuleRow.vue @@ -708,4 +708,30 @@ export default { gap: 8px; justify-content: flex-end; } + +/* + * The open select dropdown must paint ABOVE the row's action buttons. + * + * The row is a flex column: fields first, actions after. An NcSelect + * dropdown opens downward out of the fields block and lands over the + * actions, and because the actions are a LATER sibling with no stacking + * context of their own, they win and swallow the click. + * + * @nextcloud/vue 9.10 made this reachable by giving NcSelect a floating + * label (#8570), which makes the control taller and pushes the dropdown + * further down -- far enough to reach the actions. The Playwright trace + * names the interception exactly: + * + * - locator resolved to

  • + * - attempting click action + * - element is visible, enabled and stable + * -
    ... intercepts pointer events + * + * The option was never unstable; it was covered. Giving the fields their + * own stacking context puts the dropdown back on top. + */ +.visibility-rule-row__fields { + position: relative; + z-index: 1; +} diff --git a/src/modals/DashboardConfigModal.vue b/src/modals/DashboardConfigModal.vue index 6e084b12c..ba6893b92 100644 --- a/src/modals/DashboardConfigModal.vue +++ b/src/modals/DashboardConfigModal.vue @@ -136,7 +136,7 @@ class="dashboard-config__panel">
    + class="dashboard-config__field dashboard-config__field--select"> @@ -1151,6 +1151,57 @@ export default { gap: 6px; } +/* + * The sharee dropdown must paint ABOVE the fields that follow it. + * + * The modal body is a column of `__field` blocks. An NcSelect dropdown + * opens downward out of its field and lands over the next ones, and + * because those are LATER siblings with no stacking context of their own, + * they win and swallow the click. + * + * @nextcloud/vue 9.10 made this reachable by giving NcSelect a floating + * label (#8570): the control is taller, so the dropdown reaches further + * down. The Playwright trace names the interceptors exactly: + * + * - locator resolved to + * - attempting click action + * - element is visible, enabled and stable + * -
    ... + * -

    Not shared with anyone yet.

    ... + * + * The option was never unstable; it was covered. Only the field holding a + * select is raised, so nothing else changes. + */ +.dashboard-config__field--select { + position: relative; + z-index: 2; +} + +/* + * A field holding an NcSelect must paint ABOVE the fields that follow it. + * + * The modal is a column of fields. An NcSelect dropdown opens downward out + * of its own field and lands over the next one; those are LATER siblings + * with no stacking context, so they win and swallow the click. + * + * @nextcloud/vue 9.10 made this reachable by giving NcSelect a floating + * label (#8570), which makes the control taller and pushes the dropdown + * far enough down to reach them. The Playwright trace names the + * interception rather than leaving it to be guessed: + * + * - locator resolved to + * - attempting click action + * - element is visible, enabled and stable + * -

    Not shared with anyone yet.

    + * ... intercepts pointer events + * + * The option was never unstable; it was covered. + */ +.dashboard-config__field--select { + position: relative; + z-index: 2; +} + .dashboard-config__label { font-size: 13px; font-weight: 600;