Skip to content
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

Swap theme search to iframe search-frame page #1342

Merged
merged 11 commits into from
Jan 30, 2023
20 changes: 18 additions & 2 deletions packages/gatsby-theme-aio/algolia/index-records.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ const getImportedContent = require('./helpers/get-imported-content');
const getIFrameContent = require('./helpers/get-iframe-content');
const getOpenApiContent = require('./helpers/get-openapi-content');
const mdxQuery = require('./mdx-query');
const axios = require('axios').default;
const parseHtml = require('./helpers/parse-html');
const parseMdx = require('./helpers/parse-mdx');
const createAlgoliaRecord = require('./create-record');
const { getProductFromIndex } = require('./helpers/get-products-indexes');

function indexRecords() {
return [
Expand All @@ -30,6 +30,22 @@ function indexRecords() {
allFile: { nodes },
},
}) {

let productIndexMap;
try {
const result = await axios.get("https://raw.githubusercontent.com/AdobeDocs/search-indices/main/product-index-map.json");
productIndexMap = result.data;
} catch (error) {
console.error(`AIO: Failed fetching search index.\n${error}`)
process.exit();
}

const productFromPath = productIndexMap.find(prod => {
return prod.productIndices.some(index => {
return index.indexPathPrefix.includes(pathPrefix)
})
});

const markdownFiles = [];
for (const node of nodes) {
markdownFiles.push({
Expand All @@ -51,7 +67,7 @@ function indexRecords() {
objectID: node.id,
openAPISpec: node.childMdx.frontmatter.openAPISpec,
pathPrefix: `${pathPrefix}/`,
product: getProductFromIndex(repository),
product: productFromPath.productName,
size: node.size,
slug: node.childMdx.slug,
title: node.childMdx.frontmatter.title,
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby-theme-aio/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/gatsby-theme-aio",
"version": "4.7.11-rc2",
"version": "4.7.11",
"description": "The Adobe I/O theme for building markdown powered sites",
"main": "index.js",
"license": "Apache-2.0",
Expand Down
205 changes: 178 additions & 27 deletions packages/gatsby-theme-aio/src/components/Layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,65 @@ const updatePageSrc = (type, frontMatter, setIsLoading) => {
}
};

// Used to update the url in the browser
const setQueryStringParameter = (name, value) => {
const params = new URLSearchParams(window.location.search);
params.set(name, value);
window.history.replaceState({}, '', `${window.location.pathname}?${params}`);
};

/**
* @returns The query string from the URL
*/
export const getQueryString = () => {
const params = new URLSearchParams(window.location.search);
return params.toString();
};

const searchIFrameSource = () => {
/**
* Returns expected origin based on the host
* @param {*} host The host
* @param {*} suffix A suffix to append
* @returns The expected origin
*/
const setExpectedOrigin = (host, suffix = '') => {
if (isDevEnvironment(host)) {
return `http://localhost:8000`;
}
else if (isStageEnvironment(host)) {
return `https://developer-stage.adobe.com${suffix}`;
} else {
return `https://developer.adobe.com${suffix}`;
}
};

/**
* Checks whether the current URL is a dev environment based on host value
* @param {*} host The host
* @returns True if the current URL is a dev environment, false otherwise
*/
function isDevEnvironment(host) {
return host.indexOf('localhost') >= 0;
}

/**
* Checks whether the current URL is a stage environment based on host value
* @param {*} host The host
* @returns True if the current URL is a stage environment, false otherwise
*/
function isStageEnvironment(host) {
return host.indexOf('developer-stage') >= 0;
}


const src = isDevEnvironment(window.location.host) ? setExpectedOrigin(window.location.host) : `${setExpectedOrigin(window.location.host, '/search-frame')}`;
const queryString = new URLSearchParams(window.location.search);
return queryString && queryString.toString().length > 0
? `${src}?${queryString.toString()}`
: src;
};

export default ({ children, pageContext, location }) => {
const [ims, setIms] = useState(null);
const [isLoadingIms, setIsLoadingIms] = useState(true);
Expand Down Expand Up @@ -151,24 +210,6 @@ export default ({ children, pageContext, location }) => {
}
}, []);

// Set Search indexAll
useEffect(() => {
if (hasSearch) {
Axios.get("https://raw.githubusercontent.com/AdobeDocs/search-indices/main/product-index-map.json")
.then(result => {
const productIndexMap = result.data;
if (typeof productIndexMap === 'string') {
setIndexAll(JSON.parse(productIndexMap));
} else if (Object.prototype.toString.call(productIndexMap) == '[object Array]') { // https://stackoverflow.com/a/12996879/15028986
setIndexAll(productIndexMap);
}
})
.catch(err => {
console.error(`AIO: Failed fetching search index.\n${err}`)
})
}
}, []);

// Load all data once and pass it to the Provider
const data = useStaticQuery(
graphql`
Expand Down Expand Up @@ -266,6 +307,7 @@ export default ({ children, pageContext, location }) => {
const [showSearch, setShowSearch] = useState(false);
const [showSideNav, setShowSideNav] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [loadSearchFrame, setLoadSearchFrame] = useState(false);

// Show search if search param is set
useEffect(() => {
Expand Down Expand Up @@ -340,7 +382,27 @@ export default ({ children, pageContext, location }) => {
updatePageSrc('openAPI', frontMatter, setIsLoading);
updatePageSrc('frame', frontMatter, setIsLoading);

// Set Search indexAll
useEffect(() => {
if (hasSearch) {
Axios.get("https://raw.githubusercontent.com/AdobeDocs/search-indices/main/product-index-map.json")
.then(result => {
const productIndexMap = result.data;
if (typeof productIndexMap === 'string') {
setIndexAll(JSON.parse(productIndexMap));
} else if (Object.prototype.toString.call(productIndexMap) == '[object Array]') { // https://stackoverflow.com/a/12996879/15028986
setIndexAll(productIndexMap);
}
})
.catch(err => {
console.error(`AIO: Failed fetching search index.\n${err}`)
})
}
}, []);


if (pathPrefix === "/search-frame") {

return (
<>
<Helmet>
Expand Down Expand Up @@ -469,6 +531,89 @@ export default ({ children, pageContext, location }) => {
);
}

let searchPathNameCheck = "";

const searchFrameOnLoad = (counter = 0, loaded) => {
const renderedFrame = document.getElementById('searchIframe');

renderedFrame.contentWindow.postMessage(JSON.stringify({ localPathName: window.location.pathname }), '*');
if (window.location.pathname !== '/') {
if (searchPathNameCheck !== window.location.pathname) {
// attempt to establish connection for 3 seconds then time out
if (counter > 30) {
// eslint-disable-next-line no-console
console.warn('Loading Search iFrame timed out');
return;
}
window.setTimeout(() => { searchFrameOnLoad(renderedFrame, counter + 1, loaded); }, 100);
}
}
// Past this point we successfully passed the local pathname
// and received a confirmation from the iframe
if (!loaded) {
const queryString = getQueryString();
if (queryString) {
// let searchIframeContainer = document.querySelector('div.nav-console-search-frame');
// if (searchIframeContainer.length > 0) {
// searchIframeContainer.style.visibility = 'visible';
// }
setShowSearch(true);
}
}

loaded = true;
};

// Referenced https://stackoverflow.com/a/10444444/15028986
const checkIframeLoaded = () => {
const renderedFrame = document.getElementById('searchIframe');
// Get a handle to the iframe element
let iframeDoc;
try {
iframeDoc = renderedFrame.contentDocument;
// Check if loading is complete
if (iframeDoc.readyState === 'complete') {
renderedFrame.onload = () => {
searchFrameOnLoad();
};
// The loading is complete, call the function we want executed once the iframe is loaded
return;
}
} catch (error) {
window.setTimeout(checkIframeLoaded, 100);
}

};

const onMessageReceivedFromIframe = (evt) => {
// const expectedOrigin = setExpectedOrigin(window.location.host);
// if (evt.origin !== expectedOrigin) return;
try {
const message = typeof evt.data === 'string' ? JSON.parse(evt.data) : evt.data;
if (message.query) {
setQueryStringParameter(SEARCH_PARAMS.query, message.query);
setQueryStringParameter(SEARCH_PARAMS.keywords, message.keywords);
setQueryStringParameter(SEARCH_PARAMS.index, message.index);
} else if (message.received) {
searchPathNameCheck = message.received;
}
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
}
}

useEffect(() => {
window.addEventListener("message", onMessageReceivedFromIframe);
if (hasSearch) {
setLoadSearchFrame(true);
}
}, []);

useEffect(() => {
checkIframeLoaded();
}, [loadSearchFrame])

return (
<>
<Helmet>
Expand Down Expand Up @@ -693,15 +838,21 @@ export default ({ children, pageContext, location }) => {
</div>
</div>

{hasSearch && showSearch && indexAll && (
<Search
algolia={algolia}
indexAll={indexAll}
indexPrefix={algoliaIndexEnv ? algoliaIndexEnv : ""}
showSearch={showSearch}
setShowSearch={setShowSearch}
searchButtonId={searchButtonId}
/>
{hasSearch && loadSearchFrame && (
<iframe
id='searchIframe'
src={searchIFrameSource()}
css={css`position: fixed;
top: var(--spectrum-global-dimension-size-800);
left: 0px;
right: 0px;
bottom: 0px;
background-color: transparent;
z-index: 10;
width: 100%;
height: 100%;
visibility: ${showSearch ? "visible" : "hidden"};`}
></iframe>
)}

<div
Expand Down
36 changes: 15 additions & 21 deletions packages/gatsby-theme-aio/src/components/Search/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const clearQueryStringParameters = () => {
window.history.replaceState({}, '', `${window.location.pathname}`);
};

const searchSuggestions = async (algolia, query, searchIndex, indexAll, existingIndices, setExistingIndices) => {
const searchSuggestions = async (algolia, query, indexPrefix, searchIndex, indexAll, existingIndices, setExistingIndices) => {
const queries = [];
let indexes;
if (!existingIndices.length) {
Expand Down Expand Up @@ -95,7 +95,7 @@ const searchSuggestions = async (algolia, query, searchIndex, indexAll, existing
return await algolia.multipleQueries(queries);
};

const searchIndexes = async (algolia, query, selectedIndex, indexAll, existingIndices, setExistingIndices, keywords) => {
const searchIndexes = async (algolia, query, indexPrefix, selectedIndex, indexAll, existingIndices, setExistingIndices, keywords) => {

let indexes;
if (!existingIndices.length) {
Expand Down Expand Up @@ -230,9 +230,9 @@ const Search = ({ algolia, indexAll, indexPrefix, showSearch, setShowSearch, sea

if (searchQuery !== oldSearchQuery) {
setIsLoading(true);
search = await searchIndexes(algolia, searchQuery, ['all'], indexAll, existingIndices, setExistingIndices, selectedKeywords);
search = await searchIndexes(algolia, searchQuery, indexPrefix, ['all'], indexAll, existingIndices, setExistingIndices, selectedKeywords);
} else {
search = await searchIndexes(algolia, searchQuery, selectedIndex, indexAll, existingIndices, setExistingIndices, selectedKeywords);
search = await searchIndexes(algolia, searchQuery, indexPrefix, selectedIndex, indexAll, existingIndices, setExistingIndices, selectedKeywords);
}

const localProduct = searchIndex.filter((product) => product !== SEARCH_INDEX_ALL)[0];
Expand Down Expand Up @@ -295,7 +295,7 @@ const Search = ({ algolia, indexAll, indexPrefix, showSearch, setShowSearch, sea
if (!localPathName.startsWith('/')) { localPathName = `/${localPathName}` }
if (!localPathName.endsWith('/')) { localPathName = `${localPathName}/` }
const localProduct = indexAll.find(product => product.productIndices.some(idx => {
return idx.indexPathPrefix.startsWith(message.localPathName);
return localPathName.startsWith(idx.indexPathPrefix);
}));

if (localProduct?.productName) {
Expand Down Expand Up @@ -388,7 +388,8 @@ const Search = ({ algolia, indexAll, indexPrefix, showSearch, setShowSearch, sea
if (key === 'Escape') {
setShowSearch(false);
clearQueryStringParameters();
document.getElementById("aio-Search-close").focus()
const searchClose = document.getElementById("aio-Search-close");
searchClose ? searchClose.focus() : "";
}
};

Expand All @@ -405,7 +406,7 @@ const Search = ({ algolia, indexAll, indexPrefix, showSearch, setShowSearch, sea
if (searchQuery.length && !searchResults.length) {
setShowClear(true);

const suggestions = await searchSuggestions(algolia, searchQuery, searchIndex, indexAll, existingIndices, setExistingIndices);
const suggestions = await searchSuggestions(algolia, searchQuery, indexPrefix, searchIndex, indexAll, existingIndices, setExistingIndices);
setSearchQueryCounter(searchQueryCounter + 1);
console.log('Total search queries counted is:', searchQueryCounter);

Expand Down Expand Up @@ -438,22 +439,15 @@ const Search = ({ algolia, indexAll, indexPrefix, showSearch, setShowSearch, sea
useEffect(() => {
if (suggestionsRef) {
if (searchSuggestionResults.length > 0) {
suggestionsRef.current.querySelectorAll("a").forEach(link => {
link.target = "_top";
});
}
}
}, [searchSuggestionResults])

useEffect(() => {
if (searchResultsRef) {
if (searchResults.length > 0) {
searchResultsRef.current.querySelectorAll("a").forEach(link => {
link.target = "_top";
});
const allLinks = suggestionsRef.current.querySelectorAll("a");
if (allLinks.length > 0) {
allLinks.forEach(link => {
link.target = "_top";
});
}
}
}
}, [searchResults])
}, [searchSuggestionResults, searchResults])
}

return (
Expand Down
Loading