diff --git a/packages/common/src/filter-conditions/stringFilterCondition.ts b/packages/common/src/filter-conditions/stringFilterCondition.ts index 57ec95a98..d3efddee8 100644 --- a/packages/common/src/filter-conditions/stringFilterCondition.ts +++ b/packages/common/src/filter-conditions/stringFilterCondition.ts @@ -1,5 +1,6 @@ import { OperatorString, OperatorType, SearchTerm } from '../enums/index'; import { FilterCondition, FilterConditionOption } from '../interfaces/index'; +import { removeAccentFromText } from '../services/utilities'; import { testFilterCondition } from './filterUtilities'; /** Execute filter condition check on each cell */ @@ -14,12 +15,12 @@ export const executeStringFilterCondition: FilterCondition = ((options: FilterCo options.cellValue = (options.cellValue === undefined || options.cellValue === null) ? '' : options.cellValue.toString(); // make both the cell value and search value lower for case insensitive comparison - const cellValue = options.cellValue.toLowerCase(); + const cellValue = options?.ignoreAccentOnStringFilter ? removeAccentFromText(options.cellValue, true) : options.cellValue.toLowerCase(); if (typeof searchValue1 === 'string') { - searchValue1 = searchValue1.toLowerCase(); + searchValue1 = options?.ignoreAccentOnStringFilter ? removeAccentFromText(searchValue1, true) : searchValue1.toLowerCase(); } if (typeof searchValue2 === 'string') { - searchValue2 = searchValue2.toLowerCase(); + searchValue2 = options?.ignoreAccentOnStringFilter ? removeAccentFromText(searchValue2, true) : searchValue2.toLowerCase(); } if (searchValue1 !== undefined && searchValue2 !== undefined) { diff --git a/packages/common/src/global-grid-options.ts b/packages/common/src/global-grid-options.ts index 4344f2cbd..0fe9c4291 100644 --- a/packages/common/src/global-grid-options.ts +++ b/packages/common/src/global-grid-options.ts @@ -216,6 +216,7 @@ export const GlobalGridOptions: GridOption = { hideFreezeColumnsCommand: true, // opt-in command hideSortCommands: false }, + ignoreAccentOnStringFilter: false, multiColumnSort: true, numberedMultiColumnSort: true, tristateMultiColumnSort: false, diff --git a/packages/common/src/interfaces/filterConditionOption.interface.ts b/packages/common/src/interfaces/filterConditionOption.interface.ts index ec2c322d1..0608850c2 100644 --- a/packages/common/src/interfaces/filterConditionOption.interface.ts +++ b/packages/common/src/interfaces/filterConditionOption.interface.ts @@ -22,6 +22,9 @@ export interface FilterConditionOption { /** filter search field type */ filterSearchType?: typeof FieldType[keyof typeof FieldType]; + /** should we ignore any accent while filtering text? */ + ignoreAccentOnStringFilter?: any; + /** * Parsed Search Terms is similar to SearchTerms but is already parsed in the correct format, * for example on a date field the searchTerms might be in string format but their respective parsedSearchTerms will be of type Date diff --git a/packages/common/src/interfaces/gridOption.interface.ts b/packages/common/src/interfaces/gridOption.interface.ts index 19e8a867e..aec838cf8 100644 --- a/packages/common/src/interfaces/gridOption.interface.ts +++ b/packages/common/src/interfaces/gridOption.interface.ts @@ -455,6 +455,12 @@ export interface GridOption { /** Header menu options */ headerMenu?: HeaderMenu; + /** + * Defaults to false, should we ignore any accent while filtering text? + * For example if our text is "José" and we type "Jose" then it won't return unless we use this flag because "é" is not equal to "e" + */ + ignoreAccentOnStringFilter?: boolean; + /** * When using custom Locales (that is when user is NOT using a Translate Service, this property does nothing when used with Translate Service), * This is useful so that every component of the lib knows the locale. diff --git a/packages/common/src/services/__tests__/utilities.spec.ts b/packages/common/src/services/__tests__/utilities.spec.ts index 6fc743847..c6ca1d82e 100644 --- a/packages/common/src/services/__tests__/utilities.spec.ts +++ b/packages/common/src/services/__tests__/utilities.spec.ts @@ -33,6 +33,7 @@ import { mapOperatorType, parseBoolean, parseUtcDate, + removeAccentFromText, sanitizeHtmlToText, sanitizeTextByAvailableSanitizer, setDeepValue, @@ -1242,6 +1243,28 @@ describe('Service/Utilies', () => { }); }); + describe('removeAccentFromText method', () => { + it('should return a normalized string without accent', () => { + const input1 = 'José'; + const input2 = 'Chêvre'; + const input3 = 'áàãāăǎäéèêëěíìîïǐĩóòôöǒõ'; + + expect(removeAccentFromText(input1)).toBe('Jose'); + expect(removeAccentFromText(input2)).toBe('Chevre'); + expect(removeAccentFromText(input3)).toBe('aaaaaaaeeeeeiiiiiioooooo'); + }); + + it('should return a normalized string without accent and lowercase when specified', () => { + const input1 = 'José'; + const input2 = 'Chêvre'; + const input3 = 'ÁÀÃĀĂǍÄÉÈÊËĚÍÌÎÏǏĨÓÒÔÖǑÕ'; + + expect(removeAccentFromText(input1, true)).toBe('jose'); + expect(removeAccentFromText(input2, true)).toBe('chevre'); + expect(removeAccentFromText(input3, true)).toBe('aaaaaaaeeeeeiiiiiioooooo'); + }); + }); + describe('sanitizeHtmlToText method', () => { it('should return original value when input does not include any HTML tags', () => { const input = 'foo bar'; diff --git a/packages/common/src/services/filter.service.ts b/packages/common/src/services/filter.service.ts index 31c8868ef..edf940abe 100644 --- a/packages/common/src/services/filter.service.ts +++ b/packages/common/src/services/filter.service.ts @@ -515,6 +515,7 @@ export class FilterService { operator: operator as OperatorString, searchInputLastChar: columnFilter.searchInputLastChar, filterSearchType: columnDef.filterSearchType, + ignoreAccentOnStringFilter: this._gridOptions.ignoreAccentOnStringFilter ?? false, defaultFilterRangeOperator: this._gridOptions.defaultFilterRangeOperator, } as FilterConditionOption; } diff --git a/packages/common/src/services/utilities.ts b/packages/common/src/services/utilities.ts index 2af32e1b5..de34acd3c 100644 --- a/packages/common/src/services/utilities.ts +++ b/packages/common/src/services/utilities.ts @@ -871,6 +871,17 @@ export function parseUtcDate(inputDateString: any, useUtc?: boolean): string { return date; } +/** + * Remove any accents from a string by normalizing it + * @param {String} text - input text + * @param {Boolean} shouldLowerCase - should we also lowercase the string output? + * @returns + */ +export function removeAccentFromText(text: string, shouldLowerCase = false) { + const normalizedText = text.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); + return shouldLowerCase ? normalizedText.toLowerCase() : normalizedText; +} + /** * Sanitize, return only the text without HTML tags * @input htmlString