Skip to content

Commit

Permalink
Update tests and documentation to reflect local-class usage.
Browse files Browse the repository at this point in the history
  • Loading branch information
dfreeman committed Jul 12, 2016
1 parent a48ffc1 commit 35c3268
Show file tree
Hide file tree
Showing 23 changed files with 74 additions and 32 deletions.
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ When you build a component, you drop a `.js` file and a `.hbs` file in your app

### Simple Example

With ember-css-modules, you define styles on a per-component (or -controller) basis. The classes you define are then available from a `styles` object in the template. You define these styles using the same file layout you use for templates; for example, in pod structure you'd put `styles.css` alongside `template.hbs` in the component's pod:
With ember-css-modules, you define styles on a per-component (or -controller) basis. You define these styles using the same file layout you use for templates; for example, in pod structure you'd put `styles.css` alongside `template.hbs` in the component's pod. The classes in that stylesheet are then automatically namespaced to the corresponding component or controller. In order to reference them, you use the `local-class` attribute rather than the standard `class`.

```hbs
{{! app/components/my-component/template.hbs }}
<div class="{{styles.hello-class}}">Hello, world!</div>
<div local-class="hello-class">Hello, world!</div>
```

```css
Expand Down Expand Up @@ -63,10 +63,29 @@ For cases where class reuse is desired, there's [the `composes` property](https:
}
```

In the template for `my-component`, the value of `styles.component-title` will look something like `_component-title_1dr4n4 _secondary-header_1658xu _header_1658xu`, incorporating styles from all of the composing classes.
In the template for `my-component`, an element with `local-class="component-title"` will end up with an actual class string like `_component-title_1dr4n4 _secondary-header_1658xu _header_1658xu`, incorporating styles from all of the composing classes.

Note that you may also use relative paths to specify the source modules for composition.

### Programmatic Styles Access

Currently the `local-class` attribute is honored on HTML elements and component invocations with static values, e.g. `<div local-class="foo bar">` and `{{input local-class="baz"}}`. It is not (yet) supported with dynamic class values or subexpressions like the `(component)` helper.

For these situations, or any other scenario where you need to access a namespaced class outside of a `local-class` attribute, components and controllers with a corresponding styles module expose a mapping from the original class name to the namespaced version in a `styles` property. For instance, the simple "hello-class" example above is actually equivalent to:

```hbs
{{! app/components/my-component/template.hbs }}
<div class="{{unbound styles.hello-class}}">Hello, world!</div>
```

The object exposed as the `styles` property in the template can also be imported directly into JS from whatever path the corresponding CSS module occupies, e.g.

```js
import styles from 'my-app-name/components/my-component/styles';
console.log(styles['hello-class']);
// => "_hello-class_1dr4n4"
```

### Applying CSS to Component Root

In a case where you need to apply a rule to the top-level root element of the component, leverage `classNameBindings` to do so. Then just reference the class in your `styles.css` normally:
Expand Down
6 changes: 4 additions & 2 deletions lib/htmlbars-plugin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,15 @@ ClassTransformPlugin.prototype.localToPath = function(node) {
}

var locals = typeof node.chars === 'string' ? node.chars : node.value;
var builder = typeof node.chars === 'string' ? 'mustache' : 'path';
var builder = typeof node.chars === 'string' ? 'mustache' : 'sexpr';
var classes = locals.split(' ');

return classes.filter(function(local) {
return local.trim().length > 0;
}).map(function(local) {
return this.builders[builder]('styles.' + local.trim());
var unboundPath = this.builders.path('unbound');
var stylePath = this.builders.path('styles.' + local.trim());
return this.builders[builder](unboundPath, [stylePath]);
}, this);
};

Expand Down
35 changes: 26 additions & 9 deletions tests-node/htmlbars-plugin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,39 @@ var ClassTransformPlugin = require('../lib/htmlbars-plugin');

testTransformation('creating a class attribute', {
input: 'local-class="foo"',
statementOutput: 'class=(concat styles.foo)', // FIXME superfluous concat
elementOutput: 'class="{{styles.foo}}"'
statementOutput: 'class=(concat (unbound styles.foo))', // FIXME superfluous concat
elementOutput: 'class="{{unbound styles.foo}}"'
});

testTransformation('creating a class attribute with multiple classes', {
input: 'local-class="foo bar"',
statementOutput: 'class=(concat (unbound styles.foo) " " (unbound styles.bar))',
elementOutput: 'class="{{unbound styles.foo}} {{unbound styles.bar}}"'
});

testTransformation('appending to a class attribute', {
input: 'class="x" local-class="foo"',
statementOutput: 'class=(concat "x" " " styles.foo)',
elementOutput: 'class="x {{styles.foo}}"'
statementOutput: 'class=(concat "x" " " (unbound styles.foo))',
elementOutput: 'class="x {{unbound styles.foo}}"'
});

testTransformation('appending to a class attribute #2', {
testTransformation('appending to a class attribute with multiple classes', {
input: 'class="x" local-class="foo bar"',
statementOutput: 'class=(concat "x" " " (unbound styles.foo) " " (unbound styles.bar))',
elementOutput: 'class="x {{unbound styles.foo}} {{unbound styles.bar}}"'
});

testTransformation('appending to an unquoted class attribute', {
input: 'class=x local-class="foo"',
statementOutput: 'class=(concat x " " styles.foo)',
elementOutput: 'class="x {{styles.foo}}"'
statementOutput: 'class=(concat x " " (unbound styles.foo))',
elementOutput: 'class="x {{unbound styles.foo}}"'
});

// ...
testTransformation('appending to an unquoted class attribute with multiple classes', {
input: 'class=x local-class="foo bar"',
statementOutput: 'class=(concat x " " (unbound styles.foo) " " (unbound styles.bar))',
elementOutput: 'class="x {{unbound styles.foo}} {{unbound styles.bar}}"'
});

function testTransformation(title, options) {
test('ClassTransformPlugin: ' + title, function(assert) {
Expand All @@ -35,5 +51,6 @@ function assertTransforms(template, type, input, output, assert) {
var input = template.replace('[ATTRS]', input);
var output = template.replace('[ATTRS]', output);
var actual = htmlbars.print(htmlbars.parse(input, { plugins: { ast: [ClassTransformPlugin] } }));
assert.equal(actual, output, 'works for ' + type);
var expected = htmlbars.print(htmlbars.parse(output));
assert.equal(actual, expected, 'works for ' + type);
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>addon component with absolute imports</div>
<div local-class="component-class" data-test-element>addon component with absolute imports</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>addon component with relative imports</div>
<div local-class="component-class" data-test-element>addon component with relative imports</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>addon component</div>
<div local-class="component-class" data-test-element>addon component</div>
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
{{! Watch for regressions to https://github.com/salsify/ember-css-modules/issues/15 }}
<div class="{{styles.container}}" data-test-element>component with container class</div>
<div local-class="container" data-test-element>component with container class</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.component-class {
font-family: 'component-with-explicit-styles-reference';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div class="{{styles.component-class}}" data-test-element>component with explicit styles reference</div>
2 changes: 1 addition & 1 deletion tests/dummy/app/components/pod-component/template.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>pod component</div>
<div local-class="component-class" data-test-element>pod component</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>pod template only component</div>
<div local-class="component-class" data-test-element>pod template only component</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>nested pod component</div>
<div local-class="component-class" data-test-element>nested pod component</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>nested pod component</div>
<div local-class="component-class" data-test-element>nested pod component</div>
2 changes: 1 addition & 1 deletion tests/dummy/app/pod-route/template.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.route-class}}" data-test-element>pod route</div>
<div local-class="route-class" data-test-element>pod route</div>
2 changes: 1 addition & 1 deletion tests/dummy/app/pod-template-only-route/template.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.route-class}}" data-test-element>pod template-only route</div>
<div local-class="route-class" data-test-element>pod template-only route</div>
2 changes: 1 addition & 1 deletion tests/dummy/app/root-pod-component/template.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>root pod component</div>
<div local-class="component-class" data-test-element>root pod component</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>root pod template-only component</div>
<div local-class="component-class" data-test-element>root pod template-only component</div>
2 changes: 1 addition & 1 deletion tests/dummy/app/templates/classic-route.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.route-class}}" data-test-element>classic route</div>
<div local-class="route-class" data-test-element>classic route</div>
2 changes: 1 addition & 1 deletion tests/dummy/app/templates/classic-template-only-route.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.route-class}}" data-test-element>classic template-only route</div>
<div local-class="route-class" data-test-element>classic template-only route</div>
2 changes: 1 addition & 1 deletion tests/dummy/app/templates/components/classic-component.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>classic component</div>
<div local-class="component-class" data-test-element>classic component</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>classic template-only component</div>
<div local-class="component-class" data-test-element>classic template-only component</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>absolute imports</div>
<div local-class="component-class" data-test-element>absolute imports</div>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div class="{{styles.component-class}}" data-test-element>relative imports</div>
<div local-class="component-class" data-test-element>relative imports</div>

0 comments on commit 35c3268

Please sign in to comment.