Datatables for React using Material-UI - https://www.material-ui-datatables.com

Last update: May 11, 2022

MUI-Datatables - Datatables for Material-UI

Build Status NPM Downloads Coverage Status npm version

MUI-Datatables is a responsive datatables component built on Material-UI. It comes with features like filtering, resizable columns, view/hide columns, draggable columns, search, export to CSV download, printing, selectable rows, expandable rows, pagination, and sorting. On top of the ability to customize styling on most views, there are three responsive modes "vertical", "standard", and "simple" for mobile/tablet devices.

Version 3 has been released! You can read about the updates here!

Table of contents

Install

npm install mui-datatables --save

If your project doesn't already use them, you need to install @material-ui/core and @material-ui/icons as well.

Demo

Edit react-to-print

Browse live demos of all examples in this repo in here!

Usage

For a simple table:

import MUIDataTable from "mui-datatables";

const columns = ["Name", "Company", "City", "State"];

const data = [
 ["Joe James", "Test Corp", "Yonkers", "NY"],
 ["John Walsh", "Test Corp", "Hartford", "CT"],
 ["Bob Herm", "Test Corp", "Tampa", "FL"],
 ["James Houston", "Test Corp", "Dallas", "TX"],
];

const options = {
  filterType: 'checkbox',
};

<MUIDataTable
  title={"Employee List"}
  data={data}
  columns={columns}
  options={options}
/>

Or customize columns:

import MUIDataTable from "mui-datatables";

const columns = [
 {
  name: "name",
  label: "Name",
  options: {
   filter: true,
   sort: true,
  }
 },
 {
  name: "company",
  label: "Company",
  options: {
   filter: true,
   sort: false,
  }
 },
 {
  name: "city",
  label: "City",
  options: {
   filter: true,
   sort: false,
  }
 },
 {
  name: "state",
  label: "State",
  options: {
   filter: true,
   sort: false,
  }
 },
];

const data = [
 { name: "Joe James", company: "Test Corp", city: "Yonkers", state: "NY" },
 { name: "John Walsh", company: "Test Corp", city: "Hartford", state: "CT" },
 { name: "Bob Herm", company: "Test Corp", city: "Tampa", state: "FL" },
 { name: "James Houston", company: "Test Corp", city: "Dallas", state: "TX" },
];

const options = {
  filterType: 'checkbox',
};

<MUIDataTable
  title={"Employee List"}
  data={data}
  columns={columns}
  options={options}
/>

API

<MUIDataTable />

The component accepts the following props:

Name Type Description
title array Title used to caption table
columns array Columns used to describe table. Must be either an array of simple strings or objects describing a column
data array Data used to describe table. Must be either an array containing objects of key/value pairs with values that are strings or numbers, or arrays of strings or numbers (Ex: data: [{"Name": "Joe", "Job Title": "Plumber", "Age": 30}, {"Name": "Jane", "Job Title": "Electrician", "Age": 45}] or data: [["Joe", "Plumber", 30], ["Jane", "Electrician", 45]]). The customBodyRender and customBodyRenderLite options can be used to control the data display.
options object Options used to describe table
components object Custom components used to render the table

Options:

Name Type Default Description
caseSensitive boolean false Enable/disable case sensitivity for search.
confirmFilters boolean false Works in conjunction with the customFilterDialogFooter option and makes it so filters have to be confirmed before being applied to the table. When this option is true, the customFilterDialogFooter callback will receive an applyFilters function which, when called, will apply the filters to the table. Example
columnOrder array An array of numbers (column indices) indicating the order the columns should be displayed in. Defaults to the order provided by the Columns prop. This option is useful if you'd like certain columns to swap positions (see draggableColumns option).
count number User provided override for total number of rows.
customFilterDialogFooter function Add a custom footer to the filter dialog. customFilterDialogFooter(curentFilterList: array, applyFilters: function) => React Component
customFooter function Render a custom table footer. function(count, page, rowsPerPage, changeRowsPerPage, changePage, textLabels: object) => string| React Component Example
customRowRender function Override default row rendering with custom function. customRowRender(data, dataIndex, rowIndex) => React Component
customSearch function Override default search with custom function. customSearch(searchQuery: string, currentRow: array, columns: array) => boolean
customSearchRender function Render a custom table search. customSearchRender(searchText: string, handleSearch, hideSearch, options) => React Component
customSort function Override default sorting with custom function. If you just need to override the sorting for a particular column, see the sortCompare method in the column options. function(data: array, colIndex: number, order: string) => array Example
customTableBodyFooterRender function Render a footer under the table body but above the table's standard footer. This is useful for creating footers for individual columns. Example
customToolbar function Render a custom toolbar function({displayData}) => React Component
customToolbarSelect function Render a custom selected rows toolbar. function(selectedRows, displayData, setSelectedRows) => void
download boolean or string true Show/hide download icon from toolbar. Possible values:

  • true: Button is visiable and clickable.
  • false: Button is not visible.
  • disabled: Button is visible, but not clickable.

downloadOptions object see -> An object of options to change the output of the CSV file:

  • filename: string
  • separator: string
  • filterOptions: object
    • useDisplayedColumnsOnly: boolean
    • useDisplayedRowsOnly: boolean

Default Value:{filename: 'tableDownload.csv', separator: ','}

draggableColumns object {} An object of options describing how dragging columns should work. The options are:

  • enabled:boolean: Indicates if draggable columns are enabled. Defaults to false.
  • transitionTime:number: The time in milliseconds it takes for columns to swap positions. Defaults to 300.

To disable the dragging of a particular column, see the "draggable" option in the columns options. Dragging a column to a new position updates the columnOrder array and triggers the onColumnOrderChange callback.
elevation number 4 Shadow depth applied to Paper component.
enableNestedDataAccess string "" If provided a non-empty string (ex: "."), it will use that value in the column's names to access nested data. For example, given a enableNestedDataAccess value of "." and a column name of "phone.cell", the column would use the value found in phone:{cell:"555-5555"}. Any amount of nesting will work. Example demonstrates the functionality.
expandableRows boolean false Enable/disable expandable rows. Example
expandableRowsHeader boolean true Show/hide the expand all/collapse all row header for expandable rows.
expandableRowsOnClick boolean false Enable/disable expand trigger when row is clicked. When False, only expand icon will trigger this action.
filter boolean or string true Show/hide filter icon from toolbar. Possible values:

  • true: Button is visiable and clickable.
  • false: Button is not visible.
  • disabled: Button is visible, but not clickable.

filterArrayFullMatch boolean true For array values, default checks if all the filter values are included in the array. If false, checks if at least one of the filter values is in the array.
filterType string Choice of filtering view. enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom')
fixedHeader boolean true Enable/disable a fixed header for the table Example
fixedSelectColumn boolean true Enable/disable fixed select column. Example
isRowExpandable function Enable/disable expansion or collapse on certain expandable rows with custom function. Will be considered true if not provided. function(dataIndex: number, expandedRows: object(lookup: {dataIndex: number}, data: arrayOfObjects: {index: number, dataIndex: number})) => boolean.
isRowSelectable function Enable/disable selection on certain rows with custom function. Returns true if not provided. function(dataIndex: number, selectedRows: object(lookup: {dataindex: boolean}, data: arrayOfObjects: {index, dataIndex})) => boolean.
jumpToPage boolean false When true, this option adds a dropdown to the table's footer that allows a user to navigate to a specific page. Example
onCellClick function Callback function that triggers when a cell is clicked. function(colData: any, cellMeta: { colIndex: number, rowIndex: number, dataIndex: number }) => void
onChangePage function Callback function that triggers when a page has changed. function(currentPage: number) => void
onChangeRowsPerPage function Callback function that triggers when the number of rows per page has changed. function(numberOfRows: number) => void
onColumnOrderChange function Callback function that triggers when a column has been dragged to a new location. function(newColumnOrder:array, columnIndex:number, newPosition:number) => void
onColumnSortChange function Callback function that triggers when a column has been sorted. function(changedColumn: string, direction: string) => void
onDownload function A callback function that triggers when the user downloads the CSV file. In the callback, you can control what is written to the CSV file. This method can be used to add the Excel specific BOM character (see this example). function(buildHead: (columns) => string, buildBody: (data) => string, columns, data) => string. Return false to cancel download of file.
onFilterChange function Callback function that triggers when filters have changed. function(changedColumn: string, filterList: array, type: enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom', 'chip', 'reset'), changedColumnIndex, displayData) => void
onFilterChipClose function Callback function that is triggered when a user clicks the "X" on a filter chip. function(index : number, removedFilter : string, filterList : array) => void Example
onFilterConfirm function Callback function that is triggered when a user presses the "confirm" button on the filter popover. This occurs only if you've set confirmFilters option to true. function(filterList: array) => void Example
onFilterDialogClose function Callback function that triggers when the filter dialog closes. function() => void
onFilterDialogOpen function Callback function that triggers when the filter dialog opens. function() => void
onRowClick function Callback function that triggers when a row is clicked. function(rowData: string[], rowMeta: { dataIndex: number, rowIndex: number }) => void
onRowExpansionChange function Callback function that triggers when row(s) are expanded/collapsed. function(currentRowsExpanded: array, allRowsExpanded: array, rowsExpanded: array) => void
onRowsDelete function Callback function that triggers when row(s) are deleted. function(rowsDeleted: object(lookup: {[dataIndex]: boolean}, data: arrayOfObjects: {index: number, dataIndex: number}), newTableData) => void OR false (Returning false prevents row deletion.)
onRowSelectionChange function Callback function that triggers when row(s) are selected/deselected. function(currentRowsSelected: array, allRowsSelected: array, rowsSelected: array) => void
onSearchChange function Callback function that triggers when the search text value has changed. function(searchText: string) => void
onSearchClose function Callback function that triggers when the searchbox closes. function() => void
onSearchOpen function Callback function that triggers when the searchbox opens. function() => void
onTableChange function Callback function that triggers when table state has changed. function(action: string, tableState: object) => void
onTableInit function Callback function that triggers when table state has been initialized. function(action: string, tableState: object) => void
onViewColumnsChange function Callback function that triggers when a column view has been changed. Previously known as onColumnViewChange. function(changedColumn: string, action: string) => void
page number User provided page for pagination.
pagination boolean true Enable/disable pagination.
print boolean or string true Show/hide print icon from toolbar. Possible values:

  • true: Button is visiable and clickable.
  • false: Button is not visible.
  • disabled: Button is visible, but not clickable.

renderExpandableRow function Render expandable row. function(rowData, rowMeta) => React Component Example
resizableColumns boolean false Enable/disable resizable columns.
responsive string 'stacked' Enable/disable responsive table views. Options:

  • "vertical" (default value): In smaller views the table cells will collapse such that the heading is to the left of the cell value.
  • "standard": Table will stay in the standard mode but make small changes to better fit the allocated space.
  • "simple": On very small devices the table rows will collapse into simple display.

Example
rowHover boolean true Enable/disable hover style over rows.
rowsExpanded array User provided expanded rows.
rowsPerPage number 10 Number of rows allowed per page.
rowsPerPageOptions array [10,15,100] Options to provide in pagination for number of rows a user can select.
rowsSelected array User provided array of numbers (dataIndexes) which indicates the selected rows.
search boolean or string true Show/hide search icon from toolbar. Possible values:

  • true: Button is visiable and clickable.
  • false: Button is not visible.
  • disabled: Button is visible, but not clickable.

searchPlaceholder string Search text placeholder. Example
searchProps object {} Props applied to the search text box. You can set method callbacks like onBlur, onKeyUp, etc, this way. Example
searchOpen boolean false Initially displays search bar.
searchText string Search text for the table.
selectableRows string 'multiple' Indicates if rows can be selected. Options are "multiple", "single", "none".
selectableRowsHeader boolean true Show/hide the select all/deselect all checkbox header for selectable rows.
selectableRowsHideCheckboxes boolean false Hides the checkboxes that appear when selectableRows is set to "multiple" or "single". Can provide a more custom UX, especially when paired with selectableRowsOnClick.
selectableRowsOnClick boolean false Enable/disable select toggle when row is clicked. When False, only checkbox will trigger this action.
selectToolbarPlacement string 'replace' Controls the visibility of the Select Toolbar, options are 'replace' (select toolbar replaces default toolbar when a row is selected), 'above' (select toolbar will appear above default toolbar when a row is selected) and 'none' (select toolbar will never appear)
serverSide boolean false Enable remote data source.
setFilterChipProps function Is called for each filter chip and allows you to place custom props on a filter chip. function(colIndex: number, colName: string, filterValue: string) => object Example
setRowProps function Is called for each row and allows you to return custom props for this row based on its data. function(row: array, dataIndex: number, rowIndex: number) => object Example
setTableProps function Is called for the table and allows you to return custom props for the table based on its data. function() => object Example
sort boolean true Enable/disable sort on all columns.
sortFilterList boolean true Enable/disable alphanumeric sorting of filter lists.
sortOrder object {} Sets the column to sort by and its sort direction. To remove/reset sorting, input in an empty object. The object options are the column name and the direction: name: string, direction: enum('asc', 'desc') Example
tableId string auto generated A string that is used internally for identifying the table. It's auto-generated, however, if you need it set to a custom value (ex: server-side rendering), you can set it via this property.
tableBodyHeight string 'auto' CSS string for the height of the table (ex: '500px', '100%', 'auto').
tableBodyMaxHeight string CSS string for the height of the table (ex: '500px', '100%', 'auto').
textLabels object User provided labels to localize text.
viewColumns boolean or string true Show/hide viewColumns icon from toolbar. Possible values:

  • true: Button is visiable and clickable.
  • false: Button is not visible.
  • disabled: Button is visible, but not clickable.

Customize Columns

On each column object, you have the ability to customize columns to your liking with the 'options' property. Example:

const columns = [
 {
  name: "Name",
  options: {
   filter: true,
   sort: false
  }
 },
 ...
];

Column:

Name Type Description
name string Name of column (This field is required)
label string Column Header Name override
options object Options for customizing column

Column Options:

Name Type Default Description
customBodyRender function Function that returns a string or React component. Used to display data within all table cells of a given column. The value returned from this function will be used for filtering in the filter dialog. If this isn't need, you may want to consider customBodyRenderLite instead. function(value, tableMeta, updateValue) => string| React Component Example
customBodyRenderLite function Function that returns a string or React component. Used to display data within all table cells of a given column. This method performs better than customBodyRender but has the following caveats:

  • The value returned from this function is not used for filtering, so the filter dialog will use the raw data from the data array.
  • This method only gives you the dataIndex and rowIndex, leaving you to lookup the column value.

function(dataIndex, rowIndex) => string| React Component Example
customHeadLabelRender function Function that returns a string or React component. Used for creating a custom header to a column. This method only affects the display in the table's header, other areas of the table (such as the View Columns popover), will use the column's label. function(columnMeta : object) => string| React Component
customFilterListOptions object (These options only affect the filter chips that display after filters are selected. To modify the filters themselves, see filterOptions)

  • render: function that returns a string or array of strings used as the chip label(s). function(value) => string OR arrayOfStrings Example
  • update: function that returns a filterList (see above) allowing for custom filter updates when removing the filter chip. filterType must be set to "custom". function(filterList, filterPos, index) => filterList Example

customHeadRender function Function that returns a string or React component. Used as display for column header. function(columnMeta, handleToggleColumn, sortOrder) => string| React Component
display boolean or string true Display column in table. Possible values:

  • true: Column is visible and toggleable via the View Columns popover in the Toolbar.
  • false: Column is not visible but can be made visible via the View Columns popover in the Toolbar.
  • excluded: Column is not visible and not toggleable via the View Columns popover in the Toolbar.

See also: viewColumns and filter options.

download boolean true Display column in CSV download file.
draggable boolean true Determines if a column can be dragged. The draggableColumns.enabled option must also be true.
empty boolean false This denotes whether the column has data or not (for use with intentionally empty columns).
filter boolean true Display column in filter list.
filterList array Filter value list Example
filterOptions object

These options affect the filter display and functionality from the filter dialog. To modify the filter chips that display after selecting filters, see customFilterListOptions

This option is an object of several options for customizing the filter display and how filtering works.

  • names: custom names for the filter fields Example
  • logic: custom filter logic Example
  • display(filterList, onChange(filterList, index, column), index, column, filterData): Custom rendering inside the filter dialog Example. filterList must be of the same type in the main column options, that is an array of arrays, where each array corresponds to the filter list for a given column.
  • renderValue: A function to customize filter choices Example. Example use case: changing empty strings to "(empty)" in a dropdown.
  • fullWidth (boolean): Will force a filter option to take up the grid's full width.

filterType string 'dropdown' Choice of filtering view. Takes priority over global filterType option.enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom') Use 'custom' if you are supplying your own rendering via filterOptions.
hint string Display hint icon with string as tooltip on hover.
print boolean true Display column when printing.
searchable boolean true Exclude/include column from search results.
setCellHeaderProps function Is called for each header cell and allows you to return custom props for the header cell based on its data. function(columnMeta: object) => object Example
setCellProps function Is called for each cell and allows to you return custom props for this cell based on its data. function(cellValue: string, rowIndex: number, columnIndex: number) => object Example
sort boolean true Enable/disable sorting on column.
sortCompare function Custom sort function for the column. Takes in an order string and returns a function that compares the two column values. If this method and options.customSort are both defined, this method will take precedence. (order) => ({data: val1}, {data: val2}) => number Example
sortDescFirst boolean false Causes the first click on a column to sort by desc rather than asc. Example
sortThirdClickReset boolean false Allows for a third click on a column header to undo any sorting on the column. Example
viewColumns boolean true Allow user to toggle column visibility through 'View Column' list.

customHeadRender is called with these arguments:

function(columnMeta: {
  customHeadRender: func,
  display: enum('true', 'false', 'excluded'),
  filter: boolean,
  sort: boolean,
  download: boolean,
  empty: boolean,
  index: number,
  label: string,
  name: string,
  print: boolean,
  searchable: boolean,
  viewColumns: boolean
}, handleToggleColumn: function(columnIndex))

customBodyRender is called with these arguments:

function(value: any, tableMeta: {
  rowIndex: number,
  columnIndex: number,
  columnData: array, // Columns Options object
  rowData: array, // Full row data
  tableData: array, // Full table data - Please use currentTableData instead
  currentTableData: array, // The current table data
  tableState: {
    announceText: null|string,
    page: number,
    rowsPerPage: number,
    filterList: array,
    selectedRows: {
      data: array,
      lookup: object,
    },
    showResponsive: boolean,
    searchText: null|string,
  },
}, updateValue: function)

Plug-ins

The table lends itself to plug-ins in many areas, especially in the customRender functions. Many use cases for these render functions are common, so a set of plug-ins are available that you can use.

Available Plug-ins:

Name Type Default Description
debounceSearchRender function Function that returns a function for the customSearchRender method. This plug-in allows you to create a debounced search which can be useful for server-side tables and tables with large data sets. function(debounceWait) => function Example

Customize Styling

Using Material-UI theme overrides will allow you to customize styling to your liking. First, determine which component you would want to target and then lookup the override classname. Let's start with a simple example where we will change the background color of a body cell to be red:

import React from "react";
import MUIDataTable from "mui-datatables";
import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles';

class BodyCellExample extends React.Component {

  getMuiTheme = () => createMuiTheme({
    overrides: {
      MUIDataTableBodyCell: {
        root: {
          backgroundColor: "#FF0000"
        }
      }
    }
  })

  render() {

    return (
      <MuiThemeProvider theme={this.getMuiTheme()}>
        <MUIDataTable title={"ACME Employee list"} data={data} columns={columns} options={options} />
      </MuiThemeProvider>
    );

  }
}

Custom Components

You can pass custom components to further customize the table:

import React from "react";
import Chip from '@material-ui/core/Chip';
import MUIDataTable, { TableFilterList } from "mui-datatables";

const CustomChip = ({ label, onDelete }) => {
    return (
        <Chip
            variant="outlined"
            color="secondary"
            label={label}
            onDelete={onDelete}
        />
    );
};

const CustomFilterList = (props) => {
    return <TableFilterList {...props} ItemComponent={CustomChip} />;
};

class CustomDataTable extends React.Component {
    render() {
        return (
            <MUIDataTable
                columns={columns}
                data={data}
                components={{
                  TableFilterList: CustomFilterList,
                }}
            />
        );
    }
}

Supported customizable components:

  • Checkbox - A special 'data-description' prop lets you differentiate checkboxes Example. Valid values: ['row-select', 'row-select-header', 'table-filter', 'table-view-col'].The dataIndex is also passed via the "data-index" prop.
  • ExpandButton Example
  • DragDropBackend
  • TableBody
  • TableViewCol - The component that displays the view/hide list of columns on the toolbar.
  • TableFilterList - You can pass ItemComponent prop to render custom filter list item.
  • TableFooter
  • TableHead
  • TableResize
  • TableToolbar
  • TableToolbarSelect
  • Tooltip
  • icons - An object containing optional replacement icon classes for the actions toolbar. Example
    • SearchIcon
    • DownloadIcon
    • PrintIcon
    • ViewColumnIcon
    • FilterIcon

For more information, please see this example. Additionally, all examples can be viewed live at our CodeSandbox.

Remote Data

If you are looking to work with remote data sets or handle pagination, filtering, and sorting on a remote server you can do that with the following options:

const options = {
  serverSide: true,
  onTableChange: (action, tableState) => {
    this.xhrRequest('my.api.com/tableData', result => {
      this.setState({ data: result });
    });
  }
};

To see an example Click Here

Localization

This package decided that the cost of bringing in another library to perform localizations would be too expensive. Instead the ability to override all text labels (which aren't many) is offered through the options property textLabels. The available strings:

const options = {
  ...
  textLabels: {
    body: {
      noMatch: "Sorry, no matching records found",
      toolTip: "Sort",
      columnHeaderTooltip: column => `Sort for ${column.label}`
    },
    pagination: {
      next: "Next Page",
      previous: "Previous Page",
      rowsPerPage: "Rows per page:",
      displayRows: "of",
    },
    toolbar: {
      search: "Search",
      downloadCsv: "Download CSV",
      print: "Print",
      viewColumns: "View Columns",
      filterTable: "Filter Table",
    },
    filter: {
      all: "All",
      title: "FILTERS",
      reset: "RESET",
    },
    viewColumns: {
      title: "Show Columns",
      titleAria: "Show/Hide Table Columns",
    },
    selectedRows: {
      text: "row(s) selected",
      delete: "Delete",
      deleteAria: "Delete Selected Rows",
    },
  }
  ...
}

Contributing

Thanks for taking an interest in the library and the github community!

The following commands should get you started:

npm i
npm run dev

open http://localhost:5050/ in browser

After you make your changes locally, you can run the test suite with npm test.

License

The files included in this repository are licensed under the MIT license.

Thanks

Thank you to BrowserStack for providing the infrastructure that allows us to test in real browsers.

GitHub

https://github.com/gregnb/mui-datatables
Comments
  • 1. Upgrade to Material-UI 5

    Make mui-datatables to be compatible with Material-UI 5. The target version is 5.0.1. The version of the mui-datatables itself is not bumped yet.

    Followed migration instructions on this page: https://next.material-ui.com/guides/migration-v4/ Also used the codemod here: https://github.com/mui-org/material-ui/blob/next/packages/material-ui-codemod/README.md#mui-replace

    Changes:

    • Use createTheme and ThemeProvider instead of createMuiTheme, MuiThemeProvider.
    • Use ThemeProvider instead of MuiThemeProvider.
    • Import withStyles, makeStyles from @mui/styles instead of @material-ui/core/styles.
    • Added ThemeProvider to the root of the examples page.
    • Upgrade material-ui/core, icons versions to 5.0.0-rc.0. Add @material-ui/styles version 5.0.0-rc.0.
    • Use @wojtekmaj/enzyme-adapter-react-17 instead of enzyme-adapter-react-16. Caution: This is an unofficial package. Cannot find an official enzyme adapter for react 17.
    • Upgrade next version to 11.1.2.
    • Upgrade react and react-dom versions to 17.0.2.
    • Upgrade react-waypoint version to 10.1.0.
    • Upgrade rollup version to 2.3.4. Since rollup-plugin-uglify is not compatible with rollup version 2, use @lopatnov/rollup-plugin-uglify instead. Caution: This is an unofficial package. Cannot find an official rollup uglify plugin in version 2. Also add rollup-pluginutils as the dependency of the new rollup uglify plugin.
    • Add @emotion/react and @emotion/styled as required by Material-UI 5.
    • Use react-sortable-tree-patch-react-17 instead of react-sortable-tree. Caution: This is an unofficial package.

    ~~Known Issues:~~

    Able to bring up https://localhost:5050 after running npm run dev and run most examples except the following examples:

    • ~~Column Filters: Showing "Sorry, no matching records found".~~ Upon further review, this seems expected. Remove a filter to show data. Please confirm.
    • ~~Column Option Update: Showing "Sorry, no matching records found".~~ Upon further review, this seems expected. Remove a filter to show data. Please confirm.
    • ~~Component: Does not render.~~ Fixed by adding empty labels to FormControlLabel components. Reference: https://next.material-ui.com/guides/migration-v4/#formcontrollabel

    ~~We are looking into the above issues.~~ Please let us know what else need to be tested. Thank you.

    Reviewed by zxhmike at 2021-09-01 20:05
  • 2. Allow passing data as an array of objects

    It would be very useful to be able to pass in an array of object for data and then in the column definition you would specify the name of the property on the object.

    It would be even better if you could specify nested properties with dot notation, similarly to how react-table works.

    Reviewed by rooby at 2018-08-21 08:17
  • 3. #125 Add custom filter list options

    This adds 3 new options for the column configuration:

    customFilterRender: (column, index, props) => ReactNode This provides the possibility to override the default rendering of the FilterInput For example this can be used to render a DatePicker instead of a DropDown

    customFilterFn: (filterValue, columnValue) => boolean This enables the user to provide an own filtering implementation. Given the value of the column and the value of the filter, the function return either true or false if it want the row filtered out or not. This also make filtering for greater/lower than or range filter possible

    customFilterValueRender: (columnValue) => ReactNode This makes it possible to define how the filter value is rendered in the active filter list. Necessary eg. for custom date rendering.

    All of these methods are showcased in the new example customize-filtering where I added a Datepicker to filter a column of date.

    I could not find where to update the docs with the new method, if you point me to it, I'll gladly update them.

    If anything should be changed, please let me now

    Reviewed by lukas-zech-software at 2018-12-04 18:27
  • 4. Table sort is lost if data changed on a re-render

    If user has sorted a column and the table data changed afterwards, the table is render with the new data but the sort is not taken into account.

    Expected Behavior

    When the table data is updated, the re-rendering of the MUI DataTables should take into account the previously selected sort by the user.

    Current Behavior

    Currently, in this situation, the rows are back to the initial order and not sorted correctly. However, the column header still indicates the sort icon correctly (based on the previously clicked column).

    Steps to Reproduce (for bugs)

    I am storing the table data in the state and derive it from the props. If the props change, the data in the state is updated. The MUIDataTable component is retrieving the data directly from the state on the render method.

    Your Environment

    | Tech | Version | |--------------|---------| | Material-UI | 1.4.3 | | MUI-datatables | 2.0.0-beta-38 | | React | 16.5.0 | | Browser | Chrome |

    Reviewed by joaoaguiam at 2018-12-06 03:19
  • 5. TableState lost during re-render.

    The table state is lost during a re-render. All filters, page-rows, and other related information are reset.

    Example to re-produce the issue:

    import React from "react";
    import ReactDOM from "react-dom";
    import MUIDataTable from "mui-datatables";
    import {
      createMuiTheme,
      MuiThemeProvider,
      withStyles
    } from "@material-ui/core/styles";
    
    class App extends React.Component {
      constructor(props) {
        super(props);
    
        window.table = this;
      }
      getMuiTheme = () =>
        createMuiTheme({
          overrides: {
            MUIDataTable: {
              root: {
                backgroundColor: "#FF000"
              },
              paper: {
                boxShadow: "none"
              }
            },
            MUIDataTableBodyCell: {
              root: {
                backgroundColor: "#FF0000"
              }
            }
          }
        });
    
      render() {
        const columns = [
          {
            name: "Name",
            options: {
              filter: true
            }
          },
          {
            name: "Title",
            options: {
              filter: true
            }
          },
          {
            name: "Location",
            options: {
              filter: false
            }
          },
          {
            name: "Age",
            options: {
              filter: true
            }
          },
          {
            name: "Salary",
            options: {
              filter: true,
              sort: false
            }
          }
        ];
    
        const data = [
          ["Gabby George", "Business Analyst", "Minneapolis", 30, 100000],
          ["Aiden Lloyd", "Business Consultant", "Dallas", 55, 200000],
          ["Jaden Collins", "Attorney", "Santa Ana", 27, 500000],
          ["Franky Rees", "Business Analyst", "St. Petersburg", 22, 50000],
          ["Aaren Rose", "Business Consultant", "Toledo", 28, 75000],
          ["Blake Duncan", "Business Management Analyst", "San Diego", 65, 94000],
          ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, 210000],
          ["Lane Wilson", "Commercial Specialist", "Omaha", 19, 65000],
          ["Robin Duncan", "Business Analyst", "Los Angeles", 20, 77000],
          ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, 135000],
          ["Harper White", "Attorney", "Pittsburgh", 52, 420000],
          ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, 150000],
          ["Frankie Long", "Industrial Analyst", "Austin", 31, 170000],
          ["Brynn Robbins", "Business Analyst", "Norfolk", 22, 90000],
          ["Justice Mann", "Business Consultant", "Chicago", 24, 133000],
          [
            "Addison Navarro",
            "Business Management Analyst",
            "New York",
            50,
            295000
          ],
          ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, 200000],
          ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, 400000],
          ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, 110000],
          ["Danny Leon", "Computer Scientist", "Newark", 60, 220000],
          ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, 180000],
          ["Jesse Hall", "Business Analyst", "Baltimore", 44, 99000],
          ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, 90000],
          ["Terry Macdonald", "Commercial Specialist", "Miami", 39, 140000],
          ["Justice Mccarthy", "Attorney", "Tucson", 26, 330000],
          ["Silver Carey", "Computer Scientist", "Memphis", 47, 250000],
          ["Franky Miles", "Industrial Analyst", "Buffalo", 49, 190000],
          ["Glen Nixon", "Corporate Counselor", "Arlington", 44, 80000],
          [
            "Gabby Strickland",
            "Business Process Consultant",
            "Scottsdale",
            26,
            45000
          ],
          ["Mason Ray", "Computer Scientist", "San Francisco", 39, 142000]
        ];
    
        const options = {
          filter: true,
          filterType: "dropdown",
          responsive: "stacked"
        };
    
        return (
          <MuiThemeProvider theme={this.getMuiTheme()}>
            <MUIDataTable
              title={"ACME Employee list"}
              data={data}
              columns={columns}
              options={options}
            />
          </MuiThemeProvider>
        );
      }
    }
    
    window.setInterval(() => {
      window.table.forceUpdate();
    },5000)
    
    ReactDOM.render(<App />, document.getElementById("root"));
    
    

    Tested here: https://codesandbox.io/embed/0ylq1lqwp0?autoresize=1&hidenavigation=1

    Steps to reproduce: Select a filter. Wait 5 seconds. -> Everything will be reset

    Expected behavior: All table state information are kept after re-render.

    Note: This doesn't re-produce the exact issue. With the version I'm testing what is interesting is that the rows per page are not reset, but the filters are reset, maybe this helps.

    Reviewed by Fabbok1x at 2019-08-02 10:43
  • 6. Support for an initial state

    I implemented the possibility to pass an initial state to the MuiDataTable plugin. This initial state, does what it says, sets the already existed state. I had to re-arrange some of the logic to accept an initial state, since it wasn't build for that.

    • Moved page and rowsPerPage from options to the new initialState prop
    • Fallback support for page and rowsPerPage under options prop
    • Implemented searchText, filters, sort and columnOptions (for visibility)
    • Supports serverside: true
    • Adjusted readme
    • Added example
    Reviewed by nrdev88 at 2019-02-25 11:52
  • 7. Add button "create new row" in toolbar

    Hello,

    I think someone already did this but I can't find documentation nor issues on that.. I'd like to add a "new" button on datatable's toolbar to create a new line on table (with custom form, rest call etc. reload on top on that and where I will handle it my-self).

    I already saw customToolbarSelect but this does not fit to what I want to do...

    If someone can give me some information or if I must add a button on top of grid myself...

    Please tell me :) Thanks,

    Reviewed by jkeruzec at 2018-06-19 13:02
  • 8. fix(#1747): Replace deprecated props in mui based components

    Material-ui will be migrating from v4 to v5 and has been deprecating stuffs as a part of that move. Deprecation warnings as stated in #1747 appears when MUI-Datatable is used. This PR fixes it by using the recommendations provided by material-ui, in regards to the deprecations.

    • Used TransitionProps instead of the deprecated onClose and onExited Props for material-ui based Popover.

    • Used onPageChange and onRowsPageChange Props instead of the deprecated onChangePage and onChangeRowsPerPageProps respectively, for material-ui based TablePagination.

    • Used ref instead of the deprecated buttonReffor material-ui based IconButton. Note: In addition to those mentioned in #1747, I faced the below warning as well:

      Warning: Failed prop type: The prop buttonRef of ForwardRef(ButtonBase) is deprecated. Use ref instead..

      If I am not wrong, I believe it is caused by the warnings added in material ui 4.12.0 release and there are indeed going to remove buttonRef as seen inone of the v5 pre-releases.

    I tried running the npm test, however, I faced the same error as mentioned in here.

    Reviewed by Kalaiz at 2021-07-13 18:53
  • 9. Using onBlur to activate the filter instead of using onFilterChange

    I am using server-side filtering, I overrode onFilterChange to activate filter every time the filter list is changed, but the problem is every time the filter list is changed, the backend API will be called to update the table's data. It makes the layout quite lag and the backend is called too much. I want to filter every time the user leaves the field or maybe we will add the button in TableList called Submit Filter, as soon as the user enters all the field they need to filter then click the button to activate the filter.

    | Tech | Version | |--------------|---------| | Material-UI | 3.9.2 | | MUI-datatables | 2.8.0 | | React | 16.8.5 | | browser | Chrome | | etc | |

    Reviewed by hungbang at 2019-08-23 14:28
  • 10. Reactive data causing table state to be lost

    Currently using Meteor to generate reactive data, and if I change any table settings (filter, sort, etc), they are lost if any of the data is updated. Previously, I thought it was because there were functions in options and columns that were updating the props, but it seems even the data prop, if updated, causes the state to be lost (for me). A sample of my code:

    import { LabClinics } from "/imports/api/labclinics/labclinics";
    import ReactSelect from "/imports/ui/Components/ReactSelect.js";
    import {
        Button,
        Grid,
        IconButton,
        Paper,
        TextField,
        Toolbar,
        withStyles,
    } from "@material-ui/core";
    import CheckIcon from "@material-ui/icons/Check";
    import { Meteor } from "meteor/meteor";
    import { withTracker } from "meteor/react-meteor-data";
    import MUIDataTable from "mui-datatables";
    import React from "react";
    import { toast } from "react-toastify";
    
    class UsersComponent extends React.PureComponent {
        constructor(props) {
            super(props);
            this.state = {
                data: [],
                users: null,
                options: this.returnOptions(),
                columns: this.returnColumns(),
                clinic: null,
            };
            this.onSubmit = this.onSubmit.bind(this);
        }
    
        componentDidUpdate(prevProps, prevState, snapshot) {
            if (this.props.isReady && this.props !== prevProps) {
                this.createRows();
                this.createClinicSelectOptions();
            }
        }
        onChange(e) {
            console.log(e.target.value);
            this.setState({ [e.target.name]: e.target.value });
        }
    
        createClinicSelectOptions = () => {
            const clinicOptions = this.props.clinics.map((clinic) => ({
                label: clinic.AurusUser,
                value: clinic,
            }));
            this.setState({ clinicOptions });
        };
    
        onChangeClinic = (value) => {
            this.setState({ clinic: value });
        };
        
        returnOptions = () => ({
            onRowsSelect: (currentRowsSelected, allRowsSelected) => {
                if (currentRowsSelected.length <= 0) {
                    this.setState({ clinic: null, users: null });
                } else {
                    const users = [];
                    currentRowsSelected.forEach((row) => {
                        users.push(this.state.data[row.dataIndex][0]);
                    });
                    this.setState({ users });
                }
            },
            customToolbarSelect: (selectedRows, displayData, setSelectedRows) => {
                return (
                    <Toolbar
                        style={{ flexGrow: 0.1 }}
                        className={"toolbar-spacing"}
                    >
                        {/*<Typography>Reassign User to Clinic:</Typography>*/}
                        {this.state.clinic && (
                            <React.Fragment>
                                <IconButton onClick={this.onSubmit}>
                                    <CheckIcon fontSize={"small"} />
                                </IconButton>
                            </React.Fragment>
                        )}
                        <ReactSelect
                            options={this.state.clinicOptions}
                            placeholder={"Reassign User to Clinic"}
                            // label={"Reassign User to Clinic"}
                            helperText={"Reassign User to Clinic"}
                            onChange={this.onChangeClinic}
                        />
                    </Toolbar>
                );
            },
            elevation: 1,
        });
    
        createRows = () => {
            const data = this.props.users.map((user) => [
                user,
                user.profile.username,
                user.profile.clinicId,
            ]);
            this.setState({ data });
        };
    
        returnColumns = () => [
            {
                name: "User",
                options: {
                    display: "excluded",
                },
            },
            {
                name: "Username",
            },
            {
                name: "Clinic ID",
            },
        ];
    
        render() {
            const { classes } = this.props;
            const { data, options, columns } = this.state;
            return (            
                <MUIDataTable
                    title={"Users"}
                    data={data}
                    options={options}
                    columns={columns}
                />            
            );
        }
    }
    
    const styles = (theme) => ({
        iconButton: {},
        iconContainer: {
            marginRight: "24px",
        },
        inverseIcon: {
            transform: "rotate(90deg)",
        },
        textField: {
            marginLeft: theme.spacing(1),
            marginRight: theme.spacing(1),
        },
        paper: {
            ...theme.mixins.gutters(),
            paddingTop: theme.spacing(2),
            paddingBottom: theme.spacing(2),
        },
    });
    
    export default withTracker((props) => {
        const handles = [
            Meteor.subscribe("labClinics"),
            Meteor.subscribe("userList"),
        ];
        const isReady = handles.every((e) => e.ready());
        let clinics, users;
        if (isReady) {
            clinics = LabClinics.find().fetch();
            users = Meteor.users.find().fetch();
            debugger;
        }
        return {
            isReady,
            clinics,
            users,
        };
    })(withStyles(styles)(UsersComponent));
    
    

    Expected Behavior

    Table state should be retained (filters, search text, etc) should any prop update on the MUIDataTable.

    Current Behavior

    All state resets if any changes in props are made (denoted by "propsChange" in onTableChange).

    Steps to Reproduce (for bugs)

    1. Created new Meteor project and populated with test data.
    2. Fed data into datatable.
    3. De-selected a column from "View Columns".
    4. Opened database editor and changed a value in the database to see if reflected reactively in table.
    5. Witnessed the de-selected column then become visible again on data change.

    Your Environment

    | Tech | Version | |--------------|---------| | Material-UI | 4.1.3 | | MUI-datatables | 2.5.1 (but tested it on 2.4.0, 2.3.0, and 2.2.0 | | React | 16.8.6 | | browser | Chrome Version 75.0.3770.100 (Official Build) (64-bit) | | OS | MacOS 10.14.5 |

    Reviewed by kpervin at 2019-06-28 15:15
  • 11. You introduced a console.log

    Expected Behavior

    no console.log output

    Current Behavior

    logs stuff into the console

    screenshot 2018-11-12 at 15 29 57

    Steps to Reproduce (for bugs)

    I upgraded from 2.0.0-beta-36 to 2.0.0-beta-38 - now there is console logs.

    Your Environment

    While I couldn't find any console log on the github code, there is one in the compiled code on npm - which is very weird assuming you publish what you commit.

    screenshot 2018-11-12 at 15 29 38

    | Tech | Version | |--------------|---------| | Material-UI | 3.4.0 | | MUI-datatables | 2.0.0-beta-38 | | React | 16.5.2 | | browser | Chrome Canary 72.0.3608.0 | | etc | |

    Reviewed by Primajin at 2018-11-12 14:36
  • 12. customRowRender should give selected columns as argument

    Expected Behavior

    When using customRowRender, it should be possible to ascertain which columns are currently displayed in the table. Currently, the information given to customRowRender contains data for all the columns in the table, regardless of whether all are displayed. I think it would make sense if 1. the "data" argument only contains data for the currently selected columns (other columns are still accessible through dataIndex) or 2. an additional argument is given that yields the currently selected columns. If there is a workaround for this, I would happy to hear about it.

    My use case is developing draggable rows.

    For reference: Override default row rendering with custom function. customRowRender(data, dataIndex, rowIndex) => React Component

    Reviewed by TimTimT at 2022-05-13 13:36
  • 13. Static Width for Columns while Sorting

    Is it possible to have a static column width while sorting the data?

    Right now the column width is adjusted automatically which looks like columns jumping here and there

    Reviewed by iamharidasan at 2022-05-11 03:58
  • 14. Typo in README.md

    This is a small typo I found in the README.md. On this line: https://github.com/gregnb/mui-datatables/blob/master/README.md?plain=1#L181

    Changed visiable ---> visible

    Reviewed by theBeginner86 at 2022-05-06 15:10
  • 15. selectableRows messes up other Material UI elements

    Expected Behavior

    Other Material UI elements shouldn't be affected by the datatable.

    Current Behavior

    When I enable a single selectable row, it messes up the design of all the other material ui elements, like objects and the app bar.

    Steps to Reproduce (for bugs)

    1. Enable selectable rows
    2. Have other Material UI elements

    Your Environment

    | Tech | Version | |--------------|---------| | Material-UI | ^5.6.3 | | MUI-datatables | ^3.8.5 | | React | ^17.0.2 | | browser | chrome | | etc | |

    Reviewed by zclcdco at 2022-04-30 23:40
  • 16. Bump async from 2.6.3 to 2.6.4

    Bumps async from 2.6.3 to 2.6.4.

    Changelog

    Sourced from async's changelog.

    v2.6.4

    • Fix potential prototype pollution exploit (#1828)
    Commits
    Maintainer changes

    This version was pushed to npm by hargasinski, a new releaser for async since your current version.


    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-04-29 17:26
ra-datagrid - Integration of Material-ui's Datagrid into react-admin

ra-datagrid - Integration of Material-ui's Datagrid into react-admin

May 8, 2022
A simple proof-of-concept probabilistic spreadsheet in React with Material-UI.

A simple proof-of-concept probabilistic spreadsheet in React with Material-UI.

May 5, 2022
Useful Grid/Hidden algorithm from Material-UI

React Fast Grid (+ Hidden) Demo Useful algorithm extracted from Material-UI Grid and Material-UI Hidden npm install react-fast-grid IE11 Comptability

Oct 28, 2020
react hook for using google spreadsheet as a data table (API endpoint)
react hook for using google spreadsheet as a data table (API endpoint)

use-google-spreadsheet helps developers use google spreadsheet as their data table (backend endpoint) Live Demo install npm install use-google-spreads

Mar 8, 2022
Inventory App - a SPA (Single Page Application) project developed with Angular using Reactive Forms and VMware's Clarity components.
 Inventory App - a SPA (Single Page Application) project developed with Angular using Reactive Forms and VMware's Clarity components.

InventoryApp This project was generated with Angular CLI version 13.0.1. Development server Run ng serve for a dev server. Navigate to http://localhos

Apr 21, 2022
React Data Grid with Spreadsheet Look & Feel. Official React wrapper for Handsontable.
React Data Grid with Spreadsheet Look & Feel. Official React wrapper for Handsontable.

Important information We permanently moved this project to the main Handsontable repository at https://github.com/handsontable/handsontable/tree/maste

Apr 25, 2022
react-xls is the fastest in-browser excel ( .xls & .xlsx ) parser for React. It is full of useful features such as useExcelDownloader, ... etc.
react-xls is the fastest in-browser excel ( .xls & .xlsx ) parser for React. It is full of useful features such as useExcelDownloader, ... etc.

react-xls react-xls is the fastest in-browser excel ( .xls & .xlsx ) parser for React. ?? Features Compatible with both JavaScript and TypeScript ?? U

Apr 30, 2022
A windowed React table built on top of react-window

react-fluid-table A React table inspired by react-window (Note: This project is under construction) Install # using yarn yarn add react-fluid-table #

Apr 19, 2022
react-collapsing-table: a React rewrite of the jQuery table plugin from

React Collapsing Table Thanks for taking a look at the react collapsing table. This was inspired by the the jquery datatables library. You can see a w

May 8, 2022
Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components

Module SonarCloud Status ag-grid-community ag-grid-enterprise ag-Grid ag-Grid is a fully-featured and highly customizable JavaScript data grid. It del

May 17, 2022
Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like

react-data-grid Install npm install react-data-grid react-data-grid is published as ES2019 modules, you'll probably want to transpile those down to sc

May 8, 2022
Add spreadsheet-like behavior to your React app
Add spreadsheet-like behavior to your React app

ReactGrid MIT Add spreadsheet-like behavior to your React app ?? Browse our examples & docs: ?? reactgrid.com Before running ReactGrid you need to hav

May 11, 2022
Auto Responsive Layout Library For React
Auto Responsive Layout Library For React

autoresponsive-react Auto responsive grid layout library for React. Who are using ⭐ ⭐ ⭐ alibaba/ice ⭐ ⭐ ⭐ ice-lab/react-materials ⭐ ⭐ ⭐ ant-design/ant

May 14, 2022
Lightweight MIT React Table component with Sorting, Filtering, Grouping, Virtualization, Editing and many more
Lightweight MIT React Table component with Sorting, Filtering, Grouping, Virtualization, Editing and many more

The customizable, extendable, lightweight and free React Table Component Site | Demos | Docs Demo link Installation npm npm install ka-table yarn yarn

May 8, 2022
⚛️ Hooks for building fast and extendable tables and datagrids for React
⚛️ Hooks for building fast and extendable tables and datagrids for React

Hooks for building lightweight, fast and extendable datagrids for React Enjoy this library? Try them all! React Query, React Form, React Charts Visit

May 18, 2022
A React table component.
A React table component.

rsuite-table A React table component. Features Support virtualized. Support fixed header, fixed column. Support custom adjustment column width. Suppor

May 1, 2022
React component like SpreadSheet
React component like SpreadSheet

React component like SpreadSheet

May 12, 2022
React components for efficiently rendering large lists and tabular data
React components for efficiently rendering large lists and tabular data

React components for efficiently rendering large lists and tabular data

May 16, 2022
An Excel-like grid component for React with custom cell editors, performant scroll & resizable columns
An Excel-like grid component for React with custom cell editors, performant scroll & resizable columns

An Excel-like grid component for React with custom cell editors, performant scroll & resizable columns

May 7, 2022