# XlReactWidgetReduxTable

This component was made as an abstraction of **XlReactWidgetTable** so that it is possible to have all the main functionality of the table (filtering, paging, sorting), without programatically writing all the logic for it. In this component it is possible to pass the **config** object, defining all the parameters declaratively.

For the component itself to work it is required to include ***xlReactWidgetReduxTableReducer*** in your reducer config and ***xlReactWidgetReduxTableRootSaga*** in your redux-saga config. They are available as exports from **'xl-react-components'**. 

### Exports
```
- xlReactWidgetReduxTableReducer
- xlReactWidgetReduxTableRootSaga
- getTableById(tableId)
- getTables()
- getLayout(tableId)
- getSortingWithSortKey(tableId)
- getSorting(tableId)
- xlReactWidgetReduxTableActions
    - changeLoadingState(tableId, loading)
    - delete(tableId)
    - initialize({
        api,
        tableId,
        filters,
        filtersDebounceTime = 500,
        layout,
        loading,
        paging,
        sorting
    })
    - mergeContext(tableId, context)
    - paginate(tableId, api, page, resultsPerPage)
    - refresh({api, tableId})
    - show({data, error, tableId, total})
    - sort(tableId, api, name, order)
    - setFilters(tableId, filter) (for internal use)
    - setPaging(tableId, page, resultsPerPage) (for internal use)
    - setSorting(tableId, name, order) (for internal use)
```

### Usage
#### Create a resource function for the table
Redux table works in such a way that for every filtering, paging or sorting event it automatically calls the API function that is provided to it.
The method prototype is
- `fetchDataApi(filters, paging, sorting)`

The parameters are objects with these example properties:
- filter: `{filterParam1: 'filterValue1', filterParam2: 'filterValue2', filterParam3: 'filterValue3'}`
- paging: `{page: 1, resultsPerPage: 10}`
- sorting: `{name: 'sortParamName', order: 'asc/desc'}`

The function must return an object that contains data array and a headers object with 'x-total-count' for determining total row count.
- `{data: [...], headers: {'x-total-count': ...}`

An example of the resource method can be:

```js
const getApplicationsResource = (filter, paging, sorting) =>
	getRequest('/applications', {
		...filter,
		page: paging.page,
		resultsPerPage: paging.resultsPerPage,
		order: `${sorting.name}:${sorting.order}`
	});
```

This function would call an external resource that would return a Promise, which would resolve to an object with form
- `{data: [...], headers: {'x-total-count': ...}`

The function can return a promise, an object with the structure specified above, or it can be a generator function that will be run by the saga middleware. The only thing that is mandatory is for it to return the object with the specified data structure.


Generator function resource method can be used if additional data mapping is needed, or if the data is not fetched from a remote resource, but it is already available in the store. Another example is if the data is fetched from multiple resources.

For example:

```js
export function* getCisWithPermissionsResource(filter, paging, sorting) {
	const {data, headers} = yield call(getCIs, filter, paging, sorting);
	const {data: grantedPermissions} = yield call(getGrantedPermissions);
	const cisWithPermissions = mergeCisWithPermissions(data, grantedPermissions);
	return {data: cisWithPermissions, headers};
}
```

If some parameters for the resource method are only available at runtime, you can create a curried function:

```js
const getApplicationsByEnvironmentResource = (environmentId) => (filter, paging, sorting) =>
	getRequest(`/applications/by-environment/${environmentId}`, {
		...filter,
		page: paging.page,
		resultsPerPage: paging.resultsPerPage,
		order: `${sorting.name}:${sorting.order}`
	});
```

#### Initialize the table by dispatching an "initialize" Redux action
Every table must be initialized before rendering it with React. For example, if we are are using the table in *flow* logic (a new tab), we must initialize it before dispatching an action for starting a new flow. The table config must contain a **tableId** which will be used in the React component.

The action creator prototype is defined above in [Exports](###Exports)

Example:

```js
import {xlReactWidgetTableActions} from 'xl-react-components';
import {nextId} from '../utils/id-generator';
import {envHostResource} from '../resource/env-host-resource';

const tableInitialState = {
    filters: [{
            className: 'name-filter',
            fieldPath: 'name', 
            key: 'environmentId',
            label: 'Name',
            model: '',
            placeholder: 'Search by name',
            position: {
                column: 0,
                row: 0
            },
            type: 'input'
    }],
    layout: [
        {
            dataKey: 'environmentId',
            sortKey: 'nameSortParam',
            title: 'Name'
        },
        {
            dataKey: 'hostId',
            sortKey: 'hostSortParam',
            title: 'Name'
        }
    ],
    loading: false,
    paging: {
        page: 1,
        pageSizes: [
            {text: '20', value: 20},
            {text: '50', value: 50},
        ],
        resultsPerPage: 20
    },
    sorting: {
        name: 'hostId',
        order: 'asc'
    }
};

function* startNewFlow() {
    const flowId = nextId();
    const tableIdPrefix = 'environment-host-table';

    const tableId = `${tableIdPrefix}-${flowId}`;

    yield put(xlReactWidgetTableActions.initialize({
        ...tableInitialState,
        api: envHostResource,
        tableId
    }));

    yield put(flowActions.start({
        context: {},
        id: flowId
    }));
}
```

In case of using the Redux table with XLD CI Explorer *flow* logic, it is recommended to subfix the tableId with the started flow ID. If done like that, CI Explorer will automatically prune all the data for that table from Redux store when closing the flow.

Note that **api** property is optional when initializing the table. If it is not provided, table will be initialized with no data and can be refreshed later by dispatching a [refresh](###Exports) action. If it is provided, it will automatically fetch the data during the initialization phase.
	
#### The filters config
There are two ways of displaying filters with Redux table, **above** and **column**. The type is defined through props in React component.

The config must be passed as an array of the filters that we want to display.

The properties that filter object has are:
 - **className** - sets the class name on the filter HTML element
 - **fieldPath** - determines the name of the parameter that will be passed to the resource function in filters object (can be a function that receives the filter value and returns fieldPath based on that value)
 - **key** - determines which column is the filter for if the filter type is set to **column** (it must match the column name returned from the resource function)
 - **label** - sets the label for the filter if the filter type is set to **above**
 - **model** - initial state of the filter
 - **placeholder** - placeholder for the input element
 - **position** - object containing *column* and *row* properties, determines the filters position if the filter type is set to **above**
 - **type** - determines the filter kind if the filter type is set to **above** (currently only input is tested)

#### Layout config
Properties:
- **dataKey** - name of the column in the table, it must match the column name returned from the resource function
- **sortKey** - determines the name of the sort parameter that will be passed to the resource function in sorting object (for this column)
- **title** - header title for the column

#### Sorting config
The sorting property in the config object sets the initial sorting. The **name** property must match one of the column names returned from the resource function (not the sortKey set in the layout config).

#### Custom renderers
You can customize the `pageButtonRenderer` and the `sizePerPageRenderer` from the `BootstrapTable` by passing the properties into the `paging` options.
See [pageButtonRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationpagebuttonrenderer-function) and [sizePerPageRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationsizeperpagerenderer-function)

```jsx
const tableInitialState = {
    paging: {
        page: 1,
        pageSizes: [
            {text: '20', value: 20},
            {text: '50', value: 50},
        ],
        pageButtonRenderer: ({page, title, active, disabled, className}) => <h2>This is page number {page}</h2>,
        sizePerPageRenderer: ({options, currSizePerPage, onSizePerPageChange}) => <h3>Current size {currSizePerPage}</h3>,
        resultsPerPage: 20,
    }
}
```

#### Rendering in React application
To connect the data from the Redux store to the actual React component there are two main props that determine the table - **fetchDataApi** and **tableId**.

For example:
```jsx
import React, {PureComponent} from 'react';
import {XlReactWidgetReduxTable} from 'xl-react-components';
import {localeMessages} from '../../services/locale-service';
import {envHostResource} from '../resource/env-host-resource';

export default class EnvironmentHostsTableComponent extends PureComponent {
    constructor(props) {
        super(props);
        this.tableRef = React.createRef();
    }

    createColumnDefinition = (headerDef) => ({
        dataField: headerDef.dataKey,
        sort: true,
        text: headerDef.title
    });

    render() {
        return (
            <XlReactWidgetReduxTable
                createColumnDefinition={this.createColumnDefinition}
                defaultErrorMessage={localeMessages.permissionsErrorMessage}
                defaultNoDataText={localeMessages.permissionsEmptyMessage}
                fetchDataApi={envHostResource}
                filterType="above"
                keyField="environmentId"
                loadingMessage={localeMessages.tableLoadingMessage}
                register={this.tableRef}
                tableId={`environment-hosts-table-${this.props.flowId}`}
            />
        );
    }
}
```

XlReactWidgetReduxTable props:

| *prop*                                      	|                                                                        *description*                                                                        	|
|---------------------------------------------	|:-----------------------------------------------------------------------------------------------------------------------------------------------------------:	|
| className: string                           	|                                                                                                                                                             	|
| createColumnDefinition.isRequired: Function 	| Function that iterates *layout* prop and returns [column definition](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html) 	|
| defaultErrorMessage: string                 	|                                                                                                                                                             	|
| defaultNoDataText: string                   	|                                                                                                                                                             	|
| error: object                               	| Error object that was caught by the saga                                                                                                                    	|
| filterType: string                          	| "above" or "column"                                                                                                                                         	|
| keyField.isRequired: string                 	| Key field in data objects                                                                                                                                   	|
| loading: boolean                            	|                                                                                                                                                             	|
| loadingMessage: string                      	|                                                                                                                                                             	|
| onRowClick: Function                        	|                                                                                                                                                             	|
| onRowDoubleClick: Function                  	|                                                                                                                                                             	|
| register: Function                          	| Receives table ref as an argument, for assigning the ref to the parent component.                                                                           	|
| selectRow: object                           	| See: [row selection](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/row-select-props.html)                                             	|
| tableHeaderClass: string                    	|                                                                                                                                                             	|
| totalClassName: string                      	| Sets the className on the total section of pagination.                                                                                                      	|

#### Deleting the data from the Redux store after component is unmounted
If the table is not used with XLD CI Explorer *flow* logic, you must manually dispatch the [delete](###Exports) action to prune the data from the store.
