Skip to content
Morgan Dubois edited this page Jun 12, 2019 · 4 revisions

Table of Contents

  1. What is going to be developed
  2. Features
  3. Using external library
  4. Bundling project
  5. Code Quality
  6. Testing

1. What is going to be developed

A datatable component is going to be developed including the features listed below. The code must follow the "override" pattern also called "render props". What needs to be overridable :

  • Header (button functionalities)
  • Column's header (datatable)
  • Cell's header
  • Row
  • Row cells (and input cell)
  • Footer

How the datatable should look like at the end : datatable

1.1 Object structure

The developer must give to the datatable an object of this format (The format / name of key can change through the development, the wiki will be updated).

let options  = {
    title: "My super datatable",
    dimensions: {
        datatable: {
            width: "90vw",
        },
        header: {
            height: "60px"
        },
        body: {
            height: "40vh"
        },
        row:{
            height: "60px"
        }
    },
    keyColumn: 'id',
    font: "Arial",
    data: {
        columns: [ 
            {
                id: "id",
                label: "id",
                colSize: 10,
                editable: true,
                required: true,
                dataType: "text",
                valueVerification: (val) => {
                    let error = val === "whatever" ? true : false;
                    let message = val === "whatever" ? "Value is not valid" : "";
                    return {
                        error: error,
                        message: message,
                    }
                }
            },
            {
                id: "name",
                label: "name",
                colSize: 20,
                editable: false,
                dataType: "text"
            },
            {
                id: "age",
                label: "age",
                colSize: 3,
                editable: true,
                required: false,
                dataType: "number",
                valueVerification: (val) => {
                    let error = val > 100 ? true : false;
                    let message = val > 100 ? "Value is too big" : "";
                    return {
                        error: error,
                        message: message,
                    }
                }
            },
            {
                id: "adult",
                label: "adult",
                colSize: 0,
                editable: false,
                dataType: "boolean",
                inputType:"checkbox",
            },
            {
                id: "birthDate",
                label: "birth date",
                colSize: 10,
                editable: false,
                dataType: "date"
                inputType: "date",
                dateFormat: "YYYY-MM-DDTHH:MM:ss"
            },
        ],
        rows: [
            {
                id: "50cf",
                age: 28,
                name: "Kerr Mayo",
                adult: true,
                birthDate: "1972-09-04T11:09:59",
            },
            {
                id: "209",
                age: 34,
                name: "Freda Bowman",
                adult: true,
                birthDate: "1988-03-14T09:03:19"
            },
            {
                id: "2dd81ef",
                age: 14,
                name: "Becky Lawrence",
                adult: false,
                birthDate: "1969-02-10T04:02:44"
            }
        ],
    },
    features: {
        canEdit: true,
        canPrint: true,
        canDownload: true,
        canSearch: true,
        canRefreshRows: true,
        canFilterColumns: true,
        canSaveUserConfiguration: true,
        userConfiguration: {
            columnsOrder: ["id", "name", "age"],
            copyToClipboard: true,
        },
        rowsPerPage: {
            available: [10, 25, 50, 100],
            selected: 50
        },
        selection: {
            rowsSelectable: true,
            selectPageRows: true,
            selectAllRows: false,
        },
        additionalIcons: [
            {
                tooltip: 'Coffee',
                icon: <CoffeeIcon/>,
                onClick: () => alert('Coffee Time!')
            }
        ],
        selectionIcons: [
            {
                tooltip: 'Selected Rows',
                icon: <CallSplitIcon />,
                onClick: (rows) => console.log(rows)
            }
        ]  
    }
}

1.2 Props Types

1.2.1 Global properties

Property Type Required? Default Description
title string no " " Title of the datatable.
dimensions .datatable .width string no "100vw" Width of the the Datatable. (in vw or px)
dimensions .header .height string no "60px" Height of the header of the Datatable. (in vh or px)
dimensions .body .height string no "300px" Height of the body of the Datatable. (in vh or px)
dimensions .row .height string no "60px" Height of each row of the Datatable. (in vh or px)
keyColumn string yes / Name of the column that has unique value and allows to identify a row.
font string no "Arial" Name of the font you are using. It allows the datatable to calculate the overlapping of cells.
data .columns array of object yes / An array of objects where each object is defined by this keys. Click here to have more information.
data .rows array of object yes / An array of objects where each object is defined by the columns identifier as key and the value.
features .canEdit boolean no false If the user can edit the rows.
features .canPrint boolean no false If you want stripped rows.
features .canDownload boolean no false If the user can download the data.
features .canSearch boolean no false If the user can filter the data by text through a search input.
features .canRefreshRows boolean no false If the user can click to refresh the rows.
features .canFilterColumns boolean false no If the user can select the columns to display.
features .canSaveUserConfiguration boolean no false If the user can save his columns configuration. (order and which one is displayed)
features .userConfiguration .columnsOrder array of strings no [ ] An array of strings where the strings are the column identifier. Datatable will be rendered only with the columns present in the array.
features .userConfiguration .copyToClipboard boolean no true If true, when the user click on cell it will copy the value in the clipboard.
features .rowsPerPage .available array of number no [10, 25, 50, 100] An array with the numbers of rows per page you want available.
features .rowsPerPage .selected number no 50 The number of rows per page selected by default.
features .selection .rowsSelectable boolean no false If the user can select row.
features .selection .selectPageRows boolean no false If the user can do a global select and it selects all the rows of the current page.
features .selection .selectAllRows boolean no false If the user can do a global select and it selects all the rows.
features .selection .additionalIcons array of object no [ ] If you want to add icon which trigger a function. Click here to have more information.
features .selection .selectionIcons array of object no [ ] If you want to add icon which execute a function with the rows selected. Click here to have more information.

1.2.2 Columns props

Columns is an array of object construct with these keys :

Property Type Required? Description
id string yes Id of the column (need to be unique).
label string yes Label of the column.
colSize number yes Size of the column (between 1 and 15).
editable boolean no If the each value of row corresponding to the column is editable or not.
dataType string no Possible values: test, number, data.
inputType string no Possible values: checkbox, datePicker, select.
dateFormat string yes (only if datatype = date) Format of the date.
values array yes (only if inputType = select) Possible values displayed in the select.
required boolean no If the field can't be empty.
valueVerification function no If you want to verify value on save. You need to provide a function which take a parameter (the new value) and return and object in this format {error: boolan, message: string}

1.2.3 Additional icons props

Additional icons is an array of object construct with these keys :

Property Type Required? Description
tooltip string no Description of the button. The text will be displayed on hovering the icon
icon Component yes Use @material-ui/icons to provide an icon to display.
onClick function yes A function that doesn't take parameter.

1.2.4 Additional selection icons props

Additional selection icons is an array of object construct with these keys :

Property Type Required? Description
tooltip string no Description of the button. The text will be displayed on hovering the icon
icon Component yes Use @material-ui/icons to provide an icon to display.
onClick function yes A function that takes a parameter (the selected rows).

2. Features

This is the list of the "user" features. If you want to see the development process, you can see it here and also here.

2.1 Handling millions of rows

In order to develop something generic and reusable the datatable needs to handle infinite amount of rows without lag on the interface. To respond to that problem, implement a virtualization library: react-infinite. Virtualization is process where only what is visible to the user is insert in the html dom. On scrolling, the library is going to inject new elements.

2.2 Row selection

Users can select a row by clicking on a checkbox positioned at the left. Users can click on a checkbox on the column level and it will select either all the rows of the datatable. The selecting parameter is an option provided by the developer using the component :

features: {
    canSelectRow: true
    ...
}

2.3 Pagination

Users can navigate across pages by clicking on "next" and "previous" button. They can select a page by a select input. They can select the number of rows per page. The developer can provide as option the number of rows per page and which one is selected by default :

features: {
    rowsPerPage: {
        available: [10, 25, 50, 100],
        selected: 50
    },
    ...
}

2.4 Column drag and drop

Users can drag and drop column to reorder as they please. Use a library as react-sortable-hoc.

2.4 Columns display

Users can choose which columns to display. There is a button on top right (in the header), on click opens a modal with the list of column with a checkbox to display/hide.

columnsdisplay

This feature is in option :

features {
    canFilterColumns: true,
    ...
}

2.6 Save user configuration

Users can save their columns configuration and copy to clipboard option. There is a button on top right (in the header), on click opens a modal as the following picture.

saveuserconfiguration

On save, it will send to the developer the user configuration in this format :

userConfiguration: {
    columnsOrder: ["column1", "column4", "column5"], // Array where each element is the id of the column
    copyToClipboard: true,
}

The developer can give the user configuration in this format :

features: {
    userConfiguration: {
        columnsOrder: ["column1", "column4", "column5"], // Array where each element is the id of the column
        copyToClipboard: true,
    },
    ...
}

2.7 Columns sorting

Users can click on a column's name to sort by asc or desc. Users can sort by multiple column, use this library: fast-sort.

2.8 Filter by search

Users can filter rows by search input present in the header. This feature is in option :

features {
    canSearch: true,
    ...
}

2.9 Row editing

Users can edit some values of a row according to what the developer has defined. Either all rows are editable or there is an icon on the left of each row which toggle the edit mode on. Values that need to be handled: text (input), number (input), date (material-ui-pickers, select, boolean (checkbox). Developper can provide new datatype by giving a component. The developer can defined if rows are editable or not :

features: {
    canEdit: true,
    ...
}

If some columns shouldn't be editable the developer has to defined it in the columnInfo :

data: {
    columns: [ 
        {
            editable: false,
            ...
        }
    ],
    ...
}

2.10 Revert changes

Users can revert their changes by clicking on a button on the top right (in the header).

2.11 Save changes

Users can save the changes his just made by clicking on a button on the top right. Clicking on the button gives to the developer an array of object where each object is composed of the id of the edited row and the id of the column as key and the new value :

[
    {id: "4556e", name: "Louis", age: 45},
    {id: "7891e", hasChild: true},
    ...
]

2.12 Refresh rows

Users can click on a button on the top right (in the header), it will send a "notification" to the developer that the user wants to refresh the rows. This feature is in option :

features {
    canRefreshRows: true,
    ...
}

2.13 Download data

Users can click on a button on the top right (in the header), it will open a modal where they can choose the output format, the name and which rows download.

downloaddata

This feature is in option :

features {
    canDownload true,
    ...
}

2.14 Print datatable

Users can click on a button on the top right (in the header), it will open the print mode. This feature is in option :

features {
    canPrint: true,
    ...
}

2.15 Adding icons

The developer can add new button to the top right (in the header) which will trigger a function. He can also add button that trigger a function on selected rows. He can select the position of the button.

features: {
    additionalIcons: [
            {
                tooltip: 'Coffee',
                icon: <CoffeeIcon/>,
                position: 1,
                onClick: () => alert('Coffee Time!')
            }
    ],
    selectionIcons: [
            {
                tooltip: 'Selected Rows',
                icon: <CallSplitIcon />,
                position: 5,
                onClick: (rows) => console.log(rows)
            }
    ], 
    ...
}

2.16 Cells content overlap

If the content of a cell is too long for the cells, the overflow is hidden and display "...". On hover it will display a tooltip with the complete value. Use this library to calculate if content is too big : text-width.

hover

3. Using external library

You are free to use external library to help you in the development but at two conditions :

  • The library must be free to use and distribute commercial (eg: MIT)
  • You can't use a component library except if this one is a wrapper
// You can't use this one
<YourExternalLibrary data={data} />

// You can use this one
<YourExternalLibrary>
    {data}
    // Other stuff here
</YourExternalLibrary>

4. Bundling project

We are using Babel and Webpack to transform our project into a library.

4.1 Babel

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

4.2 Webpack

At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph which maps every module your project needs and generates one or more bundles.

5. Code Quality

In order to have a good quality of homogeneous code we are using some tools which are Eslint, Prettier and Husky

5.1 Eslint

Code linting is a type of static analysis that is frequently used to find problematic patterns or code that doesn’t adhere to certain style guidelines. JavaScript, being a dynamic and loosely-typed language, is especially prone to developer error. Without the benefit of a compilation process, JavaScript code is typically executed in order to find syntax or other errors. Linting tools like ESLint allow developers to discover problems with their JavaScript code without executing it. We are using the Airbnb preset.

5.2 Prettier

Prettier enforces a consistent code style (i.e. code formatting that won't affect the AST) across your entire codebase because it disregards the original styling* by parsing it away and re-printing the parsed AST with its own rules that take the maximum line length into account, wrapping code when necessary.

5.3 Husky

Husky is here to prevent bad commit and push. Before committing any changes, husky will launch eslint and prettier. If there is any error, your commit wont be effective, you need to correct your code and commit again.

6. Testing

Testing is necessary to keep the code safe. There is multiple type of testing but we will concentrate on these three :

testing

We will split our testing like this (according to google advise) :

  • 70% of unit testing
  • 20% of integration testing
  • 10% of e2e testing

6.1 Unit testing

To develop these tests, we will use Jest and Enzyme.

6.2 Integration testing

To develop these tests, we will use Enzyme.

6.3 E2E testing

To develop these tests, we will use Cypress.