diff --git a/core-web/libs/data-access/src/lib/dot-site/dot-site.service.ts b/core-web/libs/data-access/src/lib/dot-site/dot-site.service.ts index 642e8aaf752e..e0ad5e1d7a48 100644 --- a/core-web/libs/data-access/src/lib/dot-site/dot-site.service.ts +++ b/core-web/libs/data-access/src/lib/dot-site/dot-site.service.ts @@ -67,6 +67,7 @@ export interface ContentByFolderParams { showWorking?: boolean; extensions?: string[]; mimeTypes?: string[]; + languageId?: number; } export const BASE_SITE_URL = '/api/v1/site'; export const DEFAULT_PER_PAGE = 10; diff --git a/core-web/libs/dotcms-models/src/lib/dot-site.model.ts b/core-web/libs/dotcms-models/src/lib/dot-site.model.ts index 467ca53c5fc0..731a96c53ab5 100644 --- a/core-web/libs/dotcms-models/src/lib/dot-site.model.ts +++ b/core-web/libs/dotcms-models/src/lib/dot-site.model.ts @@ -32,4 +32,5 @@ export interface ContentByFolderParams { showWorking?: boolean; extensions?: string[]; mimeTypes?: string[]; + languageId?: number; } diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field/dot-file-field.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field/dot-file-field.component.ts index 534d530fd94c..f1e991641f81 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field/dot-file-field.component.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field/dot-file-field.component.ts @@ -51,6 +51,7 @@ import { UploadedFile } from '../../../../models/dot-edit-content-file.model'; import { BaseControlValueAccessor } from '../../../shared/base-control-value-accesor'; +import { DotEditContentStore } from '../../../../store/edit-content.store'; @Component({ selector: 'dot-file-field', @@ -108,6 +109,13 @@ export class DotFileFieldComponent * This service is used to provide AI-related functionalities within the component. */ readonly #dotAiService = inject(DotAiService); + /** + * Reference to the parent edit-content store, used to resolve the current + * locale when opening the browser selector so it filters assets by the + * language the user is actively editing in (including new contentlets + * where `$contentlet` has no languageId yet). + */ + readonly #editContentStore = inject(DotEditContentStore, { optional: true }); /** * Reference to the dynamic dialog. It can be null if no dialog is currently open. * @@ -442,7 +450,9 @@ export class DotFileFieldComponent showFolders: false, showWorking: true, showArchived: false, - sortByDesc: true + sortByDesc: true, + languageId: + this.#editContentStore?.currentLocale()?.id ?? this.$contentlet()?.languageId } }); diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.spec.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.spec.ts index 44f885018629..6107e38a353f 100644 --- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.spec.ts +++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.spec.ts @@ -271,6 +271,30 @@ xdescribe('DotEditContentFileFieldComponent', () => { }); }); + describe('showSelectExistingFileDialog', () => { + it('should forward the contentlet languageId as a fallback to the browser selector dialog data', () => { + const dialogService = spectator.inject(DialogService); + const spyDialogOpen = jest.spyOn(dialogService, 'open'); + + spectator.setHostInput( + 'contentlet', + createFakeContentlet({ + [FILE_FIELD_MOCK.variable]: null, + languageId: 2 + }) + ); + spectator.detectChanges(); + + const fieldComponent = spectator.query(DotFileFieldComponent); + fieldComponent.showSelectExistingFileDialog(); + + expect(spyDialogOpen).toHaveBeenCalledTimes(1); + expect(spyDialogOpen.mock.calls[0][1].data).toEqual( + expect.objectContaining({ languageId: 2 }) + ); + }); + }); + describe('Disabled State Management', () => { it('should set disabled state correctly through setDisabledState method', () => { spectator.detectChanges(); diff --git a/core-web/libs/ui/src/lib/components/dot-browser-selector/dot-browser-selector.component.spec.ts b/core-web/libs/ui/src/lib/components/dot-browser-selector/dot-browser-selector.component.spec.ts index eaad8fd7126b..e96439839196 100644 --- a/core-web/libs/ui/src/lib/components/dot-browser-selector/dot-browser-selector.component.spec.ts +++ b/core-web/libs/ui/src/lib/components/dot-browser-selector/dot-browser-selector.component.spec.ts @@ -186,3 +186,73 @@ describe('DotBrowserSelectorComponent', () => { }); }); }); + +describe('DotBrowserSelectorComponent — DynamicDialogConfig.data forwarding', () => { + let spectator: Spectator; + let mockStore: ReturnType; + + const createComponent = createComponentFactory({ + component: DotBrowserSelectorComponent, + schemas: [CUSTOM_ELEMENTS_SCHEMA], + providers: [ + mockProvider(DynamicDialogRef), + mockProvider(DotContentletService, { + getContentletByInodeWithContent: jest + .fn() + .mockReturnValue(of(createFakeContentlet())) + }), + { + provide: DynamicDialogConfig, + useValue: { + data: { + hostFolderId: SYSTEM_HOST_ID, + mimeTypes: ['image'], + languageId: 2 + } + } + } + ], + detectChanges: false + }); + + beforeEach(() => { + mockStore = createMockStore(); + + TestBed.overrideComponent(DotBrowserSelectorComponent, { + set: { + imports: [DotMessagePipe], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + providers: [{ provide: DotBrowserSelectorStore, useValue: mockStore }] + } + }); + + spectator = createComponent(); + spectator.detectChanges(); + }); + + it('should forward languageId from DynamicDialogConfig.data into $folderParams on init', () => { + expect(spectator.component.$folderParams().languageId).toBe(2); + }); + + it('should preserve languageId in $folderParams after a node selection', () => { + spectator.component.onNodeSelect(mockNodeSelectEvent('demo.dotcms.com')); + + expect(spectator.component.$folderParams()).toEqual( + expect.objectContaining({ + hostFolderId: 'demo.dotcms.com', + languageId: 2 + }) + ); + }); + + it('should pass languageId through to store.uploadFile via folderParams', () => { + const mockFile = new File(['content'], 'photo.png', { type: 'image/png' }); + + spectator.component.onFileUpload(mockFile); + + expect(mockStore.uploadFile).toHaveBeenCalledWith({ + file: mockFile, + folderParams: expect.objectContaining({ languageId: 2 }) + }); + }); +});