For a little under a year now, I’ve been working on Accusoft’s recently released PrizmDoc Editor, and I’m really excited to see how our customers start to integrate it into their products! Originally I was going to be writing a post about how to get PrizmDoc Editor running and embedded into your app, but if you’ve seen our getting started guide you already know how easy it is. Instead, I’m going to write a sample application with you that integrates the editor and goes through a pretty basic (but typical) workflow with it. If you haven’t already read the getting started guide, do that now and make sure you understand how to start the docker container. I’ll wait for you here.
Our goal will be to create an application called SketchyDocx that allows users to upload and edit DOCX files. We’ll use React to create the UI for the sample, style it with handdrawn.css, and (to keep things simple for purposes of the sample) Parcel to build the project. I chose to use yarn as my package manager, but npm works as well. If you’d like to clone the code and follow along you can find it on GitHub.
Let’s start by initializing the project and installing some dependencies:
yarn init
# Set up the project however you want, then:
yarn add react react-dom
yarn add -D parcel-bundler
And now let’s set up some scripts and a basic React app. Package.json (yours will look slightly different depending on what you specified in yarn init:
{
"name": "SketchyDocx",
"version": "1.0.0",
"description": "Sample application using PrizmDoc Editor",
"main": "index.js",
"author": "Joshua Letcher <jletcher@accusoft.com>",
"license": "MIT",
"scripts": {
"start": "parcel src/index.html"
},
"dependencies": {
"react": "^16.7.0",
"react-dom": "^16.7.0"
},
"devDependencies": {
"parcel-bundler": "^1.11.0"
}
}
src/index.html
<!DOCTYPE html>
<html>
<head>
<title>SketchyDocx – PrizmDoc Editor Sample App</title>
<link rel="stylesheet" type="text/css" href="http://fxaeberhard.github.io/handdrawn.css/handdrawn.css" />
</head>
<body>
<div id="app"></div>
<script src="index.jsx"></script>
</body>
</html>
src/index.jsx
import React from "react";
import ReactDOM from "react-dom";
class SketchyDocx extends React.Component {
render() {
return <div>Hello SketchyDocx!</div>;
}
}
var mountNode = document.getElementById("app");
ReactDOM.render(<SketchyDocx />, mountNode);
If you go ahead and run yarn start now, Parcel will build the project for you and start a development server so you can navigate to it in your browser. The default location should be http://localhost:1234/.
Uploading Documents to PrizmDoc Editor
The first thing that we need to do to start using PrizmDoc Editor in our app is to give it a document to work with. We’ll do that by creating an UploadArea component that allows a user to drag (or click and select) a document to be uploaded.
First, let’s install a dropzone component.
Terminal:
>#install react-dropzone with
>yarn add react-dropzone
Now we can use that to create an upload area. Since we aren’t using redux in this project (for simplicity), we will have the action live in our main component (SketchyDocx) and pass it to the UploadArea through props.
src/components/UploadArea/index.jsx:
import React from "react";
import Dropzone from ‘react-dropzone’;
class UploadArea extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Dropzone
multiple={false}
onDrop={this.props.uploadDocument}
accept="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
>
{({ getInputProps, getRootProps }) => {
return (
<fieldset
{...getRootProps()}
>
<input {...getInputProps()} />
<div>
<i className="icon-cloud-upload"></i><br />
Drop your document here<br />
<span>or click to browse</span>
</div>
</fieldset>
);
}}
</Dropzone>
)
}
};
And update our the entry point (index.jsx) to use it.
src/index.jsx:
import React from "react";
import ReactDOM from "react-dom";
import UploadArea from ‘./components/UploadArea’;
import config from ‘./config.json’;
class SketchyDocx extends React.Component {
constructor(props) {
super(props);
this.uploadDocument = this.uploadDocument.bind(this);
this.state = { documents: []};
}
uploadDocument(files) {
fetch(`${config.apiRoot}/api/v1/documents`, {
method: ‘POST’,
headers: {
"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
},
body: files[0]
}).then(response => response.json())
.then(success => {
this.setState({ documents: [ ...this.state.documents, { name: files[0].name,
documentId: success.documentId }]})
})
.catch(error => console.error(error));
}
render() {
return (
<div>
<UploadArea uploadDocument={this.uploadDocument} />
</div>
);
}
}
var mountNode = document.getElementById("app");
ReactDOM.render(<SketchyDocx />, mountNode);
The uploadDocument
method will now upload the first document that is accepted by the dropzone to PrizmDoc Editor and save the returned documentId
in the state. Pretty easy, right?
Notice that I also created a config.json file so that we can edit where our API lives easily:
{
"apiRoot": "http://localhost:21412"
}
If you didn’t kill the yarn start command earlier, your browser page will refresh and you’ll see our fancy new UploadArea! Make sure that your prizmdoc-editor container is running, then go ahead and try using the upload area. If everything went right, then you should see nothing happen. Exciting!
Let’s add another component that just displays the uploaded documents for now, so that we can verify that the state is being updated correctly.
src/components/DocumentList.jsx
import React from "react";
class EditorContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<table>
<thead>
<tr>
<th>My Documents</th>
</tr>
</thead>
<tbody>
{this.props.documents.map((document) => {
return (
<tr key={document.documentId}>
<td>{document.name}</td>
</tr>
);
})}
</tbody>
</table>
);
}
};
export default EditorContainer;
Now just import that in src/index.jsx and update your render method like this:
render() {
const { documents } = this.state;
return (
<div>
<UploadArea uploadDocument={this.uploadDocument} />
<DocumentList documents={documents} />
</div>
);
}
Creating an Editing Session and Embedding the Editor
Alright, we can now upload a document to PrizmDoc Editor. The next step is to make it so that we can edit those documents. If you look back to the getting started guide, you’ll see that the best way to do this is to embed the editor in an iframe in your app and the URL to use there is just {apiRoot}/?sessionId={sessionId}
. Looks like we’ll need to create a session first, so let’s add a method to the SketchyDocx component to do that.
src/index.jsx:
...
class SketchyDocx extends React.Component {
constructor(props) {
super(props);
this.uploadDocument = this.uploadDocument.bind(this);
this.createSession = this.createSession.bind(this);
this.state = { documents: []}
}
createSession(documentId) {
fetch(`${config.apiRoot}/api/v1/sessions`, {
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’
},
body: JSON.stringify({
documentId
})
}).then(response => response.json())
.then(success => {
this.setState({
sessionId: success.sessionId
});
})
.catch(error => console.error(error))
}
‘Content-Type’: ‘application/json’
},
body: JSON.stringify({
documentId
})
}).then(response => response.json())
.then(success => {
this.setState({
sessionId: success.sessionId
});
})
.catch(error => console.error(error))
}
…
We need to call this method somewhere, so we’ll pass it to DocumentList and make it so that whenever a user clicks on a document in their list, the editing session is created for that document.
src/components/DocumentList.jsx
render() {
return (
<table>
<thead>
<tr>
<th>My Documents</th>
</tr>
</thead>
<tbody>
{this.props.documents.map((document) => {
return (
<tr key={document.documentId}>
<td><a onClick={() => this.props.createSession(document.
documentId)}>{document.name}</a></td>
</tr>
);
})}
</tbody>
</table>
);
src/index.jsx
render() {
const { documents, sessionId } = this.state;
return (
<div>
<UploadArea uploadDocument={this.uploadDocument} />
<DocumentList createSession={this.createSession} documents={documents} />
</div>
);
}
Finally, we can embed the editor and let the user see and modify their document.
Create a new component to contain the editor iframe:
src/components/EditorContainer/index.jsx
import React from "react";
class EditorContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
if (!this.props.sessionId) {
return (
<div>
Select a document to open an editor!
</div>
);
}
const href = `${this.props.apiRoot}/?sessionId=${this.props.sessionId}`;
return (
<iframe style={{width: ‘100vw’, height: ‘100vh’}} src={href} />
);
}
};
export default EditorContainer;
And modify the SketchyDocx
render function to display this new component:
src/index.jsx
render() {
const { documents, sessionId } = this.state;
return (
<div>
<UploadArea uploadDocument={this.uploadDocument} />
<DocumentList createSession={this.createSession} documents={documents} />
<EditorContainer apiRoot={config.apiRoot} sessionId={sessionId} />
</div>
);
}
Now not only can users upload documents, but they can also pick one from the list and edit it.
Clean Up the Interface
We’ve now covered all the basics of loading documents for editing in PrizmDoc Editor. Now we’ll just clean up this sample interface a bit.
src/index.jsx
render() {
const { documents, sessionId } = this.state;
return (
<div>
<h1>SketchyDocx</h1>
<div style={{ display: ‘flex’, alignItems: ‘stretch’, height: ‘85vh’}}>
<nav style={{ paddingRight: ‘15px’}}>
<UploadArea uploadDocument={this.uploadDocument} />
<DocumentList createSession={this.createSession} documents={documents} />
</nav>
<main style={{flexGrow: 2}}>
<EditorContainer apiRoot={config.apiRoot} sessionId={sessionId} />
</main>
</div>
</div>
);
}
src/components/EditorContainer/index.jsx
render() {
if (!this.props.sessionId) {
return (
<div>
Select a document to open an editor!
</div>
);
}
const href = `${this.props.apiRoot}/?sessionId=${this.props.sessionId}`;
return (
<iframe style={{width: ‘100%’, height: ‘100%’}} src={href} />
);
}
src/components/DocumentList/index.jsx
render() {
return (
<table style={{width: ‘100%’}}>
<thead>
<tr>
<th>My Documents</th>
</tr>
</thead>
<tbody>
{this.props.documents.map((document) => {
return (
<tr key={document.documentId}>
<td><a style={{cursor: ‘pointer’}} onClick={() =>
this.props.createSession(document.documentId)}>{document.name}</a></td>
</tr>
);
})}
</tbody>
</table>
);
}
And, voila: SketchyDocx
Now that you know how to integrate PrizmDoc Editor, you can share your work and start collaborating on documents. All code samples are available on GitHub.