mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ACS-8065][ADF] Unify components in TagModule (#10226)
* [ACS-8065] tag creator unification * [ACS-8065] test adjustments and after review fixes * [ACS-8065] remove unnecessary style * [ACS-8065] fix style selector * [ACS-8065] remove transforming pipe and unnecessary styles * [ACS-8065] fixes * [ACS-8065] fixes * [ACS-8065] fixes * [ACS-8065] fix view more button placement * [ACS-8065] fix view more button placement * [ACS-8065] fix position of view more button for pagination * [ACS-8065] Fix imports * [ACS-8065] Unit test fixes --------- Co-authored-by: MichalKinas <michal.kinas@hyland.com>
This commit is contained in:
parent
7fa92308f0
commit
d6151308c9
@ -32,6 +32,7 @@ This component shows dynamic list of chips which render depending on free space.
|
||||
|---------------------|---------------------------------------------------------------------------------|---------------|----------------------------------------------------------------|
|
||||
| limitChipsDisplayed | `boolean` | false | Should limit number of chips displayed. |
|
||||
| showDelete | `boolean` | true | Show delete button. |
|
||||
| disableDelete | `boolean` | false | Disable delete button. |
|
||||
| roundUpChips | `boolean` | false | Round up chips increasing the border radius of a chip to 20px. |
|
||||
| pagination | [`Pagination`](../../../lib/js-api/src/api/content-rest-api/docs/Pagination.md) | | Provide if you want to use paginated chips. |
|
||||
| chips | [`Chip`](../../../lib/core/src/lib/dynamic-chip-list/chip.ts)`[]` | | List of chips to display. |
|
||||
|
@ -98,10 +98,9 @@
|
||||
</div>
|
||||
</adf-content-metadata-header>
|
||||
</mat-expansion-panel-header>
|
||||
<div *ngIf="currentPanel.panelTitle === DefaultPanels.TAGS && !editing" class="adf-metadata-properties-tags">
|
||||
<mat-chip-set>
|
||||
<mat-chip *ngFor="let tag of tags" [disableRipple]="true" class="metadata-properties-tag-chip" data-automation-id="metadata-properties-tag-chip">{{ tag }}</mat-chip>
|
||||
</mat-chip-set>
|
||||
<div *ngIf="currentPanel.panelTitle === DefaultPanels.TAGS && !editing"
|
||||
class="adf-metadata-properties-tags">
|
||||
<adf-dynamic-chip-list [chips]="tagsToDisplay" [showDelete]="false" />
|
||||
</div>
|
||||
<div *ngIf="showEmptyTagMessage" class="adf-metadata-no-item-added">
|
||||
{{ 'METADATA.BASIC.NO_TAGS_ADDED' | translate }}
|
||||
|
@ -84,18 +84,8 @@ $panel-properties-height: 56px !default;
|
||||
}
|
||||
|
||||
&-tags {
|
||||
adf-tags-creator {
|
||||
.adf-tags-creation {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&.adf-creator-with-existing-tags-panel {
|
||||
background: var(--adf-theme-background-dialog-color);
|
||||
}
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
visibility: hidden;
|
||||
.adf-dynamic-chip-list-chip {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,20 +23,20 @@ import { ContentMetadataComponent } from './content-metadata.component';
|
||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||
import { AppConfigService, CardViewBaseItemModel, CardViewComponent, NotificationService, UpdateNotification } from '@alfresco/adf-core';
|
||||
import { NodesApiService } from '../../../common/services/nodes-api.service';
|
||||
import { EMPTY, of, throwError } from 'rxjs';
|
||||
import { CardViewContentUpdateService } from '../../../common/services/card-view-content-update.service';
|
||||
import { PropertyGroup } from '../../interfaces/property-group.interface';
|
||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||
import { MatExpansionPanel } from '@angular/material/expansion';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatChipHarness } from '@angular/material/chips/testing';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatExpansionPanel } from '@angular/material/expansion';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { EMPTY, of, throwError } from 'rxjs';
|
||||
import { CategoriesManagementComponent, CategoriesManagementMode } from '../../../category';
|
||||
import { TagsCreatorComponent, TagsCreatorMode } from '../../../tag';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { PropertyGroup } from '../../interfaces/property-group.interface';
|
||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||
import { TagService } from '../../../tag/services/tag.service';
|
||||
import { CategoryService } from '../../../category/services/category.service';
|
||||
import { TagsCreatorComponent, TagsCreatorMode } from '../../../tag';
|
||||
import { CategoriesManagementComponent, CategoriesManagementMode } from '../../../category';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { CardViewContentUpdateService } from '../../../common/services/card-view-content-update.service';
|
||||
|
||||
describe('ContentMetadataComponent', () => {
|
||||
let component: ContentMetadataComponent;
|
||||
@ -71,11 +71,16 @@ describe('ContentMetadataComponent', () => {
|
||||
|
||||
const category1 = new Category({ id: 'test', name: 'testCat' });
|
||||
const category2 = new Category({ id: 'test2', name: 'testCat2' });
|
||||
const categoryPagingResponse: CategoryPaging = { list: { pagination: {}, entries: [{ entry: category1 }, { entry: category2 }] } };
|
||||
const categoryPagingResponse: CategoryPaging = {
|
||||
list: {
|
||||
pagination: {},
|
||||
entries: [{ entry: category1 }, { entry: category2 }]
|
||||
}
|
||||
};
|
||||
|
||||
const findTagElements = async (): Promise<string[]> => {
|
||||
const matChipHarnessList = await TestbedHarnessEnvironment.loader(fixture).getAllHarnesses(
|
||||
MatChipHarness.with({ selector: '[data-automation-id="metadata-properties-tag-chip"]' })
|
||||
MatChipHarness.with({ selector: '.adf-dynamic-chip-list-chip' })
|
||||
);
|
||||
const tags = [];
|
||||
for (const matChip of matChipHarnessList) {
|
||||
@ -269,7 +274,11 @@ describe('ContentMetadataComponent', () => {
|
||||
}));
|
||||
|
||||
it('nodeAspectUpdate', fakeAsync(() => {
|
||||
const fakeNode = { id: 'fake-minimal-node', aspectNames: ['ft:a', 'ft:b', 'ft:c'], name: 'fake-node' } as Node;
|
||||
const fakeNode = {
|
||||
id: 'fake-minimal-node',
|
||||
aspectNames: ['ft:a', 'ft:b', 'ft:c'],
|
||||
name: 'fake-node'
|
||||
} as Node;
|
||||
getGroupedPropertiesSpy.and.stub();
|
||||
spyOn(contentMetadataService, 'getBasicProperties').and.stub();
|
||||
updateService.updateNodeAspect(fakeNode);
|
||||
@ -1321,7 +1330,8 @@ describe('ContentMetadataComponent', () => {
|
||||
|
||||
toggleEditModeForTags();
|
||||
fixture.detectChanges();
|
||||
expect(await findTagElements()).toHaveSize(0);
|
||||
const noEditableTagsContainer = fixture.debugElement.query(By.css('div.adf-metadata-properties-tags'));
|
||||
expect(noEditableTagsContainer).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -23,6 +23,8 @@ import {
|
||||
CardViewBaseItemModel,
|
||||
CardViewComponent,
|
||||
CardViewItem,
|
||||
Chip,
|
||||
DynamicChipListComponent,
|
||||
NotificationService,
|
||||
TranslationService,
|
||||
UpdateNotification
|
||||
@ -39,17 +41,17 @@ import { CategoriesManagementMode } from '../../../category/categories-managemen
|
||||
import { AllowableOperationsEnum } from '../../../common/models/allowable-operations.enum';
|
||||
import { ContentService } from '../../../common/services/content.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { ContentMetadataHeaderComponent } from './content-metadata-header.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { CategoriesManagementComponent } from '../../../category';
|
||||
import { DynamicExtensionComponent } from '@alfresco/adf-extensions';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { TagsCreatorComponent } from '../../../tag';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ContentMetadataHeaderComponent } from './content-metadata-header.component';
|
||||
import { CategoriesManagementComponent } from '../../../category/categories-management/categories-management.component';
|
||||
import { DynamicExtensionComponent } from '@alfresco/adf-extensions';
|
||||
|
||||
const DEFAULT_SEPARATOR = ', ';
|
||||
|
||||
@ -74,7 +76,8 @@ enum DefaultPanels {
|
||||
DynamicExtensionComponent,
|
||||
MatProgressBarModule,
|
||||
TagsCreatorComponent,
|
||||
CardViewComponent
|
||||
CardViewComponent,
|
||||
DynamicChipListComponent
|
||||
],
|
||||
templateUrl: './content-metadata.component.html',
|
||||
styleUrls: ['./content-metadata.component.scss'],
|
||||
@ -153,6 +156,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
||||
basicProperties$: Observable<CardViewItem[]>;
|
||||
groupedProperties$: Observable<CardViewGroup[]>;
|
||||
|
||||
tagsToDisplay: Chip[];
|
||||
changedProperties = {};
|
||||
hasMetadataChanged = false;
|
||||
assignedCategories: Category[] = [];
|
||||
@ -215,6 +219,11 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
||||
return this._assignedTags;
|
||||
}
|
||||
|
||||
set tags(tags: string[]) {
|
||||
this._tags = tags;
|
||||
this.tagsToDisplay = this.tags.map((tag) => ({ id: tag, name: tag }));
|
||||
}
|
||||
|
||||
get tags(): string[] {
|
||||
return this._tags;
|
||||
}
|
||||
@ -309,7 +318,8 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
||||
* @param tags array of tags to register, they are not saved yet until we click save button.
|
||||
*/
|
||||
storeTagsToAssign(tags: string[]) {
|
||||
this._tags = tags;
|
||||
this.tags = tags;
|
||||
this._assignedTags = tags;
|
||||
this.hasMetadataChanged = true;
|
||||
}
|
||||
|
||||
@ -512,8 +522,8 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
||||
private loadTagsForNode(id: string) {
|
||||
this.tagService.getTagsByNodeId(id).subscribe((tagPaging) => {
|
||||
this.assignedTagsEntries = tagPaging.list.entries;
|
||||
this._tags = tagPaging.list.entries.map((tagEntry) => tagEntry.entry.tag);
|
||||
this._assignedTags = [...this._tags];
|
||||
this.tags = tagPaging.list.entries.map((tagEntry) => tagEntry.entry.tag);
|
||||
this._assignedTags = [...this.tags];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@
|
||||
},
|
||||
"TAGS_CREATOR": {
|
||||
"EXISTING_TAGS": "Existing tags:",
|
||||
"EXISTING_TAGS_SELECTION": "Select an existing tag:",
|
||||
"EXISTING_TAGS_SELECTION": "Select an existing Tag:",
|
||||
"NO_TAGS_CREATED": "No Tags Created",
|
||||
"NO_EXISTING_TAGS": "No Existing Tags",
|
||||
"TITLE": "Create Tags",
|
||||
@ -152,7 +152,6 @@
|
||||
"EXISTING_TAG": "Tag already exists",
|
||||
"ALREADY_ADDED_TAG": "Tag is already added",
|
||||
"EMPTY_TAG": "Tag name can't contain only spaces",
|
||||
"REQUIRED": "Tag name is required",
|
||||
"FETCH_TAGS": "Error while fetching the tags",
|
||||
"CREATE_TAGS": "Error while creating the tags",
|
||||
"SPECIAL_CHARACTERS": "Tag name cannot contain prohibited characters"
|
||||
@ -172,7 +171,7 @@
|
||||
"DELETE_CATEGORY": "Delete Category",
|
||||
"EXISTING_CATEGORIES": "Existing Categories:",
|
||||
"SELECT_EXISTING_CATEGORY": "Select an existing Category:",
|
||||
"NO_EXISTING_CATEGORIES": "No existing Categories",
|
||||
"NO_EXISTING_CATEGORIES": "No Existing Categories",
|
||||
"GENERIC_CREATE": "Create: {{name}}",
|
||||
"NAME": "Category name",
|
||||
"LOADING": "Loading",
|
||||
|
@ -10,26 +10,21 @@
|
||||
adf-auto-focus
|
||||
placeholder="{{ 'TAG.TAGS_CREATOR.TAG_SEARCH_PLACEHOLDER' | translate }}"
|
||||
/>
|
||||
<mat-error *ngIf="tagNameControl.invalid && tagNameControl.touched">{{ tagNameErrorMessageKey | translate }} </mat-error>
|
||||
<mat-error *ngIf="tagNameControl.invalid && tagNameControl.touched">
|
||||
{{ tagNameErrorMessageKey | translate }}
|
||||
</mat-error>
|
||||
</div>
|
||||
<p class="adf-no-tags-message" *ngIf="showEmptyTagMessage">
|
||||
{{ 'TAG.TAGS_CREATOR.NO_TAGS_CREATED' | translate }}
|
||||
</p>
|
||||
<div class="adf-tags-list" [class.adf-tags-list-fixed]="!tagNameControlVisible" #tagsList>
|
||||
<mat-chip-listbox *ngIf="tags.length > 0">
|
||||
<mat-chip *ngFor="let tag of tags" [disableRipple]="true" [title]="tag" class="adf-tags-chip">
|
||||
{{ tag }}
|
||||
<button
|
||||
data-automation-id="remove-tag-button"
|
||||
mat-icon-button
|
||||
(click)="removeTag(tag)"
|
||||
[attr.title]="'TAG.TAGS_CREATOR.TOOLTIPS.DELETE_TAG' | translate"
|
||||
[disabled]="disabledTagsRemoving"
|
||||
matChipRemove>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</mat-chip>
|
||||
</mat-chip-listbox>
|
||||
<ng-container *ngIf="tags?.length > 0">
|
||||
<adf-dynamic-chip-list
|
||||
class="adf-tags-chips-container"
|
||||
[chips]="tagsToDisplay"
|
||||
[disableDelete]="disabledTagsRemoving"
|
||||
(removedChip)="removeTag($event)" />
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="adf-existing-tags-panel" *ngIf="existingTagsPanelVisible">
|
||||
@ -40,7 +35,7 @@
|
||||
role="button"
|
||||
tabindex="0"
|
||||
(keyup.enter)="addTag()"
|
||||
[hidden]="tagNameControl.invalid || typing"
|
||||
[hidden]="!tagNameControl.value || tagNameControl.invalid || typing"
|
||||
>
|
||||
{{ 'TAG.TAGS_CREATOR.CREATE_TAG' | translate : { tag: tagNameControl.value } }}
|
||||
</span>
|
||||
@ -49,7 +44,8 @@
|
||||
</p>
|
||||
<div class="adf-tags-list">
|
||||
<mat-list *ngIf="!spinnerVisible && existingTags" [disabled]="isOnlyCreateMode()">
|
||||
<mat-list-item *ngFor="let tagRow of existingTags" class="adf-tag" (click)="addExistingTagToTagsToAssign(tagRow)">
|
||||
<mat-list-item *ngFor="let tagRow of existingTags" class="adf-tag"
|
||||
(click)="addExistingTagToTagsToAssign(tagRow)">
|
||||
{{ tagRow.entry.tag }}
|
||||
</mat-list-item>
|
||||
<p *ngIf="!existingTags?.length">{{ 'TAG.TAGS_CREATOR.NO_EXISTING_TAGS' | translate }}</p>
|
||||
|
@ -15,19 +15,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
|
||||
import { TagsCreatorComponent } from './tags-creator.component';
|
||||
import { NoopTranslateModule, NotificationService } from '@alfresco/adf-core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MatError } from '@angular/material/form-field';
|
||||
import { TagsCreatorMode, TagService } from '@alfresco/adf-content-services';
|
||||
import { EMPTY, of, throwError } from 'rxjs';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NoopTranslateModule, NotificationService } from '@alfresco/adf-core';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
|
||||
import { MatChipHarness } from '@angular/material/chips/testing';
|
||||
import { MatError } from '@angular/material/form-field';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { EMPTY, of, throwError } from 'rxjs';
|
||||
import { TagsCreatorComponent } from './tags-creator.component';
|
||||
|
||||
describe('TagsCreatorComponent', () => {
|
||||
let fixture: ComponentFixture<TagsCreatorComponent>;
|
||||
@ -94,7 +94,7 @@ describe('TagsCreatorComponent', () => {
|
||||
* @returns list of native elements
|
||||
*/
|
||||
function getRemoveTagButtons(): HTMLButtonElement[] {
|
||||
const elements = fixture.debugElement.queryAll(By.css(`[data-automation-id="remove-tag-button"]`));
|
||||
const elements = fixture.debugElement.queryAll(By.css(`.adf-dynamic-chip-list-delete-icon`));
|
||||
return elements.map((el) => el.nativeElement);
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ describe('TagsCreatorComponent', () => {
|
||||
* @returns list of tags
|
||||
*/
|
||||
async function getAddedTags(): Promise<string[]> {
|
||||
const matChipHarness = await loader.getAllHarnesses(MatChipHarness.with({ selector: '.adf-tags-chip' }));
|
||||
const matChipHarness = await loader.getAllHarnesses(MatChipHarness.with({ selector: '.adf-dynamic-chip-list-chip' }));
|
||||
const tagElements = [];
|
||||
for (const matChip of matChipHarness) {
|
||||
tagElements.push(await matChip.getText());
|
||||
@ -337,22 +337,6 @@ describe('TagsCreatorComponent', () => {
|
||||
expect(getFirstError()).toBe('TAG.TAGS_CREATOR.ERRORS.EMPTY_TAG');
|
||||
}));
|
||||
|
||||
it('should show error for required', fakeAsync(() => {
|
||||
typeTag('');
|
||||
component.tagNameControl.markAsTouched();
|
||||
fixture.detectChanges();
|
||||
const error = getFirstError();
|
||||
expect(error).toBe('TAG.TAGS_CREATOR.ERRORS.REQUIRED');
|
||||
}));
|
||||
|
||||
it('should not show error for required if tags are changed', fakeAsync(() => {
|
||||
typeTag('');
|
||||
component.tagNameControl.markAsTouched();
|
||||
component.tags = ['new tag 1', 'new tag 2'];
|
||||
fixture.detectChanges();
|
||||
expect(getFirstError()).toBeUndefined();
|
||||
}));
|
||||
|
||||
it('should show error when duplicated already added tag', fakeAsync(() => {
|
||||
const tag = 'Some tag';
|
||||
|
||||
@ -438,17 +422,6 @@ describe('TagsCreatorComponent', () => {
|
||||
const error = getFirstError();
|
||||
expect(error).toBe('TAG.TAGS_CREATOR.ERRORS.EXISTING_TAG');
|
||||
}));
|
||||
|
||||
it('should error for required when not typed anything and blur input', fakeAsync(() => {
|
||||
component.tagNameControlVisible = true;
|
||||
component.tagNameControl.markAsTouched();
|
||||
fixture.detectChanges();
|
||||
|
||||
const error = getFirstError();
|
||||
expect(error).toBe('TAG.TAGS_CREATOR.ERRORS.REQUIRED');
|
||||
|
||||
flush();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Chip, DynamicChipListComponent, NotificationService } from '@alfresco/adf-core';
|
||||
import { TagEntry, TagPaging } from '@alfresco/js-api';
|
||||
import {
|
||||
Component,
|
||||
@ -30,28 +31,26 @@ import {
|
||||
ViewChild,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { debounce, distinctUntilChanged, finalize, first, map, takeUntil, tap } from 'rxjs/operators';
|
||||
import { EMPTY, forkJoin, Observable, Subject, timer } from 'rxjs';
|
||||
import { NotificationService } from '@alfresco/adf-core';
|
||||
import { TagsCreatorMode } from './tags-creator-mode';
|
||||
import { TagService } from '../services/tag.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { AutoFocusDirective } from '../../directives';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { EMPTY, forkJoin, Observable, Subject, timer } from 'rxjs';
|
||||
import { debounce, distinctUntilChanged, finalize, first, map, takeUntil, tap } from 'rxjs/operators';
|
||||
import { AutoFocusDirective } from '../../directives';
|
||||
import { TagService } from '../services/tag.service';
|
||||
import { TagsCreatorMode } from './tags-creator-mode';
|
||||
|
||||
interface TagNameControlErrors {
|
||||
duplicatedExistingTag?: boolean;
|
||||
duplicatedAddedTag?: boolean;
|
||||
emptyTag?: boolean;
|
||||
required?: boolean;
|
||||
specialCharacters?: boolean;
|
||||
}
|
||||
|
||||
@ -76,7 +75,8 @@ const DEFAULT_TAGS_SORTING = {
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatProgressSpinnerModule
|
||||
MatProgressSpinnerModule,
|
||||
DynamicChipListComponent
|
||||
],
|
||||
templateUrl: './tags-creator.component.html',
|
||||
styleUrls: ['./tags-creator.component.scss'],
|
||||
@ -105,13 +105,11 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
set tags(tags: string[]) {
|
||||
this._tags = [...tags];
|
||||
this.tagsToDisplay = this.tags.map((tag) => ({ id: tag, name: tag }));
|
||||
this._initialExistingTags = null;
|
||||
this._existingTags = null;
|
||||
this.loadTags(this.tagNameControl.value);
|
||||
this.tagNameControl.updateValueAndValidity();
|
||||
if (this.tagNameControl.errors?.required) {
|
||||
this.tagNameControl.markAsUntouched();
|
||||
}
|
||||
}
|
||||
|
||||
get tags(): string[] {
|
||||
@ -130,9 +128,6 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
this._existingTagsPanelVisible = true;
|
||||
setTimeout(() => {
|
||||
this.tagNameInputElement?.nativeElement?.scrollIntoView();
|
||||
if (!this.tags.length) {
|
||||
this.tagNameInputElement?.nativeElement?.focus();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._existingTagsPanelVisible = false;
|
||||
@ -156,11 +151,12 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
tagsChange = new EventEmitter<string[]>();
|
||||
|
||||
tagsToDisplay: Chip[] = [];
|
||||
|
||||
readonly nameErrorMessagesByErrors = new Map<keyof TagNameControlErrors, string>([
|
||||
['duplicatedExistingTag', 'EXISTING_TAG'],
|
||||
['duplicatedAddedTag', 'ALREADY_ADDED_TAG'],
|
||||
['emptyTag', 'EMPTY_TAG'],
|
||||
['required', 'REQUIRED'],
|
||||
['specialCharacters', 'SPECIAL_CHARACTERS']
|
||||
]);
|
||||
|
||||
@ -170,7 +166,7 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
private _tags: string[] = [];
|
||||
private _tagNameControl = new FormControl<string>(
|
||||
'',
|
||||
[this.validateIfNotAlreadyAdded.bind(this), Validators.required, this.validateEmptyTag, this.validateSpecialCharacters],
|
||||
[this.validateIfNotAlreadyAdded.bind(this), this.validateEmptyTag, this.validateSpecialCharacters],
|
||||
this.validateIfNotExistingTag.bind(this)
|
||||
);
|
||||
private _tagNameControlVisible = false;
|
||||
@ -276,7 +272,7 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
addTag(): void {
|
||||
if (!this._typing && !this.tagNameControl.invalid) {
|
||||
this.tags.push(this.tagNameControl.value.trim());
|
||||
this.tags = [...this.tags, this.tagNameControl.value.trim()];
|
||||
this.clearTagNameInput();
|
||||
this.checkScrollbarVisibility();
|
||||
this.tagsChange.emit(this.tags);
|
||||
@ -291,7 +287,8 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
removeTag(tag: string): void {
|
||||
this.removeTagFromArray(this.tags, tag);
|
||||
this.tagNameControl.updateValueAndValidity({ emitEvent: false });
|
||||
this.tags = [...this.tags];
|
||||
this.tagNameControl.updateValueAndValidity();
|
||||
this.updateExistingTagsListOnRemoveFromTagsToConfirm(tag);
|
||||
this.exactTagSet$.next();
|
||||
this.checkScrollbarVisibility();
|
||||
@ -308,6 +305,7 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
this.tags.push(selectedTag.entry.tag);
|
||||
this.removeTagFromArray(this.existingTags, selectedTag);
|
||||
this.tagNameControl.updateValueAndValidity();
|
||||
this.tags = [...this.tags];
|
||||
this.exactTagSet$.next();
|
||||
this.tagsChange.emit(this.tags);
|
||||
}
|
||||
@ -337,8 +335,8 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
takeUntil(this.cancelExistingTagsLoading$),
|
||||
finalize(() => (this._typing = false))
|
||||
)
|
||||
.subscribe(
|
||||
({ exactResult, searchedResult }: { exactResult: TagEntry; searchedResult: TagPaging }) => {
|
||||
.subscribe({
|
||||
next: ({ exactResult, searchedResult }: { exactResult: TagEntry; searchedResult: TagPaging }) => {
|
||||
if (exactResult) {
|
||||
this.existingExactTag = exactResult;
|
||||
this.removeExactTagFromSearchedResult(searchedResult);
|
||||
@ -352,11 +350,11 @@ export class TagsCreatorComponent implements OnInit, OnDestroy {
|
||||
this.exactTagSet$.next();
|
||||
this._spinnerVisible = false;
|
||||
},
|
||||
() => {
|
||||
error: () => {
|
||||
this.notificationService.showError('TAG.TAGS_CREATOR.ERRORS.FETCH_TAGS');
|
||||
this._spinnerVisible = false;
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.existingExactTag = null;
|
||||
this._spinnerVisible = false;
|
||||
|
@ -18,6 +18,7 @@
|
||||
<mat-icon *ngIf="showDelete"
|
||||
id="adf-dynamic-chip-list-delete-{{ chip.name }}"
|
||||
class="adf-dynamic-chip-list-delete-icon"
|
||||
[disabled]="disableDelete"
|
||||
matChipRemove>
|
||||
close
|
||||
</mat-icon>
|
||||
@ -26,9 +27,9 @@
|
||||
<button
|
||||
data-automation-id="adf-dynamic-chip-list-view-more-button"
|
||||
mat-button
|
||||
[hidden]="chipsToDisplay.length === 0 || !limitChipsDisplayed"
|
||||
[hidden]="chipsToDisplay?.length === 0 || !limitChipsDisplayed"
|
||||
[style.left.px]="viewMoreButtonLeftOffset"
|
||||
[style.top.px]="viewMoreButtonTop"
|
||||
[style.top.px]="!!pagination ? viewMoreButtonTop : ''"
|
||||
class="adf-dynamic-chip-list-view-more-button"
|
||||
[class.adf-dynamic-chip-list-hidden-btn]="!calculationsDone"
|
||||
(click)="displayNextChips($event)">
|
||||
|
@ -10,7 +10,6 @@
|
||||
.adf-dynamic-chip-list-view-more-button {
|
||||
margin-left: 5px;
|
||||
position: absolute;
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
|
||||
&[hidden] {
|
||||
@ -27,27 +26,14 @@
|
||||
}
|
||||
|
||||
&.adf-dynamic-chip-list-paginated {
|
||||
/* TODO(mdc-migration): The following rule targets internal classes of chips that may no longer apply for the MDC version. */
|
||||
mat-chip-list {
|
||||
width: 100%;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-dynamic-chip-list-view-more-button {
|
||||
margin: -2px 4px 4px 24px;
|
||||
margin-left: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&.adf-dynamic-chip-list-button-in-next-line {
|
||||
align-items: unset;
|
||||
padding-bottom: 54px;
|
||||
|
||||
.adf-dynamic-chip-list-view-more-button {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.adf-dynamic-chip-list-paginated) {
|
||||
@ -55,7 +41,6 @@
|
||||
|
||||
&:not(.adf-dynamic-chip-list-flex-column) {
|
||||
.adf-dynamic-chip-list-view-more-button {
|
||||
margin-top: 18px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
@ -70,7 +55,6 @@
|
||||
}
|
||||
|
||||
.adf-dynamic-chip-list-chip {
|
||||
height: auto;
|
||||
word-break: break-word;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
@ -84,14 +68,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adf-dynamic-chip-list-delete-icon {
|
||||
font-size: var(--theme-title-font-size);
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
fill: currentcolor;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
color: var(--theme-primary-color-default-contrast);
|
||||
}
|
||||
}
|
||||
|
@ -146,6 +146,19 @@ describe('DynamicChipListComponent', () => {
|
||||
expect(getComputedStyle(chip.nativeElement).borderRadius).toBe('20px');
|
||||
});
|
||||
|
||||
it('should disable the delete button if disableDelete is true', async () => {
|
||||
component.disableDelete = true;
|
||||
|
||||
component.ngOnChanges({
|
||||
chips: new SimpleChange(undefined, component.chips, true)
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const chip = fixture.debugElement.query(By.css('.adf-dynamic-chip-list-delete-icon'));
|
||||
expect(Object.keys(chip.attributes)).toContain('disabled');
|
||||
});
|
||||
|
||||
it('should not render view more button by default', async () => {
|
||||
component.ngOnChanges({
|
||||
chips: new SimpleChange(undefined, component.chips, true)
|
||||
|
@ -65,6 +65,10 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni
|
||||
@Input()
|
||||
showDelete = true;
|
||||
|
||||
/** Disable delete button. */
|
||||
@Input()
|
||||
disableDelete = false;
|
||||
|
||||
/** Should limit number of chips displayed. */
|
||||
@Input()
|
||||
limitChipsDisplayed = false;
|
||||
@ -101,8 +105,10 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni
|
||||
private viewMoreButtonLeftOffsetBeforeFlexDirection: number;
|
||||
private requestedDisplayingAllChips = false;
|
||||
private resizeObserver = new ResizeObserver(() => {
|
||||
this.calculateChipsToDisplay();
|
||||
this.changeDetectorRef.detectChanges();
|
||||
if (this.initialLimitChipsDisplayed && this.chipsToDisplay.length) {
|
||||
this.calculateChipsToDisplay();
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
});
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||
@ -117,10 +123,8 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni
|
||||
this.initialChips = this.chips;
|
||||
this.chipsToDisplay = this.initialChips;
|
||||
if (this.limitChipsDisplayed && this.chipsToDisplay.length) {
|
||||
setTimeout(() => {
|
||||
this.calculateChipsToDisplay();
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
this.calculateChipsToDisplay();
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,5 +234,9 @@ export class DynamicChipListComponent implements OnChanges, OnInit, AfterViewIni
|
||||
} else {
|
||||
this.viewMoreButtonLeftOffset = this.columnFlexDirection ? 0 : this.viewMoreButtonLeftOffsetBeforeFlexDirection;
|
||||
}
|
||||
|
||||
if (!this.pagination) {
|
||||
this.viewMoreButtonTop = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user