[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:
MichalKinas
2023-02-14 11:04:07 +01:00
committed by GitHub
parent 6e99dd663a
commit b1311c6966
7 changed files with 193 additions and 24 deletions

View File

@@ -27,9 +27,23 @@ Shows tags for a node.
| ---- | ---- | ------------- | ----------- |
| nodeId | `string` | | The identifier of a node. |
| showDelete | `boolean` | true | Show delete button |
| limitTagsDisplayed | `boolean` | false | Should limit number of tags displayed to as much as fits into container |
### Events
| Name | Type | Description |
| ---- | ---- | ----------- |
| results | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when a tag is selected. |
## Details
### Limit number of tags displayed initially
To limit number of tags initially displayed set `limitTagsDisplayed` to `true`.
```html
<adf-tag-node-list
[nodeId]="nodeId"
[limitTagsDisplayed]="true">
</adf-tag-node-list>
```
Now when tag chips will exceed the size of the container number of displayed chips will be limited to as much as fits together with view more button. At least one tag will always be displayed, when one tag and view more button won't fit into one line the button will be displayed under the tag.

View File

@@ -544,5 +544,8 @@
"NODE_COUNTER": {
"SELECTED_COUNT": "{{ count }} selected"
},
"TAG_NODE_LIST": {
"VIEW_MORE": "View {{ count }} more"
},
"swsdp": "Sample: Web Site Design Project"
}

View File

@@ -1,9 +1,19 @@
<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)">
*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>

View File

@@ -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 {

View File

@@ -34,11 +34,17 @@ describe('TagNodeList', () => {
skipCount: 0,
maxItems: 100
},
entries: [{
entries: [
{
entry: {tag: 'test1', id: '0ee933fa-57fc-4587-8a77-b787e814f1d2'}
}, {entry: {tag: 'test2', id: 'fcb92659-1f10-41b4-9b17-851b72a3b597'}}, {
},
{
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);
});
});
});

View File

@@ -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);
}
}

View File

@@ -44,6 +44,7 @@ import { ExtensionService } from '../../services/extension.service';
.adf-dynamic-column {
display: flex;
align-items: center;
width: inherit;
}
`
]