[AAE-9200] - Be able to customise task/process list and header date f… (#7711)

* [AAE-9200] - Be able to customise task/process list and header date formats

* Update json schema

* Revert schema changes

* Update task and process name translation values

* move task/process header date formats inside header config

* Add documentation

* Update documentation

* Use a fixed timezone for unit tests of core and cloud

* Fix e2e due to column rename

* More e2e fixes due to column rename, remove test covered by unit test
This commit is contained in:
Ardit Domi 2022-07-19 15:46:31 +02:00 committed by GitHub
parent 46926ed818
commit fa587510bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 171 additions and 54 deletions

View File

@ -847,6 +847,16 @@ here is the sample resolver which merge the users property and status and it wil
![](../../docassets/images/custom-data-table-resolver.png)
# Tooltip
You can define the tooltip format for cells of type date using a configuration in `app.config.json`:
```json
"dateValues": {
"defaultTooltipDateFormat": "medium"
}
```
## See also
- [Data column component](data-column.component.md)

View File

@ -50,6 +50,14 @@ However, you can also choose which properties to show using a configuration in `
With this configuration, only the four listed properties will be shown.
You can also define the date format of the date properties using a configuration in `app.config.json`:
```json
"adf-cloud-process-header": {
"defaultDateFormat": "full"
}
```
## See also
- [Process header cloud service](../../process-services-cloud/services/process-header-cloud.service.md)

View File

@ -58,3 +58,11 @@ However, you can also choose which properties to show using a configuration in `
```
With this configuration, only the four listed properties will be shown.
You can also define the date format of the date properties using a configuration in `app.config.json`:
```json
"adf-cloud-task-header": {
"defaultDateFormat": "full"
}
```

View File

@ -23,11 +23,11 @@ export class ProcessDetailsCloudDemoPage {
dataTable: DataTableComponentPage = new DataTableComponentPage();
async checkTaskIsDisplayed(taskName: string): Promise<void> {
await this.dataTable.checkContentIsDisplayed('Name', taskName);
await this.dataTable.checkContentIsDisplayed('Task Name', taskName);
}
async selectProcessTaskByName(taskName: string): Promise<void> {
await this.dataTable.selectRow('Name', taskName);
await this.dataTable.selectRow('Task Name', taskName);
}
async checkListedSelectedProcessInstance(processInstanceId: string): Promise<void> {

View File

@ -156,11 +156,11 @@ describe('Process list cloud', () => {
await setFilter({ sort: 'Name' });
await setFilter({ order: SORT_DIRECTION.ASC });
await expect(await processList.getDataTable().checkListIsSorted(SORT_DIRECTION.ASC, 'Name')).toBe(true);
await expect(await processList.getDataTable().checkListIsSorted(SORT_DIRECTION.ASC, 'Process Name')).toBe(true);
await setFilter({ order: SORT_DIRECTION.DESC});
await expect(await processList.getDataTable().checkListIsSorted(SORT_DIRECTION.DESC, 'Name')).toBe(true);
await expect(await processList.getDataTable().checkListIsSorted(SORT_DIRECTION.DESC, 'Process Name')).toBe(true);
});
it('[C291783] Should display processes ordered by id when Id is selected from sort dropdown', async () => {

View File

@ -266,7 +266,7 @@ describe('Start Task Form', () => {
await processList.getDataTable().waitTillContentLoaded();
await processList.checkContentIsDisplayedByName(startEventFormProcess);
await processList.getDataTable().selectRow('Name', startEventFormProcess);
await processList.getDataTable().selectRow('Process Name', startEventFormProcess);
await browser.actions().sendKeys(protractor.Key.ENTER).perform();
await processDetailsCloudDemoPage.checkTaskIsDisplayed('StartEventFormTask');

View File

@ -20,7 +20,6 @@ import { createApiService,
AppListCloudPage,
GroupIdentityService,
IdentityService,
LocalStorageUtil,
LoginPage,
StringUtil,
TaskHeaderCloudPage,
@ -68,7 +67,6 @@ describe('Task Header cloud component', () => {
let subTask: any;
let subTaskCreatedDate: string;
let completedEndDate: string;
let defaultDate: string;
let groupInfo: any;
let testUser: any;
let unclaimedTask: any;
@ -76,7 +74,6 @@ describe('Task Header cloud component', () => {
const description = 'descriptionTask';
const formatDate = 'MMM D, YYYY';
const dateTimeFormat = 'MMM D, Y, H:mm';
const defaultFormat = 'M/D/YY';
const createCompletedTask = async function () {
const completedTaskId = await tasksService.createStandaloneTask(completedTaskName,
@ -117,7 +114,6 @@ describe('Task Header cloud component', () => {
completedCreatedDate = moment(completedTask.entry.createdDate).format(formatDate);
dueDate = moment(completedTask.entry.dueDate).format(dateTimeFormat);
completedEndDate = moment(completedTask.entry.endDate).format(formatDate);
defaultDate = moment(completedTask.entry.createdDate).format(defaultFormat);
subTask = await createSubTask(createdTaskId);
subTaskCreatedDate = moment(subTask.entry.createdDate).format(formatDate);
@ -241,29 +237,4 @@ describe('Task Header cloud component', () => {
await peopleCloudComponentPage.searchAssignee('modeler');
await peopleCloudComponentPage.checkNoResultsFoundError();
});
describe('Default Date format', () => {
beforeEach(async () => {
await LocalStorageUtil.setConfigField('dateValues', '{' +
'"defaultDateFormat": "shortDate",' +
'"defaultDateTimeFormat": "M/d/yy, h:mm a",' +
'"defaultLocale": "uk"' +
'}');
await navigationBarPage.navigateToProcessServicesCloudPage();
await appListCloudComponent.checkApsContainer();
await appListCloudComponent.goToApp(simpleApp);
});
it('[C311280] Should pick up the default date format from the app configuration', async () => {
await taskFilter.clickTaskFilter('completed-tasks');
await taskList.getDataTable().waitTillContentLoaded();
await taskList.checkContentIsDisplayedByName(completedTaskName);
await taskList.selectRow(completedTaskName);
await tasksCloudDemoPage.waitTillContentLoaded();
await taskHeaderCloudPage.checkTaskPropertyListIsDisplayed();
await expect(await taskHeaderCloudPage.getCreated()).toEqual(defaultDate);
});
});
});

View File

@ -142,10 +142,10 @@ describe('Edit task filters and task list properties', () => {
await editTaskFilter.setSortFilterDropDown('name');
await editTaskFilter.setOrderFilterDropDown(SORT_ORDER.ASC);
await expect(await taskList.getDataTable().checkListIsSorted(SORT_ORDER.ASC, 'Name')).toBe(true);
await expect(await taskList.getDataTable().checkListIsSorted(SORT_ORDER.ASC, 'Task Name')).toBe(true);
await editTaskFilter.setOrderFilterDropDown(SORT_ORDER.DESC);
await expect(await taskList.getDataTable().checkListIsSorted(SORT_ORDER.DESC, 'Name')).toBe(true);
await expect(await taskList.getDataTable().checkListIsSorted(SORT_ORDER.DESC, 'Task Name')).toBe(true);
});
it('[C290156] Should display tasks ordered by id when Id is selected from sort dropdown', async () => {

View File

@ -0,0 +1,73 @@
/*!
* @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 { UserPreferencesService } from '../../../services';
import { AppConfigService } from '../../../app-config';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CoreTestingModule } from '../../../testing';
import { DateCellComponent } from './date-cell.component';
describe('DateCellComponent', () => {
let appConfigService: AppConfigService;
let userPreferencesService: UserPreferencesService;
let fixture: ComponentFixture<DateCellComponent>;
let component: DateCellComponent;
let getLocaleSpy: jasmine.Spy;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
CoreTestingModule
],
declarations: [DateCellComponent]
});
appConfigService = TestBed.inject(AppConfigService);
userPreferencesService = TestBed.inject(UserPreferencesService);
getLocaleSpy = spyOn(userPreferencesService, 'select').and.callThrough();
appConfigService.config = {
dateValues: {
defaultDateFormat: 'mediumDate',
defaultTooltipDateFormat: 'medium'
}
};
fixture = TestBed.createComponent(DateCellComponent);
component = fixture.componentInstance;
});
it('should read locale from user preferences service', () => {
expect(getLocaleSpy).toHaveBeenCalledWith('locale');
expect(component.currentLocale).toEqual('en');
});
it('should read date format values from app config service', () => {
expect(component.format).toEqual('mediumDate');
expect(component.tooltipDateFormat).toEqual('medium');
});
it('should date values be formatted based on the formats defined in the app config', () => {
component.value$.next('2022-07-14T11:50:45.973+0000');
component.tooltip = '2022-07-14T11:50:45.973+0000';
fixture.detectChanges();
const dateCellValue = fixture.nativeElement.querySelector('.adf-datatable-cell-value');
const tooltipValue = dateCellValue.attributes['title'].value;
expect(dateCellValue.textContent.trim()).toEqual('Jul 14, 2022');
expect(dateCellValue.attributes['aria-label'].value).toEqual('Jul 14, 2022');
expect(tooltipValue).toEqual('Jul 14, 2022, 11:50:45 AM');
});
});

View File

@ -41,8 +41,7 @@ import { takeUntil } from 'rxjs/operators';
<ng-template #standard_date>
<span
class="adf-datatable-cell-value"
title="{{ tooltip | adfLocalizedDate: format }}"
class="adf-datatable-cell-value"
title="{{ tooltip | adfLocalizedDate: tooltipDateFormat }}"
[attr.aria-label]="value$ | async | adfLocalizedDate: format">
{{ value$ | async | adfLocalizedDate: format }}
</span>
@ -57,6 +56,7 @@ export class DateCellComponent extends DataTableCellComponent {
currentLocale: string;
dateFormat: string;
tooltipDateFormat: string;
get format(): string {
if (this.column) {
@ -73,6 +73,7 @@ export class DateCellComponent extends DataTableCellComponent {
super(alfrescoApiService);
this.dateFormat = appConfig.get('dateValues.defaultDateFormat', DateCellComponent.DATE_FORMAT);
this.tooltipDateFormat = appConfig.get('dateValues.defaultTooltipDateFormat', DateCellComponent.DATE_FORMAT);
if (userPreferenceService) {
userPreferenceService
.select(UserPreferenceValues.Locale)

View File

@ -102,4 +102,5 @@ module.exports = function (config) {
browsers: ['Chrome'],
singleRun: false
});
process.env.TZ = 'UTC';
};

View File

@ -84,4 +84,5 @@ module.exports = function (config) {
browsers: ['Chrome'],
singleRun: false
});
process.env.TZ = 'UTC';
};

View File

@ -6,7 +6,7 @@
"NONE": "No process instance filter selected."
},
"PROPERTIES": {
"NAME": "Name",
"NAME": "Process Name",
"CREATED": "Created",
"STATUS": "Status",
"START_DATE": "Start Date",
@ -83,7 +83,7 @@
}
},
"PROPERTIES": {
"NAME": "Name",
"NAME": "Task Name",
"ASSIGNEE": "Assignee",
"ID": "Id",
"STATUS": "Status",

View File

@ -34,7 +34,7 @@ export const processInstancePlaceholdersCloudMock: ProcessInstanceCloud = {
businessKey: '',
id: '00fcc4ab-4290-11e9-b133-0a586460016a',
initiator: 'devopsuser',
lastModified: new Date(1552152187081),
lastModified: new Date(2022, 1, 1, 1, 30, 40),
name: '',
parentId: '',
startDate: new Date(1552152187080),

View File

@ -206,4 +206,28 @@ describe('ProcessHeaderCloudComponent', () => {
expect(propertyList[1].nativeElement.textContent).toContain('ADF_CLOUD_PROCESS_HEADER.PROPERTIES.NAME');
});
});
describe('Date values format', () => {
beforeEach(() => {
appConfigService.config = {
'adf-cloud-process-header': {
defaultDateFormat: 'full'
}
};
component.ngOnInit();
component.ngOnChanges();
});
it('should format the dates based on app config format configuration', async () => {
fixture.detectChanges();
await fixture.whenStable();
const startedDateElement = fixture.debugElement.query(By.css('[data-automation-id="header-startDate"] .adf-property-value'));
const lastModifiedElement = fixture.debugElement.query(By.css('[data-automation-id="header-lastModified"] .adf-property-value'));
expect(component.dateFormat).toEqual('full');
expect(startedDateElement.nativeElement.innerText.trim()).toBe('Saturday, March 9, 2019 at 5:23:07 PM GMT+00:00');
expect(lastModifiedElement.nativeElement.innerText.trim()).toBe('Saturday, March 9, 2019 at 5:23:07 PM GMT+00:00');
});
});
});

View File

@ -55,7 +55,7 @@ export class ProcessHeaderCloudComponent implements OnChanges, OnInit, OnDestroy
}
ngOnInit() {
this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat');
this.dateFormat = this.appConfig.get('adf-cloud-process-header.defaultDateFormat');
this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale');
this.processCloudService.dataChangesDetected

View File

@ -31,7 +31,6 @@ import {
taskDetailsWithParentTaskIdMock,
createdTaskDetailsCloudMock
} from '../mocks/task-details-cloud.mock';
import moment from 'moment-es6';
import { TranslateModule } from '@ngx-translate/core';
import { MatSelectModule } from '@angular/material/select';
@ -67,9 +66,14 @@ describe('TaskHeaderCloudComponent', () => {
});
beforeEach(() => {
appConfigService = TestBed.inject(AppConfigService);
appConfigService.config = {
'adf-cloud-task-header': {
defaultDateFormat: 'full'
}
};
fixture = TestBed.createComponent(TaskHeaderCloudComponent);
component = fixture.componentInstance;
appConfigService = TestBed.inject(AppConfigService);
taskCloudService = TestBed.inject(TaskCloudService);
alfrescoApiService = TestBed.inject(AlfrescoApiService);
component.appName = 'mock-app-name';
@ -103,7 +107,7 @@ describe('TaskHeaderCloudComponent', () => {
expect(taskTitle).toBeTruthy();
});
it('should fectch task details when appName and taskId defined', async () => {
it('should fetch task details when appName and taskId defined', async () => {
fixture.detectChanges();
await fixture.whenStable();
expect(getTaskByIdSpy).toHaveBeenCalled();
@ -149,7 +153,7 @@ describe('TaskHeaderCloudComponent', () => {
fixture.detectChanges();
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe(moment(assignedTaskDetailsCloudMock.dueDate, 'x').format('MMM D, Y, H:mm'));
expect(valueEl.nativeElement.innerText.trim()).toBe('Monday, December 17, 2018 at 12:00:55 PM GMT+00:00');
});
it('should display process instance id', async () => {
@ -501,7 +505,13 @@ describe('TaskHeaderCloudComponent', () => {
describe('Config properties', () => {
it('should show only the properties from the configuration file', async () => {
spyOn(appConfigService, 'get').and.returnValue(['assignee', 'status']);
appConfigService.config = {
'adf-cloud-task-header': {
presets: {
properties: ['assignee', 'status']
}
}
};
component.ngOnChanges();
fixture.detectChanges();
const propertyList = fixture.debugElement.queryAll(By.css('.adf-property-list .adf-property'));
@ -527,6 +537,18 @@ describe('TaskHeaderCloudComponent', () => {
expect(propertyList[0].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE');
expect(propertyList[1].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.STATUS');
});
it('should format the dates based on app config format configuration', async () => {
component.ngOnInit();
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const createdDateElement = fixture.debugElement.query(By.css('[data-automation-id="header-created"] .adf-property-value'));
expect(component.dateFormat).toEqual('full');
expect(createdDateElement.nativeElement.innerText.trim()).toBe('Monday, December 17, 2018 at 12:00:55 PM GMT+00:00');
});
});
describe('Task errors', () => {

View File

@ -74,7 +74,6 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
inEdit: boolean = false;
parentTaskName: string;
dateFormat: string;
dateTimeFormat: string;
dateLocale: string;
displayDateClearAction = false;
isLoading = true;
@ -88,9 +87,8 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
private appConfig: AppConfigService,
private cardViewUpdateService: CardViewUpdateService
) {
this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat');
this.dateFormat = this.appConfig.get('adf-cloud-task-header.defaultDateFormat');
this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale');
this.dateTimeFormat = this.appConfig.get('dateValue.defaultDateTimeFormat');
}
ngOnInit() {
@ -178,7 +176,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
key: 'dueDate',
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.DUE_DATE_DEFAULT'),
editable: true,
format: this.dateTimeFormat,
format: this.dateFormat,
locale: this.dateLocale
}
),

View File

@ -49,7 +49,7 @@ export const assignedTaskDetailsCloudMock: TaskDetailsCloudModel = {
name: 'This is a new task',
description: 'This is the description ',
createdDate: new Date(1545048055900),
dueDate: new Date(),
dueDate: new Date(1545048055900),
claimedDate: null,
priority: 1,
category: null,

View File

@ -25,7 +25,7 @@ export class ProcessListCloudComponentPage {
columns = {
id: 'Id',
name: 'Name',
name: 'Process Name',
processDefinitionName: 'Process Definition Name'
};

View File

@ -23,7 +23,7 @@ import { DataTableColumnSelector } from '../../core/pages/data-table/columns-sel
const column = {
id: 'Id',
name: 'Name',
name: 'Task Name',
processInstanceId: 'ProcessInstanceId',
processDefinitionId: 'ProcessDefinitionId',
assignee: 'Assignee',