Skip to content

Commit

Permalink
Swap theme search to iframe search-frame page (#1342)
Browse files Browse the repository at this point in the history
* chore: wip + saving indexing change with new index map

* feat: local search seems to work but indexPrefix error

* feat: iframed search works

* chore(pre-release): publish@4.7.11-search1

* fix: flip pathname check

* chore(pre-release): publish@4.7.11-search2

* fix: indexPrefix undefined, aioSearch missing, link target no links check

* chore(pre-release): publish@4.7.11-search3

* chore: cleanup

* chore: update yarn.lock
  • Loading branch information
dmitrymatio committed Jan 30, 2023
1 parent fea6850 commit 0eb7e75
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 148 deletions.
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

0 comments on commit 0eb7e75

Please sign in to comment.