mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ACS-4124] Display only as much tags as fits container in tag node list (#8247)
* [ACS-4124] Display only as much tags as fits container in tag node list * [ACS-4124] CR fixes * [ACS-4124] Add hidden, lint fixes, event propagation stopped
This commit is contained in:
@@ -544,5 +544,8 @@
|
||||
"NODE_COUNTER": {
|
||||
"SELECTED_COUNT": "{{ count }} selected"
|
||||
},
|
||||
"TAG_NODE_LIST": {
|
||||
"VIEW_MORE": "View {{ count }} more"
|
||||
},
|
||||
"swsdp": "Sample: Web Site Design Project"
|
||||
}
|
||||
|
@@ -1,9 +1,19 @@
|
||||
<mat-chip-list>
|
||||
<mat-chip class="adf-tag-chips"
|
||||
*ngFor="let currentEntry of tagsEntries; let idx = index" (removed)="removeTag(currentEntry.entry.id)">
|
||||
<span id="tag_name_{{idx}}">{{currentEntry.entry.tag}}</span>
|
||||
<mat-icon *ngIf="showDelete" id="tag_chips_delete_{{currentEntry.entry.tag}}"
|
||||
class="adf-tag-chips-delete-icon" matChipRemove>cancel
|
||||
</mat-icon>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
<div class="adf-tag-node-list" [class.adf-flex-column]="limitTagsDisplayed && (!calculationsDone || columnFlexDirection)" #nodeListContainer>
|
||||
<mat-chip-list [class.adf-full-width]="limitTagsDisplayed && !calculationsDone">
|
||||
<mat-chip class="adf-tag-chips"
|
||||
*ngFor="let currentEntry of tagsEntries; let idx = index"
|
||||
(removed)="removeTag(currentEntry.entry.id)">
|
||||
<span id="tag_name_{{idx}}">{{currentEntry.entry.tag}}</span>
|
||||
<mat-icon *ngIf="showDelete" id="tag_chips_delete_{{currentEntry.entry.tag}}"
|
||||
class="adf-tag-chips-delete-icon" matChipRemove>cancel
|
||||
</mat-icon>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
<button mat-button
|
||||
*ngIf="limitTagsDisplayed"
|
||||
class="adf-view-more-button"
|
||||
[class.adf-hidden-btn]="!calculationsDone"
|
||||
(click)="displayAllTags($event)"
|
||||
>{{ 'TAG_NODE_LIST.VIEW_MORE' | translate: { count: undisplayedTagsCount} }}
|
||||
</button>
|
||||
</div>
|
||||
|
@@ -1,7 +1,31 @@
|
||||
.adf-tag-node-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
width: inherit;
|
||||
|
||||
&.adf-flex-column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.adf-view-more-button {
|
||||
margin: 4px;
|
||||
color: var(--theme-text-color);
|
||||
}
|
||||
|
||||
.adf-full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.adf-hidden-btn {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.adf-tag-chips {
|
||||
color: var(--theme-primary-color-default-contrast);
|
||||
background-color: var(--theme-primary-color);
|
||||
height: auto;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.adf-tag-chips-delete {
|
||||
|
@@ -34,11 +34,17 @@ describe('TagNodeList', () => {
|
||||
skipCount: 0,
|
||||
maxItems: 100
|
||||
},
|
||||
entries: [{
|
||||
entry: {tag: 'test1', id: '0ee933fa-57fc-4587-8a77-b787e814f1d2'}
|
||||
}, {entry: {tag: 'test2', id: 'fcb92659-1f10-41b4-9b17-851b72a3b597'}}, {
|
||||
entry: {tag: 'test3', id: 'fb4213c0-729d-466c-9a6c-ee2e937273bf'}
|
||||
}]
|
||||
entries: [
|
||||
{
|
||||
entry: {tag: 'test1', id: '0ee933fa-57fc-4587-8a77-b787e814f1d2'}
|
||||
},
|
||||
{
|
||||
entry: {tag: 'test2', id: 'fcb92659-1f10-41b4-9b17-851b72a3b597'}
|
||||
},
|
||||
{
|
||||
entry: {tag: 'test3', id: 'fb4213c0-729d-466c-9a6c-ee2e937273bf'}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -62,14 +68,13 @@ describe('TagNodeList', () => {
|
||||
|
||||
element = fixture.nativeElement;
|
||||
component = fixture.componentInstance;
|
||||
component.nodeId = 'fake-node-id';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('Rendering tests', () => {
|
||||
|
||||
it('Tag list relative a single node should be rendered', async () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -84,8 +89,6 @@ describe('TagNodeList', () => {
|
||||
});
|
||||
|
||||
it('Tag list click on delete button should delete the tag', async () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
|
||||
spyOn(tagService, 'removeTag').and.returnValue(of(true));
|
||||
|
||||
component.ngOnChanges();
|
||||
@@ -99,7 +102,6 @@ describe('TagNodeList', () => {
|
||||
});
|
||||
|
||||
it('Should not show the delete tag button if showDelete is false', async () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
component.showDelete = false;
|
||||
|
||||
component.ngOnChanges();
|
||||
@@ -111,7 +113,6 @@ describe('TagNodeList', () => {
|
||||
});
|
||||
|
||||
it('Should show the delete tag button if showDelete is true', async () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
component.showDelete = true;
|
||||
|
||||
component.ngOnChanges();
|
||||
@@ -121,5 +122,66 @@ describe('TagNodeList', () => {
|
||||
const deleteButton: any = element.querySelector('#tag_chips_delete_test1');
|
||||
expect(deleteButton).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should not render view more button by default', async () => {
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const viewMoreButton = element.querySelector('.adf-view-more-button');
|
||||
const tagChips = element.querySelectorAll('.adf-tag-chips');
|
||||
expect(viewMoreButton).toBeNull();
|
||||
expect(tagChips.length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Limit tags display', () => {
|
||||
beforeEach(async () => {
|
||||
component.limitTagsDisplayed = true;
|
||||
element.style.maxWidth = '200px';
|
||||
component.tagsEntries = dataTag.list.entries;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should render view more button when limiting is enabled', async () => {
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const viewMoreButton = element.querySelector('.adf-view-more-button');
|
||||
const tagChips = element.querySelectorAll('.adf-tag-chips');
|
||||
expect(viewMoreButton).not.toBeNull();
|
||||
expect(tagChips.length).toBe(component.tagsEntries.length);
|
||||
});
|
||||
|
||||
it('should not render view more button when limiting is enabled and all tags fits into container', async () => {
|
||||
element.style.maxWidth = '800px';
|
||||
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const viewMoreButton = element.querySelector('.adf-view-more-button');
|
||||
const tagChips = element.querySelectorAll('.adf-tag-chips');
|
||||
expect(viewMoreButton).toBeNull();
|
||||
expect(tagChips.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should display all tags when view more button is clicked', async () => {
|
||||
component.ngOnChanges();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
let viewMoreButton: HTMLButtonElement = element.querySelector('.adf-view-more-button');
|
||||
let tagChips = element.querySelectorAll('.adf-tag-chips');
|
||||
viewMoreButton.click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
viewMoreButton = element.querySelector('.adf-view-more-button');
|
||||
tagChips = element.querySelectorAll('.adf-tag-chips');
|
||||
expect(viewMoreButton).toBeNull();
|
||||
expect(tagChips.length).toBe(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -15,11 +15,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, ViewEncapsulation, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, ViewEncapsulation, OnDestroy, OnInit, ViewChild, ElementRef, ViewChildren, QueryList } from '@angular/core';
|
||||
import { TagService } from './services/tag.service';
|
||||
import { TagEntry } from '@alfresco/js-api';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { MatChip } from '@angular/material/chips';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -30,10 +31,10 @@ import { takeUntil } from 'rxjs/operators';
|
||||
selector: 'adf-tag-node-list',
|
||||
templateUrl: './tag-node-list.component.html',
|
||||
styleUrls: ['./tag-node-list.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'adf-tag-node-list' }
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit {
|
||||
/* eslint no-underscore-dangle: ["error", { "allow": ["_elementRef"] }]*/
|
||||
/** The identifier of a node. */
|
||||
@Input()
|
||||
nodeId: string;
|
||||
@@ -42,7 +43,20 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit {
|
||||
@Input()
|
||||
showDelete = true;
|
||||
|
||||
tagsEntries: TagEntry[];
|
||||
/** Should limit number of tags displayed */
|
||||
@Input()
|
||||
limitTagsDisplayed = false;
|
||||
|
||||
@ViewChild('nodeListContainer')
|
||||
containerView: ElementRef;
|
||||
|
||||
@ViewChildren(MatChip)
|
||||
tagChips: QueryList<MatChip>;
|
||||
|
||||
tagsEntries: TagEntry[] = [];
|
||||
calculationsDone = false;
|
||||
columnFlexDirection = false;
|
||||
undisplayedTagsCount = 0;
|
||||
|
||||
/** Emitted when a tag is selected. */
|
||||
@Output()
|
||||
@@ -66,6 +80,14 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit {
|
||||
this.tagService.refresh
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(() => this.refreshTag());
|
||||
|
||||
this.results
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(() => {
|
||||
if (this.limitTagsDisplayed && this.tagsEntries.length > 0) {
|
||||
this.calculateTagsToDisplay();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -87,4 +109,37 @@ export class TagNodeListComponent implements OnChanges, OnDestroy, OnInit {
|
||||
this.refreshTag();
|
||||
});
|
||||
}
|
||||
|
||||
displayAllTags(event: Event): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.limitTagsDisplayed = false;
|
||||
this.refreshTag();
|
||||
}
|
||||
|
||||
private calculateTagsToDisplay() {
|
||||
let tagsToDisplay = 1;
|
||||
const containerWidth: number = this.containerView.nativeElement.clientWidth;
|
||||
const viewMoreBtnWidth: number = this.containerView.nativeElement.children[1].offsetWidth;
|
||||
const tagChipMargin = this.getTagChipMargin(this.tagChips.get(0));
|
||||
const tagChipsWidth: number = this.tagChips.reduce((acc, val, index) => {
|
||||
if (containerWidth - viewMoreBtnWidth > acc + val._elementRef.nativeElement.offsetWidth) {
|
||||
tagsToDisplay = index + 1;
|
||||
}
|
||||
return acc + val._elementRef.nativeElement.offsetWidth + tagChipMargin;
|
||||
}, 0);
|
||||
if ((containerWidth - tagChipsWidth) <= 0) {
|
||||
this.columnFlexDirection = tagsToDisplay === 1 && (containerWidth < (this.tagChips.get(0)._elementRef.nativeElement.offsetWidth + viewMoreBtnWidth));
|
||||
this.undisplayedTagsCount = this.tagsEntries.length - tagsToDisplay;
|
||||
this.tagsEntries = this.tagsEntries.slice(0, tagsToDisplay);
|
||||
} else {
|
||||
this.limitTagsDisplayed = false;
|
||||
}
|
||||
this.calculationsDone = true;
|
||||
}
|
||||
|
||||
private getTagChipMargin(chip: MatChip): number {
|
||||
const tagChipStyles = window.getComputedStyle(chip._elementRef.nativeElement);
|
||||
return parseInt(tagChipStyles.marginLeft, 10) + parseInt(tagChipStyles.marginRight, 10);
|
||||
}
|
||||
}
|
||||
|
@@ -44,6 +44,7 @@ import { ExtensionService } from '../../services/extension.service';
|
||||
.adf-dynamic-column {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: inherit;
|
||||
}
|
||||
`
|
||||
]
|
||||
|
Reference in New Issue
Block a user