-
Notifications
You must be signed in to change notification settings - Fork 516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(insights): POC click analytics integration #3547
Conversation
Deploy preview for instantsearchjs ready! Built with commit 0094cce |
}; | ||
return ` | ||
data-analytics-event="clickedObjectIDsAfterSearch" | ||
data-analytics-payload='${JSON.stringify(payload)}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid issue with quotes or whatever we can encode/decode the value in base64
.
__index: index, | ||
__queryID: queryID, | ||
analytics: { | ||
clickedObjectIDsAfterSearch: eventName => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We expose the analytics
attribute on the hit
why not follow the same convention than we have with the highlight
helper? It's an external function that takes the required parameters as input. We could use an external function that takes eventName
, results
, hits
to create the payload and/or another function to create the attributes. Which means that we can re-use it to expose the Hogan version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand your point well, this would require to pass results
to the Template component inside data so that we can use it in the helpers, which also means exposing the results
in every hit. Is this what you had in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot of assumptions in the code because we want to support multiple data types (string and objects). I think this should be stricter. It makes it confusing for the users and it makes the code hard to follow.
I don't find the way the higher-order component is made intuitive in the widget. We could abstract this logic by creating a proxy hit component that uses HitsWithAnalytics
if the analytics
prop is passed, the basic Hits
component otherwise.
There's more things I've noticed but I'll keep it short in the first place :)
Do you plan on adding tests?
@@ -76,6 +78,16 @@ export default function connectHits(renderFn, unmountFn) { | |||
results.hits = escapeHits(results.hits); | |||
} | |||
|
|||
// should we expose this only if instantSearchInstance contains clickAnalytics: true? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I would say so.
// we could decide to have more specific helpers to make the code lighter for func templates | ||
// clickedObjectIDsAfterSearch(payload) | ||
// etc | ||
export function clickedObjectIDsAfterSearch(objectID, payload) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't objectID
be part of the payload?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the helper layer, we only pass the data we want to override.
the exact payload is build by the connector layer so that it can benefit all the flavours.
serializedPayload = JSON.stringify(payload); | ||
} catch (e) { | ||
throw new Error( | ||
`The analytics helper expects payload to be either a string or a JSON object` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we accept a single type? (I'd go for string
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answered in your next comment 😄 #3547 (comment)
payload = JSON.parse(serializedPayload); | ||
} catch (e) { | ||
// assume it's the eventName | ||
payload = { eventName: serializedPayload }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this assumption?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to allow the shorthand
<button ${analytics.clickedObjectIDsAfterSearch(item.objectID, 'Add to basket')}>
Add to basket
</button>
<!-- vs -->
<button ${analytics.clickedObjectIDsAfterSearch(item.objectID, {eventName: 'Add to basket'})}>
Add to basket
</button>
but it's debatable and I'm not holding on it
@@ -1,13 +1,15 @@ | |||
import reduce from 'lodash/reduce'; | |||
import forEach from 'lodash/forEach'; | |||
import find from 'lodash/find'; | |||
import findIndex from 'lodash/findIndex'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a new dependency – I think we can avoid it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right but I think it has no extra cost here because we are already using find
it depends on findIndex
https://github.com/lodash/lodash/blob/4.17.11-npm/find.js#L2
@@ -496,6 +498,58 @@ function createDocumentationMessageGenerator(...widgets) { | |||
}; | |||
} | |||
|
|||
function createAnalyticsHelpers(instantSearchInstance, results) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be moved to its own file.
}; | ||
|
||
const hasClickAnalytics = | ||
instantSearchInstance.searchParameters.clickAnalytics; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could either go explicit with:
const hasClickAnalytics =
Boolean(instantSearchInstance.searchParameters.clickAnalytics);
Or more strict (needs to be aligned in the codebase):
const hasClickAnalytics =
instantSearchInstance.searchParameters.clickAnalytics === true;
import { withHits } from '../.storybook/decorators'; | ||
import * as analyticsHelpers from '../src/helpers/analytics'; | ||
|
||
const insightsClientMock = (eventName, payload) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use the action
from Storybook (see Analytics story).
Co-Authored-By: tkrugg <tkrugg@users.noreply.github.com>
As I answered to your comment: <button ${analytics.clickedObjectIDsAfterSearch(item.objectID, 'Add to basket')}>
Add to basket
</button>
<!-- vs -->
<button ${analytics.clickedObjectIDsAfterSearch(item.objectID, {eventName: 'Add to basket'})}>
Add to basket
</button> but it's debatable and I'm not holding on it
The HOC component here acts like the "proxy", do I'm not sure what you mean. const HitsWithAnalytics = withClickAnalytics(Hits)
const InfiniteHitsWithAnalytics = withClickAnalytics(InfiniteHits)
Of course! also rewriting the new code in TS as much as possible. |
closing this. Replaced by #3598 |
This POC demonstrating a possible integration between our clickAnalytics and InstantSearchJS.
Test this out
add to cart
How it is used
For instant search user, sending
click
events (or other events types) can be done with the following wayThis is going to call the clickedObjectIDsAfterSearch in the following way:
With Hogan
How it works
There are three parts:
connectHits
is usingcreateAnalyticsHelpers()
:analytics
object with methods that call the insights client directly (useful for other flavours)hit.__analytics = { index: "instant_search", queryID: "xxx", objectIDs: ["1"], positions: [10] }
analytics.clickedObjectIDsAfterSearch
are helpers generates data attributesWithClickAnalyticsWrapper
wraps the Hits component and sets a click listener that reads data attribute and calls connector analytics methodsPossible improvements and unresolved questions
We expose the queryID so the user can extract the queryID in the following way: