@wix/zero-config-implementation 1.72.0 → 1.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-CdHu4-o-.js → index-K3lIO4FC.js} +11986 -11771
- package/dist/{index-CSi4SZpQ.js → index-NbI-_ozJ.js} +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/converters/to-editor-component.test.ts +88 -0
- package/src/converters/to-editor-component.ts +11 -0
- package/src/information-extractors/css/parse.test.ts +49 -1
- package/src/information-extractors/css/parse.ts +167 -3
- package/src/information-extractors/css/selector-matcher.ts +3 -1
- package/src/information-extractors/css/types.ts +11 -0
- package/src/information-extractors/react/extractors/css-properties.test.ts +294 -2
- package/src/information-extractors/react/extractors/css-properties.ts +246 -98
- package/src/information-extractors/react/extractors/index.ts +1 -0
- package/src/information-extractors/react/index.ts +1 -0
- package/src/manifest-pipeline.ts +3 -1
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import { CSS_PROPERTIES } from '@wix/react-component-schema'
|
|
1
2
|
import { describe, expect, it } from 'vitest'
|
|
2
3
|
import { parseCss } from '../../css/parse'
|
|
3
4
|
import type { MatchedCssData } from '../../css/types'
|
|
4
|
-
import {
|
|
5
|
+
import type { ExtractedElement } from './core/tree-builder'
|
|
6
|
+
import {
|
|
7
|
+
enrichContainerProperties,
|
|
8
|
+
getCssPropertiesForTag,
|
|
9
|
+
getDefaultDisplayForTag,
|
|
10
|
+
hasContainerLikeDisplay,
|
|
11
|
+
hasFlexOrGridDisplay,
|
|
12
|
+
} from './css-properties'
|
|
5
13
|
|
|
6
14
|
function matcherDataFromCss(declarations: string): MatchedCssData {
|
|
7
15
|
const properties = parseCss(`.test { ${declarations} }`).getPropertiesForSelector('.test')
|
|
@@ -12,16 +20,49 @@ function matcherDataFromCss(declarations: string): MatchedCssData {
|
|
|
12
20
|
}
|
|
13
21
|
}
|
|
14
22
|
return {
|
|
15
|
-
matches: [
|
|
23
|
+
matches: [
|
|
24
|
+
{
|
|
25
|
+
selector: '.test',
|
|
26
|
+
properties: properties.filter((property) => !property.name.startsWith('--')),
|
|
27
|
+
specificity: [0, 1, 0],
|
|
28
|
+
},
|
|
29
|
+
],
|
|
16
30
|
customProperties,
|
|
17
31
|
}
|
|
18
32
|
}
|
|
19
33
|
|
|
34
|
+
function createElementWithCssProperties(
|
|
35
|
+
tag: string,
|
|
36
|
+
declarations: string,
|
|
37
|
+
attributes: Record<string, string> = {},
|
|
38
|
+
): ExtractedElement {
|
|
39
|
+
const extractorData = new Map<string, unknown>()
|
|
40
|
+
extractorData.set('css-matcher', matcherDataFromCss(declarations))
|
|
41
|
+
extractorData.set('css-properties', { relevant: getCssPropertiesForTag(tag, attributes.role) })
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
traceId: 'trace-1',
|
|
45
|
+
name: 'element',
|
|
46
|
+
tag,
|
|
47
|
+
attributes,
|
|
48
|
+
extractorData,
|
|
49
|
+
children: [],
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
20
53
|
describe('hasFlexOrGridDisplay', () => {
|
|
21
54
|
it('returns true for display: flex', () => {
|
|
22
55
|
expect(hasFlexOrGridDisplay(matcherDataFromCss('display: flex'))).toBe(true)
|
|
23
56
|
})
|
|
24
57
|
|
|
58
|
+
it('returns true for display: block flex', () => {
|
|
59
|
+
expect(hasFlexOrGridDisplay(matcherDataFromCss('display: block flex'))).toBe(true)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('returns true for display: inline grid', () => {
|
|
63
|
+
expect(hasFlexOrGridDisplay(matcherDataFromCss('display: inline grid'))).toBe(true)
|
|
64
|
+
})
|
|
65
|
+
|
|
25
66
|
it('returns false for display: block', () => {
|
|
26
67
|
expect(hasFlexOrGridDisplay(matcherDataFromCss('display: block'))).toBe(false)
|
|
27
68
|
})
|
|
@@ -42,3 +83,254 @@ describe('hasFlexOrGridDisplay', () => {
|
|
|
42
83
|
expect(hasFlexOrGridDisplay(matcherDataFromCss('display: var(--d)'))).toBe(true)
|
|
43
84
|
})
|
|
44
85
|
})
|
|
86
|
+
|
|
87
|
+
describe('hasContainerLikeDisplay', () => {
|
|
88
|
+
it('returns true for display: block', () => {
|
|
89
|
+
expect(hasContainerLikeDisplay(matcherDataFromCss('display: block'))).toBe(true)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('returns true for display: inline-block', () => {
|
|
93
|
+
expect(hasContainerLikeDisplay(matcherDataFromCss('display: inline-block'))).toBe(true)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('returns false for display: inline', () => {
|
|
97
|
+
expect(hasContainerLikeDisplay(matcherDataFromCss('display: inline'))).toBe(false)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('returns true when var cannot be resolved', () => {
|
|
101
|
+
expect(hasContainerLikeDisplay(matcherDataFromCss('display: var(--d)'))).toBe(true)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('returns false when a missing local var falls back to inline', () => {
|
|
105
|
+
expect(hasContainerLikeDisplay(matcherDataFromCss('display: var(--d, inline)'))).toBe(false)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('returns true when a local custom property chain falls back to inline-flex', () => {
|
|
109
|
+
expect(
|
|
110
|
+
hasContainerLikeDisplay(
|
|
111
|
+
matcherDataFromCss(
|
|
112
|
+
'display: var(--component-display, inline); --component-display: var(--theme-display, inline-flex)',
|
|
113
|
+
),
|
|
114
|
+
),
|
|
115
|
+
).toBe(true)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('returns false when a locally defined cyclic var falls back to inline', () => {
|
|
119
|
+
expect(hasContainerLikeDisplay(matcherDataFromCss('display: var(--d, inline); --d: var(--d)'))).toBe(false)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('returns false when a cyclic custom property has its own inner fallback but display falls back to inline', () => {
|
|
123
|
+
expect(hasContainerLikeDisplay(matcherDataFromCss('display: var(--d, inline); --d: var(--d, inline-flex)'))).toBe(
|
|
124
|
+
false,
|
|
125
|
+
)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('uses the last matched display declaration when selectors conflict', () => {
|
|
129
|
+
const matcherData: MatchedCssData = {
|
|
130
|
+
matches: [
|
|
131
|
+
{ selector: '.label', properties: [{ name: 'display', value: 'block' }], specificity: [0, 1, 0] },
|
|
132
|
+
{ selector: '.label.compact', properties: [{ name: 'display', value: 'inline' }], specificity: [0, 2, 0] },
|
|
133
|
+
],
|
|
134
|
+
customProperties: {},
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
expect(hasContainerLikeDisplay(matcherData)).toBe(false)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('uses selector specificity before source order when display rules conflict', () => {
|
|
141
|
+
const matcherData: MatchedCssData = {
|
|
142
|
+
matches: [
|
|
143
|
+
{ selector: '.card.compact', properties: [{ name: 'display', value: 'flex' }], specificity: [0, 2, 0] },
|
|
144
|
+
{ selector: '.compact', properties: [{ name: 'display', value: 'inline' }], specificity: [0, 1, 0] },
|
|
145
|
+
],
|
|
146
|
+
customProperties: {},
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
expect(hasContainerLikeDisplay(matcherData)).toBe(true)
|
|
150
|
+
expect(hasFlexOrGridDisplay(matcherData)).toBe(true)
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
describe('getCssPropertiesForTag', () => {
|
|
155
|
+
it('treats ul as a box-generating element', () => {
|
|
156
|
+
const relevantProperties = getCssPropertiesForTag('ul')
|
|
157
|
+
|
|
158
|
+
expect(relevantProperties).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
159
|
+
expect(relevantProperties).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('treats ol as a box-generating element', () => {
|
|
163
|
+
const relevantProperties = getCssPropertiesForTag('ol')
|
|
164
|
+
|
|
165
|
+
expect(relevantProperties).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
166
|
+
expect(relevantProperties).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('treats table cells as box-generating elements', () => {
|
|
170
|
+
const relevantProperties = getCssPropertiesForTag('td')
|
|
171
|
+
|
|
172
|
+
expect(relevantProperties).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
173
|
+
expect(relevantProperties).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('derives box properties from default display for controls', () => {
|
|
177
|
+
const relevantProperties = getCssPropertiesForTag('button')
|
|
178
|
+
|
|
179
|
+
expect(relevantProperties).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
180
|
+
expect(relevantProperties).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('keeps media elements out of box properties even when their default display is non-inline', () => {
|
|
184
|
+
const relevantProperties = getCssPropertiesForTag('img')
|
|
185
|
+
|
|
186
|
+
expect(relevantProperties).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.borderTop)
|
|
187
|
+
expect(relevantProperties).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.background)
|
|
188
|
+
expect(relevantProperties).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('composes text and box properties for semantic text with box-generating default display', () => {
|
|
192
|
+
const relevantProperties = getCssPropertiesForTag('p')
|
|
193
|
+
|
|
194
|
+
expect(relevantProperties).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
195
|
+
expect(relevantProperties).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('falls back to box properties for unknown non-media tags', () => {
|
|
199
|
+
const relevantProperties = getCssPropertiesForTag('svg')
|
|
200
|
+
|
|
201
|
+
expect(relevantProperties).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
202
|
+
expect(relevantProperties).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
describe('getDefaultDisplayForTag', () => {
|
|
207
|
+
it('returns UA defaults for representative structural elements', () => {
|
|
208
|
+
expect(getDefaultDisplayForTag('html')).toBe('block')
|
|
209
|
+
expect(getDefaultDisplayForTag('legend')).toBe('block')
|
|
210
|
+
expect(getDefaultDisplayForTag('li')).toBe('list-item')
|
|
211
|
+
expect(getDefaultDisplayForTag('table')).toBe('table')
|
|
212
|
+
expect(getDefaultDisplayForTag('tbody')).toBe('table-row-group')
|
|
213
|
+
expect(getDefaultDisplayForTag('td')).toBe('table-cell')
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('returns UA defaults for representative inline and replaced elements', () => {
|
|
217
|
+
expect(getDefaultDisplayForTag('a')).toBe('inline')
|
|
218
|
+
expect(getDefaultDisplayForTag('q')).toBe('inline')
|
|
219
|
+
expect(getDefaultDisplayForTag('img')).toBe('inline')
|
|
220
|
+
expect(getDefaultDisplayForTag('iframe')).toBe('inline')
|
|
221
|
+
expect(getDefaultDisplayForTag('input')).toBe('inline-block')
|
|
222
|
+
expect(getDefaultDisplayForTag('textarea')).toBe('inline-block')
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('returns UA defaults for non-rendered metadata elements', () => {
|
|
226
|
+
expect(getDefaultDisplayForTag('head')).toBe('none')
|
|
227
|
+
expect(getDefaultDisplayForTag('meta')).toBe('none')
|
|
228
|
+
expect(getDefaultDisplayForTag('script')).toBe('none')
|
|
229
|
+
expect(getDefaultDisplayForTag('template')).toBe('none')
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('falls back to block for unknown tags', () => {
|
|
233
|
+
expect(getDefaultDisplayForTag('custom-element')).toBe('block')
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
describe('enrichContainerProperties', () => {
|
|
238
|
+
it('adds box properties to span when display becomes block', () => {
|
|
239
|
+
const elements = [createElementWithCssProperties('span', 'display: block')]
|
|
240
|
+
|
|
241
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
242
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
243
|
+
|
|
244
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
245
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it('does not add box properties to span when display stays inline', () => {
|
|
249
|
+
const elements = [createElementWithCssProperties('span', 'display: inline')]
|
|
250
|
+
|
|
251
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
252
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
253
|
+
|
|
254
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
255
|
+
expect(cssData.relevant).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('adds box properties to li when it relies on its default list-item display', () => {
|
|
259
|
+
const elements = [createElementWithCssProperties('li', '')]
|
|
260
|
+
|
|
261
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
262
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
263
|
+
|
|
264
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
265
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('adds box properties to strong when display becomes inline-flex', () => {
|
|
269
|
+
const elements = [createElementWithCssProperties('strong', 'display: inline-flex')]
|
|
270
|
+
|
|
271
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
272
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
273
|
+
|
|
274
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
275
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('does not add box properties when display var is missing locally and falls back to inline', () => {
|
|
279
|
+
const elements = [createElementWithCssProperties('span', 'display: var(--display, inline)')]
|
|
280
|
+
|
|
281
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
282
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
283
|
+
|
|
284
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
285
|
+
expect(cssData.relevant).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
it('adds box properties when a local custom property chain falls back to inline-flex', () => {
|
|
289
|
+
const elements = [
|
|
290
|
+
createElementWithCssProperties(
|
|
291
|
+
'span',
|
|
292
|
+
'display: var(--component-display, inline); --component-display: var(--theme-display, inline-flex)',
|
|
293
|
+
),
|
|
294
|
+
]
|
|
295
|
+
|
|
296
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
297
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
298
|
+
|
|
299
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
300
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('does not add box properties when a locally defined cyclic display var falls back to inline', () => {
|
|
304
|
+
const elements = [
|
|
305
|
+
createElementWithCssProperties('span', 'display: var(--display, inline); --display: var(--display)'),
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
309
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
310
|
+
|
|
311
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
312
|
+
expect(cssData.relevant).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('does not add box properties when a cyclic display var has its own inner fallback but display falls back to inline', () => {
|
|
316
|
+
const elements = [
|
|
317
|
+
createElementWithCssProperties('span', 'display: var(--display, inline); --display: var(--display, inline-flex)'),
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
321
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
322
|
+
|
|
323
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
324
|
+
expect(cssData.relevant).not.toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
it('adds box properties to role-based text elements when display becomes inline-flex', () => {
|
|
328
|
+
const elements = [createElementWithCssProperties('div', 'display: inline-flex', { role: 'heading' })]
|
|
329
|
+
|
|
330
|
+
const [enrichedElement] = enrichContainerProperties(elements)
|
|
331
|
+
const cssData = enrichedElement.extractorData.get('css-properties') as { relevant: string[] }
|
|
332
|
+
|
|
333
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.font)
|
|
334
|
+
expect(cssData.relevant).toContain(CSS_PROPERTIES.CSS_PROPERTY_TYPE.paddingTop)
|
|
335
|
+
})
|
|
336
|
+
})
|