• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yext / answers-search-ui / 13547664578

26 Feb 2025 03:38PM UTC coverage: 61.773% (-0.4%) from 62.179%
13547664578

push

github

web-flow
Version 1.18.0 (#1916)

This PR represents the work to add Generative Direct Answers support, both in basic searching functionality and analytics support, along with some vulnerability fixes.

* Add GDA Object Model

Similar to the existing DirectAnswer model, we will take in an
object from search-core (GenerativeDirectAnswerResponse in this
case) and use it to construct a model.

J=WAT-4592
TEST=auto
Ran unit tests

* Update package.json to beta version

* ksearch: Add flow for automatically calling into the GDA endpoint

In this change I added the following functionality:
- at the global config level, there will be a new property named
"useGenerativeDirectAnswers" that must be set to true in order
for GDA to work on the site
- When the universal results or vertical results change, that will
trigger a call into the GDA endpoint.

To accomplish the above, I have added two new storage keys - one
for the GDA itself, and one for the Search ID, which we need for
the request into GDA.

J=WAT-4593
TEST=auto, manual
Auto: Wrote new tests
Manual: Spun up local instance of the answers-search-ui + HH theme
and changed the vars to use a Prod Search config with GDA
configured. Saw the request being fired in the network tab when
the prop was set to true, and not fired when the prop was set to
false

* Automated update to THIRD-PARTY-NOTICES from github action's 3rd party notices check

* Include some results data in the GDA object constructed

* Remove console log

* Filter results in GDA model to only include those with name and matching uid in citations

* Add Result Component for Generative Direct Answers

This change includes the logic necessary to visualize a GDA response in
the UI via the answers-search-ui package. The component styled here
(styled using the Handlebars template and custom CSS) will be used if
there is not a custom card for GDA set (in the theme, we will create a
nearly identical, but still different, card visual).

The actual *logic* for get... (continued)

2029 of 3433 branches covered (59.1%)

Branch coverage included in aggregate %.

67 of 114 new or added lines in 6 files covered. (58.77%)

30 existing lines in 4 files now uncovered.

3483 of 5490 relevant lines covered (63.44%)

26.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.31
/src/core/analytics/visibilityanalyticshandler.js
1
import { isIE } from '../utils/useragent';
2
import AnalyticsEvent from '../analytics/analyticsevent';
3
import Searcher from '../models/searcher';
4
import StorageKeys from '../storage/storagekeys';
5

6
const RESULTS_VISIBILITY_EVENT = {
1✔
7
  HIDDEN: 'RESULTS_HIDDEN',
8
  UNHIDDEN: 'RESULTS_UNHIDDEN'
9
};
10

11
/**
12
 * Manages the document's visibility status and handles any visibility related analytics events.
13
 */
14
export default class VisibilityAnalyticsHandler {
15
  constructor (analyticsReporterService, verticalKey) {
16
    this._previousResultsVisibilityEvent = undefined;
7✔
17
    this._analyticsReporterService = analyticsReporterService;
7✔
18
    this._verticalKey = verticalKey;
7✔
19
  }
20

21
  /**
22
   * Initialize visibility change event listener(s) to send analytics events
23
   * when a result page have become visible or have been hidden.
24
   *
25
   * @param {Storage} storage - a container around application state
26
   */
27
  initVisibilityChangeListeners (storage) {
28
    /**
29
     * Safari desktop listener and IE11 listeners fire visibility change event twice when switch
30
     * to new tab and then close browser. Variable "_previousResultsVisibilityEvent" is used to ensure
31
     * RESULTS_HIDDEN analytics event does not get send again if the page is already hidden.
32
     */
33
    document.addEventListener('visibilitychange', () => {
7✔
34
      if (this._resultsVisibilityChangeToHidden()) {
×
35
        this._previousResultsVisibilityEvent = RESULTS_VISIBILITY_EVENT.HIDDEN;
×
36
        this._reportVisibilityChangeEvent(RESULTS_VISIBILITY_EVENT.HIDDEN);
×
37
      } else if (this._resultsVisibilityChangeToVisible()) {
×
38
        this._previousResultsVisibilityEvent = RESULTS_VISIBILITY_EVENT.UNHIDDEN;
×
39
        this._reportVisibilityChangeEvent(RESULTS_VISIBILITY_EVENT.UNHIDDEN);
×
40
      }
41
    });
42

43
    /**
44
     * VisibilityChange API does not register when page is terminated (close tab/browser) in IE11.
45
     * Unload event is used to capture those RESULTS_HIDDEN scenarios.
46
     */
47
    if (isIE()) {
7!
48
      window.addEventListener('unload', () => {
×
49
        if (this._previousResultsVisibilityEvent !== RESULTS_VISIBILITY_EVENT.HIDDEN) {
×
50
          this._reportVisibilityChangeEvent(RESULTS_VISIBILITY_EVENT.HIDDEN);
×
51
        }
52
      });
53
    }
54

55
    /**
56
     * Page history updates caused by pushState() or replaceState(), such as when a search is performed,
57
     * will not trigger a complete page load so the document's visibility state will not change. Use
58
     * popstate event and storage listener on QUERY_ID to report result visibility change events for
59
     * back/forward page navigation of the same answers page.
60
     */
61
    window.addEventListener('popstate', () => {
7✔
62
      const poppedStateQueryId = storage.get(StorageKeys.HISTORY_POP_STATE)?.get('pop-state-queryId');
×
63
      const storageQueryId = this._analyticsReporterService.getQueryId();
×
64
      this._analyticsReporterService.setQueryId(poppedStateQueryId);
×
65
      this._previousResultsVisibilityEvent = RESULTS_VISIBILITY_EVENT.HIDDEN;
×
66
      this._reportVisibilityChangeEvent(RESULTS_VISIBILITY_EVENT.HIDDEN);
×
67
      this._analyticsReporterService.setQueryId(storageQueryId);
×
68
    });
69

70
    storage.registerListener({
7✔
71
      eventType: 'update',
72
      storageKey: StorageKeys.QUERY_ID,
73
      callback: id => {
UNCOV
74
        if (this._resultsVisibilityChangeToVisible()) {
×
UNCOV
75
          this._previousResultsVisibilityEvent = RESULTS_VISIBILITY_EVENT.UNHIDDEN;
×
UNCOV
76
          this._reportVisibilityChangeEvent(RESULTS_VISIBILITY_EVENT.UNHIDDEN);
×
77
        }
78
      }
79
    });
80
  }
81

82
  /**
83
   * Returns true if results page was previously reported as unhidden and the page is now hidden.
84
   *
85
   * @returns {boolean}
86
   */
87
  _resultsVisibilityChangeToHidden () {
88
    return document.visibilityState === 'hidden' && this._previousResultsVisibilityEvent !== RESULTS_VISIBILITY_EVENT.HIDDEN;
×
89
  }
90

91
  /**
92
   * Returns true if results page was previously reported as hidden and the page is now visible.
93
   *
94
   * @returns {boolean}
95
   */
96
  _resultsVisibilityChangeToVisible () {
UNCOV
97
    return document.visibilityState === 'visible' && this._previousResultsVisibilityEvent !== RESULTS_VISIBILITY_EVENT.UNHIDDEN;
×
98
  }
99

100
  /**
101
   * Send visibility change related analytics event.
102
   *
103
   * @param {string} eventName - the name of the analytics event to report
104
   */
105
  _reportVisibilityChangeEvent (eventName) {
UNCOV
106
    const queryId = this._analyticsReporterService.getQueryId();
×
UNCOV
107
    if (!queryId) {
×
UNCOV
108
      return;
×
109
    }
110
    const searcher = this._verticalKey ? Searcher.VERTICAL : Searcher.UNIVERSAL;
×
111
    const event = new AnalyticsEvent(eventName).addOptions({ searcher });
×
112
    this._analyticsReporterService.report(event);
×
113
  }
114
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc