-
Notifications
You must be signed in to change notification settings - Fork 467
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(edit-content): implement endpoint to fetch sites (#30562)
### Parent Issue #30215 ### Proposed Changes This pull request includes multiple changes to the `dot-site` service and the `dot-select-existing-file` component, primarily focusing on adding pagination support and improving the file selection UI and its state management. ### DotSiteService Enhancements: * Added a `DEFAULT_PAGE` constant and included a `page` parameter in the `getSites` method to support pagination. (`core-web/libs/data-access/src/lib/dot-site/dot-site.service.ts`) [[1]](diffhunk://#diff-c4872eacb42281fa7aa6fd3d5c084e08fd41e79eb2778581421e7344f9aa4e12R20-R21) [[2]](diffhunk://#diff-c4872eacb42281fa7aa6fd3d5c084e08fd41e79eb2778581421e7344f9aa4e12L44-R56) * Updated test cases to reflect the new `page` parameter in the URLs. (`core-web/libs/data-access/src/lib/dot-site/dot-site.service.spec.ts`) [[1]](diffhunk://#diff-ad4a37aa4e92da7343f4da8185ea2031a67319500d820946e0d24fa515e33241L30-R30) [[2]](diffhunk://#diff-ad4a37aa4e92da7343f4da8185ea2031a67319500d820946e0d24fa515e33241L44-R44) ### DotSelectExistingFile Component Enhancements: * Improved the `dot-sidebar` component to handle loading states and display skeleton loaders while data is being fetched. (`core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-select-existing-file/components/dot-sidebar/dot-sidebar.component.html`, `core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-select-existing-file/components/dot-sidebar/dot-sidebar.component.ts`) [[1]](diffhunk://#diff-c3ff3590364bc12ded7173d7374838dfab13f67598f80daa198dfd96c935d459L1-R22) [[2]](diffhunk://#diff-b6e7f5a1ad7c221178b3ddf3f3dfc06021ba87b4e18ef9d7f6c6c9a6eab016d3L1-R82) * Added event handling for node expansion in the `dot-sidebar` component to dynamically load child nodes. (`core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-select-existing-file/dot-select-existing-file.component.html`) * Introduced a new test suite for the `SelectExisingFileStore` to cover folder loading and child node expansion logic. (`core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-select-existing-file/store/select-existing-file.store.test.ts`) * Updated the `SelectExisingFileStore` to manage the state of folder nodes, including loading states and expanded nodes. (`core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-select-existing-file/store/select-existing-file.store.ts`) [[1]](diffhunk://#diff-4e8a1b332dae5fb8ead291b76bf6099437aaf4aa6fd0a759ffe71c3074ffe7f0R2-R22) [[2]](diffhunk://#diff-4e8a1b332dae5fb8ead291b76bf6099437aaf4aa6fd0a759ffe71c3074ffe7f0L20-R35) [[3]](diffhunk://#diff-4e8a1b332dae5fb8ead291b76bf6099437aaf4aa6fd0a759ffe71c3074ffe7f0L36-R51) [[4]](diffhunk://#diff-4e8a1b332dae5fb8ead291b76bf6099437aaf4aa6fd0a759ffe71c3074ffe7f0R70-R71) [[5]](diffhunk://#diff-4e8a1b332dae5fb8ead291b76bf6099437aaf4aa6fd0a759ffe71c3074ffe7f0L75-R153) ### Miscellaneous: * Minor CSS adjustments to the `dot-select-existing-file` component to improve layout and styling. (`core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-select-existing-file/dot-select-existing-file.component.scss`) * Refactored the `file-field.store.spec.ts` to use `TestBed` for dependency injection and added missing imports. (`core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/store/file-field.store.spec.ts`) [[1]](diffhunk://#diff-57a0231d8f4135b48e8dd7e31f8e216157f1fd49a145d9cc0c7d7bf912a00de4L2-R4) [[2]](diffhunk://#diff-57a0231d8f4135b48e8dd7e31f8e216157f1fd49a145d9cc0c7d7bf912a00de4L19-R22) ### Checklist - [x] Tests - [x] Translations - [x] Security Implications Contemplated (add notes if applicable) ### Screenshots https://github.com/user-attachments/assets/56370a81-b204-456e-85c2-7d7dc9f26a57
- Loading branch information
Showing
14 changed files
with
356 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 22 additions & 1 deletion
23
...eld/components/dot-select-existing-file/components/dot-sidebar/dot-sidebar.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,22 @@ | ||
<p-tree [value]="$folders()" class="w-full h-full" styleClass="flex h-full" /> | ||
@let loading = $loading(); | ||
|
||
@if (!loading) { | ||
<p-tree | ||
[value]="$folders()" | ||
[loading]="loading" | ||
loadingMode="icon" | ||
class="w-full h-full" | ||
styleClass="flex h-full" | ||
loadingMode="icon" | ||
(onNodeExpand)="onNodeExpand.emit($event)"> | ||
<ng-template let-node pTemplate="default"> | ||
<span>{{ node.label | truncatePath | slice: 0 : 18 }}</span> | ||
</ng-template> | ||
</p-tree> | ||
} @else { | ||
<div class="flex w-full h-full flex-column p-2"> | ||
@for (col of $fakeColumns(); track $index) { | ||
<p-skeleton styleClass="mb-3" [width]="col" /> | ||
} | ||
</div> | ||
} |
66 changes: 60 additions & 6 deletions
66
...field/components/dot-select-existing-file/components/dot-sidebar/dot-sidebar.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,83 @@ | ||
import { ChangeDetectionStrategy, Component, input } from '@angular/core'; | ||
import { faker } from '@faker-js/faker'; | ||
|
||
import { SlicePipe } from '@angular/common'; | ||
import { | ||
ChangeDetectionStrategy, | ||
ChangeDetectorRef, | ||
Component, | ||
inject, | ||
input, | ||
output, | ||
signal | ||
} from '@angular/core'; | ||
|
||
import { TreeNode } from 'primeng/api'; | ||
import { TreeModule } from 'primeng/tree'; | ||
import { SkeletonModule } from 'primeng/skeleton'; | ||
import { TreeModule, TreeNodeExpandEvent } from 'primeng/tree'; | ||
|
||
import { TruncatePathPipe } from '@dotcms/edit-content/pipes/truncate-path.pipe'; | ||
|
||
@Component({ | ||
selector: 'dot-sidebar', | ||
standalone: true, | ||
imports: [TreeModule], | ||
imports: [TreeModule, SlicePipe, TruncatePathPipe, SkeletonModule], | ||
templateUrl: './dot-sidebar.component.html', | ||
styleUrls: ['./dot-sidebar.component.scss'], | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class DotSideBarComponent { | ||
/** | ||
* An observable that emits an array of TreeNode objects representing folders. | ||
* A readonly private field that holds an instance of ChangeDetectorRef. | ||
* This is used to detect and respond to changes in the component's data-bound properties. | ||
*/ | ||
readonly #cd = inject(ChangeDetectorRef); | ||
/** | ||
* An observable that emits an array of TreeNode objects representing the folders. | ||
* | ||
* @type {Observable<TreeNode[]>} | ||
* @alias folders | ||
*/ | ||
$folders = input.required<TreeNode[]>({ alias: 'folders' }); | ||
/** | ||
* Represents a loading state for the component. | ||
* A boolean observable that indicates the loading state. | ||
* | ||
* @type {boolean} | ||
* @alias loading | ||
*/ | ||
$loading = input.required<boolean>({ alias: 'loading' }); | ||
|
||
/** | ||
* Signal that generates an array of strings representing percentages. | ||
* Each percentage is a random value between 75% and 100%. | ||
* The array contains 50 elements. | ||
* | ||
* @returns {string[]} An array of 50 percentage strings. | ||
*/ | ||
$fakeColumns = signal<string[]>(Array.from({ length: 50 }).map((_) => this.getPercentage())); | ||
|
||
/** | ||
* Event emitter for when a tree node is expanded. | ||
* | ||
* This event is triggered when a user expands a node in the tree structure. | ||
* It emits an event of type `TreeNodeExpandEvent`. | ||
*/ | ||
onNodeExpand = output<TreeNodeExpandEvent>(); | ||
|
||
/** | ||
* Triggers change detection manually. | ||
* This method is used to ensure that the view is updated when the model changes. | ||
* It calls the `detectChanges` method on the ChangeDetectorRef instance. | ||
*/ | ||
detectChanges() { | ||
this.#cd.detectChanges(); | ||
} | ||
/** | ||
* Generates a random percentage string between 75% and 100%. | ||
* | ||
* @returns {string} A string representing a percentage between 75% and 100%. | ||
*/ | ||
getPercentage(): string { | ||
const number = faker.number.int({ max: 100, min: 75 }); | ||
|
||
return `${number}%`; | ||
} | ||
} |
9 changes: 6 additions & 3 deletions
9
...nt-file-field/components/dot-select-existing-file/dot-select-existing-file.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,9 @@ | |
::ng-deep { | ||
.p-tree { | ||
border: 0px; | ||
padding-right: 0px; | ||
padding-top: 0px; | ||
padding-bottom: 0px; | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
...t-file-field/components/dot-select-existing-file/store/select-existing-file.store.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { createFakeEvent } from '@ngneat/spectator'; | ||
import { SpyObject, mockProvider } from '@ngneat/spectator/jest'; | ||
import { of, throwError } from 'rxjs'; | ||
|
||
import { fakeAsync, TestBed, tick } from '@angular/core/testing'; | ||
|
||
import { ComponentStatus } from '@dotcms/dotcms-models'; | ||
|
||
import { SelectExisingFileStore } from './select-existing-file.store'; | ||
|
||
import { DotEditContentService } from '../../../../../services/dot-edit-content.service'; | ||
import { TREE_SELECT_MOCK, TREE_SELECT_SITES_MOCK } from '../../../../../utils/mocks'; | ||
|
||
describe('SelectExisingFileStore', () => { | ||
let store: InstanceType<typeof SelectExisingFileStore>; | ||
let service: SpyObject<DotEditContentService>; | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
providers: [SelectExisingFileStore, mockProvider(DotEditContentService)] | ||
}); | ||
|
||
store = TestBed.inject(SelectExisingFileStore); | ||
service = TestBed.inject(DotEditContentService) as SpyObject<DotEditContentService>; | ||
}); | ||
|
||
it('should be created', () => { | ||
expect(store).toBeTruthy(); | ||
}); | ||
|
||
describe('Method: loadFolders', () => { | ||
it('should set folders status to LOADING and then to LOADED with data', fakeAsync(() => { | ||
service.getSitesTreePath.mockReturnValue(of(TREE_SELECT_SITES_MOCK)); | ||
|
||
store.loadFolders(); | ||
|
||
tick(50); | ||
|
||
expect(store.folders().status).toBe(ComponentStatus.LOADED); | ||
expect(store.folders().data).toEqual(TREE_SELECT_SITES_MOCK); | ||
})); | ||
|
||
it('should set folders status to ERROR on service error', fakeAsync(() => { | ||
service.getSitesTreePath.mockReturnValue(throwError('error')); | ||
|
||
store.loadFolders(); | ||
|
||
tick(50); | ||
|
||
expect(store.folders().status).toBe(ComponentStatus.ERROR); | ||
expect(store.folders().data).toEqual([]); | ||
})); | ||
}); | ||
|
||
describe('Method: loadChildren', () => { | ||
it('should load children for a node', fakeAsync(() => { | ||
const mockChildren = [...TREE_SELECT_SITES_MOCK]; | ||
|
||
service.getFoldersTreeNode.mockReturnValue(of(mockChildren)); | ||
|
||
const node = { ...TREE_SELECT_MOCK[0] }; | ||
|
||
const mockItem = { | ||
originalEvent: createFakeEvent('click'), | ||
node | ||
}; | ||
store.loadChildren(mockItem); | ||
|
||
tick(50); | ||
|
||
expect(node.children).toEqual(mockChildren); | ||
expect(node.loading).toBe(false); | ||
expect(node.leaf).toBe(true); | ||
expect(node.icon).toBe('pi pi-folder-open'); | ||
expect(store.folders().nodeExpaned).toBe(node); | ||
})); | ||
|
||
it('should handle error when loading children', fakeAsync(() => { | ||
service.getFoldersTreeNode.mockReturnValue(throwError('error')); | ||
|
||
const node = { ...TREE_SELECT_MOCK[0], children: [] }; | ||
|
||
const mockItem = { | ||
originalEvent: createFakeEvent('click'), | ||
node | ||
}; | ||
store.loadChildren(mockItem); | ||
|
||
tick(50); | ||
|
||
expect(node.children).toEqual([]); | ||
expect(node.loading).toBe(false); | ||
})); | ||
}); | ||
}); |
Oops, something went wrong.