Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

Added multiselect support #21

Merged
merged 18 commits into from
Mar 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 23 additions & 9 deletions e2e/list-view-grid.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ import {
} from '@skyux-sdk/e2e';

describe('List view grid', () => {
it('should match the previous screenshot', (done) => {
SkyHostBrowser.get('/visual/list-view-grid');

beforeEach(() => {
SkyHostBrowser.get('visual/list-view-grid');
SkyHostBrowser.setWindowBreakpoint('lg');
});

it('should match the previous screenshot', (done) => {
SkyHostBrowser.scrollTo('#screenshot-list-view-grid');
expect('#screenshot-list-view-grid').toMatchBaselineScreenshot(done, {
screenshotName: 'list-view-grid-lg'
});
});

it('should match the previous screenshot (screen: xs)', (done) => {
SkyHostBrowser.get('/visual/list-view-grid');
SkyHostBrowser.setWindowBreakpoint('xs');
SkyHostBrowser.scrollTo('#screenshot-list-view-grid');
expect('#screenshot-list-view-grid').toMatchBaselineScreenshot(done, {
Expand All @@ -28,8 +31,6 @@ describe('List view grid', () => {
});

it('should match the previous screenshot with column chooser open', (done) => {
SkyHostBrowser.get('/visual/list-view-grid');
SkyHostBrowser.setWindowBreakpoint('lg');
SkyHostBrowser.scrollTo('#screenshot-list-view-grid');

element(by.css('[sky-cmp-id="column-chooser"] button')).click();
Expand All @@ -40,7 +41,6 @@ describe('List view grid', () => {
});

it('should match the previous screenshot with column chooser open (screen: xs)', (done) => {
Blackbaud-AlexKingman marked this conversation as resolved.
Show resolved Hide resolved
SkyHostBrowser.get('/visual/list-view-grid');
SkyHostBrowser.setWindowBreakpoint('xs');
SkyHostBrowser.scrollTo('#screenshot-list-view-grid');

Expand All @@ -52,8 +52,6 @@ describe('List view grid', () => {
});

it('should match the previous screenshot with a highlighted row', (done) => {
SkyHostBrowser.get('/visual/list-view-grid');
SkyHostBrowser.setWindowBreakpoint('lg');
SkyHostBrowser.scrollTo('#screenshot-list-view-grid');

element(by.css('#highlight-row-button')).click();
Expand All @@ -64,7 +62,6 @@ describe('List view grid', () => {
});

it('should match the previous screenshot with a highlighted row (screen: xs)', (done) => {
SkyHostBrowser.get('/visual/list-view-grid');
SkyHostBrowser.setWindowBreakpoint('xs');
SkyHostBrowser.scrollTo('#screenshot-list-view-grid');

Expand All @@ -74,4 +71,21 @@ describe('List view grid', () => {
screenshotName: 'list-view-grid-highlighted-row-xs'
});
});

it('should match the previous screenshot with multiselect enabled', (done) => {
SkyHostBrowser.scrollTo('#screenshot-list-view-grid-with-multiselect');

expect('#screenshot-list-view-grid-with-multiselect').toMatchBaselineScreenshot(done, {
screenshotName: 'list-view-grid-with-multiselect'
});
});

it('should match the previous screenshot with multiselect enabled (screen: xs)', (done) => {
SkyHostBrowser.setWindowBreakpoint('xs');
SkyHostBrowser.scrollTo('#screenshot-list-view-grid-with-multiselect');

expect('#screenshot-list-view-grid-with-multiselect').toMatchBaselineScreenshot(done, {
screenshotName: 'list-view-grid-with-multiselect-xs'
});
});
});
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@
"@skyux/grids": "^3.0.0",
"@skyux/i18n": "^3.5.0",
"@skyux/indicators": "^3.0.0",
"@skyux/list-builder": "^3.0.1",
"@skyux/list-builder": "^3.2.1",
"@skyux/list-builder-view-checklist": "^3.0.0",
"@skyux/list-builder-common": "^3.0.0",
"@skyux/modals": "^3.0.0",
"microedge-rxstate": ">=2.0.2"
},
"dependencies": {},
"devDependencies": {
"@blackbaud/skyux": "2.43.0",
"@blackbaud/skyux-builder": "1.32.1",
"@blackbaud/skyux": "2.45.0",
"@blackbaud/skyux-builder": "1.33.0",
"@skyux-sdk/builder-plugin-skyux": "1.0.0-rc.6"
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<sky-list-view-grid
fit="scroll"
selectionEnabled="true"
[enableMultiselect]="enableMultiselect"
[height]="400"
[hiddenColumns]="hiddenColumns"
[name]="gridview"
[rowHighlightedId]="rowHighlightedId"
[search]="searchFn"
[width]="600"
(multiselectSelectionChange)="multiselectSelectionChange($event)"
>

<sky-grid-column
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@ import {
ViewChildren
} from '@angular/core';

import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import {
BehaviorSubject
} from 'rxjs/BehaviorSubject';

import {
SkyGridSelectedRowsModelChange
} from '@skyux/grids';

import { SkyListViewGridComponent } from '../list-view-grid.component';
import {
SkyListViewGridComponent
} from '../list-view-grid.component';

@Component({
selector: 'sky-test-cmp',
Expand All @@ -32,6 +40,8 @@ export class ListViewGridTestComponent implements OnInit {

public rowHighlightedId: string;

public enableMultiselect = false;

public searchFn: (data: any, searchText: string) => boolean;

public showNgIfCol: boolean = false;
Expand All @@ -42,4 +52,8 @@ export class ListViewGridTestComponent implements OnInit {
this.asyncDescription.next('Column1 Description');
}, 100);
}

public multiselectSelectionChange(multiselectSelectionChange: SkyGridSelectedRowsModelChange) {
console.log(multiselectSelectionChange);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
<div *ngIf="active | async">
<sky-grid
#skyGrid
[height]="height | async"
[width]="width | async"
[fit]="fit"
[data]="items | async"
[selectedColumnIds]="selectedColumnIds | async"
[columns]="columns | async"
[data]="items | async"
[enableMultiselect]="enableMultiselect"
[fit]="fit"
[hasToolbar]="hasToolbar | async"
[height]="height | async"
[highlightText]="highlightSearchText ? (currentSearchText | async) : undefined"
[multiselectRowId]="multiselectRowId"
[rowHighlightedId]="rowHighlightedId"
(selectedColumnIdsChange)="columnIdsChanged($event)"
[selectedColumnIds]="selectedColumnIds | async"
[sortField]="sortField | async"
[width]="width | async"
(multiselectSelectionChange)="onMultiselectSelectionChange($event)"
(selectedColumnIdsChange)="columnIdsChanged($event)"
(sortFieldChange)="sortFieldChanged($event)"
>
#skyGrid
>
</sky-grid>
<sky-wait [isWaiting]="loading | async"></sky-wait>
<sky-wait
[isWaiting]="loading | async"
>
</sky-wait>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,19 @@ describe('List View Grid Component', () => {
component = fixture.componentInstance;
}));

function setupTest() {
function getSelectInputs() {
return element.queryAll(By.css('.sky-grid-multiselect-cell input'));
}

function clickSelectInputByIndex(id: number) {
const selectInputs = getSelectInputs();
selectInputs[id].nativeElement.click();
fixture.detectChanges();
}

function setupTest(enableMultiselect: boolean = false) {
component.enableMultiselect = enableMultiselect;

fixture.detectChanges();

let items = [
Expand Down Expand Up @@ -407,6 +419,44 @@ describe('List View Grid Component', () => {
});
});

describe('multiselect', () => {
it('should send action to the dispatcher when multiselect is enabled', fakeAsync(() => {
const spy = spyOn(dispatcher, 'toolbarShowMultiselectToolbar');

setupTest(true); // enable multiselect
flush();
tick(110); // wait for async heading
fixture.detectChanges();

expect(spy).toHaveBeenCalledWith(true);
}));

it('should send actions to the dispatcher on multiselectSelectionChange', fakeAsync(() => {
const spy = spyOn(dispatcher, 'setSelected').and.callThrough();

setupTest(true); // enable multiselect
flush();
tick(110); // wait for async heading
fixture.detectChanges();

// Select first row.
clickSelectInputByIndex(0);
fixture.detectChanges();

// Expect dispatcher to send action.
expect(spy).toHaveBeenCalledWith(['1'], true);

// Deselect first row.
spy.calls.reset();
flush();
clickSelectInputByIndex(0);
fixture.detectChanges();

// Expect dispatcher to send action.
expect(spy).toHaveBeenCalledWith(['1'], false);
}));
});

describe('nonstandard setup', () => {
it('should respect the hidden property when not hidden columns and displayed columns',
fakeAsync(() => {
Expand Down
86 changes: 84 additions & 2 deletions src/app/public/modules/list-view-grid/list-view-grid.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ import {
SkyGridColumnComponent,
SkyGridColumnHeadingModelChange,
SkyGridColumnDescriptionModelChange,
SkyGridColumnModel
SkyGridColumnModel,
SkyGridSelectedRowsModelChange
} from '@skyux/grids';

import {
ListSearchModel,
ListStateDispatcher,
ListState
ListState,
ListSelectedModel
} from '@skyux/list-builder/modules/list/state';

import {
Expand Down Expand Up @@ -103,6 +105,12 @@ export class SkyListViewGridComponent
@Input()
public rowHighlightedId: string;

@Input()
public enableMultiselect: boolean = false;

@Input()
public multiselectRowId: string;

@Output()
public selectedColumnIdsChange = new EventEmitter<Array<string>>();

Expand All @@ -121,6 +129,8 @@ export class SkyListViewGridComponent

public currentSearchText: Observable<string>;

private multiselectSelectedIds: string[] = [];

/* tslint:disable */
@Input('search')
private searchFunction: (data: any, searchText: string) => boolean;
Expand All @@ -141,6 +151,23 @@ export class SkyListViewGridComponent
}

public ngAfterContentInit() {

// Watch for selection changes and update multiselectSelectedIds for local comparison.
this.state.map(s => s.selected.item)
.takeUntil(this.ngUnsubscribe)
.distinctUntilChanged(this.selectedMapEqual)
.subscribe((items: ListSelectedModel) => {
const selectedIds: string[] = [];

items.selectedIdMap.forEach((isSelected, id) => {
Blackbaud-AlexKingman marked this conversation as resolved.
Show resolved Hide resolved
if (items.selectedIdMap.get(id) === true) {
selectedIds.push(id);
}
});

this.multiselectSelectedIds = selectedIds;
});

if (this.columnComponents.length === 0) {
throw new Error('Grid view requires at least one sky-grid-column to render.');
}
Expand Down Expand Up @@ -232,13 +259,38 @@ export class SkyListViewGridComponent
this.gridDispatcher.next(new ListViewGridColumnsLoadAction(columnModels, true));

this.handleColumnChange();

if (this.enableMultiselect) {
this.dispatcher.toolbarShowMultiselectToolbar(true);
}
}

public ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}

public onMultiselectSelectionChange(event: SkyGridSelectedRowsModelChange): void {
this.state.map(s => s.items.items)
.take(1)
.subscribe((items: ListItemModel[]) => {
const newItemIds = this.arrayIntersection(items.map(i => i.id), this.multiselectSelectedIds);
const newIds = items.filter(i => i.isSelected).map(i => i.id);

// Check for deselected ids & send message to dispatcher.
const deselectedIds = this.arrayDiff(newItemIds, newIds);
if (deselectedIds.length > 0) {
this.dispatcher.setSelected(deselectedIds, false);
}

// Check for selected ids & send message to dispatcher.
const selectedIds = this.arrayDiff(newIds, newItemIds);
if (selectedIds.length > 0) {
this.dispatcher.setSelected(selectedIds, true);
}
});
}

public columnIdsChanged(selectedColumnIds: Array<string>) {
this.selectedColumnIds
.take(1)
Expand Down Expand Up @@ -388,6 +440,36 @@ export class SkyListViewGridComponent
return true;
}

private selectedMapEqual(prev: ListSelectedModel, next: ListSelectedModel): boolean {
if (prev.selectedIdMap.size !== next.selectedIdMap.size) {
return false;
}

let keys: string[] = [];
next.selectedIdMap.forEach((value, key) => {
keys.push(key);
});

for (let i = 0; i < keys.length; i++) {
const key = keys[i];

const value = next.selectedIdMap.get(key);
if (value !== prev.selectedIdMap.get(key)) {
return false;
}
}

return true;
}

private arrayDiff(arrA: Array<any>, arrB: Array<any>): Array<any> {
return arrA.filter(i => arrB.indexOf(i) < 0);
}

private arrayIntersection(arrA: Array<any>, arrB: Array<any>): Array<any> {
return arrA.filter(value => -1 !== arrB.indexOf(value));
}

private arraysEqual(arrayA: any[], arrayB: any[]) {
return arrayA.length === arrayB.length &&
arrayA.every((value, index) =>
Expand Down
Loading