@miso.ai/client-sdk-ui 1.11.0-beta.9 → 1.11.0

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 (111) hide show
  1. package/dist/cjs/actor/feedback.js +6 -39
  2. package/dist/cjs/actor/fields.js +14 -6
  3. package/dist/cjs/actor/index.js +0 -7
  4. package/dist/cjs/actor/interactions.js +4 -15
  5. package/dist/cjs/actor/sessions.js +2 -32
  6. package/dist/cjs/actor/tracker.js +25 -26
  7. package/dist/cjs/actor/utils.js +0 -49
  8. package/dist/cjs/actor/view.js +40 -40
  9. package/dist/cjs/actor/views.js +29 -8
  10. package/dist/cjs/constants.js +18 -19
  11. package/dist/cjs/defaults/hybrid-search/phrases.js +3 -3
  12. package/dist/cjs/defaults/hybrid-search/templates.js +22 -7
  13. package/dist/cjs/element/role/index.js +12 -5
  14. package/dist/cjs/element/role/{miso-hits.js → miso-more.js} +4 -4
  15. package/dist/cjs/element/role/miso-total.js +20 -0
  16. package/dist/cjs/layout/affiliation/affiliation.js +0 -3
  17. package/dist/cjs/layout/banner/banner.js +0 -4
  18. package/dist/cjs/layout/button/more-button.js +73 -0
  19. package/dist/cjs/layout/container/container.js +0 -3
  20. package/dist/cjs/layout/error/error.js +0 -4
  21. package/dist/cjs/layout/index.js +7 -0
  22. package/dist/cjs/layout/input/facets.js +0 -4
  23. package/dist/cjs/layout/input/option-list.js +0 -4
  24. package/dist/cjs/layout/input/search-box.js +13 -12
  25. package/dist/cjs/layout/list/cards.js +0 -3
  26. package/dist/cjs/layout/list/carousel.js +0 -3
  27. package/dist/cjs/layout/list/collection.js +22 -7
  28. package/dist/cjs/layout/list/list.js +0 -3
  29. package/dist/cjs/layout/radio/feedback.js +0 -3
  30. package/dist/cjs/layout/radio/radio.js +0 -4
  31. package/dist/cjs/layout/raf.js +4 -2
  32. package/dist/cjs/layout/text/text.js +0 -4
  33. package/dist/cjs/layout/text/typewriter/index.js +0 -3
  34. package/dist/cjs/layout/triggerable.js +79 -0
  35. package/dist/cjs/util/states.js +0 -15
  36. package/dist/cjs/version.js +1 -1
  37. package/dist/cjs/workflow/answer-based.js +70 -105
  38. package/dist/cjs/workflow/ask.js +29 -27
  39. package/dist/cjs/workflow/asks.js +1 -1
  40. package/dist/cjs/workflow/autocompletable.js +33 -0
  41. package/dist/cjs/workflow/autocomplete.js +122 -0
  42. package/dist/cjs/workflow/base.js +156 -98
  43. package/dist/cjs/workflow/explore.js +48 -46
  44. package/dist/cjs/workflow/hybrid-search-answer.js +17 -7
  45. package/dist/cjs/workflow/hybrid-search-results.js +17 -87
  46. package/dist/cjs/workflow/hybrid-search-views.js +122 -0
  47. package/dist/cjs/workflow/hybrid-search.js +47 -168
  48. package/dist/cjs/workflow/options.js +32 -17
  49. package/dist/cjs/workflow/processors.js +166 -2
  50. package/dist/cjs/workflow/recommendation.js +36 -49
  51. package/dist/cjs/workflow/search-based.js +228 -0
  52. package/dist/cjs/workflow/search.js +21 -68
  53. package/dist/cjs/workflow/use-link.js +44 -0
  54. package/package.json +2 -2
  55. package/src/actor/feedback.js +6 -23
  56. package/src/actor/fields.js +14 -6
  57. package/src/actor/index.js +0 -1
  58. package/src/actor/interactions.js +4 -10
  59. package/src/actor/sessions.js +3 -31
  60. package/src/actor/tracker.js +25 -19
  61. package/src/actor/utils.js +0 -47
  62. package/src/actor/view.js +27 -15
  63. package/src/actor/views.js +32 -8
  64. package/src/constants.js +25 -16
  65. package/src/defaults/hybrid-search/phrases.js +2 -2
  66. package/src/defaults/hybrid-search/templates.js +17 -5
  67. package/src/element/role/index.js +2 -1
  68. package/src/element/role/{miso-hits.js → miso-more.js} +3 -3
  69. package/src/element/role/miso-total.js +16 -0
  70. package/src/layout/affiliation/affiliation.js +1 -5
  71. package/src/layout/banner/banner.js +0 -5
  72. package/src/layout/button/more-button.js +69 -0
  73. package/src/layout/container/container.js +1 -5
  74. package/src/layout/error/error.js +0 -5
  75. package/src/layout/index.js +2 -1
  76. package/src/layout/input/facets.js +0 -5
  77. package/src/layout/input/option-list.js +0 -5
  78. package/src/layout/input/search-box.js +11 -14
  79. package/src/layout/list/cards.js +0 -4
  80. package/src/layout/list/carousel.js +0 -4
  81. package/src/layout/list/collection.js +21 -9
  82. package/src/layout/list/list.js +0 -4
  83. package/src/layout/radio/feedback.js +0 -4
  84. package/src/layout/radio/radio.js +0 -5
  85. package/src/layout/raf.js +2 -2
  86. package/src/layout/text/text.js +0 -5
  87. package/src/layout/text/typewriter/index.js +1 -5
  88. package/src/layout/triggerable.js +76 -0
  89. package/src/util/states.js +0 -14
  90. package/src/version.js +1 -1
  91. package/src/workflow/answer-based.js +61 -72
  92. package/src/workflow/ask.js +30 -24
  93. package/src/workflow/asks.js +1 -1
  94. package/src/workflow/autocompletable.js +29 -0
  95. package/src/workflow/autocomplete.js +113 -0
  96. package/src/workflow/base.js +133 -87
  97. package/src/workflow/explore.js +38 -30
  98. package/src/workflow/hybrid-search-answer.js +17 -6
  99. package/src/workflow/hybrid-search-results.js +17 -77
  100. package/src/workflow/hybrid-search-views.js +124 -0
  101. package/src/workflow/hybrid-search.js +47 -157
  102. package/src/workflow/options.js +26 -11
  103. package/src/workflow/processors.js +151 -3
  104. package/src/workflow/recommendation.js +32 -33
  105. package/src/workflow/search-based.js +222 -0
  106. package/src/workflow/search.js +21 -65
  107. package/src/workflow/use-link.js +40 -0
  108. package/dist/cjs/actor/autocomplete.js +0 -160
  109. package/dist/cjs/workflow/utils.js +0 -42
  110. package/src/actor/autocomplete.js +0 -133
  111. package/src/workflow/utils.js +0 -29
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.UseLinkMixin = void 0;
7
+ exports.enableUseLink = enableUseLink;
8
+ var _commons = require("@miso.ai/commons");
9
+ function enableUseLink(prototype) {
10
+ (0, _commons.mixin)(prototype, UseLinkMixin.prototype);
11
+ }
12
+ class UseLinkMixin {
13
+ useLink(fn) {
14
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
15
+ if (typeof fn !== 'function' && fn !== false) {
16
+ throw new Error('useLink(fn) expects fn to be a function or false');
17
+ }
18
+ this._linkFn = fn ? [fn, options] : false;
19
+ return this;
20
+ }
21
+ _submitToPage() {
22
+ let args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
23
+ if (!this._linkFn) {
24
+ return;
25
+ }
26
+ // TODO: we want a submit event
27
+ const url = this._getSubmitUrl(args);
28
+ const [_, options = {}] = this._linkFn;
29
+ if (options.open === false) {
30
+ window.location.href = url;
31
+ } else {
32
+ const target = options.target || '_blank';
33
+ const windowFeatures = options.windowFeatures || '';
34
+ window.open(url, target, windowFeatures);
35
+ }
36
+ }
37
+ _getSubmitUrl(args) {
38
+ if (!this._linkFn) {
39
+ throw new Error('useLink(fn) must be called before _getSubmitUrl()');
40
+ }
41
+ return this._linkFn[0](args.q);
42
+ }
43
+ }
44
+ exports.UseLinkMixin = UseLinkMixin;
package/package.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "url": "https://github.com/MisoAI/miso-client-js-sdk/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@miso.ai/commons": "1.11.0-beta.9"
29
+ "@miso.ai/commons": "1.11.0"
30
30
  },
31
- "version": "1.11.0-beta.9"
31
+ "version": "1.11.0"
32
32
  }
@@ -1,4 +1,5 @@
1
1
  import * as fields from './fields.js';
2
+ import { EVENT_TYPE } from '../constants.js';
2
3
 
3
4
  export default class FeedbackActor {
4
5
 
@@ -10,29 +11,11 @@ export default class FeedbackActor {
10
11
  }
11
12
 
12
13
  _trigger(state) {
13
- const { [fields.data()]: data } = this._hub.states;
14
- const { question_id } = data && data.value || {};
15
- if (!question_id) {
16
- return;
17
- }
18
- // TODO: ad-hoc
19
- const { request } = data || {};
20
- const { payload } = request || {};
21
- const question_source = payload && payload._meta && payload._meta.question_source;
22
- this._hub.trigger(fields.interaction(), this._buildInteraction(state, { question_source }));
23
- }
24
-
25
- _buildInteraction({ value, unselected }, { question_source }) {
26
- return {
27
- type: 'feedback',
28
- context: {
29
- custom_context: {
30
- result_type: 'answer',
31
- question_source,
32
- value: unselected ? 'unselected' : value,
33
- },
34
- },
35
- };
14
+ this._hub.trigger(fields.tracker(), {
15
+ type: EVENT_TYPE.FEEDBACK,
16
+ result_type: 'answer',
17
+ value: state.unselected ? 'unselected' : state.value,
18
+ });
36
19
  }
37
20
 
38
21
  _destroy() {
@@ -6,6 +6,10 @@ export function suggestions() {
6
6
  return 'suggestions';
7
7
  }
8
8
 
9
+ export function completions() {
10
+ return 'completions';
11
+ }
12
+
9
13
  export function filters() {
10
14
  return 'filters';
11
15
  }
@@ -14,16 +18,16 @@ export function query() {
14
18
  return 'query';
15
19
  }
16
20
 
17
- export function request(aspect) {
18
- return aspect ? `request:${aspect}` : 'request';
21
+ export function request() {
22
+ return 'request';
19
23
  }
20
24
 
21
- export function response(aspect) {
22
- return aspect ? `response:${aspect}` : 'response';
25
+ export function response() {
26
+ return 'response';
23
27
  }
24
28
 
25
- export function data(aspect) {
26
- return aspect ? `data:${aspect}` : 'data';
29
+ export function data() {
30
+ return 'data';
27
31
  }
28
32
 
29
33
  export function view(role) {
@@ -49,3 +53,7 @@ export function feedback() {
49
53
  export function input() {
50
54
  return 'input';
51
55
  }
56
+
57
+ export function more() {
58
+ return 'more';
59
+ }
@@ -6,5 +6,4 @@ export { default as SessionMaker } from './sessions.js';
6
6
  export { default as DataActor } from './data.js';
7
7
  export { default as ViewsActor } from './views.js';
8
8
  export { default as FeedbackActor } from './feedback.js';
9
- export { default as AutocompleteActor } from './autocomplete.js';
10
9
  export { default as InteractionsActor } from './interactions.js';
@@ -12,20 +12,14 @@ export default class InteractionsActor {
12
12
  }
13
13
 
14
14
  _handle(payload) {
15
+ // TODO: omit when option == false
15
16
  payload = asArray(payload);
16
- const { preprocess = [], handle } = this._options.resolved.interactions;
17
- for (const p of preprocess) {
18
- payload = payload.map(p);
19
- }
20
- // TODO: support filter?
17
+
21
18
  if (payload.length === 0) {
22
19
  return;
23
20
  }
24
- if (handle) {
25
- handle(payload);
26
- } else {
27
- this._client.api.interactions.upload(payload, { merge: true });
28
- }
21
+
22
+ this._client.api.interactions.upload(payload, { merge: true });
29
23
  }
30
24
 
31
25
  destroy() {
@@ -1,4 +1,4 @@
1
- import { uuidv4, delegateGetters } from '@miso.ai/commons';
1
+ import { uuidv4 } from '@miso.ai/commons';
2
2
  import * as fields from './fields.js';
3
3
 
4
4
  export default class SessionMaker {
@@ -6,43 +6,15 @@ export default class SessionMaker {
6
6
  constructor(hub) {
7
7
  this._hub = hub;
8
8
  this._sessionIndex = 0;
9
- delegateGetters(hub, this, ['active']);
10
- }
11
-
12
- get active() {
13
- // TODO: may eliminate active flag?
14
- const session = this._hub.states[fields.session()];
15
- return !!(session && session.active);
16
- }
17
-
18
- new({ force = false } = {}) {
19
- const currentSession = this._hub.states[fields.session()];
20
- if (currentSession && !currentSession.active && !force) {
21
- return;
22
- }
23
- const session = this._create();
24
- this._hub.update(fields.session(), session);
25
- }
26
-
27
- start() {
28
- const session = this._hub.states[fields.session()] || this._create();
29
- if (session.active) {
30
- return;
31
- }
32
- this._hub.update(fields.session(), { ...session, active: true });
33
9
  }
34
10
 
35
11
  restart() {
36
- this._hub.update(fields.session(), this._create(true));
37
- }
38
-
39
- _create(active = false) {
40
- return Object.freeze({
41
- active,
12
+ const session = Object.freeze({
42
13
  uuid: uuidv4(),
43
14
  index: this._sessionIndex++,
44
15
  meta: {},
45
16
  });
17
+ this._hub.update(fields.session(), session);
46
18
  }
47
19
 
48
20
  }
@@ -1,8 +1,7 @@
1
- import { asArray } from '@miso.ai/commons';
2
- import { TRACKING_EVENT_TYPES, TRACKING_STATUS, EVENT_TYPE, validateEventType } from '../constants.js';
1
+ import { asArray, trimObj } from '@miso.ai/commons';
2
+ import { PERFORMANCE_EVENT_TYPES, TRACKING_STATUS, EVENT_TYPE, validateEventType } from '../constants.js';
3
3
  import * as fields from './fields.js';
4
4
  import States from '../util/states.js';
5
- import { toInteraction } from './utils.js';
6
5
 
7
6
  const VALUELESS_ITEMS = [Symbol('valueless')];
8
7
 
@@ -26,29 +25,36 @@ export default class Tracker {
26
25
  return this._states.getFullState(item);
27
26
  }
28
27
 
29
- _trigger(type, items, meta = {}) {
28
+ _trigger(type, items) {
29
+ validateEventType(type);
30
30
  if (this._valueless) {
31
- meta = items;
32
31
  items = VALUELESS_ITEMS;
33
32
  }
34
- validateEventType(type);
33
+
35
34
  this._syncSession();
36
35
 
37
- if (type !== EVENT_TYPE.SUBMIT) {
38
- items = this._states.untriggered(asArray(items), type);
39
- if (items.length === 0) {
40
- return;
41
- }
42
- this._states.set(items, type, TRACKING_STATUS.TRIGGERED);
36
+ items = this._states.untriggered(asArray(items), type);
37
+ if (items.length === 0) {
38
+ return;
43
39
  }
40
+ this._states.set(items, type, TRACKING_STATUS.TRIGGERED);
44
41
 
45
- const property = this._role === 'results' ? 'products' : this._role; // TODO: ad-hoc, see #83
46
- const misoId = this._getMisoId();
47
- const request = this._getRequestPayload();
48
- const values = this._valueless ? undefined : items;
42
+ this._sendToHub({
43
+ type,
44
+ items: this._valueless ? undefined : items,
45
+ });
46
+ }
47
+
48
+ submit({ value }) {
49
+ this._syncSession();
50
+ this._sendToHub({
51
+ type: EVENT_TYPE.SUBMIT,
52
+ value,
53
+ });
54
+ }
49
55
 
50
- // TODO: should trigger 'tracker' and let workflow translate to interactions
51
- this._hub.trigger(fields.interaction(), toInteraction({ property, misoId, request }, { event: type, values, meta }));
56
+ _sendToHub(args) {
57
+ this._hub.trigger(fields.tracker(), Object.freeze(trimObj({ role: this._role, ...args })));
52
58
  }
53
59
 
54
60
  _syncSession() {
@@ -82,7 +88,7 @@ export default class Tracker {
82
88
 
83
89
  }
84
90
 
85
- for (const type of TRACKING_EVENT_TYPES) {
91
+ for (const type of PERFORMANCE_EVENT_TYPES) {
86
92
  Tracker.prototype[type] = function(items, meta) {
87
93
  this._trigger(type, items, meta);
88
94
  };
@@ -1,50 +1,3 @@
1
- import { trimObj } from '@miso.ai/commons';
2
- import { ROLE, EVENT_TYPE } from '../constants.js';
3
-
4
- const PRODUCT_TYPED_PROPERTIES = new Set([
5
- ROLE.PRODUCTS,
6
- ROLE.SOURCES,
7
- ROLE.RELATED_RESOURCES,
8
- ]);
9
-
10
- // TODO: move away
11
- export function isProduct(property) {
12
- return PRODUCT_TYPED_PROPERTIES.has(property);
13
- }
14
-
15
- export function toInteraction({ property, misoId, request }, { event, values, manual }) {
16
- // TODO: ad-hoc
17
- const question_source = request && request._meta && request._meta.question_source;
18
- const isProductType = isProduct(property);
19
- const isSubmit = event === EVENT_TYPE.SUBMIT;
20
- const isAffiliation = property === ROLE.AFFILIATION;
21
-
22
- const product_ids = isProductType && values ? values.map(v => typeof v === 'string' ? v : v.product_id).filter(v => v) : !isSubmit ? [] : undefined;
23
- const items = !isProductType && !isSubmit && values ? values.map(v => v.text || v.id || v) : undefined;
24
- const value = isSubmit ? values : undefined;
25
-
26
- const channel = isAffiliation ? values[0] && values[0].channel : undefined;
27
- const positions = isAffiliation ? values.map(v => v.position) : undefined;
28
-
29
- const payload = trimObj({
30
- type: event === 'viewable' ? 'viewable_impression' : event,
31
- product_ids,
32
- miso_id: misoId,
33
- context: {
34
- custom_context: trimObj({
35
- property,
36
- channel,
37
- items,
38
- value,
39
- positions,
40
- trigger: manual ? 'manual' : 'auto',
41
- question_source,
42
- }),
43
- },
44
- });
45
- return payload;
46
- }
47
-
48
1
  export function isCurrentSession(hub, session) {
49
2
  if (!session) {
50
3
  return false;
package/src/actor/view.js CHANGED
@@ -22,7 +22,7 @@ export default class ViewActor {
22
22
  constructor(views, role) {
23
23
  this._events = new EventEmitter({ target: this });
24
24
  this._views = views;
25
- this._config = views._rolesConfig[role] || {};
25
+ this._mapping = asMappingFunction((views._roles.mappings || {})[role] || role);
26
26
 
27
27
  defineValues(this, {
28
28
  role,
@@ -101,6 +101,10 @@ export default class ViewActor {
101
101
  }
102
102
  return this._tracker;
103
103
  }
104
+
105
+ get workflowOptions() {
106
+ return this._views._getWorkflowOptions();
107
+ }
104
108
 
105
109
  get filters() {
106
110
  return this._views.filters;
@@ -122,21 +126,17 @@ export default class ViewActor {
122
126
  const previousData = this._data;
123
127
  this._data = data = this._sliceData(data || this._views._getData());
124
128
 
125
- // protocol: no reaction until session starts
126
- const { session, status, ongoing, request, meta } = data;
127
- const active = session && session.active;
128
- if (!force && !active) {
129
- return;
130
- }
131
-
132
129
  // if data is unchanged, skip unless forced
133
130
  if (!force && statesEqual(data, previousData)) {
134
131
  return;
135
132
  }
136
133
 
134
+ const { session, status, ongoing, request, meta, value } = data;
135
+ const baseState = { session, status, ongoing, request, meta, data: value, ...getDataEmptyness(data) };
136
+
137
137
  // render
138
138
  const notifyUpdate = (state = {}, options) => {
139
- state !== false && this.updateState({ session, status, ongoing, request, meta, ...state }, options);
139
+ state !== false && this.updateState({ ...baseState, ...state }, options);
140
140
  };
141
141
  try {
142
142
  await this._layout.render(element, data, { notifyUpdate });
@@ -146,11 +146,10 @@ export default class ViewActor {
146
146
  }
147
147
  }
148
148
 
149
- updateState({ session, status, ongoing, request, meta, error }, { silent = false } = {}) {
150
- if (error) {
151
- status = STATUS.ERRONEOUS;
152
- }
153
- const state = this._state = Object.freeze(trimObj({ session, status, ongoing, request, meta, error }));
149
+ updateState(state, { silent = false } = {}) {
150
+ // there might be error from notifyUpdate() call
151
+ const status = state.error ? STATUS.ERRONEOUS : state.status;
152
+ state = this._state = Object.freeze(trimObj({ ...state, status }));
154
153
  const { role } = this;
155
154
  this.hub.update(fields.view(role), state, { silent });
156
155
  }
@@ -163,7 +162,7 @@ export default class ViewActor {
163
162
  _sliceData(data) {
164
163
  const { value, error, status, meta, ...rest } = data;
165
164
  const sliced = {
166
- value: asMappingFunction(this._config.mapping || this.role)(data),
165
+ value: this._mapping(data),
167
166
  status,
168
167
  data: value,
169
168
  meta,
@@ -247,3 +246,16 @@ function asMappingFunction(fn) {
247
246
  throw new Error(`Invalid mapping function: ${fn}`);
248
247
  }
249
248
  }
249
+
250
+ function getDataEmptyness(data) {
251
+ return isDataStatusRelevantToEmptyness(data) ? { empty: !!isDateValueEmpty(data) } : {};
252
+ }
253
+
254
+ function isDataStatusRelevantToEmptyness(data) {
255
+ return data.status === STATUS.READY && !data.ongoing;
256
+ }
257
+
258
+ function isDateValueEmpty(data) {
259
+ const { value } = data;
260
+ return value === undefined || ((Array.isArray(value) || typeof value === 'string') && !value.length);
261
+ }
@@ -1,4 +1,4 @@
1
- import { defineValues, delegateGetters } from '@miso.ai/commons';
1
+ import { defineValues, delegateGetters, trimObj } from '@miso.ai/commons';
2
2
  import { ROLE } from '../constants.js';
3
3
  import * as fields from './fields.js';
4
4
  import ViewActor from './view.js';
@@ -12,8 +12,7 @@ export default class ViewsActor {
12
12
  workflow,
13
13
  extensions,
14
14
  layouts,
15
- roles = [],
16
- rolesConfig = {},
15
+ roles,
17
16
  options,
18
17
  }) {
19
18
  this._hub = hub;
@@ -21,14 +20,17 @@ export default class ViewsActor {
21
20
  this._extensions = extensions;
22
21
  this._layoutFactory = layouts;
23
22
  this._options = options;
24
- this._rolesConfig = rolesConfig;
23
+ this._roles = roles;
25
24
  this._containers = new Map();
26
25
  this._containerTracker = undefined;
27
26
  this._views = {};
28
- this._trackers = new Trackers(this, roles);
27
+ this._trackers = new Trackers(this, roles.members);
29
28
  this._filters = new Filters(this);
30
29
 
31
- for (const role of roles) {
30
+ for (const role of roles.members) {
31
+ if (role === ROLE.CONTAINER) {
32
+ continue;
33
+ }
32
34
  this._views[role] = new ViewActor(this, role);
33
35
  }
34
36
 
@@ -42,10 +44,14 @@ export default class ViewsActor {
42
44
  this._unsubscribes = [
43
45
  () => window.removeEventListener('resize', syncSize),
44
46
  hub.on(fields.data(), data => this.refresh({ data })),
47
+ options.on('autocomplete', () => this._syncAutocompleteOptions()),
48
+ options.on('pagination', () => this._syncPaginationOptions()),
45
49
  options.on('trackers', () => this._syncTrackers()),
46
50
  options.on('layouts', () => this._syncLayouts()),
47
51
  ];
48
52
 
53
+ this._syncAutocompleteOptions();
54
+ this._syncPaginationOptions();
49
55
  this._syncTrackers();
50
56
  this._syncLayouts();
51
57
  }
@@ -144,7 +150,7 @@ export default class ViewsActor {
144
150
  } else {
145
151
  for (let [role, [name, options]] of Object.entries(layouts)) {
146
152
  // TODO: try to omit if unchanged
147
- const fn = () => this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
153
+ const fn = () => this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName, workflowOptions: this._getWorkflowOptions() });
148
154
  if (role === ROLE.CONTAINER) {
149
155
  for (const view of this._containers.values()) {
150
156
  view.layout = fn();
@@ -162,7 +168,7 @@ export default class ViewsActor {
162
168
  return undefined;
163
169
  }
164
170
  const [name, options] = args;
165
- return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName });
171
+ return this._layoutFactory.create(name, { ...options, role, workflow: this._workflowName, workflowOptions: this._getWorkflowOptions() });
166
172
  }
167
173
 
168
174
  get(role) {
@@ -229,6 +235,21 @@ export default class ViewsActor {
229
235
  return this._containerTracker;
230
236
  }
231
237
 
238
+ _syncAutocompleteOptions() {
239
+ this._autocompleteOptions = this._options.resolved.autocomplete;
240
+ }
241
+
242
+ _syncPaginationOptions() {
243
+ this._paginationOptions = this._options.resolved.pagination;
244
+ }
245
+
246
+ _getWorkflowOptions() {
247
+ return trimObj({
248
+ autocomplete: this._autocompleteOptions,
249
+ pagination: this._paginationOptions,
250
+ });
251
+ }
252
+
232
253
  _error(e) {
233
254
  // TODO: hub trigger error event
234
255
  console.error(e);
@@ -271,6 +292,9 @@ class Trackers {
271
292
  constructor(views, roles) {
272
293
  this._views = views;
273
294
  for (const role of roles) {
295
+ if (role === ROLE.CONTAINER) {
296
+ continue;
297
+ }
274
298
  Object.defineProperty(this, role, {
275
299
  get: () => views.get(role).tracker,
276
300
  });
package/src/constants.js CHANGED
@@ -22,7 +22,8 @@ export const ROLE = Object.freeze({
22
22
  AFFILIATION: 'affiliation',
23
23
  KEYWORDS: 'keywords',
24
24
  FACETS: 'facets',
25
- HITS: 'hits',
25
+ TOTAL: 'total',
26
+ MORE: 'more',
26
27
  ERROR: 'error',
27
28
  });
28
29
 
@@ -40,34 +41,29 @@ const DATA_ROLE_SET = new Set([
40
41
  ROLE.RELATED_QUESTIONS,
41
42
  ROLE.AFFILIATION,
42
43
  ROLE.FACETS,
43
- ROLE.HITS,
44
+ ROLE.TOTAL,
44
45
  ]);
45
46
 
46
47
  export function isDataRole(role) {
47
48
  return DATA_ROLE_SET.has(role);
48
49
  }
49
50
 
50
- export const DATA_ASPECT = Object.freeze({
51
- AUTOCOMPLETE: 'autocomplete',
52
- });
51
+ const PRODUCT_ROLE_SET = new Set([
52
+ ROLE.PRODUCTS,
53
+ ROLE.SOURCES,
54
+ ROLE.RELATED_RESOURCES,
55
+ ]);
53
56
 
54
- export const LAYOUT_CATEGORY = Object.freeze({
55
- CONTAINER: 'container',
56
- BANNER: 'banner',
57
- LIST: 'list',
58
- QUERY: 'query',
59
- RADIO: 'radio',
60
- FILTERS: 'filters',
61
- TEXT: 'text',
62
- AFFILIATION: 'affiliation',
63
- ERROR: 'error',
64
- });
57
+ export function isProductRole(role) {
58
+ return PRODUCT_ROLE_SET.has(role);
59
+ }
65
60
 
66
61
  export const EVENT_TYPE = Object.freeze({
67
62
  IMPRESSION: 'impression',
68
63
  VIEWABLE: 'viewable',
69
64
  CLICK: 'click',
70
65
  SUBMIT: 'submit',
66
+ FEEDBACK: 'feedback',
71
67
  });
72
68
 
73
69
  export const TRACKING_EVENT_TYPES = [
@@ -75,8 +71,21 @@ export const TRACKING_EVENT_TYPES = [
75
71
  EVENT_TYPE.VIEWABLE,
76
72
  EVENT_TYPE.CLICK,
77
73
  EVENT_TYPE.SUBMIT,
74
+ EVENT_TYPE.FEEDBACK,
75
+ ];
76
+
77
+ export const PERFORMANCE_EVENT_TYPES = [
78
+ EVENT_TYPE.IMPRESSION,
79
+ EVENT_TYPE.VIEWABLE,
80
+ EVENT_TYPE.CLICK,
78
81
  ];
79
82
 
83
+ const PERFORMANCE_EVENT_TYPE_SET = new Set(PERFORMANCE_EVENT_TYPES);
84
+
85
+ export function isPerformanceEventType(type) {
86
+ return PERFORMANCE_EVENT_TYPE_SET.has(type);
87
+ }
88
+
80
89
  export const TRACKING_STATUS = Object.freeze({
81
90
  UNTRACKED: 'untracked',
82
91
  TRACKING: 'tracking',
@@ -14,9 +14,9 @@ export const sources = 'Sources';
14
14
  export const keywords = 'You searched for “${value}”';
15
15
 
16
16
  /**
17
- * The phrase to display <miso-hits>
17
+ * The phrase to display <miso-total>
18
18
  */
19
- export const hits = '${value} Results';
19
+ export const total = '${value} Results';
20
20
 
21
21
  export const showMore = 'Show more';
22
22
  export const showLess = 'Show less';
@@ -37,6 +37,7 @@ class Sections extends _Sections {
37
37
  this.searchResultsInfoGroup(options),
38
38
  this.searchResultsFilters(options),
39
39
  '<miso-products></miso-products>',
40
+ this.more(options),
40
41
  ]);
41
42
  }
42
43
 
@@ -45,7 +46,7 @@ class Sections extends _Sections {
45
46
  const { classPrefix } = options;
46
47
  return element('div', { classes: [`${classPrefix}__search-results-info`] }, [
47
48
  this.keywords(options),
48
- this.hits(options),
49
+ this.total(options),
49
50
  ]);
50
51
  }
51
52
 
@@ -62,9 +63,20 @@ class Sections extends _Sections {
62
63
  return phrase(options, { name: 'keywords', tag: 'div', inline: true }).replaceAll('${value}', '<miso-keywords></miso-keywords>');
63
64
  }
64
65
 
65
- hits(options) {
66
+ total(options) {
66
67
  const { phrase } = this._helpers;
67
- return phrase(options, { name: 'hits', tag: 'div', inline: true }).replaceAll('${value}', '<miso-hits></miso-hits>');
68
+ return phrase(options, { name: 'total', tag: 'div', inline: true }).replaceAll('${value}', '<miso-total></miso-total>');
69
+ }
70
+
71
+ more(options) {
72
+ if (!options.moreButton) {
73
+ return '';
74
+ }
75
+ const { element } = this._helpers;
76
+ const { classPrefix } = options;
77
+ return element('div', { classes: [`${classPrefix}__search-results-more-container`] }, [
78
+ '<miso-more></miso-more>',
79
+ ]);
68
80
  }
69
81
 
70
82
  }
@@ -75,13 +87,13 @@ export const sections = externalize(new Sections({ helpers }));
75
87
  export function root(options = {}) {
76
88
  const { normalizeOptions, section, container } = helpers;
77
89
  options = normalizeOptions(options);
78
- const { answerBox, answerGroup } = sections;
90
+ const { answerBox, answerGroup, searchResultsGroup } = sections;
79
91
  const { answerBox: answerBoxOptions = true } = options;
80
92
  return [
81
93
  section(options, { name: 'question' }, [
82
94
  container(options, { name: 'query' }, '<miso-query></miso-query>'),
83
95
  ]),
84
96
  section(options, { name: 'answer' }, answerBoxOptions ? [ answerBox(options) ] : answerGroup(options)),
85
- section(options, { name: 'search-results' }, sections.searchResultsGroup(options)),
97
+ section(options, { name: 'search-results' }, searchResultsGroup(options)),
86
98
  ].join('');
87
99
  };