patchStyle.spec.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import { patchProp } from '../src/patchProp'
  2. describe(`runtime-dom: style patching`, () => {
  3. it('string', () => {
  4. const el = document.createElement('div')
  5. patchProp(el, 'style', {}, 'color:red')
  6. expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
  7. })
  8. // #1309
  9. it('should not patch same string style', () => {
  10. const el = document.createElement('div')
  11. const fn = vi.fn()
  12. const value = (el.style.cssText = 'color:red;')
  13. Object.defineProperty(el.style, 'cssText', {
  14. get(): any {
  15. return value
  16. },
  17. set: fn,
  18. })
  19. patchProp(el, 'style', value, value)
  20. expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
  21. expect(fn).not.toBeCalled()
  22. })
  23. it('plain object', () => {
  24. const el = document.createElement('div')
  25. patchProp(el, 'style', {}, { color: 'red' })
  26. expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
  27. })
  28. it('should preserve textarea resize dimensions while reapplying unrelated unchanged object styles', () => {
  29. const el = document.createElement('textarea')
  30. const value = {
  31. width: '200px',
  32. height: '100px',
  33. display: 'none',
  34. }
  35. patchProp(el, 'style', null, value)
  36. el.style.width = '320px'
  37. el.style.height = '160px'
  38. el.style.display = 'block'
  39. patchProp(el, 'style', value, value)
  40. expect(el.style.width).toBe('320px')
  41. expect(el.style.height).toBe('160px')
  42. expect(el.style.display).toBe('none')
  43. })
  44. it('camelCase', () => {
  45. const el = document.createElement('div')
  46. patchProp(el, 'style', {}, { marginRight: '10px' })
  47. expect(el.style.cssText.replace(/\s/g, '')).toBe('margin-right:10px;')
  48. })
  49. it('remove if falsy value', () => {
  50. const el = document.createElement('div')
  51. patchProp(el, 'style', null, {
  52. color: undefined,
  53. borderRadius: null,
  54. })
  55. expect(el.style.cssText.replace(/\s/g, '')).toBe('')
  56. patchProp(
  57. el,
  58. 'style',
  59. { color: 'red' },
  60. { color: null, borderRadius: undefined },
  61. )
  62. expect(el.style.cssText.replace(/\s/g, '')).toBe('')
  63. })
  64. it('!important', () => {
  65. const el = document.createElement('div')
  66. patchProp(el, 'style', {}, { color: 'red !important' })
  67. expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red!important;')
  68. })
  69. it('camelCase with !important', () => {
  70. const el = document.createElement('div')
  71. patchProp(el, 'style', {}, { marginRight: '10px !important' })
  72. expect(el.style.cssText.replace(/\s/g, '')).toBe(
  73. 'margin-right:10px!important;',
  74. )
  75. })
  76. it('object with multiple entries', () => {
  77. const el = document.createElement('div')
  78. patchProp(el, 'style', {}, { color: 'red', marginRight: '10px' })
  79. expect(el.style.getPropertyValue('color')).toBe('red')
  80. expect(el.style.getPropertyValue('margin-right')).toBe('10px')
  81. })
  82. it('patch with falsy style value', () => {
  83. const el = document.createElement('div')
  84. patchProp(el as any, 'style', { width: '100px' }, { width: 0 })
  85. expect(el.style.width).toBe('0px')
  86. })
  87. it('should remove style attribute on falsy value', () => {
  88. const el = document.createElement('div')
  89. el.style.cssText = 'color: red;'
  90. patchProp(el as any, 'style', {}, null)
  91. expect(el.hasAttribute('style')).toBe(false)
  92. expect(el.style.cssText).toBe('')
  93. })
  94. it('should warn for trailing semicolons', () => {
  95. const el = document.createElement('div')
  96. patchProp(el, 'style', null, { color: 'red;' })
  97. expect(
  98. `Unexpected semicolon at the end of 'color' style value: 'red;'`,
  99. ).toHaveBeenWarned()
  100. patchProp(el, 'style', null, { '--custom': '100; ' })
  101. expect(
  102. `Unexpected semicolon at the end of '--custom' style value: '100; '`,
  103. ).toHaveBeenWarned()
  104. })
  105. it('should not warn for escaped trailing semicolons', () => {
  106. const el = document.createElement('div')
  107. patchProp(el, 'style', null, { '--custom': '100\\;' })
  108. expect(el.style.getPropertyValue('--custom')).toBe('100\\;')
  109. })
  110. it('shorthand properties', () => {
  111. const el = document.createElement('div')
  112. patchProp(
  113. el as any,
  114. 'style',
  115. { borderBottom: '1px solid red' },
  116. { border: '1px solid green' },
  117. )
  118. expect(el.style.border).toBe('1px solid green')
  119. expect(el.style.borderBottom).toBe('1px solid green')
  120. })
  121. // JSDOM doesn't support custom properties on style object so we have to
  122. // mock it here.
  123. function mockElementWithStyle() {
  124. const store: any = {}
  125. return {
  126. style: {
  127. display: '',
  128. WebkitTransition: '',
  129. setProperty(key: string, val: string) {
  130. store[key] = val
  131. },
  132. getPropertyValue(key: string) {
  133. return store[key]
  134. },
  135. },
  136. }
  137. }
  138. it('CSS custom properties', () => {
  139. const el = mockElementWithStyle()
  140. patchProp(el as any, 'style', {}, { '--theme': 'red' } as any)
  141. expect(el.style.getPropertyValue('--theme')).toBe('red')
  142. })
  143. it('auto vendor prefixing', () => {
  144. const el = mockElementWithStyle()
  145. patchProp(el as any, 'style', {}, { transition: 'all 1s' })
  146. expect(el.style.WebkitTransition).toBe('all 1s')
  147. })
  148. it('multiple values', () => {
  149. const el = mockElementWithStyle()
  150. patchProp(
  151. el as any,
  152. 'style',
  153. {},
  154. { display: ['-webkit-box', '-ms-flexbox', 'flex'] },
  155. )
  156. expect(el.style.display).toBe('flex')
  157. })
  158. it('should clear previous css string value', () => {
  159. const el = document.createElement('div')
  160. patchProp(el, 'style', {}, 'color:red')
  161. expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
  162. patchProp(el, 'style', 'color:red', { fontSize: '12px' })
  163. expect(el.style.cssText.replace(/\s/g, '')).toBe('font-size:12px;')
  164. })
  165. })