diff --git a/packages/runtime-dom/__tests__/patchAttrs.spec.ts b/packages/runtime-dom/__tests__/patchAttrs.spec.ts index 8436bbb4f..393b685b0 100644 --- a/packages/runtime-dom/__tests__/patchAttrs.spec.ts +++ b/packages/runtime-dom/__tests__/patchAttrs.spec.ts @@ -69,4 +69,23 @@ describe('runtime-dom: attrs patching', () => { patchProp(el, 'value', null, symbol) expect(el.value).toBe(symbol.toString()) }) + + // #11177 + test('should allow setting value to object, leaving stringification to the element/browser', () => { + // normal behavior + const el = document.createElement('div') + const obj = { toString: () => 'foo' } + patchProp(el, 'data-test', null, obj) + expect(el.dataset.test).toBe('foo') + + const el2 = document.createElement('div') + let testvalue: null | typeof obj = null + // simulating a web component that implements its own setAttribute handler + el2.setAttribute = (name, value) => { + testvalue = value + } + patchProp(el2, 'data-test', null, obj) + expect(el2.dataset.test).toBe(undefined) + expect(testvalue).toBe(obj) + }) }) diff --git a/packages/runtime-dom/src/modules/attrs.ts b/packages/runtime-dom/src/modules/attrs.ts index 3cc3468a7..6e411f5b2 100644 --- a/packages/runtime-dom/src/modules/attrs.ts +++ b/packages/runtime-dom/src/modules/attrs.ts @@ -2,6 +2,7 @@ import { NOOP, includeBooleanAttr, isSpecialBooleanAttr, + isSymbol, makeMap, } from '@vue/shared' import { @@ -37,7 +38,10 @@ export function patchAttr( el.removeAttribute(key) } else { // attribute value is a string https://html.spec.whatwg.org/multipage/dom.html#attributes - el.setAttribute(key, isBoolean ? '' : String(value)) + el.setAttribute( + key, + isBoolean ? '' : isSymbol(value) ? String(value) : value, + ) } } }