ACS-7403: migrate site dropdown to standalone (#9847)

This commit is contained in:
Denys Vuika
2024-07-01 14:25:49 -04:00
committed by GitHub
parent 5fa3afe3a5
commit 0c4259cddf
28 changed files with 244 additions and 335 deletions

View File

@@ -21,15 +21,15 @@ import { By } from '@angular/platform-browser';
import { Node, NodeEntry, NodePaging, RequestScope, ResultSetPaging, SiteEntry, SitePaging, SitePagingList } from '@alfresco/js-api';
import { of } from 'rxjs';
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
import { ContentTestingModule } from '../testing/content.testing.module';
import { DocumentListService } from '../document-list/services/document-list.service';
import { DocumentListComponent } from '../document-list/components/document-list.component';
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
import { NodeEntryEvent, ShareDataRow } from '../document-list';
import { SearchQueryBuilderService } from '../search';
import { mockSearchRequest } from '../mock/search-query.mock';
import { SitesService } from '../common/services/sites.service';
import { NodesApiService } from '../common/services/nodes-api.service';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { DocumentListService } from '../../document-list/services/document-list.service';
import { DocumentListComponent } from '../../document-list/components/document-list.component';
import { CustomResourcesService } from '../../document-list/services/custom-resources.service';
import { NodeEntryEvent, ShareDataRow } from '../../document-list';
import { SearchQueryBuilderService } from '../../search';
import { mockSearchRequest } from '../../mock/search-query.mock';
import { SitesService } from '../../common/services/sites.service';
import { NodesApiService } from '../../common/services/nodes-api.service';
const fakeResultSetPaging: ResultSetPaging = {
list: {

View File

@@ -147,8 +147,7 @@ h2.adf-search-results-label {
.adf-datatable-body .adf-datatable-row {
min-height: 40px;
@media screen and (-ms-high-contrast: active),
screen and (-ms-high-contrast: none) {
@media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) {
padding-top: 15px;
}

View File

@@ -20,17 +20,17 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin
import { By } from '@angular/platform-browser';
import { Node, NodeEntry, NodePaging, ResultSetPaging, Site, SiteEntry, SitePaging, SitePagingList, UserInfo } from '@alfresco/js-api';
import { DataRow, ThumbnailService, DataColumn } from '@alfresco/adf-core';
import { ContentService, UploadService, NodesApiService, SitesService, FileModel, FileUploadStatus, FileUploadCompleteEvent } from '../common';
import { ContentService, UploadService, NodesApiService, SitesService, FileModel, FileUploadStatus, FileUploadCompleteEvent } from '../../common';
import { of, throwError } from 'rxjs';
import { DropdownBreadcrumbComponent } from '../breadcrumb';
import { DropdownBreadcrumbComponent } from '../../breadcrumb';
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
import { ContentTestingModule } from '../testing/content.testing.module';
import { DocumentListService } from '../document-list/services/document-list.service';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { DocumentListService } from '../../document-list/services/document-list.service';
import { DropdownSitesComponent } from '../site-dropdown/sites-dropdown.component';
import { NodeEntryEvent, ShareDataRow, ShareDataTableAdapter } from '../document-list';
import { SearchQueryBuilderService } from '../search';
import { NodeEntryEvent, ShareDataRow, ShareDataTableAdapter } from '../../document-list';
import { SearchQueryBuilderService } from '../../search';
import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service';
import { mockContentModelTextProperty } from '../mock/content-model.mock';
import { mockContentModelTextProperty } from '../../mock/content-model.mock';
const fakeResultSetPaging: ResultSetPaging = {
list: {

View File

@@ -25,18 +25,18 @@ import {
DataSorting,
ShowHeaderMode
} from '@alfresco/adf-core';
import { NodesApiService, UploadService, FileUploadCompleteEvent, FileUploadDeleteEvent, SitesService } from '../common';
import { NodesApiService, UploadService, FileUploadCompleteEvent, FileUploadDeleteEvent, SitesService } from '../../common';
import { UntypedFormControl } from '@angular/forms';
import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry, SearchRequest, RequestScope } from '@alfresco/js-api';
import { DocumentListComponent } from '../document-list/components/document-list.component';
import { RowFilter } from '../document-list/data/row-filter.model';
import { ImageResolver } from '../document-list/data/image-resolver.model';
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
import { ShareDataRow } from '../document-list/data/share-data-row.model';
import { NodeEntryEvent } from '../document-list/components/node.event';
import { DocumentListComponent } from '../../document-list/components/document-list.component';
import { RowFilter } from '../../document-list/data/row-filter.model';
import { ImageResolver } from '../../document-list/data/image-resolver.model';
import { CustomResourcesService } from '../../document-list/services/custom-resources.service';
import { ShareDataRow } from '../../document-list/data/share-data-row.model';
import { NodeEntryEvent } from '../../document-list/components/node.event';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { SearchQueryBuilderService } from '../search';
import { SearchQueryBuilderService } from '../../search';
import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service';
export type ValidationFunction = (entry: Node) => boolean;

View File

@@ -16,10 +16,9 @@
*/
import { ContentNodeSelectorPanelService } from './content-node-selector-panel.service';
import { mockContentModelDateProperty, mockContentModelTextProperty, mockConvertedSearchCategoriesFromModels } from '../mock/content-model.mock';
import { mockContentModelDateProperty, mockContentModelTextProperty, mockConvertedSearchCategoriesFromModels } from '../../mock/content-model.mock';
describe('ContentNodeSelectorPanelService', () => {
const contentNodeSelectorPanelService = new ContentNodeSelectorPanelService();
it('should support text type', () => {
@@ -42,7 +41,7 @@ describe('ContentNodeSelectorPanelService', () => {
});
it('should modelPropertyTypeToSearchFilterTypeMap contain only the supported types', () => {
const expectedSupportedTypesMap = new Map<string, string> ();
const expectedSupportedTypesMap = new Map<string, string>();
expectedSupportedTypesMap.set('d:text', 'text');
expectedSupportedTypesMap.set('d:date', 'date-range');
expectedSupportedTypesMap.set('d:datetime', 'datetime-range');

View File

@@ -16,15 +16,14 @@
*/
import { Injectable } from '@angular/core';
import { SearchCategory } from '../search/models/search-category.interface';
import { SearchCategory } from '../../search/models/search-category.interface';
@Injectable({
providedIn: 'root'
})
export class ContentNodeSelectorPanelService {
propertyTypes = ['d:text', 'd:date', 'd:datetime'];
modelPropertyTypeToSearchFilterTypeMap = new Map<string, string> ();
modelPropertyTypeToSearchFilterTypeMap = new Map<string, string>();
customModels: any[];
constructor() {
@@ -35,7 +34,7 @@ export class ContentNodeSelectorPanelService {
convertCustomModelPropertiesToSearchCategories(): SearchCategory[] {
const searchConfig: SearchCategory[] = [];
this.customModels?.forEach( (propertyModel) => {
this.customModels?.forEach((propertyModel) => {
searchConfig.push(this.convertModelPropertyIntoSearchFilter(propertyModel));
});
@@ -46,7 +45,7 @@ export class ContentNodeSelectorPanelService {
let filterSearch: SearchCategory;
if (this.isTypeSupported(modelProperty.dataType)) {
filterSearch = {
id : modelProperty.prefixedName,
id: modelProperty.prefixedName,
name: modelProperty.prefixedName,
expanded: false,
enabled: true,
@@ -66,5 +65,4 @@ export class ContentNodeSelectorPanelService {
isTypeSupported(dataType: string): boolean {
return this.propertyTypes.includes(dataType);
}
}

View File

@@ -29,7 +29,7 @@ import { ContentTestingModule } from '../testing/content.testing.module';
import { DocumentListService } from '../document-list/services/document-list.service';
import { DocumentListComponent } from '../document-list/components/document-list.component';
import { UploadModule } from '../upload';
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel/content-node-selector-panel.component';
import { NodeAction } from '../document-list/models/node-action.enum';
import { SitesService } from '../common/services/sites.service';
import { NodesApiService } from '../common/services/nodes-api.service';

View File

@@ -19,10 +19,8 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MaterialModule } from '../material.module';
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel/content-node-selector-panel.component';
import { ContentNodeSelectorComponent } from './content-node-selector.component';
import { SitesDropdownModule } from '../site-dropdown/sites-dropdown.module';
import { BreadcrumbModule } from '../breadcrumb/breadcrumb.module';
import { SearchModule } from '../search/search.module';
import { CoreModule } from '@alfresco/adf-core';
@@ -31,6 +29,7 @@ import { NameLocationCellComponent } from './name-location-cell/name-location-ce
import { UploadModule } from '../upload/upload.module';
import { SearchQueryBuilderService } from '../search/services/search-query-builder.service';
import { ContentDirectiveModule } from '../directives/content-directive.module';
import { DropdownSitesComponent } from './site-dropdown/sites-dropdown.component';
@NgModule({
imports: [
@@ -39,23 +38,16 @@ import { ContentDirectiveModule } from '../directives/content-directive.module';
CoreModule,
CommonModule,
MaterialModule,
SitesDropdownModule,
DropdownSitesComponent,
BreadcrumbModule,
SearchModule,
DocumentListModule,
UploadModule,
ContentDirectiveModule
],
exports: [
ContentNodeSelectorPanelComponent,
NameLocationCellComponent,
ContentNodeSelectorComponent
],
declarations: [
ContentNodeSelectorPanelComponent,
NameLocationCellComponent,
ContentNodeSelectorComponent
ContentDirectiveModule,
NameLocationCellComponent
],
exports: [ContentNodeSelectorPanelComponent, NameLocationCellComponent, ContentNodeSelectorComponent],
declarations: [ContentNodeSelectorPanelComponent, ContentNodeSelectorComponent],
providers: [SearchQueryBuilderService]
})
export class ContentNodeSelectorModule {}

View File

@@ -27,9 +27,7 @@ describe('NameLocationCellComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
NameLocationCellComponent
]
imports: [NameLocationCellComponent]
});
fixture = TestBed.createComponent(NameLocationCellComponent);
component = fixture.componentInstance;

View File

@@ -20,6 +20,7 @@ import { DataRow } from '@alfresco/adf-core';
@Component({
selector: 'adf-name-location-cell',
standalone: true,
template: `
<div class="adf-name-location-cell-name adf-datatable-cell-value" [title]="name">{{ name }}</div>
<div class="adf-name-location-cell-location adf-datatable-cell-value" [title]="path">{{ path }}</div>
@@ -30,7 +31,6 @@ import { DataRow } from '@alfresco/adf-core';
host: { class: 'adf-name-location-cell adf-datatable-content-cell' }
})
export class NameLocationCellComponent implements OnInit {
name: string = '';
path: string = '';

View File

@@ -16,10 +16,11 @@
*/
export * from './name-location-cell/name-location-cell.component';
export * from './site-dropdown/sites-dropdown.component';
export * from './content-node-selector.component-data.interface';
export * from './content-node-selector-panel.component';
export * from './content-node-selector-panel/content-node-selector-panel.component';
export * from './content-node-selector.component';
export * from './content-node-dialog.service';
export * from './content-node-selector-panel.service';
export * from './content-node-selector-panel/content-node-selector-panel.service';
export * from './content-node-selector.module';

View File

@@ -0,0 +1,24 @@
<div id="site-dropdown-container" class="adf-site-dropdown-container">
<mat-form-field class="adf-sites-dropdown-form-field">
<mat-label>{{ 'NODE_SELECTOR.LOCATION' | translate }}</mat-label>
<mat-select
adf-infinite-select-scroll
(scrollEnd)="loadAllOnScroll()"
data-automation-id="site-my-files-option"
class="adf-site-dropdown-list-element"
id="site-dropdown"
placeholder="{{placeholder | translate}}"
[(value)]="selected"
(selectionChange)="selectedSite($event)">
<mat-select-trigger class="adf-sites-dropdown-select-trigger">
{{ selected?.entry.title | translate}}
</mat-select-trigger>
<mat-option *ngFor="let site of siteList?.list.entries;" [value]="site">
{{ site.entry.title | translate}}
</mat-option>
<mat-option *ngIf="showLoading()" disabled="true" data-automation-id="site-loading">
{{ 'ADF_DROPDOWN.LOADING' | translate}}
</mat-option>
</mat-select>
</mat-form-field>
</div>

View File

@@ -0,0 +1,310 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DropdownSitesComponent, Relations } from './sites-dropdown.component';
import { AuthenticationService } from '@alfresco/adf-core';
import { of } from 'rxjs';
import {
getFakeSitePaging,
getFakeSitePagingNoMoreItems,
getFakeSitePagingFirstPage,
getFakeSitePagingLastPage,
getFakeSitePagingWithMembers
} from '../../mock';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { SitesService } from '../../common/services/sites.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing';
import { SiteEntry } from '@alfresco/js-api';
const customSiteList = {
list: {
entries: [
{
entry: {
guid: '-my-',
title: 'PERSONAL_FILES'
}
},
{
entry: {
guid: '-mysites-',
title: 'FILE_LIBRARIES'
}
}
]
}
};
describe('DropdownSitesComponent', () => {
let loader: HarnessLoader;
let component: any;
let fixture: ComponentFixture<DropdownSitesComponent>;
let element: HTMLElement;
let siteService: SitesService;
let authService: AuthenticationService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ContentTestingModule]
});
});
describe('Rendering tests', () => {
describe('Infinite Loading', () => {
beforeEach(() => {
siteService = TestBed.inject(SitesService);
fixture = TestBed.createComponent(DropdownSitesComponent);
element = fixture.nativeElement;
component = fixture.componentInstance;
spyOn(siteService, 'getSites').and.returnValue(of(getFakeSitePaging()));
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('Should show loading item if there are more items', async () => {
fixture.detectChanges();
await fixture.whenStable();
expect(element.querySelector('[data-automation-id="site-loading"]')).toBeDefined();
});
it('Should not show loading item if there are more items', async () => {
fixture.detectChanges();
await fixture.whenStable();
fixture.detectChanges();
expect(element.querySelector('[data-automation-id="site-loading"]')).toBeNull();
});
});
describe('Sites', () => {
beforeEach(() => {
siteService = TestBed.inject(SitesService);
spyOn(siteService, 'getSites').and.returnValue(of(getFakeSitePagingNoMoreItems()));
fixture = TestBed.createComponent(DropdownSitesComponent);
element = fixture.nativeElement;
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('Dropdown sites should be rendered', async () => {
fixture.detectChanges();
await fixture.whenStable();
expect(element.querySelector('#site-dropdown-container')).toBeDefined();
expect(element.querySelector('#site-dropdown')).toBeDefined();
expect(element.querySelector('#site-dropdown-container')).not.toBeNull();
expect(element.querySelector('#site-dropdown')).not.toBeNull();
});
it('should show the "My files" option by default', async () => {
component.hideMyFiles = false;
fixture.detectChanges();
await fixture.whenStable();
const select = await loader.getHarness(MatSelectHarness);
await select.open();
const options = await select.getOptions();
expect(await options[0].getText()).toContain('DROPDOWN.MY_FILES_OPTION');
});
it('should hide the "My files" option if the developer desires that way', async () => {
component.hideMyFiles = true;
fixture.detectChanges();
await fixture.whenStable();
const select = await loader.getHarness(MatSelectHarness);
await select.open();
const options = await select.getOptions();
expect(await options[0].getText()).not.toContain('DROPDOWN.MY_FILES_OPTION');
});
it('should show the default placeholder label by default', async () => {
fixture.detectChanges();
await fixture.whenStable();
const select = await loader.getHarness(MatSelectHarness);
await select.open();
expect(fixture.nativeElement.innerText.trim()).toContain('NODE_SELECTOR.LOCATION');
});
it('should show custom placeholder label when the "placeholder" input property is given a value', async () => {
component.placeholder = 'NODE_SELECTOR.SELECT_LIBRARY';
fixture.detectChanges();
await fixture.whenStable();
const select = await loader.getHarness(MatSelectHarness);
await select.open();
expect(fixture.nativeElement.innerText.trim()).toContain('NODE_SELECTOR.LOCATION');
});
it('should load custom sites when the "siteList" input property is given a value', async () => {
component.siteList = customSiteList;
fixture.detectChanges();
await fixture.whenStable();
const select = await loader.getHarness(MatSelectHarness);
await select.open();
const options = await select.getOptions();
expect(await options[0].getText()).toContain('PERSONAL_FILES');
expect(await options[1].getText()).toContain('FILE_LIBRARIES');
});
it('should load sites by default', async () => {
fixture.detectChanges();
await fixture.whenStable();
const select = await loader.getHarness(MatSelectHarness);
await select.open();
const options = await select.getOptions();
expect(await options[1].getText()).toContain('fake-test-site');
expect(await options[2].getText()).toContain('fake-test-2');
});
it('should raise an event when a site is selected', async () => {
fixture.detectChanges();
await fixture.whenStable();
let site: SiteEntry;
component.change.subscribe((value) => (site = value));
const select = await loader.getHarness(MatSelectHarness);
await select.open();
const options = await select.getOptions();
await options[1].click();
expect(site.entry.guid).toBe('fake-1');
});
it('should be possible to select the default value', async () => {
component.value = 'swsdp';
fixture.detectChanges();
await fixture.whenStable();
expect(component.selected.entry.title).toBe('fake-test-2');
});
});
describe('Default value', () => {
beforeEach(() => {
siteService = TestBed.inject(SitesService);
spyOn(siteService, 'getSites').and.returnValues(of(getFakeSitePagingFirstPage()), of(getFakeSitePagingLastPage()));
fixture = TestBed.createComponent(DropdownSitesComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should load new sites if default value is not in the first page', (done) => {
component.value = 'fake-test-4';
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component.selected.entry.title).toBe('fake-test-4');
done();
});
});
it('should NOT reload infinitely if default value is NOT found after all sites are loaded', (done) => {
component.value = 'nonexistent-site';
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component.selected).toBeUndefined();
expect(component.loading).toBeFalsy();
done();
});
});
});
describe('Sites with members', () => {
beforeEach(() => {
siteService = TestBed.inject(SitesService);
spyOn(siteService, 'getSites').and.returnValue(of(getFakeSitePagingWithMembers()));
fixture = TestBed.createComponent(DropdownSitesComponent);
element = fixture.nativeElement;
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
fixture.destroy();
});
describe('No relations', () => {
beforeEach(() => {
component.relations = Relations.Members;
authService = TestBed.inject(AuthenticationService);
});
it('should show only sites which logged user is member of when member relation is set', async () => {
spyOn(authService, 'getEcmUsername').and.returnValue('test');
fixture.detectChanges();
await fixture.whenStable();
const select = await loader.getHarness(MatSelectHarness);
await select.open();
const options = await select.getOptions();
expect(await options[1].getText()).toContain('FAKE-SITE-PUBLIC');
expect(await options[2].getText()).toContain('FAKE-PRIVATE-SITE-MEMBER');
});
});
describe('No relations', () => {
beforeEach(() => {
component.relations = [];
authService = TestBed.inject(AuthenticationService);
});
it('should show all the sites if no relation is set', async () => {
spyOn(authService, 'getEcmUsername').and.returnValue('test');
fixture.detectChanges();
await fixture.whenStable();
const select = await loader.getHarness(MatSelectHarness);
await select.open();
const options = await select.getOptions();
expect(await options[1].getText()).toContain('FAKE-MODERATED-SITE');
expect(await options[2].getText()).toContain('FAKE-SITE-PUBLIC');
expect(await options[3].getText()).toContain('FAKE-PRIVATE-SITE-MEMBER');
});
});
});
});
});

View File

@@ -0,0 +1,196 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
import { InfiniteSelectScrollDirective, AuthenticationService } from '@alfresco/adf-core';
import { SitePaging, SiteEntry, Site } from '@alfresco/js-api';
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { SitesService } from '../../common/services/sites.service';
import { CommonModule } from '@angular/common';
import { MatFormFieldModule } from '@angular/material/form-field';
/* eslint-disable no-shadow */
/* eslint-disable @typescript-eslint/naming-convention */
export enum Relations {
Members = 'members',
Containers = 'containers'
}
@Component({
selector: 'adf-sites-dropdown',
standalone: true,
imports: [CommonModule, TranslateModule, MatFormFieldModule, MatSelectModule, InfiniteSelectScrollDirective],
templateUrl: './sites-dropdown.component.html',
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-sites-dropdown' }
})
export class DropdownSitesComponent implements OnInit {
/** Hide the "My Files" option. */
@Input()
hideMyFiles: boolean = false;
/**
* A custom list of sites to be displayed by the dropdown. If no value
* is given, the sites of the current user are displayed by default. A
* list of objects only with properties 'title' and 'guid' is enough to
* be able to display the dropdown.
*/
@Input()
siteList: SitePaging = null;
/** Id of the selected site */
@Input()
value: string = null;
/**
* Text or a translation key to act as a placeholder. Default value is the
* key "DROPDOWN.PLACEHOLDER_LABEL".
*/
@Input()
placeholder: string = 'DROPDOWN.PLACEHOLDER_LABEL';
/**
* Filter for the results of the sites query. Possible values are
* "members" and "containers". When "members" is used, the site list
* will be restricted to the sites that the user is a member of.
*/
@Input()
relations: string;
/**
* Emitted when the user selects a site. When the default option is selected,
* an empty model is emitted.
*/
@Output()
change = new EventEmitter<SiteEntry>();
@Output()
error = new EventEmitter<any>();
private loading = true;
private skipCount = 0;
selected: SiteEntry = null;
MY_FILES_VALUE = '-my-';
constructor(
private authService: AuthenticationService,
private sitesService: SitesService,
private liveAnnouncer: LiveAnnouncer,
private translateService: TranslateService
) {}
ngOnInit() {
if (!this.siteList) {
this.loadSiteList();
}
}
loadAllOnScroll() {
if (this.isInfiniteScrollingEnabled()) {
this.loading = true;
this.loadSiteList();
}
}
selectedSite(event: MatSelectChange) {
this.liveAnnouncer.announce(
this.translateService.instant('ADF_DROPDOWN.SELECTION_ARIA_LABEL', {
placeholder: this.translateService.instant(this.placeholder),
selectedOption: this.translateService.instant(event.value.entry.title)
})
);
this.change.emit(event.value);
}
private loadSiteList() {
const extendedOptions: any = {
skipCount: this.skipCount,
maxItems: InfiniteSelectScrollDirective.MAX_ITEMS
};
this.skipCount += InfiniteSelectScrollDirective.MAX_ITEMS;
if (this.relations) {
extendedOptions.relations = [this.relations];
}
this.sitesService.getSites(extendedOptions).subscribe(
(sitePaging: SitePaging) => {
if (!this.siteList) {
this.siteList = this.relations === Relations.Members ? this.filteredResultsByMember(sitePaging) : sitePaging;
if (!this.hideMyFiles) {
const siteEntry = new SiteEntry({
entry: new Site({ id: this.MY_FILES_VALUE, guid: this.MY_FILES_VALUE, title: 'DROPDOWN.MY_FILES_OPTION' })
});
this.siteList.list.entries.unshift(siteEntry);
if (!this.value) {
this.value = this.MY_FILES_VALUE;
}
}
} else {
const siteList: SitePaging = this.relations === Relations.Members ? this.filteredResultsByMember(sitePaging) : sitePaging;
this.siteList.list.entries = this.siteList.list.entries.concat(siteList.list.entries);
this.siteList.list.pagination = sitePaging.list.pagination;
}
this.selected = this.siteList.list.entries.find((site: SiteEntry) => site.entry.id === this.value);
if (this.value && !this.selected && this.siteListHasMoreItems()) {
this.loadSiteList();
}
this.loading = false;
},
(error) => {
this.error.emit(error);
}
);
}
showLoading(): boolean {
return this.loading && this.siteListHasMoreItems();
}
isInfiniteScrollingEnabled(): boolean {
return !this.loading && this.siteListHasMoreItems();
}
private siteListHasMoreItems(): boolean {
return this.siteList?.list.pagination?.hasMoreItems;
}
private filteredResultsByMember(sites: SitePaging): SitePaging {
const loggedUserName = this.authService.getEcmUsername();
sites.list.entries = sites.list.entries.filter((site) => this.isCurrentUserMember(site, loggedUserName));
return sites;
}
private isCurrentUserMember(site: SiteEntry, loggedUserName: string): boolean {
return (
site.entry.visibility === 'PUBLIC' ||
!!site.relations.members.list.entries.find((member) => member.entry.id.toLowerCase() === loggedUserName.toLowerCase())
);
}
}