Skip to content

Commit

Permalink
Use the class module to update classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Klingler committed Jun 12, 2018
1 parent 7b06852 commit f9f826d
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 69 deletions.
25 changes: 9 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,16 @@ Simply pass `css` to your [Snabbdom](https://github.com/snabbdom/snabbdom) virtu
```
The **CssModule** is essentially a wrapper around [TypeStyle style](https://typestyle.github.io/#/core/-style-) and accepts the same arguments: Any number of `NestedCssProperties` (or `Style`, which is an alias provided by [snabbdom-typestyle](https://github.com/sklingler93/snabbdom-typestyle)).

Make sure to pass the **CssModule**, along with the **Props** and **Attributes** modules, when initializing [Snabbdom](https://github.com/snabbdom/snabbdom).
Make sure to pass the **CssModule** *before* the **ClassModule** when initializing [Snabbdom](https://github.com/snabbdom/snabbdom).

```js
import { init } from 'snabbdom';
import PropsModule from 'snabbdom/modules/props';
import AttrsModule from 'snabbdom/modules/attributes';
import CssModule from 'snabbdom-typestyle';
import ClassModule from 'snabbdom/modules/class';

const modules = [
PropsModule,
AttrsModule,
CssModule
CssModule,
ClassModule
];

const patch = init(modules);
Expand All @@ -66,11 +64,8 @@ import modulesForHTML from 'snabbdom-to-html/modules';
import { h } from 'snabbdom';

const modules = [
modulesForHTML.attributes,
modulesForHTML.props,
modulesForHTML.class,
modulesForHTML.style,
serverSideCssModule
serverSideCssModule,
modulesForHTML.class
];

const patch = init(modules);
Expand All @@ -87,14 +82,12 @@ Then, on the client-side, pass a selector for the style element rendered by the
Doing this avoids duplication of the style element when the application is hydrated.

```js
import PropsModule from 'snabbdom/modules/props';
import AttrsModule from 'snabbdom/modules/attributes';
import { makeClientSideCssModule } from 'snabbdom-typestyle';
import ClassModule from 'snabbdom/modules/class';

const modules = [
PropsModule,
AttrsModule,
makeClientSideCssModule('#styles')
makeClientSideCssModule('#styles'),
ClassModule
];
```

Expand Down
5 changes: 1 addition & 4 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ const removeElement = (styleElementSelector: string) => {
};

const makeDomUpdater = (styleElementSelector: string | undefined = undefined) => (oldNode: VNode, newNode: VNode): void => {
if (newNode.elm) {
const elm: Element = newNode.elm as Element;
updateVNode(newNode, (name, value) => elm.setAttribute(name, value));
}
updateVNode(newNode);

if (typeof styleElementSelector !== 'undefined') {
removeElement(styleElementSelector);
Expand Down
2 changes: 1 addition & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { StyledVNodeData } from './types';
import { updateVNode } from './utils';

export const serverSideCssModule = (node: VNode, attributes: Map<string, number | string>): void =>
updateVNode(node, (name, value) => attributes.set(name, value));
updateVNode(node);

export const collectStyles = (node: VNode): string => {
const instance = createTypeStyle();
Expand Down
22 changes: 4 additions & 18 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,12 @@ import { style } from 'typestyle';

import { StyledVNodeData } from './types';

const makeClassName = (oldClassName: string, newClassName: string): string => {
return `${oldClassName} ${newClassName}`.trim();
};

export const updateVNode = (node: VNode, attributeAccessor: (attribute: string, className: string) => void): void => {
const data: StyledVNodeData = node.data as StyledVNodeData;
export const updateVNode = (vnode: VNode): void => {
const data: StyledVNodeData = vnode.data as StyledVNodeData;

if (data.css) {
let previousClassName;

if (data.props && data.props.className) {
previousClassName = data.props.className;
} else if (node.sel) {
const dotIdx = node.sel.indexOf('.');
if (dotIdx >= 0) {
previousClassName = node.sel.slice(dotIdx + 1);
}
}

attributeAccessor('class', makeClassName(previousClassName || '', style(data.css)));
data.class = data.class || {};
data.class[style(data.css)] = true;
}
};

Expand Down
12 changes: 6 additions & 6 deletions test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@ describe('makeModule', () => {

it('calls style on create', () => {
typestyle.style = jest.fn(typestyle.style);
module.create(mocks.vNodeWithElm(), mocks.vNodeWithElm());
module.create(mocks.vNode(), mocks.vNode());
expect(typestyle.style.mock.calls.length).toBe(1);
});

it('updates the className on create', () => {
const node = mocks.vNodeWithElm();
module.create(mocks.vNodeWithElm(), node);
expect(node.elm.class).toBe(mocks.hashedClassName());
it('updates class on create', () => {
const node = mocks.vNode();
module.create(mocks.vNode(), node);
expect(node.data.class[mocks.hashedClassName()]).toBe(true);
});

it('removes a given style element on create', () => {
const element = document.createElement('style');
element.id = mocks.styleElementSelector().replace('#', '');
document.head.appendChild(element);

module.create(mocks.vNodeWithElm(), mocks.vNodeWithElm());
module.create(mocks.vNode(), mocks.vNode());
expect(document.head.contains(element)).toBe(false);
});
});
Expand Down
23 changes: 2 additions & 21 deletions test/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,17 @@ import { h } from 'snabbdom';

export const styleElementSelector = () => '#styles';

export const oldClassName = () => 'oldClassName';

export const hashedClassName = () => 'oldClassName f1mb383g';
export const hashedClassName = () => 'f1mb383g';

export const collectedStyles = () => '.f1jvcvsh{color:red}.f1mb383g{color:blue}';

export const css = () => ({
color: 'blue'
});

export const blankVNode = () => h('div');

export const vNode = (alternateCss) => h('div', {
css: alternateCss || css(),
props: {
className: oldClassName()
}
});

export const vNodeWithElm = () => {
const node = vNode();
node.elm = {};

node.elm.setAttribute = jest.fn((name, value) => {
node.elm[name] = value;
css: alternateCss || css()
});

return node;
};

export const vNodeWithChildren = (childQuantity) => {
const node = vNode();
const child = vNode();
Expand Down
7 changes: 4 additions & 3 deletions test/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import * as server from '../src/server';
import * as mocks from './mocks';

describe('serverSideCssModule', () => {
it('updates the class attribute', () => {
it('updates the class', () => {
const node = mocks.vNode();
const attributes = new Map();
server.serverSideCssModule(mocks.vNode(), attributes);
expect(attributes.get('class')).toBe(mocks.hashedClassName());
server.serverSideCssModule(node, attributes);
expect(node.data.class[mocks.hashedClassName()]).toBe(true);
});
});

Expand Down

0 comments on commit f9f826d

Please sign in to comment.