See USAGE.md for instructions on how to use this widget in Grist. This README is for developers.
This project was bootstrapped with Create React App.
- Run
npm install
to get started. - Run
npm start
to start the development server, and open http://localhost:3000 to view it in the browser. - Run
npm run deploy
to deploy to GitHub Pages. This will build a production bundle and push it to thegh-pages
branch, and it will be served at https://gristlabs.github.io/custom-charts-widget/. This won't affect Grist users, see below for more details.
In the grist-widget repo, the chart
folder is a git submodule pointing at a commit in the gh-pages
branch of this repo. To fully deploy changes here to production for use in Grist, you need to:
- Commit and push changes to this repo. It doesn't have to be in the main branch, but an actual commit is important for reference.
- Run
npm run deploy
to build and push to thegh-pages
branch. The commit message will contain the commit hash of the commit in step 1. You can use https://gristlabs.github.io/custom-charts-widget/ to test the changes in a Grist doc. - In the
grist-widget
repo, rungit submodule update --remote --init
to update the submodule to the latest commit in thegh-pages
branch. Commit this update, push, and make a PR. The deploy preview URL can be used to test the changes. - Merge the
grist-widget
PR. This will trigger a deploy to production.
public/index.html
is the template for the final index.html
. Webpack inserts the built JavaScript here. This is also
where grist-plugin-api.js
is loaded.
src/index.js
is where all the logic is. Here's how it works:
-
The
PlotlyEditor
component provided byreact-chart-editor
is where the user does all the configuration. When the user makes a change, this callsonUpdate
with the new plotly configuration, which in particular includes thedata
array (traces) andlayout
object. -
Data is received via
grist.onRecords
and used to produce two values which get passed toPlotlyEditor
:dataSourceOptions
: an array of{value, label}
objects indicating columns of the selected table that the user can choose from dropdowns (e.g. in the Traces panel) to use in the chart. Thevalue
has the formatgristsrc:${colRef}
.dataSources
: an object mapping thevalue
s ofdataSourceOptions
to arrays of column values.
-
When a user selects a column from a dropdown, two things get inserted into an object somewhere in the
data
array:- The array of column values, i.e. a value from
dataSources
. The corresponding inserted key varies (e.g.x
ory
), in the code it's generally referred to asattr
. - The 'source' of the data. The object key is of the form
${attr}src
and the value is avalue
fromdataSourceOptions
, i.e. a string of the formgristsrc:${colRef}
.
When multiple columns are selected in a dropdown, both values above will be wrapped in an array.
So overall the react state may include something like this:
- The array of column values, i.e. a value from
{
data: [
{
type: 'scatter',
x: [1, 2, 3, ...],
xsrc: 'gristsrc:11',
y: [
[4, 5, 6, ...],
[7, 8, 9, ...],
],
ysrc: [
'gristsrc:12',
'gristsrc:13',
],
...
}
],
dataSources: {
'gristsrc:11': [1, 2, 3, ...],
'gristsrc:12': [4, 5, 6, ...],
'gristsrc:13': [7, 8, 9, ...],
...
},
dataSourceOptions: [
{value: 'gristsrc:11', label: 'Column A'},
{value: 'gristsrc:12', label: 'Column B''},
{value: 'gristsrc:13', label: 'Column C''},
...
],
}
- When the data is updated via
onRecords
, we can updatedataSources
, but this doesn't automatically updatedata
. So we recursively walk throughdata
looking for keys ending insrc
with values starting withgristsrc:
. See thefillInData
function. - The corresponding arrays of column values are gathered into an array
columns
on which we can perform data transformations such as flattening lists. We can't do this directly with the data received inonRecords
because the transformations depend on the user's selections. The transformed arrays are then put back into the place we found them indata
. - The same recursive function is used to produce a copy of
data
where the column values are replaced by empty arrays. This is saved in the widget options along withlayout
.