Browse Source

Filter state finalized.

pull/353/head
Sebastian Stehle 7 years ago
parent
commit
316a8b1d0b
  1. 6
      src/Squidex/app/features/content/pages/contents/contents-page.component.html
  2. 6
      src/Squidex/app/features/content/shared/contents-selector.component.html
  3. 24
      src/Squidex/app/shared/services/schemas.types.ts
  4. 150
      src/Squidex/app/shared/state/filter.state.spec.ts
  5. 26
      src/Squidex/app/shared/state/filter.state.ts

6
src/Squidex/app/features/content/pages/contents/contents-page.component.html

@ -54,13 +54,15 @@
<input type="checkbox" class="form-check" [ngModel]="isAllSelected" (ngModelChange)="selectAll($event)" />
</th>
<th class="cell-auto" *ngFor="let field of schema.listFields">
<sqx-table-header [text]="field.displayName" sortable="true"
<sqx-table-header [text]="field.displayName"
[sortable]="field.properties.isSortable"
[sorting]="filter.sortMode(field) | async"
(sortingChange)="sort(field, $event)">
</sqx-table-header>
</th>
<th class="cell-time">
<sqx-table-header text="Updated" sortable="true"
<sqx-table-header text="Updated"
[sortable]="true"
[sorting]="filter.sortMode('lastModified') | async"
(sortingChange)="sort('lastModified', $event)">
</sqx-table-header>

6
src/Squidex/app/features/content/shared/contents-selector.component.html

@ -31,13 +31,15 @@
<input type="checkbox" class="form-check" [ngModel]="isAllSelected" (ngModelChange)="selectAll($event)" />
</th>
<th class="cell-auto" *ngFor="let field of schema.listFields">
<sqx-table-header [text]="field.displayName" sortable="true"
<sqx-table-header [text]="field.displayName"
[sortable]="field.properties.isSortable"
[sorting]="filter.sortMode(field) | async"
(sortingChange)="sort(field, $event)">
</sqx-table-header>
</th>
<th class="cell-time">
<sqx-table-header text="Updated" sortable="true"
<sqx-table-header text="Updated"
[sortable]="true"
[sorting]="filter.sortMode('lastModified') | async"
(sortingChange)="sort('lastModified', $event)">
</sqx-table-header>

24
src/Squidex/app/shared/services/schemas.types.ts

@ -130,6 +130,10 @@ export abstract class FieldPropertiesDto {
return true;
}
public get isSortable() {
return true;
}
public abstract accept<T>(visitor: FieldPropertiesVisitor<T>): T;
}
@ -166,6 +170,10 @@ export class AssetsFieldPropertiesDto extends FieldPropertiesDto {
public readonly aspectWidth?: number;
public readonly aspectHeight?: number;
public get isSortable() {
return false;
}
constructor(
props?: Partial<AssetsFieldPropertiesDto>
) {
@ -224,6 +232,10 @@ export class DateTimeFieldPropertiesDto extends FieldPropertiesDto {
export class GeolocationFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Geolocation';
public get isSortable() {
return false;
}
constructor(
props?: Partial<GeolocationFieldPropertiesDto>
) {
@ -238,6 +250,10 @@ export class GeolocationFieldPropertiesDto extends FieldPropertiesDto {
export class JsonFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Json';
public get isSortable() {
return false;
}
constructor(
props?: Partial<JsonFieldPropertiesDto>
) {
@ -281,6 +297,10 @@ export class ReferencesFieldPropertiesDto extends FieldPropertiesDto {
public readonly maxItems?: number;
public readonly schemaId?: string;
public get isSortable() {
return false;
}
constructor(
props?: Partial<ReferencesFieldPropertiesDto>
) {
@ -330,6 +350,10 @@ export class TagsFieldPropertiesDto extends FieldPropertiesDto {
return false;
}
public get isSortable() {
return false;
}
constructor(editor: string,
props?: Partial<TagsFieldPropertiesDto>
) {

150
src/Squidex/app/shared/state/filter.state.spec.ts

@ -0,0 +1,150 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import {
FilterState,
LanguageDto,
Sorting
} from './../';
describe('FilterState', () => {
let filterState: FilterState;
let query: string | undefined;
let filter: string | undefined;
let fullText: string | undefined;
let order: string | undefined;
beforeEach(() => {
filterState = new FilterState();
filterState.setLanguage(new LanguageDto('de', 'German'));
filterState.query.subscribe(value => {
query = value;
});
filterState.filter.subscribe(value => {
filter = value;
});
filterState.fullText.subscribe(value => {
fullText = value;
});
filterState.order.subscribe(value => {
order = value;
});
});
it('should parse elements from query', () => {
const newQuery = '$filter=MY_FILTER&$orderby=MY_FIELD desc&$search=MY_TEXT';
filterState.setQuery(newQuery);
expect(order).toBe('MY_FIELD desc');
expect(query).toBe(newQuery);
expect(filter).toBe('MY_FILTER');
expect(fullText).toBe('MY_TEXT');
});
it('should set full text and order and calculate query', () => {
filterState.setFullText('Hello World');
filterState.setOrder('data/name/iv asc');
expect(query).toBe('$search=Hello World&$orderby=data/name/iv asc');
expect(fullText).toBe('Hello World');
});
it('should set full text only and calculate query', () => {
filterState.setFullText('Hello World');
expect(query).toBe('Hello World');
expect(fullText).toBe('Hello World');
});
it('should set filter and calculate query', () => {
filterState.setFilter('data/name/iv eq "Squidex"');
expect(query).toBe('$filter=data/name/iv eq "Squidex"');
expect(filter).toBe('data/name/iv eq "Squidex"');
});
it('should set order and calculate query', () => {
filterState.setOrder('data/name/iv asc');
expect(query).toBe('$orderby=data/name/iv asc');
expect(order).toBe('data/name/iv asc');
});
it('should set field name and calculate query', () => {
filterState.setOrderField('field', 'Descending');
expect(query).toBe('$orderby=field desc');
expect(order).toBe('field desc');
});
it('should set field and calculate query', () => {
filterState.setOrderField(<any>{ name: 'first-name', isLocalizable: false }, 'Ascending');
expect(query).toBe('$orderby=data/first_name/iv asc');
expect(order).toBe('data/first_name/iv asc');
});
it('should set localizable field and calculate query', () => {
filterState.setOrderField(<any>{ name: 'first-name', isLocalizable: true }, 'Ascending');
expect(query).toBe('$orderby=data/first_name/de asc');
expect(order).toBe('data/first_name/de asc');
});
it('should update field ordering for path', () => {
let sorting: Sorting;
filterState.sortMode('field').subscribe(value => {
sorting = value;
});
filterState.setQuery('$orderby=field desc');
expect(sorting!).toBe('Descending');
});
it('should update field ordering for field', () => {
let sorting: Sorting;
filterState.sortMode(<any>{ name: 'first-name', fieldId: 1, isLocalizable: false }).subscribe(value => {
sorting = value;
});
filterState.setQuery('$orderby=data/first_name/iv asc');
expect(sorting!).toBe('Ascending');
});
it('should update field ordering for localizable field', () => {
let sorting: Sorting;
filterState.sortMode(<any>{ name: 'first-name', fieldId: 1, isLocalizable: true }).subscribe(value => {
sorting = value;
});
filterState.setQuery('$orderby=data/first_name/de asc');
expect(sorting!).toBe('Ascending');
});
it('should update field ordering for localizable field and other language', () => {
let sorting: Sorting;
filterState.sortMode(<any>{ name: 'first-name', fieldId: 1, isLocalizable: true }).subscribe(value => {
sorting = value;
});
filterState.setQuery('$orderby=data/first_name/en asc');
expect(sorting!).toBe('None');
});
});

26
src/Squidex/app/shared/state/filter.state.ts

@ -8,11 +8,7 @@
import { Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/internal/operators';
import {
State,
StringHelper,
Types
} from '@app/framework';
import { State, Types } from '@app/framework';
import { LanguageDto } from '../services/languages.service';
@ -43,6 +39,8 @@ interface Snapshot {
export type Sorting = 'Ascending' | 'Descending' | 'None';
type Field = string | RootFieldDto;
export class FilterState extends State<Snapshot> {
private readonly sortModes: { [key: string]: Observable<Sorting> } = {};
@ -70,7 +68,7 @@ export class FilterState extends State<Snapshot> {
super({});
}
public sortMode(field: RootFieldDto | string) {
public sortMode(field: Field) {
const key = Types.isString(field) ? field : field.fieldId.toString();
let result = this.sortModes[key];
@ -104,12 +102,12 @@ export class FilterState extends State<Snapshot> {
this.next(s => ({ ...s, language }));
}
public setOrderField(field: RootFieldDto | string, sorting: Sorting) {
public setOrderField(field: Field, sorting: Sorting) {
this.setOrder(getFieldSorting(this.snapshot, field, sorting));
}
}
function sortMode(snapshot: Snapshot, field: RootFieldDto | string): Sorting {
function sortMode(snapshot: Snapshot, field: Field): Sorting {
let path = getFieldPath(snapshot, field);
if (snapshot.orderField === path) {
@ -123,7 +121,11 @@ function sortMode(snapshot: Snapshot, field: RootFieldDto | string): Sorting {
return 'None';
}
function getFieldSorting(snapshot: Snapshot, field: RootFieldDto | string, sorting: Sorting) {
function escapeField(value: string) {
return value.replace('-', '_');
}
function getFieldSorting(snapshot: Snapshot, field: Field, sorting: Sorting) {
if (sorting === 'Ascending') {
return `${getFieldPath(snapshot, field)} asc`;
} else {
@ -131,16 +133,16 @@ function getFieldSorting(snapshot: Snapshot, field: RootFieldDto | string, sorti
}
}
function getFieldPath(snapshot: Snapshot, field?: RootFieldDto | string) {
function getFieldPath(snapshot: Snapshot, field?: Field) {
let path: string | undefined = undefined;
if (field) {
if (Types.isString(field)) {
path = field;
} else if (field.isLocalizable && snapshot.language) {
path = `data/${StringHelper.toCamelCase(field.name)}/${snapshot.language.iso2Code}`;
path = `data/${escapeField(field.name)}/${snapshot.language.iso2Code}`;
} else {
path = `data/${StringHelper.toCamelCase(field.name)}/iv`;
path = `data/${escapeField(field.name)}/iv`;
}
}

Loading…
Cancel
Save