@webiny/app-headless-cms 6.5.0-beta.10 → 6.5.0-beta.11

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-headless-cms",
3
- "version": "6.5.0-beta.10",
3
+ "version": "6.5.0-beta.11",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -28,25 +28,25 @@
28
28
  "@fortawesome/react-fontawesome": "3.5.0",
29
29
  "@lexical/utils": "0.52.0",
30
30
  "@types/react": "18.3.31",
31
- "@webiny/admin-ui": "6.5.0-beta.10",
32
- "@webiny/app": "6.5.0-beta.10",
33
- "@webiny/app-aco": "6.5.0-beta.10",
34
- "@webiny/app-admin": "6.5.0-beta.10",
35
- "@webiny/app-graphql-playground": "6.5.0-beta.10",
36
- "@webiny/app-headless-cms-common": "6.5.0-beta.10",
37
- "@webiny/cms-sdk": "6.5.0-beta.10",
31
+ "@webiny/admin-ui": "6.5.0-beta.11",
32
+ "@webiny/app": "6.5.0-beta.11",
33
+ "@webiny/app-aco": "6.5.0-beta.11",
34
+ "@webiny/app-admin": "6.5.0-beta.11",
35
+ "@webiny/app-graphql-playground": "6.5.0-beta.11",
36
+ "@webiny/app-headless-cms-common": "6.5.0-beta.11",
37
+ "@webiny/cms-sdk": "6.5.0-beta.11",
38
38
  "@webiny/di": "1.1.0",
39
- "@webiny/feature": "6.5.0-beta.10",
40
- "@webiny/form": "6.5.0-beta.10",
41
- "@webiny/frontend-settings": "6.5.0-beta.10",
42
- "@webiny/icons": "6.5.0-beta.10",
43
- "@webiny/lexical-editor": "6.5.0-beta.10",
44
- "@webiny/lexical-nodes": "6.5.0-beta.10",
45
- "@webiny/plugins": "6.5.0-beta.10",
46
- "@webiny/react-composition": "6.5.0-beta.10",
47
- "@webiny/react-properties": "6.5.0-beta.10",
48
- "@webiny/utils": "6.5.0-beta.10",
49
- "@webiny/validation": "6.5.0-beta.10",
39
+ "@webiny/feature": "6.5.0-beta.11",
40
+ "@webiny/form": "6.5.0-beta.11",
41
+ "@webiny/frontend-settings": "6.5.0-beta.11",
42
+ "@webiny/icons": "6.5.0-beta.11",
43
+ "@webiny/lexical-editor": "6.5.0-beta.11",
44
+ "@webiny/lexical-nodes": "6.5.0-beta.11",
45
+ "@webiny/plugins": "6.5.0-beta.11",
46
+ "@webiny/react-composition": "6.5.0-beta.11",
47
+ "@webiny/react-properties": "6.5.0-beta.11",
48
+ "@webiny/utils": "6.5.0-beta.11",
49
+ "@webiny/validation": "6.5.0-beta.11",
50
50
  "dot-prop-immutable": "2.1.1",
51
51
  "graphql": "16.14.2",
52
52
  "graphql-tag": "2.12.7",
@@ -63,7 +63,7 @@
63
63
  "zod": "4.6.5"
64
64
  },
65
65
  "devDependencies": {
66
- "@webiny/build-tools": "6.5.0-beta.10",
66
+ "@webiny/build-tools": "6.5.0-beta.11",
67
67
  "rimraf": "6.1.3",
68
68
  "typescript": "7.0.2",
69
69
  "vitest": "5.0.3"
@@ -9,6 +9,7 @@ export declare class BaseAutocompletePresenter {
9
9
  searchOptions: IRefEntryOption[];
10
10
  modelIds: string[];
11
11
  searchQuery: string;
12
+ private lastSearchId;
12
13
  constructor(searchUseCase: SearchContentEntriesUseCase.Interface, getEntriesUseCase: GetContentEntriesUseCase.Interface);
13
14
  get activeOptions(): IRefEntryOption[];
14
15
  search(query: string): Promise<void>;
@@ -9,18 +9,22 @@ class BaseAutocompletePresenter {
9
9
  this.searchOptions = [];
10
10
  this.modelIds = [];
11
11
  this.searchQuery = "";
12
+ this.lastSearchId = 0;
12
13
  makeAutoObservable(this, {
13
14
  searchUseCase: false,
14
- getEntriesUseCase: false
15
+ getEntriesUseCase: false,
16
+ lastSearchId: false
15
17
  });
16
18
  }
17
19
  get activeOptions() {
18
20
  return this.searchQuery ? this.searchOptions : this.defaultOptions;
19
21
  }
20
22
  async search(query) {
23
+ const searchId = ++this.lastSearchId;
21
24
  this.searchQuery = query;
22
25
  if (!query) {
23
26
  this.searchOptions = [];
27
+ this.loading = false;
24
28
  return;
25
29
  }
26
30
  this.loading = true;
@@ -30,11 +34,12 @@ class BaseAutocompletePresenter {
30
34
  query,
31
35
  limit: 10
32
36
  });
37
+ if (searchId !== this.lastSearchId) return;
33
38
  runInAction(()=>{
34
39
  this.searchOptions = result.data.map(toOption);
35
40
  });
36
41
  } finally{
37
- runInAction(()=>{
42
+ if (searchId === this.lastSearchId) runInAction(()=>{
38
43
  this.loading = false;
39
44
  });
40
45
  }
@@ -77,6 +82,7 @@ class BaseAutocompletePresenter {
77
82
  }
78
83
  }
79
84
  clearSearch() {
85
+ this.lastSearchId++;
80
86
  this.searchQuery = "";
81
87
  this.searchOptions = [];
82
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.js","sources":["../../../../../src/presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from \"mobx\";\nimport type { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport type { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { IRefEntryOption } from \"./abstractions.js\";\nimport { toOption } from \"./toOption.js\";\n\nexport class BaseAutocompletePresenter {\n loading = false;\n defaultOptions: IRefEntryOption[] = [];\n searchOptions: IRefEntryOption[] = [];\n modelIds: string[] = [];\n searchQuery = \"\";\n\n constructor(\n private searchUseCase: SearchContentEntriesUseCase.Interface,\n private getEntriesUseCase: GetContentEntriesUseCase.Interface\n ) {\n makeAutoObservable<BaseAutocompletePresenter, \"searchUseCase\" | \"getEntriesUseCase\">(this, {\n searchUseCase: false,\n getEntriesUseCase: false\n });\n }\n\n get activeOptions(): IRefEntryOption[] {\n return this.searchQuery ? this.searchOptions : this.defaultOptions;\n }\n\n async search(query: string): Promise<void> {\n this.searchQuery = query;\n if (!query) {\n this.searchOptions = [];\n return;\n }\n\n this.loading = true;\n try {\n const result = await this.searchUseCase.execute({\n modelIds: this.modelIds,\n query,\n limit: 10\n });\n runInAction(() => {\n this.searchOptions = result.data.map(toOption);\n });\n } finally {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n\n async loadDefaults(): Promise<void> {\n this.loading = true;\n try {\n const result = await this.searchUseCase.execute({\n modelIds: this.modelIds,\n limit: 10\n });\n runInAction(() => {\n this.defaultOptions = result.data.map(toOption);\n });\n } finally {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n\n async resolveEntries(values: { id: string; modelId: string }[]): Promise<IRefEntryOption[]> {\n this.loading = true;\n try {\n const result = await this.getEntriesUseCase.execute({\n entries: values.map(v => ({ id: v.id, modelId: v.modelId }))\n });\n const options = result.latest.map(toOption);\n runInAction(() => {\n this.loading = false;\n });\n return options;\n } catch (e) {\n runInAction(() => {\n this.loading = false;\n });\n throw e;\n }\n }\n\n clearSearch(): void {\n this.searchQuery = \"\";\n this.searchOptions = [];\n }\n}\n"],"names":["BaseAutocompletePresenter","searchUseCase","getEntriesUseCase","makeAutoObservable","query","result","runInAction","toOption","values","v","options","e"],"mappings":";;AAMO,MAAMA;IAOT,YACYC,aAAoD,EACpDC,iBAAqD,CAC/D;aAFUD,aAAa,GAAbA;aACAC,iBAAiB,GAAjBA;aARZ,OAAO,GAAG;aACV,cAAc,GAAsB,EAAE;aACtC,aAAa,GAAsB,EAAE;aACrC,QAAQ,GAAa,EAAE;aACvB,WAAW,GAAG;QAMVC,mBAAqF,IAAI,EAAE;YACvF,eAAe;YACf,mBAAmB;QACvB;IACJ;IAEA,IAAI,gBAAmC;QACnC,OAAO,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,cAAc;IACtE;IAEA,MAAM,OAAOC,KAAa,EAAiB;QACvC,IAAI,CAAC,WAAW,GAAGA;QACnB,IAAI,CAACA,OAAO;YACR,IAAI,CAAC,aAAa,GAAG,EAAE;YACvB;QACJ;QAEA,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAMC,SAAS,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;gBAC5C,UAAU,IAAI,CAAC,QAAQ;gBACvBD;gBACA,OAAO;YACX;YACAE,YAAY;gBACR,IAAI,CAAC,aAAa,GAAGD,OAAO,IAAI,CAAC,GAAG,CAACE;YACzC;QACJ,SAAU;YACND,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;QACJ;IACJ;IAEA,MAAM,eAA8B;QAChC,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAMD,SAAS,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;gBAC5C,UAAU,IAAI,CAAC,QAAQ;gBACvB,OAAO;YACX;YACAC,YAAY;gBACR,IAAI,CAAC,cAAc,GAAGD,OAAO,IAAI,CAAC,GAAG,CAACE;YAC1C;QACJ,SAAU;YACND,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;QACJ;IACJ;IAEA,MAAM,eAAeE,MAAyC,EAA8B;QACxF,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAMH,SAAS,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;gBAChD,SAASG,OAAO,GAAG,CAACC,CAAAA,IAAM;wBAAE,IAAIA,EAAE,EAAE;wBAAE,SAASA,EAAE,OAAO;oBAAC;YAC7D;YACA,MAAMC,UAAUL,OAAO,MAAM,CAAC,GAAG,CAACE;YAClCD,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;YACA,OAAOI;QACX,EAAE,OAAOC,GAAG;YACRL,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;YACA,MAAMK;QACV;IACJ;IAEA,cAAoB;QAChB,IAAI,CAAC,WAAW,GAAG;QACnB,IAAI,CAAC,aAAa,GAAG,EAAE;IAC3B;AACJ"}
1
+ {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.js","sources":["../../../../../src/presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from \"mobx\";\nimport type { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport type { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { IRefEntryOption } from \"./abstractions.js\";\nimport { toOption } from \"./toOption.js\";\n\nexport class BaseAutocompletePresenter {\n loading = false;\n defaultOptions: IRefEntryOption[] = [];\n searchOptions: IRefEntryOption[] = [];\n modelIds: string[] = [];\n searchQuery = \"\";\n private lastSearchId = 0;\n\n constructor(\n private searchUseCase: SearchContentEntriesUseCase.Interface,\n private getEntriesUseCase: GetContentEntriesUseCase.Interface\n ) {\n makeAutoObservable<\n BaseAutocompletePresenter,\n \"searchUseCase\" | \"getEntriesUseCase\" | \"lastSearchId\"\n >(this, {\n searchUseCase: false,\n getEntriesUseCase: false,\n lastSearchId: false\n });\n }\n\n get activeOptions(): IRefEntryOption[] {\n return this.searchQuery ? this.searchOptions : this.defaultOptions;\n }\n\n async search(query: string): Promise<void> {\n // Every keystroke starts a search, and responses can arrive out of order.\n // Only the latest search may write its results, or an older, shorter query overwrites them.\n const searchId = ++this.lastSearchId;\n this.searchQuery = query;\n if (!query) {\n this.searchOptions = [];\n this.loading = false;\n return;\n }\n\n this.loading = true;\n try {\n const result = await this.searchUseCase.execute({\n modelIds: this.modelIds,\n query,\n limit: 10\n });\n if (searchId !== this.lastSearchId) {\n return;\n }\n runInAction(() => {\n this.searchOptions = result.data.map(toOption);\n });\n } finally {\n if (searchId === this.lastSearchId) {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n }\n\n async loadDefaults(): Promise<void> {\n this.loading = true;\n try {\n const result = await this.searchUseCase.execute({\n modelIds: this.modelIds,\n limit: 10\n });\n runInAction(() => {\n this.defaultOptions = result.data.map(toOption);\n });\n } finally {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n\n async resolveEntries(values: { id: string; modelId: string }[]): Promise<IRefEntryOption[]> {\n this.loading = true;\n try {\n const result = await this.getEntriesUseCase.execute({\n entries: values.map(v => ({ id: v.id, modelId: v.modelId }))\n });\n const options = result.latest.map(toOption);\n runInAction(() => {\n this.loading = false;\n });\n return options;\n } catch (e) {\n runInAction(() => {\n this.loading = false;\n });\n throw e;\n }\n }\n\n clearSearch(): void {\n this.lastSearchId++;\n this.searchQuery = \"\";\n this.searchOptions = [];\n }\n}\n"],"names":["BaseAutocompletePresenter","searchUseCase","getEntriesUseCase","makeAutoObservable","query","searchId","result","runInAction","toOption","values","v","options","e"],"mappings":";;AAMO,MAAMA;IAQT,YACYC,aAAoD,EACpDC,iBAAqD,CAC/D;aAFUD,aAAa,GAAbA;aACAC,iBAAiB,GAAjBA;aATZ,OAAO,GAAG;aACV,cAAc,GAAsB,EAAE;aACtC,aAAa,GAAsB,EAAE;aACrC,QAAQ,GAAa,EAAE;aACvB,WAAW,GAAG;aACN,YAAY,GAAG;QAMnBC,mBAGE,IAAI,EAAE;YACJ,eAAe;YACf,mBAAmB;YACnB,cAAc;QAClB;IACJ;IAEA,IAAI,gBAAmC;QACnC,OAAO,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,cAAc;IACtE;IAEA,MAAM,OAAOC,KAAa,EAAiB;QAGvC,MAAMC,WAAW,EAAE,IAAI,CAAC,YAAY;QACpC,IAAI,CAAC,WAAW,GAAGD;QACnB,IAAI,CAACA,OAAO;YACR,IAAI,CAAC,aAAa,GAAG,EAAE;YACvB,IAAI,CAAC,OAAO,GAAG;YACf;QACJ;QAEA,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAME,SAAS,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;gBAC5C,UAAU,IAAI,CAAC,QAAQ;gBACvBF;gBACA,OAAO;YACX;YACA,IAAIC,aAAa,IAAI,CAAC,YAAY,EAC9B;YAEJE,YAAY;gBACR,IAAI,CAAC,aAAa,GAAGD,OAAO,IAAI,CAAC,GAAG,CAACE;YACzC;QACJ,SAAU;YACN,IAAIH,aAAa,IAAI,CAAC,YAAY,EAC9BE,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;QAER;IACJ;IAEA,MAAM,eAA8B;QAChC,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAMD,SAAS,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;gBAC5C,UAAU,IAAI,CAAC,QAAQ;gBACvB,OAAO;YACX;YACAC,YAAY;gBACR,IAAI,CAAC,cAAc,GAAGD,OAAO,IAAI,CAAC,GAAG,CAACE;YAC1C;QACJ,SAAU;YACND,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;QACJ;IACJ;IAEA,MAAM,eAAeE,MAAyC,EAA8B;QACxF,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAMH,SAAS,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;gBAChD,SAASG,OAAO,GAAG,CAACC,CAAAA,IAAM;wBAAE,IAAIA,EAAE,EAAE;wBAAE,SAASA,EAAE,OAAO;oBAAC;YAC7D;YACA,MAAMC,UAAUL,OAAO,MAAM,CAAC,GAAG,CAACE;YAClCD,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;YACA,OAAOI;QACX,EAAE,OAAOC,GAAG;YACRL,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;YACA,MAAMK;QACV;IACJ;IAEA,cAAoB;QAChB,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,WAAW,GAAG;QACnB,IAAI,CAAC,aAAa,GAAG,EAAE;IAC3B;AACJ"}
@@ -109,6 +109,58 @@ describe("RefMultiAutocompletePresenter", ()=>{
109
109
  "second"
110
110
  ]);
111
111
  });
112
+ it("ignores a search response that arrives after a newer one", async ()=>{
113
+ const boulder = createEntry("boulder", "Boulder");
114
+ const baker = createEntry("baker", "Baker");
115
+ let resolveSlow = ()=>{};
116
+ const slow = new Promise((resolve)=>{
117
+ resolveSlow = resolve;
118
+ });
119
+ const container = new Container();
120
+ container.registerInstance(SearchContentEntriesUseCase, {
121
+ execute: async ({ query })=>{
122
+ if ("B" === query) {
123
+ await slow;
124
+ return {
125
+ data: [
126
+ baker
127
+ ]
128
+ };
129
+ }
130
+ return {
131
+ data: query ? [
132
+ boulder
133
+ ] : [
134
+ defaultEntry
135
+ ]
136
+ };
137
+ }
138
+ });
139
+ container.registerInstance(GetContentEntriesUseCase, {
140
+ execute: async ()=>({
141
+ latest: [],
142
+ published: []
143
+ })
144
+ });
145
+ container.register(RefMultiAutocompletePresenter_js_RefMultiAutocompletePresenter);
146
+ const presenter = container.resolve(RefMultiAutocompletePresenter);
147
+ await presenter.init({
148
+ modelIds: [
149
+ "location"
150
+ ]
151
+ });
152
+ const first = presenter.search("B");
153
+ await presenter.search("Boulder");
154
+ resolveSlow();
155
+ await first;
156
+ expect(presenter.vm.options).toEqual([
157
+ {
158
+ label: "Boulder",
159
+ value: "boulder"
160
+ }
161
+ ]);
162
+ expect(presenter.vm.loading).toBe(false);
163
+ });
112
164
  });
113
165
  describe("RefSingleAutocompletePresenter", ()=>{
114
166
  it("keeps an entry picked from the search results", async ()=>{
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js","sources":["../../../../../src/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { CmsReferenceEntry } from \"~/features/contentEntry/refTypes.js\";\nimport { RefMultiAutocompletePresenter as MultiAbstraction } from \"./multi/abstractions.js\";\nimport { RefMultiAutocompletePresenter } from \"./multi/RefMultiAutocompletePresenter.js\";\nimport { RefSingleAutocompletePresenter as SingleAbstraction } from \"./single/abstractions.js\";\nimport { RefSingleAutocompletePresenter } from \"./single/RefSingleAutocompletePresenter.js\";\n\nconst createEntry = (entryId: string, title: string): CmsReferenceEntry => ({\n id: `${entryId}#0001`,\n entryId,\n status: \"draft\",\n title,\n description: null,\n image: null,\n createdOn: \"2026-01-01T00:00:00.000Z\",\n savedOn: \"2026-01-01T00:00:00.000Z\",\n createdBy: { id: \"1\", type: \"admin\", displayName: \"Admin\" },\n modifiedBy: null,\n model: { modelId: \"location\", name: \"Location\" },\n published: null,\n wbyAco_location: null\n});\n\n// The default list holds only the first entry, so the second one can only be found by searching.\nconst defaultEntry = createEntry(\"first\", \"First\");\nconst searchedEntry = createEntry(\"second\", \"Second\");\n\nconst createContainer = () => {\n const container = new Container();\n container.registerInstance(SearchContentEntriesUseCase, {\n execute: async ({ query }) => ({ data: query ? [searchedEntry] : [defaultEntry] })\n });\n container.registerInstance(GetContentEntriesUseCase, {\n execute: async () => ({ latest: [], published: [] })\n });\n return container;\n};\n\ndescribe(\"RefMultiAutocompletePresenter\", () => {\n it(\"keeps an entry picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n await presenter.search(\"sec\");\n\n const refs = presenter.select([\"second\"]);\n\n expect(refs).toEqual([{ id: \"second#0001\", modelId: \"location\" }]);\n expect(presenter.vm.values).toEqual([\"second\"]);\n expect(presenter.vm.options).toContainEqual({ label: \"Second\", value: \"second\" });\n });\n\n it(\"keeps earlier picks when another entry is picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n presenter.select([\"first\"]);\n await presenter.search(\"sec\");\n\n const refs = presenter.select([\"first\", \"second\"]);\n\n expect(refs).toEqual([\n { id: \"first#0001\", modelId: \"location\" },\n { id: \"second#0001\", modelId: \"location\" }\n ]);\n expect(presenter.vm.values).toEqual([\"first\", \"second\"]);\n });\n});\n\ndescribe(\"RefSingleAutocompletePresenter\", () => {\n it(\"keeps an entry picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefSingleAutocompletePresenter);\n const presenter = container.resolve(SingleAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n await presenter.search(\"sec\");\n\n const ref = presenter.select(\"second\");\n\n expect(ref).toEqual({ id: \"second#0001\", modelId: \"location\" });\n expect(presenter.vm.value).toBe(\"second\");\n });\n});\n"],"names":["createEntry","entryId","title","defaultEntry","searchedEntry","createContainer","container","Container","SearchContentEntriesUseCase","query","GetContentEntriesUseCase","describe","it","RefMultiAutocompletePresenter","presenter","MultiAbstraction","refs","expect","RefSingleAutocompletePresenter","SingleAbstraction","ref"],"mappings":";;;;;;;;AAUA,MAAMA,cAAc,CAACC,SAAiBC,QAAsC;QACxE,IAAI,GAAGD,QAAQ,KAAK,CAAC;QACrBA;QACA,QAAQ;QACRC;QACA,aAAa;QACb,OAAO;QACP,WAAW;QACX,SAAS;QACT,WAAW;YAAE,IAAI;YAAK,MAAM;YAAS,aAAa;QAAQ;QAC1D,YAAY;QACZ,OAAO;YAAE,SAAS;YAAY,MAAM;QAAW;QAC/C,WAAW;QACX,iBAAiB;IACrB;AAGA,MAAMC,eAAeH,YAAY,SAAS;AAC1C,MAAMI,gBAAgBJ,YAAY,UAAU;AAE5C,MAAMK,kBAAkB;IACpB,MAAMC,YAAY,IAAIC;IACtBD,UAAU,gBAAgB,CAACE,6BAA6B;QACpD,SAAS,OAAO,EAAEC,KAAK,EAAE,GAAM;gBAAE,MAAMA,QAAQ;oBAACL;iBAAc,GAAG;oBAACD;iBAAa;YAAC;IACpF;IACAG,UAAU,gBAAgB,CAACI,0BAA0B;QACjD,SAAS,UAAa;gBAAE,QAAQ,EAAE;gBAAE,WAAW,EAAE;YAAC;IACtD;IACA,OAAOJ;AACX;AAEAK,SAAS,iCAAiC;IACtCC,GAAG,iDAAiD;QAChD,MAAMN,YAAYD;QAClBC,UAAU,QAAQ,CAACO;QACnB,MAAMC,YAAYR,UAAU,OAAO,CAACS;QAEpC,MAAMD,UAAU,IAAI,CAAC;YAAE,UAAU;gBAAC;aAAW;QAAC;QAC9C,MAAMA,UAAU,MAAM,CAAC;QAEvB,MAAME,OAAOF,UAAU,MAAM,CAAC;YAAC;SAAS;QAExCG,OAAOD,MAAM,OAAO,CAAC;YAAC;gBAAE,IAAI;gBAAe,SAAS;YAAW;SAAE;QACjEC,OAAOH,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;YAAC;SAAS;QAC9CG,OAAOH,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC;YAAE,OAAO;YAAU,OAAO;QAAS;IACnF;IAEAF,GAAG,4EAA4E;QAC3E,MAAMN,YAAYD;QAClBC,UAAU,QAAQ,CAACO;QACnB,MAAMC,YAAYR,UAAU,OAAO,CAACS;QAEpC,MAAMD,UAAU,IAAI,CAAC;YAAE,UAAU;gBAAC;aAAW;QAAC;QAC9CA,UAAU,MAAM,CAAC;YAAC;SAAQ;QAC1B,MAAMA,UAAU,MAAM,CAAC;QAEvB,MAAME,OAAOF,UAAU,MAAM,CAAC;YAAC;YAAS;SAAS;QAEjDG,OAAOD,MAAM,OAAO,CAAC;YACjB;gBAAE,IAAI;gBAAc,SAAS;YAAW;YACxC;gBAAE,IAAI;gBAAe,SAAS;YAAW;SAC5C;QACDC,OAAOH,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;YAAC;YAAS;SAAS;IAC3D;AACJ;AAEAH,SAAS,kCAAkC;IACvCC,GAAG,iDAAiD;QAChD,MAAMN,YAAYD;QAClBC,UAAU,QAAQ,CAACY;QACnB,MAAMJ,YAAYR,UAAU,OAAO,CAACa;QAEpC,MAAML,UAAU,IAAI,CAAC;YAAE,UAAU;gBAAC;aAAW;QAAC;QAC9C,MAAMA,UAAU,MAAM,CAAC;QAEvB,MAAMM,MAAMN,UAAU,MAAM,CAAC;QAE7BG,OAAOG,KAAK,OAAO,CAAC;YAAE,IAAI;YAAe,SAAS;QAAW;QAC7DH,OAAOH,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC;IACpC;AACJ"}
1
+ {"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js","sources":["../../../../../src/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { CmsReferenceEntry } from \"~/features/contentEntry/refTypes.js\";\nimport { RefMultiAutocompletePresenter as MultiAbstraction } from \"./multi/abstractions.js\";\nimport { RefMultiAutocompletePresenter } from \"./multi/RefMultiAutocompletePresenter.js\";\nimport { RefSingleAutocompletePresenter as SingleAbstraction } from \"./single/abstractions.js\";\nimport { RefSingleAutocompletePresenter } from \"./single/RefSingleAutocompletePresenter.js\";\n\nconst createEntry = (entryId: string, title: string): CmsReferenceEntry => ({\n id: `${entryId}#0001`,\n entryId,\n status: \"draft\",\n title,\n description: null,\n image: null,\n createdOn: \"2026-01-01T00:00:00.000Z\",\n savedOn: \"2026-01-01T00:00:00.000Z\",\n createdBy: { id: \"1\", type: \"admin\", displayName: \"Admin\" },\n modifiedBy: null,\n model: { modelId: \"location\", name: \"Location\" },\n published: null,\n wbyAco_location: null\n});\n\n// The default list holds only the first entry, so the second one can only be found by searching.\nconst defaultEntry = createEntry(\"first\", \"First\");\nconst searchedEntry = createEntry(\"second\", \"Second\");\n\nconst createContainer = () => {\n const container = new Container();\n container.registerInstance(SearchContentEntriesUseCase, {\n execute: async ({ query }) => ({ data: query ? [searchedEntry] : [defaultEntry] })\n });\n container.registerInstance(GetContentEntriesUseCase, {\n execute: async () => ({ latest: [], published: [] })\n });\n return container;\n};\n\ndescribe(\"RefMultiAutocompletePresenter\", () => {\n it(\"keeps an entry picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n await presenter.search(\"sec\");\n\n const refs = presenter.select([\"second\"]);\n\n expect(refs).toEqual([{ id: \"second#0001\", modelId: \"location\" }]);\n expect(presenter.vm.values).toEqual([\"second\"]);\n expect(presenter.vm.options).toContainEqual({ label: \"Second\", value: \"second\" });\n });\n\n it(\"keeps earlier picks when another entry is picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n presenter.select([\"first\"]);\n await presenter.search(\"sec\");\n\n const refs = presenter.select([\"first\", \"second\"]);\n\n expect(refs).toEqual([\n { id: \"first#0001\", modelId: \"location\" },\n { id: \"second#0001\", modelId: \"location\" }\n ]);\n expect(presenter.vm.values).toEqual([\"first\", \"second\"]);\n });\n\n it(\"ignores a search response that arrives after a newer one\", async () => {\n const boulder = createEntry(\"boulder\", \"Boulder\");\n const baker = createEntry(\"baker\", \"Baker\");\n // The \"B\" request is slower than the \"Boulder\" one, and its first page does not hold Boulder.\n let resolveSlow: () => void = () => {};\n const slow = new Promise<void>(resolve => {\n resolveSlow = resolve;\n });\n\n const container = new Container();\n container.registerInstance(SearchContentEntriesUseCase, {\n execute: async ({ query }) => {\n if (query === \"B\") {\n await slow;\n return { data: [baker] };\n }\n return { data: query ? [boulder] : [defaultEntry] };\n }\n });\n container.registerInstance(GetContentEntriesUseCase, {\n execute: async () => ({ latest: [], published: [] })\n });\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n const first = presenter.search(\"B\");\n await presenter.search(\"Boulder\");\n resolveSlow();\n await first;\n\n expect(presenter.vm.options).toEqual([{ label: \"Boulder\", value: \"boulder\" }]);\n expect(presenter.vm.loading).toBe(false);\n });\n});\n\ndescribe(\"RefSingleAutocompletePresenter\", () => {\n it(\"keeps an entry picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefSingleAutocompletePresenter);\n const presenter = container.resolve(SingleAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n await presenter.search(\"sec\");\n\n const ref = presenter.select(\"second\");\n\n expect(ref).toEqual({ id: \"second#0001\", modelId: \"location\" });\n expect(presenter.vm.value).toBe(\"second\");\n });\n});\n"],"names":["createEntry","entryId","title","defaultEntry","searchedEntry","createContainer","container","Container","SearchContentEntriesUseCase","query","GetContentEntriesUseCase","describe","it","RefMultiAutocompletePresenter","presenter","MultiAbstraction","refs","expect","boulder","baker","resolveSlow","slow","Promise","resolve","first","RefSingleAutocompletePresenter","SingleAbstraction","ref"],"mappings":"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"}