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

[DataGrid] Add fluid columns width support #480

Merged
merged 25 commits into from
Nov 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b2392dd
[DataGrid] Add fluid columns width support
DanailH Oct 21, 2020
e54cb9a
Fix doc formatting
DanailH Oct 21, 2020
c77a475
Fix documentation so that it is complient to the MUI documentation st…
DanailH Oct 22, 2020
756932d
Add unit tests
DanailH Oct 26, 2020
2d23e7d
Update packages/grid/_modules_/grid/hooks/root/useColumns.ts
DanailH Oct 27, 2020
6cdced3
Update packages/grid/_modules_/grid/models/colDef/colDef.ts
DanailH Oct 27, 2020
b6cf786
Merge branch 'feature/DataGrid-347-columns-fluid-width' of github.com…
DanailH Oct 27, 2020
12fa1fa
Fix PR comments
DanailH Oct 27, 2020
3b1d8c3
Rebase from upstream master
DanailH Oct 28, 2020
9b042c6
Fix unit tests
DanailH Oct 28, 2020
e92df89
Resolve TS issues
DanailH Oct 28, 2020
ff5e963
Add // @ts-expect-error need to migrate helpers to TypeScript before …
DanailH Oct 28, 2020
5805baa
Add storybook examples
DanailH Oct 30, 2020
6e5401c
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Nov 2, 2020
bf768d3
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Nov 2, 2020
94404aa
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Nov 2, 2020
4c6a988
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Nov 2, 2020
04f336a
Update docs/src/pages/components/data-grid/columns/columns.md
DanailH Nov 2, 2020
b358751
Update packages/grid/data-grid/src/DataGrid.test.tsx
DanailH Nov 2, 2020
2219564
Fix storybook flex col width examples
DanailH Nov 2, 2020
401b7ed
rerun ci
oliviertassinari Nov 2, 2020
2bd46a0
Fix formatting
DanailH Nov 2, 2020
28baf68
Merge branch 'feature/DataGrid-347-columns-fluid-width' of github.com…
DanailH Nov 2, 2020
9672852
Update docs/src/pages/components/data-grid/columns/columns.md
mbrookes Nov 2, 2020
d300dfe
Trigger CI
DanailH Nov 3, 2020
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';
import { DataGrid } from '@material-ui/data-grid';

const rows = [
{
id: 1,
username: 'defunkt',
age: 38,
},
];

export default function ColumnFluidWidthGrid() {
return (
<div style={{ height: 250, width: '100%' }}>
<DataGrid
columns={[
{
field: 'id',
flex: 1,
},
{
field: 'username',
width: 200,
},
{
field: 'age',
flex: 0.3,
},
]}
rows={rows}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';
import { DataGrid } from '@material-ui/data-grid';

const rows = [
{
id: 1,
username: 'defunkt',
age: 38,
},
];

export default function ColumnFluidWidthGrid() {
return (
<div style={{ height: 250, width: '100%' }}>
<DataGrid
columns={[
{
field: 'id',
flex: 1,
},
{
field: 'username',
width: 200,
},
{
field: 'age',
flex: 0.3,
},
]}
rows={rows}
/>
</div>
);
}
16 changes: 16 additions & 0 deletions docs/src/pages/components/data-grid/columns/columns.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ To change the width of a column, use the `width` property available in `ColDef`.

{{"demo": "pages/components/data-grid/columns/ColumnWidthGrid.js", "bg": "inline"}}

### Fluid width

Each column has a fixed width of 100 pixels by default, but column fluidity (responsiveness) can be by achieved by setting the `flex` property in `ColDef`.

The `flex` property accepts a value between 0 and ∞.

The `flex` property works by dividing the remaining space in the grid among all flex columns in proportion to their `flex` value.
For example, consider a grid with a total width of 500px that has three columns: the first with `width: 200`; the second with `flex: 1`; and third with `flex: 0.5`.
The first column will be 200px wide, leaving 300px remaining. The column with `flex: 1` is twice the size of `flex: 0.5`, which means that final sizes will be: 200px, 200px, 100px.

Note that `flex` doesn't work together with `width`. If you set both `flex` and `width` in `ColDef`, `flex` will override `width`.

In addition, `flex` does not work if the combined width of the columns that have `width` is more than the width of the grid itself. If that is the case a scroll bar will be visible, and the columns that have `flex` will default back to their base value of 100px.

{{"demo": "pages/components/data-grid/columns/ColumnFluidWidthGrid.js", "bg": "inline"}}

## Column resizing [<span role="img" title="Enterprise">⚡️</span>](https://material-ui.com/store/items/material-ui-x/)

By default, `XGrid` allows all columns to be resized by dragging the right portion of the column separator.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ export const useStyles = makeStyles(
cursor: 'col-resize',
'&:hover, &.Mui-resizing': {
color: theme.palette.text.primary,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
color: borderColor,
},
},
},
'& .MuiDataGrid-iconSeparator': {
Expand Down
42 changes: 39 additions & 3 deletions packages/grid/_modules_/grid/hooks/features/columns/useColumns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,47 @@ import { useApiMethod } from '../../root/useApiMethod';
import { Logger, useLogger } from '../../utils/useLogger';
import { useGridState } from '../core/useGridState';

function mapColumns(
Copy link
Member

Choose a reason for hiding this comment

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

Should this be selector?

Copy link
Member Author

Choose a reason for hiding this comment

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

I would keep it as it is. This function is only used in the hydrateColumns one and initially, that functionality was part of the hydrateColumns but I separated it from it because the hydrateColumns was becoming a bit too bloated.

columns: Columns,
columnTypes: ColumnTypesRecord,
containerWidth: number,
): Columns {
let extendedColumns = columns.map((c) => ({ ...getColDef(columnTypes, c.type), ...c }));
const numberOfFluidColumns = columns.filter((column) => !!column.flex).length;
let flexDivider = 0;

if (numberOfFluidColumns && containerWidth) {
extendedColumns.forEach((column) => {
if (!column.flex) {
containerWidth -= column.width!;
} else {
flexDivider += column.flex;
}
});
}

if (containerWidth > 0 && numberOfFluidColumns) {
const flexMultiplier = containerWidth / flexDivider;
extendedColumns = extendedColumns.map((column) => {
return {
...column,
width: column.flex! ? Math.floor(flexMultiplier * column.flex!) : column.width,
};
});
}

return extendedColumns;
}

function hydrateColumns(
columns: Columns,
columnTypes: ColumnTypesRecord,
containerWidth: number,
withCheckboxSelection: boolean,
logger: Logger,
): Columns {
logger.debug('Hydrating Columns with default definitions');
let mappedCols = columns.map((c) => ({ ...getColDef(columnTypes, c.type), ...c }));
let mappedCols = mapColumns(columns, columnTypes, containerWidth);
if (withCheckboxSelection) {
mappedCols = [checkboxSelectionColDef, ...mappedCols];
}
Expand Down Expand Up @@ -58,14 +91,15 @@ function toMeta(logger: Logger, visibleColumns: Columns): ColumnsMeta {
const resetState = (
columns: Columns,
columnTypes: ColumnTypesRecord,
containerWidth: number,
withCheckboxSelection: boolean,
logger: Logger,
): InternalColumns => {
if (columns.length === 0) {
return getInitialColumnsState();
}

const all = hydrateColumns(columns, columnTypes, withCheckboxSelection, logger);
const all = hydrateColumns(columns, columnTypes, containerWidth, withCheckboxSelection, logger);
const visible = filterVisible(logger, all);
const meta = toMeta(logger, visible);
const lookup = toLookup(logger, all);
Expand Down Expand Up @@ -115,7 +149,7 @@ const getUpdatedColumnState = (
export function useColumns(columns: Columns, apiRef: ApiRef): InternalColumns {
const logger = useLogger('useColumns');
const [gridState, setGridState, forceUpdate] = useGridState(apiRef);

const viewportWidth = gridState.containerSizes ? gridState.containerSizes.viewportSize.width : 0;
Copy link
Member

@oliviertassinari oliviertassinari Nov 5, 2020

Choose a reason for hiding this comment

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

I think that it would be better to keep the same name for variables, const containerWidth would match its usage.

const updateState = React.useCallback(
(newState: InternalColumns, emit = true) => {
logger.debug('Updating columns state.');
Expand All @@ -134,6 +168,7 @@ export function useColumns(columns: Columns, apiRef: ApiRef): InternalColumns {
const newState = resetState(
columns,
gridState.options.columnTypes,
viewportWidth,
!!gridState.options.checkboxSelection,
logger,
);
Expand All @@ -142,6 +177,7 @@ export function useColumns(columns: Columns, apiRef: ApiRef): InternalColumns {
columns,
gridState.options.columnTypes,
gridState.options.checkboxSelection,
viewportWidth,
logger,
updateState,
]);
Expand Down
4 changes: 4 additions & 0 deletions packages/grid/_modules_/grid/models/colDef/colDef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export interface ColDef {
* @default 100
*/
width?: number;
/**
* If set, it indicates that a column has fluid width. Range [0, ∞].
*/
flex?: number;
/**
* If `true`, hide the column.
* @default false;
Expand Down
118 changes: 118 additions & 0 deletions packages/grid/data-grid/src/DataGrid.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,122 @@ describe('<DataGrid />', () => {
);
});
});

describe('column width', () => {
before(function beforeHook() {
if (/jsdom/.test(window.navigator.userAgent)) {
// Need layouting
this.skip();
}
});

it('should set the columns width to 100px by default', () => {
const rows = [
{
id: 1,
username: 'John Doe',
age: 30,
},
];

const columns = [
{
field: 'id',
},
{
field: 'name',
},
{
field: 'age',
},
];

const { getAllByRole } = render(
<div style={{ width: 300, height: 300 }}>
<DataGrid columns={columns} rows={rows} />
</div>,
);

const DOMColumns = getAllByRole('columnheader');
DOMColumns.forEach((col) => {
// @ts-expect-error need to migrate helpers to TypeScript
expect(col).toHaveInlineStyle({ width: '100px' });
});
});

it('should set the columns width value to what is provided', () => {
const rows = [
{
id: 1,
username: 'John Doe',
age: 30,
},
];

const colWidthValues = [50, 50, 200];
const columns = [
{
field: 'id',
width: colWidthValues[0],
},
{
field: 'name',
width: colWidthValues[1],
},
{
field: 'age',
width: colWidthValues[2],
},
];

const { getAllByRole } = render(
<div style={{ width: 300, height: 300 }}>
<DataGrid columns={columns} rows={rows} />
</div>,
);

const DOMColumns = getAllByRole('columnheader');
DOMColumns.forEach((col, index) => {
// @ts-expect-error need to migrate helpers to TypeScript
expect(col).toHaveInlineStyle({ width: `${colWidthValues[index]}px` });
});
});

it('should set the first column to be twice as wide as the second one', () => {
const rows = [
{
id: 1,
username: 'John Doe',
age: 30,
},
];

const columns = [
{
field: 'id',
flex: 1,
},
{
field: 'name',
flex: 0.5,
},
];

render(
<div style={{ width: 200, height: 300 }}>
<DataGrid columns={columns} rows={rows} />
</div>,
);

const firstColumn = document.querySelector('[role="columnheader"][aria-colindex="1"]');
const secondColumn: HTMLElement | null = document.querySelector(
'[role="columnheader"][aria-colindex="2"]',
);
const secondColumnWidthVal = secondColumn!.style.width.split('px')[0];
// @ts-expect-error need to migrate helpers to TypeScript
expect(firstColumn).toHaveInlineStyle({
width: `${2 * parseInt(secondColumnWidthVal, 10)}px`,
});
});
});
});
51 changes: 51 additions & 0 deletions packages/storybook/src/stories/grid-columns.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,54 @@ export function NewColumnTypes() {
</div>
);
}

export const FewFlexColumns = () => {
const data = useData(20, 3);
const transformColSizes = React.useCallback(
(columns: ColDef[]) =>
columns.map((col, index) =>
index % 2 === 0 ? { ...col, flex: index + 1 } : { ...col, width: 200 },
),
[],
);

return (
<div className="grid-container">
<XGrid rows={data.rows} columns={transformColSizes(data.columns)} />
</div>
);
};

export const SeveralFlexColumn = () => {
const data = useData(20, 7);
const transformColSizes = React.useCallback(
(columns: ColDef[]) =>
columns.map((col, index) =>
index % 3 !== 0 ? { ...col, flex: index } : { ...col, flex: 1 },
),
[],
);

return (
<div className="grid-container">
<XGrid rows={data.rows} columns={transformColSizes(data.columns)} />
</div>
);
};

export const FlexColumnWidth2000 = () => {
const data = useData(20, 3);
const transformColSizes = React.useCallback(
(columns: ColDef[]) =>
columns.map((col, index) =>
index % 2 !== 0 ? { ...col, width: 2000 } : { ...col, flex: index + 1 },
),
[],
);

return (
<div className="grid-container">
<XGrid rows={data.rows} columns={transformColSizes(data.columns)} />
</div>
);
};