ripple 0.2.165 → 0.2.167
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 +2 -2
- package/src/compiler/phases/1-parse/index.js +30 -1
- package/src/compiler/phases/1-parse/style.js +36 -1
- package/src/compiler/phases/2-analyze/css-analyze.js +145 -0
- package/src/compiler/phases/2-analyze/index.js +7 -0
- package/src/compiler/phases/2-analyze/prune.js +165 -11
- package/src/compiler/phases/2-analyze/validation.js +156 -0
- package/src/compiler/phases/3-transform/client/index.js +62 -12
- package/src/compiler/phases/3-transform/stylesheet.js +102 -3
- package/src/runtime/internal/client/index.js +1 -0
- package/src/runtime/internal/client/operations.js +0 -6
- package/src/runtime/internal/client/render.js +22 -16
- package/tests/client/css/global-additional-cases.test.ripple +702 -0
- package/tests/client/css/global-advanced-selectors.test.ripple +229 -0
- package/tests/client/css/global-at-rules.test.ripple +126 -0
- package/tests/client/css/global-basic.test.ripple +165 -0
- package/tests/client/css/global-classes-ids.test.ripple +179 -0
- package/tests/client/css/global-combinators.test.ripple +124 -0
- package/tests/client/css/global-complex-nesting.test.ripple +221 -0
- package/tests/client/css/global-edge-cases.test.ripple +200 -0
- package/tests/client/css/global-keyframes.test.ripple +101 -0
- package/tests/client/css/global-nested.test.ripple +150 -0
- package/tests/client/css/global-pseudo.test.ripple +155 -0
- package/tests/client/css/global-scoping.test.ripple +229 -0
- package/tests/client/dynamic-elements.test.ripple +0 -1
- package/tests/server/streaming-ssr.test.ripple +9 -6
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { compile } from 'ripple/compiler';
|
|
2
|
+
|
|
3
|
+
describe('CSS :global with advanced selectors', () => {
|
|
4
|
+
it('handles :global with ::slotted()', () => {
|
|
5
|
+
const source = `
|
|
6
|
+
export component Test() {
|
|
7
|
+
<div>
|
|
8
|
+
<slot />
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<style>
|
|
12
|
+
:global(::slotted(*)) {
|
|
13
|
+
color: red;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
div :global(::slotted(span)) {
|
|
17
|
+
color: blue;
|
|
18
|
+
}
|
|
19
|
+
</style>
|
|
20
|
+
}`;
|
|
21
|
+
const { css } = compile(source, 'test.ripple');
|
|
22
|
+
|
|
23
|
+
expect(css).toMatch(/div\.ripple-[a-z0-9]+ ::slotted\(span\) {/);
|
|
24
|
+
expect(css).toContain('::slotted(*) {');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('handles :global with ::part()', () => {
|
|
28
|
+
const source = `
|
|
29
|
+
export component Test() {
|
|
30
|
+
<div>{'content'}</div>
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
:global(::part(button)) {
|
|
34
|
+
color: red;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
div :global(::part(input)) {
|
|
38
|
+
border: 1px solid blue;
|
|
39
|
+
}
|
|
40
|
+
</style>
|
|
41
|
+
}`;
|
|
42
|
+
const { css } = compile(source, 'test.ripple');
|
|
43
|
+
|
|
44
|
+
expect(css).toMatch(/div\.ripple-[a-z0-9]+ ::part\(input\) {/);
|
|
45
|
+
expect(css).toContain('::part(button) {');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('handles :global with :host and :host-context', () => {
|
|
49
|
+
const source = `
|
|
50
|
+
export component Test() {
|
|
51
|
+
<div>{'content'}</div>
|
|
52
|
+
|
|
53
|
+
<style>
|
|
54
|
+
:global(:host) {
|
|
55
|
+
display: block;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
:global(:host(.active)) {
|
|
59
|
+
color: red;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
:global(:host-context(.dark)) {
|
|
63
|
+
background: black;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
66
|
+
}`;
|
|
67
|
+
const { css } = compile(source, 'test.ripple');
|
|
68
|
+
|
|
69
|
+
expect(css).toContain(':host {');
|
|
70
|
+
expect(css).toContain(':host(.active) {');
|
|
71
|
+
expect(css).toContain(':host-context(.dark) {');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('handles :global with complex :nth-* selectors', () => {
|
|
75
|
+
const source = `
|
|
76
|
+
export component Test() {
|
|
77
|
+
<div>
|
|
78
|
+
<span>{'one'}</span>
|
|
79
|
+
<span>{'two'}</span>
|
|
80
|
+
<span>{'three'}</span>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<style>
|
|
84
|
+
:global(span:nth-child(odd)) {
|
|
85
|
+
color: red;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
:global(span:nth-last-child(2)) {
|
|
89
|
+
color: blue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
:global(div:nth-of-type(3n+1)) {
|
|
93
|
+
margin-top: 10px;
|
|
94
|
+
}
|
|
95
|
+
</style>
|
|
96
|
+
}`;
|
|
97
|
+
const { css } = compile(source, 'test.ripple');
|
|
98
|
+
|
|
99
|
+
expect(css).toContain('span:nth-child(odd) {');
|
|
100
|
+
expect(css).toContain('span:nth-last-child(2) {');
|
|
101
|
+
expect(css).toContain('div:nth-of-type(3n+1) {');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('handles :global with language and directional pseudo-classes', () => {
|
|
105
|
+
const source = `
|
|
106
|
+
export component Test() {
|
|
107
|
+
<div>{'content'}</div>
|
|
108
|
+
|
|
109
|
+
<style>
|
|
110
|
+
:global(:lang(en)) {
|
|
111
|
+
quotes: '"' '"';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
:global(:dir(rtl)) {
|
|
115
|
+
direction: rtl;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
:global(div:lang(fr)) {
|
|
119
|
+
quotes: '«' '»';
|
|
120
|
+
}
|
|
121
|
+
</style>
|
|
122
|
+
}`;
|
|
123
|
+
const { css } = compile(source, 'test.ripple');
|
|
124
|
+
|
|
125
|
+
expect(css).toContain(':lang(en) {');
|
|
126
|
+
expect(css).toContain(':dir(rtl) {');
|
|
127
|
+
expect(css).toContain('div:lang(fr) {');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('handles :global with :focus-within and :focus-visible', () => {
|
|
131
|
+
const source = `
|
|
132
|
+
export component Test() {
|
|
133
|
+
<div>
|
|
134
|
+
<input type="text" />
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<style>
|
|
138
|
+
:global(div:focus-within) {
|
|
139
|
+
border: 2px solid blue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
:global(input:focus-visible) {
|
|
143
|
+
outline: 2px solid red;
|
|
144
|
+
}
|
|
145
|
+
</style>
|
|
146
|
+
}`;
|
|
147
|
+
const { css } = compile(source, 'test.ripple');
|
|
148
|
+
|
|
149
|
+
expect(css).toContain('div:focus-within {');
|
|
150
|
+
expect(css).toContain('input:focus-visible {');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('handles :global with :any-link, :target, :scope', () => {
|
|
154
|
+
const source = `
|
|
155
|
+
export component Test() {
|
|
156
|
+
<div>
|
|
157
|
+
<a href="#">{'link'}</a>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<style>
|
|
161
|
+
:global(:any-link) {
|
|
162
|
+
color: blue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
:global(:target) {
|
|
166
|
+
background: yellow;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
:global(:scope) {
|
|
170
|
+
border: 1px solid black;
|
|
171
|
+
}
|
|
172
|
+
</style>
|
|
173
|
+
}`;
|
|
174
|
+
const { css } = compile(source, 'test.ripple');
|
|
175
|
+
|
|
176
|
+
expect(css).toContain(':any-link {');
|
|
177
|
+
expect(css).toContain(':target {');
|
|
178
|
+
expect(css).toContain(':scope {');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('handles :global with form-related pseudo-classes', () => {
|
|
182
|
+
const source = `
|
|
183
|
+
export component Test() {
|
|
184
|
+
<form>
|
|
185
|
+
<input type="text" required />
|
|
186
|
+
<input type="checkbox" checked />
|
|
187
|
+
</form>
|
|
188
|
+
|
|
189
|
+
<style>
|
|
190
|
+
:global(input:required) {
|
|
191
|
+
border-color: red;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
:global(input:optional) {
|
|
195
|
+
border-color: gray;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
:global(input:checked) {
|
|
199
|
+
background: blue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
:global(input:disabled) {
|
|
203
|
+
opacity: 0.5;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
:global(input:read-only) {
|
|
207
|
+
background: lightgray;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
:global(input:valid) {
|
|
211
|
+
border-color: green;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
:global(input:invalid) {
|
|
215
|
+
border-color: red;
|
|
216
|
+
}
|
|
217
|
+
</style>
|
|
218
|
+
}`;
|
|
219
|
+
const { css } = compile(source, 'test.ripple');
|
|
220
|
+
|
|
221
|
+
expect(css).toContain('input:required {');
|
|
222
|
+
expect(css).toContain('input:optional {');
|
|
223
|
+
expect(css).toContain('input:checked {');
|
|
224
|
+
expect(css).toContain('input:disabled {');
|
|
225
|
+
expect(css).toContain('input:read-only {');
|
|
226
|
+
expect(css).toContain('input:valid {');
|
|
227
|
+
expect(css).toContain('input:invalid {');
|
|
228
|
+
});
|
|
229
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { compile } from 'ripple/compiler';
|
|
2
|
+
|
|
3
|
+
describe('CSS :global with @media and @supports', () => {
|
|
4
|
+
it('handles :global inside @media queries', () => {
|
|
5
|
+
const source = `
|
|
6
|
+
export component Test() {
|
|
7
|
+
<div>{'content'}</div>
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
@media (min-width: 768px) {
|
|
11
|
+
:global(div) {
|
|
12
|
+
color: red;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
div :global(span) {
|
|
16
|
+
color: blue;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
</style>
|
|
20
|
+
}`;
|
|
21
|
+
const { css } = compile(source, 'test.ripple');
|
|
22
|
+
|
|
23
|
+
expect(css).toContain('@media (min-width: 768px) {');
|
|
24
|
+
expect(css).toContain('div {');
|
|
25
|
+
expect(css).toMatch(/div\.ripple-[a-z0-9]+ span {/);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('handles :global inside @supports queries', () => {
|
|
29
|
+
const source = `
|
|
30
|
+
export component Test() {
|
|
31
|
+
<div>{'content'}</div>
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
@supports (display: grid) {
|
|
35
|
+
:global(.container) {
|
|
36
|
+
display: grid;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
div :global(.item) {
|
|
40
|
+
grid-column: 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
}`;
|
|
45
|
+
const { css } = compile(source, 'test.ripple');
|
|
46
|
+
|
|
47
|
+
expect(css).toContain('@supports (display: grid) {');
|
|
48
|
+
expect(css).toContain('.container {');
|
|
49
|
+
expect(css).toMatch(/div\.ripple-[a-z0-9]+ \.item {/);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('handles nested @media with :global', () => {
|
|
53
|
+
const source = `
|
|
54
|
+
export component Test() {
|
|
55
|
+
<div>{'content'}</div>
|
|
56
|
+
|
|
57
|
+
<style>
|
|
58
|
+
div {
|
|
59
|
+
color: black;
|
|
60
|
+
|
|
61
|
+
@media (min-width: 768px) {
|
|
62
|
+
:global {
|
|
63
|
+
.foo {
|
|
64
|
+
color: red;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
color: blue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
</style>
|
|
72
|
+
}`;
|
|
73
|
+
const { css } = compile(source, 'test.ripple');
|
|
74
|
+
|
|
75
|
+
expect(css).toContain('@media (min-width: 768px) {');
|
|
76
|
+
expect(css).toContain('.foo {');
|
|
77
|
+
expect(css).not.toMatch(/\.foo\.ripple-[a-z0-9]+/);
|
|
78
|
+
expect(css).toMatch(/div\.ripple-[a-z0-9]+ {/);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('handles :global in @container queries', () => {
|
|
82
|
+
const source = `
|
|
83
|
+
export component Test() {
|
|
84
|
+
<div>{'content'}</div>
|
|
85
|
+
|
|
86
|
+
<style>
|
|
87
|
+
@container (min-width: 400px) {
|
|
88
|
+
:global(div) {
|
|
89
|
+
color: red;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
</style>
|
|
93
|
+
}`;
|
|
94
|
+
const { css } = compile(source, 'test.ripple');
|
|
95
|
+
|
|
96
|
+
expect(css).toContain('@container (min-width: 400px) {');
|
|
97
|
+
expect(css).toContain('div {');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('handles multiple at-rules with :global', () => {
|
|
101
|
+
const source = `
|
|
102
|
+
export component Test() {
|
|
103
|
+
<div>{'content'}</div>
|
|
104
|
+
|
|
105
|
+
<style>
|
|
106
|
+
@media screen {
|
|
107
|
+
@supports (display: flex) {
|
|
108
|
+
:global(.flex-container) {
|
|
109
|
+
display: flex;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
div :global(.flex-item) {
|
|
113
|
+
flex: 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
</style>
|
|
118
|
+
}`;
|
|
119
|
+
const { css } = compile(source, 'test.ripple');
|
|
120
|
+
|
|
121
|
+
expect(css).toContain('@media screen {');
|
|
122
|
+
expect(css).toContain('@supports (display: flex) {');
|
|
123
|
+
expect(css).toContain('.flex-container {');
|
|
124
|
+
expect(css).toMatch(/div\.ripple-[a-z0-9]+ \.flex-item {/);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { compile } from 'ripple/compiler';
|
|
2
|
+
|
|
3
|
+
describe('CSS :global basic tests', () => {
|
|
4
|
+
it('applies global selector to all elements', () => {
|
|
5
|
+
const source = `
|
|
6
|
+
export component Test() {
|
|
7
|
+
<div>{'content'}</div>
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
:global(div) {
|
|
11
|
+
color: red;
|
|
12
|
+
}
|
|
13
|
+
</style>
|
|
14
|
+
}`;
|
|
15
|
+
const { css } = compile(source, 'test.ripple');
|
|
16
|
+
|
|
17
|
+
expect(css).toContain('div {');
|
|
18
|
+
expect(css).toContain('color: red;');
|
|
19
|
+
expect(css).not.toMatch(/div\.ripple-[a-z0-9]+/);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('applies global selector with class', () => {
|
|
23
|
+
const source = `
|
|
24
|
+
export component Test() {
|
|
25
|
+
<div class="foo">{'content'}</div>
|
|
26
|
+
|
|
27
|
+
<style>
|
|
28
|
+
:global(div.foo) {
|
|
29
|
+
color: pink;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:global(.foo) {
|
|
33
|
+
font-weight: bold;
|
|
34
|
+
}
|
|
35
|
+
</style>
|
|
36
|
+
}`;
|
|
37
|
+
const { css } = compile(source, 'test.ripple');
|
|
38
|
+
|
|
39
|
+
expect(css).toContain('div.foo {');
|
|
40
|
+
expect(css).toContain('.foo {');
|
|
41
|
+
expect(css).not.toMatch(/\.foo\.ripple-[a-z0-9]+/);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('mixes global and local selectors', () => {
|
|
45
|
+
const source = `
|
|
46
|
+
export component Test() {
|
|
47
|
+
<div>
|
|
48
|
+
<p class="foo">{'red/bold'}</p>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<style>
|
|
52
|
+
:global(div) .foo {
|
|
53
|
+
color: red;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
:global(div) > .foo {
|
|
57
|
+
font-weight: bold;
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
60
|
+
}`;
|
|
61
|
+
const { css } = compile(source, 'test.ripple');
|
|
62
|
+
|
|
63
|
+
expect(css).toContain('div .foo');
|
|
64
|
+
expect(css).toContain('div > .foo');
|
|
65
|
+
expect(css).toMatch(/\.foo\.ripple-[a-z0-9]+/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('handles global block syntax', () => {
|
|
69
|
+
const source = `
|
|
70
|
+
export component Test() {
|
|
71
|
+
<div>{'content'}</div>
|
|
72
|
+
|
|
73
|
+
<style>
|
|
74
|
+
:global {
|
|
75
|
+
.x {
|
|
76
|
+
color: green;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.a, .selector, .list {
|
|
80
|
+
color: green;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
84
|
+
}`;
|
|
85
|
+
const { css } = compile(source, 'test.ripple');
|
|
86
|
+
|
|
87
|
+
expect(css).toContain('.x {');
|
|
88
|
+
expect(css).toContain('.a, .selector, .list {');
|
|
89
|
+
expect(css).not.toMatch(/\.x\.ripple-[a-z0-9]+/);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('handles global with pseudo-classes', () => {
|
|
93
|
+
const source = `
|
|
94
|
+
export component Test() {
|
|
95
|
+
<button>{'click'}</button>
|
|
96
|
+
|
|
97
|
+
<style>
|
|
98
|
+
:global(button:hover) {
|
|
99
|
+
opacity: 0.8;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
:global(button):focus {
|
|
103
|
+
outline: none;
|
|
104
|
+
}
|
|
105
|
+
</style>
|
|
106
|
+
}`;
|
|
107
|
+
const { css } = compile(source, 'test.ripple');
|
|
108
|
+
|
|
109
|
+
expect(css).toContain('button:hover {');
|
|
110
|
+
expect(css).toContain('button:focus {');
|
|
111
|
+
expect(css).not.toMatch(/button\.ripple-[a-z0-9]+/);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('handles multiple global selectors in selector list', () => {
|
|
115
|
+
const source = `
|
|
116
|
+
export component Test() {
|
|
117
|
+
<div>{'content'}</div>
|
|
118
|
+
|
|
119
|
+
<style>
|
|
120
|
+
:global(html),
|
|
121
|
+
:global(body) {
|
|
122
|
+
margin: 0;
|
|
123
|
+
padding: 0;
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
126
|
+
}`;
|
|
127
|
+
const { css } = compile(source, 'test.ripple');
|
|
128
|
+
|
|
129
|
+
expect(css).toContain('html,');
|
|
130
|
+
expect(css).toContain('body {');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('scopes local selectors while keeping globals unscoped', () => {
|
|
134
|
+
const source = `
|
|
135
|
+
export component Test() {
|
|
136
|
+
<div class="styled-container">
|
|
137
|
+
<h1>{'Styled heading'}</h1>
|
|
138
|
+
<p class="text">{'Styled paragraph'}</p>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<style>
|
|
142
|
+
.styled-container {
|
|
143
|
+
background-color: rgb(0, 0, 255);
|
|
144
|
+
padding: 16px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
:global(h1) {
|
|
148
|
+
color: rgb(255, 255, 255);
|
|
149
|
+
font-size: 32px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.text {
|
|
153
|
+
color: rgb(200, 200, 200);
|
|
154
|
+
font-size: 14px;
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
157
|
+
}`;
|
|
158
|
+
const { css } = compile(source, 'test.ripple');
|
|
159
|
+
|
|
160
|
+
expect(css).toMatch(/\.styled-container\.ripple-[a-z0-9]+/);
|
|
161
|
+
expect(css).toMatch(/\.text\.ripple-[a-z0-9]+/);
|
|
162
|
+
expect(css).toContain('h1 {');
|
|
163
|
+
expect(css).not.toMatch(/h1\.ripple-[a-z0-9]+/);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { compile } from 'ripple/compiler';
|
|
2
|
+
|
|
3
|
+
describe('CSS :global with classes and IDs', () => {
|
|
4
|
+
it('handles :global with single class', () => {
|
|
5
|
+
const source = `
|
|
6
|
+
export component Test() {
|
|
7
|
+
<div class="foo">{'content'}</div>
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
:global(.foo) {
|
|
11
|
+
color: red;
|
|
12
|
+
}
|
|
13
|
+
</style>
|
|
14
|
+
}`;
|
|
15
|
+
const { css } = compile(source, 'test.ripple');
|
|
16
|
+
|
|
17
|
+
expect(css).toContain('.foo {');
|
|
18
|
+
expect(css).not.toMatch(/\.foo\.ripple-[a-z0-9]+/);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('handles :global with chained classes', () => {
|
|
22
|
+
const source = `
|
|
23
|
+
export component Test() {
|
|
24
|
+
<div class="foo bar">{'content'}</div>
|
|
25
|
+
|
|
26
|
+
<style>
|
|
27
|
+
:global(.foo.bar) {
|
|
28
|
+
color: red;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
:global(.foo).bar {
|
|
32
|
+
color: blue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.foo:global(.bar) {
|
|
36
|
+
color: green;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
39
|
+
}`;
|
|
40
|
+
const { css } = compile(source, 'test.ripple');
|
|
41
|
+
|
|
42
|
+
expect(css).toContain('.foo.bar {');
|
|
43
|
+
expect(css).toMatch(/\.foo\.bar\.ripple-[a-z0-9]+ {/);
|
|
44
|
+
expect(css).toMatch(/\.foo\.ripple-[a-z0-9]+\.bar {/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('handles local class inside :global selector', () => {
|
|
48
|
+
const source = `
|
|
49
|
+
export component Test() {
|
|
50
|
+
<div class="outer">
|
|
51
|
+
<span class="inner">{'content'}</span>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<style>
|
|
55
|
+
:global(.outer) .inner {
|
|
56
|
+
color: red;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
:global(div) .foo {
|
|
60
|
+
color: blue;
|
|
61
|
+
}
|
|
62
|
+
</style>
|
|
63
|
+
}`;
|
|
64
|
+
const { css } = compile(source, 'test.ripple');
|
|
65
|
+
|
|
66
|
+
expect(css).toMatch(/\.outer \.inner\.ripple-[a-z0-9]+ {/);
|
|
67
|
+
expect(css).toMatch(/\(unused\) :global\(div\) \.foo {/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('handles :global with ID selectors', () => {
|
|
71
|
+
const source = `
|
|
72
|
+
export component Test() {
|
|
73
|
+
<div id="app">
|
|
74
|
+
<div id="content">{'text'}</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<style>
|
|
78
|
+
:global(#app) {
|
|
79
|
+
width: 100%;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
:global(#app) #content {
|
|
83
|
+
padding: 20px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
div :global(#content) {
|
|
87
|
+
margin: 0;
|
|
88
|
+
}
|
|
89
|
+
</style>
|
|
90
|
+
}`;
|
|
91
|
+
const { css } = compile(source, 'test.ripple');
|
|
92
|
+
|
|
93
|
+
expect(css).toContain('#app {');
|
|
94
|
+
expect(css).not.toContain('#app #content {');
|
|
95
|
+
expect(css).not.toMatch(/#app\.ripple-[a-z0-9]+/);
|
|
96
|
+
expect(css).toMatch(/div\.ripple-[a-z0-9]+ #content {/);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('handles :global with class and ID combination', () => {
|
|
100
|
+
const source = `
|
|
101
|
+
export component Test() {
|
|
102
|
+
<div id="app" class="container">{'content'}</div>
|
|
103
|
+
|
|
104
|
+
<style>
|
|
105
|
+
:global(#app.container) {
|
|
106
|
+
color: red;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
:global(.container)#app {
|
|
110
|
+
color: blue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#app:global(.container) {
|
|
114
|
+
color: green;
|
|
115
|
+
}
|
|
116
|
+
</style>
|
|
117
|
+
}`;
|
|
118
|
+
const { css } = compile(source, 'test.ripple');
|
|
119
|
+
|
|
120
|
+
expect(css).toContain('#app.container {');
|
|
121
|
+
expect(css).toMatch(/\.container#app\.ripple-[a-z0-9]+ {/);
|
|
122
|
+
expect(css).toMatch(/#app\.ripple-[a-z0-9]+\.container {/);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('handles multiple classes with :global', () => {
|
|
126
|
+
const source = `
|
|
127
|
+
export component Test() {
|
|
128
|
+
<div class="a b c">{'content'}</div>
|
|
129
|
+
|
|
130
|
+
<style>
|
|
131
|
+
:global(.a).b.c {
|
|
132
|
+
color: red;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.a:global(.b).c {
|
|
136
|
+
color: blue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.a.b:global(.c) {
|
|
140
|
+
color: green;
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
143
|
+
}`;
|
|
144
|
+
const { css } = compile(source, 'test.ripple');
|
|
145
|
+
|
|
146
|
+
expect((css.match(/\.a\.b\.c\.ripple-[a-z0-9]+ {/g) || []).length).toBe(2);
|
|
147
|
+
expect(css).toMatch(/\.a\.b\.ripple-[a-z0-9]+\.c {/);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('handles :global with class descendant selectors', () => {
|
|
151
|
+
const source = `
|
|
152
|
+
export component Test() {
|
|
153
|
+
<div class="outer">
|
|
154
|
+
<div class="middle">
|
|
155
|
+
<div class="inner">{'content'}</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<style>
|
|
160
|
+
:global(.outer) .middle .inner {
|
|
161
|
+
color: red;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.outer :global(.middle .inner) {
|
|
165
|
+
color: blue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.outer .middle:global(.inner) {
|
|
169
|
+
color: green;
|
|
170
|
+
}
|
|
171
|
+
</style>
|
|
172
|
+
}`;
|
|
173
|
+
const { css } = compile(source, 'test.ripple');
|
|
174
|
+
|
|
175
|
+
expect(css).toMatch(/\.outer \.middle\.ripple-[a-z0-9]+ \.inner:where\(\.ripple-[a-z0-9]+\)/);
|
|
176
|
+
expect(css).toMatch(/\.outer\.ripple-[a-z0-9]+ \.middle \.inner {/);
|
|
177
|
+
expect(css).toMatch(/\.outer\.ripple-[a-z0-9]+ \.middle:where\(\.ripple-[a-z0-9]+\)\.inner/);
|
|
178
|
+
});
|
|
179
|
+
});
|