@teleporthq/teleport-project-generator-html 0.44.11 → 0.44.15
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.
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FileType,
|
|
3
|
+
ProjectUIDL,
|
|
4
|
+
UIDLConditionalNode,
|
|
5
|
+
UIDLElementNode,
|
|
6
|
+
UIDLElementNodeProjectReferencedStyle,
|
|
7
|
+
} from '@teleporthq/teleport-types'
|
|
8
|
+
import uidlSample from '../../../../examples/uidl-samples/component-variants.json'
|
|
9
|
+
import { createHTMLProjectGenerator, pluginCloneGlobals, pluginHomeReplace } from '../../src'
|
|
10
|
+
import HTMLTemplate from '../../src/project-template'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A button component with four looks: `btn` always, plus `btn-<look>` while
|
|
14
|
+
* the `buttonType` prop (default "primary") names it, and `btn-full` while the
|
|
15
|
+
* boolean `fullWidth` prop is true. The HTML export has no runtime props, so
|
|
16
|
+
* every placed instance must leave the build with its own static class list.
|
|
17
|
+
*/
|
|
18
|
+
const homePage = async (uidl: ProjectUIDL): Promise<string> => {
|
|
19
|
+
const generator = createHTMLProjectGenerator()
|
|
20
|
+
generator.addPlugin(pluginHomeReplace)
|
|
21
|
+
generator.addPlugin(pluginCloneGlobals)
|
|
22
|
+
const { files } = await generator.generateProject(uidl, HTMLTemplate)
|
|
23
|
+
return (
|
|
24
|
+
files.find((file) => file.name === 'index' && file.fileType === FileType.HTML)?.content ?? ''
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const buttonsByLabel = async (uidl: ProjectUIDL): Promise<Record<string, string>> => {
|
|
29
|
+
const page = await homePage(uidl)
|
|
30
|
+
const button = /<button\b[^>]*>\s*<span>([^<]*)<\/span>([\s\S]*?)<\/button>/g
|
|
31
|
+
const contents: Record<string, string> = {}
|
|
32
|
+
for (let match = button.exec(page); match; match = button.exec(page)) {
|
|
33
|
+
contents[match[1]] = match[2]
|
|
34
|
+
}
|
|
35
|
+
return contents
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const classesByLabel = async (uidl: ProjectUIDL): Promise<Record<string, string>> => {
|
|
39
|
+
const page = await homePage(uidl)
|
|
40
|
+
const button = /<button\s+class="([^"]*)"[^>]*>\s*<span>([^<]*)<\/span>/g
|
|
41
|
+
const classes: Record<string, string> = {}
|
|
42
|
+
for (let match = button.exec(page); match; match = button.exec(page)) {
|
|
43
|
+
classes[match[2]] = match[1].replace(/\s+/g, ' ')
|
|
44
|
+
}
|
|
45
|
+
return classes
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const fixture = () => JSON.parse(JSON.stringify(uidlSample)) as ProjectUIDL
|
|
49
|
+
|
|
50
|
+
const homeOf = (uidl: ProjectUIDL) => {
|
|
51
|
+
const home = (uidl.root.node.content.children[0] as UIDLConditionalNode).content.node
|
|
52
|
+
if (home.type !== 'element') {
|
|
53
|
+
throw new Error('fixture drifted: Home is not an element')
|
|
54
|
+
}
|
|
55
|
+
return home
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const placeButton = (uidl: ProjectUIDL, key: string, attrs: Record<string, unknown>) =>
|
|
59
|
+
homeOf(uidl).content.children.push({
|
|
60
|
+
type: 'element',
|
|
61
|
+
content: {
|
|
62
|
+
elementType: 'ActionButton',
|
|
63
|
+
semanticType: 'ActionButton',
|
|
64
|
+
key,
|
|
65
|
+
name: key,
|
|
66
|
+
dependency: { type: 'local' },
|
|
67
|
+
attrs: Object.fromEntries(
|
|
68
|
+
Object.entries(attrs).map(([name, content]) => [name, { type: 'static', content }])
|
|
69
|
+
),
|
|
70
|
+
children: [],
|
|
71
|
+
},
|
|
72
|
+
} as UIDLElementNode)
|
|
73
|
+
|
|
74
|
+
const classWhen = (
|
|
75
|
+
referenceId: string,
|
|
76
|
+
propId: string,
|
|
77
|
+
operands: string[]
|
|
78
|
+
): UIDLElementNodeProjectReferencedStyle => ({
|
|
79
|
+
type: 'style-map',
|
|
80
|
+
content: {
|
|
81
|
+
mapType: 'project-referenced',
|
|
82
|
+
referenceId,
|
|
83
|
+
condition: {
|
|
84
|
+
reference: { type: 'dynamic', content: { referenceType: 'prop', id: propId } },
|
|
85
|
+
expression: { conditions: operands.map((operand) => ({ operation: '===', operand })) },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('Component variants in the HTML export', () => {
|
|
91
|
+
it('resolves every instance to the base class and exactly its own look', async () => {
|
|
92
|
+
expect(await classesByLabel(fixture())).toEqual({
|
|
93
|
+
Primary: 'btn btn-primary',
|
|
94
|
+
Secondary: 'btn btn-secondary',
|
|
95
|
+
Outline: 'btn btn-outline',
|
|
96
|
+
Link: 'btn btn-link',
|
|
97
|
+
Unset: 'btn btn-primary',
|
|
98
|
+
'Full width': 'btn btn-outline btn-full',
|
|
99
|
+
'Not full width': 'btn btn-primary',
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('builds a look without a default, a look holding quotes, and dangling conditions', async () => {
|
|
104
|
+
const uidl = fixture()
|
|
105
|
+
const actionButton = uidl.components.ActionButton
|
|
106
|
+
delete actionButton.propDefinitions.buttonType.defaultValue
|
|
107
|
+
const { referencedStyles } = actionButton.node.content
|
|
108
|
+
referencedStyles.quoted = classWhen('btn-full', 'buttonType', ['say "hi"'])
|
|
109
|
+
referencedStyles.deletedProp = classWhen('btn-link', 'removedProp', ['x'])
|
|
110
|
+
referencedStyles.noRule = classWhen('btn-secondary', 'buttonType', [])
|
|
111
|
+
placeButton(uidl, 'quotedButton', { buttonType: 'say "hi"', label: 'Quoted' })
|
|
112
|
+
|
|
113
|
+
expect(await classesByLabel(uidl)).toEqual({
|
|
114
|
+
Primary: 'btn btn-primary',
|
|
115
|
+
Secondary: 'btn btn-secondary',
|
|
116
|
+
Outline: 'btn btn-outline',
|
|
117
|
+
Link: 'btn btn-link',
|
|
118
|
+
Unset: 'btn',
|
|
119
|
+
'Full width': 'btn btn-outline btn-full',
|
|
120
|
+
'Not full width': 'btn',
|
|
121
|
+
Quoted: 'btn btn-full',
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('shows an element bound to a number prop whose rule stores the number as text', async () => {
|
|
126
|
+
const uidl = fixture()
|
|
127
|
+
const actionButton = uidl.components.ActionButton
|
|
128
|
+
actionButton.propDefinitions.count = { type: 'number', defaultValue: 0 }
|
|
129
|
+
actionButton.node.content.children.push({
|
|
130
|
+
type: 'conditional',
|
|
131
|
+
content: {
|
|
132
|
+
reference: { type: 'dynamic', content: { referenceType: 'prop', id: 'count' } },
|
|
133
|
+
condition: { conditions: [{ operation: '===', operand: '2' }] },
|
|
134
|
+
node: {
|
|
135
|
+
type: 'element',
|
|
136
|
+
content: {
|
|
137
|
+
elementType: 'text',
|
|
138
|
+
key: 'two-badge',
|
|
139
|
+
children: [{ type: 'static', content: 'Two' }],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
} as UIDLConditionalNode)
|
|
144
|
+
placeButton(uidl, 'countTwoButton', { count: 2, label: 'Count two' })
|
|
145
|
+
placeButton(uidl, 'countThreeButton', { count: 3, label: 'Count three' })
|
|
146
|
+
|
|
147
|
+
const buttons = await buttonsByLabel(uidl)
|
|
148
|
+
expect(buttons['Count two']).toContain('Two')
|
|
149
|
+
expect(buttons['Count three']).not.toContain('Two')
|
|
150
|
+
expect(buttons.Primary).not.toContain('Two')
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('applies a class on a number prop whose default and rule both store the number as text', async () => {
|
|
154
|
+
const uidl = fixture()
|
|
155
|
+
uidl.root.styleSetDefinitions['btn-size-two'] = uidl.root.styleSetDefinitions['btn-full']
|
|
156
|
+
const actionButton = uidl.components.ActionButton
|
|
157
|
+
actionButton.propDefinitions.size = { type: 'number', defaultValue: '2' }
|
|
158
|
+
actionButton.node.content.referencedStyles.sizeTwo = classWhen('btn-size-two', 'size', ['2'])
|
|
159
|
+
placeButton(uidl, 'sizeThreeButton', { size: 3, label: 'Size three' })
|
|
160
|
+
placeButton(uidl, 'sizeTwoTextButton', { size: '2', label: 'Size two as text' })
|
|
161
|
+
|
|
162
|
+
const classes = await classesByLabel(uidl)
|
|
163
|
+
expect(classes.Primary).toBe('btn btn-primary btn-size-two')
|
|
164
|
+
expect(classes['Size two as text']).toBe('btn btn-primary btn-size-two')
|
|
165
|
+
expect(classes['Size three']).toBe('btn btn-primary')
|
|
166
|
+
})
|
|
167
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teleporthq/teleport-project-generator-html",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.15",
|
|
4
4
|
"description": "Project generator for generate plain html files",
|
|
5
5
|
"author": "teleportHQ",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
"build": "tsc -p tsconfig.json && tsc -p tsconfig.json --module commonjs --outDir dist/cjs"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@teleporthq/teleport-component-generator": "^0.44.
|
|
28
|
-
"@teleporthq/teleport-component-generator-html": "^0.44.
|
|
29
|
-
"@teleporthq/teleport-plugin-css": "^0.44.
|
|
27
|
+
"@teleporthq/teleport-component-generator": "^0.44.15",
|
|
28
|
+
"@teleporthq/teleport-component-generator-html": "^0.44.15",
|
|
29
|
+
"@teleporthq/teleport-plugin-css": "^0.44.15",
|
|
30
30
|
"@teleporthq/teleport-postprocessor-prettier-html": "^0.44.11",
|
|
31
|
-
"@teleporthq/teleport-project-generator": "^0.44.
|
|
31
|
+
"@teleporthq/teleport-project-generator": "^0.44.15",
|
|
32
32
|
"@teleporthq/teleport-shared": "^0.44.11",
|
|
33
33
|
"@teleporthq/teleport-types": "^0.44.11",
|
|
34
34
|
"cheerio": "^1.0.0-rc.12",
|
|
35
35
|
"path-browserify": "^1.0.1"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "dbf1f4bff95181c3e2394634cf659af73d3ed279"
|
|
38
38
|
}
|