Skip to content

feat(grid-esf): make excel style filtering work like in excel #8193

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

Merged
merged 32 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6df89bd
feat(grid-esf): make excel style filtering work like in excel
onlyexeption Sep 23, 2020
31007e3
feat(esf): confirm & close filter by pressing enter
onlyexeption Sep 24, 2020
ccf7658
feat(esf): close esf with ctrl + shift + l combination
onlyexeption Sep 24, 2020
798c5b0
feat(esf): improve disabled apply button logic, add resource string +…
onlyexeption Sep 25, 2020
7e2ff68
fix(esf): fix tests and add listDataLoaded event emitter
onlyexeption Sep 28, 2020
9e59425
fix(esf): fix tests + update items on clear input
onlyexeption Sep 28, 2020
cc5d7d2
fix(esf): remove excel style search pipe and add tests
onlyexeption Sep 29, 2020
7e90529
fix(esf): fix tslint error
onlyexeption Sep 29, 2020
aa19a7d
chore(*): add more tests for esf
onlyexeption Sep 29, 2020
6b0e15d
Merge branch 'master' into ibarakov/fix-5813-10.2.x
DiyanDimitrov Sep 29, 2020
90e7ce4
Merge branch 'master' into ibarakov/fix-5813-10.2.x
DiyanDimitrov Sep 30, 2020
3c9c10e
chore(*): address PR comments
onlyexeption Oct 1, 2020
2c7a833
Merge branch 'ibarakov/fix-5813-10.2.x' of https://github.com/IgniteU…
onlyexeption Oct 1, 2020
76cefe7
chore(*): add note to changelog.md
onlyexeption Oct 1, 2020
43eff82
Update CHANGELOG.md
onlyexeption Oct 1, 2020
2f8e4a3
Update CHANGELOG.md
onlyexeption Oct 1, 2020
ddb1ff5
chore(*): address PR comments
onlyexeption Oct 1, 2020
f6af29c
Merge branch 'ibarakov/fix-5813-10.2.x' of https://github.com/IgniteU…
onlyexeption Oct 1, 2020
044ad78
chore(*): remove .value from object comparison
onlyexeption Oct 1, 2020
c0722c5
fix(esf): get search all button from esf listData
onlyexeption Oct 1, 2020
916907a
fix(esf): fix tests and isFiltered logic
onlyexeption Oct 2, 2020
0b77778
chore(*): remove console.log
onlyexeption Oct 2, 2020
d620ae6
fix(esf): set isFiltered of blanks
onlyexeption Oct 2, 2020
ce153ad
chore(*): revert test changes
onlyexeption Oct 2, 2020
60d0fe4
chore(*): add hidden internal decorators
onlyexeption Oct 5, 2020
c275b57
Merge branch 'master' into ibarakov/fix-5813-10.2.x
igdmdimitrov Oct 5, 2020
533fbbf
chore(*): fix esf unit tests
onlyexeption Oct 6, 2020
12ffcac
chore(*): fix test for filter ui row
onlyexeption Oct 7, 2020
ece3c07
chore(*): remove leftover fit
onlyexeption Oct 7, 2020
8591320
fix(grid-filtering): close filtering row with ctrl + shift + l
onlyexeption Oct 7, 2020
80438a0
Merge branch 'master' into ibarakov/fix-5813-10.2.x
onlyexeption Oct 8, 2020
12288e3
Merge branch 'master' into ibarakov/fix-5813-10.2.x
DiyanDimitrov Oct 8, 2020
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ All notable changes for each version of this project will be documented in this
### General
- `IgxGridActions`
- Added `asMenuItems` Input for grid actions - `igx-grid-editing-actions`, `igx-grid-pinning-actions`. When set to true will render the related action buttons as separate menu items with button and label.
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
- **Behavioral Change** - The Excel Style Filtering has been reworked to provide filtering experience such as in Excel. This includes the following changes:
- You can close the Excel Style Filtering menu by pressing `Ctrl + Shift + L`.
- You can apply the filter by pressing `Enter`.
- When searching items in the Excel Style Filtering menu, only the rows that match your search term will be filtered in.
- By checking the `Add current selection to filter` option, the new search results will be added to the previously filtered items.
- `IgxInputGroup`
- **Breaking Change** - Removed `fluent`, `fluent_search`, `bootstrap`, and `indigo` as possible values for the `type` input property.
- **Behavioral Change** - The styling of the input group is now dictated by the theme being used. The remaining `types` - `line`, `border`, and `box` will only have effect on the styling when used with the `material` theme. The `search` type will affect styling when used with all themes. Changing the theme at runtime will not change the styling of the input group, a page refresh is required.
Expand Down
4 changes: 4 additions & 0 deletions projects/igniteui-angular/src/lib/core/i18n/grid-resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ export interface IGridResourceStrings {
igx_grid_excel_apply?: string;
igx_grid_excel_search_placeholder?: string;
igx_grid_excel_select_all?: string;
igx_grid_excel_select_all_search_results?: string;
igx_grid_excel_add_to_filter?: string;
igx_grid_excel_blanks?: string;
igx_grid_excel_hide?: string;
igx_grid_excel_show?: string;
Expand Down Expand Up @@ -174,6 +176,8 @@ export const GridResourceStringsEN: IGridResourceStrings = {
igx_grid_excel_apply: 'apply',
igx_grid_excel_search_placeholder: 'Search',
igx_grid_excel_select_all: 'Select All',
igx_grid_excel_select_all_search_results: 'Select all search results',
igx_grid_excel_add_to_filter: 'Add current selection to filter',
igx_grid_excel_blanks: '(Blanks)',
igx_grid_excel_hide: 'Hide column',
igx_grid_excel_show: 'Show column',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,14 @@ export class IgxGridFilteringRowComponent implements AfterViewInit {
this.input.nativeElement.focus();
}

@HostListener('keydown.esc', ['$event'])
public onEscHandler(evt) {
evt.preventDefault();
evt.stopPropagation();
this.close();
@HostListener('keydown', ['$event'])
public onKeydownHandler(evt) {
if (evt.key === KEYS.ESCAPE || evt.key === KEYS.ESCAPE_IE ||
evt.ctrlKey && evt.shiftKey && evt.key.toLowerCase() === 'l') {
evt.preventDefault();
evt.stopPropagation();
this.close();
}
}

get disabled(): boolean {
Expand Down Expand Up @@ -260,8 +263,9 @@ export class IgxGridFilteringRowComponent implements AfterViewInit {
} else if (event.altKey && (event.key === KEYS.DOWN_ARROW || event.key === KEYS.DOWN_ARROW_IE)) {
this.inputGroupPrefix.nativeElement.focus();
this.toggleConditionsDropDown(this.inputGroupPrefix.nativeElement);
} else if (event.key === KEYS.ESCAPE || event.key === KEYS.ESCAPE_IE) {
this.close();
} else if (event.key === KEYS.ESCAPE || event.key === KEYS.ESCAPE_IE ||
event.ctrlKey && event.shiftKey && event.key.toLowerCase() === 'l') {
this.close();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
igxInput
tabindex="0"
[(ngModel)]="searchValue"
(ngModelChange)="filterListData()"
(keydown)="onInputKeyDown($event)"
[placeholder]="esf.column?.grid.resourceStrings.igx_grid_excel_search_placeholder"
autocomplete="off"/>
<igx-icon
Expand All @@ -21,7 +23,7 @@
<igx-list #list [displayDensity]="esf.displayDensity" [isLoading]="isLoading">
<div [style.overflow]="'hidden'" [style.position]="'relative'">
<igx-list-item
*igxFor="let item of esf.listData | excelStyleSearchFilter: searchValue; scrollOrientation : 'vertical'; containerSize: containerSize; itemSize: itemSize">
*igxFor="let item of displayedListData scrollOrientation : 'vertical'; containerSize: containerSize; itemSize: itemSize">
<igx-checkbox
[value]="item"
tabindex="-1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { IgxInputDirective } from '../../../directives/input/input.directive';
import { DisplayDensity } from '../../../core/density';
import { IgxForOfDirective } from '../../../directives/for-of/for_of.directive';
import { IgxGridExcelStyleFilteringComponent } from './grid.excel-style-filtering.component';
import { IgxGridExcelStyleFilteringComponent, FilterListItem } from './grid.excel-style-filtering.component';
import { FilteringExpressionsTree } from '../../../data-operations/filtering-expressions-tree';
import { FilteringLogic } from '../../../data-operations/filtering-expression.interface';
import { DataType } from '../../../data-operations/data-util';
Expand All @@ -23,6 +23,7 @@ import { Subject } from 'rxjs';
import { IgxListComponent } from '../../../list/public_api';
import { IChangeCheckboxEventArgs } from '../../../checkbox/checkbox.component';
import { takeUntil } from 'rxjs/operators';
import { KEYS } from '../../../core/utils';

@Directive({
selector: '[igxExcelStyleLoading]'
Expand All @@ -42,8 +43,30 @@ export class IgxExcelStyleLoadingValuesTemplateDirective {
export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
private static readonly filterOptimizationThreshold = 2;
private _isLoading;
private _addToCurrentFilter: FilterListItem;
private destroy$ = new Subject<boolean>();

/**
* @hidden @internal
*/
public get addToCurrentFilter(): FilterListItem {
if (!this._addToCurrentFilter) {
const addToCurrentFilterItem = {
isSelected: false,
isFiltered: false,
indeterminate: false,
isSpecial: true,
isBlanks: false,
value: this.esf.grid.resourceStrings.igx_grid_excel_add_to_filter,
label: this.esf.grid.resourceStrings.igx_grid_excel_add_to_filter
};

this._addToCurrentFilter = addToCurrentFilterItem;
}

return this._addToCurrentFilter;
}

/**
* @hidden @internal
*/
Expand All @@ -66,6 +89,11 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
*/
public searchValue: any;

/**
* @hidden @internal
*/
public displayedListData: FilterListItem[];

/**
* @hidden @internal
*/
Expand Down Expand Up @@ -106,13 +134,6 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
}
}

/**
* @hidden @internal
*/
get applyButtonDisabled() {
return this.esf.listData[0] && !this.esf.listData[0].isSelected && !this.esf.listData[0].indeterminate;
}

constructor(public cdr: ChangeDetectorRef, public esf: IgxGridExcelStyleFilteringComponent) {
esf.loadingStart.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.isLoading = true;
Expand All @@ -129,6 +150,10 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
esf.columnChange.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.virtDir.resetScrollPosition();
});

esf.listDataLoaded.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.filterListData();
});
}

public ngAfterViewInit() {
Expand All @@ -154,28 +179,38 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
*/
public clearInput() {
this.searchValue = null;
this.filterListData();
}

/**
* @hidden @internal
*/
public onCheckboxChange(eventArgs: IChangeCheckboxEventArgs) {
const selectedIndex = this.esf.listData.indexOf(eventArgs.checkbox.value);
const selectedIndex = this.displayedListData.indexOf(eventArgs.checkbox.value);
const selectAllBtn = this.displayedListData[0];

if (selectedIndex === 0) {
this.esf.listData.forEach(element => {
this.displayedListData.forEach(element => {
if (element === this.addToCurrentFilter) { return; }
element.isSelected = eventArgs.checked;
this.esf.listData[0].indeterminate = false;
});

selectAllBtn.indeterminate = false;
} else {
eventArgs.checkbox.value.isSelected = eventArgs.checked;
if (!this.esf.listData.slice(1, this.esf.listData.length).find(el => el.isSelected === false)) {
this.esf.listData[0].indeterminate = false;
this.esf.listData[0].isSelected = true;
} else if (!this.esf.listData.slice(1, this.esf.listData.length).find(el => el.isSelected === true)) {
this.esf.listData[0].indeterminate = false;
this.esf.listData[0].isSelected = false;
const indexToStartSlicing = this.displayedListData.indexOf(this.addToCurrentFilter) > -1 ? 2 : 1;

const slicedArray =
this.displayedListData.slice(indexToStartSlicing, this.displayedListData.length);

if (!slicedArray.find(el => el.isSelected === false)) {
selectAllBtn.indeterminate = false;
selectAllBtn.isSelected = true;
} else if (!slicedArray.find(el => el.isSelected === true)) {
selectAllBtn.indeterminate = false;
selectAllBtn.isSelected = false;
} else {
this.esf.listData[0].indeterminate = true;
selectAllBtn.indeterminate = true;
}
}
eventArgs.checkbox.nativeCheckbox.nativeElement.blur();
Expand Down Expand Up @@ -203,12 +238,84 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
}
}

/**
* @hidden @internal
*/
get applyButtonDisabled(): boolean {
return this.esf.listData[0] && !this.esf.listData[0].isSelected && !this.esf.listData[0].indeterminate ||
this.displayedListData && this.displayedListData.length === 0;
}

/**
* @hidden @internal
*/
public onInputKeyDown(event): void {
if (event.key === KEYS.ENTER) {
event.preventDefault();
this.applyFilter();
}
}

/**
* @hidden @internal
*/
public filterListData(): void {
if (!this.esf.listData || !this.esf.listData.length) {
this.displayedListData = [];

return;
}

const searchAllBtn = this.esf.listData[0];

if (!this.searchValue) {
const anyFiltered = this.esf.listData.some(i => i.isFiltered);
const anyUnfiltered = this.esf.listData.some(i => !i.isFiltered);

if (anyFiltered && anyUnfiltered) {
searchAllBtn.indeterminate = true;
}

this.esf.listData.forEach(i => i.isSelected = i.isFiltered);
this.displayedListData = this.esf.listData;
searchAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all;

return;
}

const searchVal = this.searchValue.toLowerCase();

this.displayedListData = this.esf.listData.filter((it, i) => (i === 0 && it.isSpecial) ||
(it.label !== null && it.label !== undefined) &&
!it.isBlanks &&
it.label.toString().toLowerCase().indexOf(searchVal) > -1);

this.esf.listData.forEach(i => i.isSelected = false);
this.displayedListData.forEach(i => i.isSelected = true);

this.displayedListData.splice(1, 0, this.addToCurrentFilter);

searchAllBtn.indeterminate = false;
searchAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all_search_results;

if (this.displayedListData.length === 2) {
this.displayedListData = [];
}
}

/**
* @hidden @internal
*/
public applyFilter() {
const filterTree = new FilteringExpressionsTree(FilteringLogic.Or, this.esf.column.field);
const selectedItems = this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected === true);

const item = this.displayedListData[1];
const addToCurrentFilterOptionVisible = item === this.addToCurrentFilter;

const selectedItems = addToCurrentFilterOptionVisible && item.isSelected ?
this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected || el.isFiltered) :
this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected);

const unselectedItem = this.esf.listData.slice(1, this.esf.listData.length).find(el => el.isSelected === false);

if (unselectedItem) {
Expand Down

This file was deleted.

Loading