Skip to content

Commit

Permalink
Add an option to limit the number of max results
Browse files Browse the repository at this point in the history
  • Loading branch information
klassm committed Oct 19, 2023
1 parent 1cf185d commit 5dd2d9a
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 16 deletions.
40 changes: 39 additions & 1 deletion __tests__/demo/demo-components/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef } from 'react';

import { Box, Input, InputAdornment } from '@mui/material';
import { Alert, Box, Input, InputAdornment } from '@mui/material';
import SearchIcon from '@mui/icons-material/Search';

// root of this project
Expand Down Expand Up @@ -1466,3 +1466,41 @@ export function LocalizationWithCustomComponents() {
/>
);
}

export function LimitedSearchResults() {
const [maxResultsExceeded, setMaxResultsExceeded] = useState(false);
const data = Array(1000)
.fill(undefined)
.map((_, index) => {
return {
name: `Name ${index}`,
surname: `Surname ${index}`,
birthYear: 1950 + (index % 80),
birthCity: 63,
id: index
};
});

const maxResultsExceededWarning = maxResultsExceeded ? (
<Alert severity="warning">
Too many results. Please refine your search
</Alert>
) : null;

return (
<>
{maxResultsExceededWarning}
<MaterialTable
data={[...data, ...global_data]}
columns={global_cols}
title="Multi Column Sort"
options={{
searchMaxResults: 10
}}
onSearchChange={(_, maxResultsExceeded) =>
setMaxResultsExceeded(maxResultsExceeded)
}
/>
</>
);
}
7 changes: 5 additions & 2 deletions __tests__/demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ import {
TableWithNumberOfPagesAround,
FixedColumnWithEdit,
TableMultiSorting,
LocalizationWithCustomComponents
LocalizationWithCustomComponents,
LimitedSearchResults
} from './demo-components';
import { createRoot } from 'react-dom/client';
import { I1353, I1941, I122 } from './demo-components/RemoteData';
Expand Down Expand Up @@ -154,7 +155,9 @@ function Demo() {
<FixedColumnWithEdit />
<h1>Localization with Custom Components</h1>
<LocalizationWithCustomComponents />
<h1>Remote Data Related</h1>
<h1>Limited Search Results</h1>
<LimitedSearchResults />
<h1>Remote Data Related</h1>
<ol>
<li>
<h3>
Expand Down
32 changes: 31 additions & 1 deletion __tests__/pre.build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
screen,
fireEvent,
waitForElementToBeRemoved,
within
within,
waitFor
} from '@testing-library/react';

import MaterialTable from '../src';
Expand Down Expand Up @@ -211,6 +212,35 @@ describe('Render Table : Pre Build', () => {
).toBeDisabled();
});
});

it('filters data in the search input until a maximum number of results has been reached', async () => {
const data = makeData().map((element) => ({
...element,
firstName: `prefix_${element.firstName}`
}));
const onSearchChange = jest.fn();

render(
<MaterialTable
data={data}
columns={columns}
options={{ searchMaxResults: 10 }}
onSearchChange={onSearchChange}
/>
);

fireEvent.input(
screen.getByRole('textbox', {
name: /search/i
}),
{ target: { value: 'prefix' } }
);

await waitFor(() => {
expect(onSearchChange).toHaveBeenCalledWith('prefix', true);
});
});

// Render table with column render function
it('renders the render function in column', () => {
const data = makeData();
Expand Down
7 changes: 6 additions & 1 deletion src/material-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export default class MaterialTable extends React.Component {
this.dataManager.setColumns(props.columns, prevColumns, savedColumns);
this.dataManager.setDefaultExpanded(props.options.defaultExpanded);
this.dataManager.changeRowEditing();
this.dataManager.setSearchMaxResults(props.options.searchMaxResults);

const { clientSorting, grouping, maxColumnSort } = props.options;
this.dataManager.setClientSorting(clientSorting);
Expand Down Expand Up @@ -783,7 +784,11 @@ export default class MaterialTable extends React.Component {
});
} else {
this.setState(this.dataManager.getRenderState(), () => {
this.props.onSearchChange && this.props.onSearchChange(searchText);
this.props.onSearchChange &&
this.props.onSearchChange(
searchText,
this.state.searchMaxResultsExceeded
);
});
}
}, this.props.options.debounceInterval);
Expand Down
36 changes: 26 additions & 10 deletions src/utils/data-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid';
import { selectFromObject } from './';
import { widthToNumber } from './common-values';
import { ALL_COLUMNS } from './constants';
import { filterWithMaxResults } from './filter-with-max-results';

export default class DataManager {
checkForId = false;
Expand All @@ -26,10 +27,13 @@ export default class DataManager {
treeDataMaxLevel = 0;
groupedDataLength = 0;
defaultExpanded = false;
searchMaxResults = undefined;
bulkEditOpen = false;
bulkEditChangedRows = {};
clientSorting = true;

searchMaxResultsExceeded = false;

data = [];
columns = [];

Expand Down Expand Up @@ -185,6 +189,10 @@ export default class DataManager {
this.defaultExpanded = expanded;
}

setSearchMaxResults(maxResults) {
this.searchMaxResults = maxResults;
}

setClientSorting(clientSorting) {
this.clientSorting = !!clientSorting;
}
Expand Down Expand Up @@ -906,7 +914,8 @@ export default class DataManager {
treefiedDataLength: this.treefiedDataLength,
treeDataMaxLevel: this.treeDataMaxLevel,
groupedDataLength: this.groupedDataLength,
tableStyleWidth: this.tableStyleWidth
tableStyleWidth: this.tableStyleWidth,
searchMaxResultsExceeded: this.searchMaxResultsExceeded
};
};

Expand Down Expand Up @@ -1040,17 +1049,20 @@ export default class DataManager {
this.grouped = this.treefied = this.sorted = this.paged = false;

this.searchedData = [...this.filteredData];
this.searchMaxResultsExceeded = false;

if (this.searchText && this.applySearch) {
const trimmedSearchText = this.searchText.trim();
this.searchedData = this.searchedData.filter((row) => {
return this.columns
.filter((columnDef) => {
return columnDef.searchable === undefined
? !columnDef.hidden
: columnDef.searchable;
})
.some((columnDef) => {
const searchableColumns = this.columns.filter((columnDef) => {
return columnDef.searchable === undefined
? !columnDef.hidden
: columnDef.searchable;
});

const searchResult = filterWithMaxResults(
this.searchedData,
(row) => {
return searchableColumns.some((columnDef) => {
if (columnDef.customFilterAndSearch) {
return !!columnDef.customFilterAndSearch(
trimmedSearchText,
Expand All @@ -1068,7 +1080,11 @@ export default class DataManager {
}
return false;
});
});
},
this.searchMaxResults
);
this.searchedData = searchResult.results;
this.searchMaxResultsExceeded = searchResult.maxResultsExceeded;
}
this.searched = true;
};
Expand Down
22 changes: 22 additions & 0 deletions src/utils/filter-with-max-results.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
function filterWithMaxResults(rows, predicate, maxResults) {
const results = [];
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
if (predicate(row)) {
results.push(row);
}

if (maxResults !== undefined && results.length > maxResults) {
return {
maxResultsExceeded: true,
results: results.slice(0, maxResults)
};
}
}
return {
maxResultsExceeded: false,
results
};
}

module.exports = { filterWithMaxResults };
3 changes: 2 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export interface MaterialTableProps<RowData extends object> {
toggleDetailPanel?: (panelIndex?: number) => void
) => void;
onRowSelected?: (rowData: RowData) => void;
onSearchChange?: (searchText: string) => void;
onSearchChange?: (searchText: string, maxResultsExceeded: boolean) => void;
/** An event fired when the table has finished filtering data
* @param {Filter<RowData>[]} filters All the filters that are applied to the table
*/
Expand Down Expand Up @@ -451,6 +451,7 @@ export interface Options<RowData extends object> {
searchFieldAlignment?: 'left' | 'right';
searchFieldStyle?: React.CSSProperties;
searchFieldVariant?: 'standard' | 'filled' | 'outlined';
searchMaxResults?: number;
searchAutoFocus?: boolean;
selection?: boolean;
selectionProps?: CheckboxProps | ((data: RowData) => CheckboxProps);
Expand Down

0 comments on commit 5dd2d9a

Please sign in to comment.