Search code examples
reactjsplotlypivot-table

Adding new chart types to react pivottable


I'm wondering if it's possible to add new type of charts, like a radar chart, to the React library plotly/react-pivottable https://github.com/plotly/react-pivottable.

I would like to add a spider chart, always from the chart library plotly, but I can't understand where to start as the documentation is a litle poor and the GitHub repo is quite silence...

Maybe it's not even possible.

Does anyone know if it's possible?


Solution

  • Yes, it's completely possible to add custom charts. You need to copy makeRenderer function from original repo and customised it according to the chart types.

    To add a new chart type (radar chart), to React Pivottable, you've to add directly on PlotlyRenderers.

    Here's an example of how you could add a radar chart to React Pivottable:

    const Plot = createPlotlyComponent(window.Plotly);
    const PlotlyRenderers = createPlotlyRenderers(Plot);
    
    const makeRenderer = (
      PlotlyComponent,
      traceOptions = {},
      layoutOptions = {},
      transpose = false
    ) {
      class Renderer extends React.PureComponent {
        render() {
          const pivotData = new PivotData(this.props);
          const rowKeys = pivotData.getRowKeys();
          const colKeys = pivotData.getColKeys();
          const traceKeys = transpose ? colKeys : rowKeys;
          if (traceKeys.length === 0) {
            traceKeys.push([]);
          }
          const datumKeys = transpose ? rowKeys : colKeys;
          if (datumKeys.length === 0) {
            datumKeys.push([]);
          }
    
          let fullAggName = this.props.aggregatorName;
          const numInputs =
            this.props.aggregators[fullAggName]([])().numInputs || 0;
          if (numInputs !== 0) {
            fullAggName += ` of ${this.props.vals.slice(0, numInputs).join(", ")}`;
          }
    
          const data = traceKeys.map((traceKey) => {
            const r = [];
            const theta = [];
            for (const datumKey of datumKeys) {
              const val = parseFloat(
                pivotData
                  .getAggregator(
                    transpose ? datumKey : traceKey,
                    transpose ? traceKey : datumKey
                  )
                  .value()
              );
              r.push(isFinite(val) ? val : null);
              theta.push(datumKey.join("-") || " ");
            }
            const trace = { name: traceKey.join("-") || fullAggName };
    
            trace.fill = "toself";
            trace.r = r;
            trace.theta = theta.length > 1 ? theta : [fullAggName];
    
            return Object.assign(trace, traceOptions);
          });
    
          const layout = {
            polar: {
              radialaxis: {
                visible: true,
                range: [0, 50]
              }
            },
            /* eslint-disable no-magic-numbers */
            // width: window.innerWidth / 1.5,
            // height: window.innerHeight / 1.4 - 50
            // /* eslint-enable no-magic-numbers */
          };
    
          return (
            <PlotlyComponent
              data={data}
              layout={Object.assign(
                layout,
                layoutOptions,
                this.props.plotlyOptions
              )}
              config={this.props.plotlyConfig}
              onUpdate={this.props.onRendererUpdate}
            />
          );
        }
      }
    
      return Renderer;
    }
    
    const radarChart = () => {
      return makeRenderer(
        Plot,
        { type: "scatterpolar" },
        {},
        true
      );
    }
    
    PlotlyRenderers["Radar Chart"] = radarChart({});
    
    const data = [
      {
        country: "Spain",
        name: "Santiago",
        surname: "Ramón y Cajal",
        sex: "Male",
        age: 57,
        subject: "Medicine"
      },
      {
        country: "United Kingdom",
        name: "Ada",
        surname: "Lovelace",
        sex: "Female",
        age: 47,
        subject: "Computing"
      },
      {
        country: "United Kingdom",
        name: "Alan",
        surname: "Turing",
        sex: "Male",
        age: 55,
        subject: "Computing"
      },
      {
        country: "France",
        name: "Antoine",
        surname: "Lavoisier",
        sex: "Male",
        age: 12,
        subject: "Chemistry"
      },
      {
        country: "Poland",
        name: "Marie",
        surname: "Curie",
        sex: "Female",
        age: 33,
        subject: "Chemistry"
      },
      {
        country: "Austria",
        name: "Hedy",
        surname: "Lamarr",
        sex: "Female",
        age: 34,
        subject: "Computing"
      },
      {
        country: "Austria",
        name: "Erwin",
        surname: "Schrödinger",
        sex: "Male",
        age: 38,
        subject: "Physics"
      }
    ];
    
    
    export default function App() {
      const [opts, setOpts] = useState({});
    
      return (
        <div className="App">
          <PivotTableUI
            data={data}
            onChange={(e) => {
              setOpts(e);
              console.log(e);
            }}
            renderers={Object.assign({}, TableRenderers, PlotlyRenderers)}
            cols={["sex"]}
            rows={["subject", "country"]}
            rendererName="Table Heatmap"
            aggregatorName="Average"
            vals={["age"]}
            derivedAttributes={{ completeName: (el) => el.name + " " + el.surname }}
            {...opts}
          />
        </div>
      );
    }
    

    Here is the complete code: https://codesandbox.io/s/react-pivot-table-custom-charts-2utqbt?file=/src/App.js:3511-4468