Skip to content
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
7 changes: 6 additions & 1 deletion dev/pages/GridPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ const users: Item[] = [...Array(1000)].map((_, i) => {
export default function () {
return (
<GridPro singleCellEdit items={users}>
<GridProEditColumn path="firstName" />
<GridProEditColumn
path="firstName"
header={<b style={{ whiteSpace: 'nowrap' }}>First name (auto width)</b>}
autoWidth
flexGrow={0}
/>
<GridProEditColumn
path="lastName"
editModeRenderer={() => <TextField />}
Expand Down
9 changes: 7 additions & 2 deletions packages/react-components-pro/src/GridPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { type ForwardedRef, forwardRef, type ReactElement, type RefAttributes }
import type { GridDefaultItem, GridProps } from '@vaadin/react-components/Grid.js';
import { GridPro as _GridPro, type GridProElement, type GridProProps as _GridProProps } from './generated/GridPro.js';
import { useModelRenderer } from '@vaadin/react-components/renderers/useModelRenderer.js';
import useDeferredColumnWidthRecalculation from '@vaadin/react-components/utils/useDeferredColumnWidthRecalculation.js';

export * from './generated/GridPro.js';

Expand All @@ -24,10 +25,14 @@ function GridPro<TItem = GridDefaultItem>(
props: GridProProps<TItem>,
ref: ForwardedRef<GridProElement<TItem>>,
): ReactElement | null {
const [portals, rowDetailsRenderer] = useModelRenderer(props.rowDetailsRenderer);
const [portals, rowDetailsRenderer] = useModelRenderer(props.rowDetailsRenderer, {
renderMode: 'microtask',
});

const finalRef = useDeferredColumnWidthRecalculation(ref);

return (
<_GridPro<TItem> {...props} ref={ref} rowDetailsRenderer={rowDetailsRenderer}>
<_GridPro<TItem> {...props} ref={finalRef} rowDetailsRenderer={rowDetailsRenderer}>
{props.children}
{portals}
</_GridPro>
Expand Down
4 changes: 4 additions & 0 deletions packages/react-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,10 @@
"./utils/mapItemsWithComponents.d.ts.map": "./utils/mapItemsWithComponents.d.ts.map",
"./utils/mapItemsWithComponents.js": "./utils/mapItemsWithComponents.js",
"./utils/mapItemsWithComponents.js.map": "./utils/mapItemsWithComponents.js.map",
"./utils/useDeferredColumnWidthRecalculation.d.ts": "./utils/useDeferredColumnWidthRecalculation.d.ts",
"./utils/useDeferredColumnWidthRecalculation.d.ts.map": "./utils/useDeferredColumnWidthRecalculation.d.ts.map",
"./utils/useDeferredColumnWidthRecalculation.js": "./utils/useDeferredColumnWidthRecalculation.js",
"./utils/useDeferredColumnWidthRecalculation.js.map": "./utils/useDeferredColumnWidthRecalculation.js.map",
"./utils/useMergedRefs.d.ts": "./utils/useMergedRefs.d.ts",
"./utils/useMergedRefs.d.ts.map": "./utils/useMergedRefs.d.ts.map",
"./utils/useMergedRefs.js": "./utils/useMergedRefs.js",
Expand Down
24 changes: 3 additions & 21 deletions packages/react-components/src/Grid.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
import {
type ComponentType,
type ForwardedRef,
forwardRef,
type ReactElement,
type RefAttributes,
useLayoutEffect,
useRef,
} from 'react';
import { type ComponentType, type ForwardedRef, forwardRef, type ReactElement, type RefAttributes } from 'react';
import {
Grid as _Grid,
type GridDefaultItem,
Expand All @@ -15,7 +7,7 @@ import {
} from './generated/Grid.js';
import type { GridRowDetailsReactRendererProps } from './renderers/grid.js';
import { useModelRenderer } from './renderers/useModelRenderer.js';
import useMergedRefs from './utils/useMergedRefs.js';
import useDeferredColumnWidthRecalculation from './utils/useDeferredColumnWidthRecalculation.js';

export * from './generated/Grid.js';

Expand All @@ -32,17 +24,7 @@ function Grid<TItem = GridDefaultItem>(
renderMode: 'microtask',
});

const innerRef = useRef<GridElement>(null);
const finalRef = useMergedRefs(innerRef, ref);

useLayoutEffect(() => {
innerRef.current!.recalculateColumnWidths = function (...args) {
// Wait for column content to finish rendering before recalculating widths.
queueMicrotask(() => {
Object.getPrototypeOf(this).recalculateColumnWidths.call(this, ...args);
});
};
}, []);
const finalRef = useDeferredColumnWidthRecalculation(ref);

return (
<_Grid<TItem> {...props} ref={finalRef} rowDetailsRenderer={rowDetailsRenderer}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { type ForwardedRef, type RefCallback, useCallback } from 'react';
import type { GridElement } from '../generated/Grid.js';
import useMergedRefs from './useMergedRefs.js';

/**
* Defers `recalculateColumnWidths()` into a microtask so that the React renderer
* portals have content before the column widths are measured.
* Returns the ref to pass to the grid element.
*/
export default function useDeferredColumnWidthRecalculation<T extends GridElement<any>>(
ref: ForwardedRef<T>,
): RefCallback<T> {
const patchRef = useCallback((element: T | null) => {
if (element) {
element.recalculateColumnWidths = function (...args) {
// Wait for column content to finish rendering before recalculating widths.
queueMicrotask(() => {
Object.getPrototypeOf(this).recalculateColumnWidths.call(this, ...args);
});
};
}
}, []);

return useMergedRefs(patchRef, ref);
}
41 changes: 40 additions & 1 deletion test/Grid.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { render } from 'vitest-browser-react';
import { Grid, type GridDataProvider } from '../packages/react-components/src/Grid.js';
import { GridColumn, GridColumnElement } from '../packages/react-components/src/GridColumn.js';
import { GridFilterColumn } from '../packages/react-components/src/GridFilterColumn.js';
import { GridProEditColumn } from '../packages/react-components-pro/src/GridProEditColumn.js';
import {
GridProEditColumn,
type GridProEditColumnElement,
} from '../packages/react-components-pro/src/GridProEditColumn.js';
import { GridSelectionColumn } from '../packages/react-components/src/GridSelectionColumn.js';
import { GridSortColumn } from '../packages/react-components/src/GridSortColumn.js';
import { GridTreeColumn } from '../packages/react-components/src/GridTreeColumn.js';
Expand Down Expand Up @@ -346,6 +349,42 @@ describe('Grid', () => {
});

describe('GridProEditColumn', () => {
it('should consider custom renderer content with column auto-width', async () => {
function GridProWithAutoWidthColumns() {
const [gridItems, setGridItems] = useState<Item[] | undefined>();

useEffect(() => {
setTimeout(() => setGridItems(items));
}, []);

return (
<GridPro<Item> items={gridItems}>
<GridProEditColumn header={<button style={{ width: '300px' }}>header</button>} autoWidth flexGrow={0} />
<GridProEditColumn autoWidth flexGrow={0}>
{({ item }) => <button style={{ width: '300px' }}>{item.name}</button>}
</GridProEditColumn>
<GridProEditColumn footer={<button style={{ width: '300px' }}>footer</button>} autoWidth flexGrow={0} />
</GridPro>
);
}

const error = sinon.stub(console, 'error');
try {
await render(<GridProWithAutoWidthColumns />);

const grid = await findByQuerySelector('vaadin-grid-pro');
const columns = Array.from(grid.querySelectorAll<GridProEditColumnElement>('vaadin-grid-pro-edit-column'));
expect(columns).to.have.length(3);

for (const column of columns) {
await until(() => parseFloat(String(column.width)) > 300);
}
} finally {
error.restore();
}
expect(error.called).to.be.false;
});

it('should render correctly', async () => {
await render(
<GridPro<Item> items={items}>
Expand Down
Loading