diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd9ebb7970..f2a7d0a861a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [3.4.18](https://github.com/vuejs/core/compare/v3.4.17...v3.4.18) (2024-02-09) + + +### Bug Fixes + +* **dx:** warn against reserved keys as prop name ([77a804b](https://github.com/vuejs/core/commit/77a804b1d0d6a3f12fb3674cdceb85ebd6481e02)), closes [#10281](https://github.com/vuejs/core/issues/10281) +* **runtime-dom:** ensure v-show respects display value set via v-bind ([#10297](https://github.com/vuejs/core/issues/10297)) ([c224897](https://github.com/vuejs/core/commit/c224897dd4e189a10ec601a97fe08cb638ebee19)), closes [#10151](https://github.com/vuejs/core/issues/10151) + + + ## [3.4.17](https://github.com/vuejs/core/compare/v3.4.16...v3.4.17) (2024-02-09) diff --git a/package.json b/package.json index ba6ddd7584d..86ab01032b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "3.4.17", + "version": "3.4.18", "packageManager": "pnpm@8.15.1", "type": "module", "scripts": { diff --git a/packages/compiler-core/package.json b/packages/compiler-core/package.json index 3357d1f0ee0..b35f85a56dd 100644 --- a/packages/compiler-core/package.json +++ b/packages/compiler-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-core", - "version": "3.4.17", + "version": "3.4.18", "description": "@vue/compiler-core", "main": "index.js", "module": "dist/compiler-core.esm-bundler.js", diff --git a/packages/compiler-dom/package.json b/packages/compiler-dom/package.json index 9656938dd8a..14448be7a9a 100644 --- a/packages/compiler-dom/package.json +++ b/packages/compiler-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-dom", - "version": "3.4.17", + "version": "3.4.18", "description": "@vue/compiler-dom", "main": "index.js", "module": "dist/compiler-dom.esm-bundler.js", diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index 3a098e25491..47b9281d6eb 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-sfc", - "version": "3.4.17", + "version": "3.4.18", "description": "@vue/compiler-sfc", "main": "dist/compiler-sfc.cjs.js", "module": "dist/compiler-sfc.esm-browser.js", diff --git a/packages/compiler-ssr/package.json b/packages/compiler-ssr/package.json index 43a2a26ddf5..471e65ec144 100644 --- a/packages/compiler-ssr/package.json +++ b/packages/compiler-ssr/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-ssr", - "version": "3.4.17", + "version": "3.4.18", "description": "@vue/compiler-ssr", "main": "dist/compiler-ssr.cjs.js", "types": "dist/compiler-ssr.d.ts", diff --git a/packages/reactivity/package.json b/packages/reactivity/package.json index 53df9ffe47d..b1c069e4c46 100644 --- a/packages/reactivity/package.json +++ b/packages/reactivity/package.json @@ -1,6 +1,6 @@ { "name": "@vue/reactivity", - "version": "3.4.17", + "version": "3.4.18", "description": "@vue/reactivity", "main": "index.js", "module": "dist/reactivity.esm-bundler.js", diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index 6760a957f12..6fc5d4dd2d7 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -729,4 +729,23 @@ describe('component props', () => { expect(Object.keys(props.msg).length).toBe(1) }) + + test('should warn against reserved prop names', () => { + const Comp = defineComponent({ + props: { + key: String, + ref: String, + $foo: String, + }, + render() {}, + }) + + const root = nodeOps.createElement('div') + + render(h(Comp, { msg: 'test' }), root) + + expect(`Invalid prop name: "key"`).toHaveBeenWarned() + expect(`Invalid prop name: "ref"`).toHaveBeenWarned() + expect(`Invalid prop name: "$foo"`).toHaveBeenWarned() + }) }) diff --git a/packages/runtime-core/package.json b/packages/runtime-core/package.json index 357e66242f9..f3cc413b697 100644 --- a/packages/runtime-core/package.json +++ b/packages/runtime-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-core", - "version": "3.4.17", + "version": "3.4.18", "description": "@vue/runtime-core", "main": "index.js", "module": "dist/runtime-core.esm-bundler.js", diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 088f4da3c6e..d1822a1638d 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -586,7 +586,7 @@ export function normalizePropsOptions( } function validatePropName(key: string) { - if (key[0] !== '$') { + if (key[0] !== '$' && !isReservedProp(key)) { return true } else if (__DEV__) { warn(`Invalid prop name: "${key}" is a reserved property.`) diff --git a/packages/runtime-dom/__tests__/directives/vShow.spec.ts b/packages/runtime-dom/__tests__/directives/vShow.spec.ts index 70b69f2df1c..fddafaaf823 100644 --- a/packages/runtime-dom/__tests__/directives/vShow.spec.ts +++ b/packages/runtime-dom/__tests__/directives/vShow.spec.ts @@ -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('') + }) }) diff --git a/packages/runtime-dom/package.json b/packages/runtime-dom/package.json index 137dc1a449a..e5daabddb8c 100644 --- a/packages/runtime-dom/package.json +++ b/packages/runtime-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-dom", - "version": "3.4.17", + "version": "3.4.18", "description": "@vue/runtime-dom", "main": "index.js", "module": "dist/runtime-dom.esm-bundler.js", diff --git a/packages/runtime-dom/src/directives/vShow.ts b/packages/runtime-dom/src/directives/vShow.ts index 2ab25136e74..d8aab92e71b 100644 --- a/packages/runtime-dom/src/directives/vShow.ts +++ b/packages/runtime-dom/src/directives/vShow.ts @@ -22,7 +22,7 @@ export const vShow: ObjectDirective & { 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) diff --git a/packages/runtime-dom/src/modules/style.ts b/packages/runtime-dom/src/modules/style.ts index 6341c8a120e..9f897a6b2b0 100644 --- a/packages/runtime-dom/src/modules/style.ts +++ b/packages/runtime-dom/src/modules/style.ts @@ -5,10 +5,13 @@ import { CSS_VAR_TEXT } from '../helpers/useCssVars' type Style = string | Record | 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 } } diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json index 4d5f5373da5..b3180b2010c 100644 --- a/packages/server-renderer/package.json +++ b/packages/server-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@vue/server-renderer", - "version": "3.4.17", + "version": "3.4.18", "description": "@vue/server-renderer", "main": "index.js", "module": "dist/server-renderer.esm-bundler.js", diff --git a/packages/shared/package.json b/packages/shared/package.json index 20475aea204..d816e3105e4 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@vue/shared", - "version": "3.4.17", + "version": "3.4.18", "description": "internal utils shared across @vue packages", "main": "index.js", "module": "dist/shared.esm-bundler.js", diff --git a/packages/vue-compat/package.json b/packages/vue-compat/package.json index 0b8ee320524..37ef5a957df 100644 --- a/packages/vue-compat/package.json +++ b/packages/vue-compat/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compat", - "version": "3.4.17", + "version": "3.4.18", "description": "Vue 3 compatibility build for Vue 2", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", diff --git a/packages/vue/package.json b/packages/vue/package.json index f863683f9dd..d743077ecb5 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "3.4.17", + "version": "3.4.18", "description": "The progressive JavaScript framework for building modern web UI.", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", diff --git a/scripts/setup-vitest.ts b/scripts/setup-vitest.ts index d5c65f0d3c0..53e7f5fff56 100644 --- a/scripts/setup-vitest.ts +++ b/scripts/setup-vitest.ts @@ -4,9 +4,9 @@ vi.stubGlobal('MathMLElement', class MathMLElement {}) expect.extend({ toHaveBeenWarned(received: string) { - asserted.add(received) const passed = warn.mock.calls.some(args => args[0].includes(received)) if (passed) { + asserted.add(received) return { pass: true, message: () => `expected "${received}" not to have been warned.`, @@ -25,10 +25,10 @@ expect.extend({ }, toHaveBeenWarnedLast(received: string) { - asserted.add(received) const passed = warn.mock.calls[warn.mock.calls.length - 1][0].includes(received) if (passed) { + asserted.add(received) return { pass: true, message: () => `expected "${received}" not to have been warned last.`, @@ -44,7 +44,6 @@ expect.extend({ }, toHaveBeenWarnedTimes(received: string, n: number) { - asserted.add(received) let found = 0 warn.mock.calls.forEach(args => { if (args[0].includes(received)) { @@ -53,6 +52,7 @@ expect.extend({ }) if (found === n) { + asserted.add(received) return { pass: true, message: () => `expected "${received}" to have been warned ${n} times.`,