From 1679135bc406b2c97986fbd8013d07caab1b5623 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 3 Mar 2017 20:19:43 -0500 Subject: [PATCH] fix(filter): Refactor filter and toolbar (consumer) to accommodate the Planner UI --- config/webpack.demo.js | 13 +- index.ts | 1 + .../examples/filter-example.component.ts | 145 +++++++------ src/app/filters/filter-event.ts | 5 +- src/app/filters/filter-field.ts | 14 +- src/app/filters/filter-fields.component.html | 19 +- src/app/filters/filter-fields.component.ts | 12 +- src/app/filters/filter-query.ts | 10 + src/app/filters/filter-results.component.html | 6 +- src/app/filters/filter-results.component.ts | 4 +- src/app/filters/filter.component.spec.ts | 137 +++++++----- src/app/filters/filter.component.ts | 10 +- src/app/filters/filter.ts | 13 +- src/app/filters/filters.module.ts | 4 +- .../examples/toolbar-example.component.ts | 142 +++++++------ src/app/toolbar/toolbar.component.html | 49 ++--- src/app/toolbar/toolbar.component.scss | 6 + src/app/toolbar/toolbar.component.spec.ts | 196 +++++++++--------- src/app/toolbar/toolbar.component.ts | 13 +- 19 files changed, 454 insertions(+), 345 deletions(-) create mode 100644 src/app/filters/filter-query.ts diff --git a/config/webpack.demo.js b/config/webpack.demo.js index acbbe16..e2adeac 100644 --- a/config/webpack.demo.js +++ b/config/webpack.demo.js @@ -35,13 +35,7 @@ module.exports = { }, resolve: { - extensions: ['.webpack.js', '.wep.js', '.js', '.ts'], - plugins: [ - // Todo: config is not loading. - new TsConfigPathsPlugin({ - configFileName: helpers.root("tsconfig-demo.json") - }) - ] + extensions: ['.webpack.js', '.wep.js', '.js', '.ts'] }, stats: { @@ -124,6 +118,11 @@ module.exports = { */ new NamedModulesPlugin(), + // Todo: config is not loading. + new TsConfigPathsPlugin({ + configFileName: helpers.root("tsconfig-demo.json") + }), + /** * Plugin: ContextReplacementPlugin * Description: Provides context to Angular's use of System.import diff --git a/index.ts b/index.ts index ea0b223..0cb8972 100644 --- a/index.ts +++ b/index.ts @@ -31,6 +31,7 @@ export { FilterEvent } from './src/app/filters/filter-event'; export { FilterField } from './src/app/filters/filter-field'; export { FilterFieldsComponent } from './src/app/filters/filter-fields.component'; export { FilterResultsComponent } from './src/app/filters/filter-results.component'; +export { FilterQuery } from './src/app/filters/filter-query'; // Notification export { ToastNotificationComponent } from './src/app/notification/toast-notification.component'; diff --git a/src/app/filters/examples/filter-example.component.ts b/src/app/filters/examples/filter-example.component.ts index 974877c..ca6ab63 100644 --- a/src/app/filters/examples/filter-example.component.ts +++ b/src/app/filters/examples/filter-example.component.ts @@ -26,63 +26,92 @@ export class FilterExampleComponent implements OnInit { } ngOnInit(): void { - this.allItems = [ - { - name: "Fred Flintstone", - address: "20 Dinosaur Way, Bedrock, Washingstone", - birthMonth: 'February' - }, - { - name: "John Smith", - address: "415 East Main Street, Norfolk, Virginia", - birthMonth: 'October' - }, - { - name: "Frank Livingston", - address: "234 Elm Street, Pittsburgh, Pennsylvania", - birthMonth: 'March' - }, - { - name: "Judy Green", - address: "2 Apple Boulevard, Cincinatti, Ohio", - birthMonth: 'December' - }, - { - name: "Pat Thomas", - address: "50 Second Street, New York, New York", - birthMonth: 'February' - } - ]; + this.allItems = [{ + name: "Fred Flintstone", + address: "20 Dinosaur Way, Bedrock, Washingstone", + birthMonth: 'February', + birthMonthId: 'month2' + },{ + name: "John Smith", address: "415 East Main Street, Norfolk, Virginia", + birthMonth: 'October', + birthMonthId: '10' + },{ + name: "Frank Livingston", + address: "234 Elm Street, Pittsburgh, Pennsylvania", + birthMonth: 'March', + birthMonthId: 'month3' + },{ + name: "Judy Green", + address: "2 Apple Boulevard, Cincinatti, Ohio", + birthMonth: 'December', + birthMonthId: 'month12' + },{ + name: "Pat Thomas", + address: "50 Second Street, New York, New York", + birthMonth: 'February', + birthMonthId: 'month2' + }]; this.items = this.allItems; this.filterConfig = { - fields: [ - { - id: 'name', - title: 'Name', - placeholder: 'Filter by Name...', - filterType: 'text' - }, - { - id: 'age', - title: 'Age', - placeholder: 'Filter by Age...', - filterType: 'text' - }, - { - id: 'address', - title: 'Address', - placeholder: 'Filter by Address...', - filterType: 'text' - }, - { - id: 'birthMonth', - title: 'Birth Month', - placeholder: 'Filter by Birth Month...', - filterType: 'select', - filterValues: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] - } - ] as FilterField[], + fields: [{ + id: 'name', + title: 'Name', + placeholder: 'Filter by Name...', + type: 'text' + },{ + id: 'age', + title: 'Age', + placeholder: 'Filter by Age...', + type: 'text' + },{ + id: 'address', + title: 'Address', + placeholder: 'Filter by Address...', + type: 'text' + },{ + id: 'birthMonth', + title: 'Birth Month', + placeholder: 'Filter by Birth Month...', + type: 'select', + queries: [{ + id: 'month1', + value: 'January' + },{ + id: 'month2', + value: 'February' + },{ + id: 'month3', + value: 'March' + },{ + id: 'month4', + value: 'April' + },{ + id: 'month5', + value: 'May' + },{ + id: 'month6', + value: 'June' + },{ + id: 'month7', + value: 'July' + },{ + id: 'month8', + value: 'August' + },{ + id: 'month9', + value: 'September' + },{ + id: 'month10', + value: 'October' + },{ + id: 'month11', + value: 'November' + },{ + id: 'month12', + value: 'December' + }] + }] as FilterField[], resultsCount: this.items.length, appliedFilters: [] } as FilterConfig; @@ -107,7 +136,7 @@ export class FilterExampleComponent implements OnInit { filterChange($event: FilterEvent): void { this.filtersText = ""; $event.appliedFilters.forEach((filter) => { - this.filtersText += filter.title + " : " + filter.value + "\n"; + this.filtersText += filter.field.title + " : " + filter.value + "\n"; }); this.applyFilters($event.appliedFilters); } @@ -115,11 +144,11 @@ export class FilterExampleComponent implements OnInit { matchesFilter(item: any, filter: Filter): boolean { let match = true; - if (filter.id === 'name') { + if (filter.field.id === 'name') { match = item.name.match(filter.value) !== null; - } else if (filter.id === 'address') { + } else if (filter.field.id === 'address') { match = item.address.match(filter.value) !== null; - } else if (filter.id === 'birthMonth') { + } else if (filter.field.id === 'birthMonth') { match = item.birthMonth === filter.value; } return match; diff --git a/src/app/filters/filter-event.ts b/src/app/filters/filter-event.ts index 86de6e9..162b7da 100644 --- a/src/app/filters/filter-event.ts +++ b/src/app/filters/filter-event.ts @@ -1,15 +1,18 @@ import { Filter } from './filter'; import { FilterField } from './filter-field'; +import { FilterQuery } from './filter-query'; /* * A filter event containing: * * appliedFilters - List of the currently applied filters * field - A filterable field + * query - A filterable query * value - The filter input field value */ export class FilterEvent { appliedFilters?: Filter[]; - field?: FilterField; + field: FilterField; + query: FilterQuery; value?: string; } diff --git a/src/app/filters/filter-field.ts b/src/app/filters/filter-field.ts index 4fe3679..7c6c69d 100644 --- a/src/app/filters/filter-field.ts +++ b/src/app/filters/filter-field.ts @@ -1,16 +1,18 @@ +import { FilterQuery } from './filter-query'; + /* * A filterable field containing: * * id - Optional unique Id for the filter field, useful for comparisons - * title - The title to display for the filter field * placeholder - Optional text to display when no filter value has been entered - * filterType - The filter input field type (any html input type, or 'select' for a select box) - * filterValues - Optional list of values used when filterType is 'select' + * queries - Optional list of filter queries used when filterType is 'select' + * title - The title to display for the filter field + * type - The filter input field type (any html input type, or 'select' for a select box) */ export class FilterField { id?: string; - title: string; placeholder?: string; - filterType: string; - filterValues?: string[]; + queries?: FilterQuery[]; + title: string; + type: string; } diff --git a/src/app/filters/filter-fields.component.html b/src/app/filters/filter-fields.component.html index 459dd9a..b302a9d 100644 --- a/src/app/filters/filter-fields.component.html +++ b/src/app/filters/filter-fields.component.html @@ -6,19 +6,19 @@ -
- +
-
+
+
+ + +
-
-
- +
+ + + +
- +
diff --git a/src/app/toolbar/toolbar.component.scss b/src/app/toolbar/toolbar.component.scss index b200862..5bff269 100644 --- a/src/app/toolbar/toolbar.component.scss +++ b/src/app/toolbar/toolbar.component.scss @@ -6,6 +6,9 @@ } } .toolbar-pf-actions { + .btn { + min-width: unset; + } .toolbar-pf-view-selector { a { cursor: pointer; @@ -19,6 +22,9 @@ .dropdown-kebab-pf { float: right; } + .toolbar-apf-filter { + padding-left: 0 !important; + } } .toolbar-pf-include-actions { display: inline-block; diff --git a/src/app/toolbar/toolbar.component.spec.ts b/src/app/toolbar/toolbar.component.spec.ts index 673e9c5..dd66555 100644 --- a/src/app/toolbar/toolbar.component.spec.ts +++ b/src/app/toolbar/toolbar.component.spec.ts @@ -80,38 +80,68 @@ describe('Toolbar component - ', () => { name: 'Grouped Action 2', title: 'Do something similar' } - ], - actionsInclude: true + ] } as ActionsConfig, filterConfig: { - fields: [ - { - id: 'name', - title: 'Name', - placeholder: 'Filter by Name...', - filterType: 'text' - }, - { - id: 'age', - title: 'Age', - placeholder: 'Filter by Age...', - filterType: 'text' - }, - { - id: 'address', - title: 'Address', - placeholder: 'Filter by Address...', - filterType: 'text' - }, - { - id: 'birthMonth', - title: 'Birth Month', - placeholder: 'Filter by Birth Month...', - filterType: 'select', - filterValues: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] - } - ] as FilterField[], + fields: [{ + id: 'name', + title: 'Name', + placeholder: 'Filter by Name...', + type: 'text' + },{ + id: 'age', + title: 'Age', + placeholder: 'Filter by Age...', + type: 'text' + },{ + id: 'address', + title: 'Address', + placeholder: 'Filter by Address...', + type: 'text' + },{ + id: 'birthMonth', + title: 'Birth Month', + placeholder: 'Filter by Birth Month...', + type: 'select', + queries: [{ + id: 'month1', + value: 'January' + },{ + id: 'month2', + value: 'February' + },{ + id: 'month3', + value: 'March' + },{ + id: 'month4', + value: 'April' + },{ + id: 'month5', + value: 'May' + },{ + id: 'month6', + value: 'June' + },{ + id: 'month7', + value: 'July' + },{ + id: 'month8', + value: 'August' + },{ + id: 'month9', + value: 'September' + },{ + id: 'month10', + value: 'October' + },{ + id: 'month11', + value: 'November' + },{ + id: 'month12', + value: 'December' + }] + }] as FilterField[], resultsCount: 5, appliedFilters: [] } as FilterConfig, @@ -183,9 +213,15 @@ describe('Toolbar component - ', () => { it('should have correct number of results', function () { let results = fixture.debugElement.query(By.css('h5')); - expect(results).not.toBeNull(); - expect(results.nativeElement.textContent.trim().slice(0, '5 Results'.length)).toBe('5 Results'); + expect(results).toBeNull(); + config.filterConfig.appliedFilters = [{ + field: { + id: 'address', + title: 'Address' + }, + value: 'New York' + }] as Filter[]; config.filterConfig.resultsCount = 10; fixture.detectChanges(); @@ -206,20 +242,20 @@ describe('Toolbar component - ', () => { expect(filterSelect).not.toBeNull(); let items = filterSelect.queryAll(By.css('li')); - expect(items.length).toBe(config.filterConfig.fields[3].filterValues.length + 1); // +1 for the null value + expect(items.length).toBe(config.filterConfig.fields[3].queries.length + 1); // +1 for the null value }); it ('should clear a filter when the close button is clicked', function () { let closeButtons = fixture.debugElement.queryAll(By.css('.pficon-close')); expect(closeButtons.length).toBe(0); - config.filterConfig.appliedFilters = [ - { + config.filterConfig.appliedFilters = [{ + field: { id: 'address', - title: 'Address', - value: 'New York' - } - ] as Filter[]; + title: 'Address' + }, + value: 'New York' + }] as Filter[]; fixture.detectChanges(); closeButtons = fixture.debugElement.queryAll(By.css('.pficon-close')); @@ -238,13 +274,13 @@ describe('Toolbar component - ', () => { expect(activeFilters.length).toBe(0); expect(clearButton).toBeNull(); - config.filterConfig.appliedFilters = [ - { + config.filterConfig.appliedFilters = [{ + field: { id: 'address', - title: 'Address', - value: 'New York' - } - ] as Filter[]; + title: 'Address' + }, + value: 'New York' + }] as Filter[]; fixture.detectChanges(); activeFilters = fixture.debugElement.queryAll(By.css('.active-filter')); @@ -263,25 +299,9 @@ describe('Toolbar component - ', () => { it ('should not show filters when a filter config is not supplied', function () { let filter = fixture.debugElement.queryAll(By.css('.filter-pf')); - expect(filter.length).toBe(2); - - config = { - viewsConfig: { - views: [ - { - id: 'listView', - title: 'List View', - iconClass: 'fa fa-th-list' - }, - { - id: 'tableView', - title: 'Table View', - iconClass: 'fa fa-table' - } - ], - } as ViewsConfig - } as ToolbarConfig; + expect(filter.length).toBe(1); + config.filterConfig = undefined; comp.config = config; fixture.detectChanges(); @@ -386,27 +406,11 @@ describe('Toolbar component - ', () => { fixture.detectChanges(); }); - it ('should not show filters when a filter config is not supplied', function () { + it ('should not show sort when a sort config is not supplied', function () { let sort = fixture.debugElement.query(By.css('.sort-pf')); expect(sort).not.toBeNull(); - config = { - viewsConfig: { - views: [ - { - id: 'listView', - title: 'List View', - iconClass: 'fa fa-th-list' - }, - { - id: 'tableView', - title: 'Table View', - iconClass: 'fa fa-table' - } - ], - } as ViewsConfig - } as ToolbarConfig; - + config.sortConfig = undefined; comp.config = config; fixture.detectChanges(); @@ -562,32 +566,20 @@ describe('Toolbar component - ', () => { expect(action).toBeNull(); }); - it ('should not show action components when an action config is not supplied', function () { - let actionBar = fixture.debugElement.query(By.css('.toolbar-pf-actions .toolbar-actions')); - expect(actionBar).not.toBeNull(); - - config = { - viewsConfig: { - views: [ - { - id: 'listView', - title: 'List View', - iconClass: 'fa fa-th-list' - }, - { - id: 'tableView', - title: 'Table View', - iconClass: 'fa fa-table' - } - ], - } as ViewsConfig - } as ToolbarConfig; + it('should not show action components when an action config is not supplied', function () { + let primaryActions = fixture.debugElement.queryAll(By.css('.toolbar-pf-actions .primary-action')); + let moreActions = fixture.debugElement.queryAll(By.css('.toolbar-pf-actions .secondary-action')); + expect(primaryActions.length).toBe(2); + expect(moreActions.length).toBe(6); + config.actionsConfig = undefined; comp.config = config; fixture.detectChanges(); - actionBar = fixture.debugElement.query(By.css('.toolbar-pf-actions .toolbar-actions')); - expect(actionBar).toBeNull(); + primaryActions = fixture.debugElement.queryAll(By.css('.toolbar-pf-actions .primary-action')); + moreActions = fixture.debugElement.queryAll(By.css('.toolbar-pf-actions .secondary-action')); + expect(primaryActions.length).toBe(0); + expect(moreActions.length).toBe(0); }); /* Todo diff --git a/src/app/toolbar/toolbar.component.ts b/src/app/toolbar/toolbar.component.ts index 8e3de99..ba43502 100644 --- a/src/app/toolbar/toolbar.component.ts +++ b/src/app/toolbar/toolbar.component.ts @@ -29,6 +29,7 @@ import * as _ from 'lodash'; export class ToolbarComponent implements OnInit { @Input() config: ToolbarConfig; @Input() actionsTemplate: TemplateRef; + @Input() viewsTemplate: TemplateRef; @Output('onActionSelect') onActionSelect = new EventEmitter(); @Output('onFilterChange') onFilterChange = new EventEmitter(); @@ -87,14 +88,13 @@ export class ToolbarComponent implements OnInit { addFilter($event: FilterEvent): void { let newFilter = { - id: $event.field.id, - title: $event.field.title, - type: $event.field.filterType, + field: $event.field, + query: $event.query, value: $event.value } as Filter; if (!this.filterExists(newFilter)) { - if (newFilter.type === 'select') { + if (newFilter.field.type === 'select') { this.enforceSingleSelect(newFilter); } this.config.filterConfig.appliedFilters.push(newFilter); @@ -104,12 +104,13 @@ export class ToolbarComponent implements OnInit { } enforceSingleSelect(filter: Filter): void { - _.remove(this.config.filterConfig.appliedFilters, {title: filter.title}); + _.remove(this.config.filterConfig.appliedFilters, {title: filter.field.title}); } filterExists(filter: Filter): boolean { let foundFilter = _.find(this.config.filterConfig.appliedFilters, { - title: filter.title, + field: filter.field, + query: filter.query, value: filter.value }); return foundFilter !== undefined;