|
1 | | -import { nextTick, ref, watchEffect } from '@vue/runtime-dom' |
2 | | -import { createComponent, setText, template } from '../src' |
| 1 | +import { type Ref, nextTick, ref, watchEffect } from '@vue/runtime-dom' |
| 2 | +import { |
| 3 | + createComponent, |
| 4 | + defineVaporComponent, |
| 5 | + renderEffect, |
| 6 | + setClassIncremental, |
| 7 | + setStyleIncremental, |
| 8 | + setText, |
| 9 | + template, |
| 10 | +} from '../src' |
3 | 11 | import { makeRender } from './_utils' |
| 12 | +import { stringifyStyle } from '@vue/shared' |
4 | 13 |
|
5 | 14 | const define = makeRender<any>() |
6 | 15 |
|
@@ -132,4 +141,135 @@ describe('attribute fallthrough', () => { |
132 | 141 | await nextTick() |
133 | 142 | expect(host.innerHTML).toBe('<div foo="2" id="b">2</div>') |
134 | 143 | }) |
| 144 | + |
| 145 | + it('should merge classes', async () => { |
| 146 | + const rootClass = ref('root') |
| 147 | + const parentClass = ref('parent') |
| 148 | + const childClass = ref('child') |
| 149 | + |
| 150 | + const Child = defineVaporComponent({ |
| 151 | + setup() { |
| 152 | + const n = document.createElement('div') |
| 153 | + renderEffect(() => { |
| 154 | + // binding on template root generates incremental class setter |
| 155 | + setClassIncremental(n, childClass.value) |
| 156 | + }) |
| 157 | + return n |
| 158 | + }, |
| 159 | + }) |
| 160 | + |
| 161 | + const Parent = defineVaporComponent({ |
| 162 | + setup() { |
| 163 | + return createComponent( |
| 164 | + Child, |
| 165 | + { |
| 166 | + class: () => parentClass.value, |
| 167 | + }, |
| 168 | + null, |
| 169 | + true, // pass single root flag |
| 170 | + ) |
| 171 | + }, |
| 172 | + }) |
| 173 | + |
| 174 | + const { host } = define({ |
| 175 | + setup() { |
| 176 | + return createComponent(Parent, { |
| 177 | + class: () => rootClass.value, |
| 178 | + }) |
| 179 | + }, |
| 180 | + }).render() |
| 181 | + |
| 182 | + const list = host.children[0].classList |
| 183 | + // assert classes without being order-sensitive |
| 184 | + function assertClasses(cls: string[]) { |
| 185 | + expect(list.length).toBe(cls.length) |
| 186 | + for (const c of cls) { |
| 187 | + expect(list.contains(c)).toBe(true) |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + assertClasses(['root', 'parent', 'child']) |
| 192 | + |
| 193 | + rootClass.value = 'root1' |
| 194 | + await nextTick() |
| 195 | + assertClasses(['root1', 'parent', 'child']) |
| 196 | + |
| 197 | + parentClass.value = 'parent1' |
| 198 | + await nextTick() |
| 199 | + assertClasses(['root1', 'parent1', 'child']) |
| 200 | + |
| 201 | + childClass.value = 'child1' |
| 202 | + await nextTick() |
| 203 | + assertClasses(['root1', 'parent1', 'child1']) |
| 204 | + }) |
| 205 | + |
| 206 | + it('should merge styles', async () => { |
| 207 | + const rootStyle: Ref<string | Record<string, string>> = ref('color:red') |
| 208 | + const parentStyle: Ref<string | null> = ref('font-size:12px') |
| 209 | + const childStyle = ref('font-weight:bold') |
| 210 | + |
| 211 | + const Child = defineVaporComponent({ |
| 212 | + setup() { |
| 213 | + const n = document.createElement('div') |
| 214 | + renderEffect(() => { |
| 215 | + // binding on template root generates incremental class setter |
| 216 | + setStyleIncremental(n, childStyle.value) |
| 217 | + }) |
| 218 | + return n |
| 219 | + }, |
| 220 | + }) |
| 221 | + |
| 222 | + const Parent = defineVaporComponent({ |
| 223 | + setup() { |
| 224 | + return createComponent( |
| 225 | + Child, |
| 226 | + { |
| 227 | + style: () => parentStyle.value, |
| 228 | + }, |
| 229 | + null, |
| 230 | + true, // pass single root flag |
| 231 | + ) |
| 232 | + }, |
| 233 | + }) |
| 234 | + |
| 235 | + const { host } = define({ |
| 236 | + setup() { |
| 237 | + return createComponent(Parent, { |
| 238 | + style: () => rootStyle.value, |
| 239 | + }) |
| 240 | + }, |
| 241 | + }).render() |
| 242 | + |
| 243 | + const el = host.children[0] as HTMLElement |
| 244 | + |
| 245 | + function getCSS() { |
| 246 | + return el.style.cssText.replace(/\s+/g, '') |
| 247 | + } |
| 248 | + |
| 249 | + function assertStyles() { |
| 250 | + const css = getCSS() |
| 251 | + expect(css).toContain(stringifyStyle(rootStyle.value)) |
| 252 | + if (parentStyle.value) { |
| 253 | + expect(css).toContain(stringifyStyle(parentStyle.value)) |
| 254 | + } |
| 255 | + expect(css).toContain(stringifyStyle(childStyle.value)) |
| 256 | + } |
| 257 | + |
| 258 | + assertStyles() |
| 259 | + |
| 260 | + rootStyle.value = { color: 'green' } |
| 261 | + await nextTick() |
| 262 | + assertStyles() |
| 263 | + expect(getCSS()).not.toContain('color:red') |
| 264 | + |
| 265 | + parentStyle.value = null |
| 266 | + await nextTick() |
| 267 | + assertStyles() |
| 268 | + expect(getCSS()).not.toContain('font-size:12px') |
| 269 | + |
| 270 | + childStyle.value = 'font-weight:500' |
| 271 | + await nextTick() |
| 272 | + assertStyles() |
| 273 | + expect(getCSS()).not.toContain('font-size:bold') |
| 274 | + }) |
135 | 275 | }) |
0 commit comments