Skip to content

Commit 5de5ac3

Browse files
authored
Merge pull request #916 from jbetancur/fix/914
fixes type inference
2 parents a9c2f84 + fcdc657 commit 5de5ac3

11 files changed

Lines changed: 151 additions & 53 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-data-table-component",
3-
"version": "7.1.0",
3+
"version": "7.2.0",
44
"description": "A simple to use declarative react based data table",
55
"main": "dist/index.cjs.js",
66
"module": "dist/index.es.js",

src/DataTable/DataTable.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ import { CellBase } from './Cell';
1818
import NoData from './NoDataWrapper';
1919
import NativePagination from './Pagination';
2020
import useDidUpdateEffect from '../hooks/useDidUpdateEffect';
21-
import { getNumberOfPages, setRowData, isEmpty, isRowSelected, recalculatePage } from './util';
21+
import { prop, getNumberOfPages, setRowData, isEmpty, isRowSelected, recalculatePage } from './util';
2222
import { defaultProps } from './defaultProps';
2323
import { createStyles } from './styles';
2424
import { Action, AllRowsAction, SingleRowAction, TableRow, SortAction, TableProps, TableState } from './types';
2525
import useColumns from '../hooks/useColumns';
2626

27-
function DataTable<T extends TableRow>(props: TableProps<T>): JSX.Element {
27+
function DataTable<T>(props: TableProps<T>): JSX.Element {
2828
const {
2929
data = defaultProps.data,
3030
columns = defaultProps.columns,
@@ -388,8 +388,7 @@ function DataTable<T extends TableRow>(props: TableProps<T>): JSX.Element {
388388
{!progressPending && rows.length > 0 && (
389389
<Body className="rdt_TableBody" role="rowgroup">
390390
{tableRows.map((row, i) => {
391-
// we need to cast key since the type is unknown beforehand
392-
const key = row[keyField] as string | number;
391+
const key = prop(row as TableRow, keyField) as string | number;
393392
const id = isEmpty(key) ? i : key;
394393
const selected = isRowSelected(row, selectedRows, keyField);
395394
const expanderExpander = !!(expandableRows && expandableRowExpanded && expandableRowExpanded(row));
@@ -400,7 +399,7 @@ function DataTable<T extends TableRow>(props: TableProps<T>): JSX.Element {
400399
id={id}
401400
key={id}
402401
keyField={keyField}
403-
data-row-id={row[keyField]}
402+
data-row-id={id}
404403
columns={tableColumns}
405404
row={row}
406405
rowCount={rows.length}

src/DataTable/TableCellCheckbox.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import styled from 'styled-components';
33
import { CellBase } from './Cell';
44
import Checkbox from './Checkbox';
5-
import { TableRow, RowState, SingleRowAction, ComponentProps } from './types';
5+
import { RowState, SingleRowAction, ComponentProps } from './types';
66

77
const TableCellCheckboxStyle = styled(CellBase)`
88
flex: 0 0 48px;
@@ -14,6 +14,7 @@ const TableCellCheckboxStyle = styled(CellBase)`
1414
`;
1515

1616
type TableCellCheckboxProps<T> = {
17+
name: string;
1718
keyField: string;
1819
row: T;
1920
rowCount: number;
@@ -25,7 +26,8 @@ type TableCellCheckboxProps<T> = {
2526
onSelectedRow: (action: SingleRowAction<T>) => void;
2627
};
2728

28-
function TableCellCheckbox<T extends TableRow>({
29+
function TableCellCheckbox<T>({
30+
name,
2931
keyField,
3032
row,
3133
rowCount,
@@ -52,7 +54,7 @@ function TableCellCheckbox<T extends TableRow>({
5254
return (
5355
<TableCellCheckboxStyle onClick={(e: React.MouseEvent) => e.stopPropagation()} className="rdt_TableCell" noPadding>
5456
<Checkbox
55-
name={`select-row-${row[keyField]}`}
57+
name={name}
5658
component={selectableRowsComponent}
5759
componentOptions={selectableRowsComponentProps}
5860
checked={selected}

src/DataTable/TableRow.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import TableCell from './TableCell';
44
import TableCellCheckbox from './TableCellCheckbox';
55
import TableCellExpander from './TableCellExpander';
66
import ExpanderRow from './ExpanderRow';
7-
import { equalizeId, getConditionalStyle, isOdd, noop } from './util';
7+
import { prop, equalizeId, getConditionalStyle, isOdd, noop } from './util';
88
import { STOP_PROP_TAG } from './constants';
99
import { TableRow, SingleRowAction, TableProps } from './types';
1010

@@ -90,7 +90,7 @@ interface TableRowProps<T> extends Required<DProps<T>> {
9090
onDragLeave: (e: React.DragEvent<HTMLDivElement>) => void;
9191
}
9292

93-
function Row<T extends TableRow>({
93+
function Row<T>({
9494
columns = [],
9595
conditionalRowStyles = [],
9696
defaultExpanded = false,
@@ -169,6 +169,7 @@ function Row<T extends TableRow>({
169169
[defaultExpanderDisabled, expandOnRowDoubleClicked, expandableRows, handleExpanded, onRowDoubleClicked, row],
170170
);
171171

172+
const rowKeyField = prop(row as TableRow, keyField);
172173
const { style, classNames } = getConditionalStyle(row, conditionalRowStyles, ['rdt_TableRow']);
173174
const highlightSelected = selectableRowsHighlight && selected;
174175
const inheritStyles = expandableInheritConditionalStyles ? style : {};
@@ -191,6 +192,7 @@ function Row<T extends TableRow>({
191192
>
192193
{selectableRows && (
193194
<TableCellCheckbox
195+
name={`select-row-${rowKeyField}`}
194196
keyField={keyField}
195197
row={row}
196198
rowCount={rowCount}
@@ -205,7 +207,7 @@ function Row<T extends TableRow>({
205207

206208
{expandableRows && !expandableRowsHideExpander && (
207209
<TableCellExpander
208-
id={row[keyField] as string}
210+
id={rowKeyField as string}
209211
expandableIcon={expandableIcon}
210212
expanded={expanded}
211213
row={row}
@@ -221,8 +223,8 @@ function Row<T extends TableRow>({
221223

222224
return (
223225
<TableCell
224-
id={`cell-${column.id}-${row[keyField]}`}
225-
key={`cell-${column.id}-${row[keyField]}`}
226+
id={`cell-${column.id}-${rowKeyField}`}
227+
key={`cell-${column.id}-${rowKeyField}`}
226228
// apply a tag that Row will use to stop event propagation when TableCell is clicked
227229
dataTag={column.ignoreRowClick || column.button ? null : STOP_PROP_TAG}
228230
column={column}
@@ -241,7 +243,7 @@ function Row<T extends TableRow>({
241243

242244
{expandableRows && expanded && (
243245
<ExpanderRow
244-
key={`expander-${row[keyField]}`}
246+
key={`expander-${rowKeyField}`}
245247
data={row}
246248
extendedRowStyle={inheritStyles}
247249
extendedClassNames={classNames}

src/DataTable/__tests__/util.test.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ const row = Object.freeze({
1818
properties: { nested: 'iamnesting', items: [{ id: 1, name: 'iamarrayname' }] },
1919
});
2020

21+
type DataRow = {
22+
id?: number;
23+
name?: string;
24+
properties?: {
25+
nested: string;
26+
items: { id: number; name: string }[];
27+
};
28+
};
29+
2130
describe('isEmpty', () => {
2231
test('if the value is a number return false', () => {
2332
expect(isEmpty(1)).toBe(false);
@@ -235,7 +244,7 @@ describe('handleFunctionProps', () => {
235244

236245
describe('getConditionalStyle', () => {
237246
test('should return a row style if the expression matches', () => {
238-
const rowStyleExpression: ConditionalStyles[] = [
247+
const rowStyleExpression: ConditionalStyles<DataRow>[] = [
239248
{
240249
when: r => r.name === 'luke',
241250
style: {
@@ -249,7 +258,7 @@ describe('getConditionalStyle', () => {
249258
});
250259

251260
test('should return {} if the expression does not match', () => {
252-
const rowStyleExpression: ConditionalStyles[] = [
261+
const rowStyleExpression: ConditionalStyles<DataRow>[] = [
253262
{
254263
when: r => r.name === 'wookie',
255264
style: {
@@ -263,15 +272,15 @@ describe('getConditionalStyle', () => {
263272
});
264273

265274
test('should return {} if there are no style object expressions', () => {
266-
const rowStyleExpression: ConditionalStyles[] = [];
275+
const rowStyleExpression: ConditionalStyles<DataRow>[] = [];
267276

268277
const { style } = getConditionalStyle({ name: 'luke' }, rowStyleExpression);
269278

270279
expect(style).toEqual({});
271280
});
272281

273282
test('should default to an empty object if the style property is not provided', () => {
274-
const rowStyleExpression: ConditionalStyles[] = [
283+
const rowStyleExpression: ConditionalStyles<DataRow>[] = [
275284
{
276285
when: r => r.name === 'luke',
277286
},
@@ -295,7 +304,7 @@ describe('getConditionalStyle', () => {
295304
});
296305

297306
test('should return "anakin leia" if the expression matches and a base class is provided', () => {
298-
const rowStyleExpression: ConditionalStyles[] = [
307+
const rowStyleExpression: ConditionalStyles<DataRow>[] = [
299308
{
300309
when: r => r.name === 'luke',
301310
classNames: ['leia'],

src/DataTable/tableReducer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { insertItem, isRowSelected, removeItem } from './util';
2-
import { Action, TableRow, TableState } from './types';
2+
import { Action, TableState } from './types';
33

4-
export function tableReducer<T extends TableRow>(state: TableState<T>, action: Action<T>): TableState<T> {
4+
export function tableReducer<T>(state: TableState<T>, action: Action<T>): TableState<T> {
55
const toggleOnSelectedRowsChange = !state.toggleOnSelectedRowsChange;
66

77
switch (action.type) {

src/DataTable/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type ExpandableRowsComponent = React.ComponentType<Record<string, unknown
1515
export type PaginationComponent = React.ComponentType<Record<string, unknown>>;
1616
export type ComponentProps = Record<string, unknown>;
1717

18-
export type TableProps<T = TableRow> = {
18+
export type TableProps<T> = {
1919
actions?: React.ReactNode | React.ReactNode[];
2020
className?: string;
2121
clearSelectedRows?: boolean;
@@ -122,7 +122,7 @@ export type TableColumnBase = {
122122
wrap?: boolean;
123123
};
124124

125-
export interface TableColumn<T = TableRow> extends TableColumnBase {
125+
export interface TableColumn<T> extends TableColumnBase {
126126
name?: string | number | React.ReactNode;
127127
sortfield?: string;
128128
cell?: (row: T, rowIndex: number, column: TableColumn<T>, id: string | number) => React.ReactNode;
@@ -132,7 +132,7 @@ export interface TableColumn<T = TableRow> extends TableColumnBase {
132132
sortFunction?: ColumnSortFunction<T>;
133133
}
134134

135-
export interface ConditionalStyles<T = TableRow> {
135+
export interface ConditionalStyles<T> {
136136
when: (row: T) => boolean;
137137
style?: CSSObject | ((row: T) => CSSObject);
138138
classNames?: string[];

src/DataTable/util.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import orderBy from 'lodash.orderby';
22
import { CSSObject } from 'styled-components';
33
import { ConditionalStyles, TableColumn, Format, TableRow, Selector, SortDirection, SortFunction } from './types';
44

5+
export function prop<T, K extends keyof T>(obj: T, key: K): T[K] {
6+
return obj[key];
7+
}
8+
59
export function isEmpty(field: string | number | undefined = ''): boolean {
610
if (typeof field === 'number') {
711
return false;
@@ -24,7 +28,7 @@ export function setRowData<T>(
2428
return sort(rows, selector, direction, sortFn);
2529
}
2630

27-
export function sort<T = TableRow>(
31+
export function sort<T>(
2832
rows: T[],
2933
selector: Selector<T> | null | undefined,
3034
direction: SortDirection,
@@ -85,12 +89,17 @@ export function insertItem<T>(array: T[] = [], item: T, index = 0): T[] {
8589
return [...array.slice(0, index), item, ...array.slice(index)];
8690
}
8791

88-
export function removeItem<T extends TableRow>(array: T[] = [], item: T, keyField = 'id'): T[] {
92+
export function removeItem<T>(array: T[] = [], item: T, keyField = 'id'): T[] {
8993
const newArray = array.slice();
94+
const outerField = prop(item as TableRow, keyField);
9095

91-
if (item[keyField]) {
96+
if (outerField) {
9297
newArray.splice(
93-
newArray.findIndex((a: T) => a[keyField] === item[keyField]),
98+
newArray.findIndex((a: T) => {
99+
const innerField = prop(a as TableRow, keyField);
100+
101+
return innerField === outerField;
102+
}),
94103
1,
95104
);
96105
} else {
@@ -187,9 +196,16 @@ export function getConditionalStyle<T>(
187196
return { style: rowStyle, classNames: classNames.join(' ') };
188197
}
189198

190-
export function isRowSelected<T extends TableRow>(row: T, selectedRows: T[] = [], keyField = 'id'): boolean {
191-
if (row[keyField]) {
192-
return selectedRows.some(r => r[keyField] === row[keyField]);
199+
export function isRowSelected<T>(row: T, selectedRows: T[] = [], keyField = 'id'): boolean {
200+
// cast row as TableRow because the property is unknown in advance therefore, typescript will throw an error
201+
const outerField = prop(row as TableRow, keyField);
202+
203+
if (outerField) {
204+
return selectedRows.some(r => {
205+
const innerField = prop(r as TableRow, keyField);
206+
207+
return innerField === outerField;
208+
});
193209
}
194210

195211
return selectedRows.some(r => r === row);

stories/DataTable/KitchenSink.stories.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import TextField from '@material-ui/core/TextField';
99
import data from '../constants/sampleMovieData';
1010
import DataTable, { Alignment, Direction, TableProps, TableColumn } from '../../src/index';
1111

12-
type Row = {
12+
interface Row {
1313
title: string;
1414
director: string;
1515
year: string;
16-
};
16+
}
1717

1818
const subHeaderComponent = (
1919
<div style={{ display: 'flex', alignItems: 'center' }}>
@@ -45,7 +45,7 @@ const columns: TableColumn<Row>[] = [
4545
},
4646
];
4747

48-
interface TablePropsExtended extends TableProps {
48+
interface TablePropsExtended extends TableProps<Row> {
4949
selectableRowsRadio: boolean;
5050
}
5151

0 commit comments

Comments
 (0)