@getlupa/client 0.10.1 → 0.10.2

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 (31) hide show
  1. package/dist/cjs/components/search-box/SearchBox.vue.d.ts +6 -1
  2. package/dist/cjs/components/search-box/products/SearchBoxProduct.vue.d.ts +1 -0
  3. package/dist/cjs/components/search-results/SearchResults.vue.d.ts +2 -0
  4. package/dist/cjs/components/search-results/products/product-card/SearchResultsProductCard.vue.d.ts +1 -0
  5. package/dist/cjs/constants/development/searchResultsDev.example.const.d.ts +1 -0
  6. package/dist/cjs/index.min.js +112 -34
  7. package/dist/cjs/store/modules/tracking.d.ts +3 -1
  8. package/dist/cjs/types/AnalyticsOptions.d.ts +3 -2
  9. package/dist/cjs/types/search-box/Common.d.ts +1 -0
  10. package/dist/cjs/types/search-results/SearchResultsProductCardOptions.d.ts +1 -0
  11. package/dist/es/components/search-box/SearchBox.vue.d.ts +6 -1
  12. package/dist/es/components/search-box/products/SearchBoxProduct.vue.d.ts +1 -0
  13. package/dist/es/components/search-results/SearchResults.vue.d.ts +2 -0
  14. package/dist/es/components/search-results/products/product-card/SearchResultsProductCard.vue.d.ts +1 -0
  15. package/dist/es/constants/development/searchResultsDev.example.const.d.ts +1 -0
  16. package/dist/es/index.min.js +112 -34
  17. package/dist/es/store/modules/tracking.d.ts +3 -1
  18. package/dist/es/types/AnalyticsOptions.d.ts +3 -2
  19. package/dist/es/types/search-box/Common.d.ts +1 -0
  20. package/dist/es/types/search-results/SearchResultsProductCardOptions.d.ts +1 -0
  21. package/dist/iife/components/search-box/SearchBox.vue.d.ts +6 -1
  22. package/dist/iife/components/search-box/products/SearchBoxProduct.vue.d.ts +1 -0
  23. package/dist/iife/components/search-results/SearchResults.vue.d.ts +2 -0
  24. package/dist/iife/components/search-results/products/product-card/SearchResultsProductCard.vue.d.ts +1 -0
  25. package/dist/iife/constants/development/searchResultsDev.example.const.d.ts +1 -0
  26. package/dist/iife/index.min.js +1 -1
  27. package/dist/iife/store/modules/tracking.d.ts +3 -1
  28. package/dist/iife/types/AnalyticsOptions.d.ts +3 -2
  29. package/dist/iife/types/search-box/Common.d.ts +1 -0
  30. package/dist/iife/types/search-results/SearchResultsProductCardOptions.d.ts +1 -0
  31. package/package.json +1 -1
@@ -12210,7 +12210,7 @@ const getRelativePath = (link) => {
12210
12210
  }
12211
12211
  catch (_a) {
12212
12212
  // Invalid url, let's return original string
12213
- return link;
12213
+ return (link === null || link === void 0 ? void 0 : link.endsWith("/")) ? link.slice(0, link.length - 1) : link;
12214
12214
  }
12215
12215
  };
12216
12216
  // Checks if url links match absolutely, or if their relative parts are equal
@@ -12268,7 +12268,18 @@ let SearchBoxProduct = class SearchBoxProduct extends Vue$1 {
12268
12268
  }
12269
12269
  return "";
12270
12270
  }
12271
+ get title() {
12272
+ if (!this.panelOptions.titleKey) {
12273
+ return "";
12274
+ }
12275
+ const title = this.item[this.panelOptions.titleKey] || "";
12276
+ this.addHistory({
12277
+ item: title,
12278
+ });
12279
+ return title;
12280
+ }
12271
12281
  handleClick(event) {
12282
+ var _a;
12272
12283
  if (this.panelOptions.titleKey) {
12273
12284
  this.addHistory({
12274
12285
  item: this.item[this.panelOptions.titleKey] || "",
@@ -12285,7 +12296,7 @@ let SearchBoxProduct = class SearchBoxProduct extends Vue$1 {
12285
12296
  type: "itemClick",
12286
12297
  analytics: {
12287
12298
  type: "autocomplete_product_click",
12288
- label: this.link,
12299
+ label: (_a = this.title) !== null && _a !== void 0 ? _a : this.link,
12289
12300
  },
12290
12301
  },
12291
12302
  });
@@ -13328,8 +13339,9 @@ let SearchBoxMainPanel = class SearchBoxMainPanel extends Vue$1 {
13328
13339
  this.sdkOptions = this.options.options;
13329
13340
  }
13330
13341
  get displayResults() {
13331
- var _a;
13332
- return ((_a = this.inputValue) === null || _a === void 0 ? void 0 : _a.length) >= this.options.minInputLength;
13342
+ var _a, _b;
13343
+ return (((_a = this.inputValue) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
13344
+ ((_b = this.inputValue) === null || _b === void 0 ? void 0 : _b.length) >= this.options.minInputLength);
13333
13345
  }
13334
13346
  get displayHistory() {
13335
13347
  var _a;
@@ -13528,7 +13540,7 @@ __vue_render__$V._withStripped = true;
13528
13540
  /* style */
13529
13541
  const __vue_inject_styles__$V = function (inject) {
13530
13542
  if (!inject) return
13531
- inject("data-v-d48b96f0_0", { source: "#lupa-search-box-panel {\n display: flex;\n justify-content: space-between;\n flex-direction: column;\n}\n.lupa-more-results {\n text-align: center;\n}", map: undefined, media: undefined });
13543
+ inject("data-v-947d134e_0", { source: "#lupa-search-box-panel {\n display: flex;\n justify-content: space-between;\n flex-direction: column;\n}\n.lupa-more-results {\n text-align: center;\n}", map: undefined, media: undefined });
13532
13544
 
13533
13545
  };
13534
13546
  /* scoped */
@@ -30865,6 +30877,7 @@ let SearchBox = class SearchBox extends Vue$1 {
30865
30877
  this.opened = true;
30866
30878
  this.inputValue = value;
30867
30879
  this.suggestedValue = defaultSuggestedValue;
30880
+ this.trackSearchQuery(value);
30868
30881
  if (this.isSearchContainer) {
30869
30882
  this.goToResultsDebounced({
30870
30883
  searchText: this.searchValue,
@@ -30957,11 +30970,22 @@ let SearchBox = class SearchBox extends Vue$1 {
30957
30970
  type: "itemClick",
30958
30971
  analytics: {
30959
30972
  type: "autocomplete_product_click",
30960
- label: doc.doc.url || doc.id,
30973
+ label: doc.title || doc.id,
30961
30974
  },
30962
30975
  },
30963
30976
  });
30964
30977
  }
30978
+ trackSearchQuery(query) {
30979
+ if (!query) {
30980
+ return;
30981
+ }
30982
+ this.trackSearch({
30983
+ queryKey: this.suggestedValue.queryKey,
30984
+ query: {
30985
+ searchText: query,
30986
+ },
30987
+ });
30988
+ }
30965
30989
  trackSuggestionClick(suggestion) {
30966
30990
  var _a;
30967
30991
  if (suggestion ||
@@ -30976,7 +31000,7 @@ let SearchBox = class SearchBox extends Vue$1 {
30976
31000
  searchQuery: this.inputValue,
30977
31001
  type: "suggestionClick",
30978
31002
  analytics: {
30979
- type: "autocomplete_keyword_click",
31003
+ type: "autocomplete_suggestion_click",
30980
31004
  label: suggestion || this.searchValue,
30981
31005
  },
30982
31006
  },
@@ -31008,6 +31032,9 @@ __decorate([
31008
31032
  __decorate([
31009
31033
  tracking$4.Action("track")
31010
31034
  ], SearchBox.prototype, "trackClick", void 0);
31035
+ __decorate([
31036
+ tracking$4.Action("trackSearch")
31037
+ ], SearchBox.prototype, "trackSearch", void 0);
31011
31038
  __decorate([
31012
31039
  params$e.Action("setSearchResultsLink")
31013
31040
  ], SearchBox.prototype, "setSearchResultsLink", void 0);
@@ -31081,7 +31108,7 @@ __vue_render__$U._withStripped = true;
31081
31108
  /* style */
31082
31109
  const __vue_inject_styles__$U = function (inject) {
31083
31110
  if (!inject) return
31084
- inject("data-v-ed058b02_0", { source: "\n#lupa-search-box {\n width: 100%;\n}\n.lupa-search-box-wrapper {\n position: relative;\n}\n", map: undefined, media: undefined });
31111
+ inject("data-v-d81b3ccc_0", { source: "\n#lupa-search-box {\n width: 100%;\n}\n.lupa-search-box-wrapper {\n position: relative;\n}\n", map: undefined, media: undefined });
31085
31112
 
31086
31113
  };
31087
31114
  /* scoped */
@@ -35363,10 +35390,14 @@ let SearchResultsProductCard = class SearchResultsProductCard extends Vue$1 {
35363
35390
  });
35364
35391
  }
35365
35392
  get id() {
35366
- if (this.options.idKey) {
35367
- return this.product[this.options.idKey];
35368
- }
35369
- return "";
35393
+ return this.options.idKey
35394
+ ? this.product[this.options.idKey]
35395
+ : "";
35396
+ }
35397
+ get title() {
35398
+ return this.options.titleKey
35399
+ ? this.product[this.options.titleKey]
35400
+ : "";
35370
35401
  }
35371
35402
  handleClick() {
35372
35403
  var _a, _b;
@@ -35376,6 +35407,10 @@ let SearchResultsProductCard = class SearchResultsProductCard extends Vue$1 {
35376
35407
  itemId: this.id,
35377
35408
  searchQuery: this.query,
35378
35409
  type: "itemClick",
35410
+ analytics: {
35411
+ type: "search_product_click",
35412
+ label: this.title || this.id || this.link,
35413
+ },
35379
35414
  },
35380
35415
  });
35381
35416
  (_b = (_a = this.searchResultOptions.callbacks) === null || _a === void 0 ? void 0 : _a.onProductClick) === null || _b === void 0 ? void 0 : _b.call(_a, {
@@ -35392,8 +35427,8 @@ let SearchResultsProductCard = class SearchResultsProductCard extends Vue$1 {
35392
35427
  type: item.type,
35393
35428
  analytics: item.type === "addToCart"
35394
35429
  ? {
35395
- type: "add_to_cart",
35396
- label: this.link,
35430
+ type: "search_add_to_cart",
35431
+ label: this.title || this.id || this.link,
35397
35432
  }
35398
35433
  : undefined,
35399
35434
  },
@@ -35563,7 +35598,7 @@ __vue_render__$o._withStripped = true;
35563
35598
 
35564
35599
  const initAnalyticsTracking = (analyticsOptions) => {
35565
35600
  try {
35566
- if ((analyticsOptions === null || analyticsOptions === void 0 ? void 0 : analyticsOptions.enabled) && analyticsOptions.type === "ua") {
35601
+ if (analyticsOptions === null || analyticsOptions === void 0 ? void 0 : analyticsOptions.enabled) {
35567
35602
  window.sessionStorage.setItem(TRACKING_ANALYTICS_KEY, JSON.stringify(analyticsOptions));
35568
35603
  }
35569
35604
  else {
@@ -35708,25 +35743,66 @@ const sendGa = (name, ...args) => {
35708
35743
  });
35709
35744
  };
35710
35745
  const trackAnalyticsEvent = (data) => {
35711
- var _a;
35746
+ var _a, _b, _c;
35712
35747
  try {
35713
35748
  const options = JSON.parse((_a = window.sessionStorage.getItem(TRACKING_ANALYTICS_KEY)) !== null && _a !== void 0 ? _a : "{}");
35714
- if (!data.analytics || !options.enabled) {
35749
+ if (!data.analytics ||
35750
+ !options.enabled ||
35751
+ ((_b = options.ignoreEvents) === null || _b === void 0 ? void 0 : _b.includes((_c = data.analytics) === null || _c === void 0 ? void 0 : _c.type))) {
35715
35752
  return;
35716
35753
  }
35717
- const ga = window.ga;
35718
- if (!ga) {
35719
- console.error("Google Analytics object not found");
35720
- return;
35754
+ switch (options.type) {
35755
+ case "ua":
35756
+ sendUaAnalyticsEvent(data, options);
35757
+ break;
35758
+ case "ga4":
35759
+ sendGa4AnalyticsEvent(data, options);
35760
+ break;
35761
+ case "debug":
35762
+ processDebugEvent(data);
35763
+ break;
35764
+ default:
35765
+ sendUaAnalyticsEvent(data, options);
35721
35766
  }
35722
- sendGa("send", "event", options.parentEventName, data.analytics.type, data.analytics.label);
35723
35767
  }
35724
- catch (_b) {
35768
+ catch (_d) {
35725
35769
  console.error("Unable to send an event to google analytics");
35726
35770
  }
35727
35771
  };
35772
+ const sendUaAnalyticsEvent = (data, options) => {
35773
+ var _a, _b, _c, _d;
35774
+ const ga = window.ga;
35775
+ if (!ga) {
35776
+ console.error("Google Analytics object not found");
35777
+ return;
35778
+ }
35779
+ sendGa("send", "event", options.parentEventName, (_b = (_a = data.analytics) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "", (_d = (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : "");
35780
+ };
35781
+ const sendGa4AnalyticsEvent = (data, options) => {
35782
+ var _a, _b, _c, _d;
35783
+ if (!window || !window.dataLayer) {
35784
+ console.error("dataLayer object not found.");
35785
+ return;
35786
+ }
35787
+ const sendItemTitle = data.searchQuery !== ((_a = data.analytics) === null || _a === void 0 ? void 0 : _a.label);
35788
+ const params = {
35789
+ search_text: data.searchQuery,
35790
+ item_title: sendItemTitle ? (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.label : undefined,
35791
+ };
35792
+ window.dataLayer.push(Object.assign({ event: (_d = (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : options.parentEventName }, params));
35793
+ };
35794
+ const processDebugEvent = (data) => {
35795
+ var _a, _b, _c;
35796
+ const sendItemTitle = data.searchQuery !== ((_a = data.analytics) === null || _a === void 0 ? void 0 : _a.label);
35797
+ const params = {
35798
+ event: (_b = data.analytics) === null || _b === void 0 ? void 0 : _b.type,
35799
+ search_text: data.searchQuery,
35800
+ item_title: sendItemTitle ? (_c = data.analytics) === null || _c === void 0 ? void 0 : _c.label : undefined,
35801
+ };
35802
+ console.debug("Analytics debug event:", params);
35803
+ };
35728
35804
  const track = (queryKey, data = {}, options) => {
35729
- if (!isTrackingEnabled()) {
35805
+ if (!isTrackingEnabled() || !data.searchQuery) {
35730
35806
  return;
35731
35807
  }
35732
35808
  trackLupaEvent(queryKey, data, options);
@@ -37393,6 +37469,7 @@ let SearchResultsProducts = class SearchResultsProducts extends Vue$1 {
37393
37469
  "labels",
37394
37470
  "queryKey",
37395
37471
  "idKey",
37472
+ "titleKey",
37396
37473
  "routingBehavior",
37397
37474
  ]);
37398
37475
  }
@@ -38079,7 +38156,11 @@ let SearchResults = class SearchResults extends Vue$1 {
38079
38156
  this.query(getPublicQuery(publicQuery, this.initialFilters, this.isProductList));
38080
38157
  }
38081
38158
  query(publicQuery) {
38082
- this.trackSearch({ queryKey: this.options.queryKey, query: publicQuery });
38159
+ this.trackSearch({
38160
+ queryKey: this.options.queryKey,
38161
+ query: publicQuery,
38162
+ type: "search_form_submit",
38163
+ });
38083
38164
  const context = getLupaTrackingContext();
38084
38165
  const limit = publicQuery.limit || this.defaultSearchResultPageSize;
38085
38166
  const query = Object.assign(Object.assign(Object.assign({}, publicQuery), context), { limit });
@@ -39345,6 +39426,7 @@ let SearchBoxModule = class SearchBoxModule extends VuexModule {
39345
39426
  link: generateLink((_b = panel.links) === null || _b === void 0 ? void 0 : _b.details, doc),
39346
39427
  queryKey: panel.queryKey,
39347
39428
  id: panel.idKey ? doc[panel.idKey] : "",
39429
+ title: panel.titleKey ? doc[panel.titleKey] : "",
39348
39430
  };
39349
39431
  }
39350
39432
  saveSuggestions({ queryKey, suggestions, inputValue, }) {
@@ -39821,11 +39903,6 @@ let ParamsModule = class ParamsModule extends VuexModule {
39821
39903
  const routing = (_a = this.context.rootGetters["options/boxRoutingBehavior"]) !== null && _a !== void 0 ? _a : "direct-link";
39822
39904
  redirectToResultsPage(this.searchResultsLink, searchText, facet, routing);
39823
39905
  }
39824
- this.context.dispatch("tracking/track", {
39825
- data: {
39826
- analytics: { type: "search_form_submit", label: searchText },
39827
- },
39828
- }, { root: true });
39829
39906
  }
39830
39907
  appendParams({ params, paramsToRemove, encode = true, save = true, searchResultsLink, }) {
39831
39908
  if (!(params === null || params === void 0 ? void 0 : params.length)) {
@@ -39976,22 +40053,23 @@ const getSearchTrackingData = (searchText, type) => {
39976
40053
  };
39977
40054
  };
39978
40055
  let TrackingModule = class TrackingModule extends VuexModule {
39979
- trackSearch({ queryKey, query, }) {
40056
+ trackSearch({ queryKey, query, type = "search_query", }) {
39980
40057
  var _a, _b;
39981
40058
  const options = (_a = this.context.rootGetters["options/envOptions"]) !== null && _a !== void 0 ? _a : {};
39982
40059
  const hasFilters = Object.keys((_b = query.filters) !== null && _b !== void 0 ? _b : {}).length > 0;
39983
40060
  if (hasFilters) {
39984
- const data = getSearchTrackingData(query.searchText, "filters");
40061
+ const data = getSearchTrackingData(query.searchText, "search_filters");
39985
40062
  track(queryKey, data, options);
40063
+ return;
39986
40064
  }
39987
- const data = getSearchTrackingData(query.searchText, "search");
40065
+ const data = getSearchTrackingData(query.searchText, type);
39988
40066
  track(queryKey, data, options);
39989
40067
  }
39990
40068
  trackResults({ queryKey, results, }) {
39991
40069
  var _a;
39992
40070
  const options = (_a = this.context.rootGetters["options/envOptions"]) !== null && _a !== void 0 ? _a : {};
39993
40071
  if (results.total < 1) {
39994
- const data = getSearchTrackingData(results.searchText, "zero_results");
40072
+ const data = getSearchTrackingData(results.searchText, "search_zero_results");
39995
40073
  track(queryKey, data, options);
39996
40074
  }
39997
40075
  }
@@ -1,10 +1,12 @@
1
+ import { AnalyticsEventType } from "@/types/AnalyticsOptions";
1
2
  import { TrackableEventData } from "@/types/search-box/Common";
2
3
  import { PublicQuery, SearchQueryResult } from "@getlupa/client-sdk/Types";
3
4
  import { VuexModule } from "vuex-module-decorators";
4
5
  export default class TrackingModule extends VuexModule {
5
- trackSearch({ queryKey, query, }: {
6
+ trackSearch({ queryKey, query, type, }: {
6
7
  queryKey: string;
7
8
  query: PublicQuery;
9
+ type?: AnalyticsEventType;
8
10
  }): void;
9
11
  trackResults({ queryKey, results, }: {
10
12
  queryKey: string;
@@ -1,7 +1,8 @@
1
1
  export declare const PARENT_EVENT_NAME = "GetLupa";
2
- export declare type AnalyticsEventType = "search" | "search_form_submit" | "autocomplete_keyword_click" | "autocomplete_product_click" | "zero_results" | "filters" | "add_to_cart";
2
+ export declare type AnalyticsEventType = "search_query" | "search_form_submit" | "autocomplete_suggestion_click" | "autocomplete_product_click" | "search_product_click" | "search_zero_results" | "search_filters" | "search_add_to_cart";
3
3
  export declare type AnalyticsOptions = {
4
- type: "ua";
4
+ type: "ua" | "ga4" | "debug";
5
5
  enabled: boolean;
6
6
  parentEventName: string;
7
+ ignoreEvents?: AnalyticsEventType[];
7
8
  };
@@ -41,4 +41,5 @@ export declare type HighlightedDocInfo = {
41
41
  link?: string;
42
42
  queryKey?: string;
43
43
  id?: unknown;
44
+ title?: string;
44
45
  };
@@ -14,6 +14,7 @@ export declare type SearchResultsProductCardOptions = {
14
14
  elements: DocumentElement[];
15
15
  queryKey: string;
16
16
  idKey?: string;
17
+ titleKey?: string;
17
18
  };
18
19
  export declare type SearchResultBadgeOptions = {
19
20
  anchor: AnchorPosition;
@@ -1,7 +1,7 @@
1
1
  import { FetchedData, HighlightedDocInfo, InputSuggestion, InputSuggestionFacet, SelectedData, TrackableEventData } from "@/types/search-box/Common";
2
2
  import { SearchBoxInputOptions, SearchBoxOptions, SearchBoxPanelOptions } from "@/types/search-box/SearchBoxOptions";
3
3
  import { QueryParams } from "@/types/search-results/QueryParams";
4
- import { Document } from "@getlupa/client-sdk/Types";
4
+ import { Document, PublicQuery } from "@getlupa/client-sdk/Types";
5
5
  import Vue from "vue";
6
6
  declare const params: import("vuex-class/lib/bindings").BindingHelpers;
7
7
  export default class SearchBox extends Vue {
@@ -28,6 +28,10 @@ export default class SearchBox extends Vue {
28
28
  queryKey: string;
29
29
  data: TrackableEventData;
30
30
  }) => void;
31
+ trackSearch: ({ queryKey, query, }: {
32
+ queryKey: string;
33
+ query: PublicQuery;
34
+ }) => void;
31
35
  setSearchResultsLink: (searchResultsLink: string) => {
32
36
  searchResultsLink: string;
33
37
  };
@@ -63,6 +67,7 @@ export default class SearchBox extends Vue {
63
67
  query: string;
64
68
  }): void;
65
69
  trackDocumentClick(doc: HighlightedDocInfo): void;
70
+ trackSearchQuery(query?: string): void;
66
71
  trackSuggestionClick(suggestion?: string): void;
67
72
  resetValues(): void;
68
73
  handleProductClick(): void;
@@ -23,5 +23,6 @@ export default class SearchBoxProduct extends Vue {
23
23
  get imageElements(): DocumentElement[];
24
24
  get detailElements(): DocumentElement[];
25
25
  get id(): string;
26
+ get title(): string;
26
27
  handleClick(event?: Event): void;
27
28
  }
@@ -2,6 +2,7 @@ import { QueryParams } from "@/types/search-results/QueryParams";
2
2
  import { ProductGrid, SearchResultsDidYouMeanLabels, SearchResultsOptions, SearchResultsProductOptions } from "@/types/search-results/SearchResultsOptions";
3
3
  import { FilterGroup, PublicQuery, SearchQueryResult } from "@getlupa/client-sdk/Types";
4
4
  import Vue from "vue";
5
+ import { AnalyticsEventType } from "@/types/AnalyticsOptions";
5
6
  export default class SearchResults extends Vue {
6
7
  options: SearchResultsOptions;
7
8
  initialFilters: FilterGroup;
@@ -16,6 +17,7 @@ export default class SearchResults extends Vue {
16
17
  trackSearch: ({ queryKey, query, }: {
17
18
  queryKey: string;
18
19
  query: PublicQuery;
20
+ type?: AnalyticsEventType;
19
21
  }) => void;
20
22
  trackResults: ({ queryKey, results, }: {
21
23
  queryKey: string;
@@ -30,6 +30,7 @@ export default class SearchResultsProductCard extends Vue {
30
30
  mounted(): void;
31
31
  checkIfIsInStock(): Promise<void>;
32
32
  get id(): string;
33
+ get title(): string;
33
34
  handleClick(): void;
34
35
  handleProductEvent(item: {
35
36
  type: ReportableEventType;
@@ -100,6 +100,7 @@ export declare const SEARCH_RESULTS_CONFIGURATION: {
100
100
  details: string;
101
101
  };
102
102
  idKey: string;
103
+ titleKey: string;
103
104
  elements: DocumentElement[];
104
105
  breadcrumbs: ({
105
106
  label: string;