Skip to content

Commit 15c38a1

Browse files
committed
feat(datagrid): add built-in date filter
1 parent c8b7490 commit 15c38a1

15 files changed

+833
-16
lines changed

projects/angular/clarity.api.md

Lines changed: 67 additions & 11 deletions
Large diffs are not rendered by default.

projects/angular/src/data/datagrid/_datagrid.clarity.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,10 @@
691691
width: $clr_baselineRem_78px;
692692
}
693693

694+
.datagrid-date-filter-input {
695+
width: $clr_baselineRem_78px;
696+
}
697+
694698
.datagrid-filter-toggle {
695699
@include clr-no-styles-button();
696700
cursor: pointer;

projects/angular/src/data/datagrid/all.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
import DomAdapterSpecs from '../../utils/dom-adapter/dom-adapter.spec';
1515
import DatagridPropertyComparatorSpecs from './built-in/comparators/datagrid-property-comparator.spec';
16+
import DatagridDateFilterImplSpecs from './built-in/filters/datagrid-date-filter-impl.spec';
17+
import DatagridDateFilterSpecs from './built-in/filters/datagrid-date-filter.spec';
1618
import DatagridNumericFilterImplSpecs from './built-in/filters/datagrid-numeric-filter-impl.spec';
1719
import DatagridNumericFilterSpecs from './built-in/filters/datagrid-numeric-filter.spec';
1820
import DatagridPropertyNumericFilterSpecs from './built-in/filters/datagrid-property-numeric-filter.spec';
@@ -114,6 +116,9 @@ describe('Datagrid', function () {
114116
DatagridPropertyComparatorSpecs();
115117
DatagridPropertyStringFilterSpecs();
116118
DatagridPropertyNumericFilterSpecs();
119+
DatagridDateFilterSpecs();
120+
DatagridDateFilterImplSpecs();
121+
DatagridPropertyDateFilterSpecs();
117122
DatagridStringFilterSpecs();
118123
DatagridStringFilterImplSpecs();
119124
DatagridNumericFilterSpecs();
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright (c) 2016-2022 VMware, Inc. All Rights Reserved.
3+
* This software is released under MIT license.
4+
* The full license information can be found in LICENSE in the root directory of this project.
5+
*/
6+
7+
import { Observable } from 'rxjs';
8+
9+
import { ClrDatagridDateFilterInterface } from '../../interfaces/date-filter.interface';
10+
import { ClrDatagridFilterInterface } from '../../interfaces/filter.interface';
11+
import { DatagridDateFilterImpl } from './datagrid-date-filter-impl';
12+
import { DatagridPropertyDateFilter } from './datagrid-property-date-filter';
13+
14+
const yesterday = new Date(Date.now() - 24 * 1000 * 60 * 60);
15+
const today = new Date();
16+
const tomorrow = new Date(Date.now() + 24 * 1000 * 60 * 60);
17+
const dayAfterTomorrow = new Date(Date.now() + 2 * 24 * 1000 * 60 * 60);
18+
const threeDayFromNow = new Date(Date.now() + 3 * 24 * 1000 * 60 * 60);
19+
20+
export default function (): void {
21+
describe('DatagridDateFilterImpl', function () {
22+
let fullFilter: DatagridDateFilterImpl<Date>;
23+
24+
beforeEach(function () {
25+
const dateFilter = new TestFilter();
26+
fullFilter = new DatagridDateFilterImpl(dateFilter);
27+
});
28+
29+
it('becomes active when either from or to are set', function () {
30+
expect(fullFilter.isActive()).toBe(false);
31+
fullFilter.to = tomorrow;
32+
expect(fullFilter.isActive()).toBe(true);
33+
fullFilter.to = null;
34+
expect(fullFilter.isActive()).toBe(false);
35+
fullFilter.from = today;
36+
expect(fullFilter.isActive()).toBe(true);
37+
fullFilter.from = null;
38+
expect(fullFilter.isActive()).toBe(false);
39+
});
40+
41+
it('filters dates that are after the to date', function () {
42+
expect(fullFilter.accepts(new Date(Date.now() + 11_000))).toBe(true);
43+
fullFilter.to = tomorrow;
44+
expect(fullFilter.accepts(dayAfterTomorrow)).toBe(false);
45+
expect(fullFilter.accepts(today)).toBe(true);
46+
fullFilter.to = null;
47+
expect(fullFilter.accepts(dayAfterTomorrow)).toBe(true);
48+
});
49+
50+
it('filters dates that are before the from date', function () {
51+
expect(fullFilter.accepts(yesterday)).toBe(true);
52+
fullFilter.from = today;
53+
expect(fullFilter.accepts(yesterday)).toBe(false);
54+
expect(fullFilter.accepts(tomorrow)).toBe(true);
55+
fullFilter.from = null;
56+
expect(fullFilter.accepts(yesterday)).toBe(true);
57+
});
58+
59+
it('only allows values within the range when both filters are set', function () {
60+
fullFilter.from = today;
61+
fullFilter.to = dayAfterTomorrow;
62+
expect(fullFilter.accepts(yesterday)).toBe(false);
63+
expect(fullFilter.accepts(tomorrow)).toBe(true);
64+
expect(fullFilter.accepts(threeDayFromNow)).toBe(false);
65+
});
66+
67+
it('exposes state', function () {
68+
expect(fullFilter.state).toBe(fullFilter);
69+
});
70+
71+
it('compares filters', function () {
72+
let otherFilter: ClrDatagridFilterInterface<any> = fullFilter;
73+
expect(fullFilter.equals(otherFilter)).toBe(true);
74+
// Reference only comparison should be enough for the common case
75+
otherFilter = new DatagridDateFilterImpl(new TestFilter());
76+
expect(fullFilter.equals(otherFilter)).toBe(false);
77+
});
78+
79+
describe('with DatagridPropertyDateFilter', function () {
80+
beforeEach(function () {
81+
const propFilter = new DatagridPropertyDateFilter('a.b.c');
82+
fullFilter = new DatagridDateFilterImpl(propFilter);
83+
});
84+
85+
it('exposes state', function () {
86+
fullFilter.to = today;
87+
fullFilter.from = null;
88+
expect(fullFilter.state).toEqual({ property: 'a.b.c', to: today, from: null });
89+
});
90+
91+
it('compares filters', function () {
92+
let otherFilter: ClrDatagridFilterInterface<any> = fullFilter;
93+
expect(fullFilter.equals(otherFilter)).toBe(true);
94+
// In the specific case we can compare different filter instances
95+
otherFilter = new DatagridDateFilterImpl(new DatagridPropertyDateFilter('a.b.c'));
96+
expect(fullFilter.equals(otherFilter)).toBe(true);
97+
// Incompatible inner function type
98+
otherFilter = new DatagridDateFilterImpl(new TestFilter());
99+
expect(fullFilter.equals(otherFilter)).toBe(false);
100+
// Incompatible filter object
101+
otherFilter = new IncompatibleFilter();
102+
expect(fullFilter.equals(otherFilter)).toBe(false);
103+
});
104+
});
105+
});
106+
}
107+
108+
class TestFilter implements ClrDatagridDateFilterInterface<Date> {
109+
accepts(item: Date, from: Date, to: Date) {
110+
if (from !== null && item < from) {
111+
return false;
112+
}
113+
114+
if (to !== null && item > to) {
115+
return false;
116+
}
117+
return true;
118+
}
119+
}
120+
121+
class IncompatibleFilter implements ClrDatagridFilterInterface<Date> {
122+
// eslint-disable-next-line
123+
accepts(_item: Date): boolean {
124+
return true;
125+
}
126+
127+
changes: Observable<any>;
128+
129+
isActive(): boolean {
130+
return true;
131+
}
132+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright (c) 2016-2022 VMware, Inc. All Rights Reserved.
3+
* This software is released under MIT license.
4+
* The full license information can be found in LICENSE in the root directory of this project.
5+
*/
6+
7+
import { Observable, Subject } from 'rxjs';
8+
9+
import { ClrDatagridDateFilterInterface } from '../../interfaces/date-filter.interface';
10+
import { ClrDatagridFilterInterface } from '../../interfaces/filter.interface';
11+
import { DatagridPropertyDateFilter } from './datagrid-property-date-filter';
12+
13+
export class DatagridDateFilterImpl<T = any> implements ClrDatagridFilterInterface<T> {
14+
constructor(public filterFn: ClrDatagridDateFilterInterface<T>) {}
15+
16+
/**
17+
* The Observable required as part of the Filter interface
18+
*/
19+
private _changes = new Subject<[Date, Date]>();
20+
// We do not want to expose the Subject itself, but the Observable which is read-only
21+
public get changes(): Observable<[Date, Date]> {
22+
return this._changes.asObservable();
23+
}
24+
25+
/**
26+
* Internal values and accessor
27+
*/
28+
private _from: Date | null = null;
29+
private _to: Date | null = null;
30+
31+
/**
32+
* Common setters for the input values, including individual limits and
33+
* both at the same time. Value is singular to make the interface similar
34+
* to the built-in string filter.
35+
*/
36+
37+
public get value(): [Date, Date] {
38+
return [this._from, this._to];
39+
}
40+
41+
public set value(vals: [Date, Date]) {
42+
const from = vals[0];
43+
const to = vals[1];
44+
if (from !== this._from || to !== this._to) {
45+
this._from = from;
46+
this._to = to;
47+
this._changes.next([this._from, this._to]);
48+
}
49+
}
50+
51+
public get from() {
52+
return this._from;
53+
}
54+
public set from(from: Date) {
55+
if (from !== this._from) {
56+
this._from = from;
57+
this._changes.next([this._from, this._to]);
58+
}
59+
}
60+
61+
public get to() {
62+
return this._to;
63+
}
64+
public set to(to: Date) {
65+
if (to !== this._to) {
66+
this._to = to;
67+
this._changes.next([this._from, this._to]);
68+
}
69+
}
70+
71+
/**
72+
* Indicates if the filter is currently active, (at least one input is set)
73+
*/
74+
public isActive(): boolean {
75+
return this._from !== null || this._to !== null;
76+
}
77+
78+
accepts(item: T): boolean {
79+
return this.filterFn.accepts(item, this._from, this._to);
80+
}
81+
82+
public clear(): void {
83+
this._from = null;
84+
this._to = null;
85+
}
86+
87+
public get state() {
88+
if (this.filterFn instanceof DatagridPropertyDateFilter) {
89+
return {
90+
property: this.filterFn.prop,
91+
from: this._from,
92+
to: this._to,
93+
};
94+
}
95+
return this;
96+
}
97+
98+
public equals(other: ClrDatagridFilterInterface<T, any>): boolean {
99+
if (other instanceof DatagridDateFilterImpl) {
100+
if (other.filterFn instanceof DatagridPropertyDateFilter) {
101+
return (
102+
this.filterFn instanceof DatagridPropertyDateFilter &&
103+
other.filterFn.prop === this.filterFn.prop &&
104+
other.from === this._from &&
105+
other.to === this._to
106+
);
107+
}
108+
return other === this;
109+
}
110+
return false;
111+
}
112+
}

0 commit comments

Comments
 (0)