Enhancement: dashboard improvements, drag-n-drop reorder dashboard views (#4252)

* Updated dashboard

* Make entire screen dropzone on dashboard too

* Floating upload widget status alerts

* Visual tweaks: spacing, borders

* Better empty view widget

* Support drag + drop reorder of dashboard saved views

* Update messages.xlf

* Disable dashbaord dnd if global dnd active

* Remove ngx-file-drop dep, rebuild file-drop & upload files widget

* Revert custom file drop implementation

* Try patch-package fix

* Simplify dropzone transitions to make more reliable

* Update messages.xlf

* Update dashboard.spec.ts

* Fix coverage
This commit is contained in:
shamoon
2023-09-28 10:18:12 -07:00
committed by GitHub
parent 96176589ca
commit 6973691cce
45 changed files with 1715 additions and 534 deletions

View File

@@ -13,16 +13,59 @@ import { PermissionsService } from 'src/app/services/permissions.service'
import { By } from '@angular/platform-browser'
import { SavedViewWidgetComponent } from './widgets/saved-view-widget/saved-view-widget.component'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { NgxFileDropModule } from 'ngx-file-drop'
import { RouterTestingModule } from '@angular/router/testing'
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
import { LogoComponent } from '../common/logo/logo.component'
import { of, throwError } from 'rxjs'
import { DndDropEvent, DndModule } from 'ngx-drag-drop'
import { ToastService } from 'src/app/services/toast.service'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
const saved_views = [
{
name: 'Saved View 0',
id: 0,
show_on_dashboard: true,
show_in_sidebar: true,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
{
name: 'Saved View 1',
id: 1,
show_on_dashboard: false,
show_in_sidebar: false,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
{
name: 'Saved View 2',
id: 2,
show_on_dashboard: true,
show_in_sidebar: false,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
{
name: 'Saved View 3',
id: 3,
show_on_dashboard: true,
show_in_sidebar: false,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
]
describe('DashboardComponent', () => {
let component: DashboardComponent
let fixture: ComponentFixture<DashboardComponent>
let settingsService: SettingsService
let tourService: TourService
let toastService: ToastService
beforeEach(async () => {
TestBed.configureTestingModule({
@@ -47,33 +90,22 @@ describe('DashboardComponent', () => {
{
provide: SavedViewService,
useValue: {
dashboardViews: [
{
id: 1,
name: 'saved view 1',
show_on_dashboard: true,
sort_field: 'added',
sort_reverse: true,
filter_rules: [],
},
{
id: 2,
name: 'saved view 2',
show_on_dashboard: true,
sort_field: 'created',
sort_reverse: true,
filter_rules: [],
},
],
listAll: () =>
of({
all: [saved_views.map((v) => v.id)],
count: saved_views.length,
results: saved_views,
}),
dashboardViews: saved_views.filter((v) => v.show_on_dashboard),
},
},
],
imports: [
NgbAlertModule,
HttpClientTestingModule,
NgxFileDropModule,
RouterTestingModule,
TourNgBootstrapModule,
DndModule,
],
}).compileComponents()
@@ -82,7 +114,11 @@ describe('DashboardComponent', () => {
first_name: 'Foo',
last_name: 'Bar',
}
jest.spyOn(settingsService, 'get').mockImplementation((key) => {
if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return [0, 2, 3]
})
tourService = TestBed.inject(TourService)
toastService = TestBed.inject(ToastService)
fixture = TestBed.createComponent(DashboardComponent)
component = fixture.componentInstance
@@ -100,7 +136,7 @@ describe('DashboardComponent', () => {
it('should show dashboard widgets', () => {
expect(
fixture.debugElement.queryAll(By.directive(SavedViewWidgetComponent))
).toHaveLength(2)
).toHaveLength(saved_views.filter((v) => v.show_on_dashboard).length)
})
it('should end tour service if still running and welcome widget dismissed', () => {
@@ -116,4 +152,44 @@ describe('DashboardComponent', () => {
component.completeTour()
expect(settingsCompleteTourSpy).toHaveBeenCalled()
})
it('should disable global dropzone on start drag + drop, re-enable after', () => {
expect(settingsService.globalDropzoneEnabled).toBeTruthy()
component.onDragStart(null)
expect(settingsService.globalDropzoneEnabled).toBeFalsy()
component.onDragEnd(null)
expect(settingsService.globalDropzoneEnabled).toBeTruthy()
})
it('should update saved view sorting on drag + drop, show info', () => {
const settingsSpy = jest.spyOn(settingsService, 'updateDashboardViewsSort')
const toastSpy = jest.spyOn(toastService, 'showInfo')
jest.spyOn(settingsService, 'storeSettings').mockReturnValue(of(true))
component.onDrop({ index: 2, data: saved_views[0] } as DndDropEvent)
component.onDragged(saved_views[0])
expect(settingsSpy).toHaveBeenCalledWith([
saved_views[2],
saved_views[0],
saved_views[3],
])
expect(toastSpy).toHaveBeenCalled()
component.onDrop({ data: saved_views[3] } as DndDropEvent)
})
it('should update saved view sorting on drag + drop, show info2', () => {
jest.spyOn(settingsService, 'get').mockImplementation((key) => {
if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return []
})
fixture.destroy()
fixture = TestBed.createComponent(DashboardComponent)
component = fixture.componentInstance
fixture.detectChanges()
const toastSpy = jest.spyOn(toastService, 'showError')
jest
.spyOn(settingsService, 'storeSettings')
.mockReturnValue(throwError(() => new Error('unable to save')))
component.onDrop({ index: 2, data: saved_views[0] } as DndDropEvent)
component.onDragged(saved_views[0])
expect(toastSpy).toHaveBeenCalled()
})
})