Skip to content

Commit

Permalink
Remove replaceSymbols method, move responsibility to transform prop
Browse files Browse the repository at this point in the history
  • Loading branch information
brian-c committed Aug 13, 2015
1 parent 61d54ba commit bcacd90
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 89 deletions.
4 changes: 2 additions & 2 deletions src/components/markdown-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export default class MarkdownEditor extends React.Component {
<div className="editor-area">
<textarea ref="textarea" className="markdown-editor-input" name={this.props.name} placeholder={this.props.placeholder} value={this.props.value} rows={this.props.rows} cols={this.props.cols} onChange={this.onInputChange.bind(this)} />

<Markdown className="markdown-editor-preview">{this.props.value}</Markdown>
<Markdown className="markdown-editor-preview" transform={this.props.transform}>{this.props.value}</Markdown>
</div>
</div>
);
Expand Down Expand Up @@ -186,6 +186,7 @@ MarkdownEditor.defaultProps = {
value: '',
placeholder: '',
rows: 5,
transform: arg => arg,
onChange: NOOP,
previewing: null,
onHelp: NOOP
Expand All @@ -194,4 +195,3 @@ MarkdownEditor.defaultProps = {
MarkdownEditor.initialState = {
previewing: false
};

46 changes: 2 additions & 44 deletions src/components/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import React from "react";
import MarkdownIt from "markdown-it";
import MarkdownItContainer from "markdown-it-container";
import twemoji from 'twemoji';
import {State} from 'react-router';
import reactMixin from 'react-mixin';

const markdownIt = new MarkdownIt({linkify: true, breaks: true})
.use(require('markdown-it-emoji'))
Expand All @@ -18,45 +16,6 @@ export default class Markdown extends React.Component {
return 'Markdown';
}

replaceSymbols(input) {
// Catch getParams in case we're in a non-routed context like an alert
var owner, name;
try {
({owner, name} = this.getParams());
} catch (_) {
owner = null;
name = null;
}

return input
// hashtags #tagname
.replace(/(?!\B[\w+-\/]+\b)\B#(\b[\w+-\/]+\b)/g, function(fullTag, tagName) {
if (owner && name) {
return `<a href='#/projects/${owner}/${name}/talk/search?query=${tagName}'>${fullTag}</a>`;
}
else {
return `<a href='#/talk/search?query=${tagName}'>${fullTag}</a>`;
}
})

// subjects in a specific project : @owner-slug/project-slug^subject_id
// \b[\w-]+\b is hyphen boundary for slugs
.replace(/@(\b[\w-]+\b)\/(\b[\w-]+\b)\^([0-9]+)/g, "<a href='#/projects/$1/$2/talk/subjects/$3'>$1/$2 - Subject $3</a>")

.replace(/\^([0-9]+)/g, function(_, subjectID) {
if (owner && name) {
return `<a href='#/projects/${owner}/${name}/talk/subjects/${subjectID}'>${owner}/${name} - Subject ${subjectID}</a>`;
}
else {
return subjectID;
}
})

// user mentions : @username
.replace(/\B@(\b[\w-]+\b)/g, "<a href='#/users/$1'>@$1</a>")

}

emojify(input) {
return twemoji.parse(input);
}
Expand All @@ -72,7 +31,7 @@ export default class Markdown extends React.Component {

getHtml() {
try {
return this.replaceSymbols(this.emojify(this.markdownify(this.props.children || this.props.content)));
return this.props.transform(this.emojify(this.markdownify(this.props.children || this.props.content)));
} catch (e) {
return this.props.children || this.props.content;
}
Expand All @@ -92,7 +51,6 @@ Markdown.defaultProps = {
tag: 'div',
content: '',
inline: false,
transform: arg => arg,
className: ''
}

reactMixin.onClass(Markdown, State);
55 changes: 12 additions & 43 deletions test/components/markdown-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,48 +25,11 @@ describe('Markdown', () => {
tag: 'div',
content: '',
inline: false,
transform: Markdown.defaultProps.transform,
className: ''
});
});

describe('#replaceSymbols', () => {
it('replaces #hashtags with hashtag links', () => {
var tagLink = markdown.replaceSymbols('#test');
expect(tagLink).to.equal("<a href='#/talk/search?query=test'>#test</a>");
});

it('replaces #hashtags inside of html without conflicting with urls', () => {
let htmlTagLink = markdown.replaceSymbols(`<p>#good \n https://www.zooniverse.org/#/talk/17/1403?page=1&comment=3063</p>`);

expect(htmlTagLink).to.equal(`<p><a href=\'#/talk/search?query=good\'>#good</a> \n https://www.zooniverse.org/#/talk/17/1403?page=1&comment=3063</p>`)
})

it('replaces ^subject mentions with subject links', () =>{
markdown.getParams = () => {
return {
owner: 'test',
name: 'project'
};
};

var subjectLink = markdown.replaceSymbols('^123456');
expect(subjectLink).to.equal("<a href='#/projects/test/project/talk/subjects/123456'>test/project - Subject 123456</a>");
});

it('does not format subject Ids when not in a routed context', () =>{
markdown.getParams = Function.prototype;

var subjectLink = markdown.replaceSymbols('^123456');
expect(subjectLink).to.equal("123456");
});

it('replaces @ownerslug/project-slug^subject_id mentions with links', () => {
var projectSubjectLink = markdown.replaceSymbols('@owner/project-d^123456');

expect(projectSubjectLink).to.equal("<a href='#/projects/owner/project-d/talk/subjects/123456'>owner/project-d - Subject 123456</a>");
});
});

describe('#markdownify', () => {
it('renders markdown', () => {
var md = markdown.markdownify('# test header');
Expand All @@ -83,7 +46,7 @@ describe('Markdown', () => {
})

it('renders bare child content on error', () => {
md.replaceSymbols = () => {
md.props.transform = () => {
throw new Error("fail")
}
let html = md.getHtml()
Expand All @@ -94,19 +57,25 @@ describe('Markdown', () => {
describe('#render', () => {
var editor, md
before(() => {
editor = React.createElement(Markdown, { className: 'MyComponent', tag: 'div'}, 'Test children')
editor = React.createElement(Markdown, {
className: 'MyComponent',
tag: 'div',
transform: (html) => {
return html.replace('foo', 'bar');
}
}, 'Test children foo');
md = TestUtils.renderIntoDocument(editor);
})

it('renders', () => {
var markdownDiv = TestUtils.findRenderedDOMComponentWithTag(md, 'div');
expect(markdownDiv.props.dangerouslySetInnerHTML.__html).to.equal('<p>Test children</p>\n');
expect(markdownDiv.props.dangerouslySetInnerHTML.__html).to.equal('<p>Test children bar</p>\n');
});

it('calls getHtml in render', () => {
let replaceSymbolsSpy = spy.on(md, 'getHtml')
let getHtmlSpy = spy.on(md, 'getHtml')
md.render()
expect(replaceSymbolsSpy).to.have.been.called()
expect(getHtmlSpy).to.have.been.called()
});

it('returns a react component, with customizable tag', () =>{
Expand Down

0 comments on commit bcacd90

Please sign in to comment.