Skip to content

Commit

Permalink
feat: core
Browse files Browse the repository at this point in the history
  • Loading branch information
romelperez committed Dec 16, 2018
1 parent 39a1448 commit 7d617b6
Show file tree
Hide file tree
Showing 13 changed files with 296 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/Component/Component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function Component(props = {}) {
this.props = props;
this.render = () => {};
}
1 change: 1 addition & 0 deletions src/Component/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Component';
5 changes: 5 additions & 0 deletions src/Node/Node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function Node(element, attributes, children) {
this.element = element;
this.attributes = attributes;
this.children = children;
}
13 changes: 13 additions & 0 deletions src/Node/Node.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-env jest */
/** @jsx createElement */

import { Node } from './Node';

describe('Node', () => {
test('Should create a node with provided parameters', () => {
const node = new Node(1, 2, 3);
expect(node.element).toBe(1);
expect(node.attributes).toBe(2);
expect(node.children).toBe(3);
});
});
1 change: 1 addition & 0 deletions src/Node/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Node';
5 changes: 5 additions & 0 deletions src/createElement/createElement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Node } from '../Node';

export function createElement(element, attributes, ...children) {
return new Node(element, attributes, children);
}
87 changes: 87 additions & 0 deletions src/createElement/createElement.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* eslint-env jest */
/** @jsx createElement */

import { Node } from '../Node';
import { createElement } from './createElement';

describe('createElement()', () => {
test('Should create node tree with provided data structure', () => {
const withStr = 'over here';
const withNum = 100;
const tree = (
<header>
<h1 name="title">Hello!</h1>
<div name="description">Awesome {withStr} <b>fun {withNum}</b>!</div>
</header>
);

expect(tree).toBeInstanceOf(Node);

expect(tree.children.length).toBe(2);
expect(tree.children[0]).toBeInstanceOf(Node);
expect(tree.children[0].children.length).toBe(1);
expect(tree.children[0].children[0]).toBe('Hello!');

expect(tree.children[1]).toBeInstanceOf(Node);
expect(tree.children[1].children.length).toBe(5);

expect(tree.children[1].children[0]).toBeString();
expect(tree.children[1].children[1]).toBeString();
expect(tree.children[1].children[2]).toBeString();

expect(tree.children[1].children[3]).toBeInstanceOf(Node);
expect(tree.children[1].children[3].children.length).toBe(2);
expect(tree.children[1].children[3].children[0]).toBeString();
expect(tree.children[1].children[3].children[1]).toBeNumber();

expect(tree.children[1].children[4]).toBeString();
});

test('Should create node tree with provided props', () => {
const withStr = 'over here';
const withNum = 100;
const tree = (
<header>
<h1 name="title">Hello!</h1>
<div name="description">Awesome {withStr} <b>fun {withNum}</b>!</div>
</header>
);

expect(tree.element).toBe('header');
expect(tree.attributes).toBeNull();

expect(tree.children[0].element).toBe('h1');
expect(tree.children[0].attributes).toEqual({ name: 'title' });
expect(tree.children[0].children.length).toBe(1);
expect(tree.children[0].children[0]).toBe('Hello!');

expect(tree.children[1].element).toBe('div');
expect(tree.children[1].attributes).toEqual({ name: 'description' });

expect(tree.children[1].children[0]).toBe('Awesome ');
expect(tree.children[1].children[1]).toBe(withStr);
expect(tree.children[1].children[2]).toBe(' ');

expect(tree.children[1].children[3].element).toBe('b');
expect(tree.children[1].children[3].attributes).toBeNull();
expect(tree.children[1].children[3].children[0]).toBe('fun ');
expect(tree.children[1].children[3].children[1]).toBe(withNum);

expect(tree.children[1].children[4]).toBe('!');
});

test('Should creates node tree with classes and functions', () => {
class MyClass {}
const MyFn = () => 'hello';
const tree = (
<MyClass>
<h1>Heading</h1>
<MyFn>Content</MyFn>
</MyClass>
);

expect(tree.element).toBe(MyClass);
expect(tree.children[0].element).toBeString();
expect(tree.children[1].element).toBe(MyFn);
});
});
1 change: 1 addition & 0 deletions src/createElement/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './createElement';
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './Component';
export * from './createComponent';
export * from './render';
1 change: 1 addition & 0 deletions src/render/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './render';
72 changes: 72 additions & 0 deletions src/render/render-domelements.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-env jest */
/** @jsx createElement */

import { createElement } from '../createElement';
import { render } from './render';

describe('render() DOMElements', () => {
test('Should render DOMElements with "className" prop as "class"', () => {
const parentRoot = document.createElement('div');
const parentElement = <div className="content">Content</div>;
render(parentElement, parentRoot);

const received = parentRoot.outerHTML;
const expected = '<div class="content">Content</div>';
expect(received).toBe(expected);
});

test('Should render DOMElements with "htmlFor" prop as "for"', () => {
const parentRoot = document.createElement('div');
const parentElement = <div htmlFor="item">Item</div>;
render(parentElement, parentRoot);

const received = parentRoot.outerHTML;
const expected = '<div for="item">Item</div>';
expect(received).toBe(expected);
});

test('Should render DOMElements with "style" prop as directly style modifications', () => {
const styles = { fontSize: '14px', color: 'blue' };
const parentRoot = document.createElement('div');
const parentElement = <div style={styles}>Item</div>;
render(parentElement, parentRoot);

const received = parentRoot.outerHTML;
const expected = '<div style="font-size: 14px;color: blue;">Item</div>';
expect(received).toBe(expected);
});

test('Should render DOMElements with provided events, props matching "/^on[A-Z][A-Za-z]+/"', () => {
const onClick = jest.fn();
const onFocus = jest.fn();
const parentRoot = document.createElement('div');
const parentElement = <button onClick={onClick} onFocus={onFocus}>Item</button>;
render(parentElement, parentRoot);

const button = parentRoot.querySelector('button');
const clickEvent = new Event('click');
const focusEvent = new Event('focus');
button.dispatchEvent(clickEvent);
button.dispatchEvent(focusEvent);

expect(onClick).toHaveBeenCalledWith(clickEvent);
expect(onFocus).toHaveBeenCalledWith(focusEvent);
});

test('Should only render DOMElements strings/numbers and ignore void values', () => {
const parentRoot = document.createElement('div');
const parentElement = <div>Hello{true} every{undefined}one{null} {10}!</div>;
render(parentElement, parentRoot);

const received = parentRoot.outerHTML;
const expected = '<div>Hello everyone 10!</div>';
expect(received).toBe(expected);
});

test('Should throw error if DOMElements have invalid types', () => {
const parentRoot = document.createElement('div');
const parentElement = <div>Hello{{}} everyone{/a/}!</div>;
const fn = () => render(parentElement, parentRoot);
expect(fn).toThrow('Invalid children.');
});
});
94 changes: 94 additions & 0 deletions src/render/render-tree.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* eslint-env jest */
/** @jsx createElement */

import { Component } from '../Component';
import { createElement } from '../createElement';
import { render } from './render';

describe('render() tree', () => {
test('Should render basic HTMLElements tree', () => {
const parentElement = <div name="parent">Hello</div>;
const parentRoot = document.createElement('div');
render(parentElement, parentRoot);

const received = parentRoot.outerHTML;
const expected = '<div name="parent">Hello</div>';
expect(received).toBe(expected);
});

test('Should render nested HTMLElements tree', () => {
const parentRoot = document.createElement('div');
const parentElement = (
<header className="heading">
<h1>Hello</h1>
<ul name="nav">
<li><a href="/">home</a></li>
<li><a href="/about">about</a></li>
</ul>
</header>
);
render(parentElement, parentRoot);

const received = parentRoot.outerHTML;
const expected = [
'<header class="heading">',
'<h1>Hello</h1>',
'<ul name="nav">',
'<li><a href="/">home</a></li>',
'<li><a href="/about">about</a></li>',
'</ul>',
'</header>'
].join('');
expect(received).toBe(expected);
});

test('Should render functions components', () => {
const Title = ({ name, children }) => <h1 name={name}>Title: {children}</h1>;
const parentRoot = document.createElement('div');
const parentElement = (
<div name="parent">
<Title name="title">Welcome</Title>
<p>Content</p>
</div>
);
render(parentElement, parentRoot);

const received = parentRoot.outerHTML;
const expected = '<div name="parent"><h1 name="title">Title: Welcome</h1><p>Content</p></div>';
expect(received).toBe(expected);
});

test('Should render class components', () => {
class Header extends Component {
render() {
const { name, children } = this.props;
return (
<header name={name}>
<h1 className="title">{children}</h1>
<h2>Best heading ever!</h2>
</header>
);
}
}

const parentRoot = document.createElement('div');
const parentElement = (
<div>
<Header name="header">Welcome</Header>
<p>Content</p>
</div>
);
render(parentElement, parentRoot);

const received = parentRoot.outerHTML;
const expected = [
'<div>',
'<header name="header">',
'<h1 class="title">Welcome</h1>',
'<h2>Best heading ever!</h2>',
'</header>',
'</div>'
].join('');
expect(received).toBe(expected);
});
});
9 changes: 9 additions & 0 deletions src/render/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function buildDOM() {
// TODO:
return document.createElement('div');
}

export function render(parentElement, parentRoot) {
const domElement = buildDOM(parentElement);
parentRoot.appendChild(domElement);
}

0 comments on commit 7d617b6

Please sign in to comment.