├── .nvmrc
├── __mocks__
├── style.mock.js
└── file.mock.js
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.md
│ └── bug_report.md
├── FUNDING.yml
└── stale.yml
├── .eslintignore
├── .yarnrc.yml
├── .gitattributes
├── .yarn
└── install-state.gz
├── .prettierrc.js
├── src
├── DataTable
│ ├── TableBody.tsx
│ ├── TableWrapper.tsx
│ ├── NoDataWrapper.tsx
│ ├── TableColExpander.tsx
│ ├── ProgressWrapper.tsx
│ ├── constants.ts
│ ├── TableHeadRow.tsx
│ ├── TableHead.tsx
│ ├── Table.tsx
│ ├── __tests__
│ │ ├── styles.test.ts
│ │ ├── themes.test.ts
│ │ ├── __snapshots__
│ │ │ └── Checkbox.test.tsx.snap
│ │ └── Checkbox.test.tsx
│ ├── media.ts
│ ├── TableSubheader.tsx
│ ├── ResponsiveWrapper.tsx
│ ├── TableCellExpander.tsx
│ ├── ExpanderButton.tsx
│ ├── ExpanderRow.tsx
│ ├── Select.tsx
│ ├── Cell.ts
│ ├── TableCellCheckbox.tsx
│ ├── TableHeader.tsx
│ ├── Checkbox.tsx
│ ├── TableColCheckbox.tsx
│ ├── themes.ts
│ ├── ContextMenu.tsx
│ ├── TableCell.tsx
│ ├── defaultProps.tsx
│ ├── tableReducer.ts
│ └── styles.ts
├── icons
│ ├── Dropdown.tsx
│ ├── ExpanderExpandedIcon.tsx
│ ├── ExpanderCollapsedIcon.tsx
│ ├── Left.tsx
│ ├── Right.tsx
│ ├── LastPage.tsx
│ ├── FirstPage.tsx
│ └── NativeSortIcon.tsx
├── hooks
│ ├── useDidUpdateEffect.ts
│ ├── useRTL.ts
│ ├── useWindowSize.ts
│ └── useColumns.ts
├── internal
│ └── test-helpers.tsx
└── index.ts
├── .npmignore
├── .stylelintrc
├── .editorconfig
├── stories
├── reporting.stories.mdx
├── DataTable
│ ├── pagination
│ │ ├── basic.mdx
│ │ ├── remote.mdx
│ │ ├── basic.stories.js
│ │ ├── options.stories.js
│ │ ├── options.mdx
│ │ └── remote.stories.js
│ ├── expandable
│ │ ├── preDisabled.mdx
│ │ ├── preExpanded.mdx
│ │ ├── preExpanded.stories.js
│ │ ├── preDisabled.stories.js
│ │ ├── basic.stories.js
│ │ └── basic.mdx
│ ├── selectableRows
│ │ ├── preSelected.mdx
│ │ ├── preDisabled.mdx
│ │ ├── basic.stories.js
│ │ ├── basic.mdx
│ │ ├── preSelected.stories.js
│ │ ├── preDisabled.stories.js
│ │ ├── rowMGMT.stories.js
│ │ └── rowMGMT.mdx
│ ├── OmitColumn.stories.js
│ ├── sorting
│ │ ├── basic.stories.js
│ │ ├── sortFunctionCol.mdx
│ │ ├── sortFunctionCol.stories.js
│ │ ├── basic.mdx
│ │ ├── sortFunction.stories.js
│ │ ├── remote.stories.js
│ │ ├── sortFunction.mdx
│ │ └── remote.mdx
│ ├── Progess.stories.js
│ ├── DelayedColumns.stories.js
│ ├── OmitColumnDynamic.stories.js
│ ├── material-ui
│ │ ├── linearProgressBar.mdx
│ │ ├── linearProgressBar.stories.js
│ │ ├── table.mdx
│ │ ├── pagination.mdx
│ │ ├── pagination.stories.js
│ │ └── table.stories.js
│ ├── ThemeDark.stories.js
│ ├── ThemeCustom.stories.js
│ ├── CustomStylesGrid.stories.js
│ ├── ColumnReorder.stories.js
│ ├── ColumnsHideMedia.stories.js
│ ├── CellStyling.stories.js
│ ├── ProgessCustom.stories.js
│ ├── CustomCell.stories.js
│ ├── ExportCSV.stories.js
│ ├── conditional
│ │ └── rows.stories.js
│ ├── OptimizedClass.stories.js
│ ├── OptimizedHook.stories.js
│ ├── CustomStylesGSheets.stories.js
│ ├── CellConditionalStyling.stories.js
│ └── Filtering.stories.js
├── shared
│ ├── users.js
│ ├── Button.js
│ └── CustomMaterialMenu.js
├── libraryIntegration.stories.mdx
├── cssEscape.stories.mdx
├── installation.stories.mdx
├── headers
│ └── fixed.stories.js
├── enums.stories.mdx
├── development.stories.mdx
├── customStyles.stories.mdx
├── constants
│ └── sampleDesserts.js
├── theming.stories.mdx
├── issues.stories.mdx
├── patterns.stories.mdx
├── intro.stories.mdx
├── typescript.stories.mdx
├── simpleExamples.stories.mdx
└── coc.stories.mdx
├── rollup
├── rollup.config.dev.js
├── rollup.config.es.js
├── rollup.config.cjs.js
├── rollup.config.umd.js
└── rollup.config.common.js
├── .babelrc.json
├── .storybook
├── base.css
├── manager.js
├── preview.ts
└── main.ts
├── netlify.toml
├── .travis.yml
├── .vscode
├── launch.json
└── settings.json
├── tsconfig.json
├── eslintrc-js.js
├── jest.config.js
├── .gitignore
├── .eslintrc.js
├── README.md
├── CODE-OF-CONDUCT.md
└── package.json
/.nvmrc:
--------------------------------------------------------------------------------
1 | v20.10.0
2 |
--------------------------------------------------------------------------------
/__mocks__/style.mock.js:
--------------------------------------------------------------------------------
1 | export default {};
2 |
--------------------------------------------------------------------------------
/__mocks__/file.mock.js:
--------------------------------------------------------------------------------
1 | export default 'test-file-stub';
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | coverage
4 | storybook-static
5 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | yarnPath: .yarn/releases/yarn-4.0.2.cjs
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=lf
3 |
--------------------------------------------------------------------------------
/.yarn/install-state.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbetancur/react-data-table-component/HEAD/.yarn/install-state.gz
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | trailingComma: 'all',
4 | singleQuote: true,
5 | printWidth: 120,
6 | tabWidth: 2,
7 | arrowParens: 'avoid',
8 | };
9 |
--------------------------------------------------------------------------------
/src/DataTable/TableBody.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const Body = styled.div`
4 | display: flex;
5 | flex-direction: column;
6 | `;
7 |
8 | export default Body;
9 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | __mocks__
2 | .storybook
3 | .vscode
4 | coverage
5 | stories
6 | !dist
7 | .babelrc
8 | .eslintignore
9 | .eslintrc.json
10 | .travis.yml
11 | jsconfig.json
12 | rollup.config.*.js
13 | test-config.js
14 | yarn.lock
15 | *.log
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["stylelint-prettier"],
3 | "extends": [
4 | "stylelint-config-standard"
5 | ],
6 | "rules": {
7 | "prettier/prettier": true
8 | },
9 | "customSyntax": "postcss-styled-syntax"
10 | }
11 |
--------------------------------------------------------------------------------
/src/DataTable/TableWrapper.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const Wrapper = styled.div`
4 | position: relative;
5 | width: 100%;
6 | ${({ theme }) => theme.tableWrapper.style};
7 | `;
8 |
9 | export default Wrapper;
10 |
--------------------------------------------------------------------------------
/src/DataTable/NoDataWrapper.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const NoDataWrapper = styled.div`
4 | box-sizing: border-box;
5 | width: 100%;
6 | height: 100%;
7 | ${({ theme }) => theme.noData.style};
8 | `;
9 |
10 | export default NoDataWrapper;
11 |
--------------------------------------------------------------------------------
/src/DataTable/TableColExpander.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { CellBase } from './Cell';
3 |
4 | const ColumnExpander = styled(CellBase)`
5 | white-space: nowrap;
6 | ${({ theme }) => theme.expanderCell.style};
7 | `;
8 |
9 | export default ColumnExpander;
10 |
--------------------------------------------------------------------------------
/src/DataTable/ProgressWrapper.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const ProgressWrapper = styled.div`
4 | position: relative;
5 | box-sizing: border-box;
6 | width: 100%;
7 | height: 100%;
8 | ${props => props.theme.progress.style};
9 | `;
10 |
11 | export default ProgressWrapper;
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = tab
8 | indent_size = 2
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | insert_final_newline = false
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/src/icons/Dropdown.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const DropdownIcon: React.FC = () => (
4 |
8 | );
9 |
10 | export default DropdownIcon;
11 |
--------------------------------------------------------------------------------
/stories/reporting.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs';
2 |
3 |
4 |
5 | # Stargazers over time
6 |
7 | [](https://starchart.cc/jbetancur/react-data-table-component)
8 |
--------------------------------------------------------------------------------
/rollup/rollup.config.dev.js:
--------------------------------------------------------------------------------
1 | import config, { plugins } from './rollup.config.common';
2 | import pkg from '../package.json';
3 |
4 | export default Object.assign(config, {
5 | output: {
6 | name: 'ReactDataTable',
7 | file: `dist/${pkg.name}.dev.js`,
8 | format: 'cjs',
9 | exports: 'named',
10 | },
11 | plugins: plugins.concat([]),
12 | });
13 |
--------------------------------------------------------------------------------
/src/DataTable/constants.ts:
--------------------------------------------------------------------------------
1 | export const STOP_PROP_TAG = 'allowRowEvents';
2 |
3 | export enum Direction {
4 | LTR = 'ltr',
5 | RTL = 'rtl',
6 | AUTO = 'auto',
7 | }
8 |
9 | export enum Alignment {
10 | LEFT = 'left',
11 | RIGHT = 'right',
12 | CENTER = 'center',
13 | }
14 |
15 | export enum Media {
16 | SM = 'sm',
17 | MD = 'md',
18 | LG = 'lg',
19 | }
20 |
--------------------------------------------------------------------------------
/.babelrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "sourceType": "unambiguous",
3 | "presets": [
4 | [
5 | "@babel/preset-env",
6 | {
7 | "targets": {
8 | "chrome": 100,
9 | "safari": 15,
10 | "firefox": 91
11 | }
12 | }
13 | ],
14 | "@babel/preset-typescript",
15 | "@babel/preset-react"
16 | ],
17 | "plugins": []
18 | }
--------------------------------------------------------------------------------
/.storybook/base.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
4 | -webkit-font-smoothing: antialiased;
5 | }
6 |
7 | table {
8 | width: 100%;
9 | }
10 |
11 | code {
12 | line-height: unset !important;
13 | white-space: normal !important;
14 | }
15 |
--------------------------------------------------------------------------------
/src/DataTable/TableHeadRow.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const HeadRow = styled.div<{
4 | $dense?: boolean;
5 | disabled?: boolean;
6 | }>`
7 | display: flex;
8 | align-items: stretch;
9 | width: 100%;
10 | ${({ theme }) => theme.headRow.style};
11 | ${({ $dense, theme }) => $dense && theme.headRow.denseStyle};
12 | `;
13 |
14 | export default HeadRow;
15 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | publish = "storybook-static"
3 | command = "yarn lint && yarn test && yarn build && yarn build-storybook"
4 | [build.environment]
5 | NODE_VERSION = "20.11.0"
6 | YARN_VERSION = "4.0.2"
7 | DOTENV_DISPLAY_WARNING = "none"
8 | STORYBOOK_EXAMPLE_APP ="true"
9 | [[headers]]
10 | for = "/*"
11 | [headers.values]
12 | Access-Control-Allow-Origin = "*"
13 |
--------------------------------------------------------------------------------
/rollup/rollup.config.es.js:
--------------------------------------------------------------------------------
1 | import { terser } from 'rollup-plugin-terser';
2 | import pkg from '../package.json';
3 |
4 | import config, { plugins } from './rollup.config.common';
5 |
6 | export default Object.assign(config, {
7 | output: [
8 | {
9 | file: pkg.module,
10 | format: 'es',
11 | exports: 'named',
12 | },
13 | ],
14 | plugins: plugins.concat([terser()]),
15 | });
16 |
--------------------------------------------------------------------------------
/src/icons/ExpanderExpandedIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ExpanderExpandedIcon: React.FC = () => (
4 |
8 | );
9 |
10 | export default ExpanderExpandedIcon;
11 |
--------------------------------------------------------------------------------
/src/icons/ExpanderCollapsedIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ExpanderCollapsedIcon: React.FC = () => (
4 |
8 | );
9 |
10 | export default ExpanderCollapsedIcon;
11 |
--------------------------------------------------------------------------------
/rollup/rollup.config.cjs.js:
--------------------------------------------------------------------------------
1 | import { terser } from 'rollup-plugin-terser';
2 | import pkg from '../package.json';
3 |
4 | import config, { plugins } from './rollup.config.common';
5 |
6 | export default Object.assign(config, {
7 | output: [
8 | {
9 | file: pkg.main,
10 | format: 'cjs',
11 | exports: 'named',
12 | sourcemap: true,
13 | },
14 | ],
15 | plugins: plugins.concat([terser()]),
16 | });
17 |
--------------------------------------------------------------------------------
/src/icons/Left.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Left: React.FC = () => (
4 |
15 | );
16 |
17 | export default Left;
18 |
--------------------------------------------------------------------------------
/src/icons/Right.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Right: React.FC = () => (
4 |
15 | );
16 |
17 | export default Right;
18 |
--------------------------------------------------------------------------------
/.storybook/manager.js:
--------------------------------------------------------------------------------
1 | import { create } from '@storybook/theming';
2 | import { addons } from '@storybook/addons';
3 |
4 | addons.setConfig({
5 | isFullscreen: false,
6 | showAddonsPanel: true,
7 | panelPosition: 'bottom',
8 | theme: create({
9 | base: 'light',
10 | brandTitle: 'React Data Table Component',
11 | brandUrl: 'https://github.com/jbetancur/react-data-table-component',
12 | gridCellSize: 12,
13 | }),
14 | });
15 |
--------------------------------------------------------------------------------
/src/icons/LastPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const LastPage: React.FC = () => (
4 |
15 | );
16 |
17 | export default LastPage;
18 |
--------------------------------------------------------------------------------
/src/icons/FirstPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const FirstPage: React.FC = () => (
4 |
15 | );
16 |
17 | export default FirstPage;
18 |
--------------------------------------------------------------------------------
/stories/DataTable/pagination/basic.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Canvas } from '@storybook/addon-docs';
2 |
3 | # Pagination
4 |
5 | Adding client side pagination functionality is easy:
6 |
7 | ```js
8 | function MyComponent() {
9 | return (
10 |
15 | );
16 | };
17 | ```
18 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/src/DataTable/TableHead.tsx:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | const fixedCSS = css`
4 | position: sticky;
5 | position: -webkit-sticky; /* Safari */
6 | top: 0;
7 | z-index: 1;
8 | `;
9 |
10 | const Head = styled.div<{
11 | $fixedHeader?: boolean;
12 | }>`
13 | display: flex;
14 | width: 100%;
15 | ${({ $fixedHeader }) => $fixedHeader && fixedCSS};
16 | ${({ theme }) => theme.head.style};
17 | `;
18 |
19 | export default Head;
20 |
--------------------------------------------------------------------------------
/stories/shared/users.js:
--------------------------------------------------------------------------------
1 | import { faker } from '@faker-js/faker';
2 |
3 | const createUser = () => ({
4 | id: faker.string.uuid(),
5 | name: faker.internet.userName(),
6 | email: faker.internet.email(),
7 | address: faker.location.streetAddress(),
8 | bio: faker.lorem.sentence(),
9 | image: faker.image.avatar(),
10 | });
11 |
12 | const createUsers = (numUsers = 5) => new Array(numUsers).fill(undefined).map(createUser);
13 |
14 | export default createUsers(20);
15 |
--------------------------------------------------------------------------------
/src/hooks/useDidUpdateEffect.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | type Hook = (fn: () => void, inputs: unknown[]) => void;
4 |
5 | const useFirstUpdate: Hook = (fn, inputs) => {
6 | const firstUpdate = React.useRef(true);
7 |
8 | React.useEffect(() => {
9 | if (firstUpdate.current) {
10 | firstUpdate.current = false;
11 | return;
12 | }
13 |
14 | fn();
15 | // eslint-disable-next-line react-hooks/exhaustive-deps
16 | }, inputs);
17 | };
18 |
19 | export default useFirstUpdate;
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 |
3 | language: node_js
4 |
5 | node_js:
6 | - 14
7 |
8 | cache:
9 | yarn: true
10 |
11 | script:
12 | - yarn lint
13 | - yarn test
14 | - yarn build
15 |
16 | before_deploy:
17 | - yarn build-storybook
18 |
19 | deploy:
20 | strategy: git
21 | provider: pages
22 | skip_cleanup: true
23 | token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable
24 | local_dir: storybook-static
25 | on:
26 | branch: next
27 | after_script:
28 | - yarn codecov
29 |
--------------------------------------------------------------------------------
/src/DataTable/Table.tsx:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | const disabledCSS = css`
4 | pointer-events: none;
5 | opacity: 0.4;
6 | `;
7 |
8 | const TableStyle = styled.div<{
9 | disabled?: boolean;
10 | }>`
11 | position: relative;
12 | box-sizing: border-box;
13 | display: flex;
14 | flex-direction: column;
15 | width: 100%;
16 | height: 100%;
17 | max-width: 100%;
18 | ${({ disabled }) => disabled && disabledCSS};
19 | ${({ theme }) => theme.table.style};
20 | `;
21 |
22 | export default TableStyle;
23 |
--------------------------------------------------------------------------------
/stories/libraryIntegration.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs';
2 |
3 |
4 |
5 | # UI Library Integration
6 |
7 | React Data Table Component makes it easy to incorporate ui components from other libraries for overriding things like the sort icon, select checkbox.
8 |
9 | - [MaterialUI 5](https://codesandbox.io/s/react-data-table-materialui-72gdo)
10 | - [Bootstrap 5](https://codesandbox.io/s/react-data-table-sandbox-z6gtg)
11 |
--------------------------------------------------------------------------------
/src/internal/test-helpers.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, RenderOptions, RenderResult } from '@testing-library/react';
3 | import { ThemeProvider } from 'styled-components';
4 | import 'jest-styled-components';
5 | import { createStyles } from '../DataTable/styles';
6 |
7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
8 | export const renderWithTheme = (tree: React.ReactElement, ...args: RenderOptions[]): RenderResult =>
9 | render({tree}, ...args);
10 |
--------------------------------------------------------------------------------
/stories/DataTable/expandable/preDisabled.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Canvas } from '@storybook/addon-docs';
2 |
3 | # Pre Disabled Rows
4 |
5 | To pre-disable rows based on your data:
6 |
7 | ```js
8 | ...
9 | const rowPreDisabled = row => row.disabled;
10 |
11 | function MyComponent() {
12 | return (
13 |
19 | );
20 | }
21 | ```
22 |
23 |
--------------------------------------------------------------------------------
/stories/DataTable/expandable/preExpanded.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Canvas } from '@storybook/addon-docs';
2 |
3 | # Pre Selected Rows
4 |
5 | To pre-select rows based on your data:
6 |
7 | ```js
8 | ...
9 | const rowPreExpanded = row => row.defaultExpanded
10 |
11 | function MyComponent() {
12 | return (
13 |
19 | );
20 | }
21 | ```
22 |
23 |
26 |
--------------------------------------------------------------------------------
/stories/DataTable/selectableRows/preSelected.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Canvas } from '@storybook/addon-docs';
2 |
3 | # Pre Selected Rows
4 |
5 | To pre-select rows based on your data:
6 |
7 | ```js
8 | ...
9 | const rowSelectCritera = row => row.fat > 6;
10 |
11 | function MyComponent() {
12 | return (
13 |
19 | );
20 | }
21 | ```
22 |
23 |
26 |
--------------------------------------------------------------------------------
/stories/shared/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const ButtonStyle = styled.button`
5 | background-color: #2979ff;
6 | border: none;
7 | color: white;
8 | padding: 8px 32px 8px 32px;
9 | text-align: center;
10 | text-decoration: none;
11 | display: inline-block;
12 | font-size: 16px;
13 | border-radius: 3px;
14 |
15 | &:hover {
16 | cursor: pointer;
17 | }
18 | `;
19 |
20 | // eslint-disable-next-line react/prop-types
21 | export default ({ children, ...rest }) => {children};
22 |
--------------------------------------------------------------------------------
/stories/DataTable/selectableRows/preDisabled.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Canvas } from '@storybook/addon-docs';
2 |
3 | # Pre Disabled Rows
4 |
5 | To pre-disable rows based on your data:
6 |
7 | ```js
8 | ...
9 | const rowDisabledCriteria = row => row.isOutOfStock;
10 |
11 | function MyComponent() {
12 | return (
13 |
19 | );
20 | }
21 | ```
22 |
23 |
26 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug Tests",
6 | "type": "node",
7 | "request": "launch",
8 | "program": "${workspaceRoot}/node_modules/jest-cli/bin/jest.js",
9 | "stopOnEntry": false,
10 | "args": ["--runInBand"],
11 | "cwd": "${workspaceRoot}",
12 | "runtimeArgs": ["--nolazy"],
13 | "env": {
14 | "BABEL_ENV": "commonjs"
15 | },
16 | "console": "integratedTerminal",
17 | "sourceMaps": true
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/rollup/rollup.config.umd.js:
--------------------------------------------------------------------------------
1 | import { terser } from 'rollup-plugin-terser';
2 | import config, { plugins } from './rollup.config.common';
3 |
4 | export default Object.assign(config, {
5 | output: [
6 | {
7 | name: 'ReactDataTable',
8 | file: 'dist/react-data-table-component.umd.js',
9 | format: 'umd',
10 | globals: {
11 | react: 'React',
12 | 'react-dom': 'ReactDOM',
13 | 'styled-components': 'styled',
14 | 'lodash.orderby': 'orderby',
15 | deepmerge: 'deepmerge',
16 | },
17 | exports: 'named',
18 | },
19 | ],
20 | plugins: plugins.concat([terser()]),
21 | });
22 |
--------------------------------------------------------------------------------
/rollup/rollup.config.common.js:
--------------------------------------------------------------------------------
1 | import resolve from '@rollup/plugin-node-resolve';
2 | import commonjs from '@rollup/plugin-commonjs';
3 | import visualizer from 'rollup-plugin-visualizer';
4 | import typescript from 'rollup-plugin-typescript2';
5 |
6 | export const plugins = [
7 | resolve({
8 | browser: true,
9 | preferBuiltins: true,
10 | extensions: ['.ts', '.tsx'],
11 | }),
12 | commonjs({
13 | include: 'node_modules/**',
14 | }),
15 | visualizer(),
16 | typescript(),
17 | ];
18 |
19 | export default {
20 | input: './src/index.ts',
21 | external: ['react', 'react-dom', 'styled-components'],
22 | };
23 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import DataTable from './DataTable/DataTable';
2 |
3 | export { defaultThemes, createTheme } from './DataTable/themes';
4 | export * from './DataTable/constants';
5 | export type {
6 | TableProps,
7 | TableProps as IDataTableProps, // this is for backwards compat with v6
8 | TableColumn,
9 | TableRow,
10 | TableStyles,
11 | Theme,
12 | Themes,
13 | ConditionalStyles,
14 | ExpanderComponentProps,
15 | PaginationComponentProps,
16 | PaginationOptions,
17 | PaginationServerOptions,
18 | ContextMessage,
19 | SortOrder,
20 | SortFunction,
21 | Selector,
22 | } from './DataTable/types';
23 |
24 | export default DataTable;
25 |
--------------------------------------------------------------------------------
/src/DataTable/__tests__/styles.test.ts:
--------------------------------------------------------------------------------
1 | import { createStyles, defaultStyles } from '../styles';
2 | import { defaultThemes } from '../themes';
3 |
4 | describe('createStyles', () => {
5 | test('it should return the default styles if customStyles is not provided', () => {
6 | const newStyles = createStyles();
7 |
8 | expect(newStyles).toEqual(defaultStyles(defaultThemes.default));
9 | });
10 |
11 | test('it should return the default styles if a non existent theme is not provided', () => {
12 | const newStyles = createStyles({}, 'poopyTheme');
13 |
14 | expect(newStyles).toEqual(defaultStyles(defaultThemes.default));
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/DataTable/__tests__/themes.test.ts:
--------------------------------------------------------------------------------
1 | import { createTheme, defaultThemes } from '../themes';
2 |
3 | describe('createTheme', () => {
4 | test('it should return the default theme if customTheme is not provided', () => {
5 | const newTheme = createTheme('whoa');
6 |
7 | expect(newTheme).toEqual(defaultThemes.default);
8 | });
9 |
10 | test('it should create a new theme that is composed with the default theme', () => {
11 | const newTheme = createTheme('wow', {
12 | text: {
13 | primary: '#',
14 | secondary: '#',
15 | disabled: '#',
16 | },
17 | });
18 |
19 | expect(defaultThemes.wow).toEqual(newTheme);
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | # github: [jbetancur]
4 | # patreon: # Replace with a single Patreon username
5 | open_collective: john-betancur
6 | # ko_fi: # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | # liberapay: # Replace with a single Liberapay username
10 | # issuehunt: # Replace with a single IssueHunt username
11 | # otechie: # Replace with a single Otechie username
12 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/stories/cssEscape.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs';
2 |
3 |
4 |
5 | ## CSS Escape Hatch
6 |
7 | If you would like to customize the layout components of React Data Table using styled-components (e.g. `styled(DataTable)`), or your favorite CSS, SCSS, LESS, etc.., pre-processor, or you simply need an escape hatch you can utilize the following classNames:
8 |
9 | - rdt_Table
10 | - rdt_TableRow
11 | - rdt_TableCol
12 | - rdt_TableCol_Sortable
13 | - rdt_TableCell
14 | - rdt_TableHeader
15 | - rdt_TableFooter
16 | - rdt_TableHead
17 | - rdt_TableHeadRow
18 | - rdt_TableBody
19 | - rdt_ExpanderRow
20 |
--------------------------------------------------------------------------------
/stories/DataTable/OmitColumn.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import data from '../constants/sampleMovieData';
3 | import DataTable from '../../src/index';
4 |
5 | const columns = [
6 | {
7 | name: 'Title',
8 | selector: row => row.title,
9 | sortable: true,
10 | },
11 | {
12 | name: 'Director',
13 | selector: row => row.director,
14 | sortable: true,
15 | omit: true,
16 | },
17 | {
18 | name: 'Year',
19 | selector: row => row.year,
20 | sortable: true,
21 | },
22 | ];
23 |
24 | export const Omit = () => ;
25 |
26 | export default {
27 | title: 'Columns/Omit',
28 | component: Omit,
29 | };
30 |
--------------------------------------------------------------------------------
/stories/DataTable/pagination/remote.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Canvas } from '@storybook/addon-docs';
2 |
3 | # Remote Pagination
4 |
5 | To enable remote/manual pagination you'll need to add the `paginationServer` property and ensure the remote API you are using supports pagination metadata.
6 |
7 | You'll also need to implement the `onChangeRowsPerPage`, and `onChangePage` callbacks.
8 |
9 | Finally, you'll need to keep track of your total rows using `paginationTotalRows`. This should be obtained from the remote API you are calling from to get the pagnination records.
10 |
11 | The following covers a working example of remote pagniation:
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/DataTable/__tests__/__snapshots__/Checkbox.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`component should render correctly with a custom checkbox 1`] = `
4 |
5 | 25 schmeckles
6 |
7 | `;
8 |
9 | exports[`component should render correctly with custom props 1`] = `
10 |
17 | `;
18 |
19 | exports[`should render correctly when a custom component is not provided 1`] = `
20 |
25 | `;
26 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 30
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | - enhancement
10 | - feature
11 | # Label to use when marking an issue as stale
12 | staleLabel: wontfix
13 | # Comment to post when marking an issue as stale. Set to `false` to disable
14 | markComment: >
15 | This issue has been automatically marked as stale because it has not had
16 | recent activity. It will be closed if no further activity occurs. Thank you
17 | for your contributions.
18 | # Comment to post when closing a stale issue. Set to `false` to disable
19 | closeComment: false
20 |
--------------------------------------------------------------------------------
/stories/DataTable/pagination/basic.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import doc from './basic.mdx';
3 | import data from '../../constants/sampleMovieData';
4 | import DataTable from '../../../src/index';
5 |
6 | const columns = [
7 | {
8 | name: 'Title',
9 | selector: row => row.title,
10 | sortable: true,
11 | },
12 | {
13 | name: 'Director',
14 | selector: row => row.director,
15 | sortable: true,
16 | },
17 | {
18 | name: 'Year',
19 | selector: row => row.year,
20 | sortable: true,
21 | },
22 | ];
23 |
24 | export const Basic = () => ;
25 |
26 | export default {
27 | title: 'Pagination/Basic',
28 | component: Basic,
29 | parameters: {
30 | docs: {
31 | page: doc,
32 | },
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/stories/DataTable/sorting/basic.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import doc from './basic.mdx';
3 | import data from '../../constants/sampleMovieData';
4 | import DataTable from '../../../src/index';
5 |
6 | const columns = [
7 | {
8 | name: 'Title',
9 | selector: row => row.title,
10 | sortable: true,
11 | },
12 | {
13 | name: 'Director',
14 | selector: row => row.director,
15 | sortable: true,
16 | },
17 | {
18 | name: 'Year',
19 | selector: row => row.year,
20 | sortable: true,
21 | },
22 | ];
23 |
24 | export const Basic = () => {
25 | return ;
26 | };
27 |
28 | export default {
29 | title: 'Sorting/Basic',
30 | component: Basic,
31 | parameters: {
32 | docs: {
33 | page: doc,
34 | },
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/stories/DataTable/selectableRows/basic.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import doc from './basic.mdx';
3 | import data from '../../constants/sampleMovieData';
4 | import DataTable from '../../../src/index';
5 |
6 | const columns = [
7 | {
8 | name: 'Title',
9 | selector: row => row.title,
10 | sortable: true,
11 | },
12 | {
13 | name: 'Director',
14 | selector: row => row.director,
15 | sortable: true,
16 | },
17 | {
18 | name: 'Year',
19 | selector: row => row.year,
20 | sortable: true,
21 | },
22 | ];
23 |
24 | export const Basic = () => ;
25 |
26 | export default {
27 | title: 'Selectable/Basic',
28 | component: Basic,
29 | parameters: {
30 | docs: {
31 | page: doc,
32 | },
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/src/icons/NativeSortIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import { SortOrder } from '../DataTable/types';
4 |
5 | const Icon = styled.span<{
6 | $sortActive: boolean;
7 | $sortDirection: SortOrder;
8 | }>`
9 | padding: 2px;
10 | color: inherit;
11 | flex-grow: 0;
12 | flex-shrink: 0;
13 | ${({ $sortActive }) => ($sortActive ? 'opacity: 1' : 'opacity: 0')};
14 | ${({ $sortDirection }) => $sortDirection === 'desc' && 'transform: rotate(180deg)'};
15 | `;
16 |
17 | interface NativeSortIconProps {
18 | sortActive: boolean;
19 | sortDirection: SortOrder;
20 | }
21 |
22 | const NativeSortIcon: React.FC = ({ sortActive, sortDirection }) => (
23 |
24 | ▲
25 |
26 | );
27 |
28 | export default NativeSortIcon;
29 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "esModuleInterop": true,
5 | "experimentalDecorators": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "isolatedModules": true,
8 | "noEmit": true,
9 | "noImplicitReturns": true,
10 | "noUnusedLocals": true,
11 | "removeComments": true,
12 | "resolveJsonModule": true,
13 | "skipLibCheck": true,
14 | "strict": true,
15 | "sourceMap": true,
16 | "baseUrl": "src",
17 | "jsx": "react",
18 | "moduleResolution": "node",
19 | "outDir": "dist",
20 | "target": "es6",
21 | "module": "esnext",
22 | "lib": [
23 | "es6",
24 | "es7",
25 | "dom"
26 | ],
27 | "declaration": true
28 | },
29 | "include": [
30 | "src/**/*"
31 | ],
32 | "exclude": [
33 | "node_modules",
34 | "dist",
35 | "src/**/__tests__",
36 | "src/**/internal"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/stories/DataTable/selectableRows/basic.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Canvas } from '@storybook/addon-docs';
2 |
3 | # Selectable Rows
4 |
5 | Adding Selectable Rows functionality is easy. Let's make our rows selectable and also access selected results `onSelectedRowsChange`:
6 |
7 | ```js
8 | function MyComponent() {
9 | const handleChange = ({ selectedRows }) => {
10 | // You can set state or dispatch with something like Redux so we can use the retrieved data
11 | console.log('Selected Rows: ', selectedRows);
12 | };
13 |
14 | return (
15 |
21 | );
22 | };
23 | ```
24 |
25 |
28 |
29 | ## Accessibility
30 |
31 | You can tab through checkboxes and use the space bar to expand.
32 |
--------------------------------------------------------------------------------
/src/hooks/useRTL.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Direction } from '../DataTable/constants';
3 |
4 | function useRTL(direction: Direction = Direction.AUTO): boolean {
5 | const isClient = typeof window === 'object';
6 |
7 | const [isRTL, setIsRTL] = React.useState(false);
8 |
9 | React.useEffect(() => {
10 | if (!isClient) {
11 | return;
12 | }
13 |
14 | if (direction === 'auto') {
15 | const canUse = !!(window.document && window.document.createElement);
16 | const bodyRTL = document.getElementsByTagName('BODY')[0];
17 | const htmlTRL = document.getElementsByTagName('HTML')[0];
18 | const hasRTL = bodyRTL.dir === 'rtl' || htmlTRL.dir === 'rtl';
19 |
20 | setIsRTL(canUse && hasRTL);
21 |
22 | return;
23 | }
24 |
25 | setIsRTL(direction === 'rtl');
26 | }, [direction, isClient]);
27 |
28 | return isRTL;
29 | }
30 |
31 | export default useRTL;
32 |
--------------------------------------------------------------------------------
/stories/DataTable/sorting/sortFunctionCol.mdx:
--------------------------------------------------------------------------------
1 | import { Story, Canvas } from '@storybook/addon-docs';
2 |
3 | # Custom Sorting Individual Columns - sortFunction
4 |
5 | If you only need to override the sorting behavior for a specific column(s). In this case, the custom sorting function takes only provides you access to two arguments, rowA and rowB.
6 |
7 | ```js
8 | const caseInsensitiveSort = (rowA, rowB) => {
9 | const a = rowA.title.toLowerCase();
10 | const b = rowB.title.toLowerCase();
11 |
12 | if (a > b) {
13 | return 1;
14 | }
15 |
16 | if (b > a) {
17 | return -1;
18 | }
19 |
20 | return 0;
21 | };
22 |
23 | const columns = [
24 | {
25 | name: 'Quantity',
26 | selector: 'quantity',
27 | sortable: true,
28 | sortFunction: caseInsensitiveSort
29 | }
30 | ];
31 |
32 | ...
33 |
34 | ```
35 |
36 |
39 |
--------------------------------------------------------------------------------
/src/DataTable/media.ts:
--------------------------------------------------------------------------------
1 | import { css, CSSObject, RuleSet } from 'styled-components';
2 |
3 | export const SMALL = 599;
4 | export const MEDIUM = 959;
5 | export const LARGE = 1280;
6 |
7 | export const media = {
8 | sm: (literals: TemplateStringsArray, ...args: CSSObject[]): RuleSet