]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-dom): ensure v-show respects display value set via v-bind (#10297)
authorzhoulixiang <18366276315@163.com>
Fri, 9 Feb 2024 02:17:35 +0000 (10:17 +0800)
committerGitHub <noreply@github.com>
Fri, 9 Feb 2024 02:17:35 +0000 (10:17 +0800)
close #10151

packages/runtime-dom/__tests__/directives/vShow.spec.ts
packages/runtime-dom/src/directives/vShow.ts
packages/runtime-dom/src/modules/style.ts

index 70b69f2df1c10050e3c42547a49d78511739752a..fddafaaf823262b8aad861d63115693eb0a2ff73 100644 (file)
@@ -211,4 +211,102 @@ describe('runtime-dom: v-show directive', () => {
     await nextTick()
     expect($div.style.display).toEqual('')
   })
+
+  // #10151
+  test('should respect the display value when v-show value is true', async () => {
+    const isVisible = ref(false)
+    const useDisplayStyle = ref(true)
+    const compStyle = ref({
+      display: 'none',
+    })
+    const withoutDisplayStyle = {
+      margin: '10px',
+    }
+
+    const Component = {
+      setup() {
+        return () => {
+          return withVShow(
+            h('div', {
+              style: useDisplayStyle.value
+                ? compStyle.value
+                : withoutDisplayStyle,
+            }),
+            isVisible.value,
+          )
+        }
+      },
+    }
+    render(h(Component), root)
+
+    const $div = root.children[0]
+
+    expect($div.style.display).toEqual('none')
+
+    isVisible.value = true
+    await nextTick()
+    expect($div.style.display).toEqual('none')
+
+    compStyle.value.display = 'block'
+    await nextTick()
+    expect($div.style.display).toEqual('block')
+
+    compStyle.value.display = 'inline-block'
+    await nextTick()
+    expect($div.style.display).toEqual('inline-block')
+
+    isVisible.value = false
+    await nextTick()
+    expect($div.style.display).toEqual('none')
+
+    isVisible.value = true
+    await nextTick()
+    expect($div.style.display).toEqual('inline-block')
+
+    useDisplayStyle.value = false
+    await nextTick()
+    expect($div.style.display).toEqual('')
+    expect(getComputedStyle($div).display).toEqual('block')
+
+    isVisible.value = false
+    await nextTick()
+    expect($div.style.display).toEqual('none')
+
+    isVisible.value = true
+    await nextTick()
+    expect($div.style.display).toEqual('')
+  })
+
+  // #10294
+  test('should record display by vShowOldKey only when display exists in style', async () => {
+    const isVisible = ref(false)
+    const style = ref({
+      margin: '10px',
+    })
+
+    const Component = {
+      setup() {
+        return () => {
+          return withVShow(
+            h('div', {
+              style: style.value,
+            }),
+            isVisible.value,
+          )
+        }
+      },
+    }
+    render(h(Component), root)
+    const $div = root.children[0]
+
+    expect($div.style.display).toEqual('none')
+
+    style.value.margin = '20px'
+    await nextTick()
+    expect($div.style.display).toEqual('none')
+
+    isVisible.value = true
+    await nextTick()
+    expect($div.style.display).toEqual('')
+  })
 })
index 2ab25136e74a7ccbd7fa3c2523298f82f1072f84..d8aab92e71bebd6ee02b2178e25675656eb25bca 100644 (file)
@@ -22,7 +22,7 @@ export const vShow: ObjectDirective<VShowElement> & { name?: 'show' } = {
     }
   },
   updated(el, { value, oldValue }, { transition }) {
-    if (!value === !oldValue) return
+    if (!value === !oldValue && el.style.display === el[vShowOldKey]) return
     if (transition) {
       if (value) {
         transition.beforeEnter(el)
index 6341c8a120e0690270e8c64dc698f4c6d094105e..9f897a6b2b00e12c2006c0054fd95b532d3cb657 100644 (file)
@@ -5,10 +5,13 @@ import { CSS_VAR_TEXT } from '../helpers/useCssVars'
 
 type Style = string | Record<string, string | string[]> | null
 
+const displayRE = /(^|;)\s*display\s*:/
+
 export function patchStyle(el: Element, prev: Style, next: Style) {
   const style = (el as HTMLElement).style
-  const currentDisplay = style.display
   const isCssString = isString(next)
+  const currentDisplay = style.display
+  let hasControlledDisplay = false
   if (next && !isCssString) {
     if (prev && !isString(prev)) {
       for (const key in prev) {
@@ -18,6 +21,9 @@ export function patchStyle(el: Element, prev: Style, next: Style) {
       }
     }
     for (const key in next) {
+      if (key === 'display') {
+        hasControlledDisplay = true
+      }
       setStyle(style, key, next[key])
     }
   } else {
@@ -29,6 +35,7 @@ export function patchStyle(el: Element, prev: Style, next: Style) {
           ;(next as string) += ';' + cssVarText
         }
         style.cssText = next as string
+        hasControlledDisplay = displayRE.test(next)
       }
     } else if (prev) {
       el.removeAttribute('style')
@@ -38,6 +45,7 @@ export function patchStyle(el: Element, prev: Style, next: Style) {
   // so we always keep the current `display` value regardless of the `style`
   // value, thus handing over control to `v-show`.
   if (vShowOldKey in el) {
+    el[vShowOldKey] = hasControlledDisplay ? style.display : ''
     style.display = currentDisplay
   }
 }