mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
8 changed files with 142 additions and 16 deletions
@ -0,0 +1 @@ |
|||
export * from './persist-sort.directive'; |
|||
@ -0,0 +1,70 @@ |
|||
import { Component } from '@angular/core'; |
|||
import { ComponentFixture, TestBed } from '@angular/core/testing'; |
|||
import { MatSort, MatSortModule } from '@angular/material/sort'; |
|||
|
|||
import { GfPersistSortDirective } from './persist-sort.directive'; |
|||
|
|||
@Component({ |
|||
imports: [GfPersistSortDirective, MatSortModule], |
|||
template: `<table
|
|||
gfPersistSort="test" |
|||
matSort |
|||
matSortActive="name" |
|||
matSortDirection="asc" |
|||
></table>` |
|||
}) |
|||
class TestHostComponent {} |
|||
|
|||
describe('GfPersistSortDirective', () => { |
|||
let fixture: ComponentFixture<TestHostComponent>; |
|||
|
|||
const create = () => { |
|||
fixture = TestBed.createComponent(TestHostComponent); |
|||
fixture.detectChanges(); |
|||
|
|||
return fixture.debugElement.children[0].injector.get(MatSort); |
|||
}; |
|||
|
|||
beforeEach(() => { |
|||
localStorage.clear(); |
|||
TestBed.configureTestingModule({ imports: [TestHostComponent] }); |
|||
}); |
|||
|
|||
it('keeps the default sorting without a stored value', () => { |
|||
const sort = create(); |
|||
|
|||
expect(sort.active).toBe('name'); |
|||
expect(sort.direction).toBe('asc'); |
|||
}); |
|||
|
|||
it('stores the sorting on change', () => { |
|||
const sort = create(); |
|||
|
|||
sort.sort({ id: 'value', start: 'desc', disableClear: false }); |
|||
|
|||
expect(JSON.parse(localStorage.getItem('gf-sort-test') ?? '{}')).toEqual({ |
|||
active: 'value', |
|||
direction: 'desc' |
|||
}); |
|||
}); |
|||
|
|||
it('restores a stored sorting', () => { |
|||
localStorage.setItem( |
|||
'gf-sort-test', |
|||
JSON.stringify({ active: 'value', direction: 'desc' }) |
|||
); |
|||
|
|||
const sort = create(); |
|||
|
|||
expect(sort.active).toBe('value'); |
|||
expect(sort.direction).toBe('desc'); |
|||
}); |
|||
|
|||
it('ignores an invalid stored sorting', () => { |
|||
localStorage.setItem('gf-sort-test', '{"active":"value","direction":"x"}'); |
|||
|
|||
const sort = create(); |
|||
|
|||
expect(sort.active).toBe('name'); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,57 @@ |
|||
import { DestroyRef, Directive, inject, input, OnInit } from '@angular/core'; |
|||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
|||
import { MatSort, SortDirection } from '@angular/material/sort'; |
|||
|
|||
const STORAGE_KEY_PREFIX = 'gf-sort-'; |
|||
|
|||
/** |
|||
* Remembers the sorting of a `matSort` table on the device and restores it |
|||
* on the next visit. |
|||
*/ |
|||
@Directive({ |
|||
selector: '[gfPersistSort]' |
|||
}) |
|||
export class GfPersistSortDirective implements OnInit { |
|||
public readonly gfPersistSort = input.required<string>(); |
|||
|
|||
private readonly destroyRef = inject(DestroyRef); |
|||
private readonly matSort = inject(MatSort, { self: true }); |
|||
|
|||
public ngOnInit() { |
|||
const storageKey = `${STORAGE_KEY_PREFIX}${this.gfPersistSort()}`; |
|||
const storedSort = this.readStoredSort(storageKey); |
|||
|
|||
if (storedSort) { |
|||
this.matSort.active = storedSort.active; |
|||
this.matSort.direction = storedSort.direction; |
|||
} |
|||
|
|||
this.matSort.sortChange |
|||
.pipe(takeUntilDestroyed(this.destroyRef)) |
|||
.subscribe(({ active, direction }) => { |
|||
try { |
|||
globalThis.localStorage?.setItem( |
|||
storageKey, |
|||
JSON.stringify({ active, direction }) |
|||
); |
|||
} catch {} |
|||
}); |
|||
} |
|||
|
|||
private readStoredSort(aStorageKey: string) { |
|||
try { |
|||
const item = globalThis.localStorage?.getItem(aStorageKey); |
|||
const { active, direction } = JSON.parse(item ?? 'null') ?? {}; |
|||
|
|||
if ( |
|||
typeof active === 'string' && |
|||
active.length > 0 && |
|||
(direction === 'asc' || direction === 'desc') |
|||
) { |
|||
return { active, direction: direction as SortDirection }; |
|||
} |
|||
} catch {} |
|||
|
|||
return undefined; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue