@webiny/app-file-manager 6.4.2 → 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.2",
3
+ "version": "6.4.3-beta.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -16,35 +16,35 @@
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.2",
20
- "@webiny/app": "6.4.2",
21
- "@webiny/app-aco": "6.4.2",
22
- "@webiny/app-admin": "6.4.2",
23
- "@webiny/app-headless-cms": "6.4.2",
24
- "@webiny/app-headless-cms-common": "6.4.2",
25
- "@webiny/app-websockets": "6.4.2",
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.2",
28
- "@webiny/form": "6.4.2",
29
- "@webiny/icons": "6.4.2",
30
- "@webiny/plugins": "6.4.2",
31
- "@webiny/react-composition": "6.4.2",
32
- "@webiny/react-properties": "6.4.2",
33
- "@webiny/sdk": "6.4.2",
34
- "@webiny/validation": "6.4.2",
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",
38
38
  "apollo-utilities": "1.3.4",
39
39
  "bytes": "3.1.2",
40
- "dayjs": "1.11.20",
40
+ "dayjs": "1.11.21",
41
41
  "dot-prop-immutable": "2.1.1",
42
- "graphql": "16.14.0",
42
+ "graphql": "16.14.2",
43
43
  "graphql-tag": "2.12.6",
44
44
  "lodash": "4.18.1",
45
45
  "mime": "4.1.0",
46
46
  "minimatch": "10.2.5",
47
- "mobx": "6.15.3",
47
+ "mobx": "6.16.1",
48
48
  "mobx-react-lite": "4.1.1",
49
49
  "react": "18.3.1",
50
50
  "react-dom": "18.3.1",
@@ -53,10 +53,10 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@svgr/webpack": "8.1.0",
56
- "@webiny/build-tools": "6.4.2",
56
+ "@webiny/build-tools": "6.4.3-beta.1",
57
57
  "rimraf": "6.1.3",
58
58
  "typescript": "6.0.3",
59
- "vitest": "4.1.7"
59
+ "vitest": "4.1.8"
60
60
  },
61
61
  "publishConfig": {
62
62
  "access": "public"
@@ -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":";;;;;;;;;;;;;;AAyBA,MAAMA,gBAAgB;AACtB,MAAMC,kBAAkB;AAExB,MAAMC;IASF,YACYC,aAA8C,EAC9CC,mBAAkD,EAClDC,oBAAoD,EACpDC,WAA6C,EAC7CC,cAA4C,EAC5CC,YAAoC,EACpCC,YAAoC,EACpCC,gBAA4C,EAC5CC,cAAwC,EACxCC,2BAAkE,EAClEC,iBAA8C,CACxD;aAXUV,aAAa,GAAbA;aACAC,mBAAmB,GAAnBA;aACAC,oBAAoB,GAApBA;aACAC,WAAW,GAAXA;aACAC,cAAc,GAAdA;aACAC,YAAY,GAAZA;aACAC,YAAY,GAAZA;aACAC,gBAAgB,GAAhBA;aACAC,cAAc,GAAdA;aACAC,2BAA2B,GAA3BA;aACAC,iBAAiB,GAAjBA;aAnBJ,SAAS,GAAqB;aAC9B,SAAS,GAAG;aACZ,gBAAgB,GAAwB;aACxC,YAAY,GAAiC;aAC7C,eAAe,GAAG;aAClB,UAAU,GAAoB;aAC9B,cAAc,GAAG;aAkEzB,OAAO,GAAwB;YAC3B,QAAQ;gBACJ,KAAK,CAACC,QAAkB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAACA;gBAC9D,OAAO,IAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK;YACxD;YACA,MAAM;gBACF,KAAK,CAACC,OAAeC,YACjB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAACD,OAAOC;gBAC/C,QAAQ,CAACD,QAAkB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAACA;YACtE;YACA,QAAQ;gBACJ,KAAK,CAACE,KAAaC,QAAmB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAACD,KAAKC;gBACjF,OAAO,CAACD,MAAgB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAACA;gBAChE,UAAU,IAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ;YAC9D;YACA,WAAW;gBACP,QAAQ,CAACE,KAAe,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAACA;gBACpE,eAAe,CAACA,KAAe,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,aAAa,CAACA;gBAClF,WAAW,IAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS;gBAC/D,aAAa,IAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,WAAW;gBACnE,YAAY,CAACC,MAAkB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,UAAU,CAACA;gBAC/E,YAAY,CAACD,KAAe,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,UAAU,CAACA;YAChF;YACA,UAAU,IAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ;YACnD,SAAS,IAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO;YACjD,QAAQ,OAAOE;gBACX,MAAMC,WAAW,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,eAAe,IAAI;gBAChE,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAC9BD,MAAM,GAAG,CAACE,CAAAA,OAAS;wBACf,MAAMA,KAAK,GAAG,CAAC,IAAI;wBACnB,MAAM;4BACF,MAAMA,KAAK,IAAI;4BACf,MAAMA,KAAK,IAAI;4BACf,UAAU;gCAAED;4BAAS;wBACzB;oBACJ;gBAEJ,IAAI,CAAC,YAAY,CAAC,KAAK;YAC3B;YACA,aAAa,CAACE;gBACV,IAAI,CAAC,SAAS,GAAGA;gBACjB,IAAI,CAAC,YAAY,CAAC,GAAG,CAACxB,eAAewB;YACzC;YACA,aAAa,CAACC;gBACV,IAAI,CAAC,SAAS,GAAGA;YACrB;YACA,aAAa;gBACT,IAAI,CAAC,eAAe,GAAG;YAC3B;YACA,aAAa;gBACT,IAAI,CAAC,eAAe,GAAG;YAC3B;YACA,iBAAiB,CAACN;gBACd,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,oBAAoB;gBACxC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAACA;YAC5C;YACA,iBAAiB;gBACb,IAAI,CAAC,YAAY,GAAG;YACxB;YACA,SAAS;gBAEL,cAAc,CAACG;oBACX,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK;oBACvC,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAACA;gBAC1C;gBACA,cAAc,CAACI,iBACX,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAACA;gBAC1C,YAAY,CAACJ,WAAqB,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAACA;gBACtE,cAAc,CAACA,WAAqB,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAACA;gBAC1E,YAAY,CAACA,UAAkBK,iBAC3B,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAACL,UAAUK;gBAClD,kBAAkB,CAACC,YACf,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAACA;gBAC9C,oBAAoB,CAACN,WACjB,IAAI,CAAC,mBAAmB,CAAC,kBAAkB,CAACA;gBAChD,gBAAgB,CAACA,WAAqB,IAAI,CAAC,mBAAmB,CAAC,cAAc,CAACA;gBAC9E,iBAAiB,IAAM,IAAI,CAAC,mBAAmB,CAAC,eAAe;gBAC/D,iBAAiB;oBACb,IAAI,CAAC,mBAAmB,CAAC,eAAe;gBAC5C;YACJ;QACJ;QApIIO,mBAGE,IAAI,EAAE;YACJ,kBAAkB;YAClB,sBAAsB;YACtB,gBAAgB;YAChB,IAAIC;QACR;QAEA,MAAMC,SAAS,IAAI,CAAC,YAAY,CAAC,GAAG,CAAmB/B;QACvD,IAAI+B,AAAW,YAAXA,UAAsBA,AAAW,WAAXA,QACtB,IAAI,CAAC,SAAS,GAAGA;IAEzB;IAEA,IAAI,KAA4B;QAC5B,OAAO;YACH,WAAW,IAAI,CAAC,UAAU;YAC1B,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE;YAC3B,SAAS,IAAI,CAAC,mBAAmB,CAAC,EAAE;YACpC,aAAa,IAAI,CAAC,YAAY;YAG9B,aAAa;gBACT,SAAS,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;gBAClC,WAAW,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;gBACtC,SAAS,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;gBAClC,WAAW,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;gBACtC,aAAa,CAACR,OAAiB,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQA;gBAChE,eAAe,CAACA,OAAiB,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,QAAQA;YACxE;YACA,QAAQ;gBACJ,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI;gBAC/B,iBAAiB;oBACb,YAAY,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,eAAe,CAAC,UAAU;oBAC3D,WAAW,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI;oBACpD,YAAY,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK;gBAC1D;gBACA,aAAa,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,WAAW;YACjD;YACA,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI;YAC9B,SAAS,IAAI,CAAC,SAAS;YACvB,OAAO,IAAI,CAAC,OAAO;YACnB,aAAa,IAAI,CAAC,iBAAiB;YACnC,UAAU,IAAI,CAAC,SAAS;YACxB,UAAU,IAAI,CAAC,SAAS;YACxB,gBAAgB,IAAI,CAAC,eAAe;QACxC;IACJ;IAqFA,KAAKS,MAA+B,EAAQ;QACxC,MAAMC,kBACFD,QAAQ,mBAAmB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAS/B,oBAAoB;QAGjF,IAAI,CAAC,cAAc,GAAG,CAAC+B,QAAQ;QAE/B,MAAME,aAAa,IAAIC,mBACnB,IAAI,CAAC,gBAAgB,EACrB,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,2BAA2B,EAChCH,QAAQ;QAGZ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YACpBE;YACA,aAAa;gBAAE,OAAO;gBAAa,WAAW;YAAO;YACrD,gBAAgB;gBAAE,UAAUD;YAAgB;YAC5C,OAAO;QACX;QAEA,IAAIA,AAAoB,WAApBA,iBACA,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAACA;QAK1C,IAAI,CAAC,gBAAgB,GAAGG,SACpB,IAAM,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,eAAe,EACjDd,CAAAA;YACI,MAAMe,oBAAoBf,YAAY;YACtC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,YAAYe;YAElD,IAAI,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,YAAY,CAAC,GAAG,CAACpC,iBAAiBoC;QAE/C;QAGC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAK7B,IAAI,CAAC,iBAAiB,CAAC,QAAQ,GAAG,IAAI,CAACC,CAAAA;YACxCC,YAAY;gBACR,IAAI,CAAC,UAAU,GAAGD;YACtB;QACJ;IACJ;IAEA,UAAgB;QACZ,IAAI,IAAI,CAAC,gBAAgB,EAAE;YACvB,IAAI,CAAC,gBAAgB;YACrB,IAAI,CAAC,gBAAgB,GAAG;QAC5B;IACJ;IAEQ,YAAqB;QACzB,OAAO,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,IAAI,AAAsC,MAAtC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM;IACxF;IAEQ,UAAmB;QACvB,IAAI,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EACxC,OAAO;QAGX,IAAI,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,GACpC,OAAO;QAGX,IAAI,IAAI,CAAC,iBAAiB,MAAM,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,GAAG,GAC9E,OAAO;QAGX,OAAO;IACX;IAKQ,oBAA6B;QACjC,MAAM,EAAEE,YAAY,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,EAAE;QAC9C,IAAI,CAACA,cACD,OAAO;QAGX,IAAIA,aAAa,MAAM,EACnB,OAAO;QAIX,MAAMC,aAAaC,OAAO,IAAI,CAACF,aAAa,OAAO,IAAI,CAAC,GAAG,MAAM,CAACG,CAAAA,IAAKA,AAAM,eAANA;QACvE,IAAIF,WAAW,MAAM,GAAG,GACpB,OAAO;QAGX,OAAO;IACX;AACJ;AAEO,MAAMG,4CAAuBC,qBAAAA,oBAAgC,CAAC;IACjE,gBAAgB3C;IAChB,cAAc;QACV4C;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;KACH;AACL"}
@@ -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":"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"}