Skip to content

Commit

Permalink
Merge pull request #10 from Himenon/dev
Browse files Browse the repository at this point in the history
feat(markdown): MarkdownをReactのComponentに対応させてレンダリングする
  • Loading branch information
Himenon committed Oct 30, 2018
2 parents 39445d8 + 3f7eb26 commit 5d45816
Show file tree
Hide file tree
Showing 21 changed files with 2,498 additions and 125 deletions.
4 changes: 3 additions & 1 deletion .vscode/cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"portfinder",
"resave",
"hoge",
"foo"
"foo",
"rehype",
"hyperscript"
],
"flagWords": [
"hte"
Expand Down
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
### 1.1.0 (2018-10-30)

##### New Features

* **markdown:** MarkdownをReactのComponentに対応させてレンダリングする (f963ebc6)

##### Other Changes

* **test:** remark系を5系にした (6a641446)
* **remark:** 一旦テストを通すようにした (dc933df2)
* **remark-rect:** contentsを直接返す (89b734df)
* **package:** packageのアップデートと、テストの追加 (6d737cd2)
* **event-emitter:** castを排除 (b792b61f)

##### Refactors

* **test:** 不要なimportの削除 (0e9f0154)

##### Tests

* **coverage:** カバレッジ100%にする (00f9eb20)
* **remark:** テストの修正 (728e9e93)
* **remark-react:** テスト追加 (4dad026d)
* **add:** テスト項目の追加 (da400f40)
* **react-markdown:** 落ちるテストを書いた (0ef54973)

#### 1.0.5 (2018-10-26)

##### Chores
Expand Down
23 changes: 21 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "js-one-shot",
"version": "1.0.5",
"version": "1.1.0",
"description": "Libraryの調査",
"main": "entry.js",
"author": "Himenon",
Expand Down Expand Up @@ -52,8 +52,12 @@
"pre-commit": "^1.2.2",
"prettier": "^1.14.3",
"react-test-renderer": "^16.5.2",
"remark-cli": "^5.0.0",
"remark-preset-wooorm": "^4.0.0",
"section-matter": "^1.0.0",
"supertest": "^3.3.0",
"tape": "^4.9.1",
"tinyify": "^2.4.3",
"ts-jest": "^23.10.4",
"ts-loader": "^5.2.2",
"ts-node": "^7.0.1",
Expand All @@ -79,15 +83,30 @@
"express": "^4.16.4",
"express-session": "^1.15.6",
"hast-to-hyperscript": "^5.0.0",
"html-whitespace-sensitive-tag-names": "^1.0.0",
"hyperscript": "^2.0.2",
"mdast-util-to-hast": "^3.0.2",
"portfinder": "^1.0.17",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"rehype": "^6.0.0",
"rehype-document": "^2.2.0",
"rehype-format": "^2.3.0",
"rehype-minify-whitespace": "^2.0.3",
"rehype-parse": "^5.0.0",
"rehype-react": "^3.0.3",
"rehype-stringify": "^4.0.0",
"remark": "^9.0.0",
"remark-html": "^8.0.0",
"remark-parse": "^5.0.0",
"remark-react": "^4.0.3",
"styled-components": "^3.4.10"
"remark-rehype": "^3.0.1",
"remark-slug": "^5.1.0",
"remark-stringify": "^5.0.0",
"styled-components": "^3.4.10",
"to-vfile": "^5.0.2",
"unified": "^7.0.0",
"unified-stream": "^1.0.2",
"vfile-reporter": "^5.1.0"
}
}
6 changes: 3 additions & 3 deletions src/event-emitter/oneshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface CustomPayload {
reset: undefined;
}

export interface CustomListener {
export interface CustomListener{
hoge: (payload: CustomPayload['hoge']) => void;
foo: (payload: CustomPayload['foo']) => void;
add: (payload: CustomPayload['add']) => void;
Expand All @@ -46,11 +46,11 @@ export class CustomEventEmitter extends EventEmitter {
}

public on<K extends keyof CustomListener>(event: K, listener : CustomListener[K]): this {
return super.on(event as string, listener);
return super.on(event, listener);
}

public addListener<K extends keyof CustomListener>(event: K, listener : CustomListener[K]): this {
return super.addListener(event as string, listener);
return super.addListener(event, listener);
}

public emit<K extends keyof CustomListener>(event: K, payload: CustomPayload[K]): boolean {
Expand Down
8 changes: 0 additions & 8 deletions src/hast-to-hyperscript/__tests__/oneshot.test.ts

This file was deleted.

20 changes: 20 additions & 0 deletions src/hast-to-hyperscript/__tests__/oneshot.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as renderer from 'react-test-renderer';
import * as OneShot from '../oneshot';

describe('hast-to-hyperscript', () => {
test('sample test with hyperscript', () => {
const doc = OneShot.main();
expect(doc).toBe('<p id="alpha" class="bravo">charlie <strong style="color:red;">delta</strong> echo.</p>');
});

test('sample test with React.createElement', () => {
const doc = OneShot.mainReact();
expect(doc).toBeUndefined();
});

test('customH', () => {
const result = renderer.create(OneShot.customH('div', {}, null));
const jsonValue = result.toJSON();
expect(jsonValue).toEqual({ type: 'div', props: {}, children: null });
});
});
50 changes: 29 additions & 21 deletions src/hast-to-hyperscript/oneshot.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
// @ts-ignore
import * as toH from 'hast-to-hyperscript'
import * as h from 'hyperscript'

const toH = require('hast-to-hyperscript');
import * as h from 'hyperscript';
import * as React from 'react';
const tree = {
type: 'element',
tagName: 'p',
properties: { id: 'alpha', className: ['bravo'] },
children: [
{ type: 'text', value: 'charlie ' },
{
type: 'element',
tagName: 'strong',
properties: { style: 'color: red' },
children: [{ type: 'text', value: 'delta' }],
},
{ type: 'text', value: ' echo.' },
],
};

export const main = () => {
const tree = {
type: 'element',
tagName: 'p',
properties: {id: 'alpha', className: ['bravo']},
children: [
{type: 'text', value: 'charlie '},
{
type: 'element',
tagName: 'strong',
properties: {style: 'color: red'},
children: [{type: 'text', value: 'delta'}]
},
{type: 'text', value: ' echo.'}
]
}
return toH(h, tree).outerHTML;
};

const hashTree = { type: 'element', tagName: 'div', properties: {}, children: [] };

export const customH = (name: string, props: any, children: any) => {
return React.createElement(name, props, children);
};

return toH(h, tree).outerHTML
}
export const mainReact = () => {
return toH(React.createElement, hashTree).outerHTML;
};
17 changes: 17 additions & 0 deletions src/react-markdown/__tests__/heading.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as renderer from 'react-test-renderer';
import { heading } from '../heading1';

test('heading', () => {
const makeComponent = heading('div');
const component = makeComponent({
title: 'Hello world',
children: null,
});
const result = renderer.create(component);
const jsonValue = result.toJSON();
expect(jsonValue).toEqual({
type: 'div',
props: { title: 'Hello world' },
children: [{ type: 'a', props: { href: '#hoge', style: { color: 'inherit', textDecoration: 'none' } }, children: null }],
});
});
43 changes: 43 additions & 0 deletions src/react-markdown/__tests__/markdown.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { Heading1, Heading1Props } from '../heading1';
import { defaultProps, Markdown, MarkdownProps } from '../markdown';

describe('MarkdownComponent', () => {
test('defaultProps', () => {
const component = defaultProps.scope.Title!({
title: 'hoge',
});
const result = renderer.create(component);
const jsonValue = result.toJSON();
expect(jsonValue).toEqual({ type: 'h1', props: {}, children: ['hoge'] });
});

test('Heading1', () => {
const result = renderer.create(<Heading1 title="sample text" />);
const jsonValue = result.toJSON();
expect(jsonValue).toEqual({ type: 'h1', props: {}, children: ['sample text'] });
});

test('Rendering Value', () => {
const props: MarkdownProps = {
text: '<h1>Hello Gen</h1>',
h1: {},
scope: { Title: (props1: Heading1Props) => <Heading1 {...props1} /> },
};
const result = renderer.create(<Markdown {...props} />);
const jsonValue = result.toJSON();
expect(jsonValue).toEqual({ type: 'div', props: {}, children: null });
});

test('Rendering Value no scope', () => {
const props: MarkdownProps = {
text: '<h1>Hello Gen</h1>',
h1: {},
scope: {},
};
const result = renderer.create(<Markdown {...props} />);
const jsonValue = result.toJSON();
expect(jsonValue).toEqual({ type: 'div', props: {}, children: null });
});
});
30 changes: 30 additions & 0 deletions src/react-markdown/heading1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react';

export interface Heading1Props {
title: string;
children?: React.ReactNode;
}

export class Heading1 extends React.Component<Heading1Props, {}> {
public render() {
return <h1>{this.props.title}</h1>;
}
}

export const heading = (Comp: any) => (props: Heading1Props): React.ReactElement<any> => {
return React.createElement(
Comp,
props,
React.createElement(
'a',
{
href: '#hoge',
style: {
color: 'inherit',
textDecoration: 'none',
},
},
props.children,
),
);
};
68 changes: 68 additions & 0 deletions src/react-markdown/markdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as React from 'react';
// @ts-ignore
import * as remark from 'remark';
// @ts-ignore
import * as remarkReact from 'remark-react';
// @ts-ignore
import * as remarkSlug from 'remark-slug';

import { heading, Heading1, Heading1Props } from './heading1';

export interface ScopedComponents {
Title?: (props: Heading1Props) => React.ReactElement<Heading1Props>;
}

export interface MarkdownProps {
text?: string;
h1: { [key: string]: number };
scope: ScopedComponents;
}

export interface MappedScope {
h1: React.ReactNode | undefined;
}

export const defaultProps: MarkdownProps = {
h1: {},
scope: {
Title: (props: Heading1Props) => <Heading1 {...props} />,
},
};

export class Markdown extends React.Component<MarkdownProps, {}> {
public render() {
const scope = this.props.scope;

const mappedScope = this.mapScope({ ...scope });
const remarkReactComponents = this.applyProps(mappedScope);

const opts = {
remarkReactComponents, // 上書きする
};
// @ts-ignore
const element = remark()
.use(remarkSlug)
.use(remarkReact, opts)
.processSync(this.props.text).contents;

return element;
}

private mapScope = (scope: ScopedComponents): MappedScope => {
const h1 = scope.Title;
return {
h1: h1 ? heading(h1) : undefined,
};
};

private applyProps = (scope: MappedScope) => {
const props = { ...defaultProps };
Object.keys(props).forEach(key => {
if (!scope[key]) {
return;
}
scope[key].defaultProps = { ...scope[key].defaultProps, ...props[key] };
});
return scope;
};
}
Loading

0 comments on commit 5d45816

Please sign in to comment.