Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SVG rendering of equations in dcc.Graph and dcc.Markdown using MathJax v3 #1949

Merged
merged 47 commits into from
Mar 12, 2022
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
d57bb36
add plotly.js-dist-min package which handles mathjax
archmoj Feb 17, 2022
2b83ef4
mathjax in graph
archmoj Feb 15, 2022
17af0b3
mathjax in markdown
archmoj Feb 21, 2022
10fa649
use config.typesetMath internally
archmoj Feb 24, 2022
91b9bdb
add mathjax tests
archmoj Feb 24, 2022
a8ea8e8
Update components/dash-core-components/tests/integration/markdown/tes…
archmoj Mar 2, 2022
84b40a0
Update components/dash-core-components/src/fragments/Math.react.js
archmoj Mar 2, 2022
a2da31d
Update components/dash-core-components/tests/integration/markdown/tes…
archmoj Mar 2, 2022
5e60b3d
Update components/dash-core-components/tests/integration/graph/test_g…
archmoj Mar 2, 2022
aa160f3
Attempt replacing String.raw
archmoj Mar 2, 2022
52fcf9c
fixup driver.execute_script
archmoj Mar 2, 2022
8b52a6d
Merge branch 'dev' into mathjax-v3
archmoj Mar 2, 2022
44be039
naming lazyLoadMathJax
archmoj Mar 2, 2022
882278d
load mathjax on updating markdown prop
archmoj Mar 3, 2022
aa825f5
add load & unload mathjax tests for markdown
archmoj Mar 2, 2022
eccca3b
bump plotly.js to v2.10.0
archmoj Mar 5, 2022
8ba1dcf
relax mathjax version
archmoj Mar 5, 2022
2a1b249
bump plotly.js 2.10.1
archmoj Mar 8, 2022
0ee422d
no need to clear typesetMath from clone of config
archmoj Mar 8, 2022
8eb89ba
drop percy mathjax tests
archmoj Mar 8, 2022
395a4af
add eager mathjax
archmoj Mar 9, 2022
7b58a30
add percy test for mathjax
archmoj Mar 9, 2022
d86f2ae
Update components/dash-core-components/tests/integration/markdown/tes…
archmoj Mar 10, 2022
937d312
Update components/dash-core-components/tests/integration/markdown/tes…
archmoj Mar 10, 2022
f2f58f8
Merge branch 'dev' into mathjax-v3
archmoj Mar 10, 2022
3b702d6
Update components/dash-core-components/tests/integration/markdown/tes…
archmoj Mar 10, 2022
3b36f60
adjust configClone
archmoj Mar 10, 2022
7674d89
Update components/dash-core-components/tests/integration/markdown/tes…
archmoj Mar 10, 2022
06a9ba3
Update components/dash-core-components/tests/integration/markdown/tes…
archmoj Mar 10, 2022
4059f99
Update components/dash-core-components/tests/integration/markdown/tes…
archmoj Mar 10, 2022
7c12efa
drop explicit wait time from mathjax test
archmoj Mar 10, 2022
6623ba2
verify that it is left as normal text
archmoj Mar 10, 2022
fa48268
verify mathjax in grva012 test
archmoj Mar 10, 2022
980961f
attempt verify sync and asyc mathjax in graph_varia tests
archmoj Mar 10, 2022
a58ac54
improve mkdw004 test
archmoj Mar 10, 2022
c61b771
improve mkdw005 test
archmoj Mar 10, 2022
f86985a
toggle mathjax via callbacks
archmoj Mar 10, 2022
eca3d52
additional test similar to markdown for graph
archmoj Mar 10, 2022
074ad26
pass current gd to Plotly.react
archmoj Mar 11, 2022
05cbf80
ensure gd has not been unmounted before call Plotly.react
archmoj Mar 11, 2022
324c85a
return Plotly.react
archmoj Mar 11, 2022
c63e649
push typeset=false into the mathjax chunk
alexcjohnson Mar 11, 2022
a53f7a2
async-friendly extend and prepend to graphs
alexcjohnson Mar 11, 2022
00f300f
sequential building on CI, to hopefully avoid flaky failures
alexcjohnson Mar 11, 2022
70af21e
update mathjax tests, also plotly.js script tag tests
alexcjohnson Mar 11, 2022
4faf64d
test typo
alexcjohnson Mar 11, 2022
d7e73d7
plotly.js 2.11 and changelog for MathJax work
alexcjohnson Mar 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/dash-core-components/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ venv/
/build
/dash_core_components
dash_core_components_base/plotly.min.js
dash_core_components_base/mathjax.js
/deps
/inst
/man
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"graph",
"highlight",
"markdown",
"mathjax",
"slider",
"upload",
]
Expand Down
40 changes: 33 additions & 7 deletions components/dash-core-components/package-lock.json

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

6 changes: 4 additions & 2 deletions components/dash-core-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"test": "run-s -c lint test:intg test:pyimport",
"test:intg": "pytest --nopercyfinalize --headless tests/integration",
"test:pyimport": "python -m unittest tests/test_dash_import.py",
"prebuild:js": "cp node_modules/plotly.js-dist-min/plotly.min.js dash_core_components_base/plotly.min.js",
"prebuild:js": "cp node_modules/plotly.js-dist-min/plotly.min.js dash_core_components_base/plotly.min.js && cp node_modules/mathjax/es5/tex-svg.js dash_core_components_base/mathjax.js",
"build:js": "webpack --mode production",
"build:backends": "dash-generate-components ./src/components dash_core_components -p package-info.json && cp dash_core_components_base/** dash_core_components/ && dash-generate-components ./src/components dash_core_components -p package-info.json -k RangeSlider,Slider,Dropdown,RadioItems,Checklist,DatePickerSingle,DatePickerRange,Input,Link --r-prefix 'dcc' --r-suggests 'dash,dashHtmlComponents,jsonlite,plotly' --jl-prefix 'dcc' && black dash_core_components",
"build": "run-s prepublishOnly build:js build:backends",
Expand All @@ -46,9 +46,10 @@
"fast-isnumeric": "^1.1.4",
"file-saver": "^2.0.5",
"highlight.js": "^11.4.0",
"mathjax": "^3.2.0",
"moment": "^2.29.1",
"node-polyfill-webpack-plugin": "^1.1.4",
"plotly.js-dist-min": "2.9.0",
"plotly.js-dist-min": "2.10.1",
"prop-types": "^15.7.2",
"ramda": "^0.27.1",
"rc-slider": "^9.7.5",
Expand All @@ -60,6 +61,7 @@
"react-resize-detector": "^6.7.6",
"react-select-fast-filter-options": "^0.2.3",
"react-virtualized-select": "^3.1.3",
"remark-math": "^3.0.1",
alexcjohnson marked this conversation as resolved.
Show resolved Hide resolved
"uniqid": "^5.4.0"
},
"devDependencies": {
Expand Down
17 changes: 16 additions & 1 deletion components/dash-core-components/src/components/Graph.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import {asyncDecorator} from '@plotly/dash-component-plugins';
import graph from '../utils/LazyLoader/graph';
import plotly from '../utils/LazyLoader/plotly';
import lazyLoadMathJax from '../utils/LazyLoader/mathjax';
import {
privatePropTypes,
privateDefaultProps,
Expand All @@ -21,6 +22,10 @@ class PlotlyGraph extends Component {
constructor(props) {
super(props);

if (props.mathjax) {
PlotlyGraph._loadMathjax = true;
}

this.state = {
prependData: [],
extendData: [],
Expand Down Expand Up @@ -120,7 +125,11 @@ class PlotlyGraph extends Component {
}

const RealPlotlyGraph = asyncDecorator(PlotlyGraph, () =>
Promise.all([plotly(), graph()]).then(([, graph]) => graph)
Promise.all([
graph(),
plotly(),
PlotlyGraph._loadMathjax ? lazyLoadMathJax() : undefined,
]).then(([graph]) => graph)
);

const ControlledPlotlyGraph = memo(props => {
Expand Down Expand Up @@ -266,6 +275,11 @@ PlotlyGraph.propTypes = {
*/
className: PropTypes.string,

/**
* If true, loads mathjax v3 (tex-svg) into the page and use it in the graph
*/
mathjax: PropTypes.bool,

/**
* Beta: If true, animate between updates using
* plotly.js's `animate` function
Expand Down Expand Up @@ -574,6 +588,7 @@ PlotlyGraph.defaultProps = {
frames: [],
},
responsive: 'auto',
mathjax: false,
animate: false,
animation_options: {
frame: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {asyncDecorator} from '@plotly/dash-component-plugins';
import PropTypes from 'prop-types';
import React, {Component, lazy, Suspense} from 'react';
import React, {Component, Suspense} from 'react';
import markdown from '../utils/LazyLoader/markdown';

const RealDashMarkdown = lazy(markdown);
import lazyLoadMathJax from '../utils/LazyLoader/mathjax';

// eslint-disable-next-line valid-jsdoc
/**
Expand All @@ -11,6 +11,14 @@ const RealDashMarkdown = lazy(markdown);
* [react-markdown](https://rexxars.github.io/react-markdown/) under the hood.
*/
export default class DashMarkdown extends Component {
constructor(props) {
super(props);

if (props.mathjax) {
DashMarkdown._loadMathjax = true;
}
}

render() {
return (
<Suspense fallback={null}>
Expand All @@ -32,6 +40,11 @@ DashMarkdown.propTypes = {
*/
className: PropTypes.string,

/**
* If true, loads mathjax v3 (tex-svg) into the page and use it in the markdown
*/
mathjax: PropTypes.bool,

/**
* A boolean to control raw HTML escaping.
* Setting HTML from code is risky because it's easy to
Expand Down Expand Up @@ -91,10 +104,18 @@ DashMarkdown.propTypes = {
};

DashMarkdown.defaultProps = {
mathjax: false,
dangerously_allow_html: false,
highlight_config: {},
dedent: true,
};

const RealDashMarkdown = asyncDecorator(DashMarkdown, () =>
Promise.all([
markdown(),
DashMarkdown._loadMathjax ? lazyLoadMathJax() : undefined,
]).then(([md]) => md)
);

export const propTypes = DashMarkdown.propTypes;
export const defaultProps = DashMarkdown.defaultProps;
59 changes: 35 additions & 24 deletions components/dash-core-components/src/fragments/Graph.react.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import lazyLoadMathJax from '../utils/LazyLoader/mathjax';
import React, {Component} from 'react';
// /build/withPolyfill for IE11 support - https://github.com/maslianok/react-resize-detector/issues/144
import ResizeDetector from 'react-resize-detector/build/withPolyfill';
Expand Down Expand Up @@ -144,17 +145,21 @@ class PlotlyGraph extends Component {

plot(props) {
let {figure, config} = props;
const {animate, animation_options, responsive} = props;
const {animate, animation_options, responsive, mathjax} = props;

const gd = this.gd.current;
figure = props._dashprivate_transformFigure(figure, gd);
config = props._dashprivate_transformConfig(config, gd);

const configClone = this.getConfig(config, responsive);
// add typesetMath | not exposed to the dash API
configClone.typesetMath = mathjax;

const figureClone = {
data: figure.data,
layout: this.getLayout(figure.layout, responsive),
frames: figure.frames,
config: this.getConfig(config, responsive),
config: configClone,
};

if (
Expand All @@ -176,32 +181,34 @@ class PlotlyGraph extends Component {

gd.classList.add('dash-graph--pending');

return Plotly.react(gd, figureClone).then(() => {
const gd = this.gd.current;
return lazyLoadMathJax(mathjax)
.then(() => Plotly.react(gd, figureClone))
.then(() => {
const gd = this.gd.current;

// double-check gd hasn't been unmounted
if (!gd) {
return;
}
// double-check gd hasn't been unmounted
if (!gd) {
return;
}

gd.classList.remove('dash-graph--pending');
gd.classList.remove('dash-graph--pending');

// in case we've made a new DOM element, transfer events
if (this._hasPlotted && gd !== this._prevGd) {
if (this._prevGd && this._prevGd.removeAllListeners) {
this._prevGd.removeAllListeners();
Plotly.purge(this._prevGd);
// in case we've made a new DOM element, transfer events
if (this._hasPlotted && gd !== this._prevGd) {
if (this._prevGd && this._prevGd.removeAllListeners) {
this._prevGd.removeAllListeners();
Plotly.purge(this._prevGd);
}
this._hasPlotted = false;
}
this._hasPlotted = false;
}

if (!this._hasPlotted) {
this.bindEvents();
this.graphResize(true);
this._hasPlotted = true;
this._prevGd = gd;
}
});
if (!this._hasPlotted) {
this.bindEvents();
this.graphResize(true);
this._hasPlotted = true;
this._prevGd = gd;
}
});
}

mergeTraces(props, dataKey, plotlyFnKey) {
Expand Down Expand Up @@ -426,6 +433,7 @@ class PlotlyGraph extends Component {
return;
}
if (
this.props.mathjax !== nextProps.mathjax ||
this.props.figure !== nextProps.figure ||
this.props._dashprivate_transformConfig !==
nextProps._dashprivate_transformConfig ||
Expand All @@ -449,7 +457,10 @@ class PlotlyGraph extends Component {
}

componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
if (
prevProps.id !== this.props.id ||
prevProps.mathjax !== this.props.mathjax
) {
this.plot(this.props);
}
}
Expand Down
12 changes: 12 additions & 0 deletions components/dash-core-components/src/fragments/Markdown.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import React, {Component} from 'react';
import {mergeDeepRight, pick, type} from 'ramda';
import JsxParser from 'react-jsx-parser';
import Markdown from 'react-markdown';
import RemarkMath from 'remark-math';

import Math from './Math.react';
import MarkdownHighlighter from '../utils/MarkdownHighlighter';
import {propTypes, defaultProps} from '../components/Markdown.react';

Expand Down Expand Up @@ -85,6 +87,7 @@ export default class DashMarkdown extends Component {
highlight_config,
loading_state,
dangerously_allow_html,
mathjax,
children,
dedent,
} = this.props;
Expand Down Expand Up @@ -131,7 +134,16 @@ export default class DashMarkdown extends Component {
<Markdown
source={displayText}
escapeHtml={!dangerously_allow_html}
plugins={mathjax ? [RemarkMath] : []}
renderers={{
math: props => (
<Math tex={props.value} inline={false} />
),

inlineMath: props => (
<Math tex={props.value} inline={true} />
),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks to @anders-kiaer for the contribution in 🏆 🏆 🏆 plotly/dash-core-components#634 🏆 🏆 🏆

html: props =>
props.escapeHtml ? (
props.value
Expand Down
Loading