mirror of
https://github.com/apache/superset.git
synced 2026-05-12 19:35:17 +00:00
fix(table chart): fix rerender bug that continuously cleared search box (#39707)
(cherry picked from commit 3395620b6e)
This commit is contained in:
committed by
Michael S. Molina
parent
61bcf578a6
commit
87e5450cbe
@@ -147,7 +147,25 @@ export default typedMemo(function DataTable<D extends object>({
|
||||
hooks || [],
|
||||
].flat();
|
||||
|
||||
const columnNames = Object.keys(data?.[0] || {});
|
||||
const columnNames = columns.map((column, index) => {
|
||||
const normalizedColumn = column as typeof column & {
|
||||
accessor?: string | ((row: D) => unknown);
|
||||
columnKey?: string;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
const accessorName =
|
||||
typeof normalizedColumn.accessor === 'string'
|
||||
? normalizedColumn.accessor
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
normalizedColumn.columnKey ??
|
||||
normalizedColumn.id ??
|
||||
accessorName ??
|
||||
String(index)
|
||||
);
|
||||
});
|
||||
const previousColumnNames = usePrevious(columnNames);
|
||||
const resultsSize = serverPagination ? rowCount : data.length;
|
||||
const sortByRef = useRef([]); // cache initial `sortby` so sorting doesn't trigger page reset
|
||||
@@ -237,6 +255,7 @@ export default typedMemo(function DataTable<D extends object>({
|
||||
getTableSize: defaultGetTableSize,
|
||||
globalFilter: defaultGlobalFilter,
|
||||
sortTypes,
|
||||
autoResetGlobalFilter: !isEqual(columnNames, previousColumnNames),
|
||||
autoResetSortBy: !isEqual(columnNames, previousColumnNames),
|
||||
manualSortBy: !!serverPagination,
|
||||
...moreUseTableOptions,
|
||||
|
||||
@@ -36,6 +36,8 @@ import {
|
||||
SMART_DATE_ID,
|
||||
getTimeFormatterForGranularity,
|
||||
} from '@superset-ui/core';
|
||||
import { CellProps, Column, HeaderProps } from 'react-table';
|
||||
import DataTable from '../src/DataTable/DataTable';
|
||||
import TableChart, { sanitizeHeaderId } from '../src/TableChart';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import transformProps from '../src/transformProps';
|
||||
@@ -1980,6 +1982,222 @@ describe('plugin-chart-table', () => {
|
||||
expect(totalCellAfter).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('preserves client-side search text across temporal table rerenders', async () => {
|
||||
const formDataWithSearch = {
|
||||
...testData.basic.formData,
|
||||
include_search: true,
|
||||
server_pagination: false,
|
||||
};
|
||||
|
||||
const renderChart = () => {
|
||||
const props = transformProps({
|
||||
...testData.basic,
|
||||
formData: formDataWithSearch,
|
||||
});
|
||||
props.includeSearch = true;
|
||||
|
||||
return (
|
||||
<ProviderWrapper>
|
||||
<TableChart {...props} sticky={false} />
|
||||
</ProviderWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const { rerender } = render(renderChart());
|
||||
|
||||
const searchInput = screen.getByRole('textbox');
|
||||
fireEvent.change(searchInput, { target: { value: 'Michael' } });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(searchInput).toHaveValue('Michael');
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
rerender(renderChart());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('textbox')).toHaveValue('Michael');
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('preserves client-side search text when rerendered with empty data', async () => {
|
||||
const formDataWithSearch = {
|
||||
...testData.basic.formData,
|
||||
include_search: true,
|
||||
server_pagination: false,
|
||||
};
|
||||
|
||||
const renderChart = (data = testData.basic.queriesData[0].data) => {
|
||||
const props = transformProps({
|
||||
...testData.basic,
|
||||
formData: formDataWithSearch,
|
||||
queriesData: [
|
||||
{
|
||||
...testData.basic.queriesData[0],
|
||||
data,
|
||||
},
|
||||
],
|
||||
});
|
||||
props.includeSearch = true;
|
||||
|
||||
return (
|
||||
<ProviderWrapper>
|
||||
<TableChart {...props} sticky={false} />
|
||||
</ProviderWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const { rerender } = render(renderChart());
|
||||
|
||||
const searchInput = screen.getByRole('textbox');
|
||||
fireEvent.change(searchInput, { target: { value: 'Michael' } });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(searchInput).toHaveValue('Michael');
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
rerender(renderChart([]));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('textbox')).toHaveValue('Michael');
|
||||
expect(screen.getByLabelText('Search 0 records')).toHaveValue(
|
||||
'Michael',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('preserves client-side search text for function accessor columns', async () => {
|
||||
type DataRow = {
|
||||
city: string;
|
||||
firstName: string;
|
||||
};
|
||||
|
||||
const makeColumns = (): Column<DataRow>[] => [
|
||||
{
|
||||
Header: ({ column }: HeaderProps<DataRow>) => (
|
||||
<th data-column-name={column.id}>First name</th>
|
||||
),
|
||||
Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
|
||||
id: 'firstName',
|
||||
accessor: ((row: DataRow) => row.firstName) as never,
|
||||
},
|
||||
{
|
||||
Header: ({ column }: HeaderProps<DataRow>) => (
|
||||
<th data-column-name={column.id}>City</th>
|
||||
),
|
||||
Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
|
||||
id: 'city',
|
||||
accessor: ((row: DataRow) => row.city) as never,
|
||||
},
|
||||
];
|
||||
|
||||
const data: DataRow[] = [
|
||||
{ firstName: 'Michael', city: 'Paris' },
|
||||
{ firstName: 'Jordan', city: 'London' },
|
||||
];
|
||||
|
||||
const renderDataTable = () => (
|
||||
<ProviderWrapper>
|
||||
<DataTable<DataRow>
|
||||
columns={makeColumns()}
|
||||
data={data}
|
||||
rowCount={data.length}
|
||||
serverPagination={false}
|
||||
serverPaginationData={{}}
|
||||
onServerPaginationChange={jest.fn()}
|
||||
handleSortByChange={jest.fn()}
|
||||
sortByFromParent={[]}
|
||||
onSearchColChange={jest.fn()}
|
||||
searchOptions={[]}
|
||||
sticky={false}
|
||||
/>
|
||||
</ProviderWrapper>
|
||||
);
|
||||
|
||||
const { rerender } = render(renderDataTable());
|
||||
|
||||
const searchInput = screen.getByRole('textbox');
|
||||
fireEvent.change(searchInput, { target: { value: 'Michael' } });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(searchInput).toHaveValue('Michael');
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
rerender(renderDataTable());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('textbox')).toHaveValue('Michael');
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('preserves client-side search text for string accessor columns without ids', async () => {
|
||||
type DataRow = {
|
||||
city: string;
|
||||
firstName: string;
|
||||
};
|
||||
|
||||
const makeColumns = (): Column<DataRow>[] => [
|
||||
{
|
||||
Header: ({ column }: HeaderProps<DataRow>) => (
|
||||
<th data-column-name={column.id}>First name</th>
|
||||
),
|
||||
Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
|
||||
accessor: 'firstName',
|
||||
},
|
||||
{
|
||||
Header: ({ column }: HeaderProps<DataRow>) => (
|
||||
<th data-column-name={column.id}>City</th>
|
||||
),
|
||||
Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
|
||||
accessor: 'city',
|
||||
},
|
||||
];
|
||||
|
||||
const data: DataRow[] = [
|
||||
{ firstName: 'Michael', city: 'Paris' },
|
||||
{ firstName: 'Jordan', city: 'London' },
|
||||
];
|
||||
|
||||
const renderDataTable = () => (
|
||||
<ProviderWrapper>
|
||||
<DataTable<DataRow>
|
||||
columns={makeColumns()}
|
||||
data={data}
|
||||
rowCount={data.length}
|
||||
serverPagination={false}
|
||||
serverPaginationData={{}}
|
||||
onServerPaginationChange={jest.fn()}
|
||||
handleSortByChange={jest.fn()}
|
||||
sortByFromParent={[]}
|
||||
onSearchColChange={jest.fn()}
|
||||
searchOptions={[]}
|
||||
sticky={false}
|
||||
/>
|
||||
</ProviderWrapper>
|
||||
);
|
||||
|
||||
const { rerender } = render(renderDataTable());
|
||||
|
||||
const searchInput = screen.getByRole('textbox');
|
||||
fireEvent.change(searchInput, { target: { value: 'Michael' } });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(searchInput).toHaveValue('Michael');
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
rerender(renderDataTable());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('textbox')).toHaveValue('Michael');
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should build columnLabelToNameMap for adhoc columns with custom labels', () => {
|
||||
|
||||
Reference in New Issue
Block a user