@webiny/app-website-builder 6.5.0-beta.0 → 6.5.0-beta.10

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.
Files changed (167) hide show
  1. package/BaseEditor/config/Sidebar/Tab.d.ts +0 -4
  2. package/BaseEditor/config/Sidebar/Tab.js +5 -21
  3. package/BaseEditor/config/Sidebar/Tab.js.map +1 -1
  4. package/BaseEditor/defaultConfig/Content/AddressBar/PreviewDomain.js +6 -66
  5. package/BaseEditor/defaultConfig/Content/AddressBar/PreviewDomain.js.map +1 -1
  6. package/BaseEditor/defaultConfig/Content/Preview/DropZoneManager.js.map +1 -1
  7. package/BaseEditor/defaultConfig/Content/Preview/Iframe.d.ts +2 -2
  8. package/BaseEditor/defaultConfig/Content/Preview/Overlays/ElementOverlays.d.ts +2 -2
  9. package/BaseEditor/defaultConfig/Content/Preview/PreviewContainer.js +1 -1
  10. package/BaseEditor/defaultConfig/Content/Preview/PreviewContainer.js.map +1 -1
  11. package/BaseEditor/defaultConfig/Content/usePreviewDomain.d.ts +1 -6
  12. package/BaseEditor/defaultConfig/Content/usePreviewDomain.js +1 -26
  13. package/BaseEditor/defaultConfig/DocumentStateEditor/DocumentStateEditor.d.ts +2 -2
  14. package/BaseEditor/defaultConfig/ElementInputRenderers.js +12 -0
  15. package/BaseEditor/defaultConfig/ElementInputRenderers.js.map +1 -1
  16. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/ElementSettingsGroup.js +2 -5
  17. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/ElementSettingsGroup.js.map +1 -1
  18. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/PreviewedBindings.d.ts +22 -0
  19. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/PreviewedBindings.js +17 -0
  20. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/PreviewedBindings.js.map +1 -0
  21. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/PreviewedBindings.test.d.ts +1 -0
  22. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/PreviewedBindings.test.js +64 -0
  23. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/PreviewedBindings.test.js.map +1 -0
  24. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/useInputValue.js +7 -2
  25. package/BaseEditor/defaultConfig/Sidebar/ElementSettings/useInputValue.js.map +1 -1
  26. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Background/BackgroundColor.d.ts +6 -2
  27. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Background/BackgroundImage.d.ts +6 -2
  28. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Background/BackgroundImage.js +8 -1
  29. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Background/BackgroundImage.js.map +1 -1
  30. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Background/BackgroundPosition.d.ts +6 -2
  31. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Background/BackgroundScaling.d.ts +6 -2
  32. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Border/BorderColor.d.ts +2 -2
  33. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Border/BorderRadius.d.ts +2 -2
  34. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Border/BorderStyle.d.ts +2 -2
  35. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Border/BorderWidth.d.ts +2 -2
  36. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Layout/Alignment.d.ts +6 -2
  37. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Layout/LengthWithUnitInput.d.ts +2 -2
  38. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/MarginPadding/Margin.d.ts +2 -2
  39. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/MarginPadding/Padding.d.ts +2 -2
  40. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/Groups/Visibility/Visibility.d.ts +2 -2
  41. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/SidebarRow.js +1 -1
  42. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/SidebarRow.js.map +1 -1
  43. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/StyleSettingsGroup.js +2 -5
  44. package/BaseEditor/defaultConfig/Sidebar/StyleSettings/StyleSettingsGroup.js.map +1 -1
  45. package/BaseEditor/defaultConfig/Toolbar/Navigator/Navigator.d.ts +2 -2
  46. package/DocumentEditor/DocumentEditor.d.ts +2 -2
  47. package/DocumentEditor/StateInspector.d.ts +4 -2
  48. package/Extension.js +3 -0
  49. package/Extension.js.map +1 -1
  50. package/domain/Page/Page.d.ts +2 -0
  51. package/domain/Page/Page.js +1 -0
  52. package/domain/Page/Page.js.map +1 -1
  53. package/domain/PageRevision/PageRevision.d.ts +11 -0
  54. package/domain/PageRevision/PageRevision.js +20 -0
  55. package/domain/PageRevision/PageRevision.js.map +1 -1
  56. package/ecommerce/components/ResourcePage.d.ts +6 -2
  57. package/ecommerce/components/ResourcesList.d.ts +4 -4
  58. package/ecommerce/components/ResourcesPicker.d.ts +9 -4
  59. package/editorSdk/CommandBus.js.map +1 -1
  60. package/features/pages/createPageRevisionFrom/CreatePageRevisionFrom.test.js +41 -1
  61. package/features/pages/createPageRevisionFrom/CreatePageRevisionFrom.test.js.map +1 -1
  62. package/features/pages/createPageRevisionFrom/CreatePageRevisionFromRepository.d.ts +3 -2
  63. package/features/pages/createPageRevisionFrom/CreatePageRevisionFromRepository.js +8 -2
  64. package/features/pages/createPageRevisionFrom/CreatePageRevisionFromRepository.js.map +1 -1
  65. package/features/pages/deletePageRevision/DeletePageRevision.test.d.ts +1 -0
  66. package/features/pages/deletePageRevision/DeletePageRevision.test.js +67 -0
  67. package/features/pages/deletePageRevision/DeletePageRevision.test.js.map +1 -0
  68. package/features/pages/deletePageRevision/DeletePageRevisionGateway.d.ts +1 -1
  69. package/features/pages/deletePageRevision/DeletePageRevisionGateway.js +2 -3
  70. package/features/pages/deletePageRevision/DeletePageRevisionGateway.js.map +1 -1
  71. package/features/pages/deletePageRevision/DeletePageRevisionRepository.d.ts +4 -2
  72. package/features/pages/deletePageRevision/DeletePageRevisionRepository.js +7 -3
  73. package/features/pages/deletePageRevision/DeletePageRevisionRepository.js.map +1 -1
  74. package/features/pages/deletePageRevision/DeletePageRevisionUseCase.js +1 -1
  75. package/features/pages/deletePageRevision/DeletePageRevisionUseCase.js.map +1 -1
  76. package/features/pages/deletePageRevision/abstractions.d.ts +2 -3
  77. package/features/pages/deletePageRevision/abstractions.js.map +1 -1
  78. package/features/pages/getPage/PageGatewayDto.d.ts +2 -0
  79. package/features/pages/publishPage/PublishPage.test.js +41 -1
  80. package/features/pages/publishPage/PublishPage.test.js.map +1 -1
  81. package/features/pages/publishPage/PublishPageRepository.d.ts +3 -2
  82. package/features/pages/publishPage/PublishPageRepository.js +15 -4
  83. package/features/pages/publishPage/PublishPageRepository.js.map +1 -1
  84. package/features/pages/shared/pageGraphQLFields.js +1 -0
  85. package/features/pages/shared/pageGraphQLFields.js.map +1 -1
  86. package/features/pages/unpublishPage/UnpublishPage.test.js +4 -1
  87. package/features/pages/unpublishPage/UnpublishPage.test.js.map +1 -1
  88. package/features/pages/unpublishPage/UnpublishPageRepository.d.ts +3 -2
  89. package/features/pages/unpublishPage/UnpublishPageRepository.js +12 -4
  90. package/features/pages/unpublishPage/UnpublishPageRepository.js.map +1 -1
  91. package/inputRenderers/DateTimeInput.d.ts +9 -0
  92. package/inputRenderers/DateTimeInput.js +21 -0
  93. package/inputRenderers/DateTimeInput.js.map +1 -0
  94. package/inputRenderers/DateTimeInput.test.d.ts +1 -0
  95. package/inputRenderers/DateTimeInput.test.js +27 -0
  96. package/inputRenderers/DateTimeInput.test.js.map +1 -0
  97. package/inputRenderers/FileInput.js +1 -1
  98. package/inputRenderers/FileInput.js.map +1 -1
  99. package/inputRenderers/RadioInput.d.ts +3 -0
  100. package/inputRenderers/RadioInput.js +20 -0
  101. package/inputRenderers/RadioInput.js.map +1 -0
  102. package/inputRenderers/TagsInput.d.ts +3 -0
  103. package/inputRenderers/TagsInput.js +22 -0
  104. package/inputRenderers/TagsInput.js.map +1 -0
  105. package/package.json +31 -31
  106. package/presentation/pages/CreatePage/CreatePageDialog.d.ts +2 -2
  107. package/presentation/pages/PageEditor/Revisions/RevisionListDrawer.js +4 -0
  108. package/presentation/pages/PageEditor/Revisions/RevisionListDrawer.js.map +1 -1
  109. package/presentation/pages/PageEditor/Revisions/RevisionListItem.d.ts +3 -3
  110. package/presentation/pages/PageEditor/Revisions/RevisionListItem.js +9 -20
  111. package/presentation/pages/PageEditor/Revisions/RevisionListItem.js.map +1 -1
  112. package/presentation/pages/PageEditor/Revisions/RevisionsList.js +7 -19
  113. package/presentation/pages/PageEditor/Revisions/RevisionsList.js.map +1 -1
  114. package/presentation/pages/PageEditor/Revisions/useRevision.d.ts +1 -1
  115. package/presentation/pages/PageEditor/Revisions/useRevision.js +84 -14
  116. package/presentation/pages/PageEditor/Revisions/useRevision.js.map +1 -1
  117. package/presentation/pages/PageEditor/Revisions/useRevisionList.d.ts +3 -0
  118. package/presentation/pages/PageEditor/Revisions/useRevisionList.js +31 -0
  119. package/presentation/pages/PageEditor/Revisions/useRevisionList.js.map +1 -0
  120. package/presentation/pages/PageEditor/TopBar/PageSettingsDrawer.d.ts +2 -2
  121. package/presentation/pages/PageEditor/TopBar/RevisionsMenu.js +7 -19
  122. package/presentation/pages/PageEditor/TopBar/RevisionsMenu.js.map +1 -1
  123. package/presentation/pages/PageList/PageListDataSource.js +1 -1
  124. package/presentation/pages/PageList/PageListDataSource.js.map +1 -1
  125. package/presentation/pages/PageList/PageListPresenter.test.d.ts +1 -0
  126. package/presentation/pages/PageList/PageListPresenter.test.js +293 -0
  127. package/presentation/pages/PageList/PageListPresenter.test.js.map +1 -0
  128. package/presentation/pages/PageList/components/BulkActions/BulkActionDelete.d.ts +2 -2
  129. package/presentation/pages/PageList/components/BulkActions/BulkActionDuplicate.d.ts +2 -2
  130. package/presentation/pages/PageList/components/BulkActions/BulkActionMovePage.d.ts +2 -2
  131. package/presentation/pages/PageList/components/BulkActions/BulkActionPublish.d.ts +2 -2
  132. package/presentation/pages/PageList/components/BulkActions/BulkActionUnpublish.d.ts +2 -2
  133. package/presentation/pages/PageList/components/DocumentList.d.ts +2 -2
  134. package/presentation/pages/PageList/components/Table/Table.d.ts +2 -2
  135. package/presentation/pages/RevisionList/RevisionListPresenter.d.ts +27 -0
  136. package/presentation/pages/RevisionList/RevisionListPresenter.js +73 -0
  137. package/presentation/pages/RevisionList/RevisionListPresenter.js.map +1 -0
  138. package/presentation/pages/RevisionList/RevisionListPresenter.test.d.ts +1 -0
  139. package/presentation/pages/RevisionList/RevisionListPresenter.test.js +219 -0
  140. package/presentation/pages/RevisionList/RevisionListPresenter.test.js.map +1 -0
  141. package/presentation/pages/RevisionList/abstractions.d.ts +34 -0
  142. package/presentation/pages/RevisionList/abstractions.js +5 -0
  143. package/presentation/pages/RevisionList/abstractions.js.map +1 -0
  144. package/presentation/pages/RevisionList/feature.d.ts +3 -0
  145. package/presentation/pages/RevisionList/feature.js +17 -0
  146. package/presentation/pages/RevisionList/feature.js.map +1 -0
  147. package/presentation/pages/RevisionList/index.d.ts +2 -0
  148. package/presentation/pages/RevisionList/index.js +1 -0
  149. package/presentation/pages/TrashBin/WbTrashBin.d.ts +2 -2
  150. package/presentation/redirects/RedirectList/RedirectListDataSource.js +1 -1
  151. package/presentation/redirects/RedirectList/RedirectListDataSource.js.map +1 -1
  152. package/presentation/redirects/RedirectList/RedirectListPresenter.js.map +1 -1
  153. package/presentation/redirects/RedirectList/RedirectListPresenter.test.d.ts +1 -0
  154. package/presentation/redirects/RedirectList/RedirectListPresenter.test.js +273 -0
  155. package/presentation/redirects/RedirectList/RedirectListPresenter.test.js.map +1 -0
  156. package/presentation/redirects/RedirectList/components/BulkActionDelete.d.ts +2 -2
  157. package/presentation/redirects/RedirectList/components/BulkActionMove.d.ts +2 -2
  158. package/presentation/redirects/RedirectList/components/CreateRedirectDialog.d.ts +2 -2
  159. package/presentation/redirects/RedirectList/components/DocumentList.d.ts +2 -2
  160. package/presentation/redirects/RedirectList/components/EditRedirectDialog.d.ts +2 -2
  161. package/presentation/redirects/RedirectList/components/Table/Table.d.ts +2 -2
  162. package/shared/fileManagerItemToValue.js +3 -1
  163. package/shared/fileManagerItemToValue.js.map +1 -1
  164. package/shared/fileManagerItemToValue.test.d.ts +1 -0
  165. package/shared/fileManagerItemToValue.test.js +73 -0
  166. package/shared/fileManagerItemToValue.test.js.map +1 -0
  167. package/BaseEditor/defaultConfig/Content/usePreviewDomain.js.map +0 -1
@@ -0,0 +1,273 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { observable } from "mobx";
3
+ import { Container } from "@webiny/di";
4
+ import { ListCache } from "@webiny/app-admin/features/listCache/index.js";
5
+ import { ListPresenter } from "@webiny/app-admin/presentation/listPresenter/ListPresenter.js";
6
+ import { FolderTreePresenter } from "@webiny/app-aco/presentation/folderTree/abstractions.js";
7
+ import { GetDescendantFoldersUseCase } from "@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js";
8
+ import { Redirect } from "../../../domain/Redirect/Redirect.js";
9
+ import { RedirectsListCache } from "../../../features/redirects/shared/abstractions.js";
10
+ import { ListRedirectsGateway } from "../../../features/redirects/listRedirects/abstractions.js";
11
+ import { ListRedirectsUseCase } from "../../../features/redirects/listRedirects/ListRedirectsUseCase.js";
12
+ import { ListRedirectsRepository } from "../../../features/redirects/listRedirects/ListRedirectsRepository.js";
13
+ import { CreateRedirectGateway, CreateRedirectUseCase } from "../../../features/redirects/createRedirect/abstractions.js";
14
+ import { CreateRedirectUseCase as CreateRedirectUseCase_js_CreateRedirectUseCase } from "../../../features/redirects/createRedirect/CreateRedirectUseCase.js";
15
+ import { CreateRedirectRepository } from "../../../features/redirects/createRedirect/CreateRedirectRepository.js";
16
+ import { UpdateRedirectGateway, UpdateRedirectUseCase } from "../../../features/redirects/updateRedirect/abstractions.js";
17
+ import { UpdateRedirectUseCase as UpdateRedirectUseCase_js_UpdateRedirectUseCase } from "../../../features/redirects/updateRedirect/UpdateRedirectUseCase.js";
18
+ import { UpdateRedirectRepository } from "../../../features/redirects/updateRedirect/UpdateRedirectRepository.js";
19
+ import { CreateRedirectPresenter, EditRedirectPresenter, RedirectListPresenter } from "./abstractions.js";
20
+ import { RedirectListPresenter as external_RedirectListPresenter_js_RedirectListPresenter } from "./RedirectListPresenter.js";
21
+ const TOTAL_REDIRECTS = 120;
22
+ const PAGE_SIZE = 50;
23
+ const BASE_TIME = Date.UTC(2026, 0, 1);
24
+ const minutes = (n)=>new Date(BASE_TIME + 60000 * n).toISOString();
25
+ function createRedirects(count) {
26
+ return Array.from({
27
+ length: count
28
+ }, (_, i)=>Redirect.create({
29
+ id: `redirect-${i}`,
30
+ location: {
31
+ folderId: "root"
32
+ },
33
+ redirectFrom: `/from-${i}`,
34
+ redirectTo: `/to-${i}`,
35
+ redirectType: "permanent",
36
+ isEnabled: i % 2 === 0,
37
+ createdOn: minutes(i),
38
+ savedOn: minutes(count - i)
39
+ }));
40
+ }
41
+ function sortRedirects(redirects, sort = "createdOn_DESC") {
42
+ const [field, direction] = sort.split("_");
43
+ const multiplier = "ASC" === direction ? 1 : -1;
44
+ return [
45
+ ...redirects
46
+ ].sort((a, b)=>{
47
+ const left = String(a[field]);
48
+ const right = String(b[field]);
49
+ return left === right ? 0 : multiplier * (left < right ? -1 : 1);
50
+ });
51
+ }
52
+ function createListGateway(redirects) {
53
+ return {
54
+ execute: vi.fn(async (params)=>{
55
+ const sorted = sortRedirects(redirects, params.sort?.[0]);
56
+ const offset = params.after ? Number(params.after) : 0;
57
+ const limit = params.limit ?? PAGE_SIZE;
58
+ const data = sorted.slice(offset, offset + limit);
59
+ const next = offset + data.length;
60
+ const hasMoreItems = next < sorted.length;
61
+ return {
62
+ data,
63
+ meta: {
64
+ cursor: hasMoreItems ? String(next) : null,
65
+ hasMoreItems,
66
+ totalCount: sorted.length
67
+ }
68
+ };
69
+ })
70
+ };
71
+ }
72
+ function createFolderTreePresenter() {
73
+ const vm = observable({
74
+ folders: [],
75
+ tree: [],
76
+ currentFolderId: null,
77
+ currentFolder: null,
78
+ loading: false,
79
+ operation: {
80
+ active: false,
81
+ mode: null
82
+ },
83
+ isRootFolder: true,
84
+ currentFolderTitle: "All redirects",
85
+ childFolders: [],
86
+ loadingNodeIds: []
87
+ });
88
+ return {
89
+ get vm () {
90
+ return vm;
91
+ },
92
+ selectFolder: vi.fn(),
93
+ createFolder: vi.fn(),
94
+ editFolder: vi.fn(),
95
+ deleteFolder: vi.fn().mockResolvedValue(void 0),
96
+ moveFolder: vi.fn().mockResolvedValue(void 0),
97
+ loadChildFolders: vi.fn().mockResolvedValue(void 0),
98
+ canManageStructure: vi.fn().mockReturnValue(true),
99
+ getAncestorIds: vi.fn().mockReturnValue([]),
100
+ submitOperation: vi.fn().mockResolvedValue(true),
101
+ cancelOperation: vi.fn(),
102
+ onFolderChange: vi.fn().mockReturnValue(()=>{})
103
+ };
104
+ }
105
+ function range(from, to) {
106
+ const step = from <= to ? 1 : -1;
107
+ const ids = [];
108
+ for(let i = from; step > 0 ? i <= to : i >= to; i += step)ids.push(`redirect-${i}`);
109
+ return ids;
110
+ }
111
+ describe("RedirectListPresenter", ()=>{
112
+ let presenter;
113
+ let container;
114
+ let redirects;
115
+ const rowIds = ()=>presenter.vm.list.rows.map((row)=>row.id);
116
+ const waitForQuery = async (field, direction)=>{
117
+ await vi.waitFor(()=>{
118
+ expect(presenter.vm.list.appliedQuery?.sort).toEqual({
119
+ field,
120
+ direction
121
+ });
122
+ expect(presenter.vm.list.pagination.loading).toBe(false);
123
+ });
124
+ };
125
+ const initAndWait = async ()=>{
126
+ presenter.init();
127
+ await waitForQuery("createdOn", "DESC");
128
+ };
129
+ const sortAndWait = async (field, direction)=>{
130
+ presenter.actions.sort.set(field, direction);
131
+ await waitForQuery(field, direction);
132
+ };
133
+ const loadAll = async ()=>{
134
+ while(presenter.vm.list.pagination.hasMore)await presenter.actions.loadMore();
135
+ };
136
+ beforeEach(()=>{
137
+ container = new Container();
138
+ redirects = createRedirects(TOTAL_REDIRECTS);
139
+ container.registerInstance(RedirectsListCache, new ListCache("id"));
140
+ container.registerInstance(ListRedirectsGateway, createListGateway(redirects));
141
+ container.register(ListRedirectsRepository).inSingletonScope();
142
+ container.register(ListRedirectsUseCase);
143
+ container.registerInstance(CreateRedirectGateway, {
144
+ execute: vi.fn(async (params)=>Redirect.create({
145
+ ...params,
146
+ id: "redirect-new",
147
+ createdOn: minutes(TOTAL_REDIRECTS + 1),
148
+ savedOn: minutes(TOTAL_REDIRECTS + 1)
149
+ }))
150
+ });
151
+ container.register(CreateRedirectRepository).inSingletonScope();
152
+ container.register(CreateRedirectUseCase_js_CreateRedirectUseCase);
153
+ container.registerInstance(UpdateRedirectGateway, {
154
+ execute: vi.fn(async (params)=>{
155
+ const existing = redirects.find((r)=>r.id === params.id);
156
+ return Redirect.create({
157
+ ...existing,
158
+ ...params,
159
+ savedOn: minutes(TOTAL_REDIRECTS + 1)
160
+ });
161
+ })
162
+ });
163
+ container.register(UpdateRedirectRepository).inSingletonScope();
164
+ container.register(UpdateRedirectUseCase_js_UpdateRedirectUseCase);
165
+ container.register(ListPresenter);
166
+ container.registerInstance(FolderTreePresenter, createFolderTreePresenter());
167
+ container.registerInstance(GetDescendantFoldersUseCase, {
168
+ execute: ()=>[]
169
+ });
170
+ container.registerInstance(CreateRedirectPresenter, {
171
+ vm: {},
172
+ init: vi.fn(),
173
+ save: vi.fn()
174
+ });
175
+ container.registerInstance(EditRedirectPresenter, {
176
+ vm: {},
177
+ loadRedirect: vi.fn(),
178
+ save: vi.fn()
179
+ });
180
+ container.register(external_RedirectListPresenter_js_RedirectListPresenter);
181
+ presenter = container.resolve(RedirectListPresenter);
182
+ });
183
+ describe("pagination", ()=>{
184
+ it("should show the most recently created redirects first", async ()=>{
185
+ await initAndWait();
186
+ expect(rowIds()).toEqual(range(119, 70));
187
+ });
188
+ it("should append the next batch of redirects to the bottom of the list", async ()=>{
189
+ await initAndWait();
190
+ await presenter.actions.loadMore();
191
+ expect(rowIds()).toEqual(range(119, 20));
192
+ });
193
+ it("should keep all redirects in order after loading everything", async ()=>{
194
+ await initAndWait();
195
+ await loadAll();
196
+ expect(rowIds()).toEqual(range(119, 0));
197
+ });
198
+ });
199
+ describe("changing the sort", ()=>{
200
+ it("should show only the first batch, sorted by the new field", async ()=>{
201
+ await initAndWait();
202
+ await sortAndWait("savedOn", "DESC");
203
+ expect(rowIds()).toEqual(range(0, 49));
204
+ });
205
+ it("should show only the first batch when sorting by a boolean field", async ()=>{
206
+ await initAndWait();
207
+ await sortAndWait("isEnabled", "DESC");
208
+ const expected = sortRedirects(redirects, "isEnabled_DESC").slice(0, PAGE_SIZE).map((r)=>r.id);
209
+ expect(rowIds()).toEqual(expected);
210
+ });
211
+ it("should restore the original order when switching back", async ()=>{
212
+ await initAndWait();
213
+ await presenter.actions.loadMore();
214
+ await sortAndWait("createdOn", "ASC");
215
+ await sortAndWait("createdOn", "DESC");
216
+ expect(rowIds()).toEqual(range(119, 70));
217
+ });
218
+ });
219
+ describe("local cache changes", ()=>{
220
+ const createRedirect = ()=>container.resolve(CreateRedirectUseCase).execute({
221
+ location: {
222
+ folderId: "root"
223
+ },
224
+ redirectFrom: "/new",
225
+ redirectTo: "/target",
226
+ redirectType: "permanent",
227
+ isEnabled: true
228
+ });
229
+ const updateRedirect = (id)=>{
230
+ const existing = redirects.find((r)=>r.id === id);
231
+ return container.resolve(UpdateRedirectUseCase).execute({
232
+ id,
233
+ redirectFrom: existing.redirectFrom,
234
+ redirectTo: "/changed",
235
+ redirectType: existing.redirectType,
236
+ isEnabled: existing.isEnabled
237
+ });
238
+ };
239
+ it("should show a created redirect at the top when it sorts first", async ()=>{
240
+ await initAndWait();
241
+ await createRedirect();
242
+ expect(rowIds()).toEqual([
243
+ "redirect-new",
244
+ ...range(119, 70)
245
+ ]);
246
+ });
247
+ it("should not show a created redirect that sorts past the loaded redirects", async ()=>{
248
+ await initAndWait();
249
+ await sortAndWait("createdOn", "ASC");
250
+ await createRedirect();
251
+ expect(rowIds()).toEqual(range(0, 49));
252
+ });
253
+ it("should keep an updated redirect in its position when sorted by createdOn", async ()=>{
254
+ await initAndWait();
255
+ await updateRedirect("redirect-100");
256
+ expect(rowIds()).toEqual(range(119, 70));
257
+ const updated = presenter.vm.list.rows.find((row)=>"redirect-100" === row.id);
258
+ expect(updated?.redirectTo).toBe("/changed");
259
+ });
260
+ it("should move an updated redirect to the top when sorted by savedOn", async ()=>{
261
+ await initAndWait();
262
+ await sortAndWait("savedOn", "DESC");
263
+ await updateRedirect("redirect-30");
264
+ expect(rowIds()).toEqual([
265
+ "redirect-30",
266
+ ...range(0, 29),
267
+ ...range(31, 49)
268
+ ]);
269
+ });
270
+ });
271
+ });
272
+
273
+ //# sourceMappingURL=RedirectListPresenter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation/redirects/RedirectList/RedirectListPresenter.test.js","sources":["../../../../src/presentation/redirects/RedirectList/RedirectListPresenter.test.ts"],"sourcesContent":["import { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { observable } 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/ListPresenter.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport { Redirect } from \"~/domain/Redirect/Redirect.js\";\nimport { RedirectsListCache } from \"~/features/redirects/shared/abstractions.js\";\nimport {\n ListRedirectsGateway,\n type ListRedirectsGatewayParams,\n type ListRedirectsGatewayResult\n} from \"~/features/redirects/listRedirects/abstractions.js\";\nimport { ListRedirectsUseCase } from \"~/features/redirects/listRedirects/ListRedirectsUseCase.js\";\nimport { ListRedirectsRepository } from \"~/features/redirects/listRedirects/ListRedirectsRepository.js\";\nimport {\n CreateRedirectGateway,\n CreateRedirectUseCase as CreateRedirectUseCaseAbstraction\n} from \"~/features/redirects/createRedirect/abstractions.js\";\nimport { CreateRedirectUseCase } from \"~/features/redirects/createRedirect/CreateRedirectUseCase.js\";\nimport { CreateRedirectRepository } from \"~/features/redirects/createRedirect/CreateRedirectRepository.js\";\nimport {\n UpdateRedirectGateway,\n UpdateRedirectUseCase as UpdateRedirectUseCaseAbstraction,\n type UpdateRedirectParams\n} from \"~/features/redirects/updateRedirect/abstractions.js\";\nimport { UpdateRedirectUseCase } from \"~/features/redirects/updateRedirect/UpdateRedirectUseCase.js\";\nimport { UpdateRedirectRepository } from \"~/features/redirects/updateRedirect/UpdateRedirectRepository.js\";\nimport {\n CreateRedirectPresenter,\n EditRedirectPresenter,\n RedirectListPresenter as Abstraction\n} from \"./abstractions.js\";\nimport { RedirectListPresenter } from \"./RedirectListPresenter.js\";\n\nconst TOTAL_REDIRECTS = 120;\nconst PAGE_SIZE = 50;\nconst BASE_TIME = Date.UTC(2026, 0, 1);\n\nconst minutes = (n: number) => new Date(BASE_TIME + n * 60_000).toISOString();\n\n/**\n * A higher index means a more recently *created* redirect, but a less recently *saved* one,\n * so sorting by `createdOn` and by `savedOn` produce opposite orders.\n */\nfunction createRedirects(count: number): Redirect[] {\n return Array.from({ length: count }, (_, i) =>\n Redirect.create({\n id: `redirect-${i}`,\n location: { folderId: \"root\" },\n redirectFrom: `/from-${i}`,\n redirectTo: `/to-${i}`,\n redirectType: \"permanent\",\n isEnabled: i % 2 === 0,\n createdOn: minutes(i),\n savedOn: minutes(count - i)\n })\n );\n}\n\n/**\n * Sorts like the API: by the requested field, ties keep the original (id) order.\n */\nfunction sortRedirects(redirects: Redirect[], sort = \"createdOn_DESC\"): Redirect[] {\n const [field, direction] = sort.split(\"_\") as [keyof Redirect, \"ASC\" | \"DESC\"];\n const multiplier = direction === \"ASC\" ? 1 : -1;\n return [...redirects].sort((a, b) => {\n const left = String(a[field]);\n const right = String(b[field]);\n return left === right ? 0 : multiplier * (left < right ? -1 : 1);\n });\n}\n\nfunction createListGateway(redirects: Redirect[]): ListRedirectsGateway.Interface {\n return {\n execute: vi.fn(\n async (params: ListRedirectsGatewayParams): Promise<ListRedirectsGatewayResult> => {\n const sorted = sortRedirects(redirects, params.sort?.[0]);\n const offset = params.after ? Number(params.after) : 0;\n const limit = params.limit ?? PAGE_SIZE;\n const data = sorted.slice(offset, offset + limit);\n const next = offset + data.length;\n const hasMoreItems = next < sorted.length;\n return {\n data,\n meta: {\n cursor: hasMoreItems ? String(next) : null,\n hasMoreItems,\n totalCount: sorted.length\n }\n };\n }\n )\n };\n}\n\nfunction createFolderTreePresenter(): FolderTreePresenter.Interface {\n const vm = 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 redirects\",\n childFolders: [],\n loadingNodeIds: []\n });\n\n return {\n get vm() {\n return vm as unknown as FolderTreePresenter.Interface[\"vm\"];\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\n/**\n * Expected redirect IDs for a contiguous range of indexes, in the given order.\n */\nfunction range(from: number, to: number): string[] {\n const step = from <= to ? 1 : -1;\n const ids: string[] = [];\n for (let i = from; step > 0 ? i <= to : i >= to; i += step) {\n ids.push(`redirect-${i}`);\n }\n return ids;\n}\n\ndescribe(\"RedirectListPresenter\", () => {\n let presenter: Abstraction.Interface;\n let container: Container;\n let redirects: Redirect[];\n\n const rowIds = () => presenter.vm.list.rows.map(row => row.id);\n\n const waitForQuery = async (field: string, direction: \"ASC\" | \"DESC\") => {\n await vi.waitFor(() => {\n expect(presenter.vm.list.appliedQuery?.sort).toEqual({ field, direction });\n expect(presenter.vm.list.pagination.loading).toBe(false);\n });\n };\n\n const initAndWait = async () => {\n presenter.init();\n await waitForQuery(\"createdOn\", \"DESC\");\n };\n\n const sortAndWait = async (field: string, direction: \"ASC\" | \"DESC\") => {\n presenter.actions.sort.set(field, direction);\n await waitForQuery(field, direction);\n };\n\n const loadAll = async () => {\n while (presenter.vm.list.pagination.hasMore) {\n await presenter.actions.loadMore();\n }\n };\n\n beforeEach(() => {\n container = new Container();\n redirects = createRedirects(TOTAL_REDIRECTS);\n\n container.registerInstance(RedirectsListCache, new ListCache<Redirect>(\"id\"));\n\n container.registerInstance(ListRedirectsGateway, createListGateway(redirects));\n container.register(ListRedirectsRepository).inSingletonScope();\n container.register(ListRedirectsUseCase);\n\n container.registerInstance(CreateRedirectGateway, {\n execute: vi.fn(async params =>\n Redirect.create({\n ...params,\n id: \"redirect-new\",\n createdOn: minutes(TOTAL_REDIRECTS + 1),\n savedOn: minutes(TOTAL_REDIRECTS + 1)\n })\n )\n });\n container.register(CreateRedirectRepository).inSingletonScope();\n container.register(CreateRedirectUseCase);\n\n container.registerInstance(UpdateRedirectGateway, {\n execute: vi.fn(async (params: UpdateRedirectParams) => {\n const existing = redirects.find(r => r.id === params.id)!;\n return Redirect.create({\n ...existing,\n ...params,\n savedOn: minutes(TOTAL_REDIRECTS + 1)\n });\n })\n });\n container.register(UpdateRedirectRepository).inSingletonScope();\n container.register(UpdateRedirectUseCase);\n\n container.register(ListPresenter);\n container.registerInstance(FolderTreePresenter, createFolderTreePresenter());\n container.registerInstance(GetDescendantFoldersUseCase, { execute: () => [] });\n container.registerInstance(CreateRedirectPresenter, {\n vm: {},\n init: vi.fn(),\n save: vi.fn()\n } as unknown as CreateRedirectPresenter.Interface);\n container.registerInstance(EditRedirectPresenter, {\n vm: {},\n loadRedirect: vi.fn(),\n save: vi.fn()\n } as unknown as EditRedirectPresenter.Interface);\n\n container.register(RedirectListPresenter);\n\n presenter = container.resolve(Abstraction);\n });\n\n describe(\"pagination\", () => {\n it(\"should show the most recently created redirects first\", async () => {\n await initAndWait();\n\n expect(rowIds()).toEqual(range(119, 70));\n });\n\n it(\"should append the next batch of redirects to the bottom of the list\", async () => {\n await initAndWait();\n\n await presenter.actions.loadMore();\n\n expect(rowIds()).toEqual(range(119, 20));\n });\n\n it(\"should keep all redirects in order after loading everything\", async () => {\n await initAndWait();\n\n await loadAll();\n\n expect(rowIds()).toEqual(range(119, 0));\n });\n });\n\n describe(\"changing the sort\", () => {\n it(\"should show only the first batch, sorted by the new field\", async () => {\n await initAndWait();\n\n await sortAndWait(\"savedOn\", \"DESC\");\n\n // `savedOn` runs opposite to `createdOn`, so the most recently saved is redirect-0.\n expect(rowIds()).toEqual(range(0, 49));\n });\n\n it(\"should show only the first batch when sorting by a boolean field\", async () => {\n await initAndWait();\n\n await sortAndWait(\"isEnabled\", \"DESC\");\n\n const expected = sortRedirects(redirects, \"isEnabled_DESC\")\n .slice(0, PAGE_SIZE)\n .map(r => r.id);\n expect(rowIds()).toEqual(expected);\n });\n\n it(\"should restore the original order when switching back\", async () => {\n await initAndWait();\n await presenter.actions.loadMore();\n\n await sortAndWait(\"createdOn\", \"ASC\");\n await sortAndWait(\"createdOn\", \"DESC\");\n\n expect(rowIds()).toEqual(range(119, 70));\n });\n });\n\n describe(\"local cache changes\", () => {\n const createRedirect = () =>\n container.resolve(CreateRedirectUseCaseAbstraction).execute({\n location: { folderId: \"root\" },\n redirectFrom: \"/new\",\n redirectTo: \"/target\",\n redirectType: \"permanent\",\n isEnabled: true\n });\n\n const updateRedirect = (id: string) => {\n const existing = redirects.find(r => r.id === id)!;\n return container.resolve(UpdateRedirectUseCaseAbstraction).execute({\n id,\n redirectFrom: existing.redirectFrom,\n redirectTo: \"/changed\",\n redirectType: existing.redirectType,\n isEnabled: existing.isEnabled\n });\n };\n\n it(\"should show a created redirect at the top when it sorts first\", async () => {\n await initAndWait();\n\n await createRedirect();\n\n expect(rowIds()).toEqual([\"redirect-new\", ...range(119, 70)]);\n });\n\n it(\"should not show a created redirect that sorts past the loaded redirects\", async () => {\n await initAndWait();\n await sortAndWait(\"createdOn\", \"ASC\");\n\n await createRedirect();\n\n // The new redirect is the most recently created, so in ASC order it belongs at the\n // very end, after redirects that are not loaded yet.\n expect(rowIds()).toEqual(range(0, 49));\n });\n\n it(\"should keep an updated redirect in its position when sorted by createdOn\", async () => {\n await initAndWait();\n\n await updateRedirect(\"redirect-100\");\n\n expect(rowIds()).toEqual(range(119, 70));\n const updated = presenter.vm.list.rows.find(row => row.id === \"redirect-100\");\n expect(updated?.redirectTo).toBe(\"/changed\");\n });\n\n it(\"should move an updated redirect to the top when sorted by savedOn\", async () => {\n await initAndWait();\n await sortAndWait(\"savedOn\", \"DESC\");\n\n await updateRedirect(\"redirect-30\");\n\n expect(rowIds()).toEqual([\"redirect-30\", ...range(0, 29), ...range(31, 49)]);\n });\n });\n});\n"],"names":["TOTAL_REDIRECTS","PAGE_SIZE","BASE_TIME","Date","minutes","n","createRedirects","count","Array","_","i","Redirect","sortRedirects","redirects","sort","field","direction","multiplier","a","b","left","String","right","createListGateway","vi","params","sorted","offset","Number","limit","data","next","hasMoreItems","createFolderTreePresenter","vm","observable","undefined","range","from","to","step","ids","describe","presenter","container","rowIds","row","waitForQuery","expect","initAndWait","sortAndWait","loadAll","beforeEach","Container","RedirectsListCache","ListCache","ListRedirectsGateway","ListRedirectsRepository","ListRedirectsUseCase","CreateRedirectGateway","CreateRedirectRepository","CreateRedirectUseCase","UpdateRedirectGateway","existing","r","UpdateRedirectRepository","UpdateRedirectUseCase","ListPresenter","FolderTreePresenter","GetDescendantFoldersUseCase","CreateRedirectPresenter","EditRedirectPresenter","RedirectListPresenter","Abstraction","it","expected","createRedirect","CreateRedirectUseCaseAbstraction","updateRedirect","id","UpdateRedirectUseCaseAbstraction","updated"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
- export declare const BulkActionDelete: (() => React.JSX.Element) & {
3
- displayName: string;
2
+ export declare const BulkActionDelete: (() => React.JSX.Element) & React.NamedExoticComponent<unknown> & {
3
+ readonly type: () => React.JSX.Element;
4
4
  };
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
- export declare const BulkActionMove: (() => React.JSX.Element) & {
3
- displayName: string;
2
+ export declare const BulkActionMove: (() => React.JSX.Element) & React.NamedExoticComponent<unknown> & {
3
+ readonly type: () => React.JSX.Element;
4
4
  };
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
- export declare const CreateRedirectDialog: (() => React.JSX.Element | null) & {
3
- displayName: string;
2
+ export declare const CreateRedirectDialog: (() => React.JSX.Element | null) & React.NamedExoticComponent<unknown> & {
3
+ readonly type: () => React.JSX.Element | null;
4
4
  };
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
- export declare const DocumentList: (() => React.JSX.Element) & {
3
- displayName: string;
2
+ export declare const DocumentList: (() => React.JSX.Element) & React.NamedExoticComponent<unknown> & {
3
+ readonly type: () => React.JSX.Element;
4
4
  };
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
- export declare const EditRedirectDialog: (() => React.JSX.Element | null) & {
3
- displayName: string;
2
+ export declare const EditRedirectDialog: (() => React.JSX.Element | null) & React.NamedExoticComponent<unknown> & {
3
+ readonly type: () => React.JSX.Element | null;
4
4
  };
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
- export declare const Table: (() => React.JSX.Element) & {
3
- displayName: string;
2
+ export declare const Table: (() => React.JSX.Element) & React.NamedExoticComponent<unknown> & {
3
+ readonly type: () => React.JSX.Element;
4
4
  };
@@ -6,7 +6,7 @@ const fileManagerItemToValue = (file)=>{
6
6
  src,
7
7
  url: src,
8
8
  name: file.name,
9
- type: file.type,
9
+ mimeType: file.type,
10
10
  size: file.size
11
11
  };
12
12
  if ("image" === getAssetCategory(file.type)) {
@@ -19,6 +19,8 @@ const fileManagerItemToValue = (file)=>{
19
19
  alt: meta?.alt ?? void 0,
20
20
  caption: meta?.caption ?? void 0
21
21
  };
22
+ asset.width = asset.image.width;
23
+ asset.height = asset.image.height;
22
24
  }
23
25
  return asset;
24
26
  };
@@ -1 +1 @@
1
- {"version":3,"file":"shared/fileManagerItemToValue.js","sources":["../../src/shared/fileManagerItemToValue.ts"],"sourcesContent":["import type { FileManagerFileItem } from \"@webiny/app-admin\";\nimport { getAssetCategory, type Asset } from \"@webiny/website-builder-sdk\";\n\n/**\n * Build the unified {@link Asset} value from a picked File Manager item.\n * For images, the per-usage `image` edit is seeded from the asset-level default\n * (set in File Manager); from here it lives on the element value and is editable\n * per placement.\n */\nexport const fileManagerItemToValue = (file: FileManagerFileItem): Asset => {\n const src = file.src || \"\";\n const asset: Asset = {\n id: file.id,\n src,\n url: src,\n name: file.name,\n type: file.type,\n size: file.size\n };\n\n if (getAssetCategory(file.type) === \"image\") {\n const meta = file.metadata?.image;\n asset.image = {\n width: meta?.width ?? file.width,\n height: meta?.height ?? file.height,\n crop: meta?.crop ?? undefined,\n focalPoint: meta?.focalPoint ?? undefined,\n alt: meta?.alt ?? undefined,\n caption: meta?.caption ?? undefined\n };\n }\n\n return asset;\n};\n"],"names":["fileManagerItemToValue","file","src","asset","getAssetCategory","meta","undefined"],"mappings":";AASO,MAAMA,yBAAyB,CAACC;IACnC,MAAMC,MAAMD,KAAK,GAAG,IAAI;IACxB,MAAME,QAAe;QACjB,IAAIF,KAAK,EAAE;QACXC;QACA,KAAKA;QACL,MAAMD,KAAK,IAAI;QACf,MAAMA,KAAK,IAAI;QACf,MAAMA,KAAK,IAAI;IACnB;IAEA,IAAIG,AAAgC,YAAhCA,iBAAiBH,KAAK,IAAI,GAAe;QACzC,MAAMI,OAAOJ,KAAK,QAAQ,EAAE;QAC5BE,MAAM,KAAK,GAAG;YACV,OAAOE,MAAM,SAASJ,KAAK,KAAK;YAChC,QAAQI,MAAM,UAAUJ,KAAK,MAAM;YACnC,MAAMI,MAAM,QAAQC;YACpB,YAAYD,MAAM,cAAcC;YAChC,KAAKD,MAAM,OAAOC;YAClB,SAASD,MAAM,WAAWC;QAC9B;IACJ;IAEA,OAAOH;AACX"}
1
+ {"version":3,"file":"shared/fileManagerItemToValue.js","sources":["../../src/shared/fileManagerItemToValue.ts"],"sourcesContent":["import type { FileManagerFileItem } from \"@webiny/app-admin\";\nimport { getAssetCategory, type Asset } from \"@webiny/website-builder-sdk\";\n\n/**\n * Build the unified {@link Asset} value from a picked File Manager item.\n * For images, the per-usage `image` edit is seeded from the asset-level default\n * (set in File Manager); from here it lives on the element value and is editable\n * per placement.\n */\nexport const fileManagerItemToValue = (file: FileManagerFileItem): Asset => {\n const src = file.src || \"\";\n const asset: Asset = {\n id: file.id,\n src,\n url: src,\n name: file.name,\n mimeType: file.type,\n size: file.size\n };\n\n if (getAssetCategory(file.type) === \"image\") {\n const meta = file.metadata?.image;\n asset.image = {\n width: meta?.width ?? file.width,\n height: meta?.height ?? file.height,\n crop: meta?.crop ?? undefined,\n focalPoint: meta?.focalPoint ?? undefined,\n alt: meta?.alt ?? undefined,\n caption: meta?.caption ?? undefined\n };\n // Root dimensions for 6.4 frontends, which read `width`/`height` there.\n asset.width = asset.image.width;\n asset.height = asset.image.height;\n }\n\n return asset;\n};\n"],"names":["fileManagerItemToValue","file","src","asset","getAssetCategory","meta","undefined"],"mappings":";AASO,MAAMA,yBAAyB,CAACC;IACnC,MAAMC,MAAMD,KAAK,GAAG,IAAI;IACxB,MAAME,QAAe;QACjB,IAAIF,KAAK,EAAE;QACXC;QACA,KAAKA;QACL,MAAMD,KAAK,IAAI;QACf,UAAUA,KAAK,IAAI;QACnB,MAAMA,KAAK,IAAI;IACnB;IAEA,IAAIG,AAAgC,YAAhCA,iBAAiBH,KAAK,IAAI,GAAe;QACzC,MAAMI,OAAOJ,KAAK,QAAQ,EAAE;QAC5BE,MAAM,KAAK,GAAG;YACV,OAAOE,MAAM,SAASJ,KAAK,KAAK;YAChC,QAAQI,MAAM,UAAUJ,KAAK,MAAM;YACnC,MAAMI,MAAM,QAAQC;YACpB,YAAYD,MAAM,cAAcC;YAChC,KAAKD,MAAM,OAAOC;YAClB,SAASD,MAAM,WAAWC;QAC9B;QAEAH,MAAM,KAAK,GAAGA,MAAM,KAAK,CAAC,KAAK;QAC/BA,MAAM,MAAM,GAAGA,MAAM,KAAK,CAAC,MAAM;IACrC;IAEA,OAAOA;AACX"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,73 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { fileManagerItemToValue } from "./fileManagerItemToValue.js";
3
+ describe("fileManagerItemToValue", ()=>{
4
+ it("writes mimeType and image dimensions at the root and in image", ()=>{
5
+ const value = fileManagerItemToValue({
6
+ id: "file-1",
7
+ src: "https://cdn.test/files/file-1/cat.jpg",
8
+ name: "cat.jpg",
9
+ type: "image/jpeg",
10
+ size: 1234,
11
+ width: 1200,
12
+ height: 800
13
+ });
14
+ expect(value).toEqual({
15
+ id: "file-1",
16
+ src: "https://cdn.test/files/file-1/cat.jpg",
17
+ url: "https://cdn.test/files/file-1/cat.jpg",
18
+ name: "cat.jpg",
19
+ mimeType: "image/jpeg",
20
+ size: 1234,
21
+ width: 1200,
22
+ height: 800,
23
+ image: {
24
+ width: 1200,
25
+ height: 800,
26
+ crop: void 0,
27
+ focalPoint: void 0,
28
+ alt: void 0,
29
+ caption: void 0
30
+ }
31
+ });
32
+ });
33
+ it("prefers the File Manager image metadata for dimensions", ()=>{
34
+ const value = fileManagerItemToValue({
35
+ id: "file-2",
36
+ src: "https://cdn.test/files/file-2/dog.png",
37
+ name: "dog.png",
38
+ type: "image/png",
39
+ size: 10,
40
+ width: 100,
41
+ height: 100,
42
+ metadata: {
43
+ image: {
44
+ width: 640,
45
+ height: 480,
46
+ alt: "A dog"
47
+ }
48
+ }
49
+ });
50
+ expect(value.width).toBe(640);
51
+ expect(value.height).toBe(480);
52
+ expect(value.image).toMatchObject({
53
+ width: 640,
54
+ height: 480,
55
+ alt: "A dog"
56
+ });
57
+ });
58
+ it("leaves dimensions out for non-image files", ()=>{
59
+ const value = fileManagerItemToValue({
60
+ id: "file-3",
61
+ src: "https://cdn.test/files/file-3/doc.pdf",
62
+ name: "doc.pdf",
63
+ type: "application/pdf",
64
+ size: 99
65
+ });
66
+ expect(value.mimeType).toBe("application/pdf");
67
+ expect(value.width).toBeUndefined();
68
+ expect(value.height).toBeUndefined();
69
+ expect(value.image).toBeUndefined();
70
+ });
71
+ });
72
+
73
+ //# sourceMappingURL=fileManagerItemToValue.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared/fileManagerItemToValue.test.js","sources":["../../src/shared/fileManagerItemToValue.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { fileManagerItemToValue } from \"./fileManagerItemToValue.js\";\n\ndescribe(\"fileManagerItemToValue\", () => {\n it(\"writes mimeType and image dimensions at the root and in image\", () => {\n const value = fileManagerItemToValue({\n id: \"file-1\",\n src: \"https://cdn.test/files/file-1/cat.jpg\",\n name: \"cat.jpg\",\n type: \"image/jpeg\",\n size: 1234,\n width: 1200,\n height: 800\n });\n\n expect(value).toEqual({\n id: \"file-1\",\n src: \"https://cdn.test/files/file-1/cat.jpg\",\n url: \"https://cdn.test/files/file-1/cat.jpg\",\n name: \"cat.jpg\",\n mimeType: \"image/jpeg\",\n size: 1234,\n width: 1200,\n height: 800,\n image: {\n width: 1200,\n height: 800,\n crop: undefined,\n focalPoint: undefined,\n alt: undefined,\n caption: undefined\n }\n });\n });\n\n it(\"prefers the File Manager image metadata for dimensions\", () => {\n const value = fileManagerItemToValue({\n id: \"file-2\",\n src: \"https://cdn.test/files/file-2/dog.png\",\n name: \"dog.png\",\n type: \"image/png\",\n size: 10,\n width: 100,\n height: 100,\n metadata: { image: { width: 640, height: 480, alt: \"A dog\" } }\n });\n\n expect(value.width).toBe(640);\n expect(value.height).toBe(480);\n expect(value.image).toMatchObject({ width: 640, height: 480, alt: \"A dog\" });\n });\n\n it(\"leaves dimensions out for non-image files\", () => {\n const value = fileManagerItemToValue({\n id: \"file-3\",\n src: \"https://cdn.test/files/file-3/doc.pdf\",\n name: \"doc.pdf\",\n type: \"application/pdf\",\n size: 99\n });\n\n expect(value.mimeType).toBe(\"application/pdf\");\n expect(value.width).toBeUndefined();\n expect(value.height).toBeUndefined();\n expect(value.image).toBeUndefined();\n });\n});\n"],"names":["describe","it","value","fileManagerItemToValue","expect","undefined"],"mappings":";;AAGAA,SAAS,0BAA0B;IAC/BC,GAAG,iEAAiE;QAChE,MAAMC,QAAQC,uBAAuB;YACjC,IAAI;YACJ,KAAK;YACL,MAAM;YACN,MAAM;YACN,MAAM;YACN,OAAO;YACP,QAAQ;QACZ;QAEAC,OAAOF,OAAO,OAAO,CAAC;YAClB,IAAI;YACJ,KAAK;YACL,KAAK;YACL,MAAM;YACN,UAAU;YACV,MAAM;YACN,OAAO;YACP,QAAQ;YACR,OAAO;gBACH,OAAO;gBACP,QAAQ;gBACR,MAAMG;gBACN,YAAYA;gBACZ,KAAKA;gBACL,SAASA;YACb;QACJ;IACJ;IAEAJ,GAAG,0DAA0D;QACzD,MAAMC,QAAQC,uBAAuB;YACjC,IAAI;YACJ,KAAK;YACL,MAAM;YACN,MAAM;YACN,MAAM;YACN,OAAO;YACP,QAAQ;YACR,UAAU;gBAAE,OAAO;oBAAE,OAAO;oBAAK,QAAQ;oBAAK,KAAK;gBAAQ;YAAE;QACjE;QAEAC,OAAOF,MAAM,KAAK,EAAE,IAAI,CAAC;QACzBE,OAAOF,MAAM,MAAM,EAAE,IAAI,CAAC;QAC1BE,OAAOF,MAAM,KAAK,EAAE,aAAa,CAAC;YAAE,OAAO;YAAK,QAAQ;YAAK,KAAK;QAAQ;IAC9E;IAEAD,GAAG,6CAA6C;QAC5C,MAAMC,QAAQC,uBAAuB;YACjC,IAAI;YACJ,KAAK;YACL,MAAM;YACN,MAAM;YACN,MAAM;QACV;QAEAC,OAAOF,MAAM,QAAQ,EAAE,IAAI,CAAC;QAC5BE,OAAOF,MAAM,KAAK,EAAE,aAAa;QACjCE,OAAOF,MAAM,MAAM,EAAE,aAAa;QAClCE,OAAOF,MAAM,KAAK,EAAE,aAAa;IACrC;AACJ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"BaseEditor/defaultConfig/Content/usePreviewDomain.js","sources":["../../../../src/BaseEditor/defaultConfig/Content/usePreviewDomain.ts"],"sourcesContent":["import { useCallback } from \"react\";\nimport { useLocalStorage } from \"@webiny/app\";\nimport { usePreviewDomain as useSharedPreviewDomain } from \"@webiny/frontend-settings/exports/admin.js\";\n\nconst CUSTOM_PREVIEW_DOMAIN = \"custom_preview_domain\";\n\nexport const usePreviewDomain = () => {\n const { previewDomain, isOverridden } = useSharedPreviewDomain();\n const localStorage = useLocalStorage();\n\n const setPreviewDomain = useCallback(\n (domain: string) => {\n if (domain === previewDomain) {\n localStorage.remove(CUSTOM_PREVIEW_DOMAIN);\n } else {\n localStorage.set(CUSTOM_PREVIEW_DOMAIN, domain);\n }\n },\n [previewDomain]\n );\n\n const unsetPreviewDomain = useCallback(() => {\n localStorage.remove(CUSTOM_PREVIEW_DOMAIN);\n }, []);\n\n return {\n previewDomain,\n setPreviewDomain,\n unsetPreviewDomain,\n isOverridden\n };\n};\n"],"names":["CUSTOM_PREVIEW_DOMAIN","usePreviewDomain","previewDomain","isOverridden","useSharedPreviewDomain","localStorage","useLocalStorage","setPreviewDomain","useCallback","domain","unsetPreviewDomain"],"mappings":";;;AAIA,MAAMA,wBAAwB;AAEvB,MAAMC,oCAAmB;IAC5B,MAAM,EAAEC,aAAa,EAAEC,YAAY,EAAE,GAAGC;IACxC,MAAMC,eAAeC;IAErB,MAAMC,mBAAmBC,YACrB,CAACC;QACG,IAAIA,WAAWP,eACXG,aAAa,MAAM,CAACL;aAEpBK,aAAa,GAAG,CAACL,uBAAuBS;IAEhD,GACA;QAACP;KAAc;IAGnB,MAAMQ,qBAAqBF,YAAY;QACnCH,aAAa,MAAM,CAACL;IACxB,GAAG,EAAE;IAEL,OAAO;QACHE;QACAK;QACAG;QACAP;IACJ;AACJ"}