mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
feat: more powerful project search (#4542)
This commit is contained in:
parent
3acff3e6d9
commit
8a3889d570
@ -72,9 +72,11 @@ export const SearchSuggestions: VFC<SearchSuggestionsProps> = ({
|
|||||||
return {
|
return {
|
||||||
name: column.filterName,
|
name: column.filterName,
|
||||||
header: column.Header ?? column.filterName,
|
header: column.Header ?? column.filterName,
|
||||||
options: [...new Set(filterOptions)].sort((a, b) =>
|
options: [...new Set(filterOptions)]
|
||||||
a.localeCompare(b)
|
.filter(Boolean)
|
||||||
),
|
.flatMap(item => item.split('\n'))
|
||||||
|
.map(item => (item.includes(' ') ? `"${item}"` : item))
|
||||||
|
.sort((a, b) => a.localeCompare(b)),
|
||||||
suggestedOption:
|
suggestedOption:
|
||||||
filterOptions[randomRow] ?? `example-${column.filterName}`,
|
filterOptions[randomRow] ?? `example-${column.filterName}`,
|
||||||
values: getFilterValues(
|
values: getFilterValues(
|
||||||
|
@ -36,7 +36,7 @@ import { createLocalStorage } from 'utils/createLocalStorage';
|
|||||||
import EnvironmentStrategyDialog from 'component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog';
|
import EnvironmentStrategyDialog from 'component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog';
|
||||||
import { FeatureStaleDialog } from 'component/common/FeatureStaleDialog/FeatureStaleDialog';
|
import { FeatureStaleDialog } from 'component/common/FeatureStaleDialog/FeatureStaleDialog';
|
||||||
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
|
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
|
||||||
import { useSearch } from 'hooks/useSearch';
|
import { getColumnValues, includesFilter, useSearch } from 'hooks/useSearch';
|
||||||
import { Search } from 'component/common/Search/Search';
|
import { Search } from 'component/common/Search/Search';
|
||||||
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
|
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
|
||||||
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
|
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
|
||||||
@ -234,6 +234,7 @@ export const ProjectFeatureToggles = ({
|
|||||||
accessor: 'type',
|
accessor: 'type',
|
||||||
Cell: FeatureTypeCell,
|
Cell: FeatureTypeCell,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
|
filterName: 'type',
|
||||||
maxWidth: 80,
|
maxWidth: 80,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -265,6 +266,16 @@ export const ProjectFeatureToggles = ({
|
|||||||
Cell: FeatureTagCell,
|
Cell: FeatureTagCell,
|
||||||
width: 80,
|
width: 80,
|
||||||
searchable: true,
|
searchable: true,
|
||||||
|
filterName: 'tags',
|
||||||
|
filterBy(
|
||||||
|
row: IFeatureToggleListItem,
|
||||||
|
values: string[]
|
||||||
|
) {
|
||||||
|
return includesFilter(
|
||||||
|
getColumnValues(this, row),
|
||||||
|
values
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
@ -3,7 +3,13 @@ import {
|
|||||||
getSearchTextGenerator,
|
getSearchTextGenerator,
|
||||||
searchInFilteredData,
|
searchInFilteredData,
|
||||||
filter,
|
filter,
|
||||||
|
useSearch,
|
||||||
|
includesFilter,
|
||||||
|
getColumnValues,
|
||||||
} from './useSearch';
|
} from './useSearch';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { IFeatureToggleListItem } from '../interfaces/featureToggle';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@ -32,6 +38,16 @@ const columns = [
|
|||||||
searchBy: (row: any, value: string) =>
|
searchBy: (row: any, value: string) =>
|
||||||
(value === 'seen' && row.seen) || (value === 'never' && !row.seen),
|
(value === 'seen' && row.seen) || (value === 'never' && !row.seen),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessor: (row: IFeatureToggleListItem) =>
|
||||||
|
row.tags?.map(({ type, value }) => `${type}:${value}`).join('\n') ||
|
||||||
|
'',
|
||||||
|
searchable: true,
|
||||||
|
filterName: 'tags',
|
||||||
|
filterBy(row: IFeatureToggleListItem, values: string[]) {
|
||||||
|
return includesFilter(getColumnValues(this, row), values);
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const data = [
|
const data = [
|
||||||
@ -41,6 +57,10 @@ const data = [
|
|||||||
stale: false,
|
stale: false,
|
||||||
type: 'release',
|
type: 'release',
|
||||||
seen: true,
|
seen: true,
|
||||||
|
tags: [
|
||||||
|
{ type: 'simple', value: 'tag' },
|
||||||
|
{ type: 'simple', value: 'some space' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'my-feature-toggle-2',
|
name: 'my-feature-toggle-2',
|
||||||
@ -48,6 +68,7 @@ const data = [
|
|||||||
stale: true,
|
stale: true,
|
||||||
type: 'experiment',
|
type: 'experiment',
|
||||||
seen: false,
|
seen: false,
|
||||||
|
tags: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'my-feature-toggle-3',
|
name: 'my-feature-toggle-3',
|
||||||
@ -55,6 +76,7 @@ const data = [
|
|||||||
stale: false,
|
stale: false,
|
||||||
type: 'operational',
|
type: 'operational',
|
||||||
seen: false,
|
seen: false,
|
||||||
|
tags: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'my-feature-toggle-4',
|
name: 'my-feature-toggle-4',
|
||||||
@ -62,6 +84,7 @@ const data = [
|
|||||||
stale: true,
|
stale: true,
|
||||||
type: 'permission',
|
type: 'permission',
|
||||||
seen: true,
|
seen: true,
|
||||||
|
tags: [],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -143,6 +166,7 @@ describe('searchInFilteredData', () => {
|
|||||||
name: 'my-feature-toggle-3',
|
name: 'my-feature-toggle-3',
|
||||||
project: 'my-project',
|
project: 'my-project',
|
||||||
stale: false,
|
stale: false,
|
||||||
|
tags: [],
|
||||||
type: 'operational',
|
type: 'operational',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -150,6 +174,7 @@ describe('searchInFilteredData', () => {
|
|||||||
name: 'my-feature-toggle-4',
|
name: 'my-feature-toggle-4',
|
||||||
project: 'my-project',
|
project: 'my-project',
|
||||||
stale: true,
|
stale: true,
|
||||||
|
tags: [],
|
||||||
type: 'permission',
|
type: 'permission',
|
||||||
seen: true,
|
seen: true,
|
||||||
},
|
},
|
||||||
@ -162,6 +187,7 @@ describe('searchInFilteredData', () => {
|
|||||||
name: 'my-feature-toggle-2',
|
name: 'my-feature-toggle-2',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
stale: true,
|
stale: true,
|
||||||
|
tags: [],
|
||||||
type: 'experiment',
|
type: 'experiment',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -187,6 +213,7 @@ describe('searchInFilteredData', () => {
|
|||||||
name: 'my-feature-toggle-2',
|
name: 'my-feature-toggle-2',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
stale: true,
|
stale: true,
|
||||||
|
tags: [],
|
||||||
type: 'experiment',
|
type: 'experiment',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -201,6 +228,7 @@ describe('searchInFilteredData', () => {
|
|||||||
name: 'my-feature-toggle-2',
|
name: 'my-feature-toggle-2',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
stale: true,
|
stale: true,
|
||||||
|
tags: [],
|
||||||
type: 'experiment',
|
type: 'experiment',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -208,6 +236,7 @@ describe('searchInFilteredData', () => {
|
|||||||
name: 'my-feature-toggle-3',
|
name: 'my-feature-toggle-3',
|
||||||
project: 'my-project',
|
project: 'my-project',
|
||||||
stale: false,
|
stale: false,
|
||||||
|
tags: [],
|
||||||
type: 'operational',
|
type: 'operational',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -225,6 +254,10 @@ describe('filter', () => {
|
|||||||
name: 'my-feature-toggle',
|
name: 'my-feature-toggle',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
stale: false,
|
stale: false,
|
||||||
|
tags: [
|
||||||
|
{ type: 'simple', value: 'tag' },
|
||||||
|
{ type: 'simple', value: 'some space' },
|
||||||
|
],
|
||||||
type: 'release',
|
type: 'release',
|
||||||
seen: true,
|
seen: true,
|
||||||
},
|
},
|
||||||
@ -232,6 +265,7 @@ describe('filter', () => {
|
|||||||
name: 'my-feature-toggle-2',
|
name: 'my-feature-toggle-2',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
stale: true,
|
stale: true,
|
||||||
|
tags: [],
|
||||||
type: 'experiment',
|
type: 'experiment',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -244,6 +278,10 @@ describe('filter', () => {
|
|||||||
name: 'my-feature-toggle',
|
name: 'my-feature-toggle',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
stale: false,
|
stale: false,
|
||||||
|
tags: [
|
||||||
|
{ type: 'simple', value: 'tag' },
|
||||||
|
{ type: 'simple', value: 'some space' },
|
||||||
|
],
|
||||||
type: 'release',
|
type: 'release',
|
||||||
seen: true,
|
seen: true,
|
||||||
},
|
},
|
||||||
@ -251,6 +289,7 @@ describe('filter', () => {
|
|||||||
name: 'my-feature-toggle-3',
|
name: 'my-feature-toggle-3',
|
||||||
project: 'my-project',
|
project: 'my-project',
|
||||||
stale: false,
|
stale: false,
|
||||||
|
tags: [],
|
||||||
type: 'operational',
|
type: 'operational',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -276,6 +315,7 @@ describe('filter', () => {
|
|||||||
name: 'my-feature-toggle-3',
|
name: 'my-feature-toggle-3',
|
||||||
project: 'my-project',
|
project: 'my-project',
|
||||||
stale: false,
|
stale: false,
|
||||||
|
tags: [],
|
||||||
type: 'operational',
|
type: 'operational',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -283,6 +323,7 @@ describe('filter', () => {
|
|||||||
name: 'my-feature-toggle-4',
|
name: 'my-feature-toggle-4',
|
||||||
project: 'my-project',
|
project: 'my-project',
|
||||||
stale: true,
|
stale: true,
|
||||||
|
tags: [],
|
||||||
type: 'permission',
|
type: 'permission',
|
||||||
seen: true,
|
seen: true,
|
||||||
},
|
},
|
||||||
@ -297,6 +338,7 @@ describe('filter', () => {
|
|||||||
name: 'my-feature-toggle-2',
|
name: 'my-feature-toggle-2',
|
||||||
project: 'default',
|
project: 'default',
|
||||||
stale: true,
|
stale: true,
|
||||||
|
tags: [],
|
||||||
type: 'experiment',
|
type: 'experiment',
|
||||||
seen: false,
|
seen: false,
|
||||||
},
|
},
|
||||||
@ -304,9 +346,154 @@ describe('filter', () => {
|
|||||||
name: 'my-feature-toggle-4',
|
name: 'my-feature-toggle-4',
|
||||||
project: 'my-project',
|
project: 'my-project',
|
||||||
stale: true,
|
stale: true,
|
||||||
|
tags: [],
|
||||||
type: 'permission',
|
type: 'permission',
|
||||||
seen: true,
|
seen: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const SearchData: FC<{ searchValue: string }> = ({ searchValue }) => {
|
||||||
|
const search = useSearch(columns, searchValue, data);
|
||||||
|
|
||||||
|
return <div>{search.data.map(item => item.name).join(',')}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SearchText: FC<{ searchValue: string }> = ({ searchValue }) => {
|
||||||
|
const search = useSearch(columns, searchValue, data);
|
||||||
|
|
||||||
|
return <div>{search.getSearchText(searchValue)}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Search and filter data', () => {
|
||||||
|
it('should filter single value', () => {
|
||||||
|
render(<SearchData searchValue={'project:my-project'} />);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle-3,my-feature-toggle-4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter multiple values', () => {
|
||||||
|
render(<SearchData searchValue={'project:my-project,another-value'} />);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle-3,my-feature-toggle-4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter multiple values with spaces', () => {
|
||||||
|
render(
|
||||||
|
<SearchData searchValue={'project:my-project , another-value'} />
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle-3,my-feature-toggle-4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple filters', () => {
|
||||||
|
render(
|
||||||
|
<SearchData
|
||||||
|
searchValue={'project:my-project ,another-value state:active'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle-3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple filters with long spaces', () => {
|
||||||
|
render(
|
||||||
|
<SearchData
|
||||||
|
searchValue={
|
||||||
|
'project:my-project , another-value state:active , stale'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle-3,my-feature-toggle-4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple filters and search string in between', () => {
|
||||||
|
render(
|
||||||
|
<SearchData
|
||||||
|
searchValue={
|
||||||
|
'project:my-project , another-value toggle-3 state:active , stale'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle-3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple filters and search string at the end', () => {
|
||||||
|
render(
|
||||||
|
<SearchData
|
||||||
|
searchValue={
|
||||||
|
'project:my-project , another-value state:active , stale toggle-3'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle-3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple filters and search string at the beginning', () => {
|
||||||
|
render(
|
||||||
|
<SearchData
|
||||||
|
searchValue={
|
||||||
|
'toggle-3 project:my-project , another-value state:active , stale'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle-3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return basic search text', () => {
|
||||||
|
render(<SearchText searchValue={'toggle-3'} />);
|
||||||
|
|
||||||
|
screen.getByText('toggle-3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return advanced search text', () => {
|
||||||
|
render(
|
||||||
|
<SearchText
|
||||||
|
searchValue={
|
||||||
|
'project:my-project , another-value toggle-3 state:active , stale'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText('toggle-3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support custom filter and accessor', () => {
|
||||||
|
render(<SearchData searchValue={'tags:simple:tag'} />);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support search on top of filter', () => {
|
||||||
|
render(<SearchText searchValue={'tags:simple:tag simple:tag'} />);
|
||||||
|
|
||||||
|
screen.getByText('simple:tag');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support custom filter with spaces', () => {
|
||||||
|
render(<SearchData searchValue={'tags:"simple:some space",tag'} />);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support custom filter with spaces - space in second term', () => {
|
||||||
|
render(<SearchData searchValue={'tags:tag,"simple:some space"'} />);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support quotes in filter and search', () => {
|
||||||
|
render(
|
||||||
|
<SearchData
|
||||||
|
searchValue={'tags:tag,"simple:some space" "my-feature-toggle"'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
screen.getByText('my-feature-toggle');
|
||||||
|
});
|
||||||
|
});
|
@ -12,32 +12,45 @@ type IUseSearchOutput<T extends any> = {
|
|||||||
getSearchContext: () => IGetSearchContextOutput<T>;
|
getSearchContext: () => IGetSearchContextOutput<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/9577930/regular-expression-to-select-all-whitespace-that-isnt-in-quotes
|
||||||
|
const SPACES_WITHOUT_QUOTES = /\s+(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)/g;
|
||||||
|
|
||||||
|
const normalizeSearchValue = (value: string) =>
|
||||||
|
value.replaceAll(/\s*,\s*/g, ',');
|
||||||
|
|
||||||
|
const removeQuotes = (value: string) =>
|
||||||
|
value.replaceAll("'", '').replaceAll('"', '');
|
||||||
|
|
||||||
export const useSearch = <T extends any>(
|
export const useSearch = <T extends any>(
|
||||||
columns: any[],
|
columns: any[],
|
||||||
searchValue: string,
|
searchValue: string,
|
||||||
data: T[]
|
data: T[]
|
||||||
): IUseSearchOutput<T> => {
|
): IUseSearchOutput<T> => {
|
||||||
const getSearchText = useCallback(
|
const getSearchText = useCallback(
|
||||||
(value: string) => getSearchTextGenerator(columns)(value),
|
(value: string) =>
|
||||||
|
removeQuotes(
|
||||||
|
getSearchTextGenerator(columns)(normalizeSearchValue(value))
|
||||||
|
),
|
||||||
[columns]
|
[columns]
|
||||||
);
|
);
|
||||||
|
const normalizedSearchValue = normalizeSearchValue(searchValue);
|
||||||
|
|
||||||
const getSearchContext = useCallback(() => {
|
const getSearchContext = useCallback(() => {
|
||||||
return { data, searchValue, columns };
|
return { data, searchValue: normalizedSearchValue, columns };
|
||||||
}, [data, searchValue, columns]);
|
}, [data, normalizedSearchValue, columns]);
|
||||||
|
|
||||||
const search = useMemo(() => {
|
const search = useMemo(() => {
|
||||||
if (!searchValue) return data;
|
if (!normalizedSearchValue) return data;
|
||||||
|
|
||||||
const filteredData = filter(columns, searchValue, data);
|
const filteredData = filter(columns, normalizedSearchValue, data);
|
||||||
const searchedData = searchInFilteredData(
|
const searchedData = searchInFilteredData(
|
||||||
columns,
|
columns,
|
||||||
getSearchText(searchValue),
|
getSearchText(normalizedSearchValue),
|
||||||
filteredData
|
filteredData
|
||||||
);
|
);
|
||||||
|
|
||||||
return searchedData;
|
return searchedData;
|
||||||
}, [columns, searchValue, data, getSearchText]);
|
}, [columns, normalizedSearchValue, data, getSearchText]);
|
||||||
|
|
||||||
return { data: search, getSearchText, getSearchContext };
|
return { data: search, getSearchText, getSearchContext };
|
||||||
};
|
};
|
||||||
@ -67,6 +80,7 @@ export const searchInFilteredData = <T extends any>(
|
|||||||
searchValue: string,
|
searchValue: string,
|
||||||
filteredData: T[]
|
filteredData: T[]
|
||||||
) => {
|
) => {
|
||||||
|
const trimmedSearchValue = searchValue.trim();
|
||||||
const searchableColumns = columns.filter(
|
const searchableColumns = columns.filter(
|
||||||
column => column.searchable && column.accessor
|
column => column.searchable && column.accessor
|
||||||
);
|
);
|
||||||
@ -74,10 +88,13 @@ export const searchInFilteredData = <T extends any>(
|
|||||||
return filteredData.filter(row => {
|
return filteredData.filter(row => {
|
||||||
return searchableColumns.some(column => {
|
return searchableColumns.some(column => {
|
||||||
if (column.searchBy) {
|
if (column.searchBy) {
|
||||||
return column.searchBy(row, searchValue);
|
return column.searchBy(row, trimmedSearchValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultSearch(getColumnValues(column, row), searchValue);
|
return defaultSearch(
|
||||||
|
getColumnValues(column, row),
|
||||||
|
trimmedSearchValue
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -85,6 +102,11 @@ export const searchInFilteredData = <T extends any>(
|
|||||||
const defaultFilter = (fieldValue: string, values: string[]) =>
|
const defaultFilter = (fieldValue: string, values: string[]) =>
|
||||||
values.some(value => fieldValue?.toLowerCase() === value?.toLowerCase());
|
values.some(value => fieldValue?.toLowerCase() === value?.toLowerCase());
|
||||||
|
|
||||||
|
export const includesFilter = (fieldValue: string, values: string[]) =>
|
||||||
|
values.some(value =>
|
||||||
|
fieldValue?.toLowerCase().includes(value?.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
const defaultSearch = (fieldValue: string, value: string) =>
|
const defaultSearch = (fieldValue: string, value: string) =>
|
||||||
fieldValue?.toLowerCase().includes(value?.toLowerCase());
|
fieldValue?.toLowerCase().includes(value?.toLowerCase());
|
||||||
|
|
||||||
@ -99,13 +121,14 @@ export const getSearchTextGenerator = (columns: any[]) => {
|
|||||||
|
|
||||||
return (searchValue: string) =>
|
return (searchValue: string) =>
|
||||||
searchValue
|
searchValue
|
||||||
.split(' ')
|
.split(SPACES_WITHOUT_QUOTES)
|
||||||
.filter(fragment => !isValidSearch(fragment))
|
.filter(fragment => !isValidSearch(fragment))
|
||||||
.join(' ');
|
.join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isValidFilter = (input: string, match: string) =>
|
export const isValidFilter = (input: string, match: string) =>
|
||||||
new RegExp(`${match}:\\w+`).test(input);
|
// name:"hello world" or name:'hello world' or name:simple
|
||||||
|
new RegExp(`${match}:(?:\\w+|["'][^"']+["'])`).test(input);
|
||||||
|
|
||||||
export const getFilterableColumns = (columns: any[]) =>
|
export const getFilterableColumns = (columns: any[]) =>
|
||||||
columns.filter(column => column.filterName && column.accessor);
|
columns.filter(column => column.filterName && column.accessor);
|
||||||
@ -130,6 +153,7 @@ export const getColumnValues = (column: any, row: any) => {
|
|||||||
export const getFilterValues = (filterName: string, searchValue: string) =>
|
export const getFilterValues = (filterName: string, searchValue: string) =>
|
||||||
searchValue
|
searchValue
|
||||||
?.split(`${filterName}:`)[1]
|
?.split(`${filterName}:`)[1]
|
||||||
?.split(' ')[0]
|
?.split(SPACES_WITHOUT_QUOTES)[0]
|
||||||
?.split(',')
|
?.split(',')
|
||||||
|
.map(removeQuotes)
|
||||||
.filter(value => value) ?? [];
|
.filter(value => value) ?? [];
|
||||||
|
Loading…
Reference in New Issue
Block a user