Skip to content

Commit

Permalink
Merge pull request #1104 from jetstreamapp/bug/1103-data-table-fixes
Browse files Browse the repository at this point in the history
Data Table Fixes
  • Loading branch information
paustint authored Dec 8, 2024
2 parents 045d620 + 359b15f commit d75f84b
Show file tree
Hide file tree
Showing 20 changed files with 588 additions and 365 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nx/eslint-plugin"],
"plugins": ["@nx/eslint-plugin", "eslint-plugin-react-compiler"],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "prettier"],
"rules": {
"@typescript-eslint/explicit-member-accessibility": "off",
Expand Down Expand Up @@ -43,6 +43,7 @@
{
"files": ["*.tsx"],
"rules": {
"react-compiler/react-compiler": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": "warn"
}
Expand Down
9 changes: 9 additions & 0 deletions apps/jetstream/src/main.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
@import '@salesforce-ux/design-system/scss/_design-tokens';

// Make react data grid checkbox look sort of like SLDS checkbox
@layer rdg.rdg-checkbox-input {
.rdg-checkbox-input {
inline-size: 14px;
block-size: 14px;
accent-color: #0176d3;
}
}

html {
background-color: rgb(17, 24, 39);
}
Expand Down
16 changes: 3 additions & 13 deletions libs/features/deploy/src/utils/deploy-metadata.utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,7 @@ import {
SalesforceDeployHistoryType,
SalesforceOrgUi,
} from '@jetstream/types';
import {
ColumnWithFilter,
Grid,
Icon,
SelectFormatter,
SelectHeaderGroupRenderer,
SelectHeaderRenderer,
setColumnFromType,
Spinner,
} from '@jetstream/ui';
import { ColumnWithFilter, Grid, Icon, SelectFormatter, SelectHeaderGroupRenderer, setColumnFromType, Spinner } from '@jetstream/ui';
import { composeQuery, getField, Query } from '@jetstreamapp/soql-parser-js';
import { formatISO } from 'date-fns/formatISO';
import { parseISO } from 'date-fns/parseISO';
Expand Down Expand Up @@ -232,16 +223,15 @@ export function getColumnDefinitions(): ColumnWithFilter<DeployMetadataTableRow>
</Grid>
);
}
return <SelectFormatter {...args} />;
return SelectColumn.renderCell?.(args) || <SelectFormatter {...args} />;
},
renderHeaderCell: SelectHeaderRenderer,
renderGroupCell: (args) => {
const { childRows } = args;
// Don't allow selection if child rows are loading
if (childRows.length === 0 || (childRows.length === 1 && (childRows[0].loading || !childRows[0].metadata))) {
return null;
}
return <SelectHeaderGroupRenderer {...args} />;
return SelectColumn.renderGroupCell?.(args) || <SelectHeaderGroupRenderer {...args} />;
},
colSpan: (args) => {
if (args.type === 'ROW') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { css } from '@emotion/react';
import { orderValues } from '@jetstream/shared/utils';
import { AutoFullHeightContainer, ColumnWithFilter, ContextMenuActionData, ContextMenuItem, DataTree } from '@jetstream/ui';
import { ContextMenuItem } from '@jetstream/types';
import { AutoFullHeightContainer, ColumnWithFilter, ContextMenuActionData, DataTree } from '@jetstream/ui';
import copyToClipboard from 'copy-to-clipboard';
import groupBy from 'lodash/groupBy';
import { FunctionComponent, useCallback, useEffect, useState } from 'react';
Expand Down
15 changes: 15 additions & 0 deletions libs/types/src/lib/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,21 @@ export interface DropDownItem<T = any> {
metadata?: T;
}

export interface ContextMenuItem<T = any> {
subheader?: string;
label: string | ReactNode;
value: T;
icon?: {
type: string;
icon: string;
description?: string;
}; // FIXME: unable to import cross module boundaries
trailingDivider?: boolean;
disabled?: boolean;
title?: string;
metadata?: T;
}

export interface QueryFieldHeader {
label: string;
accessor: string;
Expand Down
2 changes: 1 addition & 1 deletion libs/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export * from './lib/form/combobox/ComboboxWithItems';
export * from './lib/form/combobox/ComboboxWithItemsTypeAhead';
export * from './lib/form/combobox/ComboboxWithItemsVirtual';
export * from './lib/form/combobox/useFieldListItemsWithDrillIn';
export * from './lib/form/context-menu/ContextMenu';
export * from './lib/form/controlled-inputs/ControlledInput';
export * from './lib/form/controlled-inputs/ControlledTextarea';
export * from './lib/form/date-time/DateTime';
Expand Down Expand Up @@ -113,7 +114,6 @@ export * from './lib/nav/Navbar';
export * from './lib/nav/NavbarAppLauncher';
export * from './lib/nav/NavbarItem';
export * from './lib/nav/NavbarMenuItems';
export * from './lib/popover/ContextMenu';
export * from './lib/popover/Popover';
export * from './lib/popover/PopoverErrorButton';
export * from './lib/progress-indicator/ProgressIndicator';
Expand Down
79 changes: 51 additions & 28 deletions libs/ui/src/lib/data-table/DataTable.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SalesforceOrgUi } from '@jetstream/types';
import { ContextMenuItem, SalesforceOrgUi } from '@jetstream/types';
import { forwardRef } from 'react';
import DataGrid, { DataGridProps, SortColumn } from 'react-data-grid';
import 'react-data-grid/lib/styles.css';
import { ContextMenuContext, ContextMenuItem } from '../popover/ContextMenu';
import { ContextMenu } from '../form/context-menu/ContextMenu';
import { DataTableFilterContext, DataTableGenericContext } from './data-table-context';
import './data-table-styles.scss';
import { ColumnWithFilter, ContextMenuActionData, RowWithKey } from './data-table-types';
Expand Down Expand Up @@ -76,10 +76,13 @@ export const DataTable = forwardRef<any, DataTableProps<any>>(
reorderedColumns,
filterSetValues,
filteredRows,
contextMenuProps,
setSortColumns,
updateFilter,
handleReorderColumns,
handleCellKeydown,
handleCellContextMenu,
handleCloseContextMenu,
} = useDataTable({
data,
columns: _columns,
Expand All @@ -100,33 +103,53 @@ export const DataTable = forwardRef<any, DataTableProps<any>>(
});

return (
<ContextMenuContext.Provider value={new Map()}>
<DataTableGenericContext.Provider value={{ ...context, rows: filteredRows, columns }}>
<DataTableFilterContext.Provider
value={{
filterSetValues,
filters,
portalRefForFilters: context?.portalRefForFilters,
updateFilter,
}}
>
<DataGrid
data-id={gridId}
className="rdg-light fill-grid"
columns={reorderedColumns}
rows={filteredRows}
renderers={renderers}
sortColumns={sortColumns}
onSortColumnsChange={setSortColumns}
rowKeyGetter={getRowKey}
defaultColumnOptions={{ resizable: true, sortable: true, ...rest.defaultColumnOptions }}
onCellKeyDown={handleCellKeydown}
onColumnsReorder={handleReorderColumns}
{...rest}
<DataTableGenericContext.Provider value={{ ...context, rows: filteredRows, columns }}>
<DataTableFilterContext.Provider
value={{
filterSetValues,
filters,
portalRefForFilters: context?.portalRefForFilters,
updateFilter,
}}
>
<DataGrid
data-id={gridId}
className="rdg-light fill-grid"
columns={reorderedColumns}
rows={filteredRows}
// @ts-expect-error Types are incorrect, but they are generic and difficult to get correct
renderers={renderers}
sortColumns={sortColumns}
onSortColumnsChange={setSortColumns}
// @ts-expect-error Types are incorrect, but they are generic and difficult to get correct
rowKeyGetter={getRowKey}
defaultColumnOptions={{ resizable: true, sortable: true, ...rest.defaultColumnOptions } as any}
// @ts-expect-error Types are incorrect, but they are generic and difficult to get correct
onCellKeyDown={handleCellKeydown}
onColumnsReorder={handleReorderColumns}
// @ts-expect-error Types are incorrect, but they are generic and difficult to get correct
onCellContextMenu={handleCellContextMenu}
{...rest}
/>
{contextMenuProps && contextMenuItems && contextMenuAction && (
<ContextMenu
parentElement={contextMenuProps.element}
items={contextMenuItems}
onSelected={(item) => {
contextMenuAction(item, {
row: filteredRows[contextMenuProps.rowIdx] as T,
rowIdx: contextMenuProps.rowIdx,
rows: filteredRows as T[],
column: columns[contextMenuProps.rowIdx],
columns,
});
handleCloseContextMenu();
}}
onClose={handleCloseContextMenu}
/>
</DataTableFilterContext.Provider>
</DataTableGenericContext.Provider>
</ContextMenuContext.Provider>
)}
</DataTableFilterContext.Provider>
</DataTableGenericContext.Provider>
);
}
);
12 changes: 9 additions & 3 deletions libs/ui/src/lib/data-table/DataTableEditors.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FocusTrap } from '@headlessui/react';
import { logger } from '@jetstream/shared/client-logger';
import { SFDC_BLANK_PICKLIST_VALUE } from '@jetstream/shared/constants';
import { describeSObject, query } from '@jetstream/shared/data';
Expand Down Expand Up @@ -59,7 +60,7 @@ function DataTableEditorPopover({
ref={popoverRef}
isOpen
referenceElement={referenceElement as any}
className={`slds-popover slds-popover slds-popover_edit`}
className="slds-popover slds-popover slds-popover_edit"
role="dialog"
offset={[0, -28.5]}
usePortal
Expand All @@ -69,7 +70,11 @@ function DataTableEditorPopover({
}
}}
>
{referenceElement && <div className="slds-p-around_x-small">{children}</div>}
{referenceElement && (
<FocusTrap>
<div className="slds-p-around_x-small">{children}</div>
</FocusTrap>
)}
</PopoverContainer>
</OutsideClickHandler>
);
Expand Down Expand Up @@ -247,6 +252,7 @@ export function DataTableEditorDate<TRow extends { _idx: number }, TSummaryRow>(
className="d-block"
initialSelectedDate={currDate}
openOnInit
inputProps={{ autoFocus: true }}
onChange={(value) => {
/** setTimeout is used to avoid a React error about flushSync being called during a render */
setTimeout(() => {
Expand Down Expand Up @@ -328,7 +334,7 @@ export const dataTableEditorRecordLookup = ({ sobject }: { sobject: string }) =>
);

if (!org || !sobject) {
return <DataTableEditorText column={column} onClose={onClose} onRowChange={onRowChange} row={row} />;
return <DataTableEditorText rowIdx={row._idx} column={column} onClose={onClose} onRowChange={onRowChange} row={row} />;
}

return (
Expand Down
30 changes: 6 additions & 24 deletions libs/ui/src/lib/data-table/DataTableRenderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,9 @@ export function configIdLinkRenderer(serverUrl: string, org: SalesforceOrgUi, sk

// HEADER RENDERERS

/**
* SELECT ALL CHECKBOX HEADER
*/
export function SelectHeaderRenderer<T>(props: RenderHeaderCellProps<T>) {
const { column } = props;
const [isRowSelected, onRowSelectionChange] = useRowSelection();

return (
<Checkbox
id={`checkbox-${column.name}_header`} // TODO: need way to get row id
label="Select all"
hideLabel
checked={isRowSelected}
onChange={(checked) => onRowSelectionChange({ type: 'HEADER', checked })}
// WAITING ON: https://github.com/adazzle/react-data-grid/issues/3058
// indeterminate={props.row.getIsSomeSelected()}
/>
);
}

export function SelectHeaderGroupRenderer<T>(props: RenderGroupCellProps<T>) {
const { column, groupKey, row, childRows } = props;
const [isRowSelected, onRowSelectionChange] = useRowSelection();
const { isRowSelectionDisabled, isRowSelected, onRowSelectionChange } = useRowSelection();

return (
<DataTableSelectedContext.Consumer>
Expand All @@ -88,8 +68,9 @@ export function SelectHeaderGroupRenderer<T>(props: RenderGroupCellProps<T>) {
label="Select all"
hideLabel
checked={isRowSelected}
disabled={isRowSelectionDisabled}
indeterminate={selectedRowIds.size > 0 && childRows.some((childRow) => selectedRowIds.has((getRowKey || getRowId)(childRow)))}
onChange={(checked) => onRowSelectionChange({ type: 'ROW', row: row, checked, isShiftClick: false })}
onChange={(checked) => onRowSelectionChange({ row: row, checked, isShiftClick: false })}
/>
)}
</DataTableSelectedContext.Consumer>
Expand Down Expand Up @@ -511,15 +492,16 @@ export function GenericRenderer(RenderCellProps: RenderCellProps<RowWithKey>) {

export function SelectFormatter<T>(props: RenderCellProps<T>) {
const { column, row } = props;
const [isRowSelected, onRowSelectionChange] = useRowSelection();
const { isRowSelectionDisabled, isRowSelected, onRowSelectionChange } = useRowSelection();

return (
<Checkbox
id={`checkbox-${column.name}-${getRowId(row)}`} // TODO: need way to get row id
label="Select row"
hideLabel
checked={isRowSelected}
onChange={(checked) => onRowSelectionChange({ type: 'ROW', row, checked, isShiftClick: false })}
disabled={isRowSelectionDisabled}
onChange={(checked) => onRowSelectionChange({ row, checked, isShiftClick: false })}
/>
);
}
Expand Down
3 changes: 1 addition & 2 deletions libs/ui/src/lib/data-table/DataTableSubqueryRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { queryMore } from '@jetstream/shared/data';
import { copyRecordsToClipboard, formatNumber } from '@jetstream/shared/ui-utils';
import { flattenRecord } from '@jetstream/shared/utils';
import { Maybe, QueryResult, SalesforceOrgUi } from '@jetstream/types';
import { ContextMenuItem, Maybe, QueryResult, SalesforceOrgUi } from '@jetstream/types';
import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { RenderCellProps } from 'react-data-grid';
import RecordDownloadModal from '../file-download-modal/RecordDownloadModal';
import Grid from '../grid/Grid';
import AutoFullHeightContainer from '../layout/AutoFullHeightContainer';
import Modal from '../modal/Modal';
import { ContextMenuItem } from '../popover/ContextMenu';
import Icon from '../widgets/Icon';
import Spinner from '../widgets/Spinner';
import { DataTable } from './DataTable';
Expand Down
Loading

0 comments on commit d75f84b

Please sign in to comment.