Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/swift-cycles-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'polaris.shopify.com': patch
---

Added search tracking using google analytics
89 changes: 86 additions & 3 deletions polaris.shopify.com/src/components/GlobalSearch/GlobalSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ function scrollToTop() {
overflowEl?.scrollTo({top: 0, behavior: 'smooth'});
}

function captureSearchEvent(
searchTerm: string,
resultRank: number,
selectedResult?: string,
) {
const eventParams = {
searchTerm,
resultRank,
selectedResult,
};

if (searchTerm) {
window.gtag('event', 'customSearch', eventParams);
}
}

function scrollIntoView() {
const overflowEl = document.querySelector(`.${styles.ResultsInner}`);
const highlightedEl = document.querySelector(
Expand Down Expand Up @@ -152,6 +168,7 @@ function GlobalSearch() {
if (resultsInRenderedOrder.length > 0) {
setIsOpen(false);
const url = resultsInRenderedOrder[currentResultIndex].url;
captureSearchEvent(searchTerm, currentResultIndex + 1, url);
router.push(url);
}
break;
Expand All @@ -171,7 +188,14 @@ function GlobalSearch() {
Search <span className={styles.KeyboardShortcutHint}>/</span>
</button>

<Dialog open={isOpen} onClose={() => setIsOpen(false)}>
<Dialog
open={isOpen}
onClose={() => {
setIsOpen(false);
// on close we want to capture that no search result was selected
captureSearchEvent(searchTerm, 0);
}}
>
<div className={styles.PreventBackgroundInteractions}></div>
<div className="dark-mode styles-for-site-but-not-polaris-examples">
<Dialog.Panel className={styles.Results}>
Expand Down Expand Up @@ -213,6 +237,8 @@ function GlobalSearch() {
<SearchResults
searchResults={searchResults}
currentItemId={currentItemId}
searchTerm={searchTerm}
resultsInRenderedOrder={resultsInRenderedOrder}
/>
)}
</div>
Expand All @@ -226,9 +252,13 @@ function GlobalSearch() {
function SearchResults({
searchResults,
currentItemId,
searchTerm,
resultsInRenderedOrder,
}: {
searchResults: GroupedSearchResults;
currentItemId: string;
searchTerm?: string;
resultsInRenderedOrder: SearchResults;
}) {
return (
<>
Expand All @@ -243,6 +273,12 @@ function SearchResults({
if (!meta.foundations) return null;
const {title, description, icon, category} =
meta.foundations;
const resultIndex = resultsInRenderedOrder.findIndex(
(r) => {
return r.id === id;
},
);
const rank = resultIndex + 1; // zero-indexed
return (
<SearchContext.Provider
key={title}
Expand All @@ -252,6 +288,10 @@ function SearchResults({
title={title}
description={description}
url={url}
customOnClick={() =>
searchTerm &&
captureSearchEvent(searchTerm, rank, url)
}
renderPreview={() => (
<FoundationsThumbnail
icon={icon}
Expand All @@ -273,6 +313,12 @@ function SearchResults({
{results.map(({id, url, meta}) => {
if (!meta.patterns) return null;
const {title, description, previewImg} = meta.patterns;
const resultIndex = resultsInRenderedOrder.findIndex(
(r) => {
return r.id === id;
},
);
const rank = resultIndex + 1;
return (
<SearchContext.Provider
key={id}
Expand All @@ -282,6 +328,10 @@ function SearchResults({
url={url}
description={description}
title={title}
customOnClick={() =>
searchTerm &&
captureSearchEvent(searchTerm, rank, url)
}
renderPreview={() => (
<PatternThumbnailPreview
alt={title}
Expand All @@ -304,6 +354,12 @@ function SearchResults({
{results.map(({id, url, meta}) => {
if (!meta.components) return null;
const {title, description, status, group} = meta.components;
const resultIndex = resultsInRenderedOrder.findIndex(
(r) => {
return r.id === id;
},
);
const rank = resultIndex + 1;
return (
<SearchContext.Provider
key={id}
Expand All @@ -314,6 +370,10 @@ function SearchResults({
description={description}
title={title}
status={status}
customOnClick={() =>
searchTerm &&
captureSearchEvent(searchTerm, rank, url)
}
renderPreview={() => (
<ComponentThumbnail title={title} group={group} />
)}
Expand Down Expand Up @@ -342,12 +402,24 @@ function SearchResults({
{results.map(({id, meta}) => {
if (!meta.tokens) return null;
const {token, category} = meta.tokens;
const resultIndex = resultsInRenderedOrder.findIndex(
(r) => {
return r.id === id;
},
);
const rank = resultIndex + 1;
return (
<SearchContext.Provider
key={id}
value={{currentItemId, id}}
>
<TokenList.Item category={category} token={token} />
<TokenList.Item
category={category}
token={token}
customOnClick={captureSearchEvent}
searchTerm={searchTerm}
rank={rank}
/>
</SearchContext.Provider>
);
})}
Expand All @@ -363,12 +435,23 @@ function SearchResults({
{results.map(({id, meta}) => {
if (!meta.icons) return null;
const {icon} = meta.icons;
const resultIndex = resultsInRenderedOrder.findIndex(
(r) => {
return r.id === id;
},
);
const rank = resultIndex + 1;
return (
<SearchContext.Provider
key={id}
value={{currentItemId, id}}
>
<IconGrid.Item icon={icon} />
<IconGrid.Item
icon={icon}
customOnClick={captureSearchEvent}
searchTerm={searchTerm}
rank={rank}
/>
</SearchContext.Provider>
);
})}
Expand Down
34 changes: 24 additions & 10 deletions polaris.shopify.com/src/components/Grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,39 @@ export interface GridItemProps {
deepLinks?: {url: string; text: string}[];
renderPreview?: () => React.ReactNode;
status?: Status;
customOnClick?: React.MouseEventHandler<HTMLAnchorElement>;
searchQuery?: string;
rank?: number;
}

export const GridItem = forwardRef(
(
{as = 'li', title, description, url, deepLinks, renderPreview, status},
{
as = 'li',
title,
description,
url,
deepLinks,
renderPreview,
status,
customOnClick,
},
ref,
) => {
const searchAttributes = useGlobalSearchResult();
return (
<Box as={as} ref={ref} className={styles.GridItem} {...searchAttributes}>
<Link href={url} className={styles.Text}>
<SearchResultHighlight />
{renderPreview && (
<div className={styles.Preview}>{renderPreview()}</div>
)}
<h4>
{title} {status && <StatusBadge status={status} />}
</h4>
<p>{stripMarkdownLinks(description || '')}</p>
<Link legacyBehavior href={url} className={styles.Text}>
<a onClick={customOnClick}>
<SearchResultHighlight />
{renderPreview && (
<div className={styles.Preview}>{renderPreview()}</div>
)}
<h4>
{title} {status && <StatusBadge status={status} />}
</h4>
<p>{stripMarkdownLinks(description || '')}</p>
</a>
</Link>
{deepLinks && (
<ul className={styles.DeepLinks}>
Expand Down
26 changes: 22 additions & 4 deletions polaris.shopify.com/src/components/IconGrid/IconGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,26 @@ interface IconGridItemProps {
icon: IconType;
query?: string;
activeIcon?: string;
customOnClick?: Function;
rank?: number;
searchTerm?: string;
}

function IconGridItem({icon, activeIcon, query}: IconGridItemProps) {
function IconGridItem({
icon,
activeIcon,
query,
customOnClick,
rank,
searchTerm,
}: IconGridItemProps) {
const {id, name} = icon;
const searchAttributes = useGlobalSearchResult();

return (
<li key={id}>
<Link
legacyBehavior
href={{
pathname: '/icons',
query: {
Expand All @@ -51,9 +62,16 @@ function IconGridItem({icon, activeIcon, query}: IconGridItemProps) {
id={icon.id}
{...searchAttributes}
>
<SearchResultHighlight />
<Icon source={(polarisIcons as any)[id]} />
<p>{name}</p>
<a
onClick={() =>
customOnClick &&
customOnClick(searchTerm, rank, `/icons?icon=${id}`)
}
>
<SearchResultHighlight />
<Icon source={(polarisIcons as any)[id]} />
<p>{name}</p>
</a>
</Link>
</li>
);
Expand Down
21 changes: 18 additions & 3 deletions polaris.shopify.com/src/components/TokenList/TokenList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,29 @@ function getFigmaUsageForToken(
interface TokenListItemProps {
category: string;
token: TokenPropertiesWithName;
customOnClick?: Function;
searchTerm?: string;
rank?: number;
}

function TokenListItem({
category,
token: {name, value, description},
customOnClick,
searchTerm,
rank,
}: TokenListItemProps) {
const figmaUsage = getFigmaUsageForToken(name, value);
const tokenNameWithPrefix = `--p-${name}`;
const [copy, didJustCopy] = useCopyToClipboard(tokenNameWithPrefix);

const searchAttributes = useGlobalSearchResult();
const isClickableSearchResult = !!searchAttributes?.tabIndex;
const url = `/tokens/${category}#${searchAttributes?.id}`;

const customOnClickHandler = () => {
customOnClick && customOnClick(searchTerm, rank, url);
};

return (
<TokenListContext.Consumer>
Expand All @@ -152,11 +163,12 @@ function TokenListItem({
<TokenPreview name={name} value={value} />
{isClickableSearchResult && (
<Link
href={`/tokens/${category}#${searchAttributes?.id}`}
href={url}
className={styles.ClickableItemLink}
tabIndex={-1}
legacyBehavior
>
View token
<a onClick={customOnClickHandler}>View token</a>
</Link>
)}
</td>
Expand All @@ -177,7 +189,10 @@ function TokenListItem({
)}
>
<button
onClick={copy}
onClick={() => {
copy();
customOnClickHandler();
}}
tabIndex={searchAttributes?.tabIndex}
>
<Icon source={ClipboardMinor} width={14} height={14} />
Expand Down