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

Fix issues around custom datagrid columns #1840

Merged
merged 3 commits into from
Mar 30, 2023
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
3 changes: 3 additions & 0 deletions examples/custom-datagrid-column/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Custom datagrid column example

Example showcasing Adding a custom component as a datagrid column. [docs](https://mui.com/toolpad/building-ui/data-grid-component/#configuring-columns)
13 changes: 13 additions & 0 deletions examples/custom-datagrid-column/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "custom-datagrid-column",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "toolpad dev",
"build": "toolpad build",
"start": "toolpad start"
},
"dependencies": {
"@mui/toolpad": "latest"
}
}
91 changes: 91 additions & 0 deletions examples/custom-datagrid-column/toolpad.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
nodes:
yp04du0:
name: Application
attributes: {}
id: yp04du0
type: app
parentId: null
parentProp: null
parentIndex: null
yq14d6o:
name: example
attributes:
title:
type: const
value: Page 1
id: yq14d6o
type: page
parentId: yp04du0
parentProp: pages
parentIndex: a0
0h23qy9:
name: pageRow
props: {}
attributes:
component:
type: const
value: PageRow
layout: {}
id: 0h23qy9
type: element
parentId: yq14d6o
parentProp: children
parentIndex: a0
u903qie:
name: dataGrid
props:
rows:
type: const
value:
- firstname: Salman
lastname: Anthony
age: 17
- firstname: Crystal
lastname: Wise
age: 32
- firstname: Yasmin
lastname: Cherry
age: 56
- firstname: Maisha
lastname: Browning
age: 8
- firstname: Ronald
lastname: Santana
age: 11
- firstname: Bronwyn
lastname: Odling
age: 3
- firstname: Bushra
lastname: Chen
age: 43
- firstname: Savanna
lastname: Hardy
age: 45
- firstname: John
lastname: Sutherland
age: 19
- firstname: Romeo
lastname: Kerr
age: 21
columns:
type: const
value:
- field: fullname
type: codeComponent
codeComponent: FullNameColumn
width: 212
- field: age
type: codeComponent
codeComponent: AgeColumn
attributes:
component:
type: const
value: DataGrid
layout: {}
id: u903qie
type: element
parentId: 0h23qy9
parentProp: children
parentIndex: a0
root: yp04du0
version: 6
21 changes: 21 additions & 0 deletions examples/custom-datagrid-column/toolpad/components/AgeColumn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';
import { Typography } from '@mui/material';
import { createComponent } from '@mui/toolpad/browser';

export interface AgeColumnProps {
value: number;
}

function AgeColumn({ value }: AgeColumnProps) {
return (
<Typography sx={{ color: value >= 18 ? 'success.main' : 'error.main' }}>{value}</Typography>
);
}

export default createComponent(AgeColumn, {
argTypes: {
value: {
typeDef: { type: 'number' },
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { Typography } from '@mui/material';
import { createComponent } from '@mui/toolpad/browser';

export interface FullNameColumnProps {
row: {
age: number;
firstname: string;
lastname: string;
};
}

function FullNameColumn({ row }: FullNameColumnProps) {
return <Typography noWrap>{`${row.firstname} ${row.lastname}`}</Typography>;
}

export default createComponent(FullNameColumn, {
argTypes: {
row: {
typeDef: { type: 'object' },
},
},
});
80 changes: 44 additions & 36 deletions packages/toolpad-app/src/toolpad/propertyControls/GridColumns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,10 @@ function GridColumnsPropEditor({
const [editedIndex, setEditedIndex] = React.useState<number | null>(null);
const { dom } = useDom();
const toolpadComponents = useToolpadComponents(dom);
const codeComponents = React.useMemo(() => {
const entries = Object.entries(toolpadComponents);

return entries
.map(([, definition]) => definition)
.filter((definition) => definition && !definition.builtIn) as ToolpadComponentDefinition[];
const codeComponents: ToolpadComponentDefinition[] = React.useMemo(() => {
return Object.values(toolpadComponents)
.filter(Boolean)
.filter((definition) => !definition.builtIn);
}, [toolpadComponents]);

const editedColumn = typeof editedIndex === 'number' ? value[editedIndex] : null;
Expand Down Expand Up @@ -201,6 +199,7 @@ function GridColumnsPropEditor({
handleColumnChange({ ...editedColumn, field: event.target.value })
}
/>

<TextField
label="header"
value={editedColumn.headerName}
Expand All @@ -209,6 +208,37 @@ function GridColumnsPropEditor({
handleColumnChange({ ...editedColumn, headerName: event.target.value })
}
/>

<TextField
select
fullWidth
label="align"
value={editedColumn.align ?? ''}
disabled={disabled}
onChange={(event) =>
handleColumnChange({
...editedColumn,
align: (event.target.value as GridAlignment) || undefined,
})
}
>
{ALIGNMENTS.map((alignment) => (
<MenuItem key={alignment} value={alignment}>
{alignment}
</MenuItem>
))}
</TextField>

<TextField
label="width"
type="number"
value={editedColumn.width}
disabled={disabled}
onChange={(event) =>
handleColumnChange({ ...editedColumn, width: Number(event.target.value) })
}
/>

<TextField
select
fullWidth
Expand All @@ -229,6 +259,7 @@ function GridColumnsPropEditor({
</MenuItem>
))}
</TextField>

{editedColumn.type === 'number' ? (
<React.Fragment>
<TextField
Expand Down Expand Up @@ -295,50 +326,27 @@ function GridColumnsPropEditor({
</React.Fragment>
) : null}

<TextField
select
fullWidth
label="align"
value={editedColumn.align ?? ''}
disabled={disabled}
onChange={(event) =>
handleColumnChange({
...editedColumn,
align: (event.target.value as GridAlignment) || undefined,
})
}
>
{ALIGNMENTS.map((alignment) => (
<MenuItem key={alignment} value={alignment}>
{alignment}
</MenuItem>
))}
</TextField>
<TextField
label="width"
type="number"
value={editedColumn.width}
disabled={disabled}
onChange={(event) =>
handleColumnChange({ ...editedColumn, width: Number(event.target.value) })
}
/>
{editedColumn.type === 'codeComponent' ? (
<TextField
select
required
fullWidth
label="Custom component"
value={editedColumn.codeComponent ?? ''}
disabled={disabled}
error={!editedColumn.codeComponent}
helperText={
editedColumn.codeComponent ? undefined : 'Please select a component'
}
onChange={(event) =>
handleColumnChange({
...editedColumn,
codeComponent: event.target.value,
})
}
>
{codeComponents.map(({ displayName, codeComponentId }) => (
<MenuItem key={displayName} value={codeComponentId}>
{codeComponents.map(({ displayName }) => (
<MenuItem key={displayName} value={displayName}>
{displayName}
</MenuItem>
))}
Expand Down
18 changes: 18 additions & 0 deletions test/integration/data-grid/custom.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from 'path';
import { ToolpadEditor } from '../../models/ToolpadEditor';
import { test, expect } from '../../playwright/localTest';
import clickCenter from '../../utils/clickCenter';

test.use({
localAppConfig: {
Expand All @@ -23,3 +24,20 @@ test('Code component cell', async ({ page }) => {
).toBeVisible();
await expect(editorModel.pageRoot.getByText('field: "customField"')).toBeVisible();
});

test('Code component column selector', async ({ page }) => {
const editorModel = new ToolpadEditor(page);
editorModel.goto();

await editorModel.waitForOverlay();

const marker = editorModel.appCanvas.getByText('It worked!');

await expect(marker).not.toBeVisible();
await clickCenter(page, editorModel.appCanvas.locator('.MuiDataGrid-root'));
await editorModel.componentEditor.getByRole('button', { name: 'columns' }).click();
await page.getByRole('button', { name: 'customField' }).click();
await page.getByRole('button', { name: 'Custom component​' }).click();
await page.getByRole('option', { name: 'Other' }).click();
await expect(marker).toBeVisible();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createComponent } from '@mui/toolpad/browser';
import * as React from 'react';

export interface Props {
Copy link
Member

Choose a reason for hiding this comment

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

I guess these Props and argTypes still need to be defined here even though they're not used? Just making sure.

Copy link
Member Author

@Janpot Janpot Mar 30, 2023

Choose a reason for hiding this comment

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

Not really necessary, I just copied the docs. We should probably improve it with #1755

value: any;
field: any;
row: any;
}

function Test() {
return <div>It worked!</div>;
}

export default createComponent(Test, {
argTypes: {
value: {
typeDef: { type: 'object' },
},
row: {
typeDef: { type: 'object' },
},
field: {
typeDef: { type: 'string' },
defaultValue: 'Field name',
},
},
});