Skip to content
This repository has been archived by the owner on Jan 24, 2023. It is now read-only.

Commit

Permalink
Port downstream list filter changes
Browse files Browse the repository at this point in the history
- SUSE#185
- Apply to feature flag table
  • Loading branch information
richard-cox committed Oct 11, 2019
1 parent cfea99e commit d8559de
Show file tree
Hide file tree
Showing 15 changed files with 174 additions and 25 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"ng": "ng",
"start": "npm run customize && ng serve",
"start-high-mem": "npm run customize && node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng serve",
"start-dev-high-mem": "npm run customize && node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng serve --aot=false",
"start-prod-high-mem": "npm run customize && node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng serve --prod",
"start-dev": "ng serve --aot=false",
"test": "run-s test-frontend:* --continue-on-error",
Expand Down Expand Up @@ -144,4 +145,4 @@
"tslint": "~5.13.0",
"typescript": "~3.1.6"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ListDataSource,
} from '../../../../../../../core/src/shared/components/list/data-sources-controllers/list-data-source';
import { IListConfig } from '../../../../../../../core/src/shared/components/list/list.component.types';
import { PaginationEntityState } from '../../../../../../../store/src/types/pagination.types';
import { cfEntityFactory } from '../../../../../cf-entity-factory';
import { createCfFeatureFlagFetchAction } from './cf-feature-flags-data-source.helpers';

Expand All @@ -28,6 +29,9 @@ export const FeatureFlagDescriptions = {
service_instance_sharing: 'Org and Space Managers can allow service instances to be shared across different spaces.'
};
export class CfFeatureFlagsDataSource extends ListDataSource<IFeatureFlag> {
static nameColumnId = 'name';
static descriptionColumnId = 'description';

constructor(store: Store<CFAppState>, cfGuid: string, listConfig?: IListConfig<IFeatureFlag>) {
const action = createCfFeatureFlagFetchAction(cfGuid);
super({
Expand All @@ -37,7 +41,32 @@ export class CfFeatureFlagsDataSource extends ListDataSource<IFeatureFlag> {
getRowUniqueId: (ff) => ff.guid,
paginationKey: action.paginationKey,
isLocal: true,
transformEntities: [{ type: 'filter', field: 'name' }],
transformEntities: [
((entities: IFeatureFlag[], paginationState: PaginationEntityState) => {
if (!paginationState.clientPagination.filter.string) {
return entities;
}

const filterString = paginationState.clientPagination.filter.string.toUpperCase();

const filterKey = paginationState.clientPagination.filter.filterKey;

switch (filterKey) {
case CfFeatureFlagsDataSource.nameColumnId:
return entities.filter(ff => ff.name.toUpperCase().includes(filterString));
case CfFeatureFlagsDataSource.descriptionColumnId:
return entities.filter(ff => {
const description = FeatureFlagDescriptions[ff.name];
if (!description) {
return false;
}
return description.toUpperCase().includes(filterString);
});
default:
return entities;
}
})
],
listConfig
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@ import { Store } from '@ngrx/store';
import { CFAppState } from '../../../../../../../cloud-foundry/src/cf-app-state';
import { IFeatureFlag } from '../../../../../../../core/src/core/cf-api.types';
import { ITableColumn } from '../../../../../../../core/src/shared/components/list/list-table/table.types';
import { ListViewTypes } from '../../../../../../../core/src/shared/components/list/list.component.types';
import { IListFilter, ListViewTypes } from '../../../../../../../core/src/shared/components/list/list.component.types';
import { ListView } from '../../../../../../../store/src/actions/list.actions';
import { APIResource } from '../../../../../../../store/src/types/api.types';
import { ActiveRouteCfOrgSpace } from '../../../../../features/cloud-foundry/cf-page.types';
import { BaseCfListConfig } from '../base-cf/base-cf-list-config';
import { CfFeatureFlagsDataSource, FeatureFlagDescriptions } from './cf-feature-flags-data-source';
import { TableCellFeatureFlagStateComponent } from './table-cell-feature-flag-state/table-cell-feature-flag-state.component';

@Injectable()
export class CfFeatureFlagsListConfigService extends BaseCfListConfig<IFeatureFlag> {

constructor(private store: Store<CFAppState>, activeRouteCfOrgSpace: ActiveRouteCfOrgSpace) {
super();
this.dataSource = new CfFeatureFlagsDataSource(this.store, activeRouteCfOrgSpace.cfGuid, this);
}

dataSource: CfFeatureFlagsDataSource;
defaultView = 'table' as ListView;
pageSizeOptions = [25, 50, 100];
Expand All @@ -27,7 +32,7 @@ export class CfFeatureFlagsListConfigService extends BaseCfListConfig<IFeatureFl

columns: Array<ITableColumn<IFeatureFlag>> = [
{
columnId: 'name',
columnId: CfFeatureFlagsDataSource.nameColumnId,
headerCell: () => 'Name',
cellDefinition: {
getValue: (row) => `${row.name}`
Expand All @@ -41,8 +46,8 @@ export class CfFeatureFlagsListConfigService extends BaseCfListConfig<IFeatureFl
}
},
{
columnId: 'description',
headerCell: () => 'Description',
columnId: CfFeatureFlagsDataSource.descriptionColumnId,
headerCell: () => 'Descriptionaaa',
cellDefinition: {
getValue: (row) => FeatureFlagDescriptions[row.name]
},
Expand All @@ -61,10 +66,22 @@ export class CfFeatureFlagsListConfigService extends BaseCfListConfig<IFeatureFl
cellFlex: '1'
}
];
constructor(private store: Store<CFAppState>, private activeRouteCfOrgSpace: ActiveRouteCfOrgSpace) {
super();
this.dataSource = new CfFeatureFlagsDataSource(this.store, activeRouteCfOrgSpace.cfGuid, this);
}

filters: IListFilter[] = [
{
default: true,
key: CfFeatureFlagsDataSource.nameColumnId,
label: 'Name',
placeholder: 'Filter by Name'
},
{
key: CfFeatureFlagsDataSource.descriptionColumnId,
label: 'Description',
placeholder: 'Filter by Description'
}
];

getFilters = () => this.filters;
getColumns = () => this.columns;
getDataSource = () => this.dataSource;
}
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,8 @@ export abstract class ListDataSource<T, A = T> extends DataSource<T> implements
return this.pagination$.pipe(
map(pag => ({
string: this.isLocal ? pag.clientPagination.filter.string : this.getFilterFromParams(pag),
items: { ...pag.clientPagination.filter.items }
items: { ...pag.clientPagination.filter.items },
filterKey: pag.clientPagination.filter.filterKey,
})),
tag('list-filter')
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export class LocalListController<T = any> {
+ (paginationEntity.params['order-direction-field'] as string || '') + ','
+ (paginationEntity.params['order-direction'] as string || '') + ','
+ paginationEntity.clientPagination.filter.string + ','
+ paginationEntity.clientPagination.filter.filterKey + ','
+ paginationEntity.forcedLocalPage
+ Object.values(paginationEntity.clientPagination.filter.items);
// Some outlier cases actually fetch independently from this list (looking at you app variables)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@
<div class="list-component__header__left--metrics-range"
*ngIf="config.showCustomTime && !(isAddingOrSelecting$ | async)">
<app-metrics-range-selector [baseAction]="config.getDataSource().masterAction"
(metricsAction)="config.getDataSource().updateMetricsAction($event)"
[times]="config.customTimeWindows"
[selectedTimeValue]="config.customTimeInitialValue"
[pollInterval]="config.customTimePollingInterval"
(metricsAction)="config.getDataSource().updateMetricsAction($event)" [times]="config.customTimeWindows"
[selectedTimeValue]="config.customTimeInitialValue" [pollInterval]="config.customTimePollingInterval"
[validate]="config.customTimeValidation">
</app-metrics-range-selector>
</div>
Expand Down Expand Up @@ -71,12 +69,23 @@
</mat-form-field>
</div>
<!-- Filter by text input -->
<div class="filter" id="listFilterSelect"
[hidden]="filterColumns.length < 1 || !config.enableTextFilter || (!(hasRows$ | async) && !filter) || (dataSource.isAdding$ | async) || (dataSource.maxedResults$ | async)">
<mat-form-field>
<mat-label>Filter Selection</mat-label>
<mat-select [(value)]="filterSelected" (selectionChange)="updateListFilter($event.value)">
<mat-option *ngFor="let filter of filterColumns" [value]="filter">
{{ filter.label }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="filter" id="listSearchFilter"
[hidden]="!config.enableTextFilter || (!(hasRows$ | async) && !filter) || (dataSource.isAdding$ | async) || (dataSource.maxedResults$ | async)">
<mat-form-field floatLabel="never" class="list-component__header__right-filter">
<input matInput [ngModel]="filterString" #filter="ngModel"
[disabled]="(dataSource.isLoadingPage$ | async)" name="filter"
placeholder="{{config.text?.filter || 'Filter'}}">
[disabled]="(filterColumns.length > 1 && filterSelected === undefined) || (dataSource.isLoadingPage$ | async)"
name="filter" placeholder="{{ filterSelected?.placeholder || config.text?.filter || 'Filter'}}">
</mat-form-field>
</div>
<!-- Sort Button & Drop down -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BehaviorSubject, of as observableOf } from 'rxjs';
import { switchMap } from 'rxjs/operators';

import { ListView } from '../../../../../store/src/actions/list.actions';
import { GeneralAppState } from '../../../../../store/src/app-state';
import { APIResource } from '../../../../../store/src/types/api.types';
import { EndpointModel } from '../../../../../store/src/types/endpoint.types';
import { CoreTestingModule } from '../../../../test-framework/core-test.modules';
Expand All @@ -20,7 +21,6 @@ import { EndpointListHelper } from './list-types/endpoint/endpoint-list.helpers'
import { EndpointsListConfigService } from './list-types/endpoint/endpoints-list-config.service';
import { ListComponent } from './list.component';
import { ListConfig, ListViewTypes } from './list.component.types';
import { InternalAppState, GeneralAppState } from '../../../../../store/src/app-state';

class MockedNgZone {
run = fn => fn();
Expand All @@ -43,6 +43,7 @@ describe('ListComponent', () => {
getInitialised: () => null,
getMultiActions: () => null,
getMultiFiltersConfigs: () => null,
getFilters: () => null,
getSingleActions: () => null,
isLocal: false,
pageSizeOptions: [1],
Expand Down Expand Up @@ -153,6 +154,7 @@ describe('ListComponent', () => {
describe('Header', () => {
it('Nothing enabled', () => {
component.config.getMultiFiltersConfigs = () => [];
component.config.getFilters = () => [];
component.config.enableTextFilter = false;
component.config.viewType = ListViewTypes.CARD_ONLY;
component.config.defaultView = 'card' as ListView;
Expand Down Expand Up @@ -210,6 +212,19 @@ describe('ListComponent', () => {
}
];
};
component.config.getFilters = () => ([
{
default: true,
key: 'a',
label: 'A',
placeholder: 'Filter by A'
},
{
key: 'b',
label: 'B',
placeholder: 'Filter by B'
}
]);
component.config.enableTextFilter = true;
component.config.viewType = ListViewTypes.CARD_ONLY;
component.config.defaultView = 'card' as ListView;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ import {
withLatestFrom,
} from 'rxjs/operators';

import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state';
import {
ListFilter,
ListPagination,
ListSort,
ListView,
SetListViewAction,
} from '../../../../../store/src/actions/list.actions';
import { SetPage } from '../../../../../store/src/actions/pagination.actions';
import { SetClientFilterKey, SetPage } from '../../../../../store/src/actions/pagination.actions';
import { GeneralAppState } from '../../../../../store/src/app-state';
import { ActionState } from '../../../../../store/src/reducers/api-request-reducer/types';
import { getListStateObservables } from '../../../../../store/src/reducers/list.reducer';
import { entityCatalogue } from '../../../core/entity-catalogue/entity-catalogue.service';
Expand All @@ -68,13 +68,13 @@ import {
defaultPaginationPageSizeOptionsTable,
IGlobalListAction,
IListConfig,
IListFilter,
IMultiListAction,
IOptionalAction,
ListConfig,
ListViewTypes,
MultiFilterManager,
} from './list.component.types';
import { GeneralAppState } from '../../../../../store/src/app-state';

@Component({
selector: 'app-list',
Expand Down Expand Up @@ -175,6 +175,8 @@ export class ListComponent<T> implements OnInit, OnChanges, OnDestroy, AfterView
value: null
};
private sortColumns: ITableColumn<T>[];
private filterColumns: IListFilter[];
private filterSelected: IListFilter;

private paginationWidgetToStore: Subscription;
private filterWidgetToStore: Subscription;
Expand Down Expand Up @@ -395,8 +397,23 @@ export class ListComponent<T> implements OnInit, OnChanges, OnDestroy, AfterView
this.headerSort.direction = sort.direction;
}));

this.filterColumns = this.config.getFilters ? this.config.getFilters() : [];

const filterStoreToWidget = this.paginationController.filter$.pipe(tap((paginationFilter: ListFilter) => {
this.filterString = paginationFilter.string;

const filterKey = paginationFilter.filterKey;
if (filterKey) {
this.filterSelected = this.filterColumns.find(filterConfig => {
return filterConfig.key === filterKey;
});
} else if (this.filterColumns) {
this.filterSelected = this.filterColumns.find(filterConfig => filterConfig.default);
if (this.filterSelected) {
this.updateListFilter(this.filterSelected);
}
}

// Pipe store values to filter managers. This ensures any changes such as automatically selected orgs/spaces are shown in the drop
// downs (change org to one with one space results in that space being selected)
Object.values(this.multiFilterManagers).forEach((filterManager: MultiFilterManager<T>, index: number) => {
Expand Down Expand Up @@ -589,6 +606,14 @@ export class ListComponent<T> implements OnInit, OnChanges, OnDestroy, AfterView
});
}

updateListFilter(filterSelected: IListFilter) {
this.store.dispatch(new SetClientFilterKey(
this.dataSource,
this.dataSource.paginationKey,
filterSelected.key
));
}

executeActionMultiple(listActionConfig: IMultiListAction<T>) {
const result = listActionConfig.action(Array.from(this.dataSource.selectedRows.values()));
if (isObservable(result)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ export interface IListConfig<T> {
/**
* List of actions that are presented as individual buttons when one or more rows are selected. For example `Delete` of selected rows.
*/
getMultiActions: (schemaKey: string) => IMultiListAction<T>[];
getMultiActions: () => IMultiListAction<T>[];
/**
* List of actions that are presented in a mat-menu for an individual entity. For example `unmap` an application route
*/
getSingleActions: (schemaKey: string) => IListAction<T>[];
getSingleActions: () => IListAction<T>[];
/**
* Collection of column definitions to show when the list is in table mode
*/
Expand All @@ -47,6 +47,12 @@ export interface IListConfig<T> {
* to the data sources transformEntities collection should be used to apply these custom settings to the data.
*/
getMultiFiltersConfigs: () => IListMultiFilterConfig[];
/**
* Collection of filter definitions to support filtering across multiple fields in a list.
* When the filter is selected in a dropdown the filterString filters results using the chosen field.
* Combined with a transformEntities DataFunction that consumes the filterKey.
*/
getFilters?: () => IListFilter[];
/**
* Fetch an observable that will emit once the underlying config components have been created. For instance if the data source requires
* something from the store which requires an async call
Expand Down Expand Up @@ -120,6 +126,13 @@ export interface IListMultiFilterConfig {
select: BehaviorSubject<any>;
}

export interface IListFilter {
default?: boolean;
key: string;
label: string;
placeholder: string;
}

export interface IListMultiFilterConfigItem {
label: string;
item: any;
Expand All @@ -144,6 +157,7 @@ export class ListConfig<T> implements IListConfig<T> {
getColumns = (): ITableColumn<T>[] => null;
getDataSource = (): ListDataSource<T> => null;
getMultiFiltersConfigs = (): IListMultiFilterConfig[] => [];
getFilters = (): IListFilter[] => [];
getInitialised = () => observableOf(true);
}

Expand Down
1 change: 1 addition & 0 deletions src/frontend/packages/store/src/actions/list.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class ListFilter {
items: {
[key: string]: any;
};
filterKey?: string;
}

export const ListStateActionTypes = {
Expand Down
Loading

0 comments on commit d8559de

Please sign in to comment.