@wix/zero-config-implementation 1.96.0 → 1.98.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/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"registry": "https://registry.npmjs.org/",
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "1.
|
|
7
|
+
"version": "1.98.0",
|
|
8
8
|
"description": "Core library for extracting component manifests from JS and CSS files",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"main": "dist/index.js",
|
|
@@ -106,5 +106,5 @@
|
|
|
106
106
|
]
|
|
107
107
|
}
|
|
108
108
|
},
|
|
109
|
-
"falconPackageHash": "
|
|
109
|
+
"falconPackageHash": "741408430f509ba8b4ac3b6f2ff22c4fae1bf06d9f8b98e2a9e8e079"
|
|
110
110
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { DATA } from '@wix/react-component-schema'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import type { PropWriteInfo, TrackingStores } from '../information-extractors/react/types'
|
|
4
|
+
import { collectUsedA11yAttributes } from './a11y-builder'
|
|
5
|
+
|
|
6
|
+
function propUsagesFor(propPaths: string[]): TrackingStores['propUsages'] {
|
|
7
|
+
const emptyWriteInfo = (): PropWriteInfo => ({ elements: new Map(), attributes: new Map() })
|
|
8
|
+
return new Map(propPaths.map((propPath) => [propPath, emptyWriteInfo()]))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('collectUsedA11yAttributes', () => {
|
|
12
|
+
it('returns an empty array when propUsages or propPath is unavailable', () => {
|
|
13
|
+
expect(collectUsedA11yAttributes(undefined, 'props.a11y')).toEqual([])
|
|
14
|
+
expect(collectUsedA11yAttributes(propUsagesFor(['props.a11y.role']), undefined)).toEqual([])
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('returns an empty array when no accessibility fields are recorded', () => {
|
|
18
|
+
expect(collectUsedA11yAttributes(propUsagesFor([]), 'props.a11y')).toEqual([])
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('allows only the current editor-configurable fields', () => {
|
|
22
|
+
const allAttributes = Object.values(DATA.A11Y_ATTRIBUTES).map((attribute) => `props.a11y.${attribute}`)
|
|
23
|
+
|
|
24
|
+
expect(collectUsedA11yAttributes(propUsagesFor(allAttributes), 'props.a11y')).toEqual(['ariaLabel', 'tag'])
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('keeps allowed fields in schema order', () => {
|
|
28
|
+
const propUsages = propUsagesFor(['props.a11y.tag', 'props.a11y.role', 'props.a11y.ariaLabel'])
|
|
29
|
+
|
|
30
|
+
expect(collectUsedA11yAttributes(propUsages, 'props.a11y')).toEqual(['ariaLabel', 'tag'])
|
|
31
|
+
})
|
|
32
|
+
})
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import { DATA } from '@wix/react-component-schema'
|
|
2
2
|
import type { TrackingStores } from '../information-extractors/react'
|
|
3
3
|
|
|
4
|
+
type A11yAttribute = (typeof DATA.A11Y_ATTRIBUTES)[keyof typeof DATA.A11Y_ATTRIBUTES]
|
|
5
|
+
|
|
6
|
+
const EDITOR_CONFIGURABLE_A11Y_ATTRIBUTES: ReadonlySet<A11yAttribute> = new Set([
|
|
7
|
+
DATA.A11Y_ATTRIBUTES.ariaLabel,
|
|
8
|
+
DATA.A11Y_ATTRIBUTES.tag,
|
|
9
|
+
])
|
|
10
|
+
|
|
4
11
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* unavailable, returns an empty array — per the schema, an empty list means
|
|
9
|
-
* "all possible A11Y values", which is a safe default.
|
|
12
|
+
* Returns the editor-configurable attributes the component reads, in schema
|
|
13
|
+
* order. The caller must omit the a11y data item when this returns an empty
|
|
14
|
+
* list because the manifest schema interprets an empty list as all attributes.
|
|
10
15
|
*/
|
|
11
16
|
export function collectUsedA11yAttributes(
|
|
12
17
|
propUsages: TrackingStores['propUsages'] | undefined,
|
|
13
18
|
propPath: string | undefined,
|
|
14
|
-
):
|
|
19
|
+
): A11yAttribute[] {
|
|
15
20
|
if (!propUsages || !propPath) return []
|
|
16
21
|
|
|
17
|
-
const
|
|
22
|
+
const usedAttributes: A11yAttribute[] = []
|
|
18
23
|
for (const attribute of Object.values(DATA.A11Y_ATTRIBUTES)) {
|
|
19
|
-
if (attribute
|
|
24
|
+
if (!EDITOR_CONFIGURABLE_A11Y_ATTRIBUTES.has(attribute)) continue
|
|
20
25
|
if (propUsages.has(`${propPath}.${attribute}`)) {
|
|
21
|
-
|
|
26
|
+
usedAttributes.push(attribute)
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
|
-
|
|
29
|
+
|
|
30
|
+
return usedAttributes
|
|
25
31
|
}
|
|
@@ -43,17 +43,71 @@ function createElementWithDisplayMatcher(tag: string, matcherData: MatchedCssDat
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function createComponent(rootElement: ExtractedElement): ComponentInfoWithCss {
|
|
46
|
+
function createComponent(rootElement: ExtractedElement, renderDisplayElementAsChild = false): ComponentInfoWithCss {
|
|
47
|
+
const componentRoot = renderDisplayElementAsChild
|
|
48
|
+
? {
|
|
49
|
+
traceId: 'trace-container',
|
|
50
|
+
name: 'container',
|
|
51
|
+
tag: 'div',
|
|
52
|
+
attributes: {},
|
|
53
|
+
extractorData: new Map<string, unknown>(),
|
|
54
|
+
children: [{ ...rootElement, traceId: 'trace-display', name: 'display' }],
|
|
55
|
+
}
|
|
56
|
+
: rootElement
|
|
57
|
+
|
|
47
58
|
return {
|
|
48
59
|
componentName: 'DisplayComponent',
|
|
49
60
|
props: {},
|
|
50
|
-
elements: [
|
|
61
|
+
elements: [componentRoot],
|
|
51
62
|
propUsages: new Map(),
|
|
52
63
|
css: [],
|
|
53
64
|
varUsedByTraceId: new Map(),
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
|
|
68
|
+
function createComponentWithA11yUsages(propPaths: string[]): ComponentInfoWithCss {
|
|
69
|
+
return {
|
|
70
|
+
...createComponent(createElementWithDisplayMatcher('div', matcherDataFromCss(''))),
|
|
71
|
+
props: {
|
|
72
|
+
a11y: {
|
|
73
|
+
name: 'a11y',
|
|
74
|
+
type: 'A11y',
|
|
75
|
+
required: false,
|
|
76
|
+
resolvedType: { kind: 'semantic', value: 'A11y', source: '@wix/editor-react-types' },
|
|
77
|
+
propPath: 'props.a11y',
|
|
78
|
+
logicOnly: false,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
propUsages: new Map(propPaths.map((propPath) => [propPath, { elements: new Map(), attributes: new Map() }])),
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getDisplayProperty(result: ReturnType<typeof toEditorReactComponent>) {
|
|
86
|
+
return result.editorElement?.elements?.display?.inlineElement?.cssProperties?.display
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
describe('toEditorReactComponent accessibility conversion', () => {
|
|
90
|
+
it('omits the a11y item when the component reads only component-owned fields', () => {
|
|
91
|
+
const component = createComponentWithA11yUsages(['props.a11y.role', 'props.a11y.tabIndex'])
|
|
92
|
+
|
|
93
|
+
expect(toEditorReactComponent(component).editorElement?.data?.a11y).toBeUndefined()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('emits only the allowed fields when reads are mixed', () => {
|
|
97
|
+
const component = createComponentWithA11yUsages([
|
|
98
|
+
'props.a11y.role',
|
|
99
|
+
'props.a11y.ariaLabel',
|
|
100
|
+
'props.a11y.ariaExpanded',
|
|
101
|
+
'props.a11y.tag',
|
|
102
|
+
])
|
|
103
|
+
|
|
104
|
+
expect(toEditorReactComponent(component).editorElement?.data?.a11y).toMatchObject({
|
|
105
|
+
dataType: 'a11y',
|
|
106
|
+
a11y: { attributes: ['ariaLabel', 'tag'] },
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
57
111
|
function createSemanticElement({
|
|
58
112
|
traceId,
|
|
59
113
|
name,
|
|
@@ -76,6 +130,14 @@ function createSemanticElement({
|
|
|
76
130
|
}
|
|
77
131
|
|
|
78
132
|
describe('toEditorReactComponent display conversion', () => {
|
|
133
|
+
it('omits display from the root editor element', () => {
|
|
134
|
+
const rootElement = createElementWithDisplayMatcher('div', matcherDataFromCss('display: flex'))
|
|
135
|
+
|
|
136
|
+
const result = toEditorReactComponent(createComponent(rootElement))
|
|
137
|
+
|
|
138
|
+
expect(result.editorElement?.cssProperties?.display).toBeUndefined()
|
|
139
|
+
})
|
|
140
|
+
|
|
79
141
|
it('resolves local display fallback chains before emitting the manifest display value', () => {
|
|
80
142
|
const rootElement = createElementWithDisplayMatcher(
|
|
81
143
|
'span',
|
|
@@ -84,10 +146,10 @@ describe('toEditorReactComponent display conversion', () => {
|
|
|
84
146
|
),
|
|
85
147
|
)
|
|
86
148
|
|
|
87
|
-
const result = toEditorReactComponent(createComponent(rootElement))
|
|
149
|
+
const result = toEditorReactComponent(createComponent(rootElement, true))
|
|
88
150
|
expect(result.editorElement).toBeDefined()
|
|
89
151
|
|
|
90
|
-
expect(result
|
|
152
|
+
expect(getDisplayProperty(result)).toEqual({
|
|
91
153
|
display: {
|
|
92
154
|
displayValues: [CSS_PROPERTIES.DISPLAY_VALUE.none, 'inlineFlex'],
|
|
93
155
|
},
|
|
@@ -97,10 +159,10 @@ describe('toEditorReactComponent display conversion', () => {
|
|
|
97
159
|
it('keeps using the tag default when no display declaration exists', () => {
|
|
98
160
|
const rootElement = createElementWithDisplayMatcher('span', matcherDataFromCss(''))
|
|
99
161
|
|
|
100
|
-
const result = toEditorReactComponent(createComponent(rootElement))
|
|
162
|
+
const result = toEditorReactComponent(createComponent(rootElement, true))
|
|
101
163
|
expect(result.editorElement).toBeDefined()
|
|
102
164
|
|
|
103
|
-
expect(result
|
|
165
|
+
expect(getDisplayProperty(result)).toEqual({
|
|
104
166
|
display: {
|
|
105
167
|
displayValues: [CSS_PROPERTIES.DISPLAY_VALUE.none, 'inline'],
|
|
106
168
|
},
|
|
@@ -127,9 +189,9 @@ describe('toEditorReactComponent display conversion', () => {
|
|
|
127
189
|
it.each(tagDefaults)('maps a <%s> tag default (%s) onto its enum name', (tag, _cssDisplay, expectedEnumValue) => {
|
|
128
190
|
const rootElement = createElementWithDisplayMatcher(tag, matcherDataFromCss(''))
|
|
129
191
|
|
|
130
|
-
const result = toEditorReactComponent(createComponent(rootElement))
|
|
192
|
+
const result = toEditorReactComponent(createComponent(rootElement, true))
|
|
131
193
|
|
|
132
|
-
expect(result
|
|
194
|
+
expect(getDisplayProperty(result)).toEqual({
|
|
133
195
|
display: {
|
|
134
196
|
displayValues: [CSS_PROPERTIES.DISPLAY_VALUE.none, expectedEnumValue],
|
|
135
197
|
},
|
|
@@ -138,14 +200,14 @@ describe('toEditorReactComponent display conversion', () => {
|
|
|
138
200
|
|
|
139
201
|
it('treats an explicit `display: table-cell` declaration the same as the tag default', () => {
|
|
140
202
|
const fromTag = toEditorReactComponent(
|
|
141
|
-
createComponent(createElementWithDisplayMatcher('td', matcherDataFromCss(''))),
|
|
203
|
+
createComponent(createElementWithDisplayMatcher('td', matcherDataFromCss('')), true),
|
|
142
204
|
)
|
|
143
205
|
const fromCss = toEditorReactComponent(
|
|
144
|
-
createComponent(createElementWithDisplayMatcher('div', matcherDataFromCss('display: table-cell'))),
|
|
206
|
+
createComponent(createElementWithDisplayMatcher('div', matcherDataFromCss('display: table-cell')), true),
|
|
145
207
|
)
|
|
146
208
|
|
|
147
|
-
expect(fromCss
|
|
148
|
-
expect(fromCss
|
|
209
|
+
expect(getDisplayProperty(fromCss)).toEqual(getDisplayProperty(fromTag))
|
|
210
|
+
expect(getDisplayProperty(fromCss)).toEqual({
|
|
149
211
|
display: {
|
|
150
212
|
displayValues: [CSS_PROPERTIES.DISPLAY_VALUE.none, CSS_PROPERTIES.DISPLAY_VALUE.tableCell],
|
|
151
213
|
},
|
|
@@ -155,9 +217,9 @@ describe('toEditorReactComponent display conversion', () => {
|
|
|
155
217
|
it('still maps kebab-case values that do have an enum representation', () => {
|
|
156
218
|
const rootElement = createElementWithDisplayMatcher('div', matcherDataFromCss('display: inline-block'))
|
|
157
219
|
|
|
158
|
-
const result = toEditorReactComponent(createComponent(rootElement))
|
|
220
|
+
const result = toEditorReactComponent(createComponent(rootElement, true))
|
|
159
221
|
|
|
160
|
-
expect(result
|
|
222
|
+
expect(getDisplayProperty(result)).toEqual({
|
|
161
223
|
display: {
|
|
162
224
|
displayValues: [CSS_PROPERTIES.DISPLAY_VALUE.none, CSS_PROPERTIES.DISPLAY_VALUE.inlineBlock],
|
|
163
225
|
},
|
|
@@ -169,8 +231,8 @@ describe('toEditorReactComponent display conversion', () => {
|
|
|
169
231
|
|
|
170
232
|
for (const [tag] of tagDefaults) {
|
|
171
233
|
const rootElement = createElementWithDisplayMatcher(tag, matcherDataFromCss(''))
|
|
172
|
-
const result = toEditorReactComponent(createComponent(rootElement))
|
|
173
|
-
const emittedValues = result
|
|
234
|
+
const result = toEditorReactComponent(createComponent(rootElement, true))
|
|
235
|
+
const emittedValues = getDisplayProperty(result)?.display?.displayValues ?? []
|
|
174
236
|
|
|
175
237
|
expect(emittedValues.filter((displayValue) => !allowedValues.has(displayValue))).toEqual([])
|
|
176
238
|
}
|
|
@@ -195,9 +257,9 @@ describe('toEditorReactComponent display conversion', () => {
|
|
|
195
257
|
'div',
|
|
196
258
|
matcherDataFromCss(`display: ${toCssSpelling(enumValue)}`),
|
|
197
259
|
)
|
|
198
|
-
const result = toEditorReactComponent(createComponent(rootElement))
|
|
260
|
+
const result = toEditorReactComponent(createComponent(rootElement, true))
|
|
199
261
|
|
|
200
|
-
return result
|
|
262
|
+
return getDisplayProperty(result)?.display?.displayValues?.[1] !== enumValue
|
|
201
263
|
})
|
|
202
264
|
|
|
203
265
|
expect(unreachable).toEqual([])
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
ElementItem,
|
|
11
11
|
States,
|
|
12
12
|
} from '@wix/react-component-schema'
|
|
13
|
-
import { CSS_PROPERTIES, ELEMENTS } from '@wix/react-component-schema'
|
|
13
|
+
import { CSS_PROPERTIES, DATA, ELEMENTS } from '@wix/react-component-schema'
|
|
14
14
|
import { camelCase } from 'case-anything'
|
|
15
15
|
import { IoError } from '../errors'
|
|
16
16
|
import { buildRefElementManifestKey } from '../extensions/ref-elements/path-utils'
|
|
@@ -127,7 +127,7 @@ function buildEditorElement(
|
|
|
127
127
|
component.innerElementProps,
|
|
128
128
|
component.propUsages,
|
|
129
129
|
),
|
|
130
|
-
cssProperties: buildCssProperties(rootElement),
|
|
130
|
+
cssProperties: buildCssProperties(rootElement, true),
|
|
131
131
|
cssCustomProperties: rootCustomProps,
|
|
132
132
|
...(rootStates && { states: rootStates }),
|
|
133
133
|
...(displayGroups && { displayGroups }),
|
|
@@ -184,6 +184,7 @@ function buildData(
|
|
|
184
184
|
|
|
185
185
|
const result = buildDataItem(prop, defaultValue, propUsages, prop.propPath)
|
|
186
186
|
if (result.isErr()) throw result.error
|
|
187
|
+
if (result.value.dataType === DATA.DATA_TYPE.a11y && !result.value.a11y?.attributes?.length) continue
|
|
187
188
|
// ActiveItemIndex props default to 0 when no default is specified
|
|
188
189
|
if (prop.activeItemIndexTarget && result.value.defaultValue === undefined) {
|
|
189
190
|
result.value.defaultValue = 0
|
|
@@ -223,7 +224,7 @@ function buildElements(
|
|
|
223
224
|
const semanticClass = getSemanticBlockName(element)
|
|
224
225
|
|
|
225
226
|
const data = innerEntry && propUsages ? buildData(innerEntry.props, propUsages) : undefined
|
|
226
|
-
const cssProps = buildCssProperties(element)
|
|
227
|
+
const cssProps = buildCssProperties(element, false)
|
|
227
228
|
const cssCustomProps = nearestCommonAncestorCustomProps.get(element.traceId) ?? {}
|
|
228
229
|
|
|
229
230
|
// Inner-element custom states come from custom-class triggers only; prop
|
|
@@ -562,7 +563,10 @@ function toDisplayEnumValue(cssValue: string): DisplayEnumValue {
|
|
|
562
563
|
return (CSS_DISPLAY_TO_ENUM[cssValue] ?? cssValue) as DisplayEnumValue
|
|
563
564
|
}
|
|
564
565
|
|
|
565
|
-
function buildCssProperties(
|
|
566
|
+
function buildCssProperties(
|
|
567
|
+
element: ExtractedElement | undefined,
|
|
568
|
+
isRootElement: boolean,
|
|
569
|
+
): Record<string, CssPropertyItem> {
|
|
566
570
|
const result: Record<string, CssPropertyItem> = {}
|
|
567
571
|
|
|
568
572
|
const cssData = element?.extractorData.get('css-properties') as CssPropertiesData | undefined
|
|
@@ -573,7 +577,9 @@ function buildCssProperties(element: ExtractedElement | undefined): Record<strin
|
|
|
573
577
|
const cssPropertyValues = element ? getMatchedPropertyValues(element) : new Map<string, string>()
|
|
574
578
|
for (const propName of decidedProperties) {
|
|
575
579
|
if (propName === CSS_PROPERTY_TYPE.display && element) {
|
|
576
|
-
|
|
580
|
+
if (!isRootElement) {
|
|
581
|
+
result[propName] = buildDisplayProperty(element)
|
|
582
|
+
}
|
|
577
583
|
continue
|
|
578
584
|
}
|
|
579
585
|
const resolvedValue = resolveLonghand(propName, cssPropertyValues)
|