Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Next »

Component

Title

Dashlets

Selector

<sb-dashlet>

Use Case

Dashlets are reporting widgets that can be embedded in any contextual workflow - whether on a consumption, creation or administration screen. Any solution that needs to use dashlets can configure the dashlet with a data source, type of visualisation (bar, line, pi, table, map etc), legends, filters etc.

Description

This generic component/widget will be used to render a chart, table, dataset, map or any other report type. It can make use of multiple libraries & also support building custom components with a common interface.

🧐 Interface Design

Base/Root Interface

  • IBase. - This is the base interface that every report Type component should implement.

  • It deals with the base attributes and behaviours for each dashlet component like height, width , fetching the data , initialization of the component.

interface IBase <T> {

    _defaultConfig: object; // default configurations as per the reportType

    height: string; 
    
    width: string;
    
    id: string;
    
    config: object; // input meta for the dashlets component. It should be as per the report type

    data: <T>[] | IDataLocation; // input data either in JSON format, url or API configuration;

    state: EventEmitter<IReportState>;

    initialize(config: object, data: T[]); // Get the initialisation options used for rendering.

    reset(): void; // resets the component to initial view

    destroy(): void; // performs cleanup post the component is destroyed;

    update(config); // updates and re renders the view

    fetchData<T>(): Promise<T[]> | Observable<T[]>;

    /* genetic methods for addition and removal of data;
        addData();
        removeData
    */
}

type methodType = "GET" | "POST";

interface IApiConfig {
    url: string;
    headers: {
        [header: string]: string | string[];
    };
    methodType: methodType;
    params: {
        [param: string]: string | string[];
    };
    response: {
        path: string;  //Gets the value at path of response object;
    }
}

interface IDataSchema {
    type: string,
    enum?: string[],
    default?: string,
    format?: string; //url, date etc
    items?: IDataSchema // if type is array
}

interface IDataLocation {
    location?: {
        apiConfig?: IApiConfig;
        url?: string;
    },
    dataSchema?: {
        [key: string]: IDataSchema;
    }
}

type IReportState = "initialized" | "rendered" | "destroyed" | "etc";  // pending or done state;

interface EventEmitter<T>{
    emit(value? : T);
    subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;
}

IChart Interface - Base Interface for Chart Components. <ReportType = Chart>

  • This is the interface that every chart component should implement.

  • config takes the required metadata to render a chart like label datasets/series, tooltips legend, title, axes detailes etc. Please refer to the below. image to know most commonly used chart parts . Interface also takes additional metadata if required.

  • labelExpr - refers to the column name or key in the JSON which can be used as x axis labels

  • dataExpr - refers to the key in the JSON to be used on y axis as dataset.

interface IChart extends IBase {

    readonly _defaultConfig: IChartConfig; // default config for tooltips colors legend etc.

    config: IChartConfig; 

    chartClick: EventEmitter<any>;
    chartHover: EventEmitter<any>;

    chartBuilder(config); // (prepares / converts) input chart config as per the underlying chart library used.

    refreshChart(); // refreshes and updates the chart either at specific interval or explicitly triggerd

    addData({ label, data, config }); //appends the label and data at the end;

    removeData(); //pops the last data and label
    removeData(label: string) // removes a specific label

    getTelemetry();

    // mergeData(data1, data2, ...dataN): any[];

    getCurrentSelection(); 
    
    getDatasetAtIndex(index: number);
}

interface IChartConfig {
    type: IChartType;
    options?: IChartOptions;
    [key: string]: any;
}

type IChartType = "bar" | "line" | "pie" | "etc";

type IChartOptions = {
    labels? : string[], // if labels are passed explicitely
    labelExpr ? : string; // column name to use as x-axis labels;
    datasets: IDataset[]; // datasets - y axis data
    tooltip?: object;
    legend?: object
    animation?: object;
    colors?: object;
    title?: string;
    description: string;
    subtitle?: string;
    caption?: object;
    scales?: {
        axes: any;
        [key: string]: any;
    };
    [key: string]: any;
};

type IDataset  = {
    label: string;
    dataExpr?: string;
    data: any[];
}

ITable Interface - Base Interface for Table Components. <ReportType = Table>

interface ITable extends IBase {

    _defaultConfiguration: ITableConfiguration;

    config: ITableConfiguration[];

    getRowsCount(); // rows count
    getRowAtIndex(index: number); //get row at index number
    rowSelector(selectors); //fetch first row matching the config
    rowSelectorAll(selectors); // fetch rows matching a config

    addRow(data: object); // add a new row to the table by passing row configuration
    removeRow(index?: string); //removes row from the end or  at specific index;
    addColumn(config: ITableConfiguration); // adds a new column at the end
    removeColumn(columnName: string); // removes a column

    rowClick: EventEmitter<any>; // row click  event emitter
    rowHover: EventEmitter<any>; // mouse over event emitter

    exportTable(exportType: string); // exports the table into a type
    sortTable(sortByColumnName: string, orderBy: "asc" | "desc");
}

interface ITableConfiguration {
    title?: string;
    searchable?: boolean;
    orderable?: boolean;
    data?: string;
    visible?: boolean;
    render?: () => any;
    paging?: boolean;
    info?: boolean;
    autoWidth?: boolean;
    widthSize?: string;
    [key: string]: any; //any other metadata
}

Filters Config and Component Design

interface IFilter<T> {

    data: T[];

    config: IFilterConfig[];

    filteredDataEventEmitter: EventEmitter<T[]>

    init(config: IFilterConfig); //Get the initialisation options to render the filters;

    filterData(data: T, config): object[];
}

interface IFilterConfig {
    reference: string;
    label: string;
    placeholder: string;
    controlType: "single-select" | "multi-select" | "date";
    searchable?: boolean;
    filters: IFilterConfig[];
    default?: string;
}

/* Questions

    Nested Filter Capability..

*/

🧐 Proposed Design

Dashlet Main Component Proposed Structure

(**Please refer to the next sections for more detailed info about each component)

<sb-dashlet [type]="string" [options]="options"
    [dataSourcePath]?="url" , [data]?="data" , [apiInfo]?="apiInfo" [id]="string | uuid" [height]="string"
    [width]="string" [title]="string | html" [subTitle]?="string | html" [description]?="description"
    (...anyOtherEvent)="eventListener($event)">

    <some-title-element>
        <div>{{title}}</div>
        <div>{{subTitle}}</div>
        <div>{{description}}</div>
    </some-title-element>

    <some-action-buttons-component> </some-action-buttons-component>

    <dashlet-filter [data]="data" , [filters]="filters" , (filteredData)=""> </dashlet-filter>

    <container-element [ngSwitch]="reportType" [height]="height" [width]="width" [id]="id">

        <chart-element switchCase="chart" (chartClick)="eventListener($event)" (chartHover)="eventListener($event)"
            (afterChartLoaded)="eventListener($event)">

            <container-element [ngSwitch]="libraryType" default="chartjs">

                <chartJs-element switchCase="chartjs" [options]="options" [chartType]="chartType" (events)="">
                </chartJs-element>

                <highCharts-element switchCase="highCharts" [options]="options" [chartType]="chartType" (events)="">
                </highCharts-element>

                <d3-element switchCase="d3" [options]="options" [chartType]="chartType" (events)=""> </d3-element>

                <custom-chartTypes switchCase="custom">

                    <container-element [ngSwitch]="reportSubType">

                        <map-element switchCase="map" [options]="options" [chartType]="chartType" (events)=""> </map-element>

                        <bigNumber switchCase="bigNumber" [options]="chartOptions"></bigNumber>

                        <other switchCase="others" [options]="chartOptions"></other>

                    </container-element>

                </custom-chartTypes>

            </container-element>

        </chart-element>

        <table-element switchCase="table" (rowClicked)="eventListener($event)">

            <container-element [ngSwitch]="libraryType" default="datatables">

                <dataTable-component switchCase="datatables" [...options]="tableOptions"> </dataTable-component>

                <custom-table switchCase="custom">
                    <container-element [ngSwitch]="reportSubType">
                        <stripped-table switchCase="stripped-table" [...options]="tableOptions"></stripped-table>
                    </container-element>

                </custom-table>
            </container-element>

        </table-element>

        <dataset-element switchCase="dataset" (events)="eventListener($event)"> </dataset-element>

        <new-reportType-element switchCase="newReportType">

            <container-element [ngSwitch]="libraryType" default="datatables">

                <custom-table switchCase="custom">

                    <container-element [ngSwitch]="reportSubType">
                        <custom-element switchCase="type1" [...options]="customOptions"></custom-element>
                    </container-element>

                </custom-table>
            </container-element>


        </new-reportType-element>

    </container-element>

</sb-dashlet>

ChartJs Component

<chartJs-element [datasets]="datasets" [labels]="labels" [chartType]="chartType" [colors]="colors" [options]="options"
    [legends]="legends" [plugins]="plugins" (chartClick)="chartClick" (chartHover)="chartHover">
</chartJs-element>

<script>
    
    // sample high level chartOptions to configure axes, tooltips, labels, datatsets, colors etc for a chart
  
    let chartOptions = {
        datasets: [
            { data: [65, 59, 80, 81, 56, 55, 40], label: 'Series A' },
            { data: [28, 48, 40, 19, 86, 27, 90], label: 'Series B' },
            { data: [180, 480, 770, 90, 1000, 270, 400], label: 'Series C', yAxisID: 'y-axis-1' }
        ],
        labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
        options: {
            responsive: true,
            scales: {
                xAxes: [{}],
                yAxes: [
                    {
                        id: 'y-axis-0',
                        position: 'left',
                    }
                ]
            }
        },
        colors: [
            {
                backgroundColor: 'rgba(148,159,177,0.2)',
                borderColor: 'rgba(148,159,177,1)',
                pointBackgroundColor: 'rgba(148,159,177,1)',
                pointBorderColor: '#fff',
                pointHoverBackgroundColor: '#fff',
                pointHoverBorderColor: 'rgba(148,159,177,0.8)'
            }
        ],
        legends: true,
        chartType: 'line',
        plugins: [pluginAnnotations]
    }


</script>

Component specs:-

Properties

Note: For more information about possible options please refer to the original chart.js documentation

  • data (SingleOrMultiDataSet) - set of points of the chart, it should be MultiDataSet only for line, bar, radar and doughnut, otherwise SingleDataSet

  • datasets ({data: SingleDataSet, label: string}[]) - data see about, the label for the dataset which appears in the legend and tooltips

  • labels (Label[]) - x-axis labels. It's necessary for charts: line, bar, and radar. And just labels (on hover) for charts: polar area, pie, and a doughnut. The label is either a single string, or it may be a string[] representing a multi-line label where each array element is on a new line.

  • chartType (ChartType) - indicates the type of charts, it can be: line, bar, radar, pie, polar area, doughnut

  • options (ChartOptions) - chart options (as from Chart.js documentation) with some custom options.

  • colors (Color[]) - data colors, will use the default and|or random colors if not specified (see below)

  • legend: (boolean = false) - if true show legend below the chart, otherwise not be shown

Events

  • chartClick: fires, when click on a chart, has occurred, returns information regarding active points and labels

  • chartHover: fires when mousemove (hover) on a chart has occurred, returns information regarding active points and labels


Map Component

<map [options]="options" [mapData]="mapData" (featureClicked)="eventListener($event)">
</map>

<script>
    let mapData = {
        state: 'Karnataka',
        districts: ['Bengaluru', 'Mysuru'],
        reportData: [{}, {}],
        reportLoc: 'url',
        metrics: [''],
        // country: 'India', // Optional - to show india map with states
        // states: ['Karnataka'], // Optional - list of states to show on the map
    }


  // default config and can be overridden with new configurations 

    let options = {
        initialCoordinate: [20, 78],
        latBounds: [6.4626999, 68.1097],
        lonBounds: [35.513327, 97.39535869999999],
        initialZoomLevel: 5,
        controlTitle: 'India Heat Map',
        tileLayer: {
            urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            options: {
                attributions: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            }
        },
        rootStyle: {
            fillColor: '#007cbe'
        },
        otherOptions: any
    };
</script>

Table Component

<table-element [data]="[{}]" [columnsConfiguration]="columnsConfiguration" (rowClickHandler)="eventListener($click)"> 
</table-element>

<script>

    let columnsConfiguration = [{
        title: string,
        searchable: boolean,
        orderable: boolean,
        data: 'key',
        visible: boolean,
        render: () => {},
        paging: boolean,
        info: boolean,
        autoWidth: boolean,
        others: any
    }]

</script>

Filter Component

  • This generic filter component should work at the chart level, table level, and also at the report Level as global filters.

  • Moreover, it should handle nested filter capability as well.

  • selector :- <sb-dashlet-filter>

<sb-dashlet-filter [data]="JSON" [filters]="filters" (filteredData)="subscriptionFunction"> </sb-dashlet-filter>


<script>

    let data = [{ key1: any, key2: any }, {key1: any, key2: any}];
    
    filters = [{
        header: "string",
        footer: "string",
        dataExpr: "key1",
        filters: [{
            header: "string",
            footer: "string",
            dataExpr: "string",
            filters: [{
                dataExpr: "key2"
                ...
            }]
        }]
    }];
</script>

Notes:-

  • If there are more charts in a single report, then each graph should be lazy-loaded to improve page performance.

  • client-side caching for the datasets.

  • No labels