mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACS-8036] [Bulk update] [Many files to 1 Hold flow] "Manage Holds" dialog with Manage Existing Holds/Apply New Hold tabs (#4019)
* [ACS-8325] [Bulk Legal Hold] Create Bulk Actions Dropdown (#3956) * ACS-8036 create bulk-actions-dropdown * ACS-8325 update names and add unit tests * ACS-8325 added icon and unit tests * ACS-8325 fix translation * ACS-8325 add and refactor tests in app extension service * ACS-8325 resolve conversations: remove loadBulkActions, rename bulk schema, add translations * ACS-8325 update state * ACS-8325 temporary remove disabled state as it doesn't work after Angular migration * ACS-8325 add formControl and tests * ACS-8325 clean code * ACS-8325 place create component to one place * ACS-8325 add condition to use title if no description provided and cover with test * ACS-8325 add tooltip and update Badge interface * ACS-8325 rename class names and mockItem object * [ACS-8326] enable running actions from bulk dropdown and resetting selection (#3971) * ACS-8036 create bulk-actions-dropdown * ACS-8325 update names and add unit tests * ACS-8325 fix translation * ACS-8325 resolve conversations: remove loadBulkActions, rename bulk schema, add translations * ACS-8325 temporary remove disabled state as it doesn't work after Angular migration * ACS-8325 add formControl and tests * ACS-8325 add condition to use title if no description provided and cover with test * ACS-8325 add tooltip and update Badge interface * ACS-8326 enable running actions from bulk dropdown and resetting dropdown selection * ACS-8326 review remarks - use select control, fix naming * ACS-8326 fix unit test selectors * ACS-8326 review remarks - change property name to more universal, adjust unit tests --------- Co-authored-by: Darya Balvanovich <darya.balvanovich@hyland.com> * [ACS-8424][Bulk Legal Hold] Add Badge for items (#3985) * [ACS-8424] display badges in search result * [ACS-8424] display badges in search result * ACS-8424 create separate badge component, add/move unit tests, undo highlight change failing tests * ACS-8424 template cleanup * ACS-8424 template cleanup cleanup * ACS-8424 revert single deletion in template * ACS-8424 rename new component, change property order --------- Co-authored-by: g-jaskowski <grzegorz.jaskowski@hyland.com> * ACS-8458 refactor styles (#4018) * [ACS-8489] Legal Holds keyboard accessibility (#4009) * ACS-8489 handle keyboard accessibility * ACS-8489 wording fix * ACS-8489 change function name * ACS-8489 fix typo * ACS-8489 review remarks - simplify keyboard event handling, improve unit tests * ACS-8489 move duplicated code to method * ACS-8489 change type name to more precise * [ACS-8036] fix icon visibility in mat-select * ACS-8036 fix styles and import * ACS-8036 remove async from test * ACS-8036 handle event when dropdown closed * ACS-8036 fix rebase issues * ACS-8036 fix rebase issues * ACS-8036 fix rebase --------- Co-authored-by: Grzegorz Jaśkowski <138671284+g-jaskowski@users.noreply.github.com> Co-authored-by: tamaragruszka <156320606+tamaragruszka@users.noreply.github.com> Co-authored-by: g-jaskowski <grzegorz.jaskowski@hyland.com> Co-authored-by: tamaragruszka <tamara.gruszka@hyland.com>
This commit is contained in:
parent
068f6bb8e9
commit
53e90312b0
@ -115,6 +115,10 @@
|
||||
"description": "Element title",
|
||||
"type": "string"
|
||||
},
|
||||
"tooltip": {
|
||||
"description": "Element tooltip to display on hover",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Element description, used for the tooltips.",
|
||||
"type": "string"
|
||||
@ -837,6 +841,12 @@
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"bulk-actions": {
|
||||
"description": "Bulk actions entries",
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/contentActionRef" },
|
||||
"minItems": 1
|
||||
},
|
||||
"content-metadata-presets": {
|
||||
"description": "Configuration for the presets for content metadata component",
|
||||
"type": "array",
|
||||
|
@ -501,6 +501,11 @@
|
||||
}
|
||||
},
|
||||
"SEARCH": {
|
||||
"BULK_ACTIONS_DROPDOWN": {
|
||||
"TITLE": "Bulk Actions ({{ count }} Items)",
|
||||
"BULK_NOT_AVAILABLE": "Bulk Actions (Not Available)",
|
||||
"BULK_NOT_AVAILABLE_TOOLTIP": "Bulk Actions cannot be used without search results"
|
||||
},
|
||||
"INPUT": {
|
||||
"PLACEHOLDER": "Search",
|
||||
"FILES": "Files",
|
||||
|
@ -76,6 +76,7 @@ import { UserMenuComponent } from './components/sidenav/user-menu/user-menu.comp
|
||||
import { ContextMenuComponent } from './components/context-menu/context-menu.component';
|
||||
import { MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material/dialog';
|
||||
import { SearchResultsRowComponent } from './components/search/search-results-row/search-results-row.component';
|
||||
import { BulkActionsDropdownComponent } from './components/bulk-actions-dropdown/bulk-actions-dropdown.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -138,6 +139,7 @@ export class ContentServiceExtensionModule {
|
||||
'app.toolbar.toggleFavoriteLibrary': ToggleFavoriteLibraryComponent,
|
||||
'app.toolbar.toggleJoinLibrary': ToggleJoinLibraryButtonComponent,
|
||||
'app.menu.toggleJoinLibrary': ToggleJoinLibraryMenuComponent,
|
||||
'app.bulk-actions-dropdown': BulkActionsDropdownComponent,
|
||||
'app.shared-link.toggleSharedLink': ToggleSharedComponent,
|
||||
'app.columns.name': CustomNameColumnComponent,
|
||||
'app.columns.libraryName': LibraryNameColumnComponent,
|
||||
|
@ -0,0 +1,42 @@
|
||||
<mat-form-field
|
||||
*ngIf="items?.length"
|
||||
[title]="tooltip"
|
||||
appearance="outline"
|
||||
class="aca-bulk-actions-form-field"
|
||||
data-automation-id="aca-bulk-actions-form-field"
|
||||
>
|
||||
<mat-select
|
||||
[formControl]="bulkSelectControl"
|
||||
[placeholder]="placeholder"
|
||||
panelClass="aca-bulk-actions-select"
|
||||
disableOptionCentering
|
||||
data-automation-id="aca-bulk-actions-dropdown"
|
||||
(keydown)="onKeyDown($event)"
|
||||
>
|
||||
<mat-select-trigger>
|
||||
<adf-icon
|
||||
*ngIf="bulkSelectControl.value?.icon"
|
||||
[title]="bulkSelectControl.value?.title | translate"
|
||||
[value]="bulkSelectControl.value?.icon"
|
||||
[attr.data-automation-id]="'aca-bulk-action-icon-' + bulkSelectControl.value?.id"
|
||||
></adf-icon>
|
||||
{{ bulkSelectControl.value?.title | translate }}
|
||||
</mat-select-trigger>
|
||||
|
||||
<mat-option
|
||||
*ngFor="let option of items"
|
||||
[value]="option"
|
||||
[title]="option.tooltip | translate"
|
||||
[attr.data-automation-id]="option.id"
|
||||
(click)="runAction(option)"
|
||||
>
|
||||
<adf-icon
|
||||
*ngIf="option.icon"
|
||||
[title]="option.title | translate"
|
||||
[value]="option.icon"
|
||||
[attr.data-automation-id]="'aca-bulk-action-icon-' + option.id"
|
||||
></adf-icon>
|
||||
{{ option.title | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
@ -0,0 +1,24 @@
|
||||
@import '@alfresco/adf-core/lib/styles/mat-selectors';
|
||||
|
||||
.aca-bulk-actions-form-field {
|
||||
margin-left: 24px;
|
||||
margin-top: 1.4375em;
|
||||
width: 295px;
|
||||
|
||||
#{$mat-form-field-flex},
|
||||
#{$mat-form-text-field-infix} {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#{$mat-form-field-appearance-outline} #{$mat-form-field-outline} {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#{$mat-select} {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
/*!
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { BulkActionsDropdownComponent } from './bulk-actions-dropdown.component';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppStore } from '@alfresco/aca-shared/store';
|
||||
import { BehaviorSubject, of } from 'rxjs';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ContentActionRef, ContentActionType } from '@alfresco/adf-extensions';
|
||||
import { AppTestingModule } from '../../testing/app-testing.module';
|
||||
import { TranslationService } from '@alfresco/adf-core';
|
||||
import { AppExtensionService } from '@alfresco/aca-shared';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { MatSelectHarness } from '@angular/material/select/testing';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
import { FakeMatIconRegistry } from '@angular/material/icon/testing';
|
||||
|
||||
describe('BulkActionsDropdownComponent', () => {
|
||||
let component: BulkActionsDropdownComponent;
|
||||
let fixture: ComponentFixture<BulkActionsDropdownComponent>;
|
||||
let store: Store<AppStore>;
|
||||
let translationService: TranslationService;
|
||||
let bulkFormField: HTMLElement;
|
||||
let dropdown: HTMLElement;
|
||||
let extensionService: AppExtensionService;
|
||||
let loader: HarnessLoader;
|
||||
|
||||
const mockItem: ContentActionRef = {
|
||||
id: 'mockId',
|
||||
title: 'some title',
|
||||
tooltip: 'some tooltip',
|
||||
icon: 'adf:mock-icon',
|
||||
type: ContentActionType.custom,
|
||||
actions: {
|
||||
click: 'TEST_EVENT'
|
||||
},
|
||||
rules: {
|
||||
visible: 'isItemVisible'
|
||||
}
|
||||
};
|
||||
|
||||
const totalItemsMock$: BehaviorSubject<number> = new BehaviorSubject(0);
|
||||
|
||||
const getElement = (selector: string): HTMLElement | null => fixture.debugElement.query(By.css(`[data-automation-id="${selector}"]`)).nativeElement;
|
||||
|
||||
const getLabelText = (selector: string): string => getElement(selector).textContent.trim();
|
||||
|
||||
const selectOptionFromDropdown = async (selectionIndex: number) => {
|
||||
const selectHarness = await loader.getHarness(MatSelectHarness);
|
||||
await selectHarness.open();
|
||||
await selectHarness.clickOptions(await selectHarness.getOptions()[selectionIndex]);
|
||||
await fixture.whenStable();
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [BulkActionsDropdownComponent, AppTestingModule],
|
||||
providers: [{ provide: MatIconRegistry, useClass: FakeMatIconRegistry }]
|
||||
}).compileComponents();
|
||||
|
||||
store = TestBed.inject(Store);
|
||||
translationService = TestBed.inject(TranslationService);
|
||||
|
||||
spyOn(store, 'select').and.returnValue(totalItemsMock$);
|
||||
spyOn(translationService, 'get').and.callFake((key) => of(key));
|
||||
|
||||
fixture = TestBed.createComponent(BulkActionsDropdownComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
component.items = [mockItem];
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when there are no search items', () => {
|
||||
beforeEach(() => {
|
||||
totalItemsMock$.next(0);
|
||||
fixture.detectChanges();
|
||||
dropdown = getElement('aca-bulk-actions-dropdown');
|
||||
bulkFormField = getElement('aca-bulk-actions-form-field');
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should disable dropdown', () => {
|
||||
expect(dropdown.getAttribute('aria-disabled')).toBe('true');
|
||||
});
|
||||
|
||||
it('should have correct tooltip', () => {
|
||||
expect(bulkFormField.getAttribute('title')).toBe('SEARCH.BULK_ACTIONS_DROPDOWN.BULK_NOT_AVAILABLE_TOOLTIP');
|
||||
});
|
||||
|
||||
it('should have correct placeholder', () => {
|
||||
expect(getLabelText('aca-bulk-actions-dropdown')).toEqual('SEARCH.BULK_ACTIONS_DROPDOWN.BULK_NOT_AVAILABLE');
|
||||
});
|
||||
|
||||
it('should call translationService.get with correct arguments', () => {
|
||||
expect(translationService.get).toHaveBeenCalledWith('SEARCH.BULK_ACTIONS_DROPDOWN.BULK_NOT_AVAILABLE');
|
||||
expect(translationService.get).toHaveBeenCalledWith('SEARCH.BULK_ACTIONS_DROPDOWN.BULK_NOT_AVAILABLE_TOOLTIP');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are search items', () => {
|
||||
beforeEach(() => {
|
||||
totalItemsMock$.next(10);
|
||||
fixture.detectChanges();
|
||||
dropdown = getElement('aca-bulk-actions-dropdown');
|
||||
bulkFormField = getElement('aca-bulk-actions-form-field');
|
||||
dropdown.click();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should enable dropdown', () => {
|
||||
expect(dropdown.getAttribute('aria-disabled')).toBe('false');
|
||||
});
|
||||
|
||||
it('should have correct tooltip', () => {
|
||||
expect(bulkFormField.getAttribute('title')).toBe('SEARCH.BULK_ACTIONS_DROPDOWN.TITLE');
|
||||
});
|
||||
|
||||
it('should have correct placeholder', () => {
|
||||
expect(getLabelText('aca-bulk-actions-dropdown')).toEqual('SEARCH.BULK_ACTIONS_DROPDOWN.TITLE');
|
||||
});
|
||||
|
||||
it('should have option with correct tooltip', () => {
|
||||
const option = getElement('mockId');
|
||||
|
||||
expect(option.getAttribute('title')).toEqual('some tooltip');
|
||||
});
|
||||
|
||||
it('should have option with correct label', () => {
|
||||
const optionLabel = getLabelText('mockId');
|
||||
|
||||
expect(optionLabel).toEqual('some title');
|
||||
});
|
||||
|
||||
it('should have correct icon in an option', () => {
|
||||
const icon = getElement('aca-bulk-action-icon-mockId');
|
||||
|
||||
expect(icon.getAttribute('title')).toEqual('some title');
|
||||
});
|
||||
|
||||
it('should call translationService.get with correct arguments', () => {
|
||||
expect(translationService.get).toHaveBeenCalledWith('SEARCH.BULK_ACTIONS_DROPDOWN.TITLE', { count: 10 });
|
||||
});
|
||||
|
||||
describe('when extension service is used', () => {
|
||||
beforeEach(() => {
|
||||
extensionService = TestBed.inject(AppExtensionService);
|
||||
spyOn(extensionService, 'getBulkActions').and.returnValue(of([mockItem]));
|
||||
fixture.detectChanges();
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
});
|
||||
|
||||
it('should run action on selection', async () => {
|
||||
spyOn(extensionService, 'runActionById');
|
||||
await selectOptionFromDropdown(0);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(extensionService.runActionById).toHaveBeenCalledWith(mockItem.actions.click, {
|
||||
focusedElementOnCloseSelector: '.adf-context-menu-source'
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset selection on bulkActionExecuted', async () => {
|
||||
await selectOptionFromDropdown(0);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.bulkSelectControl.value).toEqual(mockItem);
|
||||
|
||||
extensionService.bulkActionExecuted();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.bulkSelectControl.value).toBeNull();
|
||||
});
|
||||
|
||||
it('should run dropdown action on Enter', () => {
|
||||
spyOn(component, 'runAction');
|
||||
component.bulkSelectControl.setValue(mockItem);
|
||||
const selectElement = getElement('aca-bulk-actions-dropdown');
|
||||
selectElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
|
||||
expect(component.runAction).toHaveBeenCalledWith(mockItem);
|
||||
});
|
||||
|
||||
it('should NOT run dropdown action on Tab', () => {
|
||||
spyOn(component, 'runAction');
|
||||
component.bulkSelectControl.setValue(mockItem);
|
||||
const selectElement = getElement('aca-bulk-actions-dropdown');
|
||||
selectElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
|
||||
|
||||
expect(component.runAction).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,109 @@
|
||||
/*!
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ContentActionRef } from '@alfresco/adf-extensions';
|
||||
import { AppStore, getSearchItemsTotalCount } from '@alfresco/aca-shared/store';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { IconComponent, TranslationService } from '@alfresco/adf-core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { AppExtensionService } from '@alfresco/aca-shared';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'aca-bulk-actions-dropdown',
|
||||
templateUrl: './bulk-actions-dropdown.component.html',
|
||||
styleUrls: ['./bulk-actions-dropdown.component.scss'],
|
||||
imports: [CommonModule, TranslateModule, MatSelectModule, IconComponent, ReactiveFormsModule],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class BulkActionsDropdownComponent implements OnInit, OnDestroy {
|
||||
@Input() items: ContentActionRef[];
|
||||
|
||||
placeholder: string;
|
||||
tooltip: string;
|
||||
bulkSelectControl = new FormControl();
|
||||
|
||||
private readonly totalItems$: Observable<number> = this.store.select(getSearchItemsTotalCount);
|
||||
private readonly onDestroy$ = new Subject();
|
||||
|
||||
constructor(private store: Store<AppStore>, private translationService: TranslationService, private extensions: AppExtensionService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.totalItems$
|
||||
.pipe(
|
||||
switchMap((totalItems) => {
|
||||
if (totalItems > 0) {
|
||||
this.bulkSelectControl.enable();
|
||||
|
||||
return combineLatest([
|
||||
this.translationService.get('SEARCH.BULK_ACTIONS_DROPDOWN.TITLE', { count: totalItems }),
|
||||
this.translationService.get('SEARCH.BULK_ACTIONS_DROPDOWN.TITLE', { count: totalItems })
|
||||
]);
|
||||
} else {
|
||||
this.bulkSelectControl.disable();
|
||||
|
||||
return combineLatest([
|
||||
this.translationService.get('SEARCH.BULK_ACTIONS_DROPDOWN.BULK_NOT_AVAILABLE'),
|
||||
this.translationService.get('SEARCH.BULK_ACTIONS_DROPDOWN.BULK_NOT_AVAILABLE_TOOLTIP')
|
||||
]);
|
||||
}
|
||||
}),
|
||||
takeUntil(this.onDestroy$)
|
||||
)
|
||||
.subscribe(([placeholder, title]) => {
|
||||
this.tooltip = title;
|
||||
this.placeholder = placeholder;
|
||||
});
|
||||
|
||||
this.extensions.bulkActionExecuted$.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
|
||||
this.bulkSelectControl.setValue(null);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy$.next();
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
runAction(actionOption: ContentActionRef) {
|
||||
this.extensions.runActionById(actionOption.actions.click, {
|
||||
focusedElementOnCloseSelector: '.adf-context-menu-source'
|
||||
});
|
||||
}
|
||||
|
||||
onKeyDown(event: KeyboardEvent) {
|
||||
if (event.key === 'Tab') {
|
||||
this.bulkSelectControl.setValue(null);
|
||||
}
|
||||
if (event.key === 'Enter' && this.bulkSelectControl.value) {
|
||||
this.runAction(this.bulkSelectControl.value);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<div class="aca-datatable-cell-badges-container">
|
||||
<ng-container *ngFor="let badge of badges">
|
||||
<adf-dynamic-component
|
||||
*ngIf="badge.component; else iconBadge"
|
||||
[id]="badge.component"
|
||||
[data]="{ node }"
|
||||
></adf-dynamic-component>
|
||||
<ng-template #iconBadge>
|
||||
<adf-icon
|
||||
class="adf-datatable-cell-badge"
|
||||
[title]="badge.tooltip | translate"
|
||||
[value]="badge.icon"
|
||||
(click)="onBadgeClick(badge)"
|
||||
(keypress.enter)="onKeyPress(badge)"
|
||||
tabindex="0"
|
||||
></adf-icon>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
@ -0,0 +1,7 @@
|
||||
.aca-datatable-cell-badges-container {
|
||||
display: flex;
|
||||
|
||||
.adf-datatable-cell-badge {
|
||||
color: var(--theme-secondary-text);
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*!
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { DatatableCellBadgesComponent } from './datatable-cell-badges.component';
|
||||
import { AppExtensionService } from '@alfresco/aca-shared';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AuthModule } from '@alfresco/adf-core';
|
||||
import { Actions } from '@ngrx/effects';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { of } from 'rxjs';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ContentActionType } from '@alfresco/adf-extensions';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
|
||||
const mockNode = {
|
||||
entry: {
|
||||
isFile: true,
|
||||
id: 'nodeId'
|
||||
}
|
||||
} as NodeEntry;
|
||||
|
||||
const mockGetBadgesResponse = {
|
||||
id: 'test',
|
||||
type: ContentActionType.custom,
|
||||
icon: 'warning',
|
||||
tooltip: 'test tooltip'
|
||||
};
|
||||
|
||||
describe('DatatableCellBadgesComponent', () => {
|
||||
let fixture: ComponentFixture<DatatableCellBadgesComponent>;
|
||||
let component: DatatableCellBadgesComponent;
|
||||
let appExtensionService: AppExtensionService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
AuthModule.forRoot(),
|
||||
HttpClientModule,
|
||||
StoreModule.forRoot(
|
||||
{ app: (state) => state },
|
||||
{
|
||||
initialState: {
|
||||
app: {
|
||||
selection: {
|
||||
nodes: [],
|
||||
libraries: [],
|
||||
isEmpty: true,
|
||||
count: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
],
|
||||
providers: [Actions]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(DatatableCellBadgesComponent);
|
||||
component = fixture.componentInstance;
|
||||
appExtensionService = TestBed.inject(AppExtensionService);
|
||||
component.node = mockNode;
|
||||
});
|
||||
|
||||
it('should get badges when component initializes', () => {
|
||||
spyOn(appExtensionService, 'getBadges').and.returnValue(of([mockGetBadgesResponse]));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
const badges = fixture.debugElement.queryAll(By.css('.adf-datatable-cell-badge')).map((badge) => badge.nativeElement);
|
||||
expect(appExtensionService.getBadges).toHaveBeenCalled();
|
||||
expect(badges.length).toBe(1);
|
||||
expect(badges[0].innerText).toBe('warning');
|
||||
expect(badges[0].attributes['title'].value).toBe('test tooltip');
|
||||
});
|
||||
|
||||
it('should render dynamic component when badge has one provided', () => {
|
||||
spyOn(appExtensionService, 'getBadges').and.returnValue(of([{ ...mockGetBadgesResponse, component: 'test-id' }]));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
const dynamicComponent = fixture.debugElement.query(By.css('adf-dynamic-component')).nativeElement;
|
||||
expect(dynamicComponent).toBeDefined();
|
||||
});
|
||||
|
||||
describe('mouse and keyboard events', () => {
|
||||
let badges: HTMLElement[];
|
||||
let runActionSpy: jasmine.Spy;
|
||||
beforeEach(() => {
|
||||
spyOn(appExtensionService, 'getBadges').and.returnValue(of([{ ...mockGetBadgesResponse, actions: { click: 'test' } }]));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
badges = fixture.debugElement.queryAll(By.css('.adf-datatable-cell-badge')).map((badge) => badge.nativeElement);
|
||||
runActionSpy = spyOn(appExtensionService, 'runActionById');
|
||||
});
|
||||
|
||||
it('should call provided handler on click', () => {
|
||||
badges[0].click();
|
||||
expect(runActionSpy).toHaveBeenCalledWith('test', component.node);
|
||||
});
|
||||
|
||||
it('should call provided handler on keypress event', () => {
|
||||
badges[0].dispatchEvent(new KeyboardEvent('keypress.enter'));
|
||||
expect(runActionSpy).toHaveBeenCalledWith('test', component.node);
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,76 @@
|
||||
/*!
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Alfresco Example Content Application
|
||||
*
|
||||
* This file is part of the Alfresco Example Content Application.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { AppExtensionService, Badge } from '@alfresco/aca-shared';
|
||||
import { IconComponent } from '@alfresco/adf-core';
|
||||
import { DynamicExtensionComponent } from '@alfresco/adf-extensions';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-datatable-cell-badges',
|
||||
templateUrl: './datatable-cell-badges.component.html',
|
||||
styleUrls: ['./datatable-cell-badges.component.scss'],
|
||||
host: { class: 'aca-datatable-cell-badges' },
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
imports: [CommonModule, TranslateModule, DynamicExtensionComponent, IconComponent],
|
||||
standalone: true
|
||||
})
|
||||
export class DatatableCellBadgesComponent implements OnInit, OnDestroy {
|
||||
@Input() node: NodeEntry;
|
||||
|
||||
badges: Badge[];
|
||||
|
||||
private onDestroy$ = new Subject<boolean>();
|
||||
|
||||
constructor(private appExtensionService: AppExtensionService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.appExtensionService
|
||||
.getBadges(this.node)
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe((badges) => {
|
||||
this.badges = badges;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
onBadgeClick(badge: Badge) {
|
||||
if (badge.actions?.click) {
|
||||
this.appExtensionService.runActionById(badge.actions?.click, this.node);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyPress(badge: Badge) {
|
||||
this.onBadgeClick(badge);
|
||||
}
|
||||
}
|
@ -19,12 +19,5 @@
|
||||
<aca-locked-by [node]="context.row.node"></aca-locked-by>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="aca-name-column-badges">
|
||||
<ng-container *ngFor="let badge of badges">
|
||||
<adf-dynamic-component *ngIf="badge.component; else iconBadge" [id]="badge.component" [data]="{ node }"></adf-dynamic-component>
|
||||
<ng-template #iconBadge>
|
||||
<adf-icon class="adf-datatable-cell-badge" [title]="badge.tooltip | translate" [value]="badge.icon" (click)="onBadgeClick(badge)"></adf-icon>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
<aca-datatable-cell-badges [node]="node"></aca-datatable-cell-badges>
|
||||
</div>
|
||||
|
@ -4,14 +4,6 @@
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
.aca-name-column-badges {
|
||||
display: flex;
|
||||
|
||||
.adf-datatable-cell-badge {
|
||||
color: var(--theme-secondary-text);
|
||||
}
|
||||
}
|
||||
|
||||
.aca-name-column-container {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@ -36,6 +28,6 @@
|
||||
position: unset;
|
||||
}
|
||||
|
||||
.adf-datatable-list .adf-datatable-link:hover .aca-name-column-badges {
|
||||
.adf-datatable-list .adf-datatable-link:hover .aca-datatable-cell-badges {
|
||||
color: var(--adf-theme-foreground-text-color);
|
||||
}
|
||||
|
@ -28,16 +28,23 @@ import { StoreModule } from '@ngrx/store';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AppExtensionService } from '@alfresco/aca-shared';
|
||||
import { of } from 'rxjs';
|
||||
import { ContentActionType } from '@alfresco/adf-extensions';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { AuthModule } from '@alfresco/adf-core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-datatable-cell-badges',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockDatatableCellBadgesComponent {
|
||||
@Input() node: NodeEntry;
|
||||
}
|
||||
|
||||
describe('CustomNameColumnComponent', () => {
|
||||
let fixture: ComponentFixture<CustomNameColumnComponent>;
|
||||
let component: CustomNameColumnComponent;
|
||||
let appExtensionService: AppExtensionService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@ -45,6 +52,7 @@ describe('CustomNameColumnComponent', () => {
|
||||
HttpClientModule,
|
||||
TranslateModule.forRoot(),
|
||||
CustomNameColumnComponent,
|
||||
MockDatatableCellBadgesComponent,
|
||||
AuthModule.forRoot(),
|
||||
StoreModule.forRoot(
|
||||
{ app: (state) => state },
|
||||
@ -67,7 +75,6 @@ describe('CustomNameColumnComponent', () => {
|
||||
|
||||
fixture = TestBed.createComponent(CustomNameColumnComponent);
|
||||
component = fixture.componentInstance;
|
||||
appExtensionService = TestBed.inject(AppExtensionService);
|
||||
});
|
||||
|
||||
it('should not render lock element if file is not locked', () => {
|
||||
@ -142,54 +149,9 @@ describe('CustomNameColumnComponent', () => {
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('Name column badges', () => {
|
||||
beforeEach(() => {
|
||||
component.context = {
|
||||
row: {
|
||||
node: {
|
||||
entry: {
|
||||
isFile: true,
|
||||
id: 'nodeId'
|
||||
}
|
||||
},
|
||||
getValue: (key: string) => key
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should get badges when component initializes', () => {
|
||||
spyOn(appExtensionService, 'getBadges').and.returnValue(
|
||||
of([{ id: 'test', type: ContentActionType.custom, icon: 'warning', tooltip: 'test tooltip' }])
|
||||
);
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
const badges = fixture.debugElement.queryAll(By.css('.adf-datatable-cell-badge')).map((badge) => badge.nativeElement);
|
||||
expect(appExtensionService.getBadges).toHaveBeenCalled();
|
||||
expect(badges.length).toBe(1);
|
||||
expect(badges[0].innerText).toBe('warning');
|
||||
expect(badges[0].attributes['title'].value).toBe('test tooltip');
|
||||
});
|
||||
|
||||
it('should call provided handler on click', () => {
|
||||
spyOn(appExtensionService, 'runActionById');
|
||||
spyOn(appExtensionService, 'getBadges').and.returnValue(
|
||||
of([{ id: 'test', type: ContentActionType.custom, icon: 'warning', tooltip: 'test tooltip', actions: { click: 'test' } }])
|
||||
);
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
const badges = fixture.debugElement.queryAll(By.css('.adf-datatable-cell-badge')).map((badge) => badge.nativeElement);
|
||||
badges[0].click();
|
||||
expect(appExtensionService.runActionById).toHaveBeenCalledWith('test', component.context.row.node);
|
||||
});
|
||||
|
||||
it('should render dynamic component when badge has one provided', () => {
|
||||
spyOn(appExtensionService, 'getBadges').and.returnValue(
|
||||
of([{ id: 'test', type: ContentActionType.custom, icon: 'warning', tooltip: 'test tooltip', component: 'test-id' }])
|
||||
);
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
const dynamicComponent = fixture.debugElement.query(By.css('adf-dynamic-component')).nativeElement;
|
||||
expect(dynamicComponent).toBeDefined();
|
||||
});
|
||||
it('should pass node to badge component', () => {
|
||||
const badgeElement = fixture.debugElement.query(By.css('aca-datatable-cell-badges'));
|
||||
expect(badgeElement).not.toBe(null);
|
||||
expect(badgeElement.componentInstance.node).toBe(component.node);
|
||||
});
|
||||
});
|
||||
|
@ -28,15 +28,24 @@ import { Actions, ofType } from '@ngrx/effects';
|
||||
import { Subject } from 'rxjs';
|
||||
import { filter, takeUntil } from 'rxjs/operators';
|
||||
import { NodeActionTypes } from '@alfresco/aca-shared/store';
|
||||
import { LockedByComponent, isLocked, AppExtensionService, Badge } from '@alfresco/aca-shared';
|
||||
import { LockedByComponent, isLocked } from '@alfresco/aca-shared';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IconComponent } from '@alfresco/adf-core';
|
||||
import { DynamicExtensionComponent } from '@alfresco/adf-extensions';
|
||||
import { DatatableCellBadgesComponent } from '../datatable-cell-badges/datatable-cell-badges.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule, TranslateModule, LockedByComponent, IconComponent, NodeNameTooltipPipe, DynamicExtensionComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
TranslateModule,
|
||||
LockedByComponent,
|
||||
IconComponent,
|
||||
NodeNameTooltipPipe,
|
||||
DynamicExtensionComponent,
|
||||
DatatableCellBadgesComponent
|
||||
],
|
||||
selector: 'aca-custom-name-column',
|
||||
templateUrl: './name-column.component.html',
|
||||
styleUrls: ['./name-column.component.scss'],
|
||||
@ -50,15 +59,8 @@ export class CustomNameColumnComponent extends NameColumnComponent implements On
|
||||
|
||||
isFile: boolean;
|
||||
isFileWriteLocked: boolean;
|
||||
badges: Badge[];
|
||||
|
||||
constructor(
|
||||
element: ElementRef,
|
||||
private cd: ChangeDetectorRef,
|
||||
private actions$: Actions,
|
||||
private nodesService: NodesApiService,
|
||||
private appExtensionService: AppExtensionService
|
||||
) {
|
||||
constructor(element: ElementRef, private cd: ChangeDetectorRef, private actions$: Actions, private nodesService: NodesApiService) {
|
||||
super(element, nodesService);
|
||||
}
|
||||
|
||||
@ -95,13 +97,6 @@ export class CustomNameColumnComponent extends NameColumnComponent implements On
|
||||
this.isFileWriteLocked = isLocked(this.node);
|
||||
this.cd.detectChanges();
|
||||
});
|
||||
|
||||
this.appExtensionService
|
||||
.getBadges(this.node)
|
||||
.pipe(takeUntil(this.onDestroy$$))
|
||||
.subscribe((badges) => {
|
||||
this.badges = badges;
|
||||
});
|
||||
}
|
||||
|
||||
onLinkClick(event: Event) {
|
||||
@ -115,10 +110,4 @@ export class CustomNameColumnComponent extends NameColumnComponent implements On
|
||||
this.onDestroy$$.next(true);
|
||||
this.onDestroy$$.complete();
|
||||
}
|
||||
|
||||
onBadgeClick(badge: Badge) {
|
||||
if (badge.actions?.click) {
|
||||
this.appExtensionService.runActionById(badge.actions?.click, this.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +1,41 @@
|
||||
<div class="search-file-name">
|
||||
<span
|
||||
tabindex="0"
|
||||
role="link"
|
||||
*ngIf="isFile"
|
||||
(click)="showPreview($event)"
|
||||
(keyup.enter)="showPreview($event)"
|
||||
class="aca-link aca-crop-text"
|
||||
[title]="nameStripped"
|
||||
[innerHTML]="name$ | async"
|
||||
></span>
|
||||
<span
|
||||
tabindex="0"
|
||||
role="link"
|
||||
*ngIf="!isFile"
|
||||
(click)="navigate($event)"
|
||||
(keyup.enter)="navigate($event)"
|
||||
class="bold aca-link aca-crop-text"
|
||||
[title]="nameStripped"
|
||||
[innerHTML]="name$ | async"
|
||||
></span>
|
||||
<span
|
||||
data-automation-id="search-results-entry-title"
|
||||
<div class="aca-search-results-text">
|
||||
<div class="search-file-name">
|
||||
<span
|
||||
tabindex="0"
|
||||
role="link"
|
||||
*ngIf="isFile"
|
||||
(click)="showPreview($event)"
|
||||
(keyup.enter)="showPreview($event)"
|
||||
class="aca-link aca-crop-text"
|
||||
[title]="nameStripped"
|
||||
[innerHTML]="name$ | async"
|
||||
></span>
|
||||
<span
|
||||
tabindex="0"
|
||||
role="link"
|
||||
*ngIf="!isFile"
|
||||
(click)="navigate($event)"
|
||||
(keyup.enter)="navigate($event)"
|
||||
class="bold aca-link aca-crop-text"
|
||||
[title]="nameStripped"
|
||||
[innerHTML]="name$ | async"
|
||||
></span>
|
||||
<span
|
||||
data-automation-id="search-results-entry-title"
|
||||
class="aca-crop-text"
|
||||
[title]="titleStripped"
|
||||
[innerHTML]="title$ | async"
|
||||
></span>
|
||||
</div>
|
||||
<div
|
||||
data-automation-id="search-results-entry-description"
|
||||
class="aca-crop-text"
|
||||
[title]="titleStripped"
|
||||
[innerHTML]="title$ | async"
|
||||
></span>
|
||||
[title]="descriptionStripped"
|
||||
[innerHTML]="description$ | async"
|
||||
></div>
|
||||
<div class="aca-result-location">
|
||||
<aca-location-link [context]="context" [showLocation]="true"></aca-location-link>
|
||||
</div>
|
||||
<div class="aca-result-content aca-crop-text" [title]="contentStripped" [innerHTML]="content$ | async"></div>
|
||||
</div>
|
||||
<div
|
||||
data-automation-id="search-results-entry-description"
|
||||
class="aca-crop-text"
|
||||
[title]="descriptionStripped"
|
||||
[innerHTML]="description$ | async"
|
||||
></div>
|
||||
<div class="aca-result-location">
|
||||
<aca-location-link [context]="context" [showLocation]="true"></aca-location-link>
|
||||
</div>
|
||||
<div class="aca-result-content aca-crop-text" [title]="contentStripped" [innerHTML]="content$ | async"></div>
|
||||
<aca-datatable-cell-badges [node]="context.row.node"></aca-datatable-cell-badges>
|
||||
|
@ -1,4 +1,7 @@
|
||||
.aca-search-results-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
width: inherit;
|
||||
|
||||
|
@ -28,6 +28,16 @@ import { By } from '@angular/platform-browser';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||
import { SearchResultsRowComponent } from './search-results-row.component';
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-datatable-cell-badges',
|
||||
standalone: true,
|
||||
template: ''
|
||||
})
|
||||
class MockDatatableCellBadgesComponent {
|
||||
@Input() node: NodeEntry;
|
||||
}
|
||||
|
||||
describe('SearchResultsRowComponent', () => {
|
||||
let component: SearchResultsRowComponent;
|
||||
@ -76,7 +86,7 @@ describe('SearchResultsRowComponent', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [AppTestingModule, SearchResultsRowComponent]
|
||||
imports: [AppTestingModule, SearchResultsRowComponent, MockDatatableCellBadgesComponent]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(SearchResultsRowComponent);
|
||||
@ -120,4 +130,11 @@ describe('SearchResultsRowComponent', () => {
|
||||
});
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should pass node to badge component', () => {
|
||||
component.context = { row: { node: nodeEntry } };
|
||||
const badgeElement = fixture.debugElement.query(By.css('aca-datatable-cell-badges'));
|
||||
expect(badgeElement).not.toBe(null);
|
||||
expect(badgeElement.componentInstance.node).toBe(component.context.node);
|
||||
});
|
||||
});
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
import { Component, Input, OnInit, ViewEncapsulation, ChangeDetectionStrategy, OnDestroy, inject } from '@angular/core';
|
||||
import { NodeEntry, SearchEntryHighlight } from '@alfresco/js-api';
|
||||
import { ViewNodeAction, NavigateToFolder } from '@alfresco/aca-shared/store';
|
||||
import { NavigateToFolder, ViewNodeAction } from '@alfresco/aca-shared/store';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { NodesApiService } from '@alfresco/adf-content-services';
|
||||
@ -34,10 +34,11 @@ import { AutoDownloadService, AppSettingsService } from '@alfresco/aca-shared';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { LocationLinkComponent } from '../../common/location-link/location-link.component';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { DatatableCellBadgesComponent } from '../../dl-custom-components/datatable-cell-badges/datatable-cell-badges.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule, LocationLinkComponent, MatDialogModule],
|
||||
imports: [CommonModule, LocationLinkComponent, MatDialogModule, DatatableCellBadgesComponent],
|
||||
selector: 'aca-search-results-row',
|
||||
templateUrl: './search-results-row.component.html',
|
||||
styleUrls: ['./search-results-row.component.scss'],
|
||||
@ -65,7 +66,6 @@ export class SearchResultsRowComponent implements OnInit, OnDestroy {
|
||||
titleStripped = '';
|
||||
descriptionStripped = '';
|
||||
contentStripped = '';
|
||||
|
||||
isFile = false;
|
||||
|
||||
constructor(
|
||||
|
@ -1,6 +1,7 @@
|
||||
<aca-page-layout>
|
||||
<div class="aca-page-layout-header">
|
||||
<aca-search-input></aca-search-input>
|
||||
<aca-bulk-actions-dropdown *ngIf="bulkActions" [items]="bulkActions"></aca-bulk-actions-dropdown>
|
||||
<div class="aca-search-toolbar-spacer"></div>
|
||||
<aca-toolbar [items]="actions"></aca-toolbar>
|
||||
</div>
|
||||
|
@ -39,7 +39,8 @@ import {
|
||||
NavigateToFolder,
|
||||
SetInfoDrawerPreviewStateAction,
|
||||
SetInfoDrawerStateAction,
|
||||
ShowInfoDrawerPreviewAction
|
||||
ShowInfoDrawerPreviewAction,
|
||||
SetSearchItemsTotalCountAction
|
||||
} from '@alfresco/aca-shared/store';
|
||||
import {
|
||||
CustomEmptyContentTemplateDirective,
|
||||
@ -75,6 +76,7 @@ import { TagsColumnComponent } from '../../dl-custom-components/tags-column/tags
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { SearchResultsRowComponent } from '../search-results-row/search-results-row.component';
|
||||
import { DocumentListPresetRef, DynamicColumnComponent } from '@alfresco/adf-extensions';
|
||||
import { BulkActionsDropdownComponent } from '../../bulk-actions-dropdown/bulk-actions-dropdown.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@ -107,7 +109,8 @@ import { DocumentListPresetRef, DynamicColumnComponent } from '@alfresco/adf-ext
|
||||
DataColumnComponent,
|
||||
DateColumnHeaderComponent,
|
||||
CustomEmptyContentTemplateDirective,
|
||||
ViewerToolbarComponent
|
||||
ViewerToolbarComponent,
|
||||
BulkActionsDropdownComponent
|
||||
],
|
||||
selector: 'aca-search-results',
|
||||
templateUrl: './search-results.component.html',
|
||||
@ -268,6 +271,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
||||
onSearchResultLoaded(nodePaging: ResultSetPaging) {
|
||||
this.data = nodePaging;
|
||||
this.totalResults = this.getNumberOfResults();
|
||||
this.store.dispatch(new SetSearchItemsTotalCountAction(this.totalResults));
|
||||
}
|
||||
|
||||
getNumberOfResults() {
|
||||
|
@ -38,7 +38,8 @@ import {
|
||||
SetInfoDrawerPreviewStateAction,
|
||||
AppActionTypes,
|
||||
ShowLoaderAction,
|
||||
INITIAL_APP_STATE
|
||||
INITIAL_APP_STATE,
|
||||
SetSearchItemsTotalCountAction
|
||||
} from '@alfresco/aca-shared/store';
|
||||
|
||||
export function appReducer(state: AppState = INITIAL_APP_STATE, action: Action): AppState {
|
||||
@ -84,6 +85,12 @@ export function appReducer(state: AppState = INITIAL_APP_STATE, action: Action):
|
||||
case AppActionTypes.ShowLoaderAction:
|
||||
newState = showLoader(state, action as ShowLoaderAction);
|
||||
break;
|
||||
case AppActionTypes.SetSearchItemsTotalCount:
|
||||
newState = {
|
||||
...state,
|
||||
searchItemsTotalCount: (action as SetSearchItemsTotalCountAction).payload
|
||||
};
|
||||
break;
|
||||
default:
|
||||
newState = { ...state };
|
||||
}
|
||||
|
@ -61,12 +61,13 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
|
||||
selection: SelectionState;
|
||||
actions: Array<ContentActionRef> = [];
|
||||
viewerToolbarActions: Array<ContentActionRef> = [];
|
||||
bulkActions: ContentActionRef[] = [];
|
||||
canUpdateNode = false;
|
||||
canUpload = false;
|
||||
nodeResult: NodePaging;
|
||||
showHeader = ShowHeaderMode.Data;
|
||||
filterSorting = 'name-asc';
|
||||
createActions: Array<ContentActionRef> = [];
|
||||
createActions: ContentActionRef[] = [];
|
||||
isSmallScreen = false;
|
||||
selectedRowItemsCount = 0;
|
||||
|
||||
@ -107,6 +108,13 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges {
|
||||
this.actions = actions;
|
||||
});
|
||||
|
||||
this.extensions
|
||||
.getBulkActions()
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe((actions) => {
|
||||
this.bulkActions = actions;
|
||||
});
|
||||
|
||||
this.extensions
|
||||
.getViewerToolbarActions()
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
|
@ -29,6 +29,8 @@ export interface ExtensionRoute extends Route {
|
||||
parentRoute?: string;
|
||||
}
|
||||
|
||||
export interface Badge extends ContentActionRef {
|
||||
export interface Badge extends Partial<Pick<ContentActionRef, 'component' | 'actions' | 'rules'>> {
|
||||
id: string;
|
||||
icon: string;
|
||||
tooltip: string;
|
||||
}
|
||||
|
@ -88,6 +88,16 @@ describe('AppExtensionService', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const defaultConfigMock = {
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
features: {}
|
||||
} as ExtensionConfig;
|
||||
|
||||
describe('configs', () => {
|
||||
it('should log an error during setup', async () => {
|
||||
spyOn(extensions, 'load').and.returnValue(Promise.resolve(null));
|
||||
@ -100,12 +110,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should load content metadata presets', () => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
'content-metadata-presets': [
|
||||
{
|
||||
@ -180,12 +185,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should support column orders', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
documentList: {
|
||||
files: [
|
||||
@ -250,12 +250,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should ignore column if visibility in rules is false', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
documentList: {
|
||||
files: [
|
||||
@ -301,12 +296,7 @@ describe('AppExtensionService', () => {
|
||||
describe('actions', () => {
|
||||
beforeEach(() => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
actions: [
|
||||
{
|
||||
id: 'aca:actions/create-folder',
|
||||
@ -415,12 +405,7 @@ describe('AppExtensionService', () => {
|
||||
describe('content actions', () => {
|
||||
it('should load content actions from the config', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
toolbar: [
|
||||
{
|
||||
@ -447,12 +432,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should sort content actions by order', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
toolbar: [
|
||||
{
|
||||
@ -483,12 +463,7 @@ describe('AppExtensionService', () => {
|
||||
describe('open with', () => {
|
||||
it('should load [open with] actions for the viewer', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
viewer: {
|
||||
openWith: [
|
||||
@ -516,12 +491,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should load only enabled [open with] actions for the viewer', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
viewer: {
|
||||
openWith: [
|
||||
@ -560,12 +530,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should sort [open with] actions by order', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
viewer: {
|
||||
openWith: [
|
||||
@ -606,12 +571,7 @@ describe('AppExtensionService', () => {
|
||||
describe('create', () => {
|
||||
it('should load [create] actions from config', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
create: [
|
||||
{
|
||||
@ -633,12 +593,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should sort [create] actions by order', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
create: [
|
||||
{
|
||||
@ -876,12 +831,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
applyConfig(
|
||||
{
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
viewer: {
|
||||
shared: {
|
||||
@ -905,14 +855,7 @@ describe('AppExtensionService', () => {
|
||||
appConfigService.config = {
|
||||
auth: { withCredentials: true }
|
||||
};
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0'
|
||||
});
|
||||
applyConfig(defaultConfigMock);
|
||||
|
||||
expect(service.withCredentials).toBe(true);
|
||||
});
|
||||
@ -921,28 +864,14 @@ describe('AppExtensionService', () => {
|
||||
appConfigService.config = {
|
||||
auth: { withCredentials: false }
|
||||
};
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0'
|
||||
});
|
||||
applyConfig(defaultConfigMock);
|
||||
|
||||
expect(service.withCredentials).toBe(false);
|
||||
});
|
||||
|
||||
it('should set `withCredentials` to false as default value if no app configuration', () => {
|
||||
appConfigService.config = {};
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0'
|
||||
});
|
||||
applyConfig(defaultConfigMock);
|
||||
|
||||
expect(service.withCredentials).toBe(false);
|
||||
});
|
||||
@ -951,12 +880,7 @@ describe('AppExtensionService', () => {
|
||||
describe('getHeaderActions', () => {
|
||||
it('should load user actions from the config', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
header: [
|
||||
{
|
||||
@ -981,12 +905,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should sort header actions by order', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
header: [
|
||||
{
|
||||
@ -1013,12 +932,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should sort header menu children actions by order', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
header: [
|
||||
{
|
||||
@ -1060,12 +974,7 @@ describe('AppExtensionService', () => {
|
||||
notVisible: () => false
|
||||
});
|
||||
config = {
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
search: [
|
||||
{
|
||||
@ -1361,12 +1270,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should set the action disabled for create actions', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
create: actions
|
||||
}
|
||||
@ -1380,12 +1284,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should set the action disabled for sidebar actions', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
sidebar: {
|
||||
toolbar: actions
|
||||
@ -1401,12 +1300,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should set the action disabled for toolbar actions', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
toolbar: actions
|
||||
}
|
||||
@ -1420,12 +1314,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should set the action disabled for viewer toolbar actions', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
viewer: { toolbarActions: actions }
|
||||
}
|
||||
@ -1440,12 +1329,7 @@ describe('AppExtensionService', () => {
|
||||
it('should set the action disabled for shared link viewer toolbar actions', (done) => {
|
||||
applyConfig(
|
||||
{
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
viewer: {
|
||||
shared: {
|
||||
@ -1465,12 +1349,7 @@ describe('AppExtensionService', () => {
|
||||
|
||||
it('should set the action disabled for header actions', (done) => {
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
header: actions
|
||||
}
|
||||
@ -1482,15 +1361,27 @@ describe('AppExtensionService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the action disabled for bulk actions dropdown actions', (done) => {
|
||||
applyConfig(
|
||||
{
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
'bulk-actions': actions
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
service.getBulkActions().subscribe((bulkActions) => {
|
||||
expect(bulkActions).toEqual(expectedActionsWithoutChildren);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the action disabled for context menu actions', (done) => {
|
||||
applyConfig(
|
||||
{
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
contextMenu: actions
|
||||
}
|
||||
@ -1524,12 +1415,7 @@ describe('AppExtensionService', () => {
|
||||
];
|
||||
|
||||
config = {
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
contextMenu: [...actions],
|
||||
toolbar: [...actions],
|
||||
@ -1629,12 +1515,7 @@ describe('AppExtensionService', () => {
|
||||
const rawUrl = './assets/images/ft_ic_ms_excel.svg';
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
icons: [
|
||||
{
|
||||
@ -1653,12 +1534,7 @@ describe('AppExtensionService', () => {
|
||||
const warn = spyOn(logService, 'warn').and.stub();
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
icons: [
|
||||
{
|
||||
@ -1675,12 +1551,7 @@ describe('AppExtensionService', () => {
|
||||
const warn = spyOn(logService, 'warn').and.stub();
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
icons: [
|
||||
{
|
||||
@ -1701,12 +1572,7 @@ describe('AppExtensionService', () => {
|
||||
});
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
badges: [
|
||||
{
|
||||
@ -1759,12 +1625,7 @@ describe('AppExtensionService', () => {
|
||||
});
|
||||
|
||||
applyConfig({
|
||||
$id: 'test',
|
||||
$name: 'test',
|
||||
$version: '1.0.0',
|
||||
$license: 'MIT',
|
||||
$vendor: 'Good company',
|
||||
$runtime: '1.5.0',
|
||||
...defaultConfigMock,
|
||||
features: {
|
||||
customMetadataPanels: [
|
||||
{
|
||||
@ -1814,4 +1675,14 @@ describe('AppExtensionService', () => {
|
||||
service.updateSidebarActions();
|
||||
expect(loader.getContentActions).toHaveBeenCalledWith(service.config, 'features.sidebar.toolbar');
|
||||
});
|
||||
|
||||
it('should emit bulkActionExecuted', (done) => {
|
||||
spyOn(service, 'bulkActionExecuted').and.callThrough();
|
||||
service.bulkActionExecuted$.subscribe(() => {
|
||||
expect(service.bulkActionExecuted).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
service.bulkActionExecuted();
|
||||
});
|
||||
});
|
||||
|
@ -50,7 +50,7 @@ import {
|
||||
sortByOrder
|
||||
} from '@alfresco/adf-extensions';
|
||||
import { AppConfigService, AuthenticationService, LogService } from '@alfresco/adf-core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { NodeEntry, RepositoryInfo } from '@alfresco/js-api';
|
||||
import { ViewerRules } from '../models/viewer.rules';
|
||||
import { Badge } from '../models/types';
|
||||
@ -63,6 +63,7 @@ import { SearchCategory } from '@alfresco/adf-content-services';
|
||||
})
|
||||
export class AppExtensionService implements RuleContext {
|
||||
private _references = new BehaviorSubject<ExtensionRef[]>([]);
|
||||
bulkActionExecuted$ = new Subject<void>();
|
||||
|
||||
navbar: Array<NavBarGroupRef> = [];
|
||||
sidebarTabs: Array<SidebarTabRef> = [];
|
||||
@ -81,6 +82,7 @@ export class AppExtensionService implements RuleContext {
|
||||
private _badges = new BehaviorSubject<Array<Badge>>([]);
|
||||
private _filesDocumentListPreset = new BehaviorSubject<Array<DocumentListPresetRef>>([]);
|
||||
private _customMetadataPanels = new BehaviorSubject<Array<ContentActionRef>>([]);
|
||||
private _bulkActions = new BehaviorSubject<Array<ContentActionRef>>([]);
|
||||
|
||||
documentListPresets: {
|
||||
libraries: Array<DocumentListPresetRef>;
|
||||
@ -160,6 +162,7 @@ export class AppExtensionService implements RuleContext {
|
||||
this._badges.next(this.loader.getElements<Badge>(config, 'features.badges'));
|
||||
this._filesDocumentListPreset.next(this.getDocumentListPreset(config, 'files'));
|
||||
this._customMetadataPanels.next(this.loader.getElements<ContentActionRef>(config, 'features.customMetadataPanels'));
|
||||
this._bulkActions.next(this.loader.getElements<ContentActionRef>(config, 'features.bulk-actions'));
|
||||
|
||||
this.navbar = this.loadNavBar(config);
|
||||
this.sidebarTabs = this.loader.getElements<SidebarTabRef>(config, 'features.sidebar.tabs');
|
||||
@ -424,6 +427,10 @@ export class AppExtensionService implements RuleContext {
|
||||
return this._viewerToolbarActions.pipe(map((viewerToolbarActions) => this.getAllowedActions(viewerToolbarActions)));
|
||||
}
|
||||
|
||||
getBulkActions(): Observable<Array<ContentActionRef>> {
|
||||
return this._bulkActions.pipe(map((bulkActions) => this.getAllowedActions(bulkActions)));
|
||||
}
|
||||
|
||||
getOpenWithActions(): Observable<Array<ContentActionRef>> {
|
||||
return this._openWithActions.pipe(map((openWithActions) => this.getAllowedActions(openWithActions)));
|
||||
}
|
||||
@ -565,4 +572,8 @@ export class AppExtensionService implements RuleContext {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bulkActionExecuted(): void {
|
||||
this.bulkActionExecuted$.next();
|
||||
}
|
||||
}
|
||||
|
@ -36,5 +36,6 @@ export enum AppActionTypes {
|
||||
SetFileUploadingDialog = 'SET_FILE_UPLOADING_DIALOG',
|
||||
ShowInfoDrawerPreview = 'SHOW_INFO_DRAWER_PREVIEW',
|
||||
SetInfoDrawerPreviewState = 'SET_INFO_DRAWER_PREVIEW_STATE',
|
||||
ShowLoaderAction = 'SHOW_LOADER'
|
||||
ShowLoaderAction = 'SHOW_LOADER',
|
||||
SetSearchItemsTotalCount = 'SET_SEARCH_ITEMS_TOTAL_COUNT'
|
||||
}
|
||||
|
@ -101,3 +101,9 @@ export class ShowLoaderAction implements Action {
|
||||
|
||||
constructor(public payload: boolean) {}
|
||||
}
|
||||
|
||||
export class SetSearchItemsTotalCountAction implements Action {
|
||||
readonly type = AppActionTypes.SetSearchItemsTotalCount;
|
||||
|
||||
constructor(public payload: number) {}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ export const isHXIConnectorEnabled = createSelector(getRepositoryStatus, (info)
|
||||
export const isAdmin = createSelector(selectApp, (state) => state.user.isAdmin);
|
||||
export const getFileUploadingDialog = createSelector(selectApp, (state) => state.fileUploadingDialog);
|
||||
export const showLoaderSelector = createSelector(selectApp, (state) => state.showLoader);
|
||||
export const getSearchItemsTotalCount = createSelector(selectApp, (state) => state.searchItemsTotalCount);
|
||||
|
||||
export const getSideNavState = createSelector(getAppSelection, getNavigationState, (selection, navigation) => ({
|
||||
selection,
|
||||
|
@ -55,7 +55,8 @@ export const INITIAL_APP_STATE: AppState = {
|
||||
status: {
|
||||
isQuickShareEnabled: true
|
||||
}
|
||||
} as any
|
||||
} as any,
|
||||
searchItemsTotalCount: null
|
||||
};
|
||||
|
||||
/** @deprecated no longer used */
|
||||
@ -74,6 +75,7 @@ export interface AppState {
|
||||
repository: RepositoryInfo;
|
||||
fileUploadingDialog: boolean;
|
||||
showLoader: boolean;
|
||||
searchItemsTotalCount: number;
|
||||
}
|
||||
|
||||
export interface AppStore {
|
||||
|
Loading…
x
Reference in New Issue
Block a user