diff --git a/docs/content-services/components/categories-management.component.md b/docs/content-services/components/categories-management.component.md index dc2f8c985b..c5e8417235 100644 --- a/docs/content-services/components/categories-management.component.md +++ b/docs/content-services/components/categories-management.component.md @@ -19,6 +19,7 @@ Component allows to both assign/unassign categories to content and create multip [parentId]="parentId" [managementMode]="categoriesManagementMode" [classifiableChanged]="classifiableChanged" + [multiSelect]="multiSelect" (categoriesChange)="storeCategoriesToAssign($event)"> ``` @@ -35,6 +36,7 @@ Component allows to both assign/unassign categories to content and create multip | disableRemoval | `boolean` | false | Determines if categories assigned/created can be unassigned/removed from the list. | | managementMode | `CategoriesManagementMode` | | Management mode determines if component works in assign/unassign mode or create mode. | | parentId | `string` | | (optional) ID of a parent category that new categories will be created under. | +| multiSelect | `boolean` | true | (optional) Toggles multiselect mode. | ### Events diff --git a/docs/content-services/dialogs/category-selector.dialog.md b/docs/content-services/dialogs/category-selector.dialog.md new file mode 100644 index 0000000000..10f02f7a62 --- /dev/null +++ b/docs/content-services/dialogs/category-selector.dialog.md @@ -0,0 +1,58 @@ +--- +Title: Category selector dialog component +Added: v6.8.0 +Status: Active +Last reviewed: 2024-03-12 +--- + +# [Category selector dialog component](../../../lib/content-services/src/lib/dialogs/category-selector.dialog.ts "Defined in category-selector.dialog.ts") + +Allows the user to select one or multiple categories. + +![Category selector dialog component](../../docassets/images/adf-category-selector-dialog.png) + +## Dialog inputs + +| Name | Type | Default value | Description | +| ---- |-----------| ------------- | ----------- | +| select | [`Subject`](https://github.com/Alfresco/alfresco-ng2-components/blob/develop/lib/js-api/src/api/content-rest-api/docs/CategoriesApi.md) | | Emits an array of selected categories when the dialog closes | +| multiSelect | `boolean` | `true` | (optional) Toggles multiselect mode | + +## Basic Usage + +```ts +constructor(private dialog: MatDialog) {} + +... + +function openCatDialog() { + const data: CategorySelectorDialogOptions = { + select: new Subject(), + multiSelect: false + }; + + this.dialog.open(CategorySelectorDialogComponent, { + data, + width: '400px' + }); + + data.select.subscribe( + (selections: Category[]) => { + ... + } + ); +} +``` +All the results will be streamed to the select [subject](http://reactivex.io/rxjs/manual/overview.html#subject) present in the `CategorySelectorDialogOptions` object passed to the dialog. +When the category is selected by clicking the `Select` button, the `options.select` stream will be completed. + +## Details + +This component lets the user select categories. Use the +Angular [`MatDialog`](https://material.angular.io/components/dialog/overview) +service to open the dialog, as shown in the example, and pass a `options` object +with properties. + +## See also + +- [Categories management component](../components/categories-management.component.md) diff --git a/docs/docassets/images/adf-category-selector-dialog.png b/docs/docassets/images/adf-category-selector-dialog.png new file mode 100644 index 0000000000..8630e6c31a Binary files /dev/null and b/docs/docassets/images/adf-category-selector-dialog.png differ diff --git a/docs/versionIndex.md b/docs/versionIndex.md index a37899d01e..b294bf6846 100644 --- a/docs/versionIndex.md +++ b/docs/versionIndex.md @@ -12,6 +12,7 @@ backend services have been tested with each released version of ADF. ## Versions +- [v6.8.0](#v680) - [v6.7.0](#v670) - [v6.4.0](#v640) - [v6.2.0](#v620) @@ -45,6 +46,14 @@ backend services have been tested with each released version of ADF. - [v2.1.0](#v210) - [v2.0.0](#v200) +## v6.8.0 + + + +- [Category selector dialog component](content-services/dialogs/category-selector.dialog.md) + + + ## v6.7.0 diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.html b/lib/content-services/src/lib/category/categories-management/categories-management.component.html index b7ae49cb2c..a58a5feeec 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.html +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.html @@ -44,7 +44,7 @@ {{ existingCategoriesMsg | translate }}

{ discardPeriodicTasks(); flush(); })); + + it ('should disable existing categories list if category already selected and multiSelect is false', fakeAsync(() => { + component.multiSelect = false; + fixture.detectChanges(); + typeCategory('test'); + + expect(getSelectionList().disabled).toBeTrue(); + })); }); describe('CRUD mode', () => { diff --git a/lib/content-services/src/lib/category/categories-management/categories-management.component.ts b/lib/content-services/src/lib/category/categories-management/categories-management.component.ts index 26af4900c5..7b1bef58fb 100644 --- a/lib/content-services/src/lib/category/categories-management/categories-management.component.ts +++ b/lib/content-services/src/lib/category/categories-management/categories-management.component.ts @@ -116,6 +116,10 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { @Input() parentId: string; + /** Toggles multiselect mode */ + @Input() + multiSelect = true; + /** Emits when state of upper categories list changes */ @Output() categoriesChange = new EventEmitter(); @@ -159,13 +163,15 @@ export class CategoriesManagementComponent implements OnInit, OnDestroy { if (!this.isCRUDMode) { this._categoryNameControl.removeValidators(Validators.required); this.categories.forEach((category) => this.initialCategories.push(category)); - this.classifiableChanged - .pipe(takeUntil(this.onDestroy$)) - .subscribe(() => { - this.categories = []; - this.categoryNameControlVisible = false; - this.categoryNameControlVisibleChange.emit(false); - }); + if (this.classifiableChanged) { + this.classifiableChanged + .pipe(takeUntil(this.onDestroy$)) + .subscribe(() => { + this.categories = []; + this.categoryNameControlVisible = false; + this.categoryNameControlVisibleChange.emit(false); + }); + } } } diff --git a/lib/content-services/src/lib/dialogs/category-selector.dialog.html b/lib/content-services/src/lib/dialogs/category-selector.dialog.html new file mode 100644 index 0000000000..4fed9bfc98 --- /dev/null +++ b/lib/content-services/src/lib/dialogs/category-selector.dialog.html @@ -0,0 +1,28 @@ +

+ {{ 'CATEGORIES_MANAGEMENT.SELECT_EXISTING_CATEGORY' | translate }} +

+ + + + + + + + + diff --git a/lib/content-services/src/lib/dialogs/category-selector.dialog.scss b/lib/content-services/src/lib/dialogs/category-selector.dialog.scss new file mode 100644 index 0000000000..e5f986a5fd --- /dev/null +++ b/lib/content-services/src/lib/dialogs/category-selector.dialog.scss @@ -0,0 +1,5 @@ +adf-category-selector-dialog { + .adf-dialog-content { + height: 400px; + } +} diff --git a/lib/content-services/src/lib/dialogs/category-selector.dialog.spec.ts b/lib/content-services/src/lib/dialogs/category-selector.dialog.spec.ts new file mode 100644 index 0000000000..a252387292 --- /dev/null +++ b/lib/content-services/src/lib/dialogs/category-selector.dialog.spec.ts @@ -0,0 +1,98 @@ +/*! + * @license + * Copyright © 2005-2023 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 { CategorySelectorDialogComponent, CategorySelectorDialogOptions } from './category-selector.dialog'; +import { Subject } from 'rxjs'; +import { Category } from '@alfresco/js-api'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { CoreTestingModule } from '@alfresco/adf-core'; +import { By } from '@angular/platform-browser'; + +describe('Category selector dialog component', () => { + let fixture: ComponentFixture; + let component: CategorySelectorDialogComponent; + let selectButton: HTMLButtonElement; + + const dialogRef = { + close: jasmine.createSpy('close') + }; + + const options: CategorySelectorDialogOptions = { + select: new Subject() + }; + + const categories: Category[] = [{id: 'id1', name: 'cat1'}, {id: 'id2', name: 'cat3'}]; + + const setCategories = () => { + component.categories = categories; + fixture.detectChanges(); + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [CoreTestingModule], + providers: [ + { provide: MatDialogRef, useValue: dialogRef }, + { provide: MAT_DIALOG_DATA, useValue: options } + ] + }); + dialogRef.close.calls.reset(); + fixture = TestBed.createComponent(CategorySelectorDialogComponent); + component = fixture.componentInstance; + + fixture.detectChanges(); + + selectButton = fixture.debugElement.query(By.css(`[data-automation-id="category-selector-dialog-select-button"]`)).nativeElement; + }); + + it('should set params if they are provided as dialog options', () => { + options.multiSelect = true; + component.ngOnInit(); + + expect(component.multiSelect).toBeTrue(); + }); + + it('should close dialog on cancel button click', () => { + fixture.debugElement.query(By.css(`[data-automation-id="category-selector-dialog-cancel-button"]`)).nativeElement.click(); + expect(dialogRef.close).toHaveBeenCalled(); + }); + + it('should close dialog if category is selected and Select button was clicked', () => { + selectButton.click(); + expect(dialogRef.close).not.toHaveBeenCalled(); + + setCategories(); + selectButton.click(); + + expect(dialogRef.close).toHaveBeenCalled(); + }); + + it('should provide selected categories as observable on Select click', () => { + spyOn(options.select, 'next'); + setCategories(); + selectButton.click(); + + expect(options.select.next).toHaveBeenCalledWith(categories); + }); + + it('should disable select button if no categories were selected', () => { + expect(selectButton.disabled).toBeTruthy(); + setCategories(); + expect(selectButton.disabled).toBeFalse(); + }); +}); diff --git a/lib/content-services/src/lib/dialogs/category-selector.dialog.ts b/lib/content-services/src/lib/dialogs/category-selector.dialog.ts new file mode 100644 index 0000000000..4c9f5e6a4e --- /dev/null +++ b/lib/content-services/src/lib/dialogs/category-selector.dialog.ts @@ -0,0 +1,54 @@ +/*! + * @license + * Copyright © 2005-2023 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, Inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Subject } from 'rxjs'; +import { Category } from '@alfresco/js-api'; +import { CategoriesManagementMode } from '../category'; + +export interface CategorySelectorDialogOptions { + select: Subject; + multiSelect?: boolean; +} + +@Component({ + selector: 'adf-category-selector-dialog', + templateUrl: './category-selector.dialog.html', + styleUrls: ['./category-selector.dialog.scss'], + encapsulation: ViewEncapsulation.None +}) +export class CategorySelectorDialogComponent implements OnInit { + categories: Category[] = []; + categoriesManagementMode = CategoriesManagementMode.ASSIGN; + multiSelect = true; + + constructor( + private dialog: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private options: CategorySelectorDialogOptions + ) { + } + + ngOnInit() { + this.multiSelect = this.options.multiSelect ?? true; + } + + selectCategories() { + this.options.select.next(this.categories); + this.dialog.close(true); + } +} diff --git a/lib/content-services/src/lib/dialogs/dialog.module.ts b/lib/content-services/src/lib/dialogs/dialog.module.ts index 51495a8fff..904eb704f6 100644 --- a/lib/content-services/src/lib/dialogs/dialog.module.ts +++ b/lib/content-services/src/lib/dialogs/dialog.module.ts @@ -28,6 +28,8 @@ import { MatDatetimepickerModule } from '@mat-datetimepicker/core'; import { LibraryDialogComponent } from './library/library.dialog'; import { ContentDirectiveModule } from '../directives'; import { DownloadZipDialogModule } from './download-zip/download-zip.dialog.module'; +import { CategorySelectorDialogComponent } from './category-selector.dialog'; +import { CategoriesModule } from '../category'; @NgModule({ imports: [ @@ -38,19 +40,22 @@ import { DownloadZipDialogModule } from './download-zip/download-zip.dialog.modu ReactiveFormsModule, MatDatetimepickerModule, ContentDirectiveModule, - DownloadZipDialogModule + DownloadZipDialogModule, + CategoriesModule ], declarations: [ FolderDialogComponent, NodeLockDialogComponent, ConfirmDialogComponent, - LibraryDialogComponent + LibraryDialogComponent, + CategorySelectorDialogComponent ], exports: [ FolderDialogComponent, NodeLockDialogComponent, ConfirmDialogComponent, - LibraryDialogComponent + LibraryDialogComponent, + CategorySelectorDialogComponent ] }) export class DialogModule {} diff --git a/lib/content-services/src/lib/dialogs/public-api.ts b/lib/content-services/src/lib/dialogs/public-api.ts index 2e3414ab0a..23a1a87fd5 100644 --- a/lib/content-services/src/lib/dialogs/public-api.ts +++ b/lib/content-services/src/lib/dialogs/public-api.ts @@ -18,6 +18,7 @@ export * from './folder.dialog'; export * from './node-lock.dialog'; export * from './confirm.dialog'; +export * from './category-selector.dialog'; export * from './dialog.module'; export * from './library/library.dialog'; diff --git a/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.scss b/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.scss index 724a9b5aa8..741f236b1e 100644 --- a/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.scss +++ b/lib/content-services/src/lib/tag/tags-creator/tags-creator.component.scss @@ -93,6 +93,8 @@ adf-tags-creator { font-size: 14px; height: auto; width: unset; + background-color: inherit; + color: inherit; &:hover { cursor: pointer;