bpmn-io / dmn-js

View and edit DMN diagrams in the browser.
https://bpmn.io/toolkit/dmn-js/
Other
297 stars 137 forks source link

Extending the Viewer as part of a React project #376

Open SebastianStamm opened 6 years ago

SebastianStamm commented 6 years ago

Hi,

I am currently working on a React project that uses dmn-js to display dmn tables. We want to extend the viewer to show some statistics about the table (like how often certain rules have been evaluated). To do so, I had a look at how the Annotations column is done and based my work on that.

I noticed that dmn-js uses inferno-js and that the jsx markup is compiled. However, in my scenario, the additional module is part of a react class as it needs to have access to the React state. We are using JSX to create our React markup. That obviously conflicts with inferno so I had to manually compile it and put it in my extension. Here is a simplified version of how my code looks like:

import React from 'react';
import Viewer from 'dmn-js';
import {Cell} from 'table-js/lib/components';
import {createComponentVNode} from 'inferno';

export default class DMNViewer extends React.Component {
  state = {
    data: 'dmn is awesome!'
  };

  container = React.createRef();

  async componentDidMount() {
    const Addon = components => {
      components.onGetComponent('cell', ({cellType}) => {
        if (cellType === 'after-rule-cells') {
          return () => {
            return createComponentVNode(2, Cell, {
              children: this.state.data
            });
          };
        }
      });
    };
    Addon.$inject = ['components'];

    this.viewer = new Viewer({
      container: this.container.current,
      decisionTable: {
        additionalModules: [
          {
            __init__: ['addon'],
            addon: ['type', Addon]
          }
        ]
      }
    });
    this.viewer.importXML(/* dmn.xml string */);
  }

  render() {
    return (
      <div ref={this.container} />
    );
  }
}

It feels a bit weird to have to use inferno just to add an additional column to the table. Is there something I am missing? Can this be easier?


The other problem I have is updating the content of the additional column. The viewer instance is completely unaware of the react lifecycle, so just calling setState does not update the view. So to force an update with the updated state, I did

componentDidUpdate() {
  this.viewer.open(this.viewer.getActiveView());
}

However I think this is quite an expensive operation. Is there any other way to update the content of the cells? Or some way to get the additional data into the dmn-js lifecycle?

I would love to discuss possible solutions with you :)

nikku commented 6 years ago

We've internally discussed a number of solution approaches:

Regarding the life cycle:

We can think of creating an example to make such extension easier for the users. Waiting for your input in that regard. :cake:

nikku commented 5 years ago

Additional sparring with React users made us assess the following: It should be fairly straight forward to build a bridge that allows libraries / frameworks supporting portals to hook into dmn-js (without knowing the internals). Will need to actually try that out by building a react + dmn-js example.