Getting Download links working

This commit is contained in:
Jeff Avallone 2018-02-16 22:47:53 -05:00
parent 6bc4306ca3
commit 06a90429ff
9 changed files with 88 additions and 31 deletions

View File

@ -157,6 +157,7 @@
"react-i18next": "^7.3.6", "react-i18next": "^7.3.6",
"style-loader": "^0.20.1", "style-loader": "^0.20.1",
"svg-react-loader": "^0.4.5", "svg-react-loader": "^0.4.5",
"throttle-debounce": "^1.0.1",
"uglifyjs-webpack-plugin": "^1.1.8", "uglifyjs-webpack-plugin": "^1.1.8",
"webpack": "^3.10.0", "webpack": "^3.10.0",
"webpack-merge": "^4.1.1", "webpack-merge": "^4.1.1",

View File

@ -3,22 +3,7 @@
exports[`App rendering 1`] = ` exports[`App rendering 1`] = `
<React.Fragment> <React.Fragment>
<Translate(Form) <Translate(Form)
downloadUrls={ downloadUrls={Array []}
Array [
Object {
"filename": "image.svg",
"label": "Download SVG",
"type": "image/svg+xml",
"url": "#svg",
},
Object {
"filename": "image.png",
"label": "Download PNG",
"type": "image/png",
"url": "#png",
},
]
}
onSubmit={[Function]} onSubmit={[Function]}
permalinkUrl="#permalink" permalinkUrl="#permalink"
syntaxes={ syntaxes={

View File

@ -1,4 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { debounce } from 'throttle-debounce';
import style from './style.css'; import style from './style.css';
@ -10,19 +13,75 @@ import { syntaxes, demoImage } from 'devel';
class App extends React.PureComponent { class App extends React.PureComponent {
state = { state = {
syntaxes, syntaxes,
image: demoImage, image: demoImage
downloadUrls: [
{ url: '#svg', filename: 'image.svg', type: 'image/svg+xml', label: 'Download SVG' },
{ url: '#png', filename: 'image.png', type: 'image/png', label: 'Download PNG' }
]
} }
setSvgUrl({ markup }) {
try {
const type = 'image/svg+xml';
const blob = new Blob([markup], { type });
this.setState({
svgUrl: {
url: URL.createObjectURL(blob),
label: this.props.t('Download SVG'),
filename: 'image.svg',
type
}
});
}
catch (e) {
console.error(e); // eslint-disable-line no-console
}
}
async setPngUrl({ element, markup }) {
try {
const type = 'image/png';
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const loader = new Image();
loader.width = canvas.width = Number(element.getAttribute('width')) * 2;
loader.height = canvas.height = Number(element.getAttribute('height')) * 2;
await new Promise(resolve => {
loader.onload = resolve;
loader.src = 'data:image/svg+xml,' + encodeURIComponent(markup);
});
context.drawImage(loader, 0, 0, loader.width, loader.height);
const blob = await new Promise(resolve => canvas.toBlob(resolve, type));
this.setState({
pngUrl: {
url: URL.createObjectURL(blob),
label: this.props.t('Download PNG'),
filename: 'image.png',
type
}
});
}
catch (e) {
console.error(e); // eslint-disable-line no-console
}
}
handleRender = debounce(0, result => {
this.setSvgUrl(result);
this.setPngUrl(result);
})
handleSubmit = ({expr, syntax}) => { handleSubmit = ({expr, syntax}) => {
console.log(syntax, expr); // eslint-disable-line no-console console.log(syntax, expr); // eslint-disable-line no-console
} }
render() { render() {
const { downloadUrls, syntaxes, image } = this.state; const { svgUrl, pngUrl, syntaxes, image } = this.state;
const downloadUrls = [
svgUrl,
pngUrl
].filter(Boolean);
return <React.Fragment> return <React.Fragment>
<Form <Form
@ -37,10 +96,15 @@ class App extends React.PureComponent {
<p>Sample warning message</p> <p>Sample warning message</p>
</Message> </Message>
<div className={ style.render }> <div className={ style.render }>
{ renderImage(image) } { renderImage(image, { onRender: this.handleRender }) }
</div> </div>
</React.Fragment>; </React.Fragment>;
} }
} }
export default App; App.propTypes = {
t: PropTypes.func
};
export default translate()(App);
export { App };

View File

@ -3,14 +3,15 @@ jest.mock('components/SVG');
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import App from 'components/App'; import { App } from 'components/App';
import renderImage from 'components/SVG'; import renderImage from 'components/SVG';
import { translate } from '__mocks__/i18n';
describe('App', () => { describe('App', () => {
test('rendering', () => { test('rendering', () => {
renderImage.mockReturnValue('Testing image'); renderImage.mockReturnValue('Testing image');
const component = shallow( const component = shallow(
<App/> <App t={ translate }/>
); );
expect(component).toMatchSnapshot(); expect(component).toMatchSnapshot();
}); });

View File

@ -46,7 +46,7 @@ class Image extends Base {
if (onRender && this.publishedMarkup !== markup) { if (onRender && this.publishedMarkup !== markup) {
this.publishedMarkup = markup; this.publishedMarkup = markup;
onRender(this.svg.outerHTML); onRender({ element: this.svg, markup: this.svg.outerHTML });
} }
} }

View File

@ -25,17 +25,17 @@ const renderChildren = children => {
return children.length === 1 ? return children.length === 1 ?
renderImage(children[0]) : renderImage(children[0]) :
children.map(renderImage); children.map((node, i) => renderImage(node, { key: i }));
}; };
const renderImage = (node, key) => { const renderImage = (node, extraProps = {}) => {
if (typeof node === 'string') { if (typeof node === 'string') {
return node; return node;
} }
const { type, props, children } = node; const { type, props, children } = node;
return React.createElement(nodeTypes[type], { key, ...props }, renderChildren(children)); return React.createElement(nodeTypes[type], { ...extraProps, ...props }, renderChildren(children));
}; };
export default renderImage; export default renderImage;

View File

@ -2,6 +2,8 @@
An error has occurred: An error has occurred An error has occurred: An error has occurred
Created by <1>Jeff Avallone</1>: Created by <1>Jeff Avallone</1> Created by <1>Jeff Avallone</1>: Created by <1>Jeff Avallone</1>
Display: Display Display: Display
Download PNG: Download PNG
Download SVG: Download SVG
Enter regular expression to display: Enter regular expression to display Enter regular expression to display: Enter regular expression to display
Generated images licensed: 'Generated images licensed: <1><0></0></1>' Generated images licensed: 'Generated images licensed: <1><0></0></1>'
Permalink: Permalink Permalink: Permalink

View File

@ -38,7 +38,7 @@ exports[`Index page component rendering 1`] = `
</p> </p>
</Message> </Message>
</noscript> </noscript>
<App /> <Translate(App) />
<Translate(Footer) /> <Translate(Footer) />
</React.Fragment> </React.Fragment>
`; `;

View File

@ -7785,6 +7785,10 @@ throat@^4.0.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
throttle-debounce@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-1.0.1.tgz#dad0fe130f9daf3719fdea33dc36a8e6ba7f30b5"
throttleit@^1.0.0: throttleit@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c"