Skip to content

Commit

Permalink
Add error handling (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
talyssonoc authored Jun 19, 2017
1 parent e91a53c commit cb26666
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 24 deletions.
2 changes: 2 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"semi": ["error", "always"],
"react/display-name": 0,
"react/jsx-filename-extension": 0,
"react/jsx-indent": [2, 2],
"react/jsx-indent-props": [2, 2],
"react/no-danger": 0,
"react/no-set-state": 0,
"react/no-unused-prop-types": 0
Expand Down
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ Display math in the middle of the text.
```jsx
var InlineMath = ReactKaTeX.InlineMath;

React.render(<InlineMath math="\\int_0^\\infty x^2 dx"/>,
ReactDOM.render(<InlineMath math="\\int_0^\\infty x^2 dx"/>,
document.getElementById('math'));

// or

React.render(<InlineMath>\int_0^\infty x^2 dx</InlineMath>,
ReactDOM.render(<InlineMath>\int_0^\infty x^2 dx</InlineMath>,
document.getElementById('math'));
```

Expand All @@ -57,15 +57,35 @@ Display math in a separated block, with larger font and symbols.
```jsx
var BlockMath = ReactKaTeX.BlockMath;

React.render(<BlockMath math="\\int_0^\\infty x^2 dx"/>,
ReactDOM.render(<BlockMath math="\\int_0^\\infty x^2 dx"/>,
document.getElementById('math'));

// or

React.render(<BlockMath>\int_0^\infty x^2 dx</BlockMath>,
ReactDOM.render(<BlockMath>\int_0^\infty x^2 dx</BlockMath>,
document.getElementById('math'));
```

It will be rendered like this:

![Block math](example/block.png)


### Error handling

It's possible to handle parse errors using the prop `renderError`. This prop must be a function that receives the error object and returns what should be rendered when parsing fails:

```jsx
var BlockMath = ReactKaTeX.BlockMath;

ReactDOM.render(
<BlockMath
math="\\int_{"
renderError={(error) => {
return <b>Fail: {error.name}</b>
}}
/>,
document.getElementById('math'));

// The code above will render '<b>Fail: ParseError</b>' because it's the value returned from `renderError`.
```
34 changes: 25 additions & 9 deletions dist/react-katex.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,24 +159,31 @@ return /******/ (function(modules) { // webpackBootstrap

_this.usedProp = props.math ? 'math' : 'children';

_this.state = {
html: _this.generateHtml(props)
};
_this.state = _this.createNewState(null, props);
return _this;
}

_createClass(MathComponent, [{
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
this.setState({
html: this.generateHtml(nextProps)
});
value: function componentWillReceiveProps() {
this.setState(this.createNewState);
}
}, {
key: 'shouldComponentUpdate',
value: function shouldComponentUpdate(nextProps) {
return nextProps[this.usedProp] !== this.props[this.usedProp];
}
}, {
key: 'createNewState',
value: function createNewState(prevState, props) {
try {
var html = this.generateHtml(props);

return { html: html, error: undefined };
} catch (error) {
return { error: error, html: undefined };
}
}
}, {
key: 'generateHtml',
value: function generateHtml(props) {
Expand All @@ -185,7 +192,15 @@ return /******/ (function(modules) { // webpackBootstrap
}, {
key: 'render',
value: function render() {
return _react2.default.createElement(Component, { html: this.state.html });
if (this.state.html) {
return _react2.default.createElement(Component, { html: this.state.html });
}

if (this.props.renderError) {
return this.props.renderError(this.state.error);
}

throw this.state.error;
}
}]);

Expand All @@ -194,7 +209,8 @@ return /******/ (function(modules) { // webpackBootstrap

MathComponent.propTypes = {
children: _react2.default.PropTypes.string,
math: _react2.default.PropTypes.string
math: _react2.default.PropTypes.string,
renderError: _react2.default.PropTypes.func
};

return MathComponent;
Expand Down
2 changes: 1 addition & 1 deletion dist/react-katex.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ <h1>With children</h1>
</div>
</div>

<div class="example">
<h1>With error handling</h1>

<div class="example-case">
Trying to render "\int_{": <div id="error_handling"></div>
</div>
</div>

<script src="../node_modules/react/dist/react.min.js"></script>
<script src="../node_modules/react-dom/dist/react-dom.min.js"></script>
<script src="../node_modules/katex/dist/katex.min.js"></script>
Expand All @@ -70,6 +78,13 @@ <h1>With children</h1>

ReactDOM.render(React.createElement(BlockMath, null, '\\int_0^\\infty x^2 dx'),
document.getElementById('children_block'));

ReactDOM.render(React.createElement(BlockMath, {
math: '\\int_{',
renderError: function(error) {
return React.createElement('b', null, 'You tried to render but got ' + error.name);
}
}), document.getElementById('error_handling'));
</script>

</body>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-katex",
"version": "1.1.0",
"version": "1.2.0",
"description": "Display math in TeX with KaTeX and ReactJS",
"keywords": [
"react",
Expand Down
33 changes: 24 additions & 9 deletions src/createMathComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,27 @@ const createMathComponent = (Component, { displayMode }) => {

this.usedProp = props.math ? 'math' : 'children';

this.state = {
html: this.generateHtml(props)
};
this.state = this.createNewState(null, props);
}

componentWillReceiveProps(nextProps) {
this.setState({
html: this.generateHtml(nextProps)
});
componentWillReceiveProps() {
this.setState(this.createNewState);
}

shouldComponentUpdate(nextProps) {
return nextProps[this.usedProp] !== this.props[this.usedProp];
}

createNewState(prevState, props) {
try {
const html = this.generateHtml(props);

return { html, error: undefined };
} catch(error) {
return { error, html: undefined };
}
}

generateHtml(props) {
return KaTeX.renderToString(
props[this.usedProp],
Expand All @@ -31,13 +37,22 @@ const createMathComponent = (Component, { displayMode }) => {
}

render() {
return <Component html={this.state.html} />;
if(this.state.html) {
return <Component html={this.state.html} />;
}

if(this.props.renderError) {
return this.props.renderError(this.state.error);
}

throw this.state.error;
}
}

MathComponent.propTypes = {
children: React.PropTypes.string,
math: React.PropTypes.string
math: React.PropTypes.string,
renderError: React.PropTypes.func
};

return MathComponent;
Expand Down
59 changes: 59 additions & 0 deletions test/sharedExamples.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import KaTeX from 'katex';
export default (Component, { wrapperTag, displayMode }) => {
const sumFormula = '\\sum_0^\\infty';
const integralFormula = '\\int_{-infty}^\\infty';
const brokenFormula = '\\int_{';
const renderError = (error) => (
<span className={'error'}>{`${error.name}: Invalid formula ${brokenFormula}`}</span>
);

context('when passing the formula as props', () => {
it('renders correctly', () => {
Expand Down Expand Up @@ -51,4 +55,59 @@ export default (Component, { wrapperTag, displayMode }) => {

});
});

describe('error handling', () => {
it('renders the returned value from `renderError` prop when formula is invalid', () => {
const math = shallow(
<Component
math={brokenFormula}
renderError={renderError}
/>
);

expect(math.html()).to.equal(
'<span class="error">ParseError: Invalid formula \\int_{</span>'
);
});

it('updates when passing from invalid to valid formula', () => {
const math = shallow(
<Component
math={brokenFormula}
renderError={renderError}
/>
);

math.setProps({
math: integralFormula
});

expect(math.html()).to.equal(
`<${wrapperTag}>${ KaTeX.renderToString(integralFormula, { displayMode }) }</${wrapperTag}>`
);
});

it('updates when passing from valid to invalid formula', () => {
const math = shallow(
<Component
math={integralFormula}
renderError={renderError}
/>
);

math.setProps({
math: brokenFormula
});

expect(math.html()).to.equal(
'<span class="error">ParseError: Invalid formula \\int_{</span>'
);
});

it('blows when no `renderError` prop is passed', () => {
expect(() => {
shallow(<Component math={brokenFormula} />);
}).to.throw('KaTeX parse error');
});
});
};

0 comments on commit cb26666

Please sign in to comment.