Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow optional DataGrid props to be null #2406

Merged
merged 3 commits into from
May 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 65 additions & 50 deletions src/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,78 +103,78 @@ export interface DataGridProps<R, SR = unknown, K extends React.Key = React.Key>
* Rows to be pinned at the bottom of the rows view for summary, the vertical scroll bar will not scroll these rows.
* Bottom horizontal scroll bar can move the row left / right. Or a customized row renderer can be used to disabled the scrolling support.
*/
summaryRows?: readonly SR[];
summaryRows?: readonly SR[] | null;
/** The getter should return a unique key for each row */
rowKeyGetter?: (row: R) => K;
onRowsChange?: (rows: R[], data: RowsChangeData<R, SR>) => void;
rowKeyGetter?: ((row: R) => K) | null;
onRowsChange?: ((rows: R[], data: RowsChangeData<R, SR>) => void) | null;

/**
* Dimensions props
*/
/** The height of each row in pixels */
rowHeight?: number | ((args: RowHeightArgs<R>) => number);
rowHeight?: number | ((args: RowHeightArgs<R>) => number) | null;
/** The height of the header row in pixels */
headerRowHeight?: number;
headerRowHeight?: number | null;
/** The height of the header filter row in pixels */
headerFiltersHeight?: number;
headerFiltersHeight?: number | null;
/** The height of each summary row in pixels */
summaryRowHeight?: number;
summaryRowHeight?: number | null;

/**
* Feature props
*/
/** Set of selected row keys */
selectedRows?: ReadonlySet<K>;
selectedRows?: ReadonlySet<K> | null;
/** Function called whenever row selection is changed */
onSelectedRowsChange?: (selectedRows: Set<K>) => void;
onSelectedRowsChange?: ((selectedRows: Set<K>) => void) | null;
/** The key of the column which is currently being sorted */
sortColumn?: string;
sortColumn?: string | null;
/** The direction to sort the sortColumn*/
sortDirection?: SortDirection;
sortDirection?: SortDirection | null;
/** Function called whenever grid is sorted*/
onSort?: (columnKey: string, direction: SortDirection) => void;
filters?: Readonly<Filters>;
onFiltersChange?: (filters: Filters) => void;
defaultColumnOptions?: DefaultColumnOptions<R, SR>;
groupBy?: readonly string[];
rowGrouper?: (rows: readonly R[], columnKey: string) => Record<string, readonly R[]>;
expandedGroupIds?: ReadonlySet<unknown>;
onExpandedGroupIdsChange?: (expandedGroupIds: Set<unknown>) => void;
onFill?: (event: FillEvent<R>) => R[];
onPaste?: (event: PasteEvent<R>) => R;
onSort?: ((columnKey: string, direction: SortDirection) => void) | null;
filters?: Readonly<Filters> | null;
onFiltersChange?: ((filters: Filters) => void) | null;
defaultColumnOptions?: DefaultColumnOptions<R, SR> | null;
groupBy?: readonly string[] | null;
rowGrouper?: ((rows: readonly R[], columnKey: string) => Record<string, readonly R[]>) | null;
expandedGroupIds?: ReadonlySet<unknown> | null;
onExpandedGroupIdsChange?: ((expandedGroupIds: Set<unknown>) => void) | null;
onFill?: ((event: FillEvent<R>) => R[]) | null;
onPaste?: ((event: PasteEvent<R>) => R) | null;

/**
* Custom renderers
*/
rowRenderer?: React.ComponentType<RowRendererProps<R, SR>>;
emptyRowsRenderer?: React.ComponentType;
rowRenderer?: React.ComponentType<RowRendererProps<R, SR>> | null;
emptyRowsRenderer?: React.ComponentType | null;

/**
* Event props
*/
/** Function called whenever a row is clicked */
onRowClick?: (rowIdx: number, row: R, column: CalculatedColumn<R, SR>) => void;
onRowClick?: ((rowIdx: number, row: R, column: CalculatedColumn<R, SR>) => void) | null;
/** Called when the grid is scrolled */
onScroll?: (event: React.UIEvent<HTMLDivElement>) => void;
onScroll?: ((event: React.UIEvent<HTMLDivElement>) => void) | null;
/** Called when a column is resized */
onColumnResize?: (idx: number, width: number) => void;
onColumnResize?: ((idx: number, width: number) => void) | null;
/** Function called whenever selected cell is changed */
onSelectedCellChange?: (position: Position) => void;
onSelectedCellChange?: ((position: Position) => void) | null;

/**
* Toggles and modes
*/
/** Toggles whether filters row is displayed or not */
enableFilterRow?: boolean;
cellNavigationMode?: CellNavigationMode;
enableVirtualization?: boolean;
enableFilterRow?: boolean | null;
cellNavigationMode?: CellNavigationMode | null;
enableVirtualization?: boolean | null;

/**
* Miscellaneous
*/
/** The node where the editor portal should mount. */
editorPortalTarget?: Element;
rowClass?: (row: R) => string | undefined;
editorPortalTarget?: Element | null;
rowClass?: ((row: R) => string | undefined | null) | null;
}

/**
Expand All @@ -184,7 +184,7 @@ export interface DataGridProps<R, SR = unknown, K extends React.Key = React.Key>
*
* <DataGrid columns={columns} rows={rows} />
*/
function DataGrid<R, SR>(
function DataGrid<R, SR, K extends React.Key>(
{
// Grid and data Props
columns: rawColumns,
Expand All @@ -193,10 +193,10 @@ function DataGrid<R, SR>(
rowKeyGetter,
onRowsChange,
// Dimensions props
rowHeight = 35,
headerRowHeight = typeof rowHeight === 'number' ? rowHeight : 35,
headerFiltersHeight = 45,
summaryRowHeight = typeof rowHeight === 'number' ? rowHeight : 35,
rowHeight,
headerRowHeight,
headerFiltersHeight,
summaryRowHeight: rawSummaryRowHeight,
// Feature props
selectedRows,
onSelectedRowsChange,
Expand All @@ -211,7 +211,7 @@ function DataGrid<R, SR>(
expandedGroupIds,
onExpandedGroupIdsChange,
// Custom renderers
rowRenderer: RowRenderer = Row,
rowRenderer,
emptyRowsRenderer: EmptyRowsRenderer,
// Event props
onRowClick,
Expand All @@ -221,21 +221,34 @@ function DataGrid<R, SR>(
onFill,
onPaste,
// Toggles and modes
enableFilterRow = false,
cellNavigationMode = 'NONE',
enableVirtualization = true,
enableFilterRow,
cellNavigationMode: rawCellNavigationMode,
enableVirtualization,
// Miscellaneous
editorPortalTarget = body,
editorPortalTarget: rawEditorPortalTarget,
className,
style,
rowClass,
// ARIA
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
'aria-describedby': ariaDescribedBy
}: DataGridProps<R, SR>,
}: DataGridProps<R, SR, K>,
ref: React.Ref<DataGridHandle>
) {
/**
* defaults
*/
rowHeight ??= 35;
headerRowHeight ??= typeof rowHeight === 'number' ? rowHeight : 35;
headerFiltersHeight ??= 45;
const summaryRowHeight = rawSummaryRowHeight ?? (typeof rowHeight === 'number' ? rowHeight : 35);
Copy link
Contributor Author

@nstepien nstepien May 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a little bit of nuance here vs default params, if a param isn't defaulted in the parameter list, then it keeps its original type in functions, as TS cannot know that those functions are only called after the ??= line. I wish we could use ??= in the param list. It also means they can be re-assigned to null or undefined if we're not careful.

function fnA(param = 1) {
  param; // type; number

  function test() {
    param; // type: number
  }
}

function fnB(param: number | undefined | null) {
  param; // type: number | undefined | null
  param ??= 1;
  param; // type: number

  function test() {
    param; // type: number | undefined | null
    // this is because we could call `test` before the line where we set it to 1, or it can be reassigned to `null | undefined`
  }
}

const RowRenderer = rowRenderer ?? Row;
enableFilterRow ??= false;
const cellNavigationMode = rawCellNavigationMode ?? 'NONE';
enableVirtualization ??= true;
const editorPortalTarget = rawEditorPortalTarget ?? body;

/**
* states
*/
Expand Down Expand Up @@ -273,7 +286,7 @@ function DataGrid<R, SR>(
const summaryRowsCount = summaryRows?.length ?? 0;
const totalHeaderHeight = headerRowHeight + (enableFilterRow ? headerFiltersHeight : 0);
const clientHeight = gridHeight - totalHeaderHeight - summaryRowsCount * summaryRowHeight;
const isSelectable = selectedRows !== undefined && onSelectedRowsChange !== undefined;
const isSelectable = selectedRows != null && onSelectedRowsChange != null;

const {
columns,
Expand Down Expand Up @@ -335,7 +348,7 @@ function DataGrid<R, SR>(
const minColIdx = hasGroups ? -1 : 0;

// Cell drag is not supported on a treegrid
const enableCellDragAndDrop = hasGroups ? false : onFill !== undefined;
const enableCellDragAndDrop = hasGroups ? false : onFill != null;

/**
* effects
Expand Down Expand Up @@ -401,7 +414,7 @@ function DataGrid<R, SR>(
function selectRow({ rowIdx, checked, isShiftClick }: SelectRowEvent) {
if (!onSelectedRowsChange) return;

assertIsValidKeyGetter(rowKeyGetter);
assertIsValidKeyGetter<R, K>(rowKeyGetter);
const newSelectedRows = new Set(selectedRows);
const row = rows[rowIdx];
if (isGroupRow(row)) {
Expand Down Expand Up @@ -973,11 +986,13 @@ function DataGrid<R, SR>(
}

startRowIndex++;
let key: React.Key = hasGroups ? startRowIndex : rowIdx;
let key;
let isRowSelected = false;
if (typeof rowKeyGetter === 'function') {
key = rowKeyGetter(row);
isRowSelected = selectedRows?.has(key) ?? false;
} else {
key = hasGroups ? startRowIndex : rowIdx;
}

rowElements.push(
Expand Down Expand Up @@ -1048,7 +1063,7 @@ function DataGrid<R, SR>(
ref={gridRef}
onScroll={handleScroll}
>
<HeaderRow<R, SR>
<HeaderRow<R, SR, K>
rowKeyGetter={rowKeyGetter}
rows={rawRows}
columns={viewportColumns}
Expand Down Expand Up @@ -1117,6 +1132,6 @@ function DataGrid<R, SR>(
);
}

export default forwardRef(DataGrid) as <R, SR = unknown, Key extends React.Key = React.Key>(
props: DataGridProps<R, SR, Key> & RefAttributes<DataGridHandle>
export default forwardRef(DataGrid) as <R, SR = unknown, K extends React.Key = React.Key>(
props: DataGridProps<R, SR, K> & RefAttributes<DataGridHandle>
) => JSX.Element;
4 changes: 2 additions & 2 deletions src/HeaderCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const cellResizable = css`

const cellResizableClassname = `rdg-cell-resizable ${cellResizable}`;

function getAriaSort(sortDirection: SortDirection | undefined) {
function getAriaSort(sortDirection: SortDirection | undefined | null) {
switch (sortDirection) {
case 'ASC':
return 'ascending';
Expand All @@ -31,7 +31,7 @@ function getAriaSort(sortDirection: SortDirection | undefined) {
}

type SharedHeaderRowProps<R, SR> = Pick<
HeaderRowProps<R, SR>,
HeaderRowProps<R, SR, React.Key>,
'sortColumn' | 'sortDirection' | 'onSort' | 'allRowsSelected'
>;

Expand Down
18 changes: 10 additions & 8 deletions src/HeaderRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import { assertIsValidKeyGetter, getColSpan } from './utils';
import type { DataGridProps } from './DataGrid';
import { headerRowClassname } from './style';

type SharedDataGridProps<R, SR> = Pick<
DataGridProps<R, SR>,
type SharedDataGridProps<R, SR, K extends React.Key> = Pick<
DataGridProps<R, SR, K>,
'rows' | 'onSelectedRowsChange' | 'sortColumn' | 'sortDirection' | 'onSort' | 'rowKeyGetter'
>;

export interface HeaderRowProps<R, SR> extends SharedDataGridProps<R, SR> {
export interface HeaderRowProps<R, SR, K extends React.Key> extends SharedDataGridProps<R, SR, K> {
columns: readonly CalculatedColumn<R, SR>[];
allRowsSelected: boolean;
onColumnResize: (column: CalculatedColumn<R, SR>, width: number) => void;
lastFrozenColumnIndex: number;
}

function HeaderRow<R, SR>({
function HeaderRow<R, SR, K extends React.Key>({
columns,
rows,
rowKeyGetter,
Expand All @@ -29,14 +29,14 @@ function HeaderRow<R, SR>({
sortDirection,
onSort,
lastFrozenColumnIndex
}: HeaderRowProps<R, SR>) {
}: HeaderRowProps<R, SR, K>) {
const handleAllRowsSelectionChange = useCallback(
(checked: boolean) => {
if (!onSelectedRowsChange) return;

assertIsValidKeyGetter(rowKeyGetter);
assertIsValidKeyGetter<R, K>(rowKeyGetter);

const newSelectedRows = new Set<React.Key>(checked ? rows.map(rowKeyGetter) : undefined);
const newSelectedRows = new Set<K>(checked ? rows.map(rowKeyGetter) : undefined);
onSelectedRowsChange(newSelectedRows);
},
[onSelectedRowsChange, rows, rowKeyGetter]
Expand Down Expand Up @@ -76,4 +76,6 @@ function HeaderRow<R, SR>({
);
}

export default memo(HeaderRow) as <R, SR>(props: HeaderRowProps<R, SR>) => JSX.Element;
export default memo(HeaderRow) as <R, SR, K extends React.Key>(
props: HeaderRowProps<R, SR, K>
) => JSX.Element;
2 changes: 1 addition & 1 deletion src/hooks/useCalculatedColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { floor, max, min } from '../utils';

interface CalculatedColumnsArgs<R, SR> extends Pick<DataGridProps<R, SR>, 'defaultColumnOptions'> {
rawColumns: readonly Column<R, SR>[];
rawGroupBy: readonly string[] | undefined;
rawGroupBy: readonly string[] | undefined | null;
viewportWidth: number;
scrollLeft: number;
columnWidths: ReadonlyMap<string, number>;
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useViewportColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface ViewportColumnsArgs<R, SR> {
columns: readonly CalculatedColumn<R, SR>[];
colSpanColumns: readonly CalculatedColumn<R, SR>[];
rows: readonly (R | GroupRow<R>)[];
summaryRows: readonly SR[] | undefined;
summaryRows: readonly SR[] | undefined | null;
colOverscanStartIdx: number;
colOverscanEndIdx: number;
lastFrozenColumnIndex: number;
Expand Down Expand Up @@ -72,7 +72,7 @@ export function useViewportColumns<R, SR>({
}

// check summary rows
if (summaryRows !== undefined) {
if (summaryRows != null) {
for (const row of summaryRows) {
if (
updateStartIdx(
Expand Down
9 changes: 6 additions & 3 deletions src/hooks/useViewportRows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ interface ViewportRowsArgs<R> {
clientHeight: number;
scrollTop: number;
groupBy: readonly string[];
rowGrouper: ((rows: readonly R[], columnKey: string) => Record<string, readonly R[]>) | undefined;
expandedGroupIds: ReadonlySet<unknown> | undefined;
rowGrouper:
| ((rows: readonly R[], columnKey: string) => Record<string, readonly R[]>)
| undefined
| null;
expandedGroupIds: ReadonlySet<unknown> | undefined | null;
enableVirtualization: boolean;
}

Expand All @@ -31,7 +34,7 @@ export function useViewportRows<R>({
enableVirtualization
}: ViewportRowsArgs<R>) {
const [groupedRows, rowsCount] = useMemo(() => {
if (groupBy.length === 0 || !rowGrouper) return [undefined, rawRows.length];
if (groupBy.length === 0 || rowGrouper == null) return [undefined, rawRows.length];

const groupRows = (
rows: readonly R[],
Expand Down
14 changes: 8 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ export interface EditorProps<TRow, TSummaryRow = unknown> extends SharedEditorPr

export interface HeaderRendererProps<TRow, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
sortColumn: string | undefined;
sortDirection: SortDirection | undefined;
onSort: ((columnKey: string, direction: SortDirection) => void) | undefined;
sortColumn: string | undefined | null;
sortDirection: SortDirection | undefined | null;
onSort: ((columnKey: string, direction: SortDirection) => void) | undefined | null;
allRowsSelected: boolean;
onAllRowsSelectionChange: (checked: boolean) => void;
}
Expand Down Expand Up @@ -156,7 +156,8 @@ export interface CellRendererProps<TRow, TSummaryRow = unknown>
onRowChange: (rowIdx: number, newRow: TRow) => void;
onRowClick:
| ((rowIdx: number, row: TRow, column: CalculatedColumn<TRow, TSummaryRow>) => void)
| undefined;
| undefined
| null;
selectCell: (position: Position, enableEditor?: boolean) => void;
}

Expand All @@ -176,8 +177,9 @@ export interface RowRendererProps<TRow, TSummaryRow = unknown>
onRowChange: (rowIdx: number, row: TRow) => void;
onRowClick:
| ((rowIdx: number, row: TRow, column: CalculatedColumn<TRow, TSummaryRow>) => void)
| undefined;
rowClass: ((row: TRow) => string | undefined) | undefined;
| undefined
| null;
rowClass: ((row: TRow) => string | undefined | null) | undefined | null;
setDraggedOverRowIdx: ((overRowIdx: number) => void) | undefined;
selectCell: (position: Position, enableEditor?: boolean) => void;
}
Expand Down
Loading