From f4f9d61d539d10a9ab79871c381029854ebb4d77 Mon Sep 17 00:00:00 2001 From: Richie Date: Wed, 3 Oct 2018 00:01:03 +0800 Subject: [PATCH 1/6] feat: support
tag for GithubHtmlView (#528) --- src/components/github-htmlview.component.js | 88 ++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/components/github-htmlview.component.js b/src/components/github-htmlview.component.js index 0b9b775b1..137de508c 100644 --- a/src/components/github-htmlview.component.js +++ b/src/components/github-htmlview.component.js @@ -1,10 +1,18 @@ import React, { Component } from 'react'; -import { Dimensions, StyleSheet, View, Text, Platform } from 'react-native'; +import { + Dimensions, + StyleSheet, + View, + Text, + Platform, + TouchableOpacity, +} from 'react-native'; import HTMLView from 'react-native-htmlview'; import { TableWrapper, Table, Cell } from 'react-native-table-component'; import SyntaxHighlighter from 'react-native-syntax-highlighter'; import { github as GithubStyle } from 'react-syntax-highlighter/dist/styles'; import entities from 'entities'; +import { Icon } from 'react-native-elements'; import { ImageZoom, ToggleView } from 'components'; import { colors, fonts, normalize } from 'config'; @@ -134,6 +142,30 @@ class CellWithImage extends Cell { } } +class WithToggle extends Component { + props: { + children: Function, + }; + + state = { expand: false }; + + toggle = () => { + const { expand } = this.state; + + this.setState({ expand: !expand }); + }; + + render() { + const { expand } = this.state; + const _fnRender = this.props.children; + + return _fnRender({ + expand, + toggle: this.toggle, + }); + } +} + const cellForNode = node => node.children.filter(elem => elem.type === 'tag' && elem.name === 'img') .length > 0 @@ -386,6 +418,60 @@ export class GithubHtmlView extends Component { ); }, + details: (node, index, siblings, parent, defaultRenderer) => { + const summaryTagIdx = + (node.children && + node.children.findIndex( + n => n.type === 'tag' && n.name === 'summary' + )) || + -1; + const summaryTag = node.children[summaryTagIdx]; + + if (!summaryTag || !summaryTag.children) { + // we have a details tag without summary, rollback to default render + return {defaultRenderer(node.children, node)}; + } + + const childrenWithoutSummary = [...node.children]; // don't touch the original data + + childrenWithoutSummary.splice(summaryTagIdx, 1); + + const renderSummary = tag => { + if (tag.children.length === 1 && tag.children[0].type === 'text') { + // if we only have one text child, make it prettier + // by removing line break and triming space + return ( + + {tag.children[0].data.replace(/\s\s+/g, ' ').trim()} + + ); + } + + return defaultRenderer(tag.children, tag); + }; + + return ( + + {({ expand, toggle }) => ( + + + + {renderSummary(summaryTag)} + + + {(expand && defaultRenderer(childrenWithoutSummary, node)) || + null} + + )} + + ); + }, }; if (_node.type === 'text') { From c93723a12d365849291c71eef372345119f5e19e Mon Sep 17 00:00:00 2001 From: Richie Date: Wed, 3 Oct 2018 22:56:19 +0800 Subject: [PATCH 2/6] test: add test case for GithubHtmlView to test custom
logic --- __tests__/tests/components/github-htmlview.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 __tests__/tests/components/github-htmlview.js diff --git a/__tests__/tests/components/github-htmlview.js b/__tests__/tests/components/github-htmlview.js new file mode 100644 index 000000000..b8a757589 --- /dev/null +++ b/__tests__/tests/components/github-htmlview.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { Text } from 'react-native'; +import renderer from 'react-test-renderer'; +import { GithubHtmlView } from 'components'; +import { Icon } from 'react-native-elements'; + +describe('', () => { + it('correctly renders
tag in GithubHtmlView', () => { + const sourceHtml = ` +
+ title +
description
+
+ `; + + const inst = renderer.create( + 0} /> + ); + + // we should be able to find a with proper name, + // which means our custom logic for
is applied + expect(inst.root.findByType(Icon).props.name).toEqual('triangle-right'); + + // we should be able to find the processed title text node, + // which means our custom logic for
is applied + expect( + !!inst.root.findAllByType(Text).find(e => e.props.children === 'title') + ).toBe(true); + }); +}); From 376517c87a24bb5a8f860762b4627fddbf9ac180 Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 4 Oct 2018 22:14:13 +0800 Subject: [PATCH 3/6] test: add more test cases against edge cases for GithubHtmlView --- __tests__/tests/components/github-htmlview.js | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/__tests__/tests/components/github-htmlview.js b/__tests__/tests/components/github-htmlview.js index b8a757589..22e4d6ca1 100644 --- a/__tests__/tests/components/github-htmlview.js +++ b/__tests__/tests/components/github-htmlview.js @@ -8,7 +8,7 @@ describe('', () => { it('correctly renders
tag in GithubHtmlView', () => { const sourceHtml = `
- title + title
description
`; @@ -20,11 +20,64 @@ describe('', () => { // we should be able to find a with proper name, // which means our custom logic for
is applied expect(inst.root.findByType(Icon).props.name).toEqual('triangle-right'); + }); + + it('if tag contains a single text node, we shoud prettify it', () => { + const sourceHtml = ` +
+ + make some space and line break here... + + +
description
+
+ `; + + const inst = renderer.create( + 0} /> + ); // we should be able to find the processed title text node, // which means our custom logic for
is applied expect( - !!inst.root.findAllByType(Text).find(e => e.props.children === 'title') + !!inst.root + .findAllByType(Text) + .find( + e => e.props.children === 'make some space and line break here...' + ) ).toBe(true); }); + + it('if there is no tag, we should do fallback render', () => { + const sourceHtml = ` +
+
no summary here!!
+
+ `; + + const inst = renderer.create( + 0} /> + ); + + // fallback render in this case won't contain any Icon + expect(inst.root.findAllByType(Icon).length).toBe(0); + }); + + it('if contain nested tags, should still be rendered without exception', () => { + const sourceHtml = ` +
+ + abcde
test
fg +
+
no summary here!!
+
+ `; + + const inst = renderer.create( + 0} /> + ); + + // should still reach here, and correct render the right arrow icon + expect(inst.root.findByType(Icon).props.name).toEqual('triangle-right'); + }); }); From 950633e07d0d3b1096b77c9bcd0e94c26fe50fba Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 4 Oct 2018 22:15:31 +0800 Subject: [PATCH 4/6] refactor: enhance ToggleView and utilize it in GithubHtmlView --- src/components/github-htmlview.component.js | 63 +++++---------------- src/components/toggle-view.component.js | 5 +- 2 files changed, 18 insertions(+), 50 deletions(-) diff --git a/src/components/github-htmlview.component.js b/src/components/github-htmlview.component.js index 137de508c..86026654e 100644 --- a/src/components/github-htmlview.component.js +++ b/src/components/github-htmlview.component.js @@ -1,12 +1,5 @@ import React, { Component } from 'react'; -import { - Dimensions, - StyleSheet, - View, - Text, - Platform, - TouchableOpacity, -} from 'react-native'; +import { Dimensions, StyleSheet, View, Text, Platform } from 'react-native'; import HTMLView from 'react-native-htmlview'; import { TableWrapper, Table, Cell } from 'react-native-table-component'; import SyntaxHighlighter from 'react-native-syntax-highlighter'; @@ -142,30 +135,6 @@ class CellWithImage extends Cell { } } -class WithToggle extends Component { - props: { - children: Function, - }; - - state = { expand: false }; - - toggle = () => { - const { expand } = this.state; - - this.setState({ expand: !expand }); - }; - - render() { - const { expand } = this.state; - const _fnRender = this.props.children; - - return _fnRender({ - expand, - toggle: this.toggle, - }); - } -} - const cellForNode = node => node.children.filter(elem => elem.type === 'tag' && elem.name === 'img') .length > 0 @@ -451,25 +420,21 @@ export class GithubHtmlView extends Component { }; return ( - - {({ expand, toggle }) => ( - - - - {renderSummary(summaryTag)} - - - {(expand && defaultRenderer(childrenWithoutSummary, node)) || - null} + ( + + + {renderSummary(summaryTag)} )} - + > + {defaultRenderer(childrenWithoutSummary, node)} + ); }, }; diff --git a/src/components/toggle-view.component.js b/src/components/toggle-view.component.js index 03797a6b2..021637906 100644 --- a/src/components/toggle-view.component.js +++ b/src/components/toggle-view.component.js @@ -6,6 +6,7 @@ export class ToggleView extends Component { props: { children: any, TouchableView: any, + renderTouchable: Function, }; state: { @@ -28,7 +29,9 @@ export class ToggleView extends Component { return ( this._toggle()}> - {this.props.TouchableView} + {this.props.renderTouchable + ? this.props.renderTouchable(this.state.collapsed) + : this.props.TouchableView} {this.props.children} From 1ac9a814cb93a0f137b60ac14b98d482c031ffcb Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 4 Oct 2018 23:12:08 +0800 Subject: [PATCH 5/6] refactor: remove extra guard against node.children --- src/components/github-htmlview.component.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/components/github-htmlview.component.js b/src/components/github-htmlview.component.js index 86026654e..14ea0c241 100644 --- a/src/components/github-htmlview.component.js +++ b/src/components/github-htmlview.component.js @@ -388,15 +388,12 @@ export class GithubHtmlView extends Component { ); }, details: (node, index, siblings, parent, defaultRenderer) => { - const summaryTagIdx = - (node.children && - node.children.findIndex( - n => n.type === 'tag' && n.name === 'summary' - )) || - -1; + const summaryTagIdx = node.children.findIndex( + n => n.type === 'tag' && n.name === 'summary' + ); const summaryTag = node.children[summaryTagIdx]; - if (!summaryTag || !summaryTag.children) { + if (!summaryTag) { // we have a details tag without summary, rollback to default render return {defaultRenderer(node.children, node)}; } From 7cfed74da96970d0054ddb0937ee22244bd22211 Mon Sep 17 00:00:00 2001 From: Richie Date: Sun, 7 Oct 2018 00:28:44 +0800 Subject: [PATCH 6/6] refactor: rename test file for GithubHtmlView component --- .../{github-htmlview.js => GithubHtmlView.js} | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) rename __tests__/tests/components/{github-htmlview.js => GithubHtmlView.js} (80%) diff --git a/__tests__/tests/components/github-htmlview.js b/__tests__/tests/components/GithubHtmlView.js similarity index 80% rename from __tests__/tests/components/github-htmlview.js rename to __tests__/tests/components/GithubHtmlView.js index 22e4d6ca1..04fe79d8c 100644 --- a/__tests__/tests/components/github-htmlview.js +++ b/__tests__/tests/components/GithubHtmlView.js @@ -5,7 +5,7 @@ import { GithubHtmlView } from 'components'; import { Icon } from 'react-native-elements'; describe('', () => { - it('correctly renders
tag in GithubHtmlView', () => { + it('correctly renders
tag in GithubHtmlView with correct icon', () => { const sourceHtml = `
title @@ -17,8 +17,6 @@ describe('', () => { 0} /> ); - // we should be able to find a with proper name, - // which means our custom logic for
is applied expect(inst.root.findByType(Icon).props.name).toEqual('triangle-right'); }); @@ -37,8 +35,6 @@ describe('', () => { 0} /> ); - // we should be able to find the processed title text node, - // which means our custom logic for
is applied expect( !!inst.root .findAllByType(Text) @@ -48,7 +44,7 @@ describe('', () => { ).toBe(true); }); - it('if there is no tag, we should do fallback render', () => { + it('if there is no tag, we should do fallback render which contains no icon', () => { const sourceHtml = `
no summary here!!
@@ -59,7 +55,6 @@ describe('', () => { 0} /> ); - // fallback render in this case won't contain any Icon expect(inst.root.findAllByType(Icon).length).toBe(0); }); @@ -77,7 +72,6 @@ describe('', () => { 0} /> ); - // should still reach here, and correct render the right arrow icon expect(inst.root.findByType(Icon).props.name).toEqual('triangle-right'); }); });