Skip to content

Commit

Permalink
barista(filter-field): Add partial option.
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-fischer committed May 15, 2020
1 parent 67caf42 commit eaef17e
Show file tree
Hide file tree
Showing 15 changed files with 220 additions and 10 deletions.
1 change: 1 addition & 0 deletions apps/dev/src/filter-field/filter-field-demo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[disabled]="_disabled"
label="Filter by"
clearAllLabel="Clear all"
(inputChange)="inputChange($event)"
(filterChanges)="filterChanges($event)"
(currentFilterChanges)="currentFilterChanges($event)"
></dt-filter-field>
Expand Down
28 changes: 27 additions & 1 deletion apps/dev/src/filter-field/filter-field-demo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ import {
import { COMPLEX_DATA } from './data';
import { KUBERNETES_DATA } from './kubernetes-data';
import { MULTIDIMENSIONAL_ANALYSIS } from './multidimensional-analysis';
import { TEST_DATA, TEST_DATA_ASYNC, TEST_DATA_ASYNC_2 } from './testdata';
import {
TEST_DATA,
TEST_DATA_ASYNC,
TEST_DATA_ASYNC_2,
TEST_DATA_PARTIAL,
TEST_DATA_PARTIAL_2,
} from './testdata';

// tslint:disable:no-any

Expand Down Expand Up @@ -130,6 +136,7 @@ export class FilterFieldDemo implements AfterViewInit, OnDestroy {
private _activeDataSourceName = 'TEST_DATA';
private _tagChangesSub = Subscription.EMPTY;
private _timerHandle: number;
private _partialFilter: boolean;
_firstTag: DtFilterFieldTag;

_dataSource = new DtFilterFieldDefaultDataSource<any>(TEST_DATA);
Expand Down Expand Up @@ -168,6 +175,25 @@ export class FilterFieldDemo implements AfterViewInit, OnDestroy {
this._timerHandle = setTimeout(() => {
this._dataSource.data = TEST_DATA_ASYNC_2;
}, 2000);
} else if (event.currentFilter[0] === TEST_DATA.autocomplete[4]) {
this._partialFilter = true;
// Simulate async data loading
this._timerHandle = setTimeout(() => {
this._dataSource.data = TEST_DATA_PARTIAL;
}, 2000);
}
}

inputChange(inputText: string): void {
// Cancel current timer if running
clearTimeout(this._timerHandle);

if (this._partialFilter) {
this.filterField.loading = true;
// Simulate partial data loading
this._timerHandle = setTimeout(() => {
this._dataSource.data = TEST_DATA_PARTIAL_2;
}, 1000);
}
}

Expand Down
27 changes: 27 additions & 0 deletions apps/dev/src/filter-field/testdata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export const TEST_DATA = {
distinct: true,
autocomplete: [],
},
{
name: 'CH (async, partial)',
async: true,
autocomplete: [],
},
{
name: 'Different Country',
suggestions: [{ name: 'IT' }, { name: 'ES' }, { name: 'UK' }],
Expand Down Expand Up @@ -105,3 +110,25 @@ export const TEST_DATA_ASYNC_2 = {
},
],
};

export const TEST_DATA_PARTIAL = {
name: 'CH (async, partial)',
autocomplete: [
{ name: 'Zürich' },
{ name: 'Genf' },
{ name: 'Basel' },
{ name: 'Bern' },
],
partial: true,
};

export const TEST_DATA_PARTIAL_2 = {
name: 'CH (async, partial)',
autocomplete: [
{ name: 'Zug' },
{ name: 'Schaffhausen' },
{ name: 'Luzern' },
{ name: 'St. Gallen' },
],
partial: true,
};
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export class DtQuickFilterDefaultDataSource<
[],
!!data.distinct,
!!data.async,
!!data.partial,
);
def.autocomplete!.optionsOrGroups = this.transformList(
data.autocomplete,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ import { buildData } from './reducer';

test('should build the data with an object', () => {
const data = { name: 'Linz' };
const autocomplete = dtAutocompleteDef(data, null, [], false, false);
const autocomplete = dtAutocompleteDef(data, null, [], false, false, false);
expect(buildData(autocomplete)).toMatchObject([data]);
});

test('should build the data with undefined', () => {
const data = undefined;
const autocomplete = dtAutocompleteDef(data, null, [], false, false);
const autocomplete = dtAutocompleteDef(data, null, [], false, false, false);

expect(buildData(autocomplete)).toMatchObject([data]);
});
9 changes: 9 additions & 0 deletions libs/barista-components/filter-field/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ data and apply it to the data source.

<ba-live-example name="DtExampleFilterFieldAsync"></ba-live-example>

## Server side filtered partial options

When loading data asynchronously, there might be the need to filter the options
on the server side, as there simply are too many options to return at once. You
can do this by setting the `partial: true` property on the autocomplete, when
replacing the datasource with the partially loaded data.

<ba-live-example name="DtExampleFilterFieldPartial"></ba-live-example>

## Unique free-text or range options

It is possible to set a free-text or range option to be unique. So it can only
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface DtFilterFieldDefaultDataSourceAutocomplete {
>;
distinct?: boolean;
async?: boolean;
partial?: boolean;
}

/** Shape of an object to be usable as a free text variant */
Expand Down Expand Up @@ -237,6 +238,7 @@ export class DtFilterFieldDefaultDataSource<
[],
!!data.distinct,
!!data.async,
!!data.partial,
);
def.autocomplete!.optionsOrGroups = this.transformList(
data.autocomplete,
Expand Down
25 changes: 22 additions & 3 deletions libs/barista-components/filter-field/src/filter-field-util.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ describe('DtFilterField Util', () => {
[optionDef],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
expect(
Expand All @@ -99,6 +100,7 @@ describe('DtFilterField Util', () => {
[optionDefInGroup],
false,
false,
false,
);
optionDefInGroup.option!.parentAutocomplete = autocompleteDef;
groupDef.group!.parentAutocomplete = autocompleteDef;
Expand All @@ -113,6 +115,7 @@ describe('DtFilterField Util', () => {
[optionDef, optionDefInGroup],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
optionDefInGroup.option!.parentAutocomplete = autocompleteDef;
Expand All @@ -128,6 +131,7 @@ describe('DtFilterField Util', () => {
[optionDef, optionDefInGroup],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
optionDefInGroup.option!.parentAutocomplete = autocompleteDef;
Expand All @@ -143,6 +147,7 @@ describe('DtFilterField Util', () => {
[optionDef],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
expect(
Expand All @@ -156,6 +161,7 @@ describe('DtFilterField Util', () => {
[optionDefInGroup],
false,
false,
false,
);
optionDefInGroup.option!.parentAutocomplete = autocompleteDef;
groupDef.group!.parentAutocomplete = autocompleteDef;
Expand All @@ -170,6 +176,7 @@ describe('DtFilterField Util', () => {
[optionDef, optionDefInGroup],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
optionDefInGroup.option!.parentAutocomplete = autocompleteDef;
Expand Down Expand Up @@ -382,6 +389,7 @@ describe('DtFilterField Util', () => {
[optionDef],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
expect(findDefForSource(optionSource, autocompleteDef)).toBe(optionDef);
Expand Down Expand Up @@ -416,6 +424,7 @@ describe('DtFilterField Util', () => {
[groupDef],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
optionDef.option!.parentGroup = groupDef;
Expand All @@ -441,6 +450,7 @@ describe('DtFilterField Util', () => {
[optionDef],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
expect(findDefForSource(optionSource, autocompleteDef)).toBe(optionDef);
Expand All @@ -463,6 +473,7 @@ describe('DtFilterField Util', () => {
[optionDef],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
expect(findDefForSource({}, autocompleteDef)).toBe(null);
Expand All @@ -488,6 +499,7 @@ describe('DtFilterField Util', () => {
[optionDef],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;

Expand Down Expand Up @@ -528,6 +540,7 @@ describe('DtFilterField Util', () => {
[groupDef],
false,
false,
false,
);
optionDef.option!.parentAutocomplete = autocompleteDef;
optionDef.option!.parentGroup = groupDef;
Expand Down Expand Up @@ -574,6 +587,7 @@ describe('DtFilterField Util', () => {
[innerOptionDef],
false,
false,
false,
);
innerOptionDef.option!.parentAutocomplete = outerOptionAutocompleteDef;
const rootAutocompleteDef = dtAutocompleteDef(
Expand All @@ -582,6 +596,7 @@ describe('DtFilterField Util', () => {
[outerOptionAutocompleteDef],
false,
false,
false,
);
outerOptionAutocompleteDef.option!.parentAutocomplete = rootAutocompleteDef;

Expand Down Expand Up @@ -627,6 +642,7 @@ describe('DtFilterField Util', () => {
[freeTextDef],
false,
false,
false,
);
freeTextDef.option!.parentAutocomplete = autocompleteDef;

Expand Down Expand Up @@ -668,6 +684,7 @@ describe('DtFilterField Util', () => {
[groupDef],
false,
false,
false,
);
freeTextDef.option!.parentGroup = groupDef;
freeTextDef.option!.parentAutocomplete = autocompleteDef;
Expand Down Expand Up @@ -779,6 +796,7 @@ describe('DtFilterField Util', () => {
[],
false,
false,
false,
);
expect(optionSelectedPredicate(optionDef, selectedIds, false)).toBe(
true,
Expand Down Expand Up @@ -806,6 +824,7 @@ describe('DtFilterField Util', () => {
[],
false,
false,
false,
);
expect(optionSelectedPredicate(optionDef, selectedIds, true)).toBe(
false,
Expand Down Expand Up @@ -945,21 +964,21 @@ describe('DtFilterField Util', () => {

describe('defDistinctPredicate', () => {
it('should return true if an autocomplete is async and also an option; it is not selected and not distinct', () => {
let def = dtAutocompleteDef({}, null, [], false, true);
let def = dtAutocompleteDef({}, null, [], false, true, false);
def = dtOptionDef({}, null, 'foo', 'id0', def, null);
const ids = new Set(['id1']);
expect(defDistinctPredicate(def, ids, false)).toBe(true);
});

it('should return true if an autocomplete is async and also an option; it is selected but not distinct', () => {
let def = dtAutocompleteDef({}, null, [], false, true);
let def = dtAutocompleteDef({}, null, [], false, true, false);
def = dtOptionDef({}, def, 'foo', 'id0', def, null);
const ids = new Set(['id0', 'id1']);
expect(defDistinctPredicate(def, ids, false)).toBe(true);
});

it('should return false if an autocomplete is async and also an option; it is selected and distinct', () => {
let def = dtAutocompleteDef({}, null, [], true, true);
let def = dtAutocompleteDef({}, null, [], true, true, false);
def = dtOptionDef({}, null, 'foo', 'id0', def, null);
const ids = new Set(['id0', 'id1']);
expect(defDistinctPredicate(def, ids, false)).toBe(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
isDtRangeDef,
_isDtRangeValue,
isDtRenderType,
isPartialDtAutocompleteDef,
} from './types';

/**
Expand All @@ -62,6 +63,7 @@ export function filterAutocompleteDef(
optionsOrGroups,
def.autocomplete!.distinct,
def.autocomplete!.async,
def.autocomplete!.partial,
)
: null;
}
Expand Down Expand Up @@ -359,7 +361,7 @@ export function findFilterValuesForSources<T>(
if (isLastSource) {
return null;
}
if (isAsyncDtAutocompleteDef(def)) {
if (isAsyncDtAutocompleteDef(def) || isPartialDtAutocompleteDef(def)) {
const asyncDef = asyncDefs.get(def);
if (asyncDef) {
parentDef = asyncDef;
Expand Down
7 changes: 6 additions & 1 deletion libs/barista-components/filter-field/src/filter-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ import {
isDtOptionDef,
isDtRangeDef,
_isDtRangeValue,
isPartialDtAutocompleteDef,
} from './types';

// tslint:disable:no-any
Expand Down Expand Up @@ -1078,7 +1079,11 @@ export class DtFilterField<T = any>
.pipe(takeUntil(this._destroy$))
.subscribe(
(def) => {
if (isAsyncDtAutocompleteDef(this._currentDef) && def) {
if (
(isAsyncDtAutocompleteDef(this._currentDef) ||
isPartialDtAutocompleteDef(this._currentDef)) &&
def
) {
this._asyncDefs.set(this._currentDef, def);
if (
def &&
Expand Down
Loading

0 comments on commit eaef17e

Please sign in to comment.