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 datagrid configurable columns management #10339

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import * as React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import expect from 'expect';
import cloneDeep from 'lodash/cloneDeep';

import {
Basic,
Omit,
PreferenceKey,
LabelElement,
NullChildren,
Wrapper,
data,
} from './DatagridConfigurable.stories';
import { DatagridConfigurable } from './DatagridConfigurable';
import { TextField } from '../../field';
import { EditButton } from '../../button';
import { memoryStore } from 'ra-core';

describe('<DatagridConfigurable>', () => {
it('should render a datagrid with configurable columns', async () => {
Expand Down Expand Up @@ -103,4 +110,256 @@ describe('<DatagridConfigurable>', () => {
expect(screen.queryAllByText('War and Peace')).toHaveLength(1);
});
});
describe('store/code synchronization', () => {
const storeDefaultValue = {
preferences: {
books1: {
datagrid: {
columns: ['0', '1', '2', '3', '4'],
availableColumns: [
{
index: '0',
source: 'id',
},
{
index: '1',
source: 'title',
label: 'Original title',
},
{
index: '2',
source: 'author',
},
{
index: '3',
source: 'year',
},
{
index: '4',
label: 'Unlabeled column #4',
},
],
},
},
},
};

it('should preserve hidden columns from the store and show new non omitted columns last', async () => {
const store = memoryStore(cloneDeep(storeDefaultValue));
store.setItem('preferences.books1.datagrid.columns', [
'1',
'0',
'3',
]);
const { rerender } = render(
<Wrapper store={store}>
<DatagridConfigurable
resource="books1"
data={data}
sort={{ field: 'title', order: 'ASC' }}
bulkActionButtons={false}
>
<TextField source="id" />
<TextField source="title" label="Original title" />
<TextField source="author" />
<EditButton />
</DatagridConfigurable>
</Wrapper>
);

await screen.findByText('War and Peace');
// author column is hidden
expect(screen.queryByText('Leo Tolstoy')).toBeNull();

// Render something else (to be able to tell when the next rerender is finished)
rerender(<Wrapper store={store}>Something Else</Wrapper>);
await screen.findByText('Something Else');

// Add 'year' column
rerender(
<Wrapper store={store}>
<DatagridConfigurable
resource="books1"
data={data}
sort={{ field: 'title', order: 'ASC' }}
bulkActionButtons={false}
>
<TextField source="id" />
<TextField source="year" />
<TextField source="title" label="Original title" />
<TextField source="author" />
<EditButton />
</DatagridConfigurable>
</Wrapper>
);
await screen.findByText('War and Peace');
// Year column should be displayed
await screen.findByText('1869');
// author column is still hidden
expect(screen.queryByText('Leo Tolstoy')).toBeNull();
// Store value should be updated
expect(
store.getItem('preferences.books1.datagrid.columns')
).toEqual(['2', '0', '1', '4']);
// Check the order is preserved
const columnsTexts = Array.from(
screen.getByText('War and Peace')?.closest('tr')
?.children as HTMLCollection
).map(child => child.textContent);
expect(columnsTexts).toEqual([
'War and Peace',
'1',
'1869',
'ra.action.edit',
]);
});

it('should preserve hidden columns from the store when column order is changed in the code', async () => {
const store = memoryStore(cloneDeep(storeDefaultValue));
// hide the 'year' column
store.setItem('preferences.books1.datagrid.columns', [
'0',
'1',
'2',
'4',
]);
const { rerender } = render(
<Wrapper store={store}>
<DatagridConfigurable
resource="books1"
data={data}
sort={{ field: 'title', order: 'ASC' }}
bulkActionButtons={false}
>
<TextField source="id" />
<TextField source="title" label="Original title" />
<TextField source="author" />
<TextField source="year" />
<EditButton />
</DatagridConfigurable>
</Wrapper>
);

await screen.findByText('Leo Tolstoy');
// Year column should be hidden
expect(screen.queryByText('1869')).toBeNull();
// Store value should be preserved
expect(
store.getItem('preferences.books1.datagrid.columns')
).toEqual(['0', '1', '2', '4']);

// Render something else (to be able to tell when the next rerender is finished)
rerender(<Wrapper store={store}>Something Else</Wrapper>);
await screen.findByText('Something Else');

// Invert 'year' and 'author' columns
rerender(
<Wrapper store={store}>
<DatagridConfigurable
resource="books1"
data={data}
sort={{ field: 'title', order: 'ASC' }}
bulkActionButtons={false}
>
<TextField source="id" />
<TextField source="title" label="Original title" />
<TextField source="year" />
<TextField source="author" />
<EditButton />
</DatagridConfigurable>
</Wrapper>
);
await screen.findByText('Leo Tolstoy');
// Year column should still be hidden
expect(screen.queryByText('1869')).toBeNull();
// Store value should be updated
expect(
store.getItem('preferences.books1.datagrid.columns')
).toEqual(['0', '1', '3', '4']);
});

it('should preserve hidden columns from the store when a column is renamed in the code', async () => {
const store = memoryStore(cloneDeep(storeDefaultValue));
// invert the 'year' and 'author' columns
store.setItem('preferences.books1.datagrid.columns', [
'0',
'1',
'3',
'2',
'4',
]);
store.setItem('preferences.books1.datagrid.availableColumns', [
{
index: '0',
source: 'id',
},
{
index: '1',
source: 'title',
label: 'Original title',
},
{
index: '3',
source: 'year',
},
{
index: '2',
source: 'author',
},
{
index: '4',
label: 'Unlabeled column #4',
},
]);
const { rerender } = render(
<Wrapper store={store}>
<DatagridConfigurable
resource="books1"
data={data}
sort={{ field: 'title', order: 'ASC' }}
bulkActionButtons={false}
>
<TextField source="id" />
<TextField source="title" label="Original title" />
<TextField source="author" />
<TextField source="year" />
<EditButton />
</DatagridConfigurable>
</Wrapper>
);

await screen.findByText('Leo Tolstoy');
// Store value should be preserved
expect(
store.getItem('preferences.books1.datagrid.columns')
).toEqual(['0', '1', '3', '2', '4']);

// Render something else (to be able to tell when the next rerender is finished)
rerender(<Wrapper store={store}>Something Else</Wrapper>);
await screen.findByText('Something Else');

// Rename the 'title' column
rerender(
<Wrapper store={store}>
<DatagridConfigurable
resource="books1"
data={data}
sort={{ field: 'title', order: 'ASC' }}
bulkActionButtons={false}
>
<TextField source="id" />
<TextField source="title" label="New title" />
<TextField source="author" />
<TextField source="year" />
<EditButton />
</DatagridConfigurable>
</Wrapper>
);
await screen.findByText('Leo Tolstoy');
// Store value should be preserved
expect(
store.getItem('preferences.books1.datagrid.columns')
).toEqual(['0', '1', '3', '2', '4']);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
StoreContextProvider,
PreferencesEditorContextProvider,
TestMemoryRouter,
localStorageStore,
} from 'ra-core';

import { DatagridConfigurable } from './DatagridConfigurable';
Expand All @@ -14,9 +15,12 @@ import { EditButton } from '../../button';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

export default { title: 'ra-ui-materialui/list/DatagridConfigurable' };
export default {
title: 'ra-ui-materialui/list/DatagridConfigurable',
excludeStories: ['data', 'Wrapper'],
};

const data = [
export const data = [
{
id: 1,
title: 'War and Peace',
Expand Down Expand Up @@ -47,8 +51,12 @@ const AuthorField = () => <TextField label="Author" source="author" />;

const theme = createTheme();

const Wrapper = ({ children, queryClient = new QueryClient() }) => (
<StoreContextProvider value={memoryStore()}>
export const Wrapper = ({
children,
queryClient = new QueryClient(),
store = memoryStore(),
}) => (
<StoreContextProvider value={store}>
<ThemeProvider theme={theme}>
<PreferencesEditorContextProvider>
<QueryClientProvider client={queryClient}>
Expand Down Expand Up @@ -163,3 +171,20 @@ export const NullChildren = () => (
</DatagridConfigurable>
</Wrapper>
);

export const LocalStorage = () => (
<Wrapper store={localStorageStore()}>
<DatagridConfigurable
resource="books1"
data={data}
sort={{ field: 'title', order: 'ASC' }}
bulkActionButtons={false}
>
<TextField source="id" />
<TextField source="title" label="Original title" />
<TextField source="author" />
<TextField source="year" />
<EditButton />
</DatagridConfigurable>
</Wrapper>
);
Loading
Loading