Skip to content

Commit

Permalink
Support attributes in HTML paths in style mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
mwilliamson committed Feb 17, 2024
1 parent 318145a commit 2ab6916
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 12 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.7.0

* Support attributes in HTML paths in style mappings.

# 1.6.0

* Add transformDocument to the TypeScript declarations.
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,12 @@ append a dot followed by the name of the class:
h1.section-title
```

To add an attribute, use square brackets similarly to a CSS attribute selector:

```
p[lang="fr"]
```

To require that an element is fresh, use `:fresh`:

```
Expand Down
48 changes: 36 additions & 12 deletions lib/style-reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,13 @@ function documentMatcherRule() {
runRule
);

var styleIdRule = lop.rules.then(
classRule,
function(styleId) {
return {styleId: styleId};
}
);
var styleIdRule = lop.rules.sequence(
lop.rules.tokenOfType("dot"),
lop.rules.sequence.cut(),
lop.rules.sequence.capture(identifierRule)
).map(function(styleId) {
return {styleId: styleId};
});

var styleNameMatcherRule = lop.rules.firstOf("style name matcher",
lop.rules.then(
Expand Down Expand Up @@ -234,15 +235,19 @@ function htmlPathRule() {

var styleElementRule = lop.rules.sequence(
capture(tagNamesRule),
capture(lop.rules.zeroOrMore(classRule)),
capture(lop.rules.zeroOrMore(attributeOrClassRule)),
capture(freshRule),
capture(separatorRule)
).map(function(tagName, classNames, fresh, separator) {
).map(function(tagName, attributesList, fresh, separator) {
var attributes = {};
var options = {};
if (classNames.length > 0) {
attributes["class"] = classNames.join(" ");
}
attributesList.forEach(function(attribute) {
if (attribute.append && attributes[attribute.name]) {
attributes[attribute.name] += " " + attribute.value;
} else {
attributes[attribute.name] = attribute.value;
}
});
if (fresh) {
options.fresh = true;
}
Expand Down Expand Up @@ -293,11 +298,30 @@ function decodeEscapeSequences(value) {
});
}

var attributeRule = lop.rules.sequence(
lop.rules.tokenOfType("open-square-bracket"),
lop.rules.sequence.cut(),
lop.rules.sequence.capture(identifierRule),
lop.rules.tokenOfType("equals"),
lop.rules.sequence.capture(stringRule),
lop.rules.tokenOfType("close-square-bracket")
).map(function(name, value) {
return {name: name, value: value, append: false};
});

var classRule = lop.rules.sequence(
lop.rules.tokenOfType("dot"),
lop.rules.sequence.cut(),
lop.rules.sequence.capture(identifierRule)
).head();
).map(function(className) {
return {name: "class", value: className, append: true};
});

var attributeOrClassRule = lop.rules.firstOf(
"attribute or class",
attributeRule,
classRule
);

function parseString(rule, string) {
var tokens = tokenise(string);
Expand Down
14 changes: 14 additions & 0 deletions test/style-reader.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ test('styleReader.readHtmlPath', {
assertHtmlPath("p.tip.help", expected);
},

'reads attribute on element': function() {
var expected = htmlPaths.elements([
htmlPaths.element("p", {"lang": "fr"})
]);
assertHtmlPath("p[lang='fr']", expected);
},

'reads multiple attributes on element': function() {
var expected = htmlPaths.elements([
htmlPaths.element("p", {"lang": "fr", "data-x": "y"})
]);
assertHtmlPath("p[lang='fr'][data-x='y']", expected);
},

'reads when element must be fresh': function() {
var expected = htmlPaths.elements([
htmlPaths.element("p", {}, {"fresh": true})
Expand Down

0 comments on commit 2ab6916

Please sign in to comment.