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.
+
+
+
+## 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 }}
0"
class="adf-categories-management-list">
{
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;