Skip to content

Commit f0ff70d

Browse files
committed
Make :dir more robust
Fixes #4952
1 parent c6a29dc commit f0ff70d

3 files changed

Lines changed: 109 additions & 19 deletions

File tree

src/lib/style-transformer.html

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
3838
* :host-context(...): scopeName..., ... scopeName
3939
40-
* ...:dir(ltr|rtl) -> [dir="ltr|rtl"] ..., ...[dir="ltr|rtl"]
40+
* ...:dir(ltr|rtl) -> :host-context([dir]) -> [dir="ltr|rtl"] scopeName ..., scopeName[dir="ltr|rtl"] ...
4141
4242
* :host(:dir[rtl]) -> scopeName:dir(rtl) -> [dir="rtl"] scopeName, scopeName[dir="rtl"]
4343
@@ -189,6 +189,17 @@
189189
return p$.join(COMPLEX_SELECTOR_SEP);
190190
},
191191

192+
// make sure `:dir() {` acts as `*:dir() {`
193+
// otherwise, it would transform to `:host-context([dir="rtl"])` and apply incorrectly to the host
194+
// so make it `:host-context([dir="rtl"]) *`
195+
_ensureScopedDir: function(s) {
196+
var m = s.match(DIR_PAREN);
197+
if (m && m[1] === '' && m[0].length === s.length) {
198+
s = '*' + s;
199+
}
200+
return s;
201+
},
202+
192203
_transformComplexSelector: function(selector, scope, hostScope) {
193204
var stop = false;
194205
var hostContext = false;
@@ -197,6 +208,7 @@
197208
selector = this._slottedToContent(selector);
198209
selector = selector.replace(ROOT, ':host > *');
199210
selector = selector.replace(CONTENT_START, HOST + ' $1');
211+
selector = this._ensureScopedDir(selector);
200212
selector = selector.replace(SIMPLE_SELECTOR_SEP, function(m, c, s) {
201213
if (!stop) {
202214
var info = self._transformCompoundSelector(s, c, scope, hostScope);
@@ -219,10 +231,19 @@
219231
return selector;
220232
},
221233

234+
_transformDir: function (s) {
235+
// replaces :host(:dir(rtl)) with :host-context([dir="rtl"])
236+
s = s.replace(HOST_DIR, HOST_DIR_REPLACE);
237+
// replaces `.foo :dir(rtl)` with `:host-context([dir="rtl") .foo`
238+
s = s.replace(DIR_PAREN, DIR_REPLACE);
239+
return s;
240+
},
241+
222242
_transformCompoundSelector: function(selector, combinator, scope, hostScope) {
223243
// replace :host with host scoping class
224244
var jumpIndex = selector.search(SCOPE_JUMP);
225245
var hostContext = false;
246+
selector = this._transformDir(selector);
226247
if (selector.indexOf(HOST_CONTEXT) >=0) {
227248
hostContext = true;
228249
} else if (selector.indexOf(HOST) >=0) {
@@ -243,7 +264,6 @@
243264
selector = selector.replace(SCOPE_JUMP, ' ');
244265
stop = true;
245266
}
246-
selector = selector.replace(DIR_PAREN, DIR_REPLACE);
247267
return {value: selector, combinator: combinator, stop: stop,
248268
hostContext: hostContext};
249269
},
@@ -318,21 +338,9 @@
318338

319339
_dirShadowTransform: function(selector) {
320340
return selector.split(',').map(function(s) {
321-
// replaces :host(:dir(rtl)) with :host-context([dir="rtl"])
322-
s = s.replace(HOST_DIR, HOST_DIR_REPLACE);
323-
var m;
324-
if ((m = s.match(DIR_PAREN))) {
325-
// replaces `.foo :dir(rtl)` with `:host-context([dir="rtl") .foo`
326-
s = s.replace(DIR_PAREN, SHADOW_DIR_REPLACE);
327-
// make sure `:dir() {` acts as `*:dir() {`
328-
// otherwise, it would transform to `:host-context([dir="rtl"])` and apply incorrectly to the host
329-
// so make it `:host-context([dir="rtl"]) *`
330-
if (!m[1] || m[1].match(/^\s*$/)) {
331-
s = s + '*';
332-
}
333-
}
334-
return s;
335-
}).join(',');
341+
s = this._ensureScopedDir(s);
342+
return this._transformDir(s);
343+
}, this).join(',');
336344
},
337345

338346
SCOPE_NAME: 'style-scope'
@@ -364,8 +372,7 @@
364372
var SLOTTED_PAREN = /(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/g;
365373
var HOST_OR_HOST_GT_STAR = /:host(?:\s*>\s*\*)?/;
366374
var DIR_PAREN = /(.*):dir\((ltr|rtl)\)/;
367-
var DIR_REPLACE = '[dir="$2"] $1, $1[dir="$2"]';
368-
var SHADOW_DIR_REPLACE = ':host-context([dir="$2"]) $1';
375+
var DIR_REPLACE = ':host-context([dir="$2"]) $1';
369376
var HOST_DIR = /:host\(:dir\((rtl|ltr)\)\)/g;
370377
var HOST_DIR_REPLACE = ':host-context([dir="$1"])';
371378

test/smoke/dir.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE html>
2+
<!--
3+
@license
4+
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
5+
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6+
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7+
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8+
Code distributed by Google as part of the polymer project is also
9+
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10+
-->
11+
<html dir="rtl">
12+
<head>
13+
<meta charset="utf-8">
14+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
15+
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
16+
17+
<title>Polymer</title>
18+
19+
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
20+
<link rel="import" href="../../polymer.html">
21+
</head>
22+
<body>
23+
24+
<x-el></x-el>
25+
26+
<dom-module id="x-el">
27+
<template>
28+
<style>
29+
#target {
30+
padding: 20px;
31+
color: white;
32+
background: blue;
33+
}
34+
:dir(rtl) #target {
35+
background: red;
36+
}
37+
</style>
38+
<div>hello</div>
39+
<div id="target">am i red?</div>
40+
</template>
41+
<script>
42+
addEventListener('WebComponentsReady', function() {
43+
Polymer({is: 'x-el'});
44+
});
45+
</script>
46+
</dom-module>
47+
48+
</body>
49+
</html>

test/unit/dir.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,28 @@
109109
</script>
110110
</dom-module>
111111

112+
<dom-module id="x-complicated">
113+
<template>
114+
<style>
115+
#target {
116+
padding: 20px;
117+
color: white;
118+
background: rgb(0, 0, 255);
119+
}
120+
:dir(rtl) #target {
121+
background: rgb(255, 0, 0);
122+
}
123+
</style>
124+
<div id="normal">hello</div>
125+
<div id="target">am i red?</div>
126+
</template>
127+
<script>
128+
addEventListener('WebComponentsReady', function() {
129+
Polymer({is: 'x-complicated'});
130+
});
131+
</script>
132+
</dom-module>
133+
112134
<test-fixture id="dir">
113135
<template>
114136
<x-dir></x-dir>
@@ -133,6 +155,12 @@
133155
</template>
134156
</test-fixture>
135157

158+
<test-fixture id="complicated">
159+
<template>
160+
<x-complicated></x-complicated>
161+
</template>
162+
</test-fixture>
163+
136164
<script>
137165
function assertComputed(node, expected, property) {
138166
property = property || 'border-top-width';
@@ -208,6 +236,12 @@
208236
var el = fixture('var')[1];
209237
assertComputed(el, '10px');
210238
assertComputed(el.$.inner, '2px');
239+
});
240+
241+
test('complicated styles work as expected', function() {
242+
var el = fixture('complicated');
243+
assertComputed(el.$.normal, '0px', 'padding-left');
244+
assertComputed(el.$.target, 'rgb(255, 0, 0)', 'background-color');
211245
})
212246
});
213247
</script>

0 commit comments

Comments
 (0)