@webiny/app-file-manager 6.4.3-beta.0 → 6.4.3-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/app-file-manager",
3
- "version": "6.4.3-beta.0",
3
+ "version": "6.4.3-beta.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -16,22 +16,22 @@
16
16
  "@apollo/react-common": "3.1.4",
17
17
  "@apollo/react-components": "3.1.5",
18
18
  "@types/react": "18.3.29",
19
- "@webiny/admin-ui": "6.4.3-beta.0",
20
- "@webiny/app": "6.4.3-beta.0",
21
- "@webiny/app-aco": "6.4.3-beta.0",
22
- "@webiny/app-admin": "6.4.3-beta.0",
23
- "@webiny/app-headless-cms": "6.4.3-beta.0",
24
- "@webiny/app-headless-cms-common": "6.4.3-beta.0",
25
- "@webiny/app-websockets": "6.4.3-beta.0",
19
+ "@webiny/admin-ui": "6.4.3-beta.1",
20
+ "@webiny/app": "6.4.3-beta.1",
21
+ "@webiny/app-aco": "6.4.3-beta.1",
22
+ "@webiny/app-admin": "6.4.3-beta.1",
23
+ "@webiny/app-headless-cms": "6.4.3-beta.1",
24
+ "@webiny/app-headless-cms-common": "6.4.3-beta.1",
25
+ "@webiny/app-websockets": "6.4.3-beta.1",
26
26
  "@webiny/di": "1.0.1",
27
- "@webiny/feature": "6.4.3-beta.0",
28
- "@webiny/form": "6.4.3-beta.0",
29
- "@webiny/icons": "6.4.3-beta.0",
30
- "@webiny/plugins": "6.4.3-beta.0",
31
- "@webiny/react-composition": "6.4.3-beta.0",
32
- "@webiny/react-properties": "6.4.3-beta.0",
33
- "@webiny/sdk": "6.4.3-beta.0",
34
- "@webiny/validation": "6.4.3-beta.0",
27
+ "@webiny/feature": "6.4.3-beta.1",
28
+ "@webiny/form": "6.4.3-beta.1",
29
+ "@webiny/icons": "6.4.3-beta.1",
30
+ "@webiny/plugins": "6.4.3-beta.1",
31
+ "@webiny/react-composition": "6.4.3-beta.1",
32
+ "@webiny/react-properties": "6.4.3-beta.1",
33
+ "@webiny/sdk": "6.4.3-beta.1",
34
+ "@webiny/validation": "6.4.3-beta.1",
35
35
  "apollo-cache": "1.3.5",
36
36
  "apollo-client": "2.6.10",
37
37
  "apollo-link": "1.2.14",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@svgr/webpack": "8.1.0",
56
- "@webiny/build-tools": "6.4.3-beta.0",
56
+ "@webiny/build-tools": "6.4.3-beta.1",
57
57
  "rimraf": "6.1.3",
58
58
  "typescript": "6.0.3",
59
59
  "vitest": "4.1.8"
@@ -358,6 +358,94 @@ describe("FileListPresenter", ()=>{
358
358
  expect(args[1].data.name).toBe("photo.png");
359
359
  expect(args[1].data.type).toBe("image/png");
360
360
  });
361
+ it("should be loading when pagination is loading and no rows exist", ()=>{
362
+ presenter.init();
363
+ runInAction(()=>{
364
+ mocks.listPresenter.vm.rows = [];
365
+ mocks.listPresenter.vm.pagination.loading = true;
366
+ });
367
+ expect(presenter.vm.loading).toBe(true);
368
+ });
369
+ it("should not be loading when rows already exist", ()=>{
370
+ presenter.init();
371
+ runInAction(()=>{
372
+ mocks.listPresenter.vm.rows = [
373
+ {
374
+ id: "file-1"
375
+ }
376
+ ];
377
+ mocks.listPresenter.vm.pagination.loading = true;
378
+ });
379
+ expect(presenter.vm.loading).toBe(false);
380
+ });
381
+ it("should not be loading when pagination is not loading", ()=>{
382
+ presenter.init();
383
+ runInAction(()=>{
384
+ mocks.listPresenter.vm.rows = [];
385
+ mocks.listPresenter.vm.pagination.loading = false;
386
+ });
387
+ expect(presenter.vm.loading).toBe(false);
388
+ });
389
+ it("should be empty when no files and no child folders", ()=>{
390
+ presenter.init();
391
+ runInAction(()=>{
392
+ mocks.listPresenter.vm.rows = [];
393
+ mocks.listPresenter.vm.pagination.loading = false;
394
+ });
395
+ expect(presenter.vm.empty).toBe(true);
396
+ });
397
+ it("should not be empty when there are files", ()=>{
398
+ presenter.init();
399
+ runInAction(()=>{
400
+ mocks.listPresenter.vm.rows = [
401
+ {
402
+ id: "file-1"
403
+ }
404
+ ];
405
+ mocks.listPresenter.vm.pagination.loading = false;
406
+ });
407
+ expect(presenter.vm.empty).toBe(false);
408
+ });
409
+ it("should not be empty when there are child folders but no files", ()=>{
410
+ presenter.init();
411
+ runInAction(()=>{
412
+ mocks.listPresenter.vm.rows = [];
413
+ mocks.listPresenter.vm.pagination.loading = false;
414
+ mocks.folderTreePresenter.vm.childFolders = [
415
+ {
416
+ id: "folder-1"
417
+ }
418
+ ];
419
+ });
420
+ expect(presenter.vm.empty).toBe(false);
421
+ });
422
+ it("should not be empty while loading", ()=>{
423
+ presenter.init();
424
+ runInAction(()=>{
425
+ mocks.listPresenter.vm.rows = [];
426
+ mocks.listPresenter.vm.pagination.loading = true;
427
+ });
428
+ expect(presenter.vm.empty).toBe(false);
429
+ });
430
+ it("should be empty when child folders exist but folders are hidden (search active)", ()=>{
431
+ presenter.init();
432
+ runInAction(()=>{
433
+ mocks.listPresenter.vm.rows = [];
434
+ mocks.listPresenter.vm.pagination.loading = false;
435
+ mocks.listPresenter.vm.appliedQuery = {
436
+ search: "photo",
437
+ filters: {
438
+ folderId: "root"
439
+ }
440
+ };
441
+ mocks.folderTreePresenter.vm.childFolders = [
442
+ {
443
+ id: "folder-1"
444
+ }
445
+ ];
446
+ });
447
+ expect(presenter.vm.empty).toBe(true);
448
+ });
361
449
  it("should show folders when appliedQuery is null", ()=>{
362
450
  presenter.init();
363
451
  expect(presenter.vm.showFolders).toBe(true);
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileList/FileListPresenter.test.js","sources":["../../../src/presentation/FileList/FileListPresenter.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { observable, runInAction } from \"mobx\";\nimport { Container } from \"@webiny/di\";\nimport { ListCache } from \"@webiny/app-admin/features/listCache/index.js\";\nimport { ListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { FileManagerPermissions } from \"../../features/permissions/abstractions.js\";\nimport { GetSettingsRepository } from \"../../features/settings/abstractions.js\";\nimport { ListTagsRepository } from \"../../features/tags/abstractions.js\";\nimport { FileUploader } from \"../../features/fileUploader/abstractions.js\";\nimport { LocalStorage } from \"@webiny/app/features/localStorage\";\nimport { ListFilesUseCase } from \"../../features/listFiles/abstractions.js\";\nimport { FilesListCache } from \"../../features/shared/abstractions.js\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport { FileModelProvider } from \"../../features/fileModel/abstractions.js\";\nimport { FileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport { FileManagerPresenter as Abstraction, type IFileManagerPresenter } from \"./abstractions.js\";\nimport { FileManagerPresenter } from \"./FileManagerPresenter.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createMockListPresenter(): ListPresenter.Interface<FmFile> {\n const vmState = observable({\n rows: [] as FmFile[],\n sort: null as { field: string; direction: \"ASC\" | \"DESC\" } | null,\n filters: {} as Record<string, unknown>,\n search: \"\",\n appliedQuery: null as\n | import(\"@webiny/app-admin/presentation/listPresenter/abstractions.js\").IDataSourceQuery\n | null,\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n error: null as\n | import(\"@webiny/app-admin/presentation/listPresenter/abstractions.js\").IListError\n | null\n });\n\n return {\n get vm() {\n return vmState;\n },\n actions: {\n search: { set: vi.fn(), clear: vi.fn() },\n sort: { set: vi.fn(), toggle: vi.fn() },\n filter: { set: vi.fn(), clear: vi.fn(), clearAll: vi.fn() },\n selection: {\n toggle: vi.fn(),\n selectRangeTo: vi.fn(),\n selectAll: vi.fn(),\n deselectAll: vi.fn(),\n selectRows: vi.fn(),\n isSelected: vi.fn().mockReturnValue(false)\n },\n loadMore: vi.fn().mockResolvedValue(undefined),\n refresh: vi.fn().mockResolvedValue(undefined)\n },\n init: vi.fn()\n };\n}\n\nfunction createMockFolderTreePresenter(): FolderTreePresenter.Interface {\n const state = observable({\n folders: [],\n tree: [],\n currentFolderId: null as string | null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders: [],\n loadingNodeIds: []\n });\n\n return {\n get vm() {\n return state;\n },\n selectFolder: vi.fn(),\n createFolder: vi.fn(),\n editFolder: vi.fn(),\n deleteFolder: vi.fn().mockResolvedValue(undefined),\n moveFolder: vi.fn().mockResolvedValue(undefined),\n loadChildFolders: vi.fn().mockResolvedValue(undefined),\n canManageStructure: vi.fn().mockReturnValue(true),\n getAncestorIds: vi.fn().mockReturnValue([]),\n submitOperation: vi.fn().mockResolvedValue(true),\n cancelOperation: vi.fn(),\n onFolderChange: vi.fn().mockReturnValue(() => {})\n };\n}\n\nfunction createMockPermissions(): FileManagerPermissions.Interface {\n return {\n canAccess: vi.fn().mockReturnValue(true),\n canRead: vi.fn().mockReturnValue(true),\n canCreate: vi.fn().mockReturnValue(true),\n canEdit: vi.fn().mockReturnValue(false),\n canDelete: vi.fn().mockReturnValue(false),\n canAction: vi.fn().mockReturnValue(true)\n } as unknown as FileManagerPermissions.Interface;\n}\n\nfunction createMockSettingsRepository(): GetSettingsRepository.Interface {\n return {\n execute: vi.fn().mockResolvedValue({\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }),\n save: vi.fn().mockImplementation(async (data: Record<string, unknown>) => data),\n settings: null\n };\n}\n\nfunction createMockTagsRepository(): ListTagsRepository.Interface {\n return {\n execute: vi.fn().mockResolvedValue([]),\n tags: [{ tag: \"photo\", count: 5 }]\n };\n}\n\nfunction createMockFileUploader(): FileUploader.Interface {\n return {\n vm: {\n jobs: [],\n overallProgress: { sent: 0, total: 0, percentage: 0 },\n isUploading: false,\n completedCount: 0,\n failedCount: 0\n },\n upload: vi.fn().mockResolvedValue(undefined),\n uploadMany: vi.fn().mockResolvedValue(undefined),\n abort: vi.fn(),\n clear: vi.fn()\n };\n}\n\nfunction createMockLocalStorage(): LocalStorage.Interface {\n const store = new Map<string, unknown>();\n return {\n get: vi.fn((key: string) => store.get(key)) as LocalStorage.Interface[\"get\"],\n set: vi.fn(<T>(key: string, value: T): void => {\n store.set(key, value);\n }),\n remove: vi.fn((key: string) => {\n store.delete(key);\n }),\n clear: vi.fn(() => store.clear()),\n keys: vi.fn(() => Array.from(store.keys()))\n };\n}\n\nfunction createMockListFilesUseCase(): ListFilesUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({\n data: [],\n meta: { cursor: null, hasMoreItems: false, totalCount: 0 }\n })\n };\n}\n\nfunction createMockGetDescendantFoldersUseCase(): GetDescendantFoldersUseCase.Interface {\n return {\n execute: vi.fn().mockReturnValue([])\n };\n}\n\nfunction createMockFileDetailsPresenter(): IFileDetailsPresenter {\n return {\n vm: {\n file: null,\n loading: null,\n form: {\n layout: [],\n errors: [],\n isDirty: false,\n isValid: null,\n submitCount: 0,\n focusField: vi.fn(),\n getData: vi.fn(() => ({})),\n setData: vi.fn()\n },\n previewUrl: null,\n permissions: { canEdit: true, canDelete: true }\n },\n loadFile: vi.fn().mockResolvedValue(undefined),\n saveFile: vi.fn().mockResolvedValue(undefined)\n };\n}\n\n// ---------------------------------------------------------------------------\n// Container setup\n// ---------------------------------------------------------------------------\n\ninterface Mocks {\n listPresenter: ListPresenter.Interface<FmFile>;\n folderTreePresenter: FolderTreePresenter.Interface;\n fileDetailsPresenter: IFileDetailsPresenter;\n permissions: FileManagerPermissions.Interface;\n settingsRepository: GetSettingsRepository.Interface;\n tagsRepository: ListTagsRepository.Interface;\n fileUploader: FileUploader.Interface;\n localStorage: LocalStorage.Interface;\n listFilesUseCase: ListFilesUseCase.Interface;\n cache: ListCache<FmFile>;\n getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface;\n fileModelProvider: FileModelProvider.Interface;\n}\n\nfunction createMocks(): Mocks {\n return {\n listPresenter: createMockListPresenter(),\n folderTreePresenter: createMockFolderTreePresenter(),\n fileDetailsPresenter: createMockFileDetailsPresenter(),\n permissions: createMockPermissions(),\n settingsRepository: createMockSettingsRepository(),\n tagsRepository: createMockTagsRepository(),\n fileUploader: createMockFileUploader(),\n localStorage: createMockLocalStorage(),\n listFilesUseCase: createMockListFilesUseCase(),\n cache: new ListCache<FmFile>(),\n getDescendantFoldersUseCase: createMockGetDescendantFoldersUseCase(),\n fileModelProvider: {\n getModel: vi.fn().mockResolvedValue({ fields: [] })\n }\n };\n}\n\nfunction createContainer(mocks: Mocks) {\n const container = new Container();\n\n container.registerInstance(ListPresenter, mocks.listPresenter);\n container.registerInstance(FolderTreePresenter, mocks.folderTreePresenter);\n container.registerInstance(FileDetailsPresenter, mocks.fileDetailsPresenter);\n container.registerInstance(FileManagerPermissions, mocks.permissions);\n container.registerInstance(GetSettingsRepository, mocks.settingsRepository);\n container.registerInstance(ListTagsRepository, mocks.tagsRepository);\n container.registerInstance(FileUploader, mocks.fileUploader);\n container.registerInstance(LocalStorage, mocks.localStorage);\n container.registerInstance(ListFilesUseCase, mocks.listFilesUseCase);\n container.registerInstance(FilesListCache, mocks.cache);\n container.registerInstance(GetDescendantFoldersUseCase, mocks.getDescendantFoldersUseCase);\n container.registerInstance(FileModelProvider, mocks.fileModelProvider);\n\n // Register the real FileListPresenter implementation.\n container.register(FileManagerPresenter).inSingletonScope();\n\n return container;\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileListPresenter\", () => {\n let mocks: Mocks;\n let presenter: IFileManagerPresenter;\n\n beforeEach(() => {\n mocks = createMocks();\n const container = createContainer(mocks);\n presenter = container.resolve(Abstraction);\n });\n\n // -----------------------------------------------------------------------\n // Composition: ListPresenter and FolderTreePresenter initialized.\n // -----------------------------------------------------------------------\n\n it(\"should call listPresenter.init with a DataSource and initialSort on init()\", () => {\n presenter.init();\n\n expect(mocks.listPresenter.init).toHaveBeenCalledTimes(1);\n const config = (mocks.listPresenter.init as ReturnType<typeof vi.fn>).mock.calls[0][0];\n expect(config.dataSource).toBeDefined();\n expect(config.initialSort).toEqual({ field: \"createdOn\", direction: \"DESC\" });\n });\n\n it(\"should forward ListPresenter.vm data as vm.list\", () => {\n presenter.init();\n expect(presenter.vm.list.rows).toEqual(mocks.listPresenter.vm.rows);\n expect(presenter.vm.list.sort).toEqual(mocks.listPresenter.vm.sort);\n expect(presenter.vm.list.search).toBe(mocks.listPresenter.vm.search);\n expect(presenter.vm.list.empty).toBe(mocks.listPresenter.vm.empty);\n });\n\n it(\"should forward FolderTreePresenter.vm as vm.folders\", () => {\n presenter.init();\n expect(presenter.vm.folders).toBe(mocks.folderTreePresenter.vm);\n });\n\n // -----------------------------------------------------------------------\n // Folder change wiring.\n // -----------------------------------------------------------------------\n\n it(\"should set folderId filter when folder changes\", async () => {\n presenter.init();\n\n // Simulate folder selection via MobX observable change.\n runInAction(() => {\n (mocks.folderTreePresenter.vm as any).currentFolderId = \"folder-abc\";\n });\n\n // MobX reaction fires synchronously in the same tick after runInAction.\n expect(mocks.listPresenter.actions.filter.set).toHaveBeenCalledWith(\n \"folderId\",\n \"folder-abc\"\n );\n });\n\n it(\"should set folderId to root when folder is set to null\", () => {\n presenter.init();\n\n // First set a folder.\n runInAction(() => {\n (mocks.folderTreePresenter.vm as any).currentFolderId = \"folder-1\";\n });\n\n // Then clear it.\n runInAction(() => {\n (mocks.folderTreePresenter.vm as any).currentFolderId = null;\n });\n\n expect(mocks.listPresenter.actions.filter.set).toHaveBeenCalledWith(\"folderId\", \"root\");\n });\n\n // -----------------------------------------------------------------------\n // Permission flags forwarded to vm.\n // -----------------------------------------------------------------------\n\n it(\"should expose permission flags in vm.permissions\", () => {\n presenter.init();\n\n expect(presenter.vm.permissions.canRead).toBe(true);\n expect(presenter.vm.permissions.canCreate).toBe(true);\n expect(presenter.vm.permissions.canEdit).toBe(false);\n expect(presenter.vm.permissions.canDelete).toBe(false);\n });\n\n // -----------------------------------------------------------------------\n // View mode persistence via LocalStorage.\n // -----------------------------------------------------------------------\n\n it(\"should default viewMode to 'grid'\", () => {\n expect(presenter.vm.viewMode).toBe(\"grid\");\n });\n\n it(\"should update viewMode and persist to localStorage\", () => {\n presenter.actions.setViewMode(\"grid\");\n\n expect(presenter.vm.viewMode).toBe(\"grid\");\n expect(mocks.localStorage.set).toHaveBeenCalledWith(\"fm:viewMode\", \"grid\");\n });\n\n it(\"should restore viewMode from localStorage on construction\", () => {\n // Pre-seed localStorage with \"grid\" before creating the presenter.\n const seededMocks = createMocks();\n (seededMocks.localStorage.get as ReturnType<typeof vi.fn>).mockReturnValue(\"grid\");\n\n const container = createContainer(seededMocks);\n const seededPresenter: IFileManagerPresenter = container.resolve(Abstraction);\n\n expect(seededPresenter.vm.viewMode).toBe(\"grid\");\n });\n\n // -----------------------------------------------------------------------\n // Upload action.\n // -----------------------------------------------------------------------\n\n it(\"should call fileUploader.uploadMany when actions.upload is called\", async () => {\n presenter.init();\n\n const file1 = {\n id: \"f1\",\n name: \"test.txt\",\n type: \"text/plain\",\n size: 7,\n src: { file: new File([\"content\"], \"test.txt\", { type: \"text/plain\" }), base64: null }\n };\n const file2 = {\n id: \"f2\",\n name: \"photo.png\",\n type: \"image/png\",\n size: 3,\n src: { file: new File([\"img\"], \"photo.png\", { type: \"image/png\" }), base64: null }\n };\n\n await presenter.actions.upload([file1, file2]);\n\n expect(mocks.fileUploader.uploadMany).toHaveBeenCalledTimes(1);\n const args = (mocks.fileUploader.uploadMany as ReturnType<typeof vi.fn>).mock.calls[0][0];\n expect(args).toHaveLength(2);\n expect(args[0].data.name).toBe(\"test.txt\");\n expect(args[0].data.type).toBe(\"text/plain\");\n expect(args[1].data.name).toBe(\"photo.png\");\n expect(args[1].data.type).toBe(\"image/png\");\n });\n\n // -----------------------------------------------------------------------\n // showFolders computed from appliedQuery.\n // -----------------------------------------------------------------------\n\n it(\"should show folders when appliedQuery is null\", () => {\n presenter.init();\n expect(presenter.vm.showFolders).toBe(true);\n });\n\n it(\"should show folders when appliedQuery has no search and only folderId filter\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n filters: { folderId: \"root\" }\n };\n });\n expect(presenter.vm.showFolders).toBe(true);\n });\n\n it(\"should hide folders when appliedQuery has a search\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n search: \"photo\",\n filters: { folderId: \"root\" }\n };\n });\n expect(presenter.vm.showFolders).toBe(false);\n });\n\n it(\"should hide folders when appliedQuery has non-folderId filters\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n filters: { folderId: \"root\", tags: [\"photo\"] }\n };\n });\n expect(presenter.vm.showFolders).toBe(false);\n });\n\n it(\"should show folders again when appliedQuery filters are cleared\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n filters: { folderId: \"root\", type: \"image\" }\n };\n });\n expect(presenter.vm.showFolders).toBe(false);\n\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n filters: { folderId: \"root\" }\n };\n });\n expect(presenter.vm.showFolders).toBe(true);\n });\n});\n"],"names":["createMockListPresenter","vmState","observable","Set","vi","undefined","createMockFolderTreePresenter","state","createMockPermissions","createMockSettingsRepository","data","createMockTagsRepository","createMockFileUploader","createMockLocalStorage","store","Map","key","value","Array","createMockListFilesUseCase","createMockGetDescendantFoldersUseCase","createMockFileDetailsPresenter","createMocks","ListCache","createContainer","mocks","container","Container","ListPresenter","FolderTreePresenter","FileDetailsPresenter","FileManagerPermissions","GetSettingsRepository","ListTagsRepository","FileUploader","LocalStorage","ListFilesUseCase","FilesListCache","GetDescendantFoldersUseCase","FileModelProvider","FileManagerPresenter","describe","presenter","beforeEach","Abstraction","it","expect","config","runInAction","seededMocks","seededPresenter","file1","File","file2","args"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/FileList/FileListPresenter.test.js","sources":["../../../src/presentation/FileList/FileListPresenter.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { observable, runInAction } from \"mobx\";\nimport { Container } from \"@webiny/di\";\nimport { ListCache } from \"@webiny/app-admin/features/listCache/index.js\";\nimport { ListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { FileManagerPermissions } from \"../../features/permissions/abstractions.js\";\nimport { GetSettingsRepository } from \"../../features/settings/abstractions.js\";\nimport { ListTagsRepository } from \"../../features/tags/abstractions.js\";\nimport { FileUploader } from \"../../features/fileUploader/abstractions.js\";\nimport { LocalStorage } from \"@webiny/app/features/localStorage\";\nimport { ListFilesUseCase } from \"../../features/listFiles/abstractions.js\";\nimport { FilesListCache } from \"../../features/shared/abstractions.js\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport { FileModelProvider } from \"../../features/fileModel/abstractions.js\";\nimport { FileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport { FileManagerPresenter as Abstraction, type IFileManagerPresenter } from \"./abstractions.js\";\nimport { FileManagerPresenter } from \"./FileManagerPresenter.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createMockListPresenter(): ListPresenter.Interface<FmFile> {\n const vmState = observable({\n rows: [] as FmFile[],\n sort: null as { field: string; direction: \"ASC\" | \"DESC\" } | null,\n filters: {} as Record<string, unknown>,\n search: \"\",\n appliedQuery: null as\n | import(\"@webiny/app-admin/presentation/listPresenter/abstractions.js\").IDataSourceQuery\n | null,\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n error: null as\n | import(\"@webiny/app-admin/presentation/listPresenter/abstractions.js\").IListError\n | null\n });\n\n return {\n get vm() {\n return vmState;\n },\n actions: {\n search: { set: vi.fn(), clear: vi.fn() },\n sort: { set: vi.fn(), toggle: vi.fn() },\n filter: { set: vi.fn(), clear: vi.fn(), clearAll: vi.fn() },\n selection: {\n toggle: vi.fn(),\n selectRangeTo: vi.fn(),\n selectAll: vi.fn(),\n deselectAll: vi.fn(),\n selectRows: vi.fn(),\n isSelected: vi.fn().mockReturnValue(false)\n },\n loadMore: vi.fn().mockResolvedValue(undefined),\n refresh: vi.fn().mockResolvedValue(undefined)\n },\n init: vi.fn()\n };\n}\n\nfunction createMockFolderTreePresenter(): FolderTreePresenter.Interface {\n const state = observable({\n folders: [],\n tree: [],\n currentFolderId: null as string | null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders: [],\n loadingNodeIds: []\n });\n\n return {\n get vm() {\n return state;\n },\n selectFolder: vi.fn(),\n createFolder: vi.fn(),\n editFolder: vi.fn(),\n deleteFolder: vi.fn().mockResolvedValue(undefined),\n moveFolder: vi.fn().mockResolvedValue(undefined),\n loadChildFolders: vi.fn().mockResolvedValue(undefined),\n canManageStructure: vi.fn().mockReturnValue(true),\n getAncestorIds: vi.fn().mockReturnValue([]),\n submitOperation: vi.fn().mockResolvedValue(true),\n cancelOperation: vi.fn(),\n onFolderChange: vi.fn().mockReturnValue(() => {})\n };\n}\n\nfunction createMockPermissions(): FileManagerPermissions.Interface {\n return {\n canAccess: vi.fn().mockReturnValue(true),\n canRead: vi.fn().mockReturnValue(true),\n canCreate: vi.fn().mockReturnValue(true),\n canEdit: vi.fn().mockReturnValue(false),\n canDelete: vi.fn().mockReturnValue(false),\n canAction: vi.fn().mockReturnValue(true)\n } as unknown as FileManagerPermissions.Interface;\n}\n\nfunction createMockSettingsRepository(): GetSettingsRepository.Interface {\n return {\n execute: vi.fn().mockResolvedValue({\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }),\n save: vi.fn().mockImplementation(async (data: Record<string, unknown>) => data),\n settings: null\n };\n}\n\nfunction createMockTagsRepository(): ListTagsRepository.Interface {\n return {\n execute: vi.fn().mockResolvedValue([]),\n tags: [{ tag: \"photo\", count: 5 }]\n };\n}\n\nfunction createMockFileUploader(): FileUploader.Interface {\n return {\n vm: {\n jobs: [],\n overallProgress: { sent: 0, total: 0, percentage: 0 },\n isUploading: false,\n completedCount: 0,\n failedCount: 0\n },\n upload: vi.fn().mockResolvedValue(undefined),\n uploadMany: vi.fn().mockResolvedValue(undefined),\n abort: vi.fn(),\n clear: vi.fn()\n };\n}\n\nfunction createMockLocalStorage(): LocalStorage.Interface {\n const store = new Map<string, unknown>();\n return {\n get: vi.fn((key: string) => store.get(key)) as LocalStorage.Interface[\"get\"],\n set: vi.fn(<T>(key: string, value: T): void => {\n store.set(key, value);\n }),\n remove: vi.fn((key: string) => {\n store.delete(key);\n }),\n clear: vi.fn(() => store.clear()),\n keys: vi.fn(() => Array.from(store.keys()))\n };\n}\n\nfunction createMockListFilesUseCase(): ListFilesUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({\n data: [],\n meta: { cursor: null, hasMoreItems: false, totalCount: 0 }\n })\n };\n}\n\nfunction createMockGetDescendantFoldersUseCase(): GetDescendantFoldersUseCase.Interface {\n return {\n execute: vi.fn().mockReturnValue([])\n };\n}\n\nfunction createMockFileDetailsPresenter(): IFileDetailsPresenter {\n return {\n vm: {\n file: null,\n loading: null,\n form: {\n layout: [],\n errors: [],\n isDirty: false,\n isValid: null,\n submitCount: 0,\n focusField: vi.fn(),\n getData: vi.fn(() => ({})),\n setData: vi.fn()\n },\n previewUrl: null,\n permissions: { canEdit: true, canDelete: true }\n },\n loadFile: vi.fn().mockResolvedValue(undefined),\n saveFile: vi.fn().mockResolvedValue(undefined)\n };\n}\n\n// ---------------------------------------------------------------------------\n// Container setup\n// ---------------------------------------------------------------------------\n\ninterface Mocks {\n listPresenter: ListPresenter.Interface<FmFile>;\n folderTreePresenter: FolderTreePresenter.Interface;\n fileDetailsPresenter: IFileDetailsPresenter;\n permissions: FileManagerPermissions.Interface;\n settingsRepository: GetSettingsRepository.Interface;\n tagsRepository: ListTagsRepository.Interface;\n fileUploader: FileUploader.Interface;\n localStorage: LocalStorage.Interface;\n listFilesUseCase: ListFilesUseCase.Interface;\n cache: ListCache<FmFile>;\n getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface;\n fileModelProvider: FileModelProvider.Interface;\n}\n\nfunction createMocks(): Mocks {\n return {\n listPresenter: createMockListPresenter(),\n folderTreePresenter: createMockFolderTreePresenter(),\n fileDetailsPresenter: createMockFileDetailsPresenter(),\n permissions: createMockPermissions(),\n settingsRepository: createMockSettingsRepository(),\n tagsRepository: createMockTagsRepository(),\n fileUploader: createMockFileUploader(),\n localStorage: createMockLocalStorage(),\n listFilesUseCase: createMockListFilesUseCase(),\n cache: new ListCache<FmFile>(),\n getDescendantFoldersUseCase: createMockGetDescendantFoldersUseCase(),\n fileModelProvider: {\n getModel: vi.fn().mockResolvedValue({ fields: [] })\n }\n };\n}\n\nfunction createContainer(mocks: Mocks) {\n const container = new Container();\n\n container.registerInstance(ListPresenter, mocks.listPresenter);\n container.registerInstance(FolderTreePresenter, mocks.folderTreePresenter);\n container.registerInstance(FileDetailsPresenter, mocks.fileDetailsPresenter);\n container.registerInstance(FileManagerPermissions, mocks.permissions);\n container.registerInstance(GetSettingsRepository, mocks.settingsRepository);\n container.registerInstance(ListTagsRepository, mocks.tagsRepository);\n container.registerInstance(FileUploader, mocks.fileUploader);\n container.registerInstance(LocalStorage, mocks.localStorage);\n container.registerInstance(ListFilesUseCase, mocks.listFilesUseCase);\n container.registerInstance(FilesListCache, mocks.cache);\n container.registerInstance(GetDescendantFoldersUseCase, mocks.getDescendantFoldersUseCase);\n container.registerInstance(FileModelProvider, mocks.fileModelProvider);\n\n // Register the real FileListPresenter implementation.\n container.register(FileManagerPresenter).inSingletonScope();\n\n return container;\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileListPresenter\", () => {\n let mocks: Mocks;\n let presenter: IFileManagerPresenter;\n\n beforeEach(() => {\n mocks = createMocks();\n const container = createContainer(mocks);\n presenter = container.resolve(Abstraction);\n });\n\n // -----------------------------------------------------------------------\n // Composition: ListPresenter and FolderTreePresenter initialized.\n // -----------------------------------------------------------------------\n\n it(\"should call listPresenter.init with a DataSource and initialSort on init()\", () => {\n presenter.init();\n\n expect(mocks.listPresenter.init).toHaveBeenCalledTimes(1);\n const config = (mocks.listPresenter.init as ReturnType<typeof vi.fn>).mock.calls[0][0];\n expect(config.dataSource).toBeDefined();\n expect(config.initialSort).toEqual({ field: \"createdOn\", direction: \"DESC\" });\n });\n\n it(\"should forward ListPresenter.vm data as vm.list\", () => {\n presenter.init();\n expect(presenter.vm.list.rows).toEqual(mocks.listPresenter.vm.rows);\n expect(presenter.vm.list.sort).toEqual(mocks.listPresenter.vm.sort);\n expect(presenter.vm.list.search).toBe(mocks.listPresenter.vm.search);\n expect(presenter.vm.list.empty).toBe(mocks.listPresenter.vm.empty);\n });\n\n it(\"should forward FolderTreePresenter.vm as vm.folders\", () => {\n presenter.init();\n expect(presenter.vm.folders).toBe(mocks.folderTreePresenter.vm);\n });\n\n // -----------------------------------------------------------------------\n // Folder change wiring.\n // -----------------------------------------------------------------------\n\n it(\"should set folderId filter when folder changes\", async () => {\n presenter.init();\n\n // Simulate folder selection via MobX observable change.\n runInAction(() => {\n (mocks.folderTreePresenter.vm as any).currentFolderId = \"folder-abc\";\n });\n\n // MobX reaction fires synchronously in the same tick after runInAction.\n expect(mocks.listPresenter.actions.filter.set).toHaveBeenCalledWith(\n \"folderId\",\n \"folder-abc\"\n );\n });\n\n it(\"should set folderId to root when folder is set to null\", () => {\n presenter.init();\n\n // First set a folder.\n runInAction(() => {\n (mocks.folderTreePresenter.vm as any).currentFolderId = \"folder-1\";\n });\n\n // Then clear it.\n runInAction(() => {\n (mocks.folderTreePresenter.vm as any).currentFolderId = null;\n });\n\n expect(mocks.listPresenter.actions.filter.set).toHaveBeenCalledWith(\"folderId\", \"root\");\n });\n\n // -----------------------------------------------------------------------\n // Permission flags forwarded to vm.\n // -----------------------------------------------------------------------\n\n it(\"should expose permission flags in vm.permissions\", () => {\n presenter.init();\n\n expect(presenter.vm.permissions.canRead).toBe(true);\n expect(presenter.vm.permissions.canCreate).toBe(true);\n expect(presenter.vm.permissions.canEdit).toBe(false);\n expect(presenter.vm.permissions.canDelete).toBe(false);\n });\n\n // -----------------------------------------------------------------------\n // View mode persistence via LocalStorage.\n // -----------------------------------------------------------------------\n\n it(\"should default viewMode to 'grid'\", () => {\n expect(presenter.vm.viewMode).toBe(\"grid\");\n });\n\n it(\"should update viewMode and persist to localStorage\", () => {\n presenter.actions.setViewMode(\"grid\");\n\n expect(presenter.vm.viewMode).toBe(\"grid\");\n expect(mocks.localStorage.set).toHaveBeenCalledWith(\"fm:viewMode\", \"grid\");\n });\n\n it(\"should restore viewMode from localStorage on construction\", () => {\n // Pre-seed localStorage with \"grid\" before creating the presenter.\n const seededMocks = createMocks();\n (seededMocks.localStorage.get as ReturnType<typeof vi.fn>).mockReturnValue(\"grid\");\n\n const container = createContainer(seededMocks);\n const seededPresenter: IFileManagerPresenter = container.resolve(Abstraction);\n\n expect(seededPresenter.vm.viewMode).toBe(\"grid\");\n });\n\n // -----------------------------------------------------------------------\n // Upload action.\n // -----------------------------------------------------------------------\n\n it(\"should call fileUploader.uploadMany when actions.upload is called\", async () => {\n presenter.init();\n\n const file1 = {\n id: \"f1\",\n name: \"test.txt\",\n type: \"text/plain\",\n size: 7,\n src: { file: new File([\"content\"], \"test.txt\", { type: \"text/plain\" }), base64: null }\n };\n const file2 = {\n id: \"f2\",\n name: \"photo.png\",\n type: \"image/png\",\n size: 3,\n src: { file: new File([\"img\"], \"photo.png\", { type: \"image/png\" }), base64: null }\n };\n\n await presenter.actions.upload([file1, file2]);\n\n expect(mocks.fileUploader.uploadMany).toHaveBeenCalledTimes(1);\n const args = (mocks.fileUploader.uploadMany as ReturnType<typeof vi.fn>).mock.calls[0][0];\n expect(args).toHaveLength(2);\n expect(args[0].data.name).toBe(\"test.txt\");\n expect(args[0].data.type).toBe(\"text/plain\");\n expect(args[1].data.name).toBe(\"photo.png\");\n expect(args[1].data.type).toBe(\"image/png\");\n });\n\n // -----------------------------------------------------------------------\n // loading: true only during initial load (no rows yet).\n // -----------------------------------------------------------------------\n\n it(\"should be loading when pagination is loading and no rows exist\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).rows = [];\n (mocks.listPresenter.vm as any).pagination.loading = true;\n });\n expect(presenter.vm.loading).toBe(true);\n });\n\n it(\"should not be loading when rows already exist\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).rows = [{ id: \"file-1\" }];\n (mocks.listPresenter.vm as any).pagination.loading = true;\n });\n expect(presenter.vm.loading).toBe(false);\n });\n\n it(\"should not be loading when pagination is not loading\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).rows = [];\n (mocks.listPresenter.vm as any).pagination.loading = false;\n });\n expect(presenter.vm.loading).toBe(false);\n });\n\n // -----------------------------------------------------------------------\n // empty: accounts for both files and child folders.\n // -----------------------------------------------------------------------\n\n it(\"should be empty when no files and no child folders\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).rows = [];\n (mocks.listPresenter.vm as any).pagination.loading = false;\n });\n expect(presenter.vm.empty).toBe(true);\n });\n\n it(\"should not be empty when there are files\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).rows = [{ id: \"file-1\" }];\n (mocks.listPresenter.vm as any).pagination.loading = false;\n });\n expect(presenter.vm.empty).toBe(false);\n });\n\n it(\"should not be empty when there are child folders but no files\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).rows = [];\n (mocks.listPresenter.vm as any).pagination.loading = false;\n (mocks.folderTreePresenter.vm as any).childFolders = [{ id: \"folder-1\" }];\n });\n expect(presenter.vm.empty).toBe(false);\n });\n\n it(\"should not be empty while loading\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).rows = [];\n (mocks.listPresenter.vm as any).pagination.loading = true;\n });\n expect(presenter.vm.empty).toBe(false);\n });\n\n it(\"should be empty when child folders exist but folders are hidden (search active)\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).rows = [];\n (mocks.listPresenter.vm as any).pagination.loading = false;\n (mocks.listPresenter.vm as any).appliedQuery = {\n search: \"photo\",\n filters: { folderId: \"root\" }\n };\n (mocks.folderTreePresenter.vm as any).childFolders = [{ id: \"folder-1\" }];\n });\n expect(presenter.vm.empty).toBe(true);\n });\n\n // -----------------------------------------------------------------------\n // showFolders computed from appliedQuery.\n // -----------------------------------------------------------------------\n\n it(\"should show folders when appliedQuery is null\", () => {\n presenter.init();\n expect(presenter.vm.showFolders).toBe(true);\n });\n\n it(\"should show folders when appliedQuery has no search and only folderId filter\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n filters: { folderId: \"root\" }\n };\n });\n expect(presenter.vm.showFolders).toBe(true);\n });\n\n it(\"should hide folders when appliedQuery has a search\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n search: \"photo\",\n filters: { folderId: \"root\" }\n };\n });\n expect(presenter.vm.showFolders).toBe(false);\n });\n\n it(\"should hide folders when appliedQuery has non-folderId filters\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n filters: { folderId: \"root\", tags: [\"photo\"] }\n };\n });\n expect(presenter.vm.showFolders).toBe(false);\n });\n\n it(\"should show folders again when appliedQuery filters are cleared\", () => {\n presenter.init();\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n filters: { folderId: \"root\", type: \"image\" }\n };\n });\n expect(presenter.vm.showFolders).toBe(false);\n\n runInAction(() => {\n (mocks.listPresenter.vm as any).appliedQuery = {\n filters: { folderId: \"root\" }\n };\n });\n expect(presenter.vm.showFolders).toBe(true);\n });\n});\n"],"names":["createMockListPresenter","vmState","observable","Set","vi","undefined","createMockFolderTreePresenter","state","createMockPermissions","createMockSettingsRepository","data","createMockTagsRepository","createMockFileUploader","createMockLocalStorage","store","Map","key","value","Array","createMockListFilesUseCase","createMockGetDescendantFoldersUseCase","createMockFileDetailsPresenter","createMocks","ListCache","createContainer","mocks","container","Container","ListPresenter","FolderTreePresenter","FileDetailsPresenter","FileManagerPermissions","GetSettingsRepository","ListTagsRepository","FileUploader","LocalStorage","ListFilesUseCase","FilesListCache","GetDescendantFoldersUseCase","FileModelProvider","FileManagerPresenter","describe","presenter","beforeEach","Abstraction","it","expect","config","runInAction","seededMocks","seededPresenter","file1","File","file2","args"],"mappings":"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"}
@@ -35,6 +35,8 @@ declare class FileManagerPresenterImpl implements IFileManagerPresenter {
35
35
  actions: IFileManagerActions;
36
36
  init(config?: IFileManagerInitConfig): void;
37
37
  dispose(): void;
38
+ private isLoading;
39
+ private isEmpty;
38
40
  private shouldShowFolders;
39
41
  }
40
42
  export declare const FileManagerPresenter: typeof FileManagerPresenterImpl & {
@@ -143,6 +143,8 @@ class FileManagerPresenterImpl {
143
143
  isUploading: this.fileUploader.vm.isUploading
144
144
  },
145
145
  tags: this.tagsRepository.tags,
146
+ loading: this.isLoading(),
147
+ empty: this.isEmpty(),
146
148
  showFolders: this.shouldShowFolders(),
147
149
  viewMode: this._viewMode,
148
150
  dragging: this._dragging,
@@ -183,6 +185,15 @@ class FileManagerPresenterImpl {
183
185
  this._disposeReaction = null;
184
186
  }
185
187
  }
188
+ isLoading() {
189
+ return this.listPresenter.vm.pagination.loading && 0 === this.listPresenter.vm.rows.length;
190
+ }
191
+ isEmpty() {
192
+ if (this.listPresenter.vm.pagination.loading) return false;
193
+ if (this.listPresenter.vm.rows.length > 0) return false;
194
+ if (this.shouldShowFolders() && this.folderTreePresenter.vm.childFolders.length > 0) return false;
195
+ return true;
196
+ }
186
197
  shouldShowFolders() {
187
198
  const { appliedQuery } = this.listPresenter.vm;
188
199
  if (!appliedQuery) return true;
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileList/FileManagerPresenter.js","sources":["../../../src/presentation/FileList/FileManagerPresenter.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction, computed } from \"mobx\";\nimport type { FmFile } from \"@webiny/sdk\";\nimport type { CmsModel } from \"@webiny/app-headless-cms-common/types/index.js\";\nimport { ListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { LocalStorage } from \"@webiny/app/features/localStorage\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport {\n FileManagerPresenter as Abstraction,\n type IFileManagerPresenter,\n type IFileManagerViewModel,\n type IFileManagerActions,\n type IFileManagerInitConfig\n} from \"./abstractions.js\";\nimport { FileListDataSource } from \"./FileListDataSource.js\";\nimport { FileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport { FileManagerPermissions } from \"~/features/permissions/abstractions.js\";\nimport { ListTagsRepository } from \"~/features/tags/index.js\";\nimport { FileUploader } from \"~/features/fileUploader/index.js\";\nimport { ListFilesUseCase } from \"~/features/listFiles/index.js\";\nimport { FilesListCache } from \"~/features/shared/index.js\";\nimport { FileModelProvider } from \"~/features/fileModel/index.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { SelectedFile } from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\n\nconst VIEW_MODE_KEY = \"fm:viewMode\";\nconst LAST_FOLDER_KEY = \"fm:lastFolder\";\n\nclass FileManagerPresenterImpl implements IFileManagerPresenter {\n private _viewMode: \"table\" | \"grid\" = \"grid\";\n private _dragging = false;\n private _disposeReaction: (() => void) | null = null;\n private _fileDetails: IFileDetailsPresenter | null = null;\n private _showingFilters = false;\n private _fileModel: CmsModel | null = null;\n private _persistFolder = true;\n\n constructor(\n private listPresenter: ListPresenter.Interface<FmFile>,\n private folderTreePresenter: FolderTreePresenter.Interface,\n private fileDetailsPresenter: FileDetailsPresenter.Interface,\n private permissions: FileManagerPermissions.Interface,\n private tagsRepository: ListTagsRepository.Interface,\n private fileUploader: FileUploader.Interface,\n private localStorage: LocalStorage.Interface,\n private listFilesUseCase: ListFilesUseCase.Interface,\n private filesListCache: FilesListCache.Interface,\n private getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface,\n private fileModelProvider: FileModelProvider.Interface\n ) {\n makeAutoObservable<\n FileManagerPresenterImpl,\n \"_disposeReaction\" | \"fileDetailsPresenter\" | \"_persistFolder\"\n >(this, {\n _disposeReaction: false,\n fileDetailsPresenter: false,\n _persistFolder: false,\n vm: computed\n });\n\n const stored = this.localStorage.get<\"table\" | \"grid\">(VIEW_MODE_KEY);\n if (stored === \"table\" || stored === \"grid\") {\n this._viewMode = stored;\n }\n }\n\n get vm(): IFileManagerViewModel {\n return {\n fileModel: this._fileModel,\n list: this.listPresenter.vm,\n folders: this.folderTreePresenter.vm,\n fileDetails: this._fileDetails,\n // Flat booleans for UI visibility (e.g., show/hide buttons).\n // Per-file methods for ownership-aware checks (e.g., \"own\" scope).\n permissions: {\n canRead: this.permissions.canRead(\"file\"),\n canCreate: this.permissions.canCreate(\"file\"),\n canEdit: this.permissions.canEdit(\"file\"),\n canDelete: this.permissions.canDelete(\"file\"),\n canEditFile: (file: FmFile) => this.permissions.canEdit(\"file\", file),\n canDeleteFile: (file: FmFile) => this.permissions.canDelete(\"file\", file)\n },\n upload: {\n jobs: this.fileUploader.vm.jobs,\n overallProgress: {\n percentage: this.fileUploader.vm.overallProgress.percentage,\n bytesSent: this.fileUploader.vm.overallProgress.sent,\n totalBytes: this.fileUploader.vm.overallProgress.total\n },\n isUploading: this.fileUploader.vm.isUploading\n },\n tags: this.tagsRepository.tags,\n showFolders: this.shouldShowFolders(),\n viewMode: this._viewMode,\n dragging: this._dragging,\n showingFilters: this._showingFilters\n };\n }\n\n actions: IFileManagerActions = {\n search: {\n set: (query: string) => this.listPresenter.actions.search.set(query),\n clear: () => this.listPresenter.actions.search.clear()\n },\n sort: {\n set: (field: string, direction: \"ASC\" | \"DESC\") =>\n this.listPresenter.actions.sort.set(field, direction),\n toggle: (field: string) => this.listPresenter.actions.sort.toggle(field)\n },\n filter: {\n set: (key: string, value: unknown) => this.listPresenter.actions.filter.set(key, value),\n clear: (key: string) => this.listPresenter.actions.filter.clear(key),\n clearAll: () => this.listPresenter.actions.filter.clearAll()\n },\n selection: {\n toggle: (id: string) => this.listPresenter.actions.selection.toggle(id),\n selectRangeTo: (id: string) => this.listPresenter.actions.selection.selectRangeTo(id),\n selectAll: () => this.listPresenter.actions.selection.selectAll(),\n deselectAll: () => this.listPresenter.actions.selection.deselectAll(),\n selectRows: (ids: string[]) => this.listPresenter.actions.selection.selectRows(ids),\n isSelected: (id: string) => this.listPresenter.actions.selection.isSelected(id)\n },\n loadMore: () => this.listPresenter.actions.loadMore(),\n refresh: () => this.listPresenter.actions.refresh(),\n upload: async (files: SelectedFile[]) => {\n const folderId = this.folderTreePresenter.vm.currentFolderId ?? \"root\";\n await this.fileUploader.uploadMany(\n files.map(file => ({\n file: file.src.file,\n data: {\n name: file.name,\n type: file.type,\n location: { folderId }\n }\n }))\n );\n this.fileUploader.clear();\n },\n setViewMode: (mode: \"table\" | \"grid\") => {\n this._viewMode = mode;\n this.localStorage.set(VIEW_MODE_KEY, mode);\n },\n setDragging: (dragging: boolean) => {\n this._dragging = dragging;\n },\n showFilters: () => {\n this._showingFilters = true;\n },\n hideFilters: () => {\n this._showingFilters = false;\n },\n showFileDetails: (id: string) => {\n this._fileDetails = this.fileDetailsPresenter;\n void this.fileDetailsPresenter.loadFile(id);\n },\n hideFileDetails: () => {\n this._fileDetails = null;\n },\n folders: {\n // Clear search when navigating to a folder — gives the user a clean slate.\n selectFolder: (folderId: string | null) => {\n this.listPresenter.actions.search.clear();\n this.folderTreePresenter.selectFolder(folderId);\n },\n createFolder: (parentFolderId?: string) =>\n this.folderTreePresenter.createFolder(parentFolderId),\n editFolder: (folderId: string) => this.folderTreePresenter.editFolder(folderId),\n deleteFolder: (folderId: string) => this.folderTreePresenter.deleteFolder(folderId),\n moveFolder: (folderId: string, targetParentId: string | null) =>\n this.folderTreePresenter.moveFolder(folderId, targetParentId),\n loadChildFolders: (parentIds: string[]) =>\n this.folderTreePresenter.loadChildFolders(parentIds),\n canManageStructure: (folderId: string) =>\n this.folderTreePresenter.canManageStructure(folderId),\n getAncestorIds: (folderId: string) => this.folderTreePresenter.getAncestorIds(folderId),\n submitOperation: () => this.folderTreePresenter.submitOperation(),\n cancelOperation: () => {\n this.folderTreePresenter.cancelOperation();\n }\n }\n };\n\n init(config?: IFileManagerInitConfig): void {\n const initialFolderId =\n config?.initialFolderId ?? this.localStorage.get<string>(LAST_FOLDER_KEY) ?? \"root\";\n\n // Don't persist folder navigation when an explicit initial folder is provided (e.g., overlay).\n this._persistFolder = !config?.initialFolderId;\n\n const dataSource = new FileListDataSource(\n this.listFilesUseCase,\n this.filesListCache,\n this.getDescendantFoldersUseCase,\n config?.scope\n );\n\n this.listPresenter.init({\n dataSource,\n initialSort: { field: \"createdOn\", direction: \"DESC\" },\n initialFilters: { folderId: initialFolderId },\n limit: 50\n });\n\n if (initialFolderId !== \"root\") {\n this.folderTreePresenter.selectFolder(initialFolderId);\n }\n\n // Sync folder navigation → file list: when the user selects a folder in the tree,\n // update the folderId filter so the file list re-queries for that folder's contents.\n this._disposeReaction = reaction(\n () => this.folderTreePresenter.vm.currentFolderId,\n folderId => {\n const effectiveFolderId = folderId ?? \"root\";\n this.listPresenter.actions.filter.set(\"folderId\", effectiveFolderId);\n\n if (this._persistFolder) {\n this.localStorage.set(LAST_FOLDER_KEY, effectiveFolderId);\n }\n }\n );\n\n void this.tagsRepository.execute({});\n\n // Load the CMS model in the background. The FileModelModule already kicks off\n // loading at app startup, so this usually resolves from cache instantly.\n // The view shows a loading overlay until `_fileModel` is set.\n void this.fileModelProvider.getModel().then(model => {\n runInAction(() => {\n this._fileModel = model;\n });\n });\n }\n\n dispose(): void {\n if (this._disposeReaction) {\n this._disposeReaction();\n this._disposeReaction = null;\n }\n }\n\n // Hide folders when the user is actively filtering or searching.\n // Uses `appliedQuery` (not `vm.search`) so folders stay visible while typing\n // and only disappear once the debounced query has actually executed.\n private shouldShowFolders(): boolean {\n const { appliedQuery } = this.listPresenter.vm;\n if (!appliedQuery) {\n return true;\n }\n\n if (appliedQuery.search) {\n return false;\n }\n\n // `folderId` is always present as a default filter; ignore it.\n const filterKeys = Object.keys(appliedQuery.filters ?? {}).filter(k => k !== \"folderId\");\n if (filterKeys.length > 0) {\n return false;\n }\n\n return true;\n }\n}\n\nexport const FileManagerPresenter = Abstraction.createImplementation({\n implementation: FileManagerPresenterImpl,\n dependencies: [\n ListPresenter,\n FolderTreePresenter,\n FileDetailsPresenter,\n FileManagerPermissions,\n ListTagsRepository,\n FileUploader,\n LocalStorage,\n ListFilesUseCase,\n FilesListCache,\n GetDescendantFoldersUseCase,\n FileModelProvider\n ]\n});\n"],"names":["VIEW_MODE_KEY","LAST_FOLDER_KEY","FileManagerPresenterImpl","listPresenter","folderTreePresenter","fileDetailsPresenter","permissions","tagsRepository","fileUploader","localStorage","listFilesUseCase","filesListCache","getDescendantFoldersUseCase","fileModelProvider","query","field","direction","key","value","id","ids","files","folderId","file","mode","dragging","parentFolderId","targetParentId","parentIds","makeAutoObservable","computed","stored","config","initialFolderId","dataSource","FileListDataSource","reaction","effectiveFolderId","model","runInAction","appliedQuery","filterKeys","Object","k","FileManagerPresenter","Abstraction","ListPresenter","FolderTreePresenter","FileDetailsPresenter","FileManagerPermissions","ListTagsRepository","FileUploader","LocalStorage","ListFilesUseCase","FilesListCache","GetDescendantFoldersUseCase","FileModelProvider"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/FileList/FileManagerPresenter.js","sources":["../../../src/presentation/FileList/FileManagerPresenter.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction, computed } from \"mobx\";\nimport type { FmFile } from \"@webiny/sdk\";\nimport type { CmsModel } from \"@webiny/app-headless-cms-common/types/index.js\";\nimport { ListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { LocalStorage } from \"@webiny/app/features/localStorage\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport {\n FileManagerPresenter as Abstraction,\n type IFileManagerPresenter,\n type IFileManagerViewModel,\n type IFileManagerActions,\n type IFileManagerInitConfig\n} from \"./abstractions.js\";\nimport { FileListDataSource } from \"./FileListDataSource.js\";\nimport { FileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport { FileManagerPermissions } from \"~/features/permissions/abstractions.js\";\nimport { ListTagsRepository } from \"~/features/tags/index.js\";\nimport { FileUploader } from \"~/features/fileUploader/index.js\";\nimport { ListFilesUseCase } from \"~/features/listFiles/index.js\";\nimport { FilesListCache } from \"~/features/shared/index.js\";\nimport { FileModelProvider } from \"~/features/fileModel/index.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { SelectedFile } from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\n\nconst VIEW_MODE_KEY = \"fm:viewMode\";\nconst LAST_FOLDER_KEY = \"fm:lastFolder\";\n\nclass FileManagerPresenterImpl implements IFileManagerPresenter {\n private _viewMode: \"table\" | \"grid\" = \"grid\";\n private _dragging = false;\n private _disposeReaction: (() => void) | null = null;\n private _fileDetails: IFileDetailsPresenter | null = null;\n private _showingFilters = false;\n private _fileModel: CmsModel | null = null;\n private _persistFolder = true;\n\n constructor(\n private listPresenter: ListPresenter.Interface<FmFile>,\n private folderTreePresenter: FolderTreePresenter.Interface,\n private fileDetailsPresenter: FileDetailsPresenter.Interface,\n private permissions: FileManagerPermissions.Interface,\n private tagsRepository: ListTagsRepository.Interface,\n private fileUploader: FileUploader.Interface,\n private localStorage: LocalStorage.Interface,\n private listFilesUseCase: ListFilesUseCase.Interface,\n private filesListCache: FilesListCache.Interface,\n private getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface,\n private fileModelProvider: FileModelProvider.Interface\n ) {\n makeAutoObservable<\n FileManagerPresenterImpl,\n \"_disposeReaction\" | \"fileDetailsPresenter\" | \"_persistFolder\"\n >(this, {\n _disposeReaction: false,\n fileDetailsPresenter: false,\n _persistFolder: false,\n vm: computed\n });\n\n const stored = this.localStorage.get<\"table\" | \"grid\">(VIEW_MODE_KEY);\n if (stored === \"table\" || stored === \"grid\") {\n this._viewMode = stored;\n }\n }\n\n get vm(): IFileManagerViewModel {\n return {\n fileModel: this._fileModel,\n list: this.listPresenter.vm,\n folders: this.folderTreePresenter.vm,\n fileDetails: this._fileDetails,\n // Flat booleans for UI visibility (e.g., show/hide buttons).\n // Per-file methods for ownership-aware checks (e.g., \"own\" scope).\n permissions: {\n canRead: this.permissions.canRead(\"file\"),\n canCreate: this.permissions.canCreate(\"file\"),\n canEdit: this.permissions.canEdit(\"file\"),\n canDelete: this.permissions.canDelete(\"file\"),\n canEditFile: (file: FmFile) => this.permissions.canEdit(\"file\", file),\n canDeleteFile: (file: FmFile) => this.permissions.canDelete(\"file\", file)\n },\n upload: {\n jobs: this.fileUploader.vm.jobs,\n overallProgress: {\n percentage: this.fileUploader.vm.overallProgress.percentage,\n bytesSent: this.fileUploader.vm.overallProgress.sent,\n totalBytes: this.fileUploader.vm.overallProgress.total\n },\n isUploading: this.fileUploader.vm.isUploading\n },\n tags: this.tagsRepository.tags,\n loading: this.isLoading(),\n empty: this.isEmpty(),\n showFolders: this.shouldShowFolders(),\n viewMode: this._viewMode,\n dragging: this._dragging,\n showingFilters: this._showingFilters\n };\n }\n\n actions: IFileManagerActions = {\n search: {\n set: (query: string) => this.listPresenter.actions.search.set(query),\n clear: () => this.listPresenter.actions.search.clear()\n },\n sort: {\n set: (field: string, direction: \"ASC\" | \"DESC\") =>\n this.listPresenter.actions.sort.set(field, direction),\n toggle: (field: string) => this.listPresenter.actions.sort.toggle(field)\n },\n filter: {\n set: (key: string, value: unknown) => this.listPresenter.actions.filter.set(key, value),\n clear: (key: string) => this.listPresenter.actions.filter.clear(key),\n clearAll: () => this.listPresenter.actions.filter.clearAll()\n },\n selection: {\n toggle: (id: string) => this.listPresenter.actions.selection.toggle(id),\n selectRangeTo: (id: string) => this.listPresenter.actions.selection.selectRangeTo(id),\n selectAll: () => this.listPresenter.actions.selection.selectAll(),\n deselectAll: () => this.listPresenter.actions.selection.deselectAll(),\n selectRows: (ids: string[]) => this.listPresenter.actions.selection.selectRows(ids),\n isSelected: (id: string) => this.listPresenter.actions.selection.isSelected(id)\n },\n loadMore: () => this.listPresenter.actions.loadMore(),\n refresh: () => this.listPresenter.actions.refresh(),\n upload: async (files: SelectedFile[]) => {\n const folderId = this.folderTreePresenter.vm.currentFolderId ?? \"root\";\n await this.fileUploader.uploadMany(\n files.map(file => ({\n file: file.src.file,\n data: {\n name: file.name,\n type: file.type,\n location: { folderId }\n }\n }))\n );\n this.fileUploader.clear();\n },\n setViewMode: (mode: \"table\" | \"grid\") => {\n this._viewMode = mode;\n this.localStorage.set(VIEW_MODE_KEY, mode);\n },\n setDragging: (dragging: boolean) => {\n this._dragging = dragging;\n },\n showFilters: () => {\n this._showingFilters = true;\n },\n hideFilters: () => {\n this._showingFilters = false;\n },\n showFileDetails: (id: string) => {\n this._fileDetails = this.fileDetailsPresenter;\n void this.fileDetailsPresenter.loadFile(id);\n },\n hideFileDetails: () => {\n this._fileDetails = null;\n },\n folders: {\n // Clear search when navigating to a folder — gives the user a clean slate.\n selectFolder: (folderId: string | null) => {\n this.listPresenter.actions.search.clear();\n this.folderTreePresenter.selectFolder(folderId);\n },\n createFolder: (parentFolderId?: string) =>\n this.folderTreePresenter.createFolder(parentFolderId),\n editFolder: (folderId: string) => this.folderTreePresenter.editFolder(folderId),\n deleteFolder: (folderId: string) => this.folderTreePresenter.deleteFolder(folderId),\n moveFolder: (folderId: string, targetParentId: string | null) =>\n this.folderTreePresenter.moveFolder(folderId, targetParentId),\n loadChildFolders: (parentIds: string[]) =>\n this.folderTreePresenter.loadChildFolders(parentIds),\n canManageStructure: (folderId: string) =>\n this.folderTreePresenter.canManageStructure(folderId),\n getAncestorIds: (folderId: string) => this.folderTreePresenter.getAncestorIds(folderId),\n submitOperation: () => this.folderTreePresenter.submitOperation(),\n cancelOperation: () => {\n this.folderTreePresenter.cancelOperation();\n }\n }\n };\n\n init(config?: IFileManagerInitConfig): void {\n const initialFolderId =\n config?.initialFolderId ?? this.localStorage.get<string>(LAST_FOLDER_KEY) ?? \"root\";\n\n // Don't persist folder navigation when an explicit initial folder is provided (e.g., overlay).\n this._persistFolder = !config?.initialFolderId;\n\n const dataSource = new FileListDataSource(\n this.listFilesUseCase,\n this.filesListCache,\n this.getDescendantFoldersUseCase,\n config?.scope\n );\n\n this.listPresenter.init({\n dataSource,\n initialSort: { field: \"createdOn\", direction: \"DESC\" },\n initialFilters: { folderId: initialFolderId },\n limit: 50\n });\n\n if (initialFolderId !== \"root\") {\n this.folderTreePresenter.selectFolder(initialFolderId);\n }\n\n // Sync folder navigation → file list: when the user selects a folder in the tree,\n // update the folderId filter so the file list re-queries for that folder's contents.\n this._disposeReaction = reaction(\n () => this.folderTreePresenter.vm.currentFolderId,\n folderId => {\n const effectiveFolderId = folderId ?? \"root\";\n this.listPresenter.actions.filter.set(\"folderId\", effectiveFolderId);\n\n if (this._persistFolder) {\n this.localStorage.set(LAST_FOLDER_KEY, effectiveFolderId);\n }\n }\n );\n\n void this.tagsRepository.execute({});\n\n // Load the CMS model in the background. The FileModelModule already kicks off\n // loading at app startup, so this usually resolves from cache instantly.\n // The view shows a loading overlay until `_fileModel` is set.\n void this.fileModelProvider.getModel().then(model => {\n runInAction(() => {\n this._fileModel = model;\n });\n });\n }\n\n dispose(): void {\n if (this._disposeReaction) {\n this._disposeReaction();\n this._disposeReaction = null;\n }\n }\n\n private isLoading(): boolean {\n return this.listPresenter.vm.pagination.loading && this.listPresenter.vm.rows.length === 0;\n }\n\n private isEmpty(): boolean {\n if (this.listPresenter.vm.pagination.loading) {\n return false;\n }\n\n if (this.listPresenter.vm.rows.length > 0) {\n return false;\n }\n\n if (this.shouldShowFolders() && this.folderTreePresenter.vm.childFolders.length > 0) {\n return false;\n }\n\n return true;\n }\n\n // Hide folders when the user is actively filtering or searching.\n // Uses `appliedQuery` (not `vm.search`) so folders stay visible while typing\n // and only disappear once the debounced query has actually executed.\n private shouldShowFolders(): boolean {\n const { appliedQuery } = this.listPresenter.vm;\n if (!appliedQuery) {\n return true;\n }\n\n if (appliedQuery.search) {\n return false;\n }\n\n // `folderId` is always present as a default filter; ignore it.\n const filterKeys = Object.keys(appliedQuery.filters ?? {}).filter(k => k !== \"folderId\");\n if (filterKeys.length > 0) {\n return false;\n }\n\n return true;\n }\n}\n\nexport const FileManagerPresenter = Abstraction.createImplementation({\n implementation: FileManagerPresenterImpl,\n dependencies: [\n ListPresenter,\n FolderTreePresenter,\n FileDetailsPresenter,\n FileManagerPermissions,\n ListTagsRepository,\n FileUploader,\n LocalStorage,\n ListFilesUseCase,\n FilesListCache,\n GetDescendantFoldersUseCase,\n FileModelProvider\n ]\n});\n"],"names":["VIEW_MODE_KEY","LAST_FOLDER_KEY","FileManagerPresenterImpl","listPresenter","folderTreePresenter","fileDetailsPresenter","permissions","tagsRepository","fileUploader","localStorage","listFilesUseCase","filesListCache","getDescendantFoldersUseCase","fileModelProvider","query","field","direction","key","value","id","ids","files","folderId","file","mode","dragging","parentFolderId","targetParentId","parentIds","makeAutoObservable","computed","stored","config","initialFolderId","dataSource","FileListDataSource","reaction","effectiveFolderId","model","runInAction","appliedQuery","filterKeys","Object","k","FileManagerPresenter","Abstraction","ListPresenter","FolderTreePresenter","FileDetailsPresenter","FileManagerPermissions","ListTagsRepository","FileUploader","LocalStorage","ListFilesUseCase","FilesListCache","GetDescendantFoldersUseCase","FileModelProvider"],"mappings":"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"}
@@ -26,6 +26,8 @@ export interface IFileManagerViewModel {
26
26
  };
27
27
  isUploading: boolean;
28
28
  };
29
+ loading: boolean;
30
+ empty: boolean;
29
31
  showFolders: boolean;
30
32
  viewMode: "table" | "grid";
31
33
  dragging: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileList/abstractions.js","sources":["../../../src/presentation/FileList/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { SelectedFile } from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\nimport type { CmsModel } from \"@webiny/app-headless-cms-common/types/index.js\";\nimport type { IListViewModel } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IListActions } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IFolderTreeViewModel } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\nimport type { FmTag } from \"../../features/shared/types.js\";\nimport type { UploadJob } from \"../../features/fileUploader/abstractions.js\";\n\n// ---------------------------------------------------------------------------\n// Init config passed to presenter.init().\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerInitConfig {\n initialFolderId?: string;\n scope?: string;\n}\n\n// ---------------------------------------------------------------------------\n// FileManagerViewModel\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerViewModel {\n fileModel: CmsModel | null;\n list: IListViewModel<FmFile>;\n folders: IFolderTreeViewModel;\n fileDetails: IFileDetailsPresenter | null;\n tags: FmTag[];\n upload: {\n jobs: UploadJob[];\n overallProgress: { percentage: number; bytesSent: number; totalBytes: number };\n isUploading: boolean;\n };\n showFolders: boolean;\n viewMode: \"table\" | \"grid\";\n dragging: boolean;\n showingFilters: boolean;\n permissions: {\n canRead: boolean;\n canCreate: boolean;\n canEdit: boolean;\n canDelete: boolean;\n canEditFile: (file: FmFile) => boolean;\n canDeleteFile: (file: FmFile) => boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Folder actions exposed by the FileManagerPresenter.\n// ---------------------------------------------------------------------------\n\nexport interface IFolderActions {\n selectFolder(folderId: string | null): void;\n createFolder(parentFolderId?: string): void;\n editFolder(folderId: string): void;\n deleteFolder(folderId: string): Promise<void>;\n moveFolder(folderId: string, targetParentId: string | null): Promise<void>;\n loadChildFolders(parentIds: string[]): Promise<void>;\n canManageStructure(folderId: string): boolean;\n getAncestorIds(folderId: string): string[];\n submitOperation(): Promise<boolean>;\n cancelOperation(): void;\n}\n\n// ---------------------------------------------------------------------------\n// FileManagerPresenter actions (extends ListActions with domain-specific actions).\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerActions extends IListActions {\n showFileDetails(id: string): void;\n hideFileDetails(): void;\n upload(files: SelectedFile[]): Promise<void>;\n setViewMode(mode: \"table\" | \"grid\"): void;\n setDragging(dragging: boolean): void;\n showFilters(): void;\n hideFilters(): void;\n folders: IFolderActions;\n}\n\n// ---------------------------------------------------------------------------\n// IFileManagerPresenter\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerPresenter {\n vm: IFileManagerViewModel;\n actions: IFileManagerActions;\n init(config?: IFileManagerInitConfig): void;\n dispose(): void;\n}\n\nexport const FileManagerPresenter =\n createAbstraction<IFileManagerPresenter>(\"FileManagerPresenter\");\n\nexport namespace FileManagerPresenter {\n export type Interface = IFileManagerPresenter;\n export type ViewModel = IFileManagerViewModel;\n export type Actions = IFileManagerActions;\n export type InitConfig = IFileManagerInitConfig;\n}\n"],"names":["FileManagerPresenter","createAbstraction"],"mappings":";AA4FO,MAAMA,uBACTC,kBAAyC"}
1
+ {"version":3,"file":"presentation/FileList/abstractions.js","sources":["../../../src/presentation/FileList/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { SelectedFile } from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\nimport type { CmsModel } from \"@webiny/app-headless-cms-common/types/index.js\";\nimport type { IListViewModel } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IListActions } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IFolderTreeViewModel } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\nimport type { FmTag } from \"../../features/shared/types.js\";\nimport type { UploadJob } from \"../../features/fileUploader/abstractions.js\";\n\n// ---------------------------------------------------------------------------\n// Init config passed to presenter.init().\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerInitConfig {\n initialFolderId?: string;\n scope?: string;\n}\n\n// ---------------------------------------------------------------------------\n// FileManagerViewModel\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerViewModel {\n fileModel: CmsModel | null;\n list: IListViewModel<FmFile>;\n folders: IFolderTreeViewModel;\n fileDetails: IFileDetailsPresenter | null;\n tags: FmTag[];\n upload: {\n jobs: UploadJob[];\n overallProgress: { percentage: number; bytesSent: number; totalBytes: number };\n isUploading: boolean;\n };\n loading: boolean;\n empty: boolean;\n showFolders: boolean;\n viewMode: \"table\" | \"grid\";\n dragging: boolean;\n showingFilters: boolean;\n permissions: {\n canRead: boolean;\n canCreate: boolean;\n canEdit: boolean;\n canDelete: boolean;\n canEditFile: (file: FmFile) => boolean;\n canDeleteFile: (file: FmFile) => boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Folder actions exposed by the FileManagerPresenter.\n// ---------------------------------------------------------------------------\n\nexport interface IFolderActions {\n selectFolder(folderId: string | null): void;\n createFolder(parentFolderId?: string): void;\n editFolder(folderId: string): void;\n deleteFolder(folderId: string): Promise<void>;\n moveFolder(folderId: string, targetParentId: string | null): Promise<void>;\n loadChildFolders(parentIds: string[]): Promise<void>;\n canManageStructure(folderId: string): boolean;\n getAncestorIds(folderId: string): string[];\n submitOperation(): Promise<boolean>;\n cancelOperation(): void;\n}\n\n// ---------------------------------------------------------------------------\n// FileManagerPresenter actions (extends ListActions with domain-specific actions).\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerActions extends IListActions {\n showFileDetails(id: string): void;\n hideFileDetails(): void;\n upload(files: SelectedFile[]): Promise<void>;\n setViewMode(mode: \"table\" | \"grid\"): void;\n setDragging(dragging: boolean): void;\n showFilters(): void;\n hideFilters(): void;\n folders: IFolderActions;\n}\n\n// ---------------------------------------------------------------------------\n// IFileManagerPresenter\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerPresenter {\n vm: IFileManagerViewModel;\n actions: IFileManagerActions;\n init(config?: IFileManagerInitConfig): void;\n dispose(): void;\n}\n\nexport const FileManagerPresenter =\n createAbstraction<IFileManagerPresenter>(\"FileManagerPresenter\");\n\nexport namespace FileManagerPresenter {\n export type Interface = IFileManagerPresenter;\n export type ViewModel = IFileManagerViewModel;\n export type Actions = IFileManagerActions;\n export type InitConfig = IFileManagerInitConfig;\n}\n"],"names":["FileManagerPresenter","createAbstraction"],"mappings":";AA8FO,MAAMA,uBACTC,kBAAyC"}
@@ -114,6 +114,8 @@ function createMockPresenter(vmOverrides = {}) {
114
114
  isUploading: false
115
115
  },
116
116
  tags: [],
117
+ loading: false,
118
+ empty: false,
117
119
  showFolders: true,
118
120
  viewMode: "grid",
119
121
  dragging: false,
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileList/components/Grid/FileGrid.test.js","sources":["../../../../../src/presentation/FileList/components/Grid/FileGrid.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi } from \"vitest\";\nimport { observable } from \"mobx\";\nimport type { IFileManagerPresenter, IFileManagerViewModel } from \"../../abstractions.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\nimport type { FmFile } from \"~/features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Mock factories.\n// ---------------------------------------------------------------------------\n\nfunction createMockFile(overrides: Partial<FmFile> = {}): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 1024,\n metadata: {},\n tags: [],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-02T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n location: { folderId: \"root\" },\n ...overrides\n };\n}\n\nconst emptyIdentity = { id: \"\", displayName: \"\", type: \"\" };\n\nfunction createMockFolderDto(overrides: Partial<FolderDto> = {}): FolderDto {\n return {\n id: \"folder-1\",\n title: \"Documents\",\n slug: \"documents\",\n type: \"\",\n parentId: null,\n path: \"\",\n permissions: [],\n hasNonInheritedPermissions: false,\n canManagePermissions: false,\n canManageStructure: true,\n canManageContent: false,\n createdBy: emptyIdentity,\n createdOn: \"\",\n savedBy: emptyIdentity,\n savedOn: \"\",\n modifiedBy: null,\n modifiedOn: null,\n extensions: {},\n ...overrides\n };\n}\n\nfunction createMockPresenter(\n vmOverrides: Partial<IFileManagerViewModel> = {}\n): IFileManagerPresenter {\n const defaultVm: IFileManagerViewModel = observable({\n fileModel: null,\n list: {\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n appliedQuery: null,\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n error: null\n },\n folders: {\n folders: [],\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders: [],\n loadingNodeIds: []\n },\n permissions: {\n canRead: true,\n canCreate: true,\n canEdit: true,\n canDelete: true,\n canEditFile: () => true,\n canDeleteFile: () => true\n },\n upload: {\n jobs: [],\n overallProgress: { percentage: 0, bytesSent: 0, totalBytes: 0 },\n isUploading: false\n },\n tags: [],\n showFolders: true,\n viewMode: \"grid\" as const,\n dragging: false,\n showingFilters: false,\n isOverlay: false,\n accept: [],\n multiple: false,\n scope: undefined,\n fileDetails: null,\n ...vmOverrides\n });\n\n return {\n vm: defaultVm,\n actions: {\n search: { set: vi.fn(), clear: vi.fn() },\n sort: { set: vi.fn(), toggle: vi.fn() },\n filter: { set: vi.fn(), clear: vi.fn(), clearAll: vi.fn() },\n selection: {\n toggle: vi.fn(),\n selectRangeTo: vi.fn(),\n selectAll: vi.fn(),\n deselectAll: vi.fn(),\n selectRows: vi.fn(),\n isSelected: vi.fn().mockReturnValue(false)\n },\n loadMore: vi.fn().mockResolvedValue(undefined),\n refresh: vi.fn().mockResolvedValue(undefined),\n upload: vi.fn().mockResolvedValue(undefined),\n setViewMode: vi.fn(),\n showFileDetails: vi.fn(),\n hideFileDetails: vi.fn(),\n setDragging: vi.fn(),\n showFilters: vi.fn(),\n hideFilters: vi.fn(),\n folders: {\n selectFolder: vi.fn(),\n createFolder: vi.fn(),\n editFolder: vi.fn(),\n deleteFolder: vi.fn().mockResolvedValue(undefined),\n moveFolder: vi.fn().mockResolvedValue(undefined),\n loadChildFolders: vi.fn().mockResolvedValue(undefined),\n canManageStructure: vi.fn().mockReturnValue(true),\n getAncestorIds: vi.fn().mockReturnValue([]),\n submitOperation: vi.fn().mockResolvedValue(true),\n cancelOperation: vi.fn()\n }\n },\n init: vi.fn(),\n dispose: vi.fn()\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tests — Grid data composition logic.\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileGrid — child folder resolution\", () => {\n it(\"should use vm.folders.childFolders when no current folder is selected\", () => {\n const childFolders = [\n createMockFolderDto({ id: \"f1\", title: \"Docs\" }),\n createMockFolderDto({ id: \"f2\", title: \"Photos\" })\n ];\n const presenter = createMockPresenter({\n folders: observable({\n folders: childFolders,\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders,\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(2);\n expect(presenter.vm.folders.childFolders[0].id).toBe(\"f1\");\n expect(presenter.vm.folders.childFolders[1].id).toBe(\"f2\");\n });\n\n it(\"should expose child folders via vm.folders.childFolders when a folder is selected\", () => {\n const childA = createMockFolderDto({ id: \"child-a\", title: \"Sub A\" });\n const childB = createMockFolderDto({ id: \"child-b\", title: \"Sub B\" });\n\n const presenter = createMockPresenter({\n folders: observable({\n folders: [],\n tree: [],\n currentFolderId: \"parent\",\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: false,\n currentFolderTitle: \"Parent\",\n childFolders: [childA, childB],\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(2);\n expect(presenter.vm.folders.childFolders[0].id).toBe(\"child-a\");\n expect(presenter.vm.folders.childFolders[1].id).toBe(\"child-b\");\n });\n\n it(\"should return empty childFolders when current folder has no children\", () => {\n const presenter = createMockPresenter({\n folders: observable({\n folders: [],\n tree: [],\n currentFolderId: \"leaf\",\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: false,\n currentFolderTitle: \"Leaf\",\n childFolders: [],\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(0);\n });\n});\n\ndescribe(\"FileGrid — selection action wiring\", () => {\n it(\"should call actions.selection.toggle with file id on file click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when a file card is clicked.\n const fileId = \"file-1\";\n presenter.actions.selection.toggle(fileId);\n\n expect(presenter.actions.selection.toggle).toHaveBeenCalledWith(\"file-1\");\n });\n\n it(\"should call actions.selection.deselectAll on background click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate background click handler.\n presenter.actions.selection.deselectAll();\n\n expect(presenter.actions.selection.deselectAll).toHaveBeenCalled();\n });\n\n it(\"should correctly determine selected state from vm.list.selection.selectedIds\", () => {\n const file1 = createMockFile({ id: \"file-1\" });\n const file2 = createMockFile({ id: \"file-2\", name: \"doc.pdf\" });\n\n const presenter = createMockPresenter({\n list: observable({\n rows: [file1, file2],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 2,\n currentCount: 2\n },\n selection: {\n selectedIds: new Set<string>([\"file-1\"]),\n selectedCount: 1,\n allSelected: false\n },\n empty: false,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n expect(presenter.vm.list.selection.selectedIds.has(\"file-1\")).toBe(true);\n expect(presenter.vm.list.selection.selectedIds.has(\"file-2\")).toBe(false);\n });\n});\n\ndescribe(\"FileGrid — folder navigation wiring\", () => {\n it(\"should call actions.filter.set with folderId on folder card click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when a folder card is clicked.\n const folderId = \"folder-1\";\n presenter.actions.filter.set(\"folderId\", folderId);\n\n expect(presenter.actions.filter.set).toHaveBeenCalledWith(\"folderId\", \"folder-1\");\n });\n});\n\ndescribe(\"FileGrid — loading state\", () => {\n it(\"should show loading state when loading with no rows\", () => {\n const presenter = createMockPresenter({\n list: observable({\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: true,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n // Replicate the component's loading check.\n const showLoader =\n presenter.vm.list.pagination.loading && presenter.vm.list.rows.length === 0;\n\n expect(showLoader).toBe(true);\n });\n\n it(\"should not show loading state when loading with existing rows\", () => {\n const presenter = createMockPresenter({\n list: observable({\n rows: [createMockFile()],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: true,\n loadingMore: false,\n totalCount: 1,\n currentCount: 1\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: false,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n const showLoader =\n presenter.vm.list.pagination.loading && presenter.vm.list.rows.length === 0;\n\n expect(showLoader).toBe(false);\n });\n});\n"],"names":["createMockFile","overrides","emptyIdentity","createMockFolderDto","createMockPresenter","vmOverrides","defaultVm","observable","Set","undefined","vi","describe","it","childFolders","presenter","expect","childA","childB","fileId","file1","file2","folderId","showLoader"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/FileList/components/Grid/FileGrid.test.js","sources":["../../../../../src/presentation/FileList/components/Grid/FileGrid.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi } from \"vitest\";\nimport { observable } from \"mobx\";\nimport type { IFileManagerPresenter, IFileManagerViewModel } from \"../../abstractions.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\nimport type { FmFile } from \"~/features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Mock factories.\n// ---------------------------------------------------------------------------\n\nfunction createMockFile(overrides: Partial<FmFile> = {}): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 1024,\n metadata: {},\n tags: [],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-02T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n location: { folderId: \"root\" },\n ...overrides\n };\n}\n\nconst emptyIdentity = { id: \"\", displayName: \"\", type: \"\" };\n\nfunction createMockFolderDto(overrides: Partial<FolderDto> = {}): FolderDto {\n return {\n id: \"folder-1\",\n title: \"Documents\",\n slug: \"documents\",\n type: \"\",\n parentId: null,\n path: \"\",\n permissions: [],\n hasNonInheritedPermissions: false,\n canManagePermissions: false,\n canManageStructure: true,\n canManageContent: false,\n createdBy: emptyIdentity,\n createdOn: \"\",\n savedBy: emptyIdentity,\n savedOn: \"\",\n modifiedBy: null,\n modifiedOn: null,\n extensions: {},\n ...overrides\n };\n}\n\nfunction createMockPresenter(\n vmOverrides: Partial<IFileManagerViewModel> = {}\n): IFileManagerPresenter {\n const defaultVm: IFileManagerViewModel = observable({\n fileModel: null,\n list: {\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n appliedQuery: null,\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n error: null\n },\n folders: {\n folders: [],\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders: [],\n loadingNodeIds: []\n },\n permissions: {\n canRead: true,\n canCreate: true,\n canEdit: true,\n canDelete: true,\n canEditFile: () => true,\n canDeleteFile: () => true\n },\n upload: {\n jobs: [],\n overallProgress: { percentage: 0, bytesSent: 0, totalBytes: 0 },\n isUploading: false\n },\n tags: [],\n loading: false,\n empty: false,\n showFolders: true,\n viewMode: \"grid\" as const,\n dragging: false,\n showingFilters: false,\n isOverlay: false,\n accept: [],\n multiple: false,\n scope: undefined,\n fileDetails: null,\n ...vmOverrides\n });\n\n return {\n vm: defaultVm,\n actions: {\n search: { set: vi.fn(), clear: vi.fn() },\n sort: { set: vi.fn(), toggle: vi.fn() },\n filter: { set: vi.fn(), clear: vi.fn(), clearAll: vi.fn() },\n selection: {\n toggle: vi.fn(),\n selectRangeTo: vi.fn(),\n selectAll: vi.fn(),\n deselectAll: vi.fn(),\n selectRows: vi.fn(),\n isSelected: vi.fn().mockReturnValue(false)\n },\n loadMore: vi.fn().mockResolvedValue(undefined),\n refresh: vi.fn().mockResolvedValue(undefined),\n upload: vi.fn().mockResolvedValue(undefined),\n setViewMode: vi.fn(),\n showFileDetails: vi.fn(),\n hideFileDetails: vi.fn(),\n setDragging: vi.fn(),\n showFilters: vi.fn(),\n hideFilters: vi.fn(),\n folders: {\n selectFolder: vi.fn(),\n createFolder: vi.fn(),\n editFolder: vi.fn(),\n deleteFolder: vi.fn().mockResolvedValue(undefined),\n moveFolder: vi.fn().mockResolvedValue(undefined),\n loadChildFolders: vi.fn().mockResolvedValue(undefined),\n canManageStructure: vi.fn().mockReturnValue(true),\n getAncestorIds: vi.fn().mockReturnValue([]),\n submitOperation: vi.fn().mockResolvedValue(true),\n cancelOperation: vi.fn()\n }\n },\n init: vi.fn(),\n dispose: vi.fn()\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tests — Grid data composition logic.\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileGrid — child folder resolution\", () => {\n it(\"should use vm.folders.childFolders when no current folder is selected\", () => {\n const childFolders = [\n createMockFolderDto({ id: \"f1\", title: \"Docs\" }),\n createMockFolderDto({ id: \"f2\", title: \"Photos\" })\n ];\n const presenter = createMockPresenter({\n folders: observable({\n folders: childFolders,\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders,\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(2);\n expect(presenter.vm.folders.childFolders[0].id).toBe(\"f1\");\n expect(presenter.vm.folders.childFolders[1].id).toBe(\"f2\");\n });\n\n it(\"should expose child folders via vm.folders.childFolders when a folder is selected\", () => {\n const childA = createMockFolderDto({ id: \"child-a\", title: \"Sub A\" });\n const childB = createMockFolderDto({ id: \"child-b\", title: \"Sub B\" });\n\n const presenter = createMockPresenter({\n folders: observable({\n folders: [],\n tree: [],\n currentFolderId: \"parent\",\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: false,\n currentFolderTitle: \"Parent\",\n childFolders: [childA, childB],\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(2);\n expect(presenter.vm.folders.childFolders[0].id).toBe(\"child-a\");\n expect(presenter.vm.folders.childFolders[1].id).toBe(\"child-b\");\n });\n\n it(\"should return empty childFolders when current folder has no children\", () => {\n const presenter = createMockPresenter({\n folders: observable({\n folders: [],\n tree: [],\n currentFolderId: \"leaf\",\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: false,\n currentFolderTitle: \"Leaf\",\n childFolders: [],\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(0);\n });\n});\n\ndescribe(\"FileGrid — selection action wiring\", () => {\n it(\"should call actions.selection.toggle with file id on file click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when a file card is clicked.\n const fileId = \"file-1\";\n presenter.actions.selection.toggle(fileId);\n\n expect(presenter.actions.selection.toggle).toHaveBeenCalledWith(\"file-1\");\n });\n\n it(\"should call actions.selection.deselectAll on background click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate background click handler.\n presenter.actions.selection.deselectAll();\n\n expect(presenter.actions.selection.deselectAll).toHaveBeenCalled();\n });\n\n it(\"should correctly determine selected state from vm.list.selection.selectedIds\", () => {\n const file1 = createMockFile({ id: \"file-1\" });\n const file2 = createMockFile({ id: \"file-2\", name: \"doc.pdf\" });\n\n const presenter = createMockPresenter({\n list: observable({\n rows: [file1, file2],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 2,\n currentCount: 2\n },\n selection: {\n selectedIds: new Set<string>([\"file-1\"]),\n selectedCount: 1,\n allSelected: false\n },\n empty: false,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n expect(presenter.vm.list.selection.selectedIds.has(\"file-1\")).toBe(true);\n expect(presenter.vm.list.selection.selectedIds.has(\"file-2\")).toBe(false);\n });\n});\n\ndescribe(\"FileGrid — folder navigation wiring\", () => {\n it(\"should call actions.filter.set with folderId on folder card click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when a folder card is clicked.\n const folderId = \"folder-1\";\n presenter.actions.filter.set(\"folderId\", folderId);\n\n expect(presenter.actions.filter.set).toHaveBeenCalledWith(\"folderId\", \"folder-1\");\n });\n});\n\ndescribe(\"FileGrid — loading state\", () => {\n it(\"should show loading state when loading with no rows\", () => {\n const presenter = createMockPresenter({\n list: observable({\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: true,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n // Replicate the component's loading check.\n const showLoader =\n presenter.vm.list.pagination.loading && presenter.vm.list.rows.length === 0;\n\n expect(showLoader).toBe(true);\n });\n\n it(\"should not show loading state when loading with existing rows\", () => {\n const presenter = createMockPresenter({\n list: observable({\n rows: [createMockFile()],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: true,\n loadingMore: false,\n totalCount: 1,\n currentCount: 1\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: false,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n const showLoader =\n presenter.vm.list.pagination.loading && presenter.vm.list.rows.length === 0;\n\n expect(showLoader).toBe(false);\n });\n});\n"],"names":["createMockFile","overrides","emptyIdentity","createMockFolderDto","createMockPresenter","vmOverrides","defaultVm","observable","Set","undefined","vi","describe","it","childFolders","presenter","expect","childA","childB","fileId","file1","file2","folderId","showLoader"],"mappings":"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"}
@@ -141,6 +141,8 @@ function createMockPresenter(vmOverrides = {}) {
141
141
  isUploading: false
142
142
  },
143
143
  tags: [],
144
+ loading: false,
145
+ empty: false,
144
146
  showFolders: true,
145
147
  viewMode: "table",
146
148
  dragging: false,
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileList/components/Table/FileTable.test.js","sources":["../../../../../src/presentation/FileList/components/Table/FileTable.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi } from \"vitest\";\nimport { observable } from \"mobx\";\nimport type { IFileManagerPresenter, IFileManagerViewModel } from \"../../abstractions.js\";\nimport type { IFolderTreeNode } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport type { FmFile } from \"~/features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers — extracted from FileTable.tsx for unit testing.\n// ---------------------------------------------------------------------------\n\n/**\n * Convert presenter sort state to DataTable sorting format.\n */\nfunction toDataTableSorting(\n sort: { field: string; direction: \"ASC\" | \"DESC\" } | null\n): Array<{ id: string; desc: boolean }> {\n if (!sort) {\n return [];\n }\n return [{ id: sort.field, desc: sort.direction === \"DESC\" }];\n}\n\n/**\n * Map IFolderTreeNode to FolderTableRow-compatible shape.\n */\nfunction toFolderTableRows(nodes: IFolderTreeNode[]) {\n return nodes.map(node => ({\n id: node.id,\n $type: \"FOLDER\" as const,\n $selectable: false,\n data: {\n id: node.id,\n title: node.name,\n slug: node.slug,\n type: \"\",\n parentId: node.parentId,\n path: \"\",\n permissions: [],\n hasNonInheritedPermissions: false,\n canManagePermissions: false,\n canManageStructure: false,\n canManageContent: false,\n createdBy: { id: \"\", displayName: \"\" },\n createdOn: \"\",\n savedBy: { id: \"\", displayName: \"\" },\n savedOn: \"\",\n modifiedBy: null,\n modifiedOn: null,\n extensions: {}\n }\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Mock file factory.\n// ---------------------------------------------------------------------------\n\nfunction createMockFile(overrides: Partial<FmFile> = {}): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 1024,\n metadata: {},\n tags: [],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-02T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n location: { folderId: \"root\" },\n ...overrides\n };\n}\n\nfunction createMockFolderNode(overrides: Partial<IFolderTreeNode> = {}): IFolderTreeNode {\n return {\n id: \"folder-1\",\n name: \"Documents\",\n slug: \"documents\",\n parentId: null,\n children: [],\n hasNonInheritedPermissions: false,\n canManagePermissions: false,\n canManageStructure: true,\n ...overrides\n };\n}\n\nfunction createMockPresenter(\n vmOverrides: Partial<IFileManagerViewModel> = {}\n): IFileManagerPresenter {\n const defaultVm: IFileManagerViewModel = observable({\n fileModel: null,\n list: {\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n appliedQuery: null,\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n error: null\n },\n folders: {\n folders: [],\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders: [],\n loadingNodeIds: []\n },\n permissions: {\n canRead: true,\n canCreate: true,\n canEdit: true,\n canDelete: true,\n canEditFile: () => true,\n canDeleteFile: () => true\n },\n upload: {\n jobs: [],\n overallProgress: { percentage: 0, bytesSent: 0, totalBytes: 0 },\n isUploading: false\n },\n tags: [],\n showFolders: true,\n viewMode: \"table\" as const,\n dragging: false,\n showingFilters: false,\n isOverlay: false,\n accept: [],\n multiple: false,\n scope: undefined,\n fileDetails: null,\n ...vmOverrides\n });\n\n return {\n vm: defaultVm,\n actions: {\n search: { set: vi.fn(), clear: vi.fn() },\n sort: { set: vi.fn(), toggle: vi.fn() },\n filter: { set: vi.fn(), clear: vi.fn(), clearAll: vi.fn() },\n selection: {\n toggle: vi.fn(),\n selectRangeTo: vi.fn(),\n selectAll: vi.fn(),\n deselectAll: vi.fn(),\n selectRows: vi.fn(),\n isSelected: vi.fn().mockReturnValue(false)\n },\n loadMore: vi.fn().mockResolvedValue(undefined),\n refresh: vi.fn().mockResolvedValue(undefined),\n upload: vi.fn().mockResolvedValue(undefined),\n setViewMode: vi.fn(),\n showFileDetails: vi.fn(),\n hideFileDetails: vi.fn(),\n setDragging: vi.fn(),\n showFilters: vi.fn(),\n hideFilters: vi.fn(),\n folders: {\n selectFolder: vi.fn(),\n createFolder: vi.fn(),\n editFolder: vi.fn(),\n deleteFolder: vi.fn().mockResolvedValue(undefined),\n moveFolder: vi.fn().mockResolvedValue(undefined),\n loadChildFolders: vi.fn().mockResolvedValue(undefined),\n canManageStructure: vi.fn().mockReturnValue(true),\n getAncestorIds: vi.fn().mockReturnValue([]),\n submitOperation: vi.fn().mockResolvedValue(true),\n cancelOperation: vi.fn()\n }\n },\n init: vi.fn(),\n dispose: vi.fn()\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileTable — sorting conversion\", () => {\n it(\"should convert null sort to empty DataTable sorting\", () => {\n expect(toDataTableSorting(null)).toEqual([]);\n });\n\n it(\"should convert ASC sort to DataTable format\", () => {\n const result = toDataTableSorting({ field: \"name\", direction: \"ASC\" });\n expect(result).toEqual([{ id: \"name\", desc: false }]);\n });\n\n it(\"should convert DESC sort to DataTable format\", () => {\n const result = toDataTableSorting({ field: \"createdOn\", direction: \"DESC\" });\n expect(result).toEqual([{ id: \"createdOn\", desc: true }]);\n });\n});\n\ndescribe(\"FileTable — folder data mapping\", () => {\n it(\"should map IFolderTreeNode to FolderTableRow with $type FOLDER\", () => {\n const node = createMockFolderNode({ id: \"f1\", name: \"Images\", slug: \"images\" });\n const rows = toFolderTableRows([node]);\n\n expect(rows).toHaveLength(1);\n expect(rows[0].id).toBe(\"f1\");\n expect(rows[0].$type).toBe(\"FOLDER\");\n expect(rows[0].$selectable).toBe(false);\n expect(rows[0].data.title).toBe(\"Images\");\n expect(rows[0].data.slug).toBe(\"images\");\n });\n\n it(\"should map multiple folder nodes\", () => {\n const nodes = [\n createMockFolderNode({ id: \"f1\", name: \"Docs\" }),\n createMockFolderNode({ id: \"f2\", name: \"Photos\" })\n ];\n const rows = toFolderTableRows(nodes);\n\n expect(rows).toHaveLength(2);\n expect(rows[0].id).toBe(\"f1\");\n expect(rows[1].id).toBe(\"f2\");\n });\n\n it(\"should return empty array for empty folder list\", () => {\n expect(toFolderTableRows([])).toEqual([]);\n });\n});\n\ndescribe(\"FileTable — sort action wiring\", () => {\n it(\"should call actions.sort.set when sorting changes to DESC\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when onSortingChange fires.\n const newSorting = [{ id: \"size\", desc: true }];\n if (newSorting.length > 0) {\n const { id, desc } = newSorting[0];\n presenter.actions.sort.set(id, desc ? \"DESC\" : \"ASC\");\n }\n\n expect(presenter.actions.sort.set).toHaveBeenCalledWith(\"size\", \"DESC\");\n });\n\n it(\"should call actions.sort.set when sorting changes to ASC\", () => {\n const presenter = createMockPresenter();\n\n const newSorting = [{ id: \"name\", desc: false }];\n if (newSorting.length > 0) {\n const { id, desc } = newSorting[0];\n presenter.actions.sort.set(id, desc ? \"DESC\" : \"ASC\");\n }\n\n expect(presenter.actions.sort.set).toHaveBeenCalledWith(\"name\", \"ASC\");\n });\n});\n\ndescribe(\"FileTable — selection action wiring\", () => {\n it(\"should call actions.selection.toggle for RECORD row toggle\", () => {\n const presenter = createMockPresenter();\n\n // Simulate onToggleRow for a file row.\n const row = {\n id: \"file-1\",\n $type: \"RECORD\" as const,\n $selectable: true,\n data: createMockFile()\n };\n if (row.$type === \"RECORD\") {\n presenter.actions.selection.toggle(row.id);\n }\n\n expect(presenter.actions.selection.toggle).toHaveBeenCalledWith(\"file-1\");\n });\n\n it(\"should not call actions.selection.toggle for FOLDER row toggle\", () => {\n const presenter = createMockPresenter();\n\n // Simulate onToggleRow for a folder row — folders are not selectable.\n const row = { id: \"folder-1\", $type: \"FOLDER\" as const, $selectable: false, data: {} };\n if ((row.$type as string) === \"RECORD\") {\n presenter.actions.selection.toggle(row.id);\n }\n\n expect(presenter.actions.selection.toggle).not.toHaveBeenCalled();\n });\n\n it(\"should call actions.selection.deselectAll when empty selection\", () => {\n const presenter = createMockPresenter();\n\n // Simulate onSelectRow with empty array.\n const rows: Array<{ id: string; $type: string }> = [];\n if (rows.length === 0) {\n presenter.actions.selection.deselectAll();\n }\n\n expect(presenter.actions.selection.deselectAll).toHaveBeenCalled();\n });\n\n it(\"should call actions.selection.selectRows with file IDs only\", () => {\n const presenter = createMockPresenter();\n\n // Simulate onSelectRow with mixed rows.\n const rows = [\n {\n id: \"file-1\",\n $type: \"RECORD\" as const,\n $selectable: true,\n data: createMockFile({ id: \"file-1\" })\n },\n { id: \"folder-1\", $type: \"FOLDER\" as const, $selectable: false, data: {} },\n {\n id: \"file-2\",\n $type: \"RECORD\" as const,\n $selectable: true,\n data: createMockFile({ id: \"file-2\" })\n }\n ];\n\n const ids = rows.filter(row => row.$type === \"RECORD\").map(row => row.id);\n presenter.actions.selection.selectRows(ids);\n\n expect(presenter.actions.selection.selectRows).toHaveBeenCalledWith([\"file-1\", \"file-2\"]);\n });\n});\n"],"names":["toDataTableSorting","sort","toFolderTableRows","nodes","node","createMockFile","overrides","createMockFolderNode","createMockPresenter","vmOverrides","defaultVm","observable","Set","undefined","vi","describe","it","expect","result","rows","presenter","newSorting","id","desc","row","ids"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/FileList/components/Table/FileTable.test.js","sources":["../../../../../src/presentation/FileList/components/Table/FileTable.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi } from \"vitest\";\nimport { observable } from \"mobx\";\nimport type { IFileManagerPresenter, IFileManagerViewModel } from \"../../abstractions.js\";\nimport type { IFolderTreeNode } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport type { FmFile } from \"~/features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers — extracted from FileTable.tsx for unit testing.\n// ---------------------------------------------------------------------------\n\n/**\n * Convert presenter sort state to DataTable sorting format.\n */\nfunction toDataTableSorting(\n sort: { field: string; direction: \"ASC\" | \"DESC\" } | null\n): Array<{ id: string; desc: boolean }> {\n if (!sort) {\n return [];\n }\n return [{ id: sort.field, desc: sort.direction === \"DESC\" }];\n}\n\n/**\n * Map IFolderTreeNode to FolderTableRow-compatible shape.\n */\nfunction toFolderTableRows(nodes: IFolderTreeNode[]) {\n return nodes.map(node => ({\n id: node.id,\n $type: \"FOLDER\" as const,\n $selectable: false,\n data: {\n id: node.id,\n title: node.name,\n slug: node.slug,\n type: \"\",\n parentId: node.parentId,\n path: \"\",\n permissions: [],\n hasNonInheritedPermissions: false,\n canManagePermissions: false,\n canManageStructure: false,\n canManageContent: false,\n createdBy: { id: \"\", displayName: \"\" },\n createdOn: \"\",\n savedBy: { id: \"\", displayName: \"\" },\n savedOn: \"\",\n modifiedBy: null,\n modifiedOn: null,\n extensions: {}\n }\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Mock file factory.\n// ---------------------------------------------------------------------------\n\nfunction createMockFile(overrides: Partial<FmFile> = {}): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 1024,\n metadata: {},\n tags: [],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-02T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n location: { folderId: \"root\" },\n ...overrides\n };\n}\n\nfunction createMockFolderNode(overrides: Partial<IFolderTreeNode> = {}): IFolderTreeNode {\n return {\n id: \"folder-1\",\n name: \"Documents\",\n slug: \"documents\",\n parentId: null,\n children: [],\n hasNonInheritedPermissions: false,\n canManagePermissions: false,\n canManageStructure: true,\n ...overrides\n };\n}\n\nfunction createMockPresenter(\n vmOverrides: Partial<IFileManagerViewModel> = {}\n): IFileManagerPresenter {\n const defaultVm: IFileManagerViewModel = observable({\n fileModel: null,\n list: {\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n appliedQuery: null,\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n error: null\n },\n folders: {\n folders: [],\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders: [],\n loadingNodeIds: []\n },\n permissions: {\n canRead: true,\n canCreate: true,\n canEdit: true,\n canDelete: true,\n canEditFile: () => true,\n canDeleteFile: () => true\n },\n upload: {\n jobs: [],\n overallProgress: { percentage: 0, bytesSent: 0, totalBytes: 0 },\n isUploading: false\n },\n tags: [],\n loading: false,\n empty: false,\n showFolders: true,\n viewMode: \"table\" as const,\n dragging: false,\n showingFilters: false,\n isOverlay: false,\n accept: [],\n multiple: false,\n scope: undefined,\n fileDetails: null,\n ...vmOverrides\n });\n\n return {\n vm: defaultVm,\n actions: {\n search: { set: vi.fn(), clear: vi.fn() },\n sort: { set: vi.fn(), toggle: vi.fn() },\n filter: { set: vi.fn(), clear: vi.fn(), clearAll: vi.fn() },\n selection: {\n toggle: vi.fn(),\n selectRangeTo: vi.fn(),\n selectAll: vi.fn(),\n deselectAll: vi.fn(),\n selectRows: vi.fn(),\n isSelected: vi.fn().mockReturnValue(false)\n },\n loadMore: vi.fn().mockResolvedValue(undefined),\n refresh: vi.fn().mockResolvedValue(undefined),\n upload: vi.fn().mockResolvedValue(undefined),\n setViewMode: vi.fn(),\n showFileDetails: vi.fn(),\n hideFileDetails: vi.fn(),\n setDragging: vi.fn(),\n showFilters: vi.fn(),\n hideFilters: vi.fn(),\n folders: {\n selectFolder: vi.fn(),\n createFolder: vi.fn(),\n editFolder: vi.fn(),\n deleteFolder: vi.fn().mockResolvedValue(undefined),\n moveFolder: vi.fn().mockResolvedValue(undefined),\n loadChildFolders: vi.fn().mockResolvedValue(undefined),\n canManageStructure: vi.fn().mockReturnValue(true),\n getAncestorIds: vi.fn().mockReturnValue([]),\n submitOperation: vi.fn().mockResolvedValue(true),\n cancelOperation: vi.fn()\n }\n },\n init: vi.fn(),\n dispose: vi.fn()\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileTable — sorting conversion\", () => {\n it(\"should convert null sort to empty DataTable sorting\", () => {\n expect(toDataTableSorting(null)).toEqual([]);\n });\n\n it(\"should convert ASC sort to DataTable format\", () => {\n const result = toDataTableSorting({ field: \"name\", direction: \"ASC\" });\n expect(result).toEqual([{ id: \"name\", desc: false }]);\n });\n\n it(\"should convert DESC sort to DataTable format\", () => {\n const result = toDataTableSorting({ field: \"createdOn\", direction: \"DESC\" });\n expect(result).toEqual([{ id: \"createdOn\", desc: true }]);\n });\n});\n\ndescribe(\"FileTable — folder data mapping\", () => {\n it(\"should map IFolderTreeNode to FolderTableRow with $type FOLDER\", () => {\n const node = createMockFolderNode({ id: \"f1\", name: \"Images\", slug: \"images\" });\n const rows = toFolderTableRows([node]);\n\n expect(rows).toHaveLength(1);\n expect(rows[0].id).toBe(\"f1\");\n expect(rows[0].$type).toBe(\"FOLDER\");\n expect(rows[0].$selectable).toBe(false);\n expect(rows[0].data.title).toBe(\"Images\");\n expect(rows[0].data.slug).toBe(\"images\");\n });\n\n it(\"should map multiple folder nodes\", () => {\n const nodes = [\n createMockFolderNode({ id: \"f1\", name: \"Docs\" }),\n createMockFolderNode({ id: \"f2\", name: \"Photos\" })\n ];\n const rows = toFolderTableRows(nodes);\n\n expect(rows).toHaveLength(2);\n expect(rows[0].id).toBe(\"f1\");\n expect(rows[1].id).toBe(\"f2\");\n });\n\n it(\"should return empty array for empty folder list\", () => {\n expect(toFolderTableRows([])).toEqual([]);\n });\n});\n\ndescribe(\"FileTable — sort action wiring\", () => {\n it(\"should call actions.sort.set when sorting changes to DESC\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when onSortingChange fires.\n const newSorting = [{ id: \"size\", desc: true }];\n if (newSorting.length > 0) {\n const { id, desc } = newSorting[0];\n presenter.actions.sort.set(id, desc ? \"DESC\" : \"ASC\");\n }\n\n expect(presenter.actions.sort.set).toHaveBeenCalledWith(\"size\", \"DESC\");\n });\n\n it(\"should call actions.sort.set when sorting changes to ASC\", () => {\n const presenter = createMockPresenter();\n\n const newSorting = [{ id: \"name\", desc: false }];\n if (newSorting.length > 0) {\n const { id, desc } = newSorting[0];\n presenter.actions.sort.set(id, desc ? \"DESC\" : \"ASC\");\n }\n\n expect(presenter.actions.sort.set).toHaveBeenCalledWith(\"name\", \"ASC\");\n });\n});\n\ndescribe(\"FileTable — selection action wiring\", () => {\n it(\"should call actions.selection.toggle for RECORD row toggle\", () => {\n const presenter = createMockPresenter();\n\n // Simulate onToggleRow for a file row.\n const row = {\n id: \"file-1\",\n $type: \"RECORD\" as const,\n $selectable: true,\n data: createMockFile()\n };\n if (row.$type === \"RECORD\") {\n presenter.actions.selection.toggle(row.id);\n }\n\n expect(presenter.actions.selection.toggle).toHaveBeenCalledWith(\"file-1\");\n });\n\n it(\"should not call actions.selection.toggle for FOLDER row toggle\", () => {\n const presenter = createMockPresenter();\n\n // Simulate onToggleRow for a folder row — folders are not selectable.\n const row = { id: \"folder-1\", $type: \"FOLDER\" as const, $selectable: false, data: {} };\n if ((row.$type as string) === \"RECORD\") {\n presenter.actions.selection.toggle(row.id);\n }\n\n expect(presenter.actions.selection.toggle).not.toHaveBeenCalled();\n });\n\n it(\"should call actions.selection.deselectAll when empty selection\", () => {\n const presenter = createMockPresenter();\n\n // Simulate onSelectRow with empty array.\n const rows: Array<{ id: string; $type: string }> = [];\n if (rows.length === 0) {\n presenter.actions.selection.deselectAll();\n }\n\n expect(presenter.actions.selection.deselectAll).toHaveBeenCalled();\n });\n\n it(\"should call actions.selection.selectRows with file IDs only\", () => {\n const presenter = createMockPresenter();\n\n // Simulate onSelectRow with mixed rows.\n const rows = [\n {\n id: \"file-1\",\n $type: \"RECORD\" as const,\n $selectable: true,\n data: createMockFile({ id: \"file-1\" })\n },\n { id: \"folder-1\", $type: \"FOLDER\" as const, $selectable: false, data: {} },\n {\n id: \"file-2\",\n $type: \"RECORD\" as const,\n $selectable: true,\n data: createMockFile({ id: \"file-2\" })\n }\n ];\n\n const ids = rows.filter(row => row.$type === \"RECORD\").map(row => row.id);\n presenter.actions.selection.selectRows(ids);\n\n expect(presenter.actions.selection.selectRows).toHaveBeenCalledWith([\"file-1\", \"file-2\"]);\n });\n});\n"],"names":["toDataTableSorting","sort","toFolderTableRows","nodes","node","createMockFile","overrides","createMockFolderNode","createMockPresenter","vmOverrides","defaultVm","observable","Set","undefined","vi","describe","it","expect","result","rows","presenter","newSorting","id","desc","row","ids"],"mappings":"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"}
@@ -78,14 +78,12 @@ const FileManagerView_FileManagerViewLayout = observer(function() {
78
78
  if (!vm.fileModel) return /*#__PURE__*/ react.createElement(OverlayLoader, {
79
79
  text: t`Preparing File Manager...`
80
80
  });
81
- const isLoading = vm.list.pagination.loading && 0 === vm.list.rows.length;
82
- const isEmpty = !vm.list.pagination.loading && 0 === vm.list.rows.length;
83
81
  const renderList = (browseFiles)=>{
84
- if (isLoading) return /*#__PURE__*/ react.createElement(OverlayLoader, {
82
+ if (vm.loading) return /*#__PURE__*/ react.createElement(OverlayLoader, {
85
83
  text: t`Loading files...`,
86
84
  size: "lg"
87
85
  });
88
- if (isEmpty) return /*#__PURE__*/ react.createElement(Empty, {
86
+ if (vm.empty) return /*#__PURE__*/ react.createElement(Empty, {
89
87
  isSearchResult: !vm.showFolders,
90
88
  browseFiles: browseFiles
91
89
  });
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileManager/FileManagerView.js","sources":["../../../src/presentation/FileManager/FileManagerView.tsx"],"sourcesContent":["import React, { useMemo, useEffect, useCallback } from \"react\";\nimport { DiContainerProvider, useContainer, useFeature } from \"@webiny/app\";\nimport { observer } from \"mobx-react-lite\";\nimport {\n BrowserFilePicker,\n type BrowserFilePickerRenderProps\n} from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\nimport debounce from \"lodash/debounce.js\";\nimport { Heading, OverlayLoader, ScrollArea, Separator, useToast } from \"@webiny/admin-ui\";\nimport { i18n } from \"@webiny/app/i18n/index.js\";\nimport {\n LeftPanel,\n OverlayLayout,\n RightPanel,\n SplitView,\n DialogsProvider,\n useHotkeys\n} from \"@webiny/app-admin\";\nimport { FoldersFeature } from \"@webiny/app-aco/features/folders/feature.js\";\nimport { FolderTreePresenterFeature } from \"@webiny/app-aco/presentation/folderTree/feature.js\";\nimport { FileManagerPresenterFeature } from \"~/presentation/FileList/feature.js\";\nimport {\n FileManagerPresenterProvider,\n useFileManagerPresenter\n} from \"~/presentation/FileList/FileManagerPresenterProvider.js\";\nimport { FileDetailsPresenterFeature } from \"~/presentation/FileDetails/feature.js\";\nimport { SharedCacheFeature } from \"~/features/shared/feature.js\";\nimport { ListFilesFeature } from \"~/features/listFiles/feature.js\";\nimport { GetFileFeature } from \"~/features/getFile/feature.js\";\nimport { DeleteFileFeature } from \"~/features/deleteFile/feature.js\";\nimport { UpdateFileFeature } from \"~/features/updateFile/feature.js\";\nimport { FileUploaderFeature } from \"~/features/fileUploader/feature.js\";\nimport { ListTagsFeature } from \"~/features/tags/feature.js\";\nimport { GetSettingsFeature } from \"~/features/settings/feature.js\";\nimport {\n FileManagerViewWithConfig,\n useFileManagerConfig\n} from \"~/presentation/config/FileManagerViewConfig.js\";\nimport { FolderTree } from \"@webiny/app-aco/presentation/folderTree/FolderTree.js\";\nimport { BottomInfoBar } from \"~/presentation/FileList/components/BottomInfoBar/index.js\";\nimport { BulkActionBar } from \"~/presentation/FileList/components/BulkActions/index.js\";\nimport { FileDropPlaceholder } from \"~/presentation/FileList/components/FileDropPlaceholder/index.js\";\nimport { Empty } from \"~/presentation/FileList/components/Empty/index.js\";\nimport { FileDetailsDrawer } from \"~/presentation/FileDetails/components/FileDetailsDrawer.js\";\nimport { FileGrid } from \"~/presentation/FileList/components/Grid/index.js\";\nimport { FileManagerHeader } from \"~/presentation/FileList/components/Header/FileManagerHeader.js\";\nimport { FileTable } from \"~/presentation/FileList/components/Table/index.js\";\nimport { TagsList } from \"~/presentation/FileList/components/TagsList/index.js\";\nimport { UploadProgress } from \"~/presentation/FileList/components/Upload/index.js\";\nimport { GetSettingsRepository } from \"~/features/settings/abstractions.js\";\nimport { OverlayProvider, useOverlay } from \"./OverlayContext.js\";\n\nimport type { FmFile } from \"~/features/shared/types.js\";\nimport type {\n FileError,\n SelectedFile\n} from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\nimport { outputFileSelectionError } from \"~/presentation/FileManager/outputFileSelectionError.js\";\nimport type { ScrollPosition } from \"@webiny/admin-ui\";\n\nexport interface FileManagerViewProps {\n overlay?: boolean;\n onChange?: (files: FmFile[]) => void;\n onClose?: () => void;\n multiple?: boolean;\n accept?: string[];\n scope?: string;\n children?: React.ReactNode;\n}\n\nconst t = i18n.ns(\"app-admin/file-manager/file-manager-view\");\n\n// ---------------------------------------------------------------------------\n// Layout — uses original UI components, wired to the presenter.\n// ---------------------------------------------------------------------------\n\nconst FileManagerViewLayout = observer(function FileManagerViewLayout() {\n const { vm, actions } = useFileManagerPresenter();\n const { browser } = useFileManagerConfig();\n const overlay = useOverlay();\n const toast = useToast();\n\n useHotkeys({\n zIndex: 20,\n keys: {\n esc: () => overlay && overlay.onClose()\n }\n });\n\n const container = useContainer();\n const settingsRepository = useMemo(() => container.resolve(GetSettingsRepository), [container]);\n const settings = settingsRepository.settings;\n\n const uploadFiles = async (files: SelectedFile[]) => {\n await actions.upload(files);\n toast.showSuccessToast({ title: \"File upload complete.\" });\n };\n\n const onError = useCallback((errors: FileError[]) => {\n const message = outputFileSelectionError(errors);\n if (message) {\n toast.showWarningToast({ title: message });\n } else {\n toast.showWarningToast({ title: \"Couldn't process selected files.\" });\n console.error(errors);\n }\n }, []);\n\n const loadMoreOnScroll = useCallback(\n debounce(async (scrollFrame: ScrollPosition) => {\n if (scrollFrame.top > 0.8) {\n void actions.loadMore();\n }\n }, 200),\n [vm.list.pagination, actions]\n );\n\n if (!vm.fileModel) {\n return <OverlayLoader text={t`Preparing File Manager...`} />;\n }\n\n const isLoading = vm.list.pagination.loading && vm.list.rows.length === 0;\n const isEmpty = !vm.list.pagination.loading && vm.list.rows.length === 0;\n\n const renderList = (browseFiles: BrowserFilePickerRenderProps[\"browseFiles\"]) => {\n if (isLoading) {\n return <OverlayLoader text={t`Loading files...`} size={\"lg\"} />;\n }\n\n if (isEmpty) {\n return <Empty isSearchResult={!vm.showFolders} browseFiles={browseFiles} />;\n }\n\n if (vm.viewMode === \"table\") {\n return (\n <ScrollArea onScroll={loadMoreOnScroll}>\n <FileTable />\n </ScrollArea>\n );\n }\n\n return (\n <ScrollArea onScroll={loadMoreOnScroll}>\n <FileGrid />\n </ScrollArea>\n );\n };\n\n const content = (\n <BrowserFilePicker\n multiple\n maxSize={settings ? settings.uploadMaxFileSize + \"b\" : \"1TB\"}\n multipleMaxSize={\"1TB\"}\n accept={overlay?.accept ?? []}\n onSuccess={uploadFiles}\n onError={onError}\n >\n {({ getDropZoneProps, browseFiles }) => (\n <>\n <FileDetailsDrawer />\n <SplitView namespace={\"fm/file/list\"}>\n <LeftPanel span={2}>\n <div className={\"flex flex-col h-main-content\"}>\n <div className={\"py-sm px-md\"}>\n <Heading level={5}>{t`File Manager`}</Heading>\n </div>\n <Separator />\n <div className={\"shrink-0 overflow-y-auto max-h-[66vh]\"}>\n <FolderTree\n vm={vm.folders}\n actions={actions.folders}\n folderActions={browser.folder.actions}\n dropConfirmation={browser.folder.dropConfirmation}\n enableActions={true}\n enableCreate={true}\n />\n </div>\n {browser.filterByTags ? (\n <>\n <Separator />\n <div className={\"flex-1 overflow-y-auto min-h-0\"}>\n <TagsList\n loading={false}\n activeTags={\n (vm.list.filters[\"tags\"] as string[]) ?? []\n }\n tags={vm.tags.map(tag => ({\n tag: tag.tag,\n count: tag.count\n }))}\n onActivatedTagsChange={tags => {\n if (tags.length > 0) {\n actions.filter.set(\"tags\", tags);\n } else {\n actions.filter.clear(\"tags\");\n actions.filter.clear(\"tags_rule\");\n }\n }}\n />\n </div>\n </>\n ) : null}\n </div>\n </LeftPanel>\n <RightPanel span={10}>\n <div\n className={\"flex flex-col relative\"}\n style={{ height: \"calc(100vh - 45px\" }}\n >\n <FileManagerHeader browseFiles={browseFiles} />\n <div\n className={\"flex-1\"}\n {...getDropZoneProps({\n onDragEnter: () => actions.setDragging(true),\n onDrop: () => actions.setDragging(false),\n onDragLeave: (e: React.DragEvent) => {\n if (\n !e.relatedTarget ||\n !e.currentTarget.contains(e.relatedTarget as Node)\n ) {\n actions.setDragging(false);\n }\n }\n })}\n data-testid={\"fm-list-wrapper\"}\n >\n {!overlay && <BulkActionBar />}\n {renderList(browseFiles)}\n {vm.dragging && <FileDropPlaceholder />}\n <UploadProgress />\n </div>\n <BottomInfoBar\n accept={overlay?.accept ?? []}\n listing={vm.list.pagination.loadingMore}\n loading={vm.list.pagination.loading}\n totalCount={vm.list.pagination.totalCount}\n currentCount={vm.list.pagination.currentCount}\n />\n </div>\n </RightPanel>\n </SplitView>\n </>\n )}\n </BrowserFilePicker>\n );\n\n if (overlay) {\n return (\n <OverlayLayout variant={\"strong\"} onExited={() => overlay.onClose()}>\n {content}\n </OverlayLayout>\n );\n }\n\n return content;\n});\n\n// ---------------------------------------------------------------------------\n// Inner component that resolves the presenter from the scoped container.\n// ---------------------------------------------------------------------------\n\nconst FileManagerViewInner = observer(\n ({\n overlay = false,\n onChange,\n onClose,\n multiple,\n accept,\n scope,\n children\n }: FileManagerViewProps) => {\n const { presenter } = useFeature(FileManagerPresenterFeature);\n\n const overlayConfig = useMemo(() => {\n if (!overlay || !onChange || !onClose) {\n return null;\n }\n\n const handleFileClick = (file: FmFile) => {\n if (multiple) {\n presenter.actions.selection.toggle(file.id);\n } else {\n onChange([file]);\n }\n };\n\n const confirmSelection = () => {\n const selectedIds = presenter.vm.list.selection.selectedIds;\n const selectedFiles = presenter.vm.list.rows.filter(f => selectedIds.has(f.id));\n if (selectedFiles.length > 0) {\n onChange(selectedFiles);\n }\n };\n\n return {\n onFileClick: handleFileClick,\n confirmSelection,\n onClose,\n accept: accept ?? [],\n multiple: multiple ?? false\n };\n }, [overlay, onChange, onClose, multiple, accept]);\n\n useEffect(() => {\n presenter.init({ scope });\n return () => presenter.dispose();\n }, [presenter, overlay, scope]);\n\n const inner = (\n <DialogsProvider>\n <FileManagerViewWithConfig>\n <FileManagerPresenterProvider presenter={presenter}>\n <FileManagerViewLayout />\n {children}\n </FileManagerPresenterProvider>\n </FileManagerViewWithConfig>\n </DialogsProvider>\n );\n\n if (overlayConfig) {\n return <OverlayProvider config={overlayConfig}>{inner}</OverlayProvider>;\n }\n\n return inner;\n }\n);\n\n// ---------------------------------------------------------------------------\n// FileManagerView — sets up DI container and renders the inner component.\n// ---------------------------------------------------------------------------\n\nexport const FileManagerView = (props: FileManagerViewProps) => {\n const container = useContainer();\n\n const scopedContainer = useMemo(() => {\n const child = container.createChildContainer();\n\n SharedCacheFeature.register(child);\n FoldersFeature.register(child, { type: \"FmFile\" });\n FolderTreePresenterFeature.register(child);\n ListFilesFeature.register(child);\n GetFileFeature.register(child);\n DeleteFileFeature.register(child);\n UpdateFileFeature.register(child);\n FileUploaderFeature.register(child);\n ListTagsFeature.register(child);\n GetSettingsFeature.register(child);\n FileDetailsPresenterFeature.register(child);\n FileManagerPresenterFeature.register(child);\n\n return child;\n }, []);\n\n return (\n <DiContainerProvider container={scopedContainer}>\n <FileManagerViewInner {...props} />\n </DiContainerProvider>\n );\n};\n"],"names":["t","i18n","FileManagerViewLayout","observer","vm","actions","useFileManagerPresenter","browser","useFileManagerConfig","overlay","useOverlay","toast","useToast","useHotkeys","container","useContainer","settingsRepository","useMemo","GetSettingsRepository","settings","uploadFiles","files","onError","useCallback","errors","message","outputFileSelectionError","console","loadMoreOnScroll","debounce","scrollFrame","OverlayLoader","isLoading","isEmpty","renderList","browseFiles","Empty","ScrollArea","FileTable","FileGrid","content","BrowserFilePicker","getDropZoneProps","FileDetailsDrawer","SplitView","LeftPanel","Heading","Separator","FolderTree","TagsList","tag","tags","RightPanel","FileManagerHeader","e","BulkActionBar","FileDropPlaceholder","UploadProgress","BottomInfoBar","OverlayLayout","FileManagerViewInner","onChange","onClose","multiple","accept","scope","children","presenter","useFeature","FileManagerPresenterFeature","overlayConfig","handleFileClick","file","confirmSelection","selectedIds","selectedFiles","f","useEffect","inner","DialogsProvider","FileManagerViewWithConfig","FileManagerPresenterProvider","OverlayProvider","FileManagerView","props","scopedContainer","child","SharedCacheFeature","FoldersFeature","FolderTreePresenterFeature","ListFilesFeature","GetFileFeature","DeleteFileFeature","UpdateFileFeature","FileUploaderFeature","ListTagsFeature","GetSettingsFeature","FileDetailsPresenterFeature","DiContainerProvider"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/FileManager/FileManagerView.js","sources":["../../../src/presentation/FileManager/FileManagerView.tsx"],"sourcesContent":["import React, { useMemo, useEffect, useCallback } from \"react\";\nimport { DiContainerProvider, useContainer, useFeature } from \"@webiny/app\";\nimport { observer } from \"mobx-react-lite\";\nimport {\n BrowserFilePicker,\n type BrowserFilePickerRenderProps\n} from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\nimport debounce from \"lodash/debounce.js\";\nimport { Heading, OverlayLoader, ScrollArea, Separator, useToast } from \"@webiny/admin-ui\";\nimport { i18n } from \"@webiny/app/i18n/index.js\";\nimport {\n LeftPanel,\n OverlayLayout,\n RightPanel,\n SplitView,\n DialogsProvider,\n useHotkeys\n} from \"@webiny/app-admin\";\nimport { FoldersFeature } from \"@webiny/app-aco/features/folders/feature.js\";\nimport { FolderTreePresenterFeature } from \"@webiny/app-aco/presentation/folderTree/feature.js\";\nimport { FileManagerPresenterFeature } from \"~/presentation/FileList/feature.js\";\nimport {\n FileManagerPresenterProvider,\n useFileManagerPresenter\n} from \"~/presentation/FileList/FileManagerPresenterProvider.js\";\nimport { FileDetailsPresenterFeature } from \"~/presentation/FileDetails/feature.js\";\nimport { SharedCacheFeature } from \"~/features/shared/feature.js\";\nimport { ListFilesFeature } from \"~/features/listFiles/feature.js\";\nimport { GetFileFeature } from \"~/features/getFile/feature.js\";\nimport { DeleteFileFeature } from \"~/features/deleteFile/feature.js\";\nimport { UpdateFileFeature } from \"~/features/updateFile/feature.js\";\nimport { FileUploaderFeature } from \"~/features/fileUploader/feature.js\";\nimport { ListTagsFeature } from \"~/features/tags/feature.js\";\nimport { GetSettingsFeature } from \"~/features/settings/feature.js\";\nimport {\n FileManagerViewWithConfig,\n useFileManagerConfig\n} from \"~/presentation/config/FileManagerViewConfig.js\";\nimport { FolderTree } from \"@webiny/app-aco/presentation/folderTree/FolderTree.js\";\nimport { BottomInfoBar } from \"~/presentation/FileList/components/BottomInfoBar/index.js\";\nimport { BulkActionBar } from \"~/presentation/FileList/components/BulkActions/index.js\";\nimport { FileDropPlaceholder } from \"~/presentation/FileList/components/FileDropPlaceholder/index.js\";\nimport { Empty } from \"~/presentation/FileList/components/Empty/index.js\";\nimport { FileDetailsDrawer } from \"~/presentation/FileDetails/components/FileDetailsDrawer.js\";\nimport { FileGrid } from \"~/presentation/FileList/components/Grid/index.js\";\nimport { FileManagerHeader } from \"~/presentation/FileList/components/Header/FileManagerHeader.js\";\nimport { FileTable } from \"~/presentation/FileList/components/Table/index.js\";\nimport { TagsList } from \"~/presentation/FileList/components/TagsList/index.js\";\nimport { UploadProgress } from \"~/presentation/FileList/components/Upload/index.js\";\nimport { GetSettingsRepository } from \"~/features/settings/abstractions.js\";\nimport { OverlayProvider, useOverlay } from \"./OverlayContext.js\";\n\nimport type { FmFile } from \"~/features/shared/types.js\";\nimport type {\n FileError,\n SelectedFile\n} from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\nimport { outputFileSelectionError } from \"~/presentation/FileManager/outputFileSelectionError.js\";\nimport type { ScrollPosition } from \"@webiny/admin-ui\";\n\nexport interface FileManagerViewProps {\n overlay?: boolean;\n onChange?: (files: FmFile[]) => void;\n onClose?: () => void;\n multiple?: boolean;\n accept?: string[];\n scope?: string;\n children?: React.ReactNode;\n}\n\nconst t = i18n.ns(\"app-admin/file-manager/file-manager-view\");\n\n// ---------------------------------------------------------------------------\n// Layout — uses original UI components, wired to the presenter.\n// ---------------------------------------------------------------------------\n\nconst FileManagerViewLayout = observer(function FileManagerViewLayout() {\n const { vm, actions } = useFileManagerPresenter();\n const { browser } = useFileManagerConfig();\n const overlay = useOverlay();\n const toast = useToast();\n\n useHotkeys({\n zIndex: 20,\n keys: {\n esc: () => overlay && overlay.onClose()\n }\n });\n\n const container = useContainer();\n const settingsRepository = useMemo(() => container.resolve(GetSettingsRepository), [container]);\n const settings = settingsRepository.settings;\n\n const uploadFiles = async (files: SelectedFile[]) => {\n await actions.upload(files);\n toast.showSuccessToast({ title: \"File upload complete.\" });\n };\n\n const onError = useCallback((errors: FileError[]) => {\n const message = outputFileSelectionError(errors);\n if (message) {\n toast.showWarningToast({ title: message });\n } else {\n toast.showWarningToast({ title: \"Couldn't process selected files.\" });\n console.error(errors);\n }\n }, []);\n\n const loadMoreOnScroll = useCallback(\n debounce(async (scrollFrame: ScrollPosition) => {\n if (scrollFrame.top > 0.8) {\n void actions.loadMore();\n }\n }, 200),\n [vm.list.pagination, actions]\n );\n\n if (!vm.fileModel) {\n return <OverlayLoader text={t`Preparing File Manager...`} />;\n }\n\n const renderList = (browseFiles: BrowserFilePickerRenderProps[\"browseFiles\"]) => {\n if (vm.loading) {\n return <OverlayLoader text={t`Loading files...`} size={\"lg\"} />;\n }\n\n if (vm.empty) {\n return <Empty isSearchResult={!vm.showFolders} browseFiles={browseFiles} />;\n }\n\n if (vm.viewMode === \"table\") {\n return (\n <ScrollArea onScroll={loadMoreOnScroll}>\n <FileTable />\n </ScrollArea>\n );\n }\n\n return (\n <ScrollArea onScroll={loadMoreOnScroll}>\n <FileGrid />\n </ScrollArea>\n );\n };\n\n const content = (\n <BrowserFilePicker\n multiple\n maxSize={settings ? settings.uploadMaxFileSize + \"b\" : \"1TB\"}\n multipleMaxSize={\"1TB\"}\n accept={overlay?.accept ?? []}\n onSuccess={uploadFiles}\n onError={onError}\n >\n {({ getDropZoneProps, browseFiles }) => (\n <>\n <FileDetailsDrawer />\n <SplitView namespace={\"fm/file/list\"}>\n <LeftPanel span={2}>\n <div className={\"flex flex-col h-main-content\"}>\n <div className={\"py-sm px-md\"}>\n <Heading level={5}>{t`File Manager`}</Heading>\n </div>\n <Separator />\n <div className={\"shrink-0 overflow-y-auto max-h-[66vh]\"}>\n <FolderTree\n vm={vm.folders}\n actions={actions.folders}\n folderActions={browser.folder.actions}\n dropConfirmation={browser.folder.dropConfirmation}\n enableActions={true}\n enableCreate={true}\n />\n </div>\n {browser.filterByTags ? (\n <>\n <Separator />\n <div className={\"flex-1 overflow-y-auto min-h-0\"}>\n <TagsList\n loading={false}\n activeTags={\n (vm.list.filters[\"tags\"] as string[]) ?? []\n }\n tags={vm.tags.map(tag => ({\n tag: tag.tag,\n count: tag.count\n }))}\n onActivatedTagsChange={tags => {\n if (tags.length > 0) {\n actions.filter.set(\"tags\", tags);\n } else {\n actions.filter.clear(\"tags\");\n actions.filter.clear(\"tags_rule\");\n }\n }}\n />\n </div>\n </>\n ) : null}\n </div>\n </LeftPanel>\n <RightPanel span={10}>\n <div\n className={\"flex flex-col relative\"}\n style={{ height: \"calc(100vh - 45px\" }}\n >\n <FileManagerHeader browseFiles={browseFiles} />\n <div\n className={\"flex-1\"}\n {...getDropZoneProps({\n onDragEnter: () => actions.setDragging(true),\n onDrop: () => actions.setDragging(false),\n onDragLeave: (e: React.DragEvent) => {\n if (\n !e.relatedTarget ||\n !e.currentTarget.contains(e.relatedTarget as Node)\n ) {\n actions.setDragging(false);\n }\n }\n })}\n data-testid={\"fm-list-wrapper\"}\n >\n {!overlay && <BulkActionBar />}\n {renderList(browseFiles)}\n {vm.dragging && <FileDropPlaceholder />}\n <UploadProgress />\n </div>\n <BottomInfoBar\n accept={overlay?.accept ?? []}\n listing={vm.list.pagination.loadingMore}\n loading={vm.list.pagination.loading}\n totalCount={vm.list.pagination.totalCount}\n currentCount={vm.list.pagination.currentCount}\n />\n </div>\n </RightPanel>\n </SplitView>\n </>\n )}\n </BrowserFilePicker>\n );\n\n if (overlay) {\n return (\n <OverlayLayout variant={\"strong\"} onExited={() => overlay.onClose()}>\n {content}\n </OverlayLayout>\n );\n }\n\n return content;\n});\n\n// ---------------------------------------------------------------------------\n// Inner component that resolves the presenter from the scoped container.\n// ---------------------------------------------------------------------------\n\nconst FileManagerViewInner = observer(\n ({\n overlay = false,\n onChange,\n onClose,\n multiple,\n accept,\n scope,\n children\n }: FileManagerViewProps) => {\n const { presenter } = useFeature(FileManagerPresenterFeature);\n\n const overlayConfig = useMemo(() => {\n if (!overlay || !onChange || !onClose) {\n return null;\n }\n\n const handleFileClick = (file: FmFile) => {\n if (multiple) {\n presenter.actions.selection.toggle(file.id);\n } else {\n onChange([file]);\n }\n };\n\n const confirmSelection = () => {\n const selectedIds = presenter.vm.list.selection.selectedIds;\n const selectedFiles = presenter.vm.list.rows.filter(f => selectedIds.has(f.id));\n if (selectedFiles.length > 0) {\n onChange(selectedFiles);\n }\n };\n\n return {\n onFileClick: handleFileClick,\n confirmSelection,\n onClose,\n accept: accept ?? [],\n multiple: multiple ?? false\n };\n }, [overlay, onChange, onClose, multiple, accept]);\n\n useEffect(() => {\n presenter.init({ scope });\n return () => presenter.dispose();\n }, [presenter, overlay, scope]);\n\n const inner = (\n <DialogsProvider>\n <FileManagerViewWithConfig>\n <FileManagerPresenterProvider presenter={presenter}>\n <FileManagerViewLayout />\n {children}\n </FileManagerPresenterProvider>\n </FileManagerViewWithConfig>\n </DialogsProvider>\n );\n\n if (overlayConfig) {\n return <OverlayProvider config={overlayConfig}>{inner}</OverlayProvider>;\n }\n\n return inner;\n }\n);\n\n// ---------------------------------------------------------------------------\n// FileManagerView — sets up DI container and renders the inner component.\n// ---------------------------------------------------------------------------\n\nexport const FileManagerView = (props: FileManagerViewProps) => {\n const container = useContainer();\n\n const scopedContainer = useMemo(() => {\n const child = container.createChildContainer();\n\n SharedCacheFeature.register(child);\n FoldersFeature.register(child, { type: \"FmFile\" });\n FolderTreePresenterFeature.register(child);\n ListFilesFeature.register(child);\n GetFileFeature.register(child);\n DeleteFileFeature.register(child);\n UpdateFileFeature.register(child);\n FileUploaderFeature.register(child);\n ListTagsFeature.register(child);\n GetSettingsFeature.register(child);\n FileDetailsPresenterFeature.register(child);\n FileManagerPresenterFeature.register(child);\n\n return child;\n }, []);\n\n return (\n <DiContainerProvider container={scopedContainer}>\n <FileManagerViewInner {...props} />\n </DiContainerProvider>\n );\n};\n"],"names":["t","i18n","FileManagerViewLayout","observer","vm","actions","useFileManagerPresenter","browser","useFileManagerConfig","overlay","useOverlay","toast","useToast","useHotkeys","container","useContainer","settingsRepository","useMemo","GetSettingsRepository","settings","uploadFiles","files","onError","useCallback","errors","message","outputFileSelectionError","console","loadMoreOnScroll","debounce","scrollFrame","OverlayLoader","renderList","browseFiles","Empty","ScrollArea","FileTable","FileGrid","content","BrowserFilePicker","getDropZoneProps","FileDetailsDrawer","SplitView","LeftPanel","Heading","Separator","FolderTree","TagsList","tag","tags","RightPanel","FileManagerHeader","e","BulkActionBar","FileDropPlaceholder","UploadProgress","BottomInfoBar","OverlayLayout","FileManagerViewInner","onChange","onClose","multiple","accept","scope","children","presenter","useFeature","FileManagerPresenterFeature","overlayConfig","handleFileClick","file","confirmSelection","selectedIds","selectedFiles","f","useEffect","inner","DialogsProvider","FileManagerViewWithConfig","FileManagerPresenterProvider","OverlayProvider","FileManagerView","props","scopedContainer","child","SharedCacheFeature","FoldersFeature","FolderTreePresenterFeature","ListFilesFeature","GetFileFeature","DeleteFileFeature","UpdateFileFeature","FileUploaderFeature","ListTagsFeature","GetSettingsFeature","FileDetailsPresenterFeature","DiContainerProvider"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,MAAMA,IAAIC,KAAK,EAAE,CAAC;AAMlB,MAAMC,wCAAwBC,SAAS;IACnC,MAAM,EAAEC,EAAE,EAAEC,OAAO,EAAE,GAAGC;IACxB,MAAM,EAAEC,OAAO,EAAE,GAAGC;IACpB,MAAMC,UAAUC;IAChB,MAAMC,QAAQC;IAEdC,WAAW;QACP,QAAQ;QACR,MAAM;YACF,KAAK,IAAMJ,WAAWA,QAAQ,OAAO;QACzC;IACJ;IAEA,MAAMK,YAAYC;IAClB,MAAMC,qBAAqBC,QAAQ,IAAMH,UAAU,OAAO,CAACI,wBAAwB;QAACJ;KAAU;IAC9F,MAAMK,WAAWH,mBAAmB,QAAQ;IAE5C,MAAMI,cAAc,OAAOC;QACvB,MAAMhB,QAAQ,MAAM,CAACgB;QACrBV,MAAM,gBAAgB,CAAC;YAAE,OAAO;QAAwB;IAC5D;IAEA,MAAMW,UAAUC,YAAY,CAACC;QACzB,MAAMC,UAAUC,yBAAyBF;QACzC,IAAIC,SACAd,MAAM,gBAAgB,CAAC;YAAE,OAAOc;QAAQ;aACrC;YACHd,MAAM,gBAAgB,CAAC;gBAAE,OAAO;YAAmC;YACnEgB,QAAQ,KAAK,CAACH;QAClB;IACJ,GAAG,EAAE;IAEL,MAAMI,mBAAmBL,YACrBM,SAAS,OAAOC;QACZ,IAAIA,YAAY,GAAG,GAAG,KACbzB,QAAQ,QAAQ;IAE7B,GAAG,MACH;QAACD,GAAG,IAAI,CAAC,UAAU;QAAEC;KAAQ;IAGjC,IAAI,CAACD,GAAG,SAAS,EACb,OAAO,WAAP,GAAO,oBAAC2B,eAAaA;QAAC,MAAM/B,CAAC,CAAC,yBAAyB,CAAC;;IAG5D,MAAMgC,aAAa,CAACC;QAChB,IAAI7B,GAAG,OAAO,EACV,OAAO,WAAP,GAAO,oBAAC2B,eAAaA;YAAC,MAAM/B,CAAC,CAAC,gBAAgB,CAAC;YAAE,MAAM;;QAG3D,IAAII,GAAG,KAAK,EACR,OAAO,WAAP,GAAO,oBAAC8B,OAAKA;YAAC,gBAAgB,CAAC9B,GAAG,WAAW;YAAE,aAAa6B;;QAGhE,IAAI7B,AAAgB,YAAhBA,GAAG,QAAQ,EACX,OAAO,WAAP,GACI,oBAAC+B,YAAUA;YAAC,UAAUP;yBAClB,oBAACQ,WAASA;QAKtB,OAAO,WAAP,GACI,oBAACD,YAAUA;YAAC,UAAUP;yBAClB,oBAACS,UAAQA;IAGrB;IAEA,MAAMC,UAAU,WAAVA,GACF,oBAACC,mBAAiBA;QACd;QACA,SAASpB,WAAWA,SAAS,iBAAiB,GAAG,MAAM;QACvD,iBAAiB;QACjB,QAAQV,SAAS,UAAU,EAAE;QAC7B,WAAWW;QACX,SAASE;OAER,CAAC,EAAEkB,gBAAgB,EAAEP,WAAW,EAAE,iBAC/B,wDACI,oBAACQ,mBAAiBA,OAAAA,WAAAA,GAClB,oBAACC,WAASA;YAAC,WAAW;yBAClB,oBAACC,WAASA;YAAC,MAAM;yBACb,oBAAC;YAAI,WAAW;yBACZ,oBAAC;YAAI,WAAW;yBACZ,oBAACC,SAAOA;YAAC,OAAO;WAAI5C,CAAC,CAAC,YAAY,CAAC,kBAEvC,oBAAC6C,WAASA,OAAAA,WAAAA,GACV,oBAAC;YAAI,WAAW;yBACZ,oBAACC,YAAUA;YACP,IAAI1C,GAAG,OAAO;YACd,SAASC,QAAQ,OAAO;YACxB,eAAeE,QAAQ,MAAM,CAAC,OAAO;YACrC,kBAAkBA,QAAQ,MAAM,CAAC,gBAAgB;YACjD,eAAe;YACf,cAAc;aAGrBA,QAAQ,YAAY,GAAG,WAAH,GACjB,wDACI,oBAACsC,WAASA,OAAAA,WAAAA,GACV,oBAAC;YAAI,WAAW;yBACZ,oBAACE,UAAQA;YACL,SAAS;YACT,YACK3C,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,IAAiB,EAAE;YAE/C,MAAMA,GAAG,IAAI,CAAC,GAAG,CAAC4C,CAAAA,MAAQ;oBACtB,KAAKA,IAAI,GAAG;oBACZ,OAAOA,IAAI,KAAK;gBACpB;YACA,uBAAuBC,CAAAA;gBACnB,IAAIA,KAAK,MAAM,GAAG,GACd5C,QAAQ,MAAM,CAAC,GAAG,CAAC,QAAQ4C;qBACxB;oBACH5C,QAAQ,MAAM,CAAC,KAAK,CAAC;oBACrBA,QAAQ,MAAM,CAAC,KAAK,CAAC;gBACzB;YACJ;eAIZ,sBAGZ,oBAAC6C,YAAUA;YAAC,MAAM;yBACd,oBAAC;YACG,WAAW;YACX,OAAO;gBAAE,QAAQ;YAAoB;yBAErC,oBAACC,mBAAiBA;YAAC,aAAalB;0BAChC,oBAAC;YACG,WAAW;YACV,GAAGO,iBAAiB;gBACjB,aAAa,IAAMnC,QAAQ,WAAW,CAAC;gBACvC,QAAQ,IAAMA,QAAQ,WAAW,CAAC;gBAClC,aAAa,CAAC+C;oBACV,IACI,CAACA,EAAE,aAAa,IAChB,CAACA,EAAE,aAAa,CAAC,QAAQ,CAACA,EAAE,aAAa,GAEzC/C,QAAQ,WAAW,CAAC;gBAE5B;YACJ,EAAE;YACF,eAAa;WAEZ,CAACI,WAAW,WAAXA,GAAW,oBAAC4C,eAAaA,OAC1BrB,WAAWC,cACX7B,GAAG,QAAQ,IAAI,WAAJ,GAAI,oBAACkD,qBAAmBA,OAAAA,WAAAA,GACpC,oBAACC,gBAAcA,QAAAA,WAAAA,GAEnB,oBAACC,eAAaA;YACV,QAAQ/C,SAAS,UAAU,EAAE;YAC7B,SAASL,GAAG,IAAI,CAAC,UAAU,CAAC,WAAW;YACvC,SAASA,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO;YACnC,YAAYA,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU;YACzC,cAAcA,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY;;IAU7E,IAAIK,SACA,OAAO,WAAP,GACI,oBAACgD,eAAaA;QAAC,SAAS;QAAU,UAAU,IAAMhD,QAAQ,OAAO;OAC5D6B;IAKb,OAAOA;AACX;AAMA,MAAMoB,uBAAuBvD,SACzB,CAAC,EACGM,UAAU,KAAK,EACfkD,QAAQ,EACRC,OAAO,EACPC,QAAQ,EACRC,MAAM,EACNC,KAAK,EACLC,QAAQ,EACW;IACnB,MAAM,EAAEC,SAAS,EAAE,GAAGC,WAAWC;IAEjC,MAAMC,gBAAgBnD,QAAQ;QAC1B,IAAI,CAACR,WAAW,CAACkD,YAAY,CAACC,SAC1B,OAAO;QAGX,MAAMS,kBAAkB,CAACC;YACrB,IAAIT,UACAI,UAAU,OAAO,CAAC,SAAS,CAAC,MAAM,CAACK,KAAK,EAAE;iBAE1CX,SAAS;gBAACW;aAAK;QAEvB;QAEA,MAAMC,mBAAmB;YACrB,MAAMC,cAAcP,UAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW;YAC3D,MAAMQ,gBAAgBR,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAACS,CAAAA,IAAKF,YAAY,GAAG,CAACE,EAAE,EAAE;YAC7E,IAAID,cAAc,MAAM,GAAG,GACvBd,SAASc;QAEjB;QAEA,OAAO;YACH,aAAaJ;YACbE;YACAX;YACA,QAAQE,UAAU,EAAE;YACpB,UAAUD,YAAY;QAC1B;IACJ,GAAG;QAACpD;QAASkD;QAAUC;QAASC;QAAUC;KAAO;IAEjDa,UAAU;QACNV,UAAU,IAAI,CAAC;YAAEF;QAAM;QACvB,OAAO,IAAME,UAAU,OAAO;IAClC,GAAG;QAACA;QAAWxD;QAASsD;KAAM;IAE9B,MAAMa,QAAQ,WAARA,GACF,oBAACC,iBAAeA,MAAAA,WAAAA,GACZ,oBAACC,2BAAyBA,MAAAA,WAAAA,GACtB,oBAACC,8BAA4BA;QAAC,WAAWd;qBACrC,oBAAC/D,uCAAqBA,OACrB8D;IAMjB,IAAII,eACA,OAAO,WAAP,GAAO,oBAACY,iBAAeA;QAAC,QAAQZ;OAAgBQ;IAGpD,OAAOA;AACX;AAOG,MAAMK,kBAAkB,CAACC;IAC5B,MAAMpE,YAAYC;IAElB,MAAMoE,kBAAkBlE,QAAQ;QAC5B,MAAMmE,QAAQtE,UAAU,oBAAoB;QAE5CuE,mBAAmB,QAAQ,CAACD;QAC5BE,eAAe,QAAQ,CAACF,OAAO;YAAE,MAAM;QAAS;QAChDG,2BAA2B,QAAQ,CAACH;QACpCI,iBAAiB,QAAQ,CAACJ;QAC1BK,eAAe,QAAQ,CAACL;QACxBM,kBAAkB,QAAQ,CAACN;QAC3BO,kBAAkB,QAAQ,CAACP;QAC3BQ,oBAAoB,QAAQ,CAACR;QAC7BS,gBAAgB,QAAQ,CAACT;QACzBU,mBAAmB,QAAQ,CAACV;QAC5BW,4BAA4B,QAAQ,CAACX;QACrCjB,4BAA4B,QAAQ,CAACiB;QAErC,OAAOA;IACX,GAAG,EAAE;IAEL,OAAO,WAAP,GACI,oBAACY,qBAAmBA;QAAC,WAAWb;qBAC5B,oBAACzB,sBAAyBwB;AAGtC"}