diff --git a/projects/aca-content/assets/app.extensions.json b/projects/aca-content/assets/app.extensions.json index e8aa3798e8..ba5eefd0aa 100644 --- a/projects/aca-content/assets/app.extensions.json +++ b/projects/aca-content/assets/app.extensions.json @@ -175,6 +175,13 @@ "description": "APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.SIDENAV_LINK.TOOLTIP", "route": "favorite/libraries" }, + { + "id": "app.navbar.libraries.all", + "order": 350, + "title": "APP.BROWSE.LIBRARIES.MENU.ALL_LIBRARIES.SIDENAV_LINK.LABEL", + "description": "APP.BROWSE.LIBRARIES.MENU.ALL_LIBRARIES.SIDENAV_LINK.TOOLTIP", + "route": "all/libraries" + }, { "id": "app.navbar.recentFiles", "order": 400, @@ -504,8 +511,7 @@ "component": "app.toolbar.toggleInfoDrawer", "rules": { "visible": [ - "app.selection.library", - "isLibraryManager" + "app.selection.library" ] } }, @@ -1583,8 +1589,7 @@ "component": "app.components.tabs.library.metadata", "rules": { "visible": [ - "app.selection.library", - "isLibraryManager" + "app.selection.library" ] } } diff --git a/projects/aca-content/assets/i18n/ar.json b/projects/aca-content/assets/i18n/ar.json index df49bde265..9b73e25950 100644 --- a/projects/aca-content/assets/i18n/ar.json +++ b/projects/aca-content/assets/i18n/ar.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "لا توجد مكتبات مفضلة", "TEXT": "حدد مكتبة تريد العثور عليها بسهولة لاحقًا كمفضلة." + }, + "ALL_LIBRARIES":{ + "TITLE": "لم يتم العثور على مكتبة", + "TEXT": "أنشئ مكتبة جديدة للبدء" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "المكتبات المفضلة", "TOOLTIP": "الوصول إلى مكتباتي المفضلة" } + }, + "ALL_LIBRARIES": { + "TITLE": "كل المكتبات", + "SIDENAV_LINK": { + "LABEL": "كل المكتبات" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/cs.json b/projects/aca-content/assets/i18n/cs.json index 4c2444c6ba..c1849911e2 100644 --- a/projects/aca-content/assets/i18n/cs.json +++ b/projects/aca-content/assets/i18n/cs.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Žádné oblíbené knihovny souborů", "TEXT": "Pro snadnější dostupnost můžete označit vybrané knihovny za oblíbené." + }, + "ALL_LIBRARIES": { + "TITLE": "Nebyla nalezena žádná knihovna", + "TEXT": "Začněte vytvořením nové knihovny" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Oblíbené knihovny", "TOOLTIP": "Přejít do oblíbených knihoven" } + }, + "ALL_LIBRARIES": { + "TITLE": "Všechny knihovny", + "SIDENAV_LINK": { + "LABEL": "Všechny knihovny" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/da.json b/projects/aca-content/assets/i18n/da.json index aea914d17e..33cdd6243a 100644 --- a/projects/aca-content/assets/i18n/da.json +++ b/projects/aca-content/assets/i18n/da.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Du har ikke angivet nogen biblioteker som favorit", "TEXT": "Angiv et bibliotek som favorit for at gøre det nemt at finde biblioteket igen." + }, + "ALL_LIBRARIES": { + "TITLE": "Intet bibliotek fundet", + "TEXT": "Opret et ny bibliotek, og kom i gang" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Biblioteker, der er angivet som favorit", "TOOLTIP": "Adgang til de biblioteker, jeg har angivet som favorit" } + }, + "ALL_LIBRARIES": { + "TITLE": "Alle biblioteker", + "SIDENAV_LINK": { + "LABEL": "Alle biblioteker" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/de.json b/projects/aca-content/assets/i18n/de.json index e079936166..d26e5eb64c 100644 --- a/projects/aca-content/assets/i18n/de.json +++ b/projects/aca-content/assets/i18n/de.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Keine Bibliotheken als Favoriten markiert", "TEXT": "Markieren Sie eine Bibliothek als Favorit, um sie später leichter zu finden." + }, + "ALL_LIBRARIES": { + "TITLE": "Keine Bibliothek gefunden", + "TEXT": "Erstellen Sie zu Beginn eine neue Bibliothek." } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Als Favoriten markierte Bibliotheken", "TOOLTIP": "Auf meine als Favoriten markierten Bibliotheken zugreifen" } + }, + "ALL_LIBRARIES": { + "TITLE": "Alle Bibliotheken", + "SIDENAV_LINK": { + "LABEL": "Alle Bibliotheken" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/en.json b/projects/aca-content/assets/i18n/en.json index c5353b36c9..83c1c87857 100644 --- a/projects/aca-content/assets/i18n/en.json +++ b/projects/aca-content/assets/i18n/en.json @@ -135,6 +135,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "No Favorite Libraries", "TEXT": "Favorite a library that you want to find easily later." + }, + "ALL_LIBRARIES": { + "TITLE": "No library found", + "TEXT": "Create a new library to get started." } }, "MENU": { @@ -151,6 +155,13 @@ "LABEL": "Favorite Libraries", "TOOLTIP": "Access my favorite libraries" } + }, + "ALL_LIBRARIES": { + "TITLE": "All Libraries", + "SIDENAV_LINK": { + "LABEL": "All Libraries", + "TOOLTIP": "Access all libraries" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/es.json b/projects/aca-content/assets/i18n/es.json index 2c4cd81583..dd23bacad2 100644 --- a/projects/aca-content/assets/i18n/es.json +++ b/projects/aca-content/assets/i18n/es.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "No hay bibliotecas favoritas", "TEXT": "Marcar como favorita una biblioteca para encontrarla más rápidamente." + }, + "ALL_LIBRARIES": { + "TITLE": "No se ha encontrado ninguna biblioteca", + "TEXT ": "Crear una biblioteca nueva para empezar" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Bibliotecas favoritas", "TOOLTIP": "Acceder a mis bibliotecas favoritas" } + }, + "ALL_LIBRARIES": { + "TITLE": "Todas las bibliotecas", + "SIDENAV_LINK": { + "LABEL": "Todas las bibliotecas" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/fi.json b/projects/aca-content/assets/i18n/fi.json index c71a837089..366468b102 100644 --- a/projects/aca-content/assets/i18n/fi.json +++ b/projects/aca-content/assets/i18n/fi.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Ei suosikkikirjastoja", "TEXT": "Voit lisätä suosikkeihin kirjastoja, jotka haluat löytää helposti myöhemmin." + }, + "ALL_LIBRARIES": { + "TITLE": "Yhtään kirjastoa ei löytynyt", + "TEXT": "Luo uusi kirjasto aloittaaksesi" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Suosikkikirjastot", "TOOLTIP": "Siirry omiin suosikkikirjastoihin" } + }, + "ALL_LIBRARIES": { + "TITLE": "Kaikki kirjastot", + "SIDENAV_LINK": { + "LABEL": "Kaikki kirjastot" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/fr.json b/projects/aca-content/assets/i18n/fr.json index e8b9ea46d1..b78a23081b 100644 --- a/projects/aca-content/assets/i18n/fr.json +++ b/projects/aca-content/assets/i18n/fr.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Pas de bibliothèques favorites", "TEXT": "Ajouter aux favoris une bibliothèque pour la retrouver facilement par la suite." + }, + "ALL_LIBRARIES": { + "TITLE": "Aucune bibliothèque trouvée", + "TEXT": "Créez une nouvelle bibliothèque pour commencer" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Bibliothèques favorites", "TOOLTIP": "Accéder à mes bibliothèques favorites" } + }, + "ALL_LIBRARIES": { + "TITLE": "Toutes les bibliothèques", + "SIDENAV_LINK": { + "LABEL": "Toutes les bibliothèques" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/it.json b/projects/aca-content/assets/i18n/it.json index 3d25f5bc73..571d28d83f 100644 --- a/projects/aca-content/assets/i18n/it.json +++ b/projects/aca-content/assets/i18n/it.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Nessuna libreria preferita", "TEXT": "Imposta una libreria come preferita per trovarla facilmente in seguito." + }, + "ALL_LIBRARIES": { + "TITLE": "Nessuna libreria trovata", + "TEXT": "Crea una nuova libreria per iniziare" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Librerie preferite", "TOOLTIP": "Accedi alle librerie preferite" } + }, + "ALL_LIBRARIES": { + "TITLE": "Tutte le librerie", + "SIDENAV_LINK": { + "LABEL": "Tutte le librerie" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/ja.json b/projects/aca-content/assets/i18n/ja.json index 073ff80be7..e065bb5f15 100644 --- a/projects/aca-content/assets/i18n/ja.json +++ b/projects/aca-content/assets/i18n/ja.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "お気に入りのライブラリはありません", "TEXT": "後で簡単に見つけられるようにライブラリをお気に入りに追加します。" + }, + "ALL_LIBRARIES": { + "TITLE": "ライブラリが見つかりません", + "TEXT": "最初に新しいライブラリを作成してください" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "お気に入りのライブラリ", "TOOLTIP": "お気に入りのライブラリにアクセス" } + }, + "ALL_LIBRARIES": { + "TITLE": "すべてのライブラリ", + "SIDENAV_LINK": { + "LABEL": "すべてのライブラリ" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/nb.json b/projects/aca-content/assets/i18n/nb.json index 24f5fb0e7b..ce867053ea 100644 --- a/projects/aca-content/assets/i18n/nb.json +++ b/projects/aca-content/assets/i18n/nb.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Ingen favorittbiblioteker", "TEXT": "Legg et bibliotek til som favoritt, slik at du lett kan finne det senere." + }, + "ALL_LIBRARIES": { + "TITLE": "Fant ingen biblioteker", + "TEXT": "Opprett et nytt bibliotek for å komme i gang" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Favorittbiblioteker", "TOOLTIP": "Gå inn i mine favorittbiblioteker" } + }, + "ALL_LIBRARIES": { + "TITLE": "Alle biblioteker", + "SIDENAV_LINK": { + "LABEL": "Alle biblioteker" + } } } }, diff --git a/projects/aca-content/assets/i18n/nl.json b/projects/aca-content/assets/i18n/nl.json index 0888c7a167..2407d244bd 100644 --- a/projects/aca-content/assets/i18n/nl.json +++ b/projects/aca-content/assets/i18n/nl.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Geen favoriete bibliotheken", "TEXT": "Voeg een bibliotheek toe aan uw favorieten zodat u deze later gemakkelijk kunt vinden." + }, + "ALL_LIBRARIES": { + "TITLE": "Geen bibliotheek gevonden", + "TEXT": "Maak een nieuwe bibliotheek om aan de slag te gaan" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Favoriete bibliotheken", "TOOLTIP": "Toegang krijgen tot mijn favoriete bibliotheken" } + }, + "ALL_LIBRARIES": { + "TITLE": "Alle bibliotheken", + "SIDENAV_LINK": { + "LABEL": "Alle bibliotheken" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/no.json b/projects/aca-content/assets/i18n/no.json index 1bcf46ff37..e987e6f7af 100644 --- a/projects/aca-content/assets/i18n/no.json +++ b/projects/aca-content/assets/i18n/no.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Ingen favorittbiblioteker", "TEXT": "Merk et bibliotek som favoritt for å enkelt finne det senere." + }, + "ALL_LIBRARIES": { + "TITLE": "Fant ingen bibliotek", + "TEXT": "Opprett et nytt bibliotek for å komme i gang" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Favorittbiblioteker", "TOOLTIP": "Få tilgang til mine favorittbiblioteker" } + }, + "ALL_LIBRARIES": { + "TITLE": "Alle biblioteker", + "SIDENAV_LINK": { + "LABEL": "Alle biblioteker" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/pl.json b/projects/aca-content/assets/i18n/pl.json index ea9f33b948..f0dcc388c8 100644 --- a/projects/aca-content/assets/i18n/pl.json +++ b/projects/aca-content/assets/i18n/pl.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Brak ulubionych bibliotek", "TEXT": "Dodaj do ulubionych bibliotekę, którą później chcesz łatwo znaleźć." + }, + "ALL_LIBRARIES": { + "TITLE": "Nie znaleziono biblioteki", + "TEXT": "Utwórz nową bibliotekę, aby rozpocząć" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Ulubione biblioteki", "TOOLTIP": "Uzyskaj dostęp do moich ulubionych bibliotek" } + }, + "ALL_LIBRARIES": { + "TITLE": "Wszystkie biblioteki", + "SIDENAV_LINK": { + "LABEL": "Wszystkie biblioteki" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/pt-BR.json b/projects/aca-content/assets/i18n/pt-BR.json index 51b13c8e27..2da5efbed7 100644 --- a/projects/aca-content/assets/i18n/pt-BR.json +++ b/projects/aca-content/assets/i18n/pt-BR.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Nenhuma biblioteca favorita", "TEXT": "Adicione uma biblioteca como favorita para facilitar o acesso a ela." + }, + "ALL_LIBRARIES": { + "TITLE": "Nenhuma biblioteca encontrada", + "TEXT": "Crie uma nova biblioteca para iniciar" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Bibliotecas favoritas", "TOOLTIP": "Acessar minhas bibliotecas favoritas" } + }, + "ALL_LIBRARIES": { + "TITLE": "Todas as bibliotecas", + "SIDENAV_LINK": { + "LABEL": "Todas as bibliotecas" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/ru.json b/projects/aca-content/assets/i18n/ru.json index b90599efb4..05cb887388 100644 --- a/projects/aca-content/assets/i18n/ru.json +++ b/projects/aca-content/assets/i18n/ru.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Нет избранных библиотек", "TEXT": "Добавляйте библиотеки в избранные, чтобы быстро находить их, когда они понадобятся снова." + }, + "ALL_LIBRARIES": { + "TITLE": "Библиотеки не найдены", + "TEXT": "Создайте новую библиотеку, чтобы приступить к работе" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Избранные библиотеки", "TOOLTIP": "Доступ к моим избранным библиотекам" } + }, + "ALL_LIBRARIES": { + "TITLE": "Все библиотеки", + "SIDENAV_LINK": { + "LABEL": "Все библиотеки" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/sv.json b/projects/aca-content/assets/i18n/sv.json index 6eb4bae178..540e68a808 100644 --- a/projects/aca-content/assets/i18n/sv.json +++ b/projects/aca-content/assets/i18n/sv.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "Inga favoritbibliotek", "TEXT": "Favoritmarkera ett bibliotek som du enkelt vill hitta senare." + }, + "ALL_LIBRARIES": { + "TITLE": "Inget bibliotek hittades", + "TEXT": "Skapa ett nytt bibliotek för att komma igång" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "Favoritbibliotek", "TOOLTIP": "Kom åt mina favoritbibliotek" } + }, + "ALL_LIBRARIES": { + "TITLE": "Alla bibliotek", + "SIDENAV_LINK": { + "LABEL": "Alla bibliotek" + } } }, "ERRORS": { diff --git a/projects/aca-content/assets/i18n/zh-CN.json b/projects/aca-content/assets/i18n/zh-CN.json index 84e491eeba..b88292b8f3 100644 --- a/projects/aca-content/assets/i18n/zh-CN.json +++ b/projects/aca-content/assets/i18n/zh-CN.json @@ -133,6 +133,10 @@ "FAVORITE_LIBRARIES": { "TITLE": "无收藏库", "TEXT": "收藏您希望以后轻松查找的库" + }, + "ALL_LIBRARIES": { + "TITLE": "未发现任何库", + "TEXT": "创建新库以开始使用" } }, "MENU": { @@ -149,6 +153,12 @@ "LABEL": "收藏库", "TOOLTIP": "访问我的收藏库" } + }, + "ALL_LIBRARIES": { + "TITLE": "所有库", + "SIDENAV_LINK": { + "LABEL": "所有库" + } } }, "ERRORS": { diff --git a/projects/aca-content/src/lib/aca-content.routes.ts b/projects/aca-content/src/lib/aca-content.routes.ts index bbc4f25ac4..482286e986 100644 --- a/projects/aca-content/src/lib/aca-content.routes.ts +++ b/projects/aca-content/src/lib/aca-content.routes.ts @@ -42,6 +42,7 @@ import { TrashcanComponent } from './components/trashcan/trashcan.component'; import { ShellLayoutComponent } from '@alfresco/adf-core/shell'; import { SearchAiResultsComponent } from './components/knowledge-retrieval/search-ai/search-ai-results/search-ai-results.component'; import { SavedSearchesSmartListComponent } from './components/search/search-save/list/smart-list/saved-searches-smart-list.component'; +import { LibraryListComponent } from './components/library-list/library-list.component'; export const CONTENT_ROUTES: ExtensionRoute[] = [ { @@ -230,6 +231,24 @@ export const CONTENT_LAYOUT_ROUTES: Route[] = [ ...createViewRoutes('libraries') ] }, + { + path: 'all', + children: [ + { + path: '', + pathMatch: 'full', + redirectTo: 'libraries' + }, + { + path: 'libraries', + component: LibraryListComponent, + data: { + title: 'APP.BROWSE.LIBRARIES.MENU.ALL_LIBRARIES.TITLE', + sortingPreferenceKey: 'all-libraries' + } + } + ] + }, { path: 'favorites', data: { 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 fcf74cab88..693f72c70a 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,94 +1,14 @@ - -
-

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

- -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
-
-
+ 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 47d211d296..45e415b643 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 @@ -23,189 +23,129 @@ */ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Router } from '@angular/router'; import { UserPreferencesService } from '@alfresco/adf-core'; -import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { FavoriteLibrariesComponent } from './favorite-libraries.component'; import { AppTestingModule } from '../../testing/app-testing.module'; -import { AppHookService, ContentApiService } from '@alfresco/aca-shared'; +import { AppExtensionService, AppHookService, ContentApiService } from '@alfresco/aca-shared'; import { provideEffects } from '@ngrx/effects'; -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'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { SiteEntry } from '@alfresco/js-api/typings'; +import { libraryColumnsPresetMock, favoriteLibrariesMock, libraryPaginationMock } from '../../mock/libraries-mock'; describe('FavoriteLibrariesComponent', () => { let fixture: ComponentFixture; let component: FavoriteLibrariesComponent; - let alfrescoApi: AlfrescoApiService; let userPreference: UserPreferencesService; let contentApiService: ContentApiService; - let router: Router; - let page; let appHookService: AppHookService; - - beforeEach(() => { - page = { - list: { - entries: [{ entry: { id: 1 } }, { entry: { id: 2 } }], - pagination: { data: 'data' } - } - }; - }); + let appExtensionService: AppExtensionService; beforeEach(() => { TestBed.configureTestingModule({ imports: [AppTestingModule, FavoriteLibrariesComponent, MatSnackBarModule], - providers: [provideEffects([RouterEffects, LibraryEffects])] + providers: [provideEffects([LibraryEffects])] }); fixture = TestBed.createComponent(FavoriteLibrariesComponent); component = fixture.componentInstance; - alfrescoApi = TestBed.inject(AlfrescoApiService); contentApiService = TestBed.inject(ContentApiService); userPreference = TestBed.inject(UserPreferencesService); appHookService = TestBed.inject(AppHookService); - alfrescoApi.reset(); - router = TestBed.inject(Router); + appExtensionService = TestBed.inject(AppExtensionService); spyOn(contentApiService, 'getNode').and.returnValue(of({ entry: { id: 'libraryId' } } as NodeEntry)); }); - describe('on initialization', () => { - it('should set data', () => { - spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(page)); - fixture.detectChanges(); - - expect(component.list).toBe(page); - expect(component.pagination).toBe(page.list.pagination); - }); - - it('should get data with user preference pagination size', () => { - userPreference.paginationSize = 1; - spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(page)); + it('should set data', () => { + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(favoriteLibrariesMock)); + fixture.detectChanges(); - fixture.detectChanges(); + expect(component.list).toBe(favoriteLibrariesMock); + expect(component.pagination).toBe(favoriteLibrariesMock.list.pagination); + }); - expect(contentApiService.getFavoriteLibraries).toHaveBeenCalledWith('-me-', { - maxItems: userPreference.paginationSize - }); - }); + it('should get data with user preference pagination size', () => { + userPreference.paginationSize = 1; + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(favoriteLibrariesMock)); - it('should set data on error', () => { - spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(throwError('error')); - fixture.detectChanges(); + fixture.detectChanges(); - expect(component.list).toBe(null); - expect(component.pagination).toBe(null); - expect(component.isLoading).toBe(false); + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalledWith('-me-', { + maxItems: userPreference.paginationSize }); + }); - it('should set title based on selectedRowItemsCount', () => { - fixture.detectChanges(); - - expect(getTitleElementText(fixture)).toBe('APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE'); + it('should set data on error', () => { + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(throwError(() => 'error')); + fixture.detectChanges(); - component.selectedRowItemsCount = 5; - fixture.detectChanges(); - - expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); - }); + expect(component.list).toBe(null); + expect(component.pagination).toBe(null); + expect(component.isLoading).toBe(false); }); - describe('Node navigation', () => { - it('does not navigate when id is not passed', () => { - spyOn(router, 'navigate').and.stub(); - component.navigateTo(null); - - expect(router.navigate).not.toHaveBeenCalled(); - }); + it('should set columns from extensions on init', () => { + appExtensionService.documentListPresets.libraries = libraryColumnsPresetMock; + fixture.detectChanges(); + expect(component.columns).toEqual(appExtensionService.documentListPresets.favoriteLibraries); + }); - it('does not navigate when id is not passed', () => { - spyOn(router, 'navigate').and.stub(); - component.navigateTo({ - entry: { - guid: 'test-guid', - visibility: 'PUBLIC', - role: 'SiteConsumer' - } - } as SiteEntry); - - expect(router.navigate).toHaveBeenCalledWith(['favorite/libraries', 'libraryId']); - }); + it('should handle no columns preset in extensions', () => { + appExtensionService.documentListPresets.favoriteLibraries = undefined; + component.ngOnInit(); + expect(component.columns.length).toBe(0); }); - describe('Reload on actions', () => { + describe('Library hooks', () => { beforeEach(() => { - spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(page)); + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(favoriteLibrariesMock)); fixture.detectChanges(); }); - it('should reload on libraryDeleted action', () => { + it('should reload on libraryDeleted hook', () => { appHookService.libraryDeleted.next(''); expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); }); - it('should reload on libraryUpdated action', () => { + it('should reload on libraryUpdated hook', () => { appHookService.libraryUpdated.next({} as any); expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); }); - it('should reload on favoriteLibraryToggle action', () => { + it('should reload on favoriteLibraryToggle hook', () => { appHookService.favoriteLibraryToggle.next(); expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); }); - it('should reload on libraryJoined action', () => { + it('should reload on libraryJoined hook', () => { appHookService.libraryJoined.next(); expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); }); - it('should reload on libraryLeft action', () => { + it('should reload on libraryLeft hook', () => { appHookService.libraryLeft.next({} as any); expect(contentApiService.getFavoriteLibraries).toHaveBeenCalled(); }); }); describe('Pagination', () => { - let pagination; - - beforeEach(() => { - pagination = { - count: 100, - hasMoreItems: true, - totalItems: 300, - skipCount: 25, - maxItems: 25 - }; - }); - - it('should get list with pagination data onChange event', () => { - spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(page)); - - component.onChange(pagination); - - expect(contentApiService.getFavoriteLibraries).toHaveBeenCalledWith('-me-', pagination); - }); - it('should get list with pagination data onChangePageSize event', () => { - spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(page)); + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(favoriteLibrariesMock)); - component.onChangePageSize(pagination); + component.onChangePageSize(libraryPaginationMock); - expect(contentApiService.getFavoriteLibraries).toHaveBeenCalledWith('-me-', pagination); + expect(contentApiService.getFavoriteLibraries).toHaveBeenCalledWith('-me-', libraryPaginationMock); }); it('should set preference page size onChangePageSize event', () => { - spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(page)); + spyOn(contentApiService, 'getFavoriteLibraries').and.returnValue(of(favoriteLibrariesMock)); - component.onChangePageSize(pagination); + component.onChangePageSize(libraryPaginationMock); - expect(userPreference.paginationSize).toBe(pagination.maxItems); + expect(userPreference.paginationSize).toBe(libraryPaginationMock.maxItems); }); }); }); diff --git a/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.ts b/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.ts index 250547a2c9..0b1027f64b 100644 --- a/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.ts +++ b/projects/aca-content/src/lib/components/favorite-libraries/favorite-libraries.component.ts @@ -23,63 +23,30 @@ */ import { ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core'; -import { FavoritePaging, Pagination, SiteEntry } from '@alfresco/js-api'; -import { - AppHookService, - ContentApiService, - ContextActionsDirective, - InfoDrawerComponent, - PageComponent, - PageLayoutComponent, - ToolbarComponent -} from '@alfresco/aca-shared'; -import { NavigateLibraryAction } from '@alfresco/aca-shared/store'; -import { - CustomEmptyContentTemplateDirective, - DataColumnComponent, - DataColumnListComponent, - EmptyContentComponent, - PaginationComponent, - UserPreferencesService -} from '@alfresco/adf-core'; -import { DocumentListPresetRef, DynamicColumnComponent } from '@alfresco/adf-extensions'; -import { CommonModule } from '@angular/common'; -import { DocumentListDirective } from '../../directives/document-list.directive'; -import { TranslatePipe } from '@ngx-translate/core'; -import { DocumentListComponent } from '@alfresco/adf-content-services'; +import { FavoritePaging, Pagination } from '@alfresco/js-api'; +import { ContentApiService } from '@alfresco/aca-shared'; +import { UserPreferencesService } from '@alfresco/adf-core'; +import { DocumentListPresetRef } from '@alfresco/adf-extensions'; +import { LibrariesBaseComponent } from '../libraries-base/libraries-base.component'; @Component({ - imports: [ - CommonModule, - DocumentListDirective, - ContextActionsDirective, - PaginationComponent, - InfoDrawerComponent, - PageLayoutComponent, - TranslatePipe, - ToolbarComponent, - EmptyContentComponent, - DynamicColumnComponent, - DataColumnListComponent, - DataColumnComponent, - DocumentListComponent, - CustomEmptyContentTemplateDirective - ], + selector: 'aca-favorite-libraries', + standalone: true, templateUrl: './favorite-libraries.component.html', + imports: [LibrariesBaseComponent], encapsulation: ViewEncapsulation.None }) -export class FavoriteLibrariesComponent extends PageComponent implements OnInit { +export class FavoriteLibrariesComponent extends LibrariesBaseComponent implements OnInit { pagination: Pagination = new Pagination({ skipCount: 0, maxItems: 25, totalItems: 0 }); isLoading = false; - list: FavoritePaging; + list: FavoritePaging = null; columns: DocumentListPresetRef[] = []; constructor( - private appHookService: AppHookService, private contentApiService: ContentApiService, private preferences: UserPreferencesService, private changeDetectorRef: ChangeDetectorRef @@ -92,36 +59,22 @@ export class FavoriteLibrariesComponent extends PageComponent implements OnInit this.getList({ maxItems: this.preferences.paginationSize }); - this.subscriptions = this.subscriptions.concat([ + this.subscriptions.push( this.appHookService.libraryDeleted.subscribe(() => this.reloadList()), this.appHookService.libraryUpdated.subscribe(() => this.reloadList()), this.appHookService.libraryJoined.subscribe(() => this.reloadList()), this.appHookService.libraryLeft.subscribe(() => this.reloadList()), this.appHookService.favoriteLibraryToggle.subscribe(() => this.reloadList()) - ]); + ); this.columns = this.extensions.documentListPresets.favoriteLibraries || []; } - navigateTo(node: SiteEntry) { - if (node?.entry?.guid) { - this.store.dispatch(new NavigateLibraryAction(node.entry, 'favorite/libraries')); - } - } - - handleNodeClick(event: Event) { - this.navigateTo((event as CustomEvent).detail?.node); - } - onChangePageSize(pagination: Pagination) { this.preferences.paginationSize = pagination.maxItems; this.getList(pagination); } - onChange(pagination: Pagination) { - this.getList(pagination); - } - - private getList(pagination: Pagination) { + getList(pagination: Pagination) { this.isLoading = true; this.contentApiService.getFavoriteLibraries('-me-', pagination).subscribe( (favoriteLibraries: FavoritePaging) => { diff --git a/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.html b/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.html new file mode 100644 index 0000000000..efa6c8ed89 --- /dev/null +++ b/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.html @@ -0,0 +1,96 @@ + +
+

+ @if (selectedRowItemsCount < 1) { + {{ titleKey | translate: { count: selectedRowItemsCount } }} + } @else { + {{ 'APP.HEADER.SELECTED' | translate: { count: selectedRowItemsCount } }} + } +

+ +
+ +
+
+ + + + + + + @for (column of columns; track trackByColumnId($index, column)) { + @if (column.template && !(column.desktopOnly && isSmallScreen)) { + + + + + + } @else if (!column.template && !(column.desktopOnly && isSmallScreen)) { + + } + } + + + + +
+ + @if (infoDrawerOpened$ | async) { + + } +
+
diff --git a/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.spec.ts b/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.spec.ts new file mode 100644 index 0000000000..b748604a39 --- /dev/null +++ b/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.spec.ts @@ -0,0 +1,145 @@ +/*! + * Copyright © 2005-2025 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, TestBed } from '@angular/core/testing'; +import { LibrariesBaseComponent } from './libraries-base.component'; +import { AppTestingModule } from '../../testing/app-testing.module'; +import { CustomEmptyContentTemplateDirective, PaginationComponent, UnitTestingUtils } from '@alfresco/adf-core'; +import { DocumentListComponent } from '@alfresco/adf-content-services'; +import { provideEffects } from '@ngrx/effects'; +import { NavigateLibraryAction, RouterEffects } from '@alfresco/aca-shared/store'; +import { LibraryEffects } from '../../store/effects'; +import { getTitleElementText } from '../../testing/test-utils'; +import { librariesMock, libraryPaginationMock } from '../../mock/libraries-mock'; +import { DebugElement } from '@angular/core'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { initialState } from '@alfresco/aca-shared'; + +describe('LibrariesBaseComponent', () => { + let component: LibrariesBaseComponent; + let fixture: ComponentFixture; + let unitTestingUtils: UnitTestingUtils; + let store: MockStore; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppTestingModule, LibrariesBaseComponent], + providers: [provideEffects([RouterEffects, LibraryEffects]), provideMockStore({ initialState })] + }).compileComponents(); + fixture = TestBed.createComponent(LibrariesBaseComponent); + component = fixture.componentInstance; + + unitTestingUtils = new UnitTestingUtils(fixture.debugElement); + store = TestBed.inject(MockStore); + + component.list = librariesMock; + fixture.detectChanges(); + }); + + it('should set title based on selectedRowItemsCount', () => { + component.titleKey = 'TITLE_TRANSLATION_KEY'; + fixture.detectChanges(); + + expect(getTitleElementText(fixture)).toBe('TITLE_TRANSLATION_KEY'); + + component.selectedRowItemsCount = 5; + fixture.detectChanges(); + + expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); + }); + + it('should show empty state when list is empty', () => { + component.list = null; + component.emptyTitleKey = 'EMPTY_TITLE'; + component.emptySubtitleKey = 'EMPTY_SUBTITLE'; + fixture.detectChanges(); + const emptyContent = unitTestingUtils.getByDirective(CustomEmptyContentTemplateDirective); + + expect(emptyContent.nativeElement.textContent).toContain('EMPTY_TITLE'); + expect(emptyContent.nativeElement.textContent).toContain('EMPTY_SUBTITLE'); + }); + + describe('Pagination events', () => { + let paginationComponent: DebugElement; + + beforeEach(() => { + paginationComponent = unitTestingUtils.getByDirective(PaginationComponent); + }); + + it('should emit changePageSize event', () => { + spyOn(component.changePageSize, 'emit'); + paginationComponent.triggerEventHandler('changePageSize', libraryPaginationMock); + expect(component.changePageSize.emit).toHaveBeenCalledWith(libraryPaginationMock); + }); + + it('should emit changePageNumber event', () => { + spyOn(component.changePageNumber, 'emit'); + paginationComponent.triggerEventHandler('changePageNumber', libraryPaginationMock); + expect(component.changePageNumber.emit).toHaveBeenCalledWith(libraryPaginationMock); + }); + + it('should emit nextPage event', () => { + spyOn(component.nextPage, 'emit'); + paginationComponent.triggerEventHandler('nextPage', libraryPaginationMock); + expect(component.nextPage.emit).toHaveBeenCalledWith(libraryPaginationMock); + }); + + it('should emit prevPage event', () => { + spyOn(component.prevPage, 'emit'); + paginationComponent.triggerEventHandler('prevPage', libraryPaginationMock); + expect(component.prevPage.emit).toHaveBeenCalledWith(libraryPaginationMock); + }); + }); + + describe('Node navigation', () => { + let documentListComponent: DebugElement; + + beforeEach(() => { + documentListComponent = unitTestingUtils.getByDirective(DocumentListComponent); + }); + + it('should not navigate when node is null or missing guid', () => { + spyOn(store, 'dispatch').and.stub(); + documentListComponent.triggerEventHandler('node-dblclick', { detail: { node: null } }); + expect(store.dispatch).not.toHaveBeenCalled(); + + documentListComponent.triggerEventHandler('node-dblclick', { detail: { node: { entry: {} } } }); + expect(store.dispatch).not.toHaveBeenCalled(); + }); + + it('should handle node double click', () => { + spyOn(store, 'dispatch').and.stub(); + const siteEntry = librariesMock.list.entries[0].entry; + documentListComponent.triggerEventHandler('node-dblclick', { detail: { node: { entry: siteEntry } } }); + expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({ ...new NavigateLibraryAction(siteEntry) })); + }); + + it('should handle name click', () => { + spyOn(store, 'dispatch').and.stub(); + const siteEntry = librariesMock.list.entries[0].entry; + documentListComponent.triggerEventHandler('name-click', { detail: { node: { entry: siteEntry } } }); + expect(store.dispatch).toHaveBeenCalledWith(jasmine.objectContaining({ ...new NavigateLibraryAction(siteEntry) })); + }); + }); +}); diff --git a/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.ts b/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.ts new file mode 100644 index 0000000000..06d7a795a7 --- /dev/null +++ b/projects/aca-content/src/lib/components/libraries-base/libraries-base.component.ts @@ -0,0 +1,98 @@ +/*! + * Copyright © 2005-2025 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 { CommonModule } from '@angular/common'; +import { Component, Input, Output, EventEmitter, ViewEncapsulation, inject } from '@angular/core'; +import { TranslatePipe } from '@ngx-translate/core'; +import { + ContextActionsDirective, + ToolbarComponent, + InfoDrawerComponent, + PageLayoutComponent, + PageComponent, + AppHookService +} from '@alfresco/aca-shared'; +import { DocumentListDirective } from '../../directives/document-list.directive'; +import { + CustomEmptyContentTemplateDirective, + DataColumnComponent, + DataColumnListComponent, + EmptyContentComponent, + PaginationComponent +} from '@alfresco/adf-core'; +import { DocumentListComponent } from '@alfresco/adf-content-services'; +import { DocumentListPresetRef, DynamicColumnComponent } from '@alfresco/adf-extensions'; +import { NavigateLibraryAction } from '@alfresco/aca-shared/store'; +import { FavoritePaging, Pagination, SiteEntry, SitePaging } from '@alfresco/js-api'; + +@Component({ + selector: 'aca-libraries-base', + standalone: true, + templateUrl: './libraries-base.component.html', + imports: [ + CommonModule, + TranslatePipe, + ToolbarComponent, + InfoDrawerComponent, + PageLayoutComponent, + DocumentListDirective, + ContextActionsDirective, + DocumentListComponent, + DataColumnListComponent, + DataColumnComponent, + DynamicColumnComponent, + CustomEmptyContentTemplateDirective, + EmptyContentComponent, + PaginationComponent + ], + encapsulation: ViewEncapsulation.None +}) +export class LibrariesBaseComponent extends PageComponent { + @Input() titleKey: string; + @Input() list: SitePaging | FavoritePaging; + @Input() isLoading: boolean; + @Input() emptyTitleKey: string; + @Input() emptySubtitleKey: string; + @Input() pagination: Pagination; + @Input() currentFolderId: string; + @Input() columns: DocumentListPresetRef[]; + @Input() navigateRoute: string; + + @Output() changePageSize = new EventEmitter(); + @Output() changePageNumber = new EventEmitter(); + @Output() nextPage = new EventEmitter(); + @Output() prevPage = new EventEmitter(); + + protected appHookService = inject(AppHookService); + + private navigateTo(node: SiteEntry) { + if (node?.entry?.guid) { + this.store.dispatch(new NavigateLibraryAction(node.entry, this.navigateRoute)); + } + } + + handleNodeClick(event: Event) { + this.navigateTo((event as CustomEvent).detail?.node); + } +} 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 fe98257102..73eb0975f1 100644 --- a/projects/aca-content/src/lib/components/libraries/libraries.component.html +++ b/projects/aca-content/src/lib/components/libraries/libraries.component.html @@ -1,86 +1,7 @@ - -
-

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

- -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
-
-
+ 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 40de31f9bb..7a7e2fe177 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 @@ -23,32 +23,20 @@ */ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Router } from '@angular/router'; -import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { LibrariesComponent } from './libraries.component'; import { AppTestingModule } from '../../testing/app-testing.module'; import { provideEffects } from '@ngrx/effects'; import { LibraryEffects } from '../../store/effects'; -import { ContentApiService } from '@alfresco/aca-shared'; -import { getTitleElementText } from '../../testing/test-utils'; +import { AppExtensionService, AppHookService, ContentApiService } from '@alfresco/aca-shared'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { libraryColumnsPresetMock, librariesMock } from '../../mock/libraries-mock'; describe('LibrariesComponent', () => { let fixture: ComponentFixture; let component: LibrariesComponent; - let alfrescoApi: AlfrescoApiService; let contentApiService: ContentApiService; - let router: Router; - let page; - - beforeEach(() => { - page = { - list: { - entries: [{ entry: { id: 1 } }, { entry: { id: 2 } }], - pagination: { data: 'data' } - } - }; - }); + let appHookService: AppHookService; + let appExtensionService: AppExtensionService; beforeEach(() => { TestBed.configureTestingModule({ @@ -59,34 +47,49 @@ describe('LibrariesComponent', () => { fixture = TestBed.createComponent(LibrariesComponent); component = fixture.componentInstance; - alfrescoApi = TestBed.inject(AlfrescoApiService); contentApiService = TestBed.inject(ContentApiService); - alfrescoApi.reset(); - router = TestBed.inject(Router); + appHookService = TestBed.inject(AppHookService); + appExtensionService = TestBed.inject(AppExtensionService); const sitesApi = contentApiService.sitesApi; - spyOn(sitesApi, 'listSites').and.returnValue(Promise.resolve(page)); + spyOn(sitesApi, 'listSites').and.returnValue(Promise.resolve(librariesMock)); 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'); + it('should set columns from extensions on init', () => { + appExtensionService.documentListPresets.libraries = libraryColumnsPresetMock; + fixture.detectChanges(); + expect(component.columns).toEqual(appExtensionService.documentListPresets.libraries); + }); + + it('should handle no columns preset in extensions', () => { + appExtensionService.documentListPresets.libraries = undefined; + component.ngOnInit(); + expect(component.columns.length).toBe(0); + }); + + describe('Library hooks', () => { + let reloadSpy: jasmine.Spy<() => void>; - component.selectedRowItemsCount = 2; + beforeEach(() => { + reloadSpy = spyOn(component, 'reload'); 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(); - component.navigateTo(null); + it('should reload on libraryDeleted hook', () => { + appHookService.libraryDeleted.next(''); + expect(reloadSpy).toHaveBeenCalled(); + }); + + it('should reload on libraryUpdated hook', () => { + appHookService.libraryUpdated.next(librariesMock.list.entries[0]); + expect(reloadSpy).toHaveBeenCalled(); + }); - expect(router.navigate).not.toHaveBeenCalled(); + it('should reload on libraryLeft hook', () => { + appHookService.libraryLeft.next(''); + expect(reloadSpy).toHaveBeenCalled(); }); }); }); diff --git a/projects/aca-content/src/lib/components/libraries/libraries.component.ts b/projects/aca-content/src/lib/components/libraries/libraries.component.ts index eb9bde8e53..935ad23126 100644 --- a/projects/aca-content/src/lib/components/libraries/libraries.component.ts +++ b/projects/aca-content/src/lib/components/libraries/libraries.component.ts @@ -22,59 +22,17 @@ * from Hyland Software. If not, see . */ -import { NavigateLibraryAction } from '@alfresco/aca-shared/store'; -import { SiteEntry } from '@alfresco/js-api'; import { Component, OnInit, ViewEncapsulation } from '@angular/core'; -import { - AppHookService, - ContextActionsDirective, - InfoDrawerComponent, - PageComponent, - PageLayoutComponent, - PaginationDirective, - ToolbarComponent -} from '@alfresco/aca-shared'; -import { DocumentListPresetRef, DynamicColumnComponent } from '@alfresco/adf-extensions'; -import { CommonModule } from '@angular/common'; -import { - CustomEmptyContentTemplateDirective, - DataColumnComponent, - DataColumnListComponent, - EmptyContentComponent, - PaginationComponent -} from '@alfresco/adf-core'; -import { DocumentListDirective } from '../../directives/document-list.directive'; -import { TranslatePipe } from '@ngx-translate/core'; -import { DocumentListComponent } from '@alfresco/adf-content-services'; +import { LibrariesBaseComponent } from '../libraries-base/libraries-base.component'; @Component({ - imports: [ - CommonModule, - DocumentListDirective, - ContextActionsDirective, - PaginationComponent, - PaginationDirective, - InfoDrawerComponent, - PageLayoutComponent, - TranslatePipe, - ToolbarComponent, - EmptyContentComponent, - DynamicColumnComponent, - DocumentListComponent, - DataColumnComponent, - DataColumnListComponent, - CustomEmptyContentTemplateDirective - ], + selector: 'aca-libraries', + standalone: true, templateUrl: './libraries.component.html', + imports: [LibrariesBaseComponent], encapsulation: ViewEncapsulation.None }) -export class LibrariesComponent extends PageComponent implements OnInit { - columns: DocumentListPresetRef[] = []; - - constructor(private appHookService: AppHookService) { - super(); - } - +export class LibrariesComponent extends LibrariesBaseComponent implements OnInit { ngOnInit() { super.ngOnInit(); @@ -86,14 +44,4 @@ export class LibrariesComponent extends PageComponent implements OnInit { this.columns = this.extensions.documentListPresets.libraries || []; } - - navigateTo(node: SiteEntry) { - if (node?.entry?.guid) { - this.store.dispatch(new NavigateLibraryAction(node.entry)); - } - } - - handleNodeClick(event: Event) { - this.navigateTo((event as CustomEvent).detail?.node); - } } diff --git a/projects/aca-content/src/lib/components/library-list/library-list.component.html b/projects/aca-content/src/lib/components/library-list/library-list.component.html new file mode 100644 index 0000000000..0fbe7923af --- /dev/null +++ b/projects/aca-content/src/lib/components/library-list/library-list.component.html @@ -0,0 +1,13 @@ + diff --git a/projects/aca-content/src/lib/components/library-list/library-list.component.spec.ts b/projects/aca-content/src/lib/components/library-list/library-list.component.spec.ts new file mode 100644 index 0000000000..f8fdf3d8ce --- /dev/null +++ b/projects/aca-content/src/lib/components/library-list/library-list.component.spec.ts @@ -0,0 +1,136 @@ +/*! + * Copyright © 2005-2025 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, TestBed } from '@angular/core/testing'; +import { UserPreferencesService } from '@alfresco/adf-core'; +import { SitesService } from '@alfresco/adf-content-services'; +import { LibraryListComponent } from './library-list.component'; +import { AppTestingModule } from '../../testing/app-testing.module'; +import { AppExtensionService, AppHookService } from '@alfresco/aca-shared'; +import { provideEffects } from '@ngrx/effects'; +import { Observable, of, throwError } from 'rxjs'; +import { LibraryEffects } from '../../store/effects'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { SitePaging } from '@alfresco/js-api'; +import { libraryColumnsPresetMock, librariesMock, libraryPaginationMock } from '../../mock/libraries-mock'; + +describe('LibraryListComponent', () => { + let fixture: ComponentFixture; + let component: LibraryListComponent; + let userPreference: UserPreferencesService; + let sitesService: SitesService; + let appHookService: AppHookService; + let getSitesSpy: jasmine.Spy<(options?: any) => Observable>; + let appExtensionService: AppExtensionService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule, LibraryListComponent, MatSnackBarModule], + providers: [provideEffects([LibraryEffects])] + }); + + fixture = TestBed.createComponent(LibraryListComponent); + component = fixture.componentInstance; + + sitesService = TestBed.inject(SitesService); + userPreference = TestBed.inject(UserPreferencesService); + appHookService = TestBed.inject(AppHookService); + appExtensionService = TestBed.inject(AppExtensionService); + + getSitesSpy = spyOn(sitesService, 'getSites'); + getSitesSpy.and.returnValue(of(librariesMock)); + fixture.detectChanges(); + }); + + it('should set data', () => { + expect(component.list).toBe(librariesMock); + expect(component.pagination).toBe(librariesMock.list.pagination); + }); + + it('should get data with user preference pagination size', () => { + userPreference.paginationSize = 1; + component.ngOnInit(); + expect(sitesService.getSites).toHaveBeenCalledWith({ maxItems: 1 }); + }); + + it('should set data on error', () => { + getSitesSpy.and.returnValue(throwError(() => 'error')); + component.ngOnInit(); + + expect(component.list).toBeNull(); + expect(component.pagination).toBeNull(); + expect(component.isLoading).toBe(false); + }); + + it('should set columns from extensions on init', () => { + appExtensionService.documentListPresets.libraries = libraryColumnsPresetMock; + component.ngOnInit(); + expect(component.columns).toEqual(appExtensionService.documentListPresets.libraries); + }); + + it('should handle no columns preset in extensions', () => { + appExtensionService.documentListPresets.libraries = undefined; + component.ngOnInit(); + expect(component.columns.length).toBe(0); + }); + + describe('Library hooks', () => { + it('should reload on libraryDeleted action', () => { + appHookService.libraryDeleted.next(''); + expect(sitesService.getSites).toHaveBeenCalled(); + }); + + it('should reload on libraryUpdated action', () => { + appHookService.libraryUpdated.next(librariesMock.list.entries[0]); + expect(sitesService.getSites).toHaveBeenCalled(); + }); + + it('should reload on libraryJoined action', () => { + appHookService.libraryJoined.next(); + expect(sitesService.getSites).toHaveBeenCalled(); + }); + + it('should reload on libraryLeft action', () => { + appHookService.libraryLeft.next(''); + expect(sitesService.getSites).toHaveBeenCalled(); + }); + }); + + describe('Pagination', () => { + it('should get list with pagination data onChange event', () => { + component.getList(libraryPaginationMock); + expect(sitesService.getSites).toHaveBeenCalledWith(libraryPaginationMock); + }); + + it('should get list with pagination data onChangePageSize event', () => { + component.onChangePageSize(libraryPaginationMock); + expect(sitesService.getSites).toHaveBeenCalledWith(libraryPaginationMock); + }); + + it('should set preference page size onChangePageSize event', () => { + component.onChangePageSize(libraryPaginationMock); + expect(userPreference.paginationSize).toBe(libraryPaginationMock.maxItems); + }); + }); +}); diff --git a/projects/aca-content/src/lib/components/library-list/library-list.component.ts b/projects/aca-content/src/lib/components/library-list/library-list.component.ts new file mode 100644 index 0000000000..6a0edf37a4 --- /dev/null +++ b/projects/aca-content/src/lib/components/library-list/library-list.component.ts @@ -0,0 +1,94 @@ +/*! + * Copyright © 2005-2025 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 { ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Pagination, SitePaging } from '@alfresco/js-api'; +import { UserPreferencesService } from '@alfresco/adf-core'; +import { SitesService } from '@alfresco/adf-content-services'; +import { LibrariesBaseComponent } from '../libraries-base/libraries-base.component'; + +@Component({ + selector: 'aca-library-list', + standalone: true, + templateUrl: './library-list.component.html', + imports: [LibrariesBaseComponent], + encapsulation: ViewEncapsulation.None +}) +export class LibraryListComponent extends LibrariesBaseComponent implements OnInit { + pagination = new Pagination({ + skipCount: 0, + maxItems: 25, + totalItems: 0 + }); + isLoading = false; + list: SitePaging = null; + + constructor( + private readonly preferences: UserPreferencesService, + private readonly changeDetectorRef: ChangeDetectorRef, + private readonly sitesService: SitesService + ) { + super(); + } + + ngOnInit() { + super.ngOnInit(); + + this.getList({ maxItems: this.preferences.paginationSize }); + this.subscriptions.push( + this.appHookService.libraryDeleted.subscribe(() => this.reloadList()), + this.appHookService.libraryUpdated.subscribe(() => this.reloadList()), + this.appHookService.libraryJoined.subscribe(() => this.reloadList()), + this.appHookService.libraryLeft.subscribe(() => this.reloadList()) + ); + this.columns = this.extensions.documentListPresets.libraries || []; + } + + onChangePageSize(pagination: Pagination) { + this.preferences.paginationSize = pagination.maxItems; + this.getList(pagination); + } + + getList(pagination: Pagination) { + this.isLoading = true; + this.sitesService.getSites(pagination).subscribe({ + next: (libraryList: SitePaging) => { + this.list = libraryList; + this.pagination = libraryList.list.pagination; + this.isLoading = false; + this.changeDetectorRef.detectChanges(); + }, + error: () => { + this.list = null; + this.pagination = null; + this.isLoading = false; + } + }); + } + + private reloadList() { + this.reload(); + this.getList(this.pagination); + } +} diff --git a/projects/aca-content/src/lib/mock/libraries-mock.ts b/projects/aca-content/src/lib/mock/libraries-mock.ts new file mode 100644 index 0000000000..5c7acd2047 --- /dev/null +++ b/projects/aca-content/src/lib/mock/libraries-mock.ts @@ -0,0 +1,66 @@ +/*! + * Copyright © 2005-2025 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 { DocumentListPresetRef } from '@alfresco/adf-extensions'; +import { FavoritePaging, Pagination, SitePaging } from '@alfresco/js-api'; + +export const librariesMock: SitePaging = { + list: { + entries: [ + { entry: { id: '1', guid: '1', title: 'Library 1', visibility: 'public' } }, + { entry: { id: '2', guid: '2', title: 'Library 2', visibility: 'private' } } + ], + pagination: { count: 25, skipCount: 0 } + } +}; + +export const favoriteLibrariesMock: FavoritePaging = { + list: { + entries: [ + { entry: { targetGuid: '1', target: { id: '1', guid: '1', title: 'Favorite Library 1', visibility: 'public' } } }, + { entry: { targetGuid: '2', target: { id: '2', guid: '2', title: 'Favorite Library 2', visibility: 'private' } } } + ], + pagination: { count: 25, skipCount: 0 } + } +}; + +export const libraryColumnsPresetMock: DocumentListPresetRef[] = [ + { + key: 'name', + type: 'text', + sortable: false, + template: '', + desktopOnly: false, + sortingKey: '', + id: '' + } +]; + +export const libraryPaginationMock: Pagination = { + count: 100, + hasMoreItems: true, + totalItems: 300, + skipCount: 25, + maxItems: 25 +};