npm package: @lithiamotors/lpp-kendo-grid
Description: LPP Grid Leveraging Telerik Kendo Grid
Author: Andy Barnes
Latest version: 2.3.5

## Table of Contents

- [Table of Contents](#table-of-contents)
- [Usage](#usage)
- [Version History](#version-history)
  - [Version 2.3.5](#version-235-jrodes-122518-featuremnygren-136753)
  - [Version 2.3.4](#version-234-bugmnygren-127600)
  - [Version 2.3.3](#version-233-gradcliffe-126416)
  - [Version 2.3.2](#version-232-bugmnygren-124199)
  - [Version 2.3.1](#version-231-bugmnygren-121143)
  - [Version 2.3.0](#version-230-develop)
  - [Version 2.2.0](#version-220-develop)
  - [Version 2.0.1](#version-201-develop)
  - [Version 2.0.0](#version-200-featurecmorrissey-79230)
  - [Version 1.3.0](#version-130-featurecmorrissey-85080)
  - [Version 1.2.3](#version-123-grid-multi-select-fix)
  - [Version 1.2.2](#version-122-bugcmorrissey-68166)
  - [Version 1.2.1](#version-121-bugcmorrissey-62392)
  - [Version 1.2.0](#version-120-featurecmorrissey-42882)
  - [Version 1.1.2](#version-112-featurecmorrissey-62224)
  - [Version 1.1.1](#version-111-featurecmorrissey-49264)
  - [Version 1.1.0](#version-110-featurecmorrissey-49496)
  - [Version 1.0.2](#version-102-featurecmorrissey-45951)
  - [Version 1.0.0](#version-100-featurecmorrissey-42886)
  - [Version 1.0.0-alpha.9](#version-100-alpha9-featurecmorrissey-42886)
  - [Version 1.0.0-alpha.8](#version-100-alpha8-featurecmorrissey-42886)
  - [Version 1.0.0-alpha.7](#version-100-alpha7-featurecmorrissey-42886)
  - [Version 1.0.0-alpha.6](#version-100-alpha6-featurecmorrissey-42886)
  - [Version 1.0.0-alpha.5](#version-100-alpha5-featurecmorrissey-42886)
- [Kendo Grid Overview](#kendo-grid-overview)
- [Config](#config)
- [Current Custom Features](#current-custom-features)
- [Implementation Example](#implementation-example)
- [Implementation Notes](#implementation-notes)
- [Useful Links](#useful-links)


---

## Usage

`@lithiamotors/lpp-kendo-grid`

---

## Version History

### Version 2.3.5 (jrodes-122518-featuremnygren-136753)

- Add type definition file
- Update the available filtering options to include a range of dates. Previously a user could filter data by one exact date, but this version update increases functionality to allow a user to filter between a range of two dates.

### Version 2.3.4 (bug/mnygren-127600)

- Update the way 'data' is passed to the `<Grid />` component when multiSelect is enabled, fixing pagination issues.

### Version 2.3.3 (gradcliffe-126416)

- Revises the icons used for the display of "approved" and "unapproved" data in the grid cells.

### Version 2.3.2 (bug/mnygren-124199)

- Updates styling/UI in 'Filter > Columns'. These changes include rendering the correct checkbox color, adding a little space around the checkbox, and rendering the correct button formatting.

### Version 2.3.1 (bug/mnygren-121143)

- Updates package imports coming from Telerik/KendoReact, as the previous versions were not working well together.
- Fixes styling on 'active' className.
- Reinstates built-in styling on filter modal.

### Version 2.3.0 (develop)

- Fixes ellipses rendering on page load on 'multiSelect' column
- Fixes checkmark border color (to render light blue instead of dark blue)
- Reinstates 'active' className on filtering, pulling filter parameters from localStorage values
- Adds styling to 'active' className on filtering, so users can see which column they have filtered

### Version 2.2.0 (develop)

- Fixes to the bulk edit form input field
- Telerik Kendo Grid package updates to v5.9.0
- Updates to the Custom Styles for checkbox colors

### Version 2.0.1 (develop)

- Added comment about css prop and pragma

### Version 2.0.0 (feature/cmorrissey-79230)

- Upgraded to MUI v5
- Added theme prop, only way to pass theme from MUI to Kendo that inherits from theme provider.
- Custom row styling done via the host app will now be done using the `@emotion/styled` package. We are still able to access theme in our config, but this is the only way we can handle styling moving forward.

### Version 1.3.0 (feature/cmorrissey-85080)

- Restructured kendo grid, splitting our main grid into multiple components
- Added the `handleAppDataStateChange` callback prop to pass dataState to the Host app. Data state is still controlled locally, but we can now fetch data using query params from our data state.

### Version 1.2.3 (grid-multi-select-fix)

- Added useEffect to selectedData state
- - Issue was grid would render, selectedData would init to an empty array unless data was static, and would not update after AJAX call

### Version 1.2.2 (bug/cmorrissey-68166)

- Changed CSS selector for column elipses color styling

### Version 1.2.1 (bug/cmorrissey-62392)

** Story Change **

- Removed the '(All)' option and filter icon button from custom boolean filter. Clicking on (All) would not let us remove any current filters, and the filter icon button would just select (All) for us.

** Additional Changes **

- Removed default props object in dataState array, when clearing filter settings it would sort by ID (not intended)
- Updated story to have check on length of selectedIds, if array has 1 or more items we disable the main handleClick functionality. This is interesting and works better than expected, as normal clicking will break back to the normal flow but CMD clicking will not override the current array.

### Version 1.2.0 (feature/cmorrissey-42882)

- Added mutliSelect local kendo state from documentation (https://www.telerik.com/kendo-react-ui/components/grid/selection/#toc-customizing-the-selection)
- Added some mutliSelect paramaters into kendoConfig
- Added method to get multiSelect data in toolbar
- Added LppKendoGrid prop to pass host app props into toolbar

### Version 1.1.2 (feature/cmorrissey-62224)

- Changed importance level of dataGridRowSelected styling

### Version 1.1.1 (feature/cmorrissey-49264)

- Changed row render logic, allowing alt shading and correct hover effects for both custom and default rows
- Added some useCursor logic that will make the grid look and feel more static than interactive for future "display" grids

### Version 1.1.0 (feature/cmorrissey-49496)

- Added custom column filters, and custom cells to package.
- Updated storybook example to show these changes

### Version 1.0.2 (feature/cmorrissey-45951)

- Added "kendo:license" script in package json

### Version 1.0.0 (feature/cmorrissey-42886)

- Passing usePointerCursor to customRow

### Version 1.0.0-alpha.9 (feature/cmorrissey-42886)

- Added dataGridRowSelected to kendo component

### Version 1.0.0-alpha.8 (feature/cmorrissey-42886)

- Passing classes object to CustomRow, allows us to insert class styles via config and consume them in host apps custom row component.

### Version 1.0.0-alpha.7 (feature/cmorrissey-42886)

- Updated td and th padding

### Version 1.0.0-alpha.6 (feature/cmorrissey-42886)

- Added check to render bottom row border based on custom height
  - Tech debt for this item, if custom height is less than total contents height, renders bottom row anyway
- Added alt row shading, pointer prop to main grid

### Version 1.0.0-alpha.5 (feature/cmorrissey-42886)

- Deployed base version of Kendo Grid

---

## Kendo Grid Overview

<ul>
<li>KENDO grid is a <a href="https://en.wikipedia.org/wiki/Data_grid" target="_blank">Data Grid</a> we have chosen that fits the needs for our applications. We have been able to break out its functionality into reusable components, allowing us to preform data analysis while being modular enough to reuse across multiple platforms.</li>
</ul>

---

## Config

<ul>

<li>
Our Grid is driven by a config.js file. There will be a code snippet of our current config below this section.
</li>

<li>
<b>Properties</b>
<ul>
<li>
Properties is an object that holds a number of "props" that we want to pass to the Grid component.
</li>
</ul>
</li>

<li>
<b>Rows</b>
<ul>
<li>
The rows value takes in a component that is responsible for row styling (background color, text color programmatically rendered based on the rows data). Please refer to the LienPayoffGridRowCustom component for more info, and for the Grid itself for logic being used to render a custom row.
</li>
</ul>
</li>

<li>
<b>Columns</b>
<ul>
<li>
Columns is an array of data points. Title is what is displayed in the column, and the value of the cell is referenced using field. Show, filter, and width are pretty self-explanatory. Cell is used to render custom styling/formatting, and filterCell is custom filtering using the column drop down menu. Please ever to each of the respective components, and its implementation in KendoGrid for a better understanding.
</li>
</ul>
</li>

<li>
<b>Styles</b>
<ul>
<li>
Styles is essentially our MUI style content. We achieve this by passing our theme into our styles function, generating our custom MUI style, and inserting it into the LppKendoGrid. Note that we have this turned off in storybook, but you can turn it on either in the code or in the 'useCustomRowStyle' toggle below. It is NOT RECOMMENDED to override styles currently, as this method only provides a few key/name pairs to change and will completely override default styles.
</li>
</ul>
</li>

<li>
<b>Filter Operators</b>
<ul>
<li>
Here is where we define our filter settings, each column comes with a set of filters that allow us to find data in the grid. These are passed as a filterOperator prop to the KendoGrid component, more information can be found <a href="https://www.telerik.com/kendo-react-ui/components/grid/api/GridFilterOperators/" target="_blank">here</a> and <a href="https://www.telerik.com/kendo-react-ui/components/grid/api/GridProps/#toc-filteroperators">here</a>.
</li>
</ul>
</li>

</ul>

```jsx
const LienPayoffGridConfig = {
	properties: {
		sortable: true,
		resizable: true,
		style: { height: 'calc(100vh - 64px - 48px)' }, // 64px (Toolbar), 48px (Padding Top / Bottom)
		selectedField: 'selected',
		pageable: { pageSizes: [20, 100, 500] },
	},

	rows: LienPayoffGridRowCustom,

	columns: [
		{
			title: 'APPROVED',
			field: 'approvalStatus',
			show: true,
			filter: 'boolean',
			width: '160px',
			cell: (props) => {
				return <CustomCellCheckmark data={props.dataItem.approvalStatus} />;
			},
			filterCell: (props) => (
				<CustomColumnApprovedStatus
					{...props} // Needed to bind Kendo event methods and state (onChange, form values, etc)
					data={['Approved', 'Unapproved']}
					defaultItem="(All)"
				/>
			),
		},
		{
			title: 'ID',
			field: 'dealNumber',
			show: true,
			filter: 'text',
			width: '160px',
			cell: (props) => {
				return <CustomTradeId data={props.dataItem} />;
			},
		},
		{
			title: 'STORE #',
			field: 'storeNumber',
			show: true,
			width: '120px',
		},
		{
			title: 'GROUP',
			field: 'filterGroup',
			show: true,
			width: '120px',
		},
		{
			title: 'CUSTOMER',
			field: 'customerName',
			show: true,
			width: '320px',
		},
		{
			title: 'TRADE VIN',
			field: 'tradeVin',
			show: true,
			width: '200px',
		},
		{
			title: 'CIT',
			field: 'cit',
			show: true,
			width: '120px',
			cell: (props) => {
				return <CustomNumberFormat data={props.dataItem.cit} />;
			},
		},
		{
			title: 'VEH REC',
			field: 'vehRec',
			show: true,
			width: '160px',
			cell: (props) => {
				return <CustomNumberFormat data={props.dataItem.vehRec} />;
			},
		},
		{
			title: 'LPO BALANCE',
			field: 'lpoBalance',
			show: true,
			width: '160px',
			cell: (props) => {
				return <CustomNumberFormat data={props.dataItem.lpoBalance} />;
			},
		},
		{
			title: 'LIEN PAYOFF AMOUNT',
			field: 'payoff',
			show: true,
			width: '240px',
			cell: (props) => {
				return <CustomNumberFormat data={props.dataItem.payoff} />;
			},
		},
		{
			title: 'ADJUSTED PAYOFF AMOUNT',
			field: 'adjPayoff',
			show: true,
			width: '240px',
			cell: (props) => {
				return <CustomNumberFormat data={props.dataItem.adjPayoff} />;
			},
		},
		{
			title: 'SOLD DATE',
			field: 'soldDate',
			show: true,
			width: '160px',
			format: '{0: MM/dd/yyyy}',
			cell: (props) => {
				return <CustomSoldDateFormat data={props.dataItem} />;
			},
		},
		{
			title: 'CRITERIA',
			field: 'criteria',
			show: true,
			width: '160',
		},
		{
			title: 'FUNDING BANK',
			field: 'bank',
			show: true,
			width: '280px',
		},
		{
			title: 'LIEN BANK',
			field: 'lienBank',
			show: true,
			width: '240px',
		},
		{
			title: 'DOCUMENTS',
			field: 'hasDocuments',
			show: true,
			width: '160px',
			cell: (props) => {
				return <CustomCellDocument data={props.dataItem.hasDocuments} />;
			},
			filterCell: (props) => (
				<CustomColumnDocuments
					{...props} // Needed to bind Kendo event methods and state (onChange, form values, etc)
					data={['Has Documents', 'No Documents']}
					defaultItem="(All)"
				/>
			),
		},
		{
			title: 'FUND NOTICE',
			field: 'fundingNotice',
			show: true,
			width: '160px',
			filter: 'boolean',
			cell: (props) => {
				return <CustomCellCheckmark data={props.dataItem.fundingNotice} />;
			},
		},
		{
			title: 'NOTES',
			field: 'notes',
			show: true,
			width: '320px',
			cell: (props) => {
				return <CustomCellNotes data={props.dataItem} />;
			},
		},
	],

	styles: (theme) => {
		return {
			icon: {
				width: '100%',
				margin: '0 auto',
			},
			approvedIcon: { color: green[500] },
			unapprovedIcon: { color: red[500] },
			documentIcon: { color: blue[500] },
	},

	filterOperators: {
		text: [{ text: 'grid.filterContainsOperator', operator: 'contains' }],
		numeric: [{ text: 'grid.filterEqOperator', operator: 'eq' }],
		date: [{ text: 'grid.filterEqOperator', operator: 'eq' }],
		boolean: [{ text: 'grid.filterEqOperator', operator: 'eq' }],
	},
};
```

---

## Current Custom Features

<ul>

<li>
<b>Data Export</b>
<ul>
<li>
The Grid comes with a method that is passed to a toolbar as a prop. This method is called kendoDataToToolbar, and when called will return an array with the current grid filter and sort settings allowing you to parse this out in the host application to return a CSV or any other file type.
</li>
</ul>
</li>

<li>
<b>Local Storage</b>
<ul>
<li>
Local storage was added as a quality of life improvement. Currently, it is baked into the grid, with no way to remove the functionality, although this may change in the future if we want to make localStorage optional. If you are using more than one kendo grid in an application, you must pass in an object with localColumnFilter: 'Insert Unique Name'. This is because local storage will affect each grid the same if namespacing isn't given.

</li>
</ul>
</li>

<li>
<b>Clear Local Storage</b>
<ul>
<li>
This is a callback passed directly to the Toolbar component, if it is provided. This will set local storage to its inital state, useful for clearing all filters/sorting quickly. Please see the implementation section for more details.

</li>
</ul>
</li>

</ul>

---

## Implementation Notes

<ul>

<li>
<b>Data</b>
<ul>
<li>
Data takes in an array of objects. This is the data that is displayed inside the grid, please make sure that when defining your columns config, that the appropriate keys are matching the objects.
</li>
</ul>
</li>

<li>
<b>Data State</b>
<ul>
<li>
Data State is essentially initialState for the grid. It contains some default setup values, or if localStorage is present will default to localStorage instead. We defined ours in context, but this could be done at the component level as well.

</li>
</ul>
</li>

<li>
<b>Handle Row Click</b>
<ul>
<li>
This prop accepts a callback method that returns the selected row data. In the callback we defined in LPO, we dispatch an action that updates our context to have a 'selectedID' that renders an additional Details panel component, but you can do anything you want with the callback.
</li>
</ul>
</li>

<li>
<b>Config</b>
<ul>
<li>
Config has been explained above, necessary prop as it tells which values to put in what columns.
</li>
</ul>
</li>

<li>
<b>Toolbar</b>
<ul>
<li>
Toolbar is an optional component, when passed in is rendered at the top of the grid. This can be as simple as a div, or something much more complex. We have multiple action items in our toolbar, as well as a global search feature and button to reset our local storage.
</li>
</ul>
</li>

<li>
<b>Helpful Tip for Testing Kendo Changes On A Project-Level </b>
<ul>
<li>
It can be tricky to test changes made to Kendo in the React Component Library, since we need to publish a new version before we can bring the changes into a project. There is a workaround! If you want to see the functioning behaviors of Kendo on a project-level before publishing, do this:
	<ul>
	<li>In the project (such as LPO or DMM), create a folder near the .config file</li>
	<li>In this new folder, paste the entire `lib` folder found in the  React Component library at RCL > packages > lpp-kendo-grid > lib. This includes the components folder and the `index.js` file. </li>
	<li>In the project, find where LppStandardKendoGrid is being imported from @lithiamotors/lpp-kendo-grid. Comment this out, and instead import LppStandardKendoGrid from the BaseGrid file of the folder that was copied over from RCL. (Like this: import LppStandardKendoGrid from './components/KENDO/lib/components/BaseGrid';). </li>
	<li>If the project is using the same package versions from Telerik as RCL, this should work! If not, you may need to update the local versions using npm. This can be found by comparing the project's package-lock.json file with the package-lock.json file in the lpp-kendo-grid folder in RCL. A good place to start is to search for is kendo-react-grid and compare versions. If you need to update the local versions, Just remember to undo this! </li>
    </ul>
</li>
</ul>
</li>

</ul>

---

## Implementation Example

```jsx
import './kendo-css.css'; // Required for pagination styles
import { MuiThemeProvider, Button } from '@material-ui/core';
import LppMuiTheme from '../../packages/lpp-mui-theme/dist/lpp-mui-theme';
import { LppStandardKendoGrid } from '../../packages/lpp-kendo-grid/dist/lpp-kendo-grid';
import GridConfig from './config';

const Toolbar = ({ kendoDataToToolbar, handleRemoveLocalStorage }) => {
	return (
		<div>
			<Button
				style={{ marginLeft: 16 }}
				color="primary"
				variant="contained"
				disabled
			>
				IMPORT
			</Button>{' '}
			<Button
				style={{ backgroundColor: 'green', color: 'white', marginLeft: 16 }}
				variant="contained"
				onClick={() =>
					alert(JSON.stringify(kendoDataToToolbar().data, null, 4))
				}
			>
				EXPORT GRID DATA
			</Button>
			<Button
				style={{ backgroundColor: 'orange', color: 'white', marginLeft: 16 }}
				variant="contained"
				onClick={() => handleRemoveLocalStorage()}
			>
				CLEAR FILTERS
			</Button>
		</div>
	);
};

const App = (props) => {
	const data = [
		{
			name: 'Cory',
			state: 'Oregon',
			position: 'Software Engineer',
		},
		{
			name: 'Andy',
			state: 'Oregon',
			position: 'Senior Mangager Application Development',
		},
		{
			name: 'Daylon',
			state: 'California',
			position: 'Software Engineer',
		},
		{
			name: 'Katie',
			state: 'New Mexico',
			position: 'Business Analyst',
		},
		{
			name: 'Adam',
			state: 'Oregon',
			position: 'Senior UX/UI Designer',
		},
	];

	return (
		<MuiThemeProvider theme={LppMuiTheme}>
			<LppStandardKendoGrid
				data={data}
				config={GridConfig}
				Toolbar={props.useToolbar && Toolbar}
				localStorageMapper={{
					localColumnFilter: 'lpp-kendo-grid-local-storage',
					localDataState: 'lpp-kendo-grid-local-datastate',
				}}
			/>
		</MuiThemeProvider>
	);
};

export default App;
```

---

## Useful Links

<ul>
<li><a href="https://www.telerik.com/kendo-react-ui/components/introduction/" target="_blank">Kendo React Frontpage</a></li>
<li><a href="https://www.telerik.com/kendo-react-ui/components/grid/api/GridFilterOperators/" target="_blank">Default Filter Operators</a></li>
<li><a href="https://www.telerik.com/kendo-react-ui/components/grid/api/GridProps/" target="_blank">Grid Props</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" target="_blank">Local Storage</a></li>
</ul>

---