Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CumulativeFlow #35

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Add CumulativeFlow #35

wants to merge 1 commit into from

Conversation

ptomli
Copy link

@ptomli ptomli commented Apr 17, 2015

A somewhat useful mashup to display a CFD

@khmylov khmylov self-assigned this Apr 18, 2015
@khmylov
Copy link

khmylov commented Apr 20, 2015

Hi @ptomli !
Thanks for the great suggestion on adding CFD widget!

As you've already noticed, manually hacking the dashboards DOM to add a chart is too messy. What's more important is that this approach is too fragile—we have no obligations in preserving the DOM structure the same in the subsequent releases, and thus we can't guarantee that mashups written this way wouldn't break at some point.

Fortunately, there is a much cleaner and stable way to create a new widget via the dashboards API (please note that it's not officially published yet, but we use it internally for all widgets ourselves, so you may expect it to be quite stable).

Basically, the dashboard extension comes down to 2 steps:

  • Define a widget template which describes how to display a content (chart, data list, RSS feed, YouTube video, or anything else) inside a dashboard
  • Register that template in a widget registry to make it available in the "add widget" panel.

Define a widget template

The most basic widget template is an object with a string id field and insert function.

var template = {
    id: 'sampleCumulativeFlowDiagram', // unique template ID, used to identify a widget in Targetprocess
    insert: function(placeholder) {
        // This function accepts an HTML element which should be used as a container for the widget.
        // It's called for every widget instance every time the user opens a dashboard (there are more triggers, but that's not important now).
        var content = document.createTextNode('This is a sample text');
        placeholder.appendChild(content);
    }
};

Register your widget template

Widget gallery is a collection of all widgets available in Targetprocess. It is the primary tool for users to discover and add widgets to their dashboards.

Unlike the machines, the regular users, are not so good at operating the unique template IDs. To make their lives easier you should add user-friendly information to your widget template to make it easier to find in a widget gallery. You may extend the previously defined template with name, description, tags and previewSrc fields.

var template = {
    id: 'sampleCumulativeFlowDiagram',

    // name and description are rendered on a widget card in a gallery
    name: 'Cumulative Flow',
    description: 'The Cumulative Flow mashup produces a CFD from the history of entities, from the REST API',
    // user can use tags to quickly narrow down the widget list to a specific category
    tags: ['Reports', 'Custom Widgets'],
    // image rendered on a widget card helps the user to visually identify the template in a list
    previewSrc: '../img/widget-cfd.png',

    //insert: function() ...
};

previewSrc is a little bit tricky here, you have to point it to some valid image URL, or ignore it if you don't want the image to be displayed in the gallery.

To let the application know about your new widget template and to make it available in a widget gallery for the end users, you need to register it in a widget template registry.

configurator.getDashboardWidgetTemplateRegistry().addWidgetTemplate(template);

Putting it all together

The final mashup module now should look like this

tau.mashups
    .addDependency('libs/jquery/jquery')
    .addDependency('tau/configurator')
    .addDependency('jquery_cfd')
    .addMashup(function($, configurator, config) {
        try {
            // Just a basic boilerplate to get the actual application service container
            configurator.getGlobalBus().once('configurator.ready', function(evt, configurator) {
                var template = {
                    id: 'sampleCumulativeFlowDiagram',
                    name: 'Cumulative Flow',
                    description: 'The Cumulative Flow mashup produces a CFD from the history of entities, from the REST API',
                    tags: ['Reports', 'Custom Widgets'],
                    insert: function(placeholder) {
                        $(placeholder).cfd({
                            chart: { title: 'Team 1 CFD' },
                            filter: "UserStory.Team.Name eq 'Team 1'",
                            start: new Date(2015, 0, 1),
                            ignore: ['Backlog']
                        });
                    }
                };

                configurator.getDashboardWidgetTemplateRegistry().addWidgetTemplate(template);
            });
        }
        catch (e) {
            console.log('caught exception in cfd mashup: ' + e);
        }
});

Also it looks like jquery_initialize is not required anymore, so you can safely remove it.

What's next

There are some other great things which you can do with that API.
For example you may want to let the users configure CFD settings instead of hardcoding them in the widget template. That's totally possible (insert accepts the 2nd settings argument, and you may also define insertSettings function on the template to render the settings popup, just like our built-in widgets do).

So would you be willing to try this API? I'd love to hear your feedback, and we will be able to merge it into mashups library if it works great.

@ptomli
Copy link
Author

ptomli commented Apr 20, 2015

@khmylov thanks for that, it looks quite possible to improve quite a bit on the code I have so far. I'll have a look and see what I can come up with

@ptomli
Copy link
Author

ptomli commented Apr 20, 2015

@khmylov is there some documentation for the API? Particularly the second argument, settings, to the insert function, and the insertSettings defaults and popup-rendering

@khmylov
Copy link

khmylov commented Apr 20, 2015

@ptomli Like I said, it's not officially published yet, but here's the excerpt of what we have documented for now: https://gist.github.com/khmylov/f0c3e774735d9de80cea. It should cover everything related to widget integration, but feel free to ask if you stumble into any issues.

@ptomli
Copy link
Author

ptomli commented Apr 21, 2015

@khmylov I've been looking to see if tauCharts would be a suitable replacement for highcharts, on the basis that it's likely already integrated in some form into TP. However, I can't see that tauCharts can produce a stacked chart natively, so I'd have to produce the stacked data myself. Any suggestions?

Also, I think I've determined that the settings storage doesn't seem to support some objects, in this case specifically a Date instance. Can you confirm this, and other limitations thereof? Are settings expected to be stored in a single, non-nested object, with string properties?

@khmylov
Copy link

khmylov commented Apr 21, 2015

@ptomli Dashboards use our "client storage" underneath, so the settings object goes through JSON.stringify and JSON.parse at some point, I guess that breaks the Date serialization. Which probably means you should store the text representation and construct the proper Date object inside insert function.

settings is expected to be a single object, not an array, but it should support nested fields (we use it in ToDo widget, for example).

Concerning the tauCharts: I'll ask @Mavrin to help you, he's got much more expertise here :)

@Mavrin
Copy link
Contributor

Mavrin commented Apr 21, 2015

Hi @ptomli , Unfortunately tauCharts doesn't support area and stacked bar charts now. We will implement these chart types in the near future.

@khmylov
Copy link

khmylov commented Apr 30, 2015

Hey @ptomli, do you need any help with making CFD work with dashboards API?

@ptomli
Copy link
Author

ptomli commented May 1, 2015

Thanks @khmylov, I've not had a chance to look at it for a few days, but I was getting to grips with trying to present the settings inputs. It seemed somewhat verbose, manually creating inputs and such, so I didn't progress much further yet. Pity there isn't some API to allow a widget to provide a structure detailing the settings UI needed, and TP deals with generating as needed.

insertSettings: function(placeholder, currentSettings) {
    something.renderSettingsUi([
        { name: 'title', label: 'Title', type: 'string', pattern: '/\w+/' },
        { name: 'foo', label: 'Foo', options: [ 'foo1', 'foo2' ], required: false }
    ]);
}

I'm sure that wouldn't cover all requirements for all widgets, but I imagine it would make most widgets easier to write.

So, that's where I am. Not wanting to write lots of lines of verbose DOM manipulation code, not wanting to learn React (simply to render a few inputs).

I'll make the WIP available when I'm back in the office on Monday

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants