mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[AAE-7817] Show hide columns on datatable (#7580)
* [AAE-7817] Show hide columns for data-table * Update * update tests and uses material harness * added pipes test * update * update * added tests for datatable * update * Added documentation * Fix for drop column header * lint fix * fix lint
This commit is contained in:
@@ -39,6 +39,17 @@
|
|||||||
</data-column>
|
</data-column>
|
||||||
</data-columns>
|
</data-columns>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<adf-main-menu-datatable-template>
|
||||||
|
<ng-template let-mainMenuTrigger>
|
||||||
|
<adf-datatable-column-selector
|
||||||
|
[columns]="data.getColumns()"
|
||||||
|
[mainMenuTrigger]="mainMenuTrigger"
|
||||||
|
(submitColumnsVisibility)="onColumnsVisibilityChange($event)">
|
||||||
|
</adf-datatable-column-selector>
|
||||||
|
</ng-template>
|
||||||
|
</adf-main-menu-datatable-template>
|
||||||
|
|
||||||
</adf-datatable>
|
</adf-datatable>
|
||||||
|
|
||||||
<ng-template #customColumnHeaderTemplate>
|
<ng-template #customColumnHeaderTemplate>
|
||||||
|
@@ -326,6 +326,10 @@ export class DataTableComponent {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onColumnsVisibilityChange(columns: DataColumn[]): void {
|
||||||
|
this.data.setColumns(columns);
|
||||||
|
}
|
||||||
|
|
||||||
onExecuteRowAction(event: DataRowActionEvent) {
|
onExecuteRowAction(event: DataRowActionEvent) {
|
||||||
const args = event.value;
|
const args = event.value;
|
||||||
window.alert(`My custom action: ${args.action.title}`);
|
window.alert(`My custom action: ${args.action.title}`);
|
||||||
|
@@ -53,6 +53,7 @@ Defines column properties for DataTable, Tasklist, Document List and other compo
|
|||||||
| key | `string` | | Data source key. Can be either a column/property key like `title` or a property path like `createdBy.name`. |
|
| key | `string` | | Data source key. Can be either a column/property key like `title` or a property path like `createdBy.name`. |
|
||||||
| sortable | `boolean` | true | Toggles ability to sort by this column, for example by clicking the column header. |
|
| sortable | `boolean` | true | Toggles ability to sort by this column, for example by clicking the column header. |
|
||||||
| draggable | `boolean` | false | Toggles drag and drop for header column. |
|
| draggable | `boolean` | false | Toggles drag and drop for header column. |
|
||||||
|
| isHidden | `boolean` | false | Hides columns |
|
||||||
| sortingKey | `string` | | When using server side sorting the column used by the api call where the sorting will be performed |
|
| sortingKey | `string` | | When using server side sorting the column used by the api call where the sorting will be performed |
|
||||||
| srTitle | `string` | | Title to be used for screen readers. |
|
| srTitle | `string` | | Title to be used for screen readers. |
|
||||||
| title | `string` | "" | Display title of the column, typically used for column headers. You can use the i18n resource key to get it translated automatically. |
|
| title | `string` | "" | Display title of the column, typically used for column headers. You can use the i18n resource key to get it translated automatically. |
|
||||||
|
@@ -257,7 +257,7 @@ You can add [Data column component](data-column.component.md) instances to defin
|
|||||||
<!--Add your custom empty template here-->
|
<!--Add your custom empty template here-->
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<div></div>
|
<div></div>
|
||||||
<span> My custom value </spam>
|
<span> My custom value </span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</data-column>
|
</data-column>
|
||||||
</adf-datatable>
|
</adf-datatable>
|
||||||
@@ -312,6 +312,27 @@ while the data for the table is loading:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also show main menu for datatable using `<adf-main-menu-datatable-template>`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<adf-datatable ...>
|
||||||
|
<adf-main-menu-datatable-template>
|
||||||
|
<ng-template let-mainMenuTrigger>
|
||||||
|
<!--Add your custom main menu template here-->
|
||||||
|
<adf-datatable-column-selector
|
||||||
|
[columns]="data.getColumns()"
|
||||||
|
[mainMenuTrigger]="mainMenuTrigger"
|
||||||
|
(submitColumnsVisibility)="onColumnsVisibilityChange($event)">
|
||||||
|
</adf-datatable-column-selector>
|
||||||
|
</ng-template>
|
||||||
|
</adf-main-menu-datatable-template>
|
||||||
|
</adf-datatable>
|
||||||
|
```
|
||||||
|
|
||||||
|
Provided template receives `let-mainMenuTrigger`, so you can programaticaly work with the menu (please see [MatMenuTrigger](https://material.angular.io/components/menu/overview#toggling-the-menu-programmatically)).
|
||||||
|
|
||||||
|
For convenience, you can use `<adf-datatable-column-selector>` which will allow you to change column visibility.
|
||||||
|
|
||||||
\###Styling transcluded content
|
\###Styling transcluded content
|
||||||
|
|
||||||
When adding your custom templates you can style them as you like. However, for an out of the box experience, if you want to apply datatable styles to your column you will need to follow this structure:
|
When adding your custom templates you can style them as you like. However, for an out of the box experience, if you want to apply datatable styles to your column you will need to follow this structure:
|
||||||
@@ -375,6 +396,8 @@ Learm more about styling your datatable: [Customizing the component's styles](#c
|
|||||||
| rowDblClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataRowEvent`](../../../lib/core/datatable/data/data-row-event.model.ts)`>` | Emitted when the user double-clicks a row. |
|
| rowDblClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataRowEvent`](../../../lib/core/datatable/data/data-row-event.model.ts)`>` | Emitted when the user double-clicks a row. |
|
||||||
| showRowActionsMenu | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataCellEvent`](../../../lib/core/datatable/components/data-cell.event.ts)`>` | Emitted before the actions menu is displayed for a row. |
|
| showRowActionsMenu | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataCellEvent`](../../../lib/core/datatable/components/data-cell.event.ts)`>` | Emitted before the actions menu is displayed for a row. |
|
||||||
| showRowContextMenu | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataCellEvent`](../../../lib/core/datatable/components/data-cell.event.ts)`>` | Emitted before the context menu is displayed for a row. |
|
| showRowContextMenu | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataCellEvent`](../../../lib/core/datatable/components/data-cell.event.ts)`>` | Emitted before the context menu is displayed for a row. |
|
||||||
|
| columnOrderChanged | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`DataColumn`](../../../lib/core/datatable/components/data-cell.event.ts)`>` | Emitted after dragging and dropping column header. |
|
||||||
|
|
||||||
|
|
||||||
## Details
|
## Details
|
||||||
|
|
||||||
@@ -440,7 +463,7 @@ Given that DataTable raises bubbling DOM events, you can handle drop behavior fr
|
|||||||
(header-drop)="onDrop($event)"
|
(header-drop)="onDrop($event)"
|
||||||
(cell-dragover)="onDragOver($event)"
|
(cell-dragover)="onDragOver($event)"
|
||||||
(cell-drop)="onDrop($event)">
|
(cell-drop)="onDrop($event)">
|
||||||
|
|
||||||
<adf-datatable [data]="data">
|
<adf-datatable [data]="data">
|
||||||
</adf-datatable>
|
</adf-datatable>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -0,0 +1,52 @@
|
|||||||
|
<div class="adf-columns-selector" (click)="$event.stopPropagation();">
|
||||||
|
<div class="adf-columns-selector-header">
|
||||||
|
<span class="adf-columns-selector-header-label">
|
||||||
|
{{"ADF-DATATABLE.COLUMNS_SELECTOR.COLUMNS" | translate}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button mat-icon-button (click)="closeMenu()">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider class="adf-columns-selector-divider"></mat-divider>
|
||||||
|
|
||||||
|
<div class="adf-columns-selector-search-input-container">
|
||||||
|
<mat-icon
|
||||||
|
class="adf-columns-selector-search-input-icon">
|
||||||
|
search
|
||||||
|
</mat-icon>
|
||||||
|
|
||||||
|
<input
|
||||||
|
[formControl]="searchInputControl"
|
||||||
|
class="adf-columns-selector-search-input"
|
||||||
|
type="text"
|
||||||
|
[placeholder]='"ADF-DATATABLE.COLUMNS_SELECTOR.SEARCH" | translate'>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngFor="let column of columnItems">
|
||||||
|
<div
|
||||||
|
*ngIf="(column.title | translate | filterString:searchQuery) as translatedTitle"
|
||||||
|
class="adf-columns-selector-list-item-container">
|
||||||
|
<mat-checkbox
|
||||||
|
color="primary"
|
||||||
|
class="adf-columns-selector-column-checkbox"
|
||||||
|
[attr.data-automation-id]="'adf-columns-selector-column-checkbox-' + column.title"
|
||||||
|
[checked]="!column.isHidden"
|
||||||
|
(change)="changeColumnVisibility(column)">
|
||||||
|
<div class="adf-columns-selector-list-content">{{translatedTitle}}</div>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-divider class="adf-columns-selector-divider"></mat-divider>
|
||||||
|
|
||||||
|
<div class="adf-columns-selector-footer">
|
||||||
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
color="primary"
|
||||||
|
(click)="apply()">
|
||||||
|
{{"ADF-DATATABLE.COLUMNS_SELECTOR.APPLY" | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,87 @@
|
|||||||
|
$adf-columns-selector-space: 12px;
|
||||||
|
|
||||||
|
@mixin adf-columns-selector-side-padding {
|
||||||
|
padding: 0 $adf-columns-selector-space;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin adf-columns-selector-top-bottom-padding {
|
||||||
|
padding: $adf-columns-selector-space 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-columns-selector {
|
||||||
|
@include adf-columns-selector-top-bottom-padding;
|
||||||
|
|
||||||
|
min-width: 277px;
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
@include adf-columns-selector-side-padding;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header-label {
|
||||||
|
font-size: var(--theme-body-1-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-list-item-container {
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--theme-bg-hover-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-list-content {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
width: 210px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-column-checkbox {
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-footer {
|
||||||
|
@include adf-columns-selector-side-padding;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-divider {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search-input-container {
|
||||||
|
@include adf-columns-selector-side-padding;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search-input {
|
||||||
|
padding: 10px 10px 10px 29px;
|
||||||
|
width: 100%;
|
||||||
|
outline: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--theme-background-color);
|
||||||
|
background: var(--theme-background-color);
|
||||||
|
|
||||||
|
:focus {
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search-input-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 17px;
|
||||||
|
top: 10px;
|
||||||
|
font-size: var(--theme-adf-icon-1-font-size);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,162 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* 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, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
import { ColumnsSelectorComponent } from './columns-selector.component';
|
||||||
|
import { DataColumn } from '../../data/data-column.model';
|
||||||
|
import { Observable, Subject } from 'rxjs';
|
||||||
|
import { MatMenuTrigger } from '@angular/material/menu';
|
||||||
|
import { CoreTestingModule } from 'core/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||||
|
import { HarnessLoader } from '@angular/cdk/testing';
|
||||||
|
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
|
||||||
|
|
||||||
|
describe('ColumnsSelectorComponent', () => {
|
||||||
|
let fixture: ComponentFixture<ColumnsSelectorComponent>;
|
||||||
|
let loader: HarnessLoader;
|
||||||
|
|
||||||
|
let component: ColumnsSelectorComponent;
|
||||||
|
let inputColumns: DataColumn[] = [];
|
||||||
|
|
||||||
|
const menuOpenedTrigger = new Subject<void>();
|
||||||
|
const menuClosedTrigger = new Subject<void>();
|
||||||
|
|
||||||
|
let mainMenuTrigger: { menuOpened: Observable<void>; menuClosed: Observable<void> };
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
CoreTestingModule
|
||||||
|
],
|
||||||
|
declarations: [ColumnsSelectorComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ColumnsSelectorComponent);
|
||||||
|
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||||
|
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
inputColumns = [{
|
||||||
|
id: 'id0',
|
||||||
|
key: 'key0',
|
||||||
|
title: 'title0',
|
||||||
|
type: 'text'
|
||||||
|
}, {
|
||||||
|
id: 'id1',
|
||||||
|
key: 'key1',
|
||||||
|
title: 'title1',
|
||||||
|
type: 'text'
|
||||||
|
}, {
|
||||||
|
id: 'id2',
|
||||||
|
key: 'key2',
|
||||||
|
title: 'title2',
|
||||||
|
type: 'text'
|
||||||
|
}, {
|
||||||
|
id: 'id3',
|
||||||
|
key: 'NoTitle',
|
||||||
|
type: 'text'
|
||||||
|
}, {
|
||||||
|
id: 'id4',
|
||||||
|
key: 'IsHidden',
|
||||||
|
type: 'text',
|
||||||
|
title: 'title4',
|
||||||
|
isHidden: true
|
||||||
|
}];
|
||||||
|
|
||||||
|
mainMenuTrigger = {
|
||||||
|
menuOpened: menuOpenedTrigger.asObservable(),
|
||||||
|
menuClosed: menuClosedTrigger.asObservable()
|
||||||
|
};
|
||||||
|
|
||||||
|
component.columns = inputColumns;
|
||||||
|
component.mainMenuTrigger = mainMenuTrigger as MatMenuTrigger;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear search after closing menu', fakeAsync(() => {
|
||||||
|
menuOpenedTrigger.next();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
let searchInput = fixture.debugElement.query(By.css('.adf-columns-selector-search-input')).nativeElement;
|
||||||
|
searchInput.value = 'TEST';
|
||||||
|
searchInput.dispatchEvent(new Event('input'));
|
||||||
|
|
||||||
|
tick(300);
|
||||||
|
expect(searchInput.value).toBe('TEST');
|
||||||
|
|
||||||
|
menuClosedTrigger.next();
|
||||||
|
tick(300);
|
||||||
|
searchInput = fixture.debugElement.query(By.css('.adf-columns-selector-search-input')).nativeElement;
|
||||||
|
|
||||||
|
expect(searchInput.value).toBe('');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should list only columns with title', async () => {
|
||||||
|
menuOpenedTrigger.next();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);
|
||||||
|
|
||||||
|
expect(checkboxes.length).toBe(4);
|
||||||
|
expect(await checkboxes[0].getLabelText()).toBe(inputColumns[0].title);
|
||||||
|
expect(await checkboxes[1].getLabelText()).toBe(inputColumns[1].title);
|
||||||
|
expect(await checkboxes[2].getLabelText()).toBe(inputColumns[2].title);
|
||||||
|
expect(await checkboxes[3].getLabelText()).toBe(inputColumns[4].title);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter columns by search text', fakeAsync(async () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
menuOpenedTrigger.next();
|
||||||
|
|
||||||
|
const searchInput = fixture.debugElement.query(By.css('.adf-columns-selector-search-input')).nativeElement;
|
||||||
|
searchInput.value = inputColumns[0].title;
|
||||||
|
searchInput.dispatchEvent(new Event('input'));
|
||||||
|
|
||||||
|
tick(400);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const columnCheckboxes = await loader.getAllHarnesses(MatCheckboxHarness);
|
||||||
|
|
||||||
|
expect(columnCheckboxes.length).toBe(1);
|
||||||
|
expect(await columnCheckboxes[0].getLabelText()).toBe(inputColumns[0].title);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should change column visibility', async () => {
|
||||||
|
menuOpenedTrigger.next();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const firstColumnCheckbox = await loader.getHarness(MatCheckboxHarness);
|
||||||
|
await firstColumnCheckbox.toggle();
|
||||||
|
|
||||||
|
expect(component.columnItems[0].isHidden).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set proper default state for checkboxes', async () => {
|
||||||
|
menuOpenedTrigger.next();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const checkboxes = await loader.getAllHarnesses(MatCheckboxHarness);
|
||||||
|
|
||||||
|
expect(await checkboxes[0].isChecked()).toBe(true);
|
||||||
|
expect(await checkboxes[1].isChecked()).toBe(true);
|
||||||
|
expect(await checkboxes[2].isChecked()).toBe(true);
|
||||||
|
expect(await checkboxes[3].isChecked()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,83 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* 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, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
import { MatMenuTrigger } from '@angular/material/menu';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||||
|
import { DataColumn } from '../../data/data-column.model';
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-datatable-column-selector',
|
||||||
|
templateUrl: './columns-selector.component.html',
|
||||||
|
styleUrls: ['./columns-selector.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class ColumnsSelectorComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
columns: DataColumn[] = [];
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
mainMenuTrigger: MatMenuTrigger;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
submitColumnsVisibility = new EventEmitter<DataColumn[]>();
|
||||||
|
|
||||||
|
onDestroy$ = new Subject();
|
||||||
|
columnItems: DataColumn[] = [];
|
||||||
|
searchInputControl = new FormControl('');
|
||||||
|
searchQuery = '';
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.mainMenuTrigger.menuOpened.pipe(
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
).subscribe(() => {
|
||||||
|
this.columnItems = this.columns.map(column => ({...column}));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mainMenuTrigger.menuClosed.pipe(
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
).subscribe(() => {
|
||||||
|
this.searchInputControl.setValue('');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchInputControl.valueChanges.pipe(
|
||||||
|
debounceTime(300),
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
).subscribe((searchQuery) => {
|
||||||
|
this.searchQuery = searchQuery;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.onDestroy$.next(true);
|
||||||
|
this.onDestroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
closeMenu(): void {
|
||||||
|
this.mainMenuTrigger.closeMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeColumnVisibility(column: DataColumn): void {
|
||||||
|
column.isHidden = !column.isHidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(): void {
|
||||||
|
this.submitColumnsVisibility.emit(this.columnItems);
|
||||||
|
this.closeMenu();
|
||||||
|
}
|
||||||
|
}
|
@@ -27,7 +27,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-cell-header"
|
<div class="adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}} adf-datatable-cell-header"
|
||||||
*ngFor="let col of data.getColumns(); let columnIndex = index"
|
*ngFor="
|
||||||
|
let col of (data.getColumns() | filterOutEvery:'isHidden':true);
|
||||||
|
let columnIndex = index"
|
||||||
[class.adf-sortable]="col.sortable"
|
[class.adf-sortable]="col.sortable"
|
||||||
[attr.data-automation-id]="'auto_id_' + col.key"
|
[attr.data-automation-id]="'auto_id_' + col.key"
|
||||||
[class.adf-datatable__header--sorted-asc]="isColumnSorted(col, 'asc')"
|
[class.adf-datatable__header--sorted-asc]="isColumnSorted(col, 'asc')"
|
||||||
@@ -93,10 +95,31 @@
|
|||||||
<div class="adf-drop-header-cell-placeholder" *cdkDragPlaceholder></div>
|
<div class="adf-drop-header-cell-placeholder" *cdkDragPlaceholder></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Actions (right) -->
|
<!-- Header actions (right) -->
|
||||||
<div *ngIf="actions && actionsPosition === 'right'" class="adf-actions-column adf-datatable-cell-header adf-datatable__actions-cell">
|
<div
|
||||||
<span class="adf-sr-only">{{ 'ADF-DATATABLE.ACCESSIBILITY.ACTIONS' | translate }}</span>
|
*ngIf="actions && actionsPosition === 'right' || mainActionTemplate"
|
||||||
|
class="adf-actions-column adf-datatable-cell-header adf-datatable__actions-cell"
|
||||||
|
>
|
||||||
|
<ng-container *ngIf="mainActionTemplate">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
#mainMenuTrigger="matMenuTrigger"
|
||||||
|
[matMenuTriggerFor]="mainMenu">
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<mat-menu #mainMenu>
|
||||||
|
<ng-container
|
||||||
|
[ngTemplateOutlet]="mainActionTemplate"
|
||||||
|
[ngTemplateOutletContext]="{
|
||||||
|
$implicit: mainMenuTrigger
|
||||||
|
}">
|
||||||
|
</ng-container>
|
||||||
|
</mat-menu>
|
||||||
|
<span class="adf-sr-only">{{ 'ADF-DATATABLE.ACCESSIBILITY.ACTIONS' | translate }}</span>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</adf-datatable-row>
|
</adf-datatable-row>
|
||||||
<mat-form-field *ngIf="display === 'gallery' && isHeaderVisible()">
|
<mat-form-field *ngIf="display === 'gallery' && isHeaderVisible()">
|
||||||
<mat-select [value]="getSortingKey()" [attr.data-automation-id]="'grid-view-sorting'">
|
<mat-select [value]="getSortingKey()" [attr.data-automation-id]="'grid-view-sorting'">
|
||||||
@@ -156,7 +179,7 @@
|
|||||||
{{ 'ADF-DATATABLE.ACCESSIBILITY.SELECT_FILE' | translate }}
|
{{ 'ADF-DATATABLE.ACCESSIBILITY.SELECT_FILE' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div *ngFor="let col of data.getColumns()"
|
<div *ngFor="let col of (data.getColumns() | filterOutEvery:'isHidden':true);"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
class="adf-datatable-cell adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}}"
|
class="adf-datatable-cell adf-datatable-cell--{{col.type || 'text'}} {{col.cssClass}}"
|
||||||
[attr.title]="col.title | translate"
|
[attr.title]="col.title | translate"
|
||||||
@@ -262,28 +285,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Actions (right) -->
|
<!-- Row actions (right) -->
|
||||||
<div *ngIf="actions && actionsPosition === 'right'"
|
<div *ngIf="(actions && actionsPosition === 'right') || mainActionTemplate"
|
||||||
role="gridcell"
|
role="gridcell"
|
||||||
class="adf-datatable-cell adf-datatable__actions-cell adf-datatable-center-actions-column-ie">
|
class="adf-datatable-cell adf-datatable__actions-cell adf-datatable-center-actions-column-ie">
|
||||||
<button mat-icon-button [matMenuTriggerFor]="menu" #actionsMenuTrigger="matMenuTrigger"
|
|
||||||
[ngClass]="getHideActionsWithoutHoverClass(actionsMenuTrigger)"
|
<ng-container *ngIf="(actions && actionsPosition === 'right')">
|
||||||
[attr.aria-label]="'ADF-DATATABLE.ACCESSIBILITY.ROW_OPTION_BUTTON' | translate"
|
<button mat-icon-button [matMenuTriggerFor]="menu" #actionsMenuTrigger="matMenuTrigger"
|
||||||
[title]="'ADF-DATATABLE.CONTENT-ACTIONS.TOOLTIP' | translate"
|
[ngClass]="getHideActionsWithoutHoverClass(actionsMenuTrigger)"
|
||||||
[attr.id]="'action_menu_right_' + idx"
|
[attr.aria-label]="'ADF-DATATABLE.ACCESSIBILITY.ROW_OPTION_BUTTON' | translate"
|
||||||
[attr.data-automation-id]="'action_menu_' + idx">
|
[title]="'ADF-DATATABLE.CONTENT-ACTIONS.TOOLTIP' | translate"
|
||||||
<mat-icon>more_vert</mat-icon>
|
[attr.id]="'action_menu_right_' + idx"
|
||||||
</button>
|
[attr.data-automation-id]="'action_menu_' + idx">
|
||||||
<mat-menu #menu="matMenu">
|
<mat-icon>more_vert</mat-icon>
|
||||||
<button mat-menu-item *ngFor="let action of getRowActions(row)"
|
|
||||||
[attr.data-automation-id]="action.title"
|
|
||||||
[attr.aria-label]="action.title | translate"
|
|
||||||
[disabled]="action.disabled"
|
|
||||||
(click)="onExecuteRowAction(row, action)">
|
|
||||||
<mat-icon *ngIf="action.icon">{{ action.icon }}</mat-icon>
|
|
||||||
<span>{{ action.title | translate }}</span>
|
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
<mat-menu #menu="matMenu">
|
||||||
|
<button mat-menu-item *ngFor="let action of getRowActions(row)"
|
||||||
|
[attr.data-automation-id]="action.title"
|
||||||
|
[attr.aria-label]="action.title | translate"
|
||||||
|
[disabled]="action.disabled"
|
||||||
|
(click)="onExecuteRowAction(row, action)">
|
||||||
|
<mat-icon *ngIf="action.icon">{{ action.icon }}</mat-icon>
|
||||||
|
<span>{{ action.title | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</adf-datatable-row>
|
</adf-datatable-row>
|
||||||
<div *ngIf="isEmpty()"
|
<div *ngIf="isEmpty()"
|
||||||
|
@@ -1720,3 +1720,70 @@ describe('Drag&Drop column header', () => {
|
|||||||
expect(headerCells[1].innerText).toBe(dataTableSchema[0].title);
|
expect(headerCells[1].innerText).toBe(dataTableSchema[0].title);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Show/hide columns', () => {
|
||||||
|
let fixture: ComponentFixture<DataTableComponent>;
|
||||||
|
let dataTable: DataTableComponent;
|
||||||
|
let data: DataColumn[] = [];
|
||||||
|
let dataTableSchema: DataColumn[] = [];
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
CoreTestingModule
|
||||||
|
],
|
||||||
|
declarations: [CustomColumnTemplateComponent],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DataTableComponent);
|
||||||
|
dataTable = fixture.componentInstance;
|
||||||
|
data = [
|
||||||
|
{ id: '1', title: 'name1', key: 'key', type: 'text' },
|
||||||
|
{ id: '2', title: 'name1', key: 'key', type: 'text' },
|
||||||
|
{ id: '3', title: 'name1', key: 'key', type: 'text' }
|
||||||
|
];
|
||||||
|
|
||||||
|
dataTableSchema = [
|
||||||
|
new ObjectDataColumn({ key: 'id', title: 'ID' }),
|
||||||
|
new ObjectDataColumn({ key: 'name', title: 'Name'}),
|
||||||
|
new ObjectDataColumn({ key: 'status', title: 'status', isHidden: true })
|
||||||
|
];
|
||||||
|
|
||||||
|
dataTable.data = new ObjectDataTableAdapter(
|
||||||
|
[...data],
|
||||||
|
[...dataTableSchema]
|
||||||
|
);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hide columns with isHidden prop', () => {
|
||||||
|
const headerCells = fixture.debugElement.nativeElement.querySelectorAll('.adf-datatable-cell--text.adf-datatable-cell-header');
|
||||||
|
|
||||||
|
expect(headerCells.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload columns after changing columns visibility', () => {
|
||||||
|
const columns = [
|
||||||
|
new ObjectDataColumn({ key: 'id', title: 'ID' }),
|
||||||
|
new ObjectDataColumn({ key: 'name', title: 'Name', isHidden: true }),
|
||||||
|
new ObjectDataColumn({ key: 'status', title: 'status', isHidden: true })
|
||||||
|
];
|
||||||
|
|
||||||
|
dataTable.ngOnChanges({
|
||||||
|
columns: {
|
||||||
|
previousValue: undefined,
|
||||||
|
currentValue: columns,
|
||||||
|
firstChange: false,
|
||||||
|
isFirstChange: () => false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const headerCells = fixture.debugElement.nativeElement.querySelectorAll('.adf-datatable-cell--text.adf-datatable-cell-header');
|
||||||
|
expect(headerCells.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@@ -104,6 +104,10 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
|
|||||||
@Input()
|
@Input()
|
||||||
multiselect: boolean = false;
|
multiselect: boolean = false;
|
||||||
|
|
||||||
|
/** Toggles main data table action column. */
|
||||||
|
@Input()
|
||||||
|
mainTableAction: boolean = true;
|
||||||
|
|
||||||
/** Toggles the data actions column. */
|
/** Toggles the data actions column. */
|
||||||
@Input()
|
@Input()
|
||||||
actions: boolean = false;
|
actions: boolean = false;
|
||||||
@@ -200,6 +204,7 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
|
|||||||
noContentTemplate: TemplateRef<any>;
|
noContentTemplate: TemplateRef<any>;
|
||||||
noPermissionTemplate: TemplateRef<any>;
|
noPermissionTemplate: TemplateRef<any>;
|
||||||
loadingTemplate: TemplateRef<any>;
|
loadingTemplate: TemplateRef<any>;
|
||||||
|
mainActionTemplate: TemplateRef<any>;
|
||||||
|
|
||||||
isSelectAllIndeterminate: boolean = false;
|
isSelectAllIndeterminate: boolean = false;
|
||||||
isSelectAllChecked: boolean = false;
|
isSelectAllChecked: boolean = false;
|
||||||
@@ -313,10 +318,16 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDropHeaderColumn(event: CdkDragDrop<unknown>): void {
|
onDropHeaderColumn(event: CdkDragDrop<unknown>): void {
|
||||||
const columns = this.data.getColumns();
|
const allColumns = this.data.getColumns();
|
||||||
moveItemInArray(columns, event.previousIndex, event.currentIndex);
|
const shownColumns = allColumns.filter(column => !column.isHidden);
|
||||||
|
const hiddenColumns = allColumns.filter(column => column.isHidden);
|
||||||
|
|
||||||
|
moveItemInArray(shownColumns, event.previousIndex, event.currentIndex);
|
||||||
|
const allColumnsWithNewOrder = [...shownColumns, ...hiddenColumns];
|
||||||
|
|
||||||
|
this.setTableColumns(allColumnsWithNewOrder);
|
||||||
|
this.columnOrderChanged.emit(allColumnsWithNewOrder);
|
||||||
|
|
||||||
this.columnOrderChanged.emit(columns);
|
|
||||||
this.isDraggingHeaderColumn = false;
|
this.isDraggingHeaderColumn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,4 +46,5 @@ export interface DataColumn {
|
|||||||
sortingKey?: string;
|
sortingKey?: string;
|
||||||
header?: TemplateRef<any>;
|
header?: TemplateRef<any>;
|
||||||
draggable?: boolean;
|
draggable?: boolean;
|
||||||
|
isHidden?: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -38,6 +38,9 @@ export abstract class DataTableSchema {
|
|||||||
protected columnsOrder: string[] | undefined;
|
protected columnsOrder: string[] | undefined;
|
||||||
protected columnsOrderedByKey: string = 'id';
|
protected columnsOrderedByKey: string = 'id';
|
||||||
|
|
||||||
|
protected hiddenColumns: string[] | undefined;
|
||||||
|
protected hiddenColumnsKey: string = 'id';
|
||||||
|
|
||||||
private layoutPresets = {};
|
private layoutPresets = {};
|
||||||
|
|
||||||
private columnsSchemaSubject$ = new ReplaySubject<boolean>();
|
private columnsSchemaSubject$ = new ReplaySubject<boolean>();
|
||||||
@@ -59,7 +62,8 @@ export abstract class DataTableSchema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createColumns(): void {
|
public createColumns(): void {
|
||||||
const columns = this.mergeJsonAndHtmlSchema();
|
const allColumns = this.mergeJsonAndHtmlSchema();
|
||||||
|
const columns = this.setHiddenColumns(allColumns);
|
||||||
this.columns = this.sortColumnsByKey(columns);
|
this.columns = this.sortColumnsByKey(columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,4 +131,19 @@ export abstract class DataTableSchema {
|
|||||||
|
|
||||||
return [...columnsWithProperOrder, ...defaultColumns];
|
return [...columnsWithProperOrder, ...defaultColumns];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setHiddenColumns(columns: DataColumn[]): DataColumn[] {
|
||||||
|
if (this.hiddenColumns) {
|
||||||
|
return columns.map(column => {
|
||||||
|
const columnShouldBeHidden = this.hiddenColumns.includes(column[this.hiddenColumnsKey]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...column,
|
||||||
|
isHidden: columnShouldBeHidden
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@ export class ObjectDataColumn implements DataColumn {
|
|||||||
sortingKey?: string;
|
sortingKey?: string;
|
||||||
header?: TemplateRef<any>;
|
header?: TemplateRef<any>;
|
||||||
draggable: boolean;
|
draggable: boolean;
|
||||||
|
isHidden: boolean;
|
||||||
|
|
||||||
constructor(input: any) {
|
constructor(input: any) {
|
||||||
this.id = input.id ?? '';
|
this.id = input.id ?? '';
|
||||||
@@ -50,5 +51,6 @@ export class ObjectDataColumn implements DataColumn {
|
|||||||
this.sortingKey = input.sortingKey;
|
this.sortingKey = input.sortingKey;
|
||||||
this.header = input.header;
|
this.header = input.header;
|
||||||
this.draggable = input.draggable ?? false;
|
this.draggable = input.draggable ?? false;
|
||||||
|
this.isHidden = input.isHidden ?? false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,7 @@ import { DataTableCellComponent } from './components/datatable-cell/datatable-ce
|
|||||||
import { DataTableRowComponent } from './components/datatable-row/datatable-row.component';
|
import { DataTableRowComponent } from './components/datatable-row/datatable-row.component';
|
||||||
import { DataTableComponent } from './components/datatable/datatable.component';
|
import { DataTableComponent } from './components/datatable/datatable.component';
|
||||||
import { DateCellComponent } from './components/date-cell/date-cell.component';
|
import { DateCellComponent } from './components/date-cell/date-cell.component';
|
||||||
|
import { ColumnsSelectorComponent } from './components/columns-selector/columns-selector.component';
|
||||||
import { EmptyListBodyDirective,
|
import { EmptyListBodyDirective,
|
||||||
EmptyListComponent,
|
EmptyListComponent,
|
||||||
EmptyListFooterDirective,
|
EmptyListFooterDirective,
|
||||||
@@ -42,12 +43,14 @@ import { HeaderFilterTemplateDirective } from './directives/header-filter-templa
|
|||||||
import { CustomEmptyContentTemplateDirective } from './directives/custom-empty-content-template.directive';
|
import { CustomEmptyContentTemplateDirective } from './directives/custom-empty-content-template.directive';
|
||||||
import { CustomLoadingContentTemplateDirective } from './directives/custom-loading-template.directive';
|
import { CustomLoadingContentTemplateDirective } from './directives/custom-loading-template.directive';
|
||||||
import { CustomNoPermissionTemplateDirective } from './directives/custom-no-permission-template.directive';
|
import { CustomNoPermissionTemplateDirective } from './directives/custom-no-permission-template.directive';
|
||||||
|
import { MainMenuDataTableTemplateDirective } from './directives/main-data-table-action-template.directive';
|
||||||
import { JsonCellComponent } from './components/json-cell/json-cell.component';
|
import { JsonCellComponent } from './components/json-cell/json-cell.component';
|
||||||
import { ClipboardModule } from '../clipboard/clipboard.module';
|
import { ClipboardModule } from '../clipboard/clipboard.module';
|
||||||
import { DropZoneDirective } from './directives/drop-zone.directive';
|
import { DropZoneDirective } from './directives/drop-zone.directive';
|
||||||
import { DataColumnModule } from '../data-column/data-column.module';
|
import { DataColumnModule } from '../data-column/data-column.module';
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { IconModule } from '../icon/icon.module';
|
import { IconModule } from '../icon/icon.module';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -61,7 +64,9 @@ import { IconModule } from '../icon/icon.module';
|
|||||||
DirectiveModule,
|
DirectiveModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
IconModule
|
IconModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
DataTableComponent,
|
DataTableComponent,
|
||||||
@@ -75,6 +80,7 @@ import { IconModule } from '../icon/icon.module';
|
|||||||
FileSizeCellComponent,
|
FileSizeCellComponent,
|
||||||
LocationCellComponent,
|
LocationCellComponent,
|
||||||
JsonCellComponent,
|
JsonCellComponent,
|
||||||
|
ColumnsSelectorComponent,
|
||||||
NoContentTemplateDirective,
|
NoContentTemplateDirective,
|
||||||
NoPermissionTemplateDirective,
|
NoPermissionTemplateDirective,
|
||||||
LoadingContentTemplateDirective,
|
LoadingContentTemplateDirective,
|
||||||
@@ -82,6 +88,7 @@ import { IconModule } from '../icon/icon.module';
|
|||||||
CustomEmptyContentTemplateDirective,
|
CustomEmptyContentTemplateDirective,
|
||||||
CustomLoadingContentTemplateDirective,
|
CustomLoadingContentTemplateDirective,
|
||||||
CustomNoPermissionTemplateDirective,
|
CustomNoPermissionTemplateDirective,
|
||||||
|
MainMenuDataTableTemplateDirective,
|
||||||
DropZoneDirective
|
DropZoneDirective
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
@@ -93,6 +100,7 @@ import { IconModule } from '../icon/icon.module';
|
|||||||
DataTableCellComponent,
|
DataTableCellComponent,
|
||||||
DataTableRowComponent,
|
DataTableRowComponent,
|
||||||
DateCellComponent,
|
DateCellComponent,
|
||||||
|
ColumnsSelectorComponent,
|
||||||
FileSizeCellComponent,
|
FileSizeCellComponent,
|
||||||
LocationCellComponent,
|
LocationCellComponent,
|
||||||
JsonCellComponent,
|
JsonCellComponent,
|
||||||
@@ -103,6 +111,7 @@ import { IconModule } from '../icon/icon.module';
|
|||||||
CustomEmptyContentTemplateDirective,
|
CustomEmptyContentTemplateDirective,
|
||||||
CustomLoadingContentTemplateDirective,
|
CustomLoadingContentTemplateDirective,
|
||||||
CustomNoPermissionTemplateDirective,
|
CustomNoPermissionTemplateDirective,
|
||||||
|
MainMenuDataTableTemplateDirective,
|
||||||
DropZoneDirective
|
DropZoneDirective
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@@ -0,0 +1,44 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* 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 { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||||
|
import { DataTableComponent } from '../components/datatable/datatable.component';
|
||||||
|
import { MainMenuDataTableTemplateDirective } from './main-data-table-action-template.directive';
|
||||||
|
|
||||||
|
describe('MainMenuDataTableTemplateDirective', () => {
|
||||||
|
|
||||||
|
let fixture: ComponentFixture<DataTableComponent>;
|
||||||
|
let dataTable: DataTableComponent;
|
||||||
|
let directive: MainMenuDataTableTemplateDirective;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DataTableComponent);
|
||||||
|
dataTable = fixture.componentInstance;
|
||||||
|
directive = new MainMenuDataTableTemplateDirective(dataTable);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applies template to the datatable', () => {
|
||||||
|
const template: any = 'test template';
|
||||||
|
directive.template = template;
|
||||||
|
directive.ngAfterContentInit();
|
||||||
|
expect(dataTable.mainActionTemplate).toBe(template);
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* 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 { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core';
|
||||||
|
import { DataTableComponent } from '../components/datatable/datatable.component';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'adf-main-menu-datatable-template'
|
||||||
|
})
|
||||||
|
export class MainMenuDataTableTemplateDirective implements AfterContentInit {
|
||||||
|
|
||||||
|
@ContentChild(TemplateRef)
|
||||||
|
template: any;
|
||||||
|
|
||||||
|
constructor(private dataTable: DataTableComponent) {}
|
||||||
|
|
||||||
|
ngAfterContentInit() {
|
||||||
|
if (this.dataTable) {
|
||||||
|
this.dataTable.mainActionTemplate = this.template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -38,6 +38,7 @@ export * from './components/empty-list/empty-list.component';
|
|||||||
export * from './components/filesize-cell/filesize-cell.component';
|
export * from './components/filesize-cell/filesize-cell.component';
|
||||||
export * from './components/json-cell/json-cell.component';
|
export * from './components/json-cell/json-cell.component';
|
||||||
export * from './components/location-cell/location-cell.component';
|
export * from './components/location-cell/location-cell.component';
|
||||||
|
export * from './components/columns-selector/columns-selector.component';
|
||||||
export * from './data/data-table.schema';
|
export * from './data/data-table.schema';
|
||||||
|
|
||||||
export * from './directives/loading-template.directive';
|
export * from './directives/loading-template.directive';
|
||||||
|
@@ -350,6 +350,11 @@
|
|||||||
"PRESENTATION": "Presentation",
|
"PRESENTATION": "Presentation",
|
||||||
"SPREADSHEET": "Spreadsheet",
|
"SPREADSHEET": "Spreadsheet",
|
||||||
"MISCELLANEOUS": "Miscellaneous"
|
"MISCELLANEOUS": "Miscellaneous"
|
||||||
|
},
|
||||||
|
"COLUMNS_SELECTOR": {
|
||||||
|
"COLUMNS": "Columns",
|
||||||
|
"SEARCH": "Search",
|
||||||
|
"APPLY": "Apply"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"USER_PROFILE": {
|
"USER_PROFILE": {
|
||||||
|
75
lib/core/pipes/filter-out-every-object-by-prop.pipe.spec.ts
Normal file
75
lib/core/pipes/filter-out-every-object-by-prop.pipe.spec.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* 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 { FilterOutArrayObjectsByPropPipe } from './filter-out-every-object-by-prop.pipe';
|
||||||
|
|
||||||
|
describe('FilterOutArrayObjectsByPropPipe', () => {
|
||||||
|
let pipe: FilterOutArrayObjectsByPropPipe<any>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
pipe = new FilterOutArrayObjectsByPropPipe();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter out object', () => {
|
||||||
|
const testArray = [{
|
||||||
|
id: 1
|
||||||
|
}, {
|
||||||
|
id: 2
|
||||||
|
}, {
|
||||||
|
id: 3
|
||||||
|
}];
|
||||||
|
|
||||||
|
const result = pipe.transform(testArray, 'id', 3);
|
||||||
|
|
||||||
|
expect(result.length).toBe(testArray.length - 1);
|
||||||
|
expect(result[0]).toEqual(testArray[0]);
|
||||||
|
expect(result[1]).toEqual(testArray[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter out multiple objects', () => {
|
||||||
|
const testArray = [{
|
||||||
|
isHidden: true
|
||||||
|
}, {
|
||||||
|
isHidden: true
|
||||||
|
}, {
|
||||||
|
isHidden: true
|
||||||
|
}, {
|
||||||
|
isHidden: false
|
||||||
|
}];
|
||||||
|
|
||||||
|
const result = pipe.transform(testArray, 'isHidden', true);
|
||||||
|
|
||||||
|
expect(result.length).toBe(1);
|
||||||
|
expect(result[0]).toEqual(testArray[3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with empty array', () => {
|
||||||
|
const testArray = [];
|
||||||
|
|
||||||
|
const result = pipe.transform(testArray, 'prop', true);
|
||||||
|
|
||||||
|
expect(result.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with non existing prop', () => {
|
||||||
|
const testArray = [{ prop: 1 }];
|
||||||
|
|
||||||
|
const result = pipe.transform(testArray, 'nonExistionProp', 1);
|
||||||
|
|
||||||
|
expect(result.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
25
lib/core/pipes/filter-out-every-object-by-prop.pipe.ts
Normal file
25
lib/core/pipes/filter-out-every-object-by-prop.pipe.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* 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 { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({ name: 'filterOutEvery' })
|
||||||
|
export class FilterOutArrayObjectsByPropPipe<T> implements PipeTransform {
|
||||||
|
transform(values: T[], filterKey: string, filterValue: any): T[] {
|
||||||
|
return (values ?? []).filter(value => value[filterKey] !== filterValue);
|
||||||
|
}
|
||||||
|
}
|
44
lib/core/pipes/filter-string.pipe.spec.ts
Normal file
44
lib/core/pipes/filter-string.pipe.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* 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 { FilterStringPipe } from './filter-string.pipe';
|
||||||
|
|
||||||
|
describe('FilterStringPipe', () => {
|
||||||
|
let pipe: FilterStringPipe;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
pipe = new FilterStringPipe();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should left string', () => {
|
||||||
|
const result = pipe.transform('ABC', 'B');
|
||||||
|
|
||||||
|
expect(result).toBe('ABC');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter out string', () => {
|
||||||
|
const result = pipe.transform('ABC', 'D');
|
||||||
|
|
||||||
|
expect(result).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should left string when no query string is passed', () => {
|
||||||
|
const result = pipe.transform('ABC');
|
||||||
|
|
||||||
|
expect(result).toBe('ABC');
|
||||||
|
});
|
||||||
|
});
|
30
lib/core/pipes/filter-string.pipe.ts
Normal file
30
lib/core/pipes/filter-string.pipe.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2019 Alfresco Software, Ltd.
|
||||||
|
*
|
||||||
|
* 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 { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({ name: 'filterString' })
|
||||||
|
export class FilterStringPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: string = '', filterBy: string = ''): string {
|
||||||
|
const testResult = filterBy ?
|
||||||
|
value.toLowerCase().indexOf(filterBy.toLowerCase()) > -1 :
|
||||||
|
true;
|
||||||
|
|
||||||
|
return testResult ? value : '';
|
||||||
|
}
|
||||||
|
}
|
@@ -34,6 +34,8 @@ import { LocalizedRolePipe } from './localized-role.pipe';
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MomentDatePipe } from './moment-date.pipe';
|
import { MomentDatePipe } from './moment-date.pipe';
|
||||||
import { MomentDateTimePipe } from './moment-datetime.pipe';
|
import { MomentDateTimePipe } from './moment-datetime.pipe';
|
||||||
|
import { FilterStringPipe } from './filter-string.pipe';
|
||||||
|
import { FilterOutArrayObjectsByPropPipe } from './filter-out-every-object-by-prop.pipe';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -55,7 +57,9 @@ import { MomentDateTimePipe } from './moment-datetime.pipe';
|
|||||||
DecimalNumberPipe,
|
DecimalNumberPipe,
|
||||||
LocalizedRolePipe,
|
LocalizedRolePipe,
|
||||||
MomentDatePipe,
|
MomentDatePipe,
|
||||||
MomentDateTimePipe
|
MomentDateTimePipe,
|
||||||
|
FilterStringPipe,
|
||||||
|
FilterOutArrayObjectsByPropPipe
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
FileSizePipe,
|
FileSizePipe,
|
||||||
@@ -71,7 +75,9 @@ import { MomentDateTimePipe } from './moment-datetime.pipe';
|
|||||||
DecimalNumberPipe,
|
DecimalNumberPipe,
|
||||||
LocalizedRolePipe,
|
LocalizedRolePipe,
|
||||||
MomentDatePipe,
|
MomentDatePipe,
|
||||||
MomentDateTimePipe
|
MomentDateTimePipe,
|
||||||
|
FilterStringPipe,
|
||||||
|
FilterOutArrayObjectsByPropPipe
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
FileSizePipe,
|
FileSizePipe,
|
||||||
@@ -88,7 +94,9 @@ import { MomentDateTimePipe } from './moment-datetime.pipe';
|
|||||||
DecimalNumberPipe,
|
DecimalNumberPipe,
|
||||||
LocalizedRolePipe,
|
LocalizedRolePipe,
|
||||||
MomentDatePipe,
|
MomentDatePipe,
|
||||||
MomentDateTimePipe
|
MomentDateTimePipe,
|
||||||
|
FilterStringPipe,
|
||||||
|
FilterOutArrayObjectsByPropPipe
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class PipeModule {
|
export class PipeModule {
|
||||||
|
@@ -31,3 +31,5 @@ export * from './localized-role.pipe';
|
|||||||
export * from './pipe.module';
|
export * from './pipe.module';
|
||||||
export * from './moment-date.pipe';
|
export * from './moment-date.pipe';
|
||||||
export * from './moment-datetime.pipe';
|
export * from './moment-datetime.pipe';
|
||||||
|
export * from './filter-string.pipe';
|
||||||
|
export * from './filter-out-every-object-by-prop.pipe';
|
||||||
|
Reference in New Issue
Block a user