Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port downstream list free text search changes #3981

Merged
merged 2 commits into from
Dec 5, 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
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,7 +46,7 @@ export class CfFeatureFlagsListConfigService extends BaseCfListConfig<IFeatureFl
}
},
{
columnId: 'description',
columnId: CfFeatureFlagsDataSource.descriptionColumnId,
headerCell: () => 'Description',
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