diff --git a/projects/aca-content/assets/i18n/en.json b/projects/aca-content/assets/i18n/en.json index de1acbfe5c..47896d3b34 100644 --- a/projects/aca-content/assets/i18n/en.json +++ b/projects/aca-content/assets/i18n/en.json @@ -98,7 +98,8 @@ "CREATE_TOOLTIP": "Create content", "UPLOAD": "Upload", "UPLOAD_TOOLTIP": "Upload content" - } + }, + "SELECTED": "Selected ({{ count }})" }, "BROWSE": { "FILE": { diff --git a/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.html b/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.html index c8ae1db407..f4adbbd237 100644 --- a/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.html +++ b/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.html @@ -1,6 +1,8 @@
-

{{ 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE' | translate }}

+

+ {{ (selectedRowItemsCount < 1 ? 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE' : 'APP.HEADER.SELECTED') | translate: { count: selectedRowItemsCount } }} +

@@ -19,6 +21,7 @@

{{ 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITL [sortingMode]="'client'" (node-dblclick)="handleNodeClick($event)" [imageResolver]="imageResolver" + (selectedItemsCountChanged)="onSelectedItemsCountChanged($event)" [isResizingEnabled]="true" [blurOnResize]="false" (name-click)="handleNodeClick($event)" diff --git a/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.spec.ts b/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.spec.ts index ac86c8c0b6..0d68a60ffa 100644 --- a/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.spec.ts +++ b/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.spec.ts @@ -33,6 +33,7 @@ import { RouterEffects } from '@alfresco/aca-shared/store'; import { of, throwError } from 'rxjs'; import { LibraryEffects } from '../../store/effects'; import { NodeEntry } from '@alfresco/js-api'; +import { getTitleElementText } from '../../testing/test-utils'; describe('FavoriteLibrariesComponent', () => { let fixture: ComponentFixture; @@ -99,6 +100,17 @@ describe('FavoriteLibrariesComponent', () => { expect(component.pagination).toBe(null); expect(component.isLoading).toBe(false); }); + + it('should set title based on selectedRowItemsCount', () => { + fixture.detectChanges(); + + expect(getTitleElementText(fixture)).toBe('APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE'); + + component.selectedRowItemsCount = 5; + fixture.detectChanges(); + + expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); + }); }); describe('Node navigation', () => { diff --git a/projects/aca-content/src/lib/components/favorites/favorites.component.html b/projects/aca-content/src/lib/components/favorites/favorites.component.html index c95652f541..b3f4cfc49f 100644 --- a/projects/aca-content/src/lib/components/favorites/favorites.component.html +++ b/projects/aca-content/src/lib/components/favorites/favorites.component.html @@ -1,6 +1,8 @@
-

{{ 'APP.BROWSE.FAVORITES.TITLE' | translate }}

+

+ {{ (selectedRowItemsCount < 1 ? 'APP.BROWSE.FAVORITES.TITLE' : 'APP.HEADER.SELECTED') | translate: { count: selectedRowItemsCount } }} +

@@ -17,6 +19,7 @@

{{ 'APP.BROWSE.FAVORITES.TITLE' | translate }}

[sorting]="['modifiedAt', 'desc']" [sortingMode]="'client'" [imageResolver]="imageResolver" + (selectedItemsCountChanged)="onSelectedItemsCountChanged($event)" [isResizingEnabled]="true" [blurOnResize]="false" (node-dblclick)="handleNodeClick($event)" diff --git a/projects/aca-content/src/lib/components/favorites/favorites.component.spec.ts b/projects/aca-content/src/lib/components/favorites/favorites.component.spec.ts index cf77ee29dd..36a9089412 100644 --- a/projects/aca-content/src/lib/components/favorites/favorites.component.spec.ts +++ b/projects/aca-content/src/lib/components/favorites/favorites.component.spec.ts @@ -29,6 +29,7 @@ import { BehaviorSubject, of, Subject } from 'rxjs'; import { FavoritesComponent } from './favorites.component'; import { AppTestingModule } from '../../testing/app-testing.module'; import { AppService, ContentApiService } from '@alfresco/aca-shared'; +import { getTitleElementText } from '../../testing/test-utils'; describe('FavoritesComponent', () => { let fixture: ComponentFixture; @@ -129,4 +130,13 @@ describe('FavoritesComponent', () => { location: 'favorites' }); }); + + it('should set title based on selectedRowItemsCount', () => { + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.BROWSE.FAVORITES.TITLE'); + + component.selectedRowItemsCount = 5; + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); + }); }); diff --git a/projects/aca-content/src/lib/components/files/files.component.html b/projects/aca-content/src/lib/components/files/files.component.html index d3d377852f..a314631a9a 100644 --- a/projects/aca-content/src/lib/components/files/files.component.html +++ b/projects/aca-content/src/lib/components/files/files.component.html @@ -1,6 +1,11 @@
- + +
@@ -31,6 +36,7 @@ [blurOnResize]="false" (node-dblclick)="handleNodeClick($event)" (name-click)="handleNodeClick($event)" + (selectedItemsCountChanged)="onSelectedItemsCountChanged($event)" (filterSelection)="onFilterSelected($event)" (error)="onError()" > diff --git a/projects/aca-content/src/lib/components/libraries/libraries.component.html b/projects/aca-content/src/lib/components/libraries/libraries.component.html index 6f74f05ec5..a48cdccb33 100644 --- a/projects/aca-content/src/lib/components/libraries/libraries.component.html +++ b/projects/aca-content/src/lib/components/libraries/libraries.component.html @@ -1,6 +1,8 @@
-

{{ 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE' | translate }}

+

+ {{ (selectedRowItemsCount < 1 ? 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE' : 'APP.HEADER.SELECTED') | translate: { count: selectedRowItemsCount } }} +

@@ -18,6 +20,7 @@

{{ 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE' | t [sortingMode]="'client'" [imageResolver]="imageResolver" [isResizingEnabled]="true" + (selectedItemsCountChanged)="onSelectedItemsCountChanged($event)" [blurOnResize]="false" (node-dblclick)="handleNodeClick($event)" (name-click)="handleNodeClick($event)" diff --git a/projects/aca-content/src/lib/components/libraries/libraries.component.spec.ts b/projects/aca-content/src/lib/components/libraries/libraries.component.spec.ts index 3a2f15c2bb..1c7f07d02c 100644 --- a/projects/aca-content/src/lib/components/libraries/libraries.component.spec.ts +++ b/projects/aca-content/src/lib/components/libraries/libraries.component.spec.ts @@ -30,6 +30,7 @@ import { AppTestingModule } from '../../testing/app-testing.module'; import { EffectsModule } from '@ngrx/effects'; import { LibraryEffects } from '../../store/effects'; import { ContentApiService } from '@alfresco/aca-shared'; +import { getTitleElementText } from '../../testing/test-utils'; describe('LibrariesComponent', () => { let fixture: ComponentFixture; @@ -67,6 +68,17 @@ describe('LibrariesComponent', () => { spyOn(sitesApi, 'listSiteMembershipsForPerson').and.returnValue(Promise.resolve({})); }); + describe('Initialization', () => { + it('should set title to MY_LIBRARIES.TITLE based on selectedRowItemsCount', () => { + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE'); + + component.selectedRowItemsCount = 2; + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); + }); + }); + describe('Node navigation', () => { it('does not navigate when id is not passed', () => { spyOn(router, 'navigate').and.stub(); diff --git a/projects/aca-content/src/lib/components/recent-files/recent-files.component.html b/projects/aca-content/src/lib/components/recent-files/recent-files.component.html index ef06f8ed9b..9a7854c0a5 100644 --- a/projects/aca-content/src/lib/components/recent-files/recent-files.component.html +++ b/projects/aca-content/src/lib/components/recent-files/recent-files.component.html @@ -1,6 +1,8 @@
-

{{ 'APP.BROWSE.RECENT.TITLE' | translate }}

+

+ {{ (selectedRowItemsCount < 1 ? 'APP.BROWSE.RECENT.TITLE' : 'APP.HEADER.SELECTED') | translate: { count: selectedRowItemsCount } }} +

@@ -18,6 +20,7 @@

{{ 'APP.BROWSE.RECENT.TITLE' | translate }}

[sortingMode]="'client'" [imageResolver]="imageResolver" [isResizingEnabled]="true" + (selectedItemsCountChanged)="onSelectedItemsCountChanged($event)" [blurOnResize]="false" (node-dblclick)="handleNodeClick($event)" (name-click)="handleNodeClick($event)" diff --git a/projects/aca-content/src/lib/components/recent-files/recent-files.component.spec.ts b/projects/aca-content/src/lib/components/recent-files/recent-files.component.spec.ts index 65274c1fc7..e601f22fb4 100644 --- a/projects/aca-content/src/lib/components/recent-files/recent-files.component.spec.ts +++ b/projects/aca-content/src/lib/components/recent-files/recent-files.component.spec.ts @@ -29,6 +29,7 @@ import { AppTestingModule } from '../../testing/app-testing.module'; import { Router } from '@angular/router'; import { NodePaging, SearchApi } from '@alfresco/js-api'; import { of } from 'rxjs'; +import { getTitleElementText } from '../../testing/test-utils'; describe('RecentFilesComponent', () => { let fixture: ComponentFixture; @@ -99,4 +100,13 @@ describe('RecentFilesComponent', () => { location: 'recent-files' }); }); + + it('should set title based on selectedRowItemsCount', () => { + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.BROWSE.RECENT.TITLE'); + + component.selectedRowItemsCount = 5; + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); + }); }); diff --git a/projects/aca-content/src/lib/components/shared-files/shared-files.component.html b/projects/aca-content/src/lib/components/shared-files/shared-files.component.html index f896aacd78..92756a6645 100644 --- a/projects/aca-content/src/lib/components/shared-files/shared-files.component.html +++ b/projects/aca-content/src/lib/components/shared-files/shared-files.component.html @@ -1,6 +1,9 @@
-

{{ 'APP.BROWSE.SHARED.TITLE' | translate }}

+

+ {{ (selectedRowItemsCount < 1 ? 'APP.BROWSE.SHARED.TITLE' : 'APP.HEADER.SELECTED') | translate: { count: selectedRowItemsCount } }} +

+
@@ -17,6 +20,7 @@

{{ 'APP.BROWSE.SHARED.TITLE' | translate }}

[imageResolver]="imageResolver" [sortingMode]="'client'" [isResizingEnabled]="true" + (selectedItemsCountChanged)="onSelectedItemsCountChanged($event)" [blurOnResize]="false" (node-dblclick)="handleNodeClick($event)" (name-click)="handleNodeClick($event)" diff --git a/projects/aca-content/src/lib/components/shared-files/shared-files.component.spec.ts b/projects/aca-content/src/lib/components/shared-files/shared-files.component.spec.ts index eb386b6f2c..1b108d51d8 100644 --- a/projects/aca-content/src/lib/components/shared-files/shared-files.component.spec.ts +++ b/projects/aca-content/src/lib/components/shared-files/shared-files.component.spec.ts @@ -26,15 +26,28 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { CustomResourcesService } from '@alfresco/adf-content-services'; import { SharedFilesComponent } from './shared-files.component'; import { AppTestingModule } from '../../testing/app-testing.module'; -import { Router } from '@angular/router'; import { BehaviorSubject, of, Subject } from 'rxjs'; import { By } from '@angular/platform-browser'; import { SharedLinkPaging } from '@alfresco/js-api'; import { AppService } from '@alfresco/aca-shared'; +import { getTitleElementText } from '../../testing/test-utils'; +import { ActivatedRoute, Router } from '@angular/router'; describe('SharedFilesComponent', () => { let fixture: ComponentFixture; let page: SharedLinkPaging; + let component: SharedFilesComponent; + const routerMock = { + routerState: { root: '' }, + url: 'shared-files' + }; + const route = { + snapshot: { + data: { + sortingPreferenceKey: '' + } + } + }; const appServiceMock = { appNavNarMode$: new BehaviorSubject('collapsed'), @@ -45,11 +58,10 @@ describe('SharedFilesComponent', () => { TestBed.configureTestingModule({ imports: [AppTestingModule, SharedFilesComponent], providers: [ + { provide: ActivatedRoute, useValue: route }, { provide: Router, - useValue: { - url: 'shared-files' - } + useValue: routerMock }, { provide: AppService, @@ -68,6 +80,16 @@ describe('SharedFilesComponent', () => { const customResourcesService = TestBed.inject(CustomResourcesService); spyOn(customResourcesService, 'loadSharedLinks').and.returnValue(of(page)); fixture = TestBed.createComponent(SharedFilesComponent); + component = fixture.componentInstance; + }); + + it('should set title based on selectedRowItemsCount', () => { + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.BROWSE.SHARED.TITLE'); + + component.selectedRowItemsCount = 5; + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); }); // TODO: needs better testing strategy diff --git a/projects/aca-content/src/lib/components/trashcan/trashcan.component.html b/projects/aca-content/src/lib/components/trashcan/trashcan.component.html index 5e9f75849f..50417efaf6 100644 --- a/projects/aca-content/src/lib/components/trashcan/trashcan.component.html +++ b/projects/aca-content/src/lib/components/trashcan/trashcan.component.html @@ -1,6 +1,9 @@
-

{{ 'APP.BROWSE.TRASHCAN.TITLE' | translate }}

+

+ {{ (selectedRowItemsCount < 1 ? 'APP.BROWSE.TRASHCAN.TITLE' : 'APP.HEADER.SELECTED') | translate: { count: selectedRowItemsCount } }} +

+
@@ -16,6 +19,7 @@

{{ 'APP.BROWSE.TRASHCAN.TITLE' | translate }}

[navigate]="false" [sortingMode]="'client'" [imageResolver]="imageResolver" + (selectedItemsCountChanged)="onSelectedItemsCountChanged($event)" [sorting]="['archivedAt', 'desc']" [isResizingEnabled]="true" [blurOnResize]="false" diff --git a/projects/aca-content/src/lib/components/trashcan/trashcan.component.spec.ts b/projects/aca-content/src/lib/components/trashcan/trashcan.component.spec.ts index d7209c19a8..a1b15b9846 100644 --- a/projects/aca-content/src/lib/components/trashcan/trashcan.component.spec.ts +++ b/projects/aca-content/src/lib/components/trashcan/trashcan.component.spec.ts @@ -26,6 +26,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { AlfrescoApiService } from '@alfresco/adf-core'; import { TrashcanComponent } from './trashcan.component'; import { AppTestingModule } from '../../testing/app-testing.module'; +import { getTitleElementText } from '../../testing/test-utils'; describe('TrashcanComponent', () => { let fixture: ComponentFixture; @@ -54,4 +55,13 @@ describe('TrashcanComponent', () => { await fixture.whenStable(); expect(fixture.nativeElement.querySelector('adf-document-list')).not.toBeNull(); }); + + it('should set title based on selectedRowItemsCount', () => { + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.BROWSE.TRASHCAN.TITLE'); + + component.selectedRowItemsCount = 5; + fixture.detectChanges(); + expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); + }); }); diff --git a/projects/aca-content/src/lib/testing/test-utils.ts b/projects/aca-content/src/lib/testing/test-utils.ts new file mode 100644 index 0000000000..9dcb913e1b --- /dev/null +++ b/projects/aca-content/src/lib/testing/test-utils.ts @@ -0,0 +1,29 @@ +/*! + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Alfresco Example Content Application + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * from Hyland Software. If not, see . + */ + +import { ComponentFixture } from '@angular/core/testing'; + +export const getTitleElementText = (fixture: ComponentFixture): string => { + return fixture.nativeElement.querySelector('.aca-page-title').innerText.trim(); +}; diff --git a/projects/aca-content/src/lib/ui/theme.scss b/projects/aca-content/src/lib/ui/theme.scss index 0600543ee9..b3d6b477f1 100644 --- a/projects/aca-content/src/lib/ui/theme.scss +++ b/projects/aca-content/src/lib/ui/theme.scss @@ -49,6 +49,44 @@ mat-toolbar.mat-toolbar.mat-toolbar-single-row { } } +.mat-checkbox-indeterminate, +.mat-checkbox-checked { + &.mat-accent { + .mat-checkbox-background { + background-color: var(--theme-blue-checkbox-color); + } + } +} + +.adf-datatable-list { + .adf-datatable-row:is(adf-datatable-row) { + &:focus { + outline: 1px solid var(--theme-blue-checkbox-color); + } + + .adf-cell-value:focus { + outline: 1px solid var(--theme-blue-checkbox-color); + } + + .adf-datatable-cell-header:focus { + outline: 1px solid var(--theme-blue-checkbox-color); + } + + .adf-datatable-link:hover { + color: var(--theme-blue-checkbox-color); + } + } + + .adf-datatable-body { + .adf-datatable-row:is(adf-datatable-row) { + &.adf-is-selected, + &.adf-is-selected:hover { + background-color: var(--theme-blue-active-table-row-color); + } + } + } +} + .adf-property-field { .adf-textitem-edit-icon.mat-icon { color: var(--theme-secondary-text); diff --git a/projects/aca-content/src/lib/ui/variables/variables.scss b/projects/aca-content/src/lib/ui/variables/variables.scss index aa2e77f543..800e472803 100644 --- a/projects/aca-content/src/lib/ui/variables/variables.scss +++ b/projects/aca-content/src/lib/ui/variables/variables.scss @@ -25,6 +25,8 @@ $grey-background: rgba(33, 33, 33, 0.12); $grey-text-background: rgba(33, 33, 33, 0.05); $grey-hover-background: rgba(33, 33, 33, 0.24); $blue-save-button-background: #1f74db; +$blue-checkbox-background: rgb(10,96,206); +$blue-active-table-row: rgb(10,96,206, 0.24); $black-heading: #4e4c4c; $theme-dropdown-background: darken($background-color, 5%); $theme-dropdown-background-hover: darken($background-color, 10%); @@ -67,6 +69,8 @@ $defaults: ( --theme-grey-background-color: $grey-background, --theme-grey-hover-background-color: $grey-hover-background, --theme-blue-button-color: $blue-save-button-background, + --theme-blue-checkbox-color: $blue-checkbox-background, + --theme-blue-active-table-row-color: $blue-active-table-row, --theme-heading-color: $black-heading, --theme-dropdown-color: $theme-dropdown-background, --theme-dropdown-background-hover: $theme-dropdown-background-hover, diff --git a/projects/aca-shared/src/lib/components/document-base-page/document-base-page.component.ts b/projects/aca-shared/src/lib/components/document-base-page/document-base-page.component.ts index fe17f45c29..61b438431a 100644 --- a/projects/aca-shared/src/lib/components/document-base-page/document-base-page.component.ts +++ b/projects/aca-shared/src/lib/components/document-base-page/document-base-page.component.ts @@ -70,6 +70,7 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges { filterSorting = 'name-asc'; createActions: Array = []; isSmallScreen = false; + selectedRowItemsCount = 0; protected extensions = inject(AppExtensionService); protected content = inject(DocumentBasePageService); @@ -162,6 +163,10 @@ export abstract class PageComponent implements OnInit, OnDestroy, OnChanges { } } + onSelectedItemsCountChanged(count: number) { + this.selectedRowItemsCount = count; + } + getParentNodeId(): string { return this.node ? this.node.id : null; }