From d108be243dee61236478d409f0274df6164cac72 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Thu, 4 Aug 2022 12:01:53 -0700 Subject: [PATCH 01/46] fix(#1999): refactor to use SearchAutocomplete and v3 of AlgoliaSearch API --- packages/dev/docs/package.json | 3 +- packages/dev/docs/src/client.js | 96 ++++++++++++++++++++++- packages/dev/docs/src/docs.css | 23 ++++++ yarn.lock | 134 ++++++++++++++++++++++++++++++-- 4 files changed, 249 insertions(+), 7 deletions(-) diff --git a/packages/dev/docs/package.json b/packages/dev/docs/package.json index 74c9519f1ff..f548ffb476d 100644 --- a/packages/dev/docs/package.json +++ b/packages/dev/docs/package.json @@ -23,9 +23,10 @@ "@spectrum-icons/illustrations": "^3.1.0", "@spectrum-icons/ui": "^3.1.0", "@spectrum-icons/workflow": "^4.0.0", + "algoliasearch": "^4.14.1", "clsx": "^1.1.1", "globals-docs": "^2.4.1", - "highlight.js": "9.18.1", + "highlight.js": "^11.6.0", "markdown-to-jsx": "^6.11.0", "quicklink": "^2.0.0", "react": "^18.0.0", diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index e688d1f88f2..6c1c6d86d72 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -10,8 +10,10 @@ * governing permissions and limitations under the License. */ -import {ActionButton} from '@react-spectrum/button'; +import {ActionButton, defaultTheme, Provider, Text} from '@adobe/react-spectrum'; +import algoliasearch from 'algoliasearch/lite'; import docsStyle from './docs.css'; +import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; import {listen} from 'quicklink'; import React, {useEffect, useRef, useState} from 'react'; import ReactDOM from 'react-dom'; @@ -183,8 +185,100 @@ function Hamburger() { ); } +function DocSearch() { + const client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); + const searchIndex = client.initIndex('react-spectrum'); + const searchOptions = { + highlightPreTag: ``, + highlightPostTag: '' + }; + + const [searchValue, setSearchValue] = useState(''); + const [predictions, setPredictions] = useState(null); + const [suggestions, setSuggestions] = useState(null); + + let updatePredictions = ({hits}) => { + setPredictions(hits); + let sections = []; + hits.forEach(prediction => { + let hierarchy = prediction.hierarchy; + let objectID = prediction.objectID; + let lvl0 = hierarchy.lvl0; + let section = sections.find(section => section.title === lvl0); + if (!section) { + section = {title: lvl0, items: []}; + sections.push(section); + } + let text = []; + let textValue = []; + for (let i = 1; i < 6; i++) { + if (hierarchy[`lvl${i}`]) { + text.push(prediction._highlightResult.hierarchy[`lvl${i}`].value); + textValue.push(hierarchy[`lvl${i}`]); + } + } + section.items.push( + + + { + prediction.content && + + + + } + + ); + }); + let suggestions = sections.map((section, index) =>
{section.items}
); + setSuggestions(suggestions); + }; + + let onInputChange = (query) => { + if (!query && predictions) { + setPredictions(null); + setSuggestions(null); + } + setSearchValue(query); + searchIndex + .search( + query, + searchOptions + ) + .then(updatePredictions); + }; + + let onSubmit = (value, key) => { + if (key) { + let prediction = predictions.find(prediction => key === prediction.objectID); + let url = prediction.url; + if ( + url.includes('https://react-spectrum.adobe.com') && + window.location.hostname === 'localhost') { + url = url.replace('https://react-spectrum.adobe.com', window.location.origin); + } + window.location.href = url; + } + }; + + return ( + + + {suggestions} + + + ); +} + ReactDOM.render(<> + , document.querySelector('.' + docsStyle.pageHeader)); diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index cda7ea35037..91dc6dd6a90 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -608,6 +608,7 @@ h2.sectionHeader { } .pageHeader { + gap: var(--spectrum-global-dimension-size-100); position: fixed; top: 0; display: inline-flex; @@ -622,6 +623,22 @@ h2.sectionHeader { [dir='rtl'] & { left: 0; } + + z-index: 1; +} + +.docSearchBox { + margin-inline-start: auto; + width: var(--spectrum-global-dimension-size-3600); + > div { + width: 100%; + } +} + +.docSearchBoxMark { + font-weight: bold; + background: var(--spectrum-alias-text-highlight-color); + color: var(--spectrum-global-color-blue-700); } /* hamburger menu should be hidden so that it doesn't receive focus before the sidenav */ @@ -706,6 +723,12 @@ h2.sectionHeader { } } +@media (max-width: 569px) { + .docSearchBox { + width: var(--spectrum-global-dimension-size-2400); + } +} + @media (max-width: 1200px) { /* Hide the table of contents */ main { diff --git a/yarn.lock b/yarn.lock index c388fc661e5..89c5d13aff6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -52,6 +52,110 @@ resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.0.0.tgz#913f3a251ef5ea3154929a7a23d4fbd6de1553da" integrity sha512-aJbk7XhsUNefoufMpneq2aIT3VuD++KUY0C08VKbt5p/WOW0aDX8WYavUxW4oiK5cAgsvapcaPaS+eWpBMwXLg== +"@algolia/cache-browser-local-storage@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.1.tgz#a0b85a6c3fe3d5c49fca01b16f00b41bf38a918c" + integrity sha512-BBdibsPn3hLBajc/NRAtHEeoXsw+ziSGR/3bqRNB5puUuwKPQZSE2MaMVWSADnlc3KV3bEj4xsfKOVLJyfJSPQ== + dependencies: + "@algolia/cache-common" "4.14.1" + +"@algolia/cache-common@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.14.1.tgz#11d44a6442f83deb3629a04c20df8408088f6449" + integrity sha512-XhAzm0Sm3D3DuOWUyDoVSXZ/RjYMvI1rbki+QH4ODAVaHDWVhMhg3IJPv3gIbBQnEQdtPdBhsf2hyPxAu28E5w== + +"@algolia/cache-in-memory@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.14.1.tgz#68ede8520f054bc65938209b59962056ae5b56c7" + integrity sha512-fVUu7N1hYb/zZYfV9Krlij70NwS+8bQm5vmDJyfp0+9FjSjz2V7wj1CUxvaY8ZcgoBPj9ehQ8sRuqSM2m5OPww== + dependencies: + "@algolia/cache-common" "4.14.1" + +"@algolia/client-account@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.14.1.tgz#b92e091f698630c49ec7df48816ae75af3cbac40" + integrity sha512-Zm4+PN3bsBPhv1dKKwzBaRGzf0G1JcjjSTpE231L7Z7LsEDcFDW4E6L5ctwMz3SliSBeL/j1ghmaunJrZlkRIg== + dependencies: + "@algolia/client-common" "4.14.1" + "@algolia/client-search" "4.14.1" + "@algolia/transporter" "4.14.1" + +"@algolia/client-analytics@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.14.1.tgz#aca3436775f608a6141cc81899e1d75ef030efa2" + integrity sha512-EhZLR0ezBZx7ZGkwzj7OTvnI8j2Alyv1ByC0Mx48qh3KqRhVwMFm/Uf34zAv4Dum2PTFin41Y4smAvAypth9nQ== + dependencies: + "@algolia/client-common" "4.14.1" + "@algolia/client-search" "4.14.1" + "@algolia/requester-common" "4.14.1" + "@algolia/transporter" "4.14.1" + +"@algolia/client-common@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.14.1.tgz#2709bddf934a3545cd9feecc0591e9285fbed7c2" + integrity sha512-WDwziD7Rt1yCRDfONmeLOfh1Lt8uOy6Vn7dma171KOH9NN3q8yDQpOhPqdFOCz1j3GC1FfIZxaC0YEOIobZ2lg== + dependencies: + "@algolia/requester-common" "4.14.1" + "@algolia/transporter" "4.14.1" + +"@algolia/client-personalization@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.14.1.tgz#58f0b85b8f6d531e13877a099f54513ac2bec154" + integrity sha512-D4eeW7bTi769PWcEYZO+QiKuUXFOC5zK5Iy83Ey6FHqS7m5TXws5MP1rmETE018lTXeYq2NSHWp/F07fRRg0RA== + dependencies: + "@algolia/client-common" "4.14.1" + "@algolia/requester-common" "4.14.1" + "@algolia/transporter" "4.14.1" + +"@algolia/client-search@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.14.1.tgz#44bfc65b3e6939b725f8f97aad725593f2a4ad7f" + integrity sha512-K6XrdIIQq8a3o+kCedj5slUVzA1aKttae4mLzwnY0bS7tYduv1IQggi9Sz8gOG6/MMyKMB4IwYqr47t/0z4Vxw== + dependencies: + "@algolia/client-common" "4.14.1" + "@algolia/requester-common" "4.14.1" + "@algolia/transporter" "4.14.1" + +"@algolia/logger-common@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.14.1.tgz#acbd36b66e3b408f99cacfb581ad3bd28defcc28" + integrity sha512-58CK87wTjUWI1QNXc3nFDQ7EXBi28NoLufXE9sMjng2fAL1wPdyO+KFD8KTBoXOZnJWflPj5F7p6jLyGAfgvcQ== + +"@algolia/logger-console@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.14.1.tgz#7e7d7486d71ccfe38e63234626931083592149d2" + integrity sha512-not+VwH1Dx2B/BaN+4+4+YnGRBJ9lduNz2qbMCTxZ4yFHb+84j4ewHRPBTtEmibn7caVCPybdTKfHLQhimSBLQ== + dependencies: + "@algolia/logger-common" "4.14.1" + +"@algolia/requester-browser-xhr@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.1.tgz#9e683dc0916afae221bf946255a998b06830be78" + integrity sha512-mpH6QsFBbXjTy9+iU86Rcdt9LxS7GA/tWhGMr0+Ap8+4Za5+ELToz0PC7euVeVOcclgGGi7gbjOAmf6k8b10iA== + dependencies: + "@algolia/requester-common" "4.14.1" + +"@algolia/requester-common@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.14.1.tgz#b07ffa00ae0cf61442dcda71a3209051fed130d8" + integrity sha512-EbXBKrfYcX5/JJfaw7IZxhWlbUtjd5Chs+Alrfc4tutgRQn4dmImWS07n3iffwJcYdOWY1eRrnfBK5BwopuN5A== + +"@algolia/requester-node-http@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.14.1.tgz#5e5f4ff55deb5aa0e92f3105d77299de744b1471" + integrity sha512-/sbRqL9P8aVuYUG50BgpCbdJyyCS7fia+sQIx9d1DiGPO7hunwLaEyR4H7JDHc/PLKdVEPygJx3rnbJWix4Btg== + dependencies: + "@algolia/requester-common" "4.14.1" + +"@algolia/transporter@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.14.1.tgz#7eca8568ff710d9d1a7bbd3c1dafbbf44a6143f5" + integrity sha512-xbmoIqszFDOCCZqizBQ2TNHcGtjZX7EkJCzABsrokA0WqtfZzClFmtc+tZYgtEiyAfIF70alTegG19poQGdkvg== + dependencies: + "@algolia/cache-common" "4.14.1" + "@algolia/logger-common" "4.14.1" + "@algolia/requester-common" "4.14.1" + "@babel/cli@^7.12.10": version "7.12.10" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.12.10.tgz#67a1015b1cd505bde1696196febf910c4c339a48" @@ -5786,6 +5890,26 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv json-schema-traverse "^0.4.1" uri-js "^4.2.2" +algoliasearch@^4.14.1: + version "4.14.1" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.14.1.tgz#7f24cabd264f8294b461d108e1603e673571e806" + integrity sha512-ZWqnbsGUgU03/IyG995pMCc+EmNVDA/4c9ntr8B0dWQwFqazOQ4ErvKZxarbgSNmyPo/eZcVsTb0bNplJMttGQ== + dependencies: + "@algolia/cache-browser-local-storage" "4.14.1" + "@algolia/cache-common" "4.14.1" + "@algolia/cache-in-memory" "4.14.1" + "@algolia/client-account" "4.14.1" + "@algolia/client-analytics" "4.14.1" + "@algolia/client-common" "4.14.1" + "@algolia/client-personalization" "4.14.1" + "@algolia/client-search" "4.14.1" + "@algolia/logger-common" "4.14.1" + "@algolia/logger-console" "4.14.1" + "@algolia/requester-browser-xhr" "4.14.1" + "@algolia/requester-common" "4.14.1" + "@algolia/requester-node-http" "4.14.1" + "@algolia/transporter" "4.14.1" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -12324,11 +12448,6 @@ header-case@^1.0.0: no-case "^2.2.0" upper-case "^1.1.3" -highlight.js@9.18.1: - version "9.18.1" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c" - integrity sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg== - highlight.js@^10.1.1: version "10.5.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.5.0.tgz#3f09fede6a865757378f2d9ebdcbc15ba268f98f" @@ -12339,6 +12458,11 @@ highlight.js@^10.4.1: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== +highlight.js@^11.6.0: + version "11.6.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.6.0.tgz#a50e9da05763f1bb0c1322c8f4f755242cff3f5a" + integrity sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw== + highlight.js@~10.4.0: version "10.4.1" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" From 982b77013a0e4a31bb3016375d258250fb8c8ccc Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 5 Aug 2022 13:25:33 -0700 Subject: [PATCH 02/46] fix(#1999): links should work regardless of host --- packages/dev/docs/src/client.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index 6c1c6d86d72..42c7e976d28 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -251,12 +251,7 @@ function DocSearch() { if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); let url = prediction.url; - if ( - url.includes('https://react-spectrum.adobe.com') && - window.location.hostname === 'localhost') { - url = url.replace('https://react-spectrum.adobe.com', window.location.origin); - } - window.location.href = url; + window.location.href = `..${url.replace('https://react-spectrum.adobe.com', '')}`; } }; From 833e3b17a50d3a9c8316fb38e9d62076fef2ee13 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 5 Aug 2022 15:28:36 -0700 Subject: [PATCH 03/46] fix(#1999): links should work regardless of host --- packages/dev/docs/src/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index 42c7e976d28..dbd3332dacb 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -251,7 +251,7 @@ function DocSearch() { if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); let url = prediction.url; - window.location.href = `..${url.replace('https://react-spectrum.adobe.com', '')}`; + window.location.href = `..${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? '/docs' : ''}${url.replace('https://react-spectrum.adobe.com', '')}`; } }; From 36dad294f51ec637b0e2b8bd7f68bdf6229ae212 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 5 Aug 2022 15:39:40 -0700 Subject: [PATCH 04/46] fix(#1999): links should work regardless of host --- packages/dev/docs/src/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index dbd3332dacb..85e7a78d0d7 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -251,7 +251,7 @@ function DocSearch() { if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); let url = prediction.url; - window.location.href = `..${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? '/docs' : ''}${url.replace('https://react-spectrum.adobe.com', '')}`; + window.location.href = `/${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? 'docs' : ''}${url.replace('https://react-spectrum.adobe.com/', '')}`; } }; From b936169d26b82dfb2a1db7dc149d6ebff69a22f8 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 5 Aug 2022 15:47:44 -0700 Subject: [PATCH 05/46] fix(#1999): links should work regardless of host --- packages/dev/docs/src/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index 85e7a78d0d7..3aa718756f3 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -251,7 +251,7 @@ function DocSearch() { if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); let url = prediction.url; - window.location.href = `/${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? 'docs' : ''}${url.replace('https://react-spectrum.adobe.com/', '')}`; + window.location.href = `/${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? 'docs/' : ''}${url.replace('https://react-spectrum.adobe.com/', '')}`; } }; From 9cce7fe4e5f385d265194130c049b5329b3e58e6 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 5 Aug 2022 16:14:05 -0700 Subject: [PATCH 06/46] fix(#1999): links should work regardless of host (again) --- packages/dev/docs/src/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index 3aa718756f3..b741bcc70f4 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -251,7 +251,7 @@ function DocSearch() { if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); let url = prediction.url; - window.location.href = `/${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? 'docs/' : ''}${url.replace('https://react-spectrum.adobe.com/', '')}`; + window.location.href = `${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? window.location.href.replace(/(.+\/docs)(.+)/, '$1') : '/'}${url.replace('https://react-spectrum.adobe.com/', '')}`; } }; From 11f621d7383cc12115266e88e322d7b2c0f5521a Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 5 Aug 2022 16:22:48 -0700 Subject: [PATCH 07/46] fix(#1999): links should work regardless of host (again again) --- packages/dev/docs/src/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index b741bcc70f4..f349d9b65af 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -251,7 +251,7 @@ function DocSearch() { if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); let url = prediction.url; - window.location.href = `${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? window.location.href.replace(/(.+\/docs)(.+)/, '$1') : '/'}${url.replace('https://react-spectrum.adobe.com/', '')}`; + window.location.href = `${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? window.location.href.replace(/(.+\/docs\/)(.+)/, '$1') : '/'}${url.replace('https://react-spectrum.adobe.com/', '')}`; } }; From 21f587b7872f1948d55d8208a4e3174a8947dd3f Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 12 Aug 2022 14:57:16 -0400 Subject: [PATCH 08/46] fix(#1999): sanitize text for ListBox items using DOMPurify --- packages/dev/docs/package.json | 1 + packages/dev/docs/src/client.js | 5 +++-- yarn.lock | 5 +++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/dev/docs/package.json b/packages/dev/docs/package.json index f548ffb476d..211e2d86212 100644 --- a/packages/dev/docs/package.json +++ b/packages/dev/docs/package.json @@ -25,6 +25,7 @@ "@spectrum-icons/workflow": "^4.0.0", "algoliasearch": "^4.14.1", "clsx": "^1.1.1", + "dompurify": "^2.3.10", "globals-docs": "^2.4.1", "highlight.js": "^11.6.0", "markdown-to-jsx": "^6.11.0", diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index f349d9b65af..aec259eb6aa 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -13,6 +13,7 @@ import {ActionButton, defaultTheme, Provider, Text} from '@adobe/react-spectrum'; import algoliasearch from 'algoliasearch/lite'; import docsStyle from './docs.css'; +import DOMPurify from 'dompurify'; import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; import {listen} from 'quicklink'; import React, {useEffect, useRef, useState} from 'react'; @@ -219,11 +220,11 @@ function DocSearch() { } section.items.push( - + { prediction.content && - + } diff --git a/yarn.lock b/yarn.lock index 89c5d13aff6..02d2cc24535 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9616,6 +9616,11 @@ dompurify@2.3.8: resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f" integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw== +dompurify@^2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.10.tgz#901f7390ffe16a91a5a556b94043314cd4850385" + integrity sha512-o7Fg/AgC7p/XpKjf/+RC3Ok6k4St5F7Q6q6+Nnm3p2zGWioAY6dh0CbbuwOhH2UcSzKsdniE/YnE2/92JcsA+g== + domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" From 570f2899b165fdfb3c1eded05fc95dc4d65469b5 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 6 Sep 2022 14:46:20 -0400 Subject: [PATCH 09/46] fox(#1999): remove deprecated placeholder attribute --- packages/dev/docs/src/client.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index aec259eb6aa..b4d4a633b22 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -260,7 +260,6 @@ function DocSearch() { Date: Wed, 7 Sep 2022 10:01:59 -0400 Subject: [PATCH 10/46] fix(#1999): Organize results to better reflect the sitemap --- packages/dev/docs/src/client.js | 35 +++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index b4d4a633b22..3c9a020a471 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -190,10 +190,23 @@ function DocSearch() { const client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); const searchIndex = client.initIndex('react-spectrum'); const searchOptions = { + distinct: 1, highlightPreTag: ``, highlightPostTag: '' }; + const sectionTitles = { + 'react-aria': 'React Aria', + 'react-spectrum': 'React Spectrum', + 'react-stately': 'React Stately', + 'internationalized': 'Internationalized', + 'blog': 'Blog', + 'architecture': 'Architecture', + 'contribute': 'Contribute', + 'releases': 'Releases', + 'support': 'Support' + }; + const [searchValue, setSearchValue] = useState(''); const [predictions, setPredictions] = useState(null); const [suggestions, setSuggestions] = useState(null); @@ -204,10 +217,21 @@ function DocSearch() { hits.forEach(prediction => { let hierarchy = prediction.hierarchy; let objectID = prediction.objectID; - let lvl0 = hierarchy.lvl0; - let section = sections.find(section => section.title === lvl0); + let url = prediction.url; + let sectionTitle; + for (const [path, title] of Object.entries(sectionTitles)) { + let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); + if (url.match(regexp)) { + sectionTitle = title; + break; + } + } + if (!sectionTitle) { + sectionTitle = 'Documentation'; + } + let section = sections.find(section => section.title === sectionTitle); if (!section) { - section = {title: lvl0, items: []}; + section = {title: sectionTitle, items: []}; sections.push(section); } let text = []; @@ -230,7 +254,10 @@ function DocSearch() { ); }); - let suggestions = sections.map((section, index) =>
{section.items}
); + let titles = Object.values(sectionTitles); + sections.forEach((section, index) => console.log(index, section.title, titles.indexOf(section.title))); + sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); + let suggestions = sections.map((section, index) =>
{section.items}
); setSuggestions(suggestions); }; From 38d3ad67c305930b81c7da685ac0e3e6aad57ac5 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Thu, 8 Sep 2022 11:02:24 -0400 Subject: [PATCH 11/46] fix(#1999): use ThemeProvider, remove console.log --- packages/dev/docs/src/client.js | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index 3c9a020a471..cf11b66b61f 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import {ActionButton, defaultTheme, Provider, Text} from '@adobe/react-spectrum'; +import {ActionButton, Text} from '@adobe/react-spectrum'; import algoliasearch from 'algoliasearch/lite'; import docsStyle from './docs.css'; import DOMPurify from 'dompurify'; @@ -19,7 +19,7 @@ import {listen} from 'quicklink'; import React, {useEffect, useRef, useState} from 'react'; import ReactDOM from 'react-dom'; import ShowMenu from '@spectrum-icons/workflow/ShowMenu'; -import {ThemeSwitcher} from './ThemeSwitcher'; +import {ThemeProvider, ThemeSwitcher} from './ThemeSwitcher'; import {watchModals} from '@react-aria/aria-modal-polyfill'; if (process.env.NODE_ENV === 'production') { @@ -255,7 +255,6 @@ function DocSearch() { ); }); let titles = Object.values(sectionTitles); - sections.forEach((section, index) => console.log(index, section.title, titles.indexOf(section.title))); sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); let suggestions = sections.map((section, index) =>
{section.items}
); setSuggestions(suggestions); @@ -284,17 +283,19 @@ function DocSearch() { }; return ( - - - {suggestions} - - + + + + {suggestions} + + + ); } From a53f8496bfec2dfb161dac36866876eb16f491a1 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Thu, 8 Sep 2022 14:16:52 -0400 Subject: [PATCH 12/46] fix(#1999): adjust css so that main scrolls rather than body --- packages/dev/docs/src/client.js | 2 +- packages/dev/docs/src/docs.css | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index cf11b66b61f..8f661ff75f3 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -283,7 +283,7 @@ function DocSearch() { }; return ( - + Date: Fri, 9 Sep 2022 13:47:54 -0400 Subject: [PATCH 13/46] fix(#1999): refactor DocSearch component into separate file --- packages/dev/docs/src/DocSearch.js | 120 ++++++++++++++++++++++++++++ packages/dev/docs/src/client.js | 121 +---------------------------- 2 files changed, 123 insertions(+), 118 deletions(-) create mode 100644 packages/dev/docs/src/DocSearch.js diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js new file mode 100644 index 00000000000..6b74a972ef5 --- /dev/null +++ b/packages/dev/docs/src/DocSearch.js @@ -0,0 +1,120 @@ +import algoliasearch from 'algoliasearch/lite'; +import docsStyle from './docs.css'; +import DOMPurify from 'dompurify'; +import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; +import React, {useState} from 'react'; +import {Text} from '@adobe/react-spectrum'; +import {ThemeProvider} from './ThemeSwitcher'; + +export default function DocSearch() { + const client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); + const searchIndex = client.initIndex('react-spectrum'); + const searchOptions = { + distinct: 1, + highlightPreTag: ``, + highlightPostTag: '' + }; + + const sectionTitles = { + 'react-aria': 'React Aria', + 'react-spectrum': 'React Spectrum', + 'react-stately': 'React Stately', + 'internationalized': 'Internationalized', + 'blog': 'Blog', + 'architecture': 'Architecture', + 'contribute': 'Contribute', + 'releases': 'Releases', + 'support': 'Support' + }; + + const [searchValue, setSearchValue] = useState(''); + const [predictions, setPredictions] = useState(null); + const [suggestions, setSuggestions] = useState(null); + + let updatePredictions = ({hits}) => { + setPredictions(hits); + let sections = []; + hits.forEach(prediction => { + let hierarchy = prediction.hierarchy; + let objectID = prediction.objectID; + let url = prediction.url; + let sectionTitle; + for (const [path, title] of Object.entries(sectionTitles)) { + let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); + if (url.match(regexp)) { + sectionTitle = title; + break; + } + } + if (!sectionTitle) { + sectionTitle = 'Documentation'; + } + let section = sections.find(section => section.title === sectionTitle); + if (!section) { + section = {title: sectionTitle, items: []}; + sections.push(section); + } + let text = []; + let textValue = []; + for (let i = 1; i < 6; i++) { + if (hierarchy[`lvl${i}`]) { + text.push(prediction._highlightResult.hierarchy[`lvl${i}`].value); + textValue.push(hierarchy[`lvl${i}`]); + } + } + section.items.push( + + + { + prediction.content && + + + + } + + ); + }); + let titles = Object.values(sectionTitles); + sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); + let suggestions = sections.map((section, index) =>
{section.items}
); + setSuggestions(suggestions); + }; + + let onInputChange = (query) => { + if (!query && predictions) { + setPredictions(null); + setSuggestions(null); + } + setSearchValue(query); + searchIndex + .search( + query, + searchOptions + ) + .then(updatePredictions); + }; + + let onSubmit = (value, key) => { + if (key) { + let prediction = predictions.find(prediction => key === prediction.objectID); + let url = prediction.url; + window.location.href = `${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? window.location.href.replace(/(.+\/docs\/)(.+)/, '$1') : '/'}${url.replace('https://react-spectrum.adobe.com/', '')}`; + } + }; + + return ( + + + + {suggestions} + + + + ); +} diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index 8f661ff75f3..090a38b0484 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -10,16 +10,14 @@ * governing permissions and limitations under the License. */ -import {ActionButton, Text} from '@adobe/react-spectrum'; -import algoliasearch from 'algoliasearch/lite'; +import {ActionButton} from '@adobe/react-spectrum'; +import DocSearch from './DocSearch'; import docsStyle from './docs.css'; -import DOMPurify from 'dompurify'; -import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; import {listen} from 'quicklink'; import React, {useEffect, useRef, useState} from 'react'; import ReactDOM from 'react-dom'; import ShowMenu from '@spectrum-icons/workflow/ShowMenu'; -import {ThemeProvider, ThemeSwitcher} from './ThemeSwitcher'; +import {ThemeSwitcher} from './ThemeSwitcher'; import {watchModals} from '@react-aria/aria-modal-polyfill'; if (process.env.NODE_ENV === 'production') { @@ -186,119 +184,6 @@ function Hamburger() { ); } -function DocSearch() { - const client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); - const searchIndex = client.initIndex('react-spectrum'); - const searchOptions = { - distinct: 1, - highlightPreTag: ``, - highlightPostTag: '' - }; - - const sectionTitles = { - 'react-aria': 'React Aria', - 'react-spectrum': 'React Spectrum', - 'react-stately': 'React Stately', - 'internationalized': 'Internationalized', - 'blog': 'Blog', - 'architecture': 'Architecture', - 'contribute': 'Contribute', - 'releases': 'Releases', - 'support': 'Support' - }; - - const [searchValue, setSearchValue] = useState(''); - const [predictions, setPredictions] = useState(null); - const [suggestions, setSuggestions] = useState(null); - - let updatePredictions = ({hits}) => { - setPredictions(hits); - let sections = []; - hits.forEach(prediction => { - let hierarchy = prediction.hierarchy; - let objectID = prediction.objectID; - let url = prediction.url; - let sectionTitle; - for (const [path, title] of Object.entries(sectionTitles)) { - let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); - if (url.match(regexp)) { - sectionTitle = title; - break; - } - } - if (!sectionTitle) { - sectionTitle = 'Documentation'; - } - let section = sections.find(section => section.title === sectionTitle); - if (!section) { - section = {title: sectionTitle, items: []}; - sections.push(section); - } - let text = []; - let textValue = []; - for (let i = 1; i < 6; i++) { - if (hierarchy[`lvl${i}`]) { - text.push(prediction._highlightResult.hierarchy[`lvl${i}`].value); - textValue.push(hierarchy[`lvl${i}`]); - } - } - section.items.push( - - - { - prediction.content && - - - - } - - ); - }); - let titles = Object.values(sectionTitles); - sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); - let suggestions = sections.map((section, index) =>
{section.items}
); - setSuggestions(suggestions); - }; - - let onInputChange = (query) => { - if (!query && predictions) { - setPredictions(null); - setSuggestions(null); - } - setSearchValue(query); - searchIndex - .search( - query, - searchOptions - ) - .then(updatePredictions); - }; - - let onSubmit = (value, key) => { - if (key) { - let prediction = predictions.find(prediction => key === prediction.objectID); - let url = prediction.url; - window.location.href = `${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? window.location.href.replace(/(.+\/docs\/)(.+)/, '$1') : '/'}${url.replace('https://react-spectrum.adobe.com/', '')}`; - } - }; - - return ( - - - - {suggestions} - - - - ); -} - ReactDOM.render(<> From ef14ba19df3cf62d3478e020e2d9c438c6e5e9f9 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 9 Sep 2022 13:51:11 -0400 Subject: [PATCH 14/46] fix(#1999): fix scroll bar positioning on Windows and updateToc --- packages/dev/docs/src/attachToToC.js | 5 +++-- packages/dev/docs/src/docs.css | 10 +--------- packages/dev/docs/src/toc.css | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/dev/docs/src/attachToToC.js b/packages/dev/docs/src/attachToToC.js index a53947b67a6..1b0065c6a9f 100644 --- a/packages/dev/docs/src/attachToToC.js +++ b/packages/dev/docs/src/attachToToC.js @@ -13,6 +13,7 @@ import sideNavStyles from '@adobe/spectrum-css-temp/components/sidenav/vars.css'; export function attachToToC() { + let main = document.getElementsByTagName('main')[0]; let tocLinks = document.querySelectorAll('#toc a'); let headers = []; for (let link of tocLinks) { @@ -26,7 +27,7 @@ export function attachToToC() { // this needs to be improved a little but the math hurts my head right now // right now it's impossible to select the last section if the last two heights combined are smaller than the viewport height headers.some((header, i) => { - if ((header.header.offsetTop + header.header.getBoundingClientRect().height) > document.documentElement.scrollTop) { + if ((header.header.offsetTop + header.header.getBoundingClientRect().height) > main.scrollTop + main.offsetTop) { let currentSelection = document.querySelectorAll(`#toc .${sideNavStyles['is-selected']}`); if (currentSelection) { currentSelection.forEach(node => { @@ -47,5 +48,5 @@ export function attachToToC() { updateToc(); - document.addEventListener('scroll', updateToc); + main.addEventListener('scroll', updateToc); } diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index e97d7fd9e38..7a4eb0ea6b4 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -295,8 +295,7 @@ html, body { main { margin-top: var(--spectrum-global-dimension-size-550); margin-left: 256px; - margin-right: 180px; - padding: 0 52px; + padding: 0 244px 0 52px; box-sizing: border-box; display: flex; height: calc(100vh - var(--spectrum-global-dimension-size-550)); @@ -738,13 +737,6 @@ h2.sectionHeader { } } -@media (max-width: 1200px) { - /* Hide the table of contents */ - main { - margin-right: 0; - } -} - @define-mixin small-propTable { /* Collapse the prop table into a list with key/value pairs */ .propTable { diff --git a/packages/dev/docs/src/toc.css b/packages/dev/docs/src/toc.css index 522cb9dc7f1..ae01c634a5c 100644 --- a/packages/dev/docs/src/toc.css +++ b/packages/dev/docs/src/toc.css @@ -13,7 +13,7 @@ .toc { position: fixed; top: 80px; - right: 8px; + right: 20px; overflow-y: auto; max-height: calc(100vh - 80px); } From 29c3c150c4b9eb24dd032fc74b2760287d6ff9ef Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 12 Sep 2022 17:55:54 -0700 Subject: [PATCH 15/46] More minimal layout changes --- packages/dev/docs/src/docs.css | 43 +++++++++++++++++++++++++++------- packages/dev/docs/src/toc.css | 2 +- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index 7a4eb0ea6b4..5ef73fa1575 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -71,9 +71,6 @@ html, body { margin: 0; text-size-adjust: none; - height: 100vh; - overflow-y: hidden; - overflow-anchor: none; } .provider { @@ -293,15 +290,14 @@ html, body { } main { - margin-top: var(--spectrum-global-dimension-size-550); margin-left: 256px; - padding: 0 244px 0 52px; + margin-right: 180px; + padding: 40px 52px 0 52px; box-sizing: border-box; display: flex; - height: calc(100vh - var(--spectrum-global-dimension-size-550)); + min-height: 100vh; flex-direction: column; align-items: center; - overflow-y: auto; } .article { @@ -722,6 +718,26 @@ h2.sectionHeader { content: ' • '; } +/* Show line under page header and push main down to make room for search. */ +@media (max-width: 1780px) { + html, body { + scroll-padding-top: var(--spectrum-global-dimension-size-600); + } + + main { + margin-top: 48px; + } + + .pageHeader { + border-bottom: 1px solid var(--spectrum-global-color-gray-200); + background: var(--page-background); + z-index: 50; + display: flex; + justify-content: flex-end; + left: 256px; + } +} + @media (max-width: 750px) { .float { float: none; @@ -731,6 +747,13 @@ h2.sectionHeader { } } +@media (max-width: 1200px) { + /* Hide the table of contents */ + main { + margin-right: 0; + } +} + @media (max-width: 569px) { .docSearchBox { width: var(--spectrum-global-dimension-size-2400); @@ -846,8 +869,8 @@ h2.sectionHeader { main { margin-left: 0; - margin-top: var(--spectrum-global-dimension-size-550); - padding: var(--spectrum-global-dimension-size-325) var(--spectrum-global-dimension-size-250); + margin-top: 48px; + padding: 40px 24px; } /* Show the toolbar at the top with the hamburger and theme switcher buttons */ @@ -856,6 +879,8 @@ h2.sectionHeader { background: var(--page-background); z-index: 50; display: flex; + justify-content: space-between; + left: 0; width: 100%; } diff --git a/packages/dev/docs/src/toc.css b/packages/dev/docs/src/toc.css index ae01c634a5c..522cb9dc7f1 100644 --- a/packages/dev/docs/src/toc.css +++ b/packages/dev/docs/src/toc.css @@ -13,7 +13,7 @@ .toc { position: fixed; top: 80px; - right: 20px; + right: 8px; overflow-y: auto; max-height: calc(100vh - 80px); } From aee0c664155f8c80581789e51c6a743187fdc063 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 20 Sep 2022 16:29:43 -0400 Subject: [PATCH 16/46] fix(#1999): add "Search by Algolia" logo To be in right relations with the DocSearch TOS, https://docsearch.algolia.com/docs/DocSearch-program#how-much-does-it-cost, we should display the "Search by Algolia" logo. --- packages/dev/docs/src/DocSearch.js | 42 ++++++++++++++++++++++++++--- packages/dev/docs/src/docs.css | 43 ++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 6b74a972ef5..e3d62aa9403 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -2,8 +2,9 @@ import algoliasearch from 'algoliasearch/lite'; import docsStyle from './docs.css'; import DOMPurify from 'dompurify'; import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; -import React, {useState} from 'react'; -import {Text} from '@adobe/react-spectrum'; +import React, {useRef, useState} from 'react'; +import * as ReactDOM from 'react-dom/client'; +import {Text, VisuallyHidden} from '@adobe/react-spectrum'; import {ThemeProvider} from './ThemeSwitcher'; export default function DocSearch() { @@ -102,19 +103,54 @@ export default function DocSearch() { } }; + + const searchAutocompleteRef = useRef(); + let logoFragment = document.createElement('div'); + logoFragment.className = docsStyle.docSearchFooter; + let logoRoot = ReactDOM.createRoot(logoFragment); + logoRoot.render(AlgoliaSearchLogo); + + let onOpenChange = (isOpen) => { + if (isOpen) { + requestAnimationFrame(() => { + const listboxes = document.querySelectorAll('[role="listbox"]'); + const listbox = listboxes[listboxes.length - 1]; + if (logoFragment.parentElement !== listbox.parentElement) { + listbox.parentElement.insertBefore(logoFragment, listbox.nextElementSibling); + } + }); + } else { + logoFragment.remove(); + } + }; + return ( + onSubmit={onSubmit} + onOpenChange={onOpenChange}> {suggestions} ); } + +const AlgoliaSearchLogo = ( + +); diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index 5ef73fa1575..924cbb7f27c 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -92,6 +92,7 @@ html, body { --anatomy-gray-100: #f4f6fc; --anatomy-gray-75: #FDFDFE; --anatomy-gray-50: #FFFFFF; + --docsearch-logo-color: #5468FF; } .dark { @@ -109,6 +110,7 @@ html, body { --anatomy-gray-100: #141e35; --anatomy-gray-75: #0e1525; --anatomy-gray-50: #0c1220; + --docsearch-logo-color: #fff; .example { background-color: var(--spectrum-global-color-gray-100); } @@ -645,6 +647,47 @@ h2.sectionHeader { color: var(--spectrum-global-color-blue-700); } +.docSearchFooter { + align-items: center; + display: flex; + flex-direction: row-reverse; + flex-shrink: 0; + justify-content: space-between; + + border-top-color: var(--spectrum-global-color-gray-300); + border-top-style: solid; + border-top-width: var(--spectrum-alias-border-size-thick); + margin-left: var(--spectrum-global-dimension-size-150); + margin-right: var(--spectrum-global-dimension-size-150); + margin-top: var(--spectrum-global-dimension-size-40); +} + +.docSearchLogo { + padding-top: var(--spectrum-global-dimension-size-75); + padding-bottom: var(--spectrum-global-dimension-size-75); +} + +.docSearchLogo a { + color: var(--spectrum-global-color-gray-700); + display: flex; + text-decoration: none; +} + +.docSearchLogoLabel { + font-size: var(--spectrum-global-dimension-font-size-75); + line-height: 1.6em; +} + +.docSearchLogo a:hover .docSearchLogoLabel, +.docSearchLogo a:focus .docSearchLogoLabel { + text-decoration: underline; +} + +.docSearchLogo svg { + color: var(--docsearch-logo-color); + margin-left: var(--spectrum-global-dimension-size-100); +} + /* hamburger menu should be hidden so that it doesn't receive focus before the sidenav */ .hamburgerButton { display:none; From 6f1deae38c815c3e8ff54fef9ac572c7b0f89e0b Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 20 Sep 2022 20:20:08 -0400 Subject: [PATCH 17/46] fix(#1999): improve vertical alignment of "Search by Algolia" text in tray --- packages/dev/docs/src/docs.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index 924cbb7f27c..069e4cd3c7b 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -675,7 +675,11 @@ h2.sectionHeader { .docSearchLogoLabel { font-size: var(--spectrum-global-dimension-font-size-75); - line-height: 1.6em; + line-height: 1.6; +} + +[role="dialog"] .docSearchLogoLabel { + line-height: 1.2; } .docSearchLogo a:hover .docSearchLogoLabel, From bc059fd3e368925ad622170169365925c11676a1 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Wed, 21 Sep 2022 09:53:22 -0400 Subject: [PATCH 18/46] fix(#1999): simplify vertical alignment of "Search by Algolia" text --- packages/dev/docs/src/docs.css | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index 069e4cd3c7b..aa540f60833 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -670,16 +670,12 @@ h2.sectionHeader { .docSearchLogo a { color: var(--spectrum-global-color-gray-700); display: flex; + align-items: center; text-decoration: none; } .docSearchLogoLabel { font-size: var(--spectrum-global-dimension-font-size-75); - line-height: 1.6; -} - -[role="dialog"] .docSearchLogoLabel { - line-height: 1.2; } .docSearchLogo a:hover .docSearchLogoLabel, From 67b389878a8c87bf076598b3f2f9a972134a6426 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Mon, 21 Nov 2022 11:38:53 -0500 Subject: [PATCH 19/46] fix(#1999): Fix rendering of SearchAutocomplete "No results" option item --- .../@react-spectrum/autocomplete/src/SearchAutocomplete.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx b/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx index 026fec5150a..549dc4f62e2 100644 --- a/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx +++ b/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx @@ -165,7 +165,7 @@ function _SearchAutocompleteBase(props: SpectrumSearchAutocomp isLoading={loadingState === 'loadingMore'} onLoadMore={onLoadMore} renderEmptyState={() => isAsync && ( - + {stringFormatter.format('noResults')} )} /> From abfe118bee9f3986cc07b0879cf16a8809ffcb25 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Mon, 21 Nov 2022 11:43:53 -0500 Subject: [PATCH 20/46] fix(#1999): Prevent AlgoliaSearch logo from rendering twice in listbox overlay --- packages/dev/docs/src/DocSearch.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index e3d62aa9403..26b801bb48b 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -13,7 +13,8 @@ export default function DocSearch() { const searchOptions = { distinct: 1, highlightPreTag: ``, - highlightPostTag: '' + highlightPostTag: '', + hitsPerPage: 20 }; const sectionTitles = { @@ -103,19 +104,17 @@ export default function DocSearch() { } }; - const searchAutocompleteRef = useRef(); - let logoFragment = document.createElement('div'); + const logoFragment = document.createElement('div'); logoFragment.className = docsStyle.docSearchFooter; - let logoRoot = ReactDOM.createRoot(logoFragment); + const logoRoot = ReactDOM.createRoot(logoFragment); logoRoot.render(AlgoliaSearchLogo); let onOpenChange = (isOpen) => { if (isOpen) { requestAnimationFrame(() => { - const listboxes = document.querySelectorAll('[role="listbox"]'); - const listbox = listboxes[listboxes.length - 1]; - if (logoFragment.parentElement !== listbox.parentElement) { + const listbox = document.querySelector('[role="listbox"]'); + if (listbox.nextElementSibling.innerHTML !== logoFragment.innerHTML) { listbox.parentElement.insertBefore(logoFragment, listbox.nextElementSibling); } }); From 302dadccd3c6b2a2ebe5d40f93e8c7ab4905305a Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 22 Nov 2022 20:26:08 -0500 Subject: [PATCH 21/46] fix(#1999): refine display of search results --- packages/dev/docs/src/DocSearch.js | 201 ++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 4 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 26b801bb48b..6a0fed0bc0b 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -1,17 +1,42 @@ import algoliasearch from 'algoliasearch/lite'; +import {createRoot} from 'react-dom/client'; import docsStyle from './docs.css'; import DOMPurify from 'dompurify'; import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; +import Link from '@spectrum-icons/workflow/Link'; +import News from '@spectrum-icons/workflow/News'; import React, {useRef, useState} from 'react'; -import * as ReactDOM from 'react-dom/client'; import {Text, VisuallyHidden} from '@adobe/react-spectrum'; import {ThemeProvider} from './ThemeSwitcher'; +import WebPage from '@spectrum-icons/workflow/WebPage'; export default function DocSearch() { const client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); const searchIndex = client.initIndex('react-spectrum'); const searchOptions = { distinct: 1, + attributesToRetrieve: [ + 'hierarchy.lvl0', + 'hierarchy.lvl1', + 'hierarchy.lvl2', + 'hierarchy.lvl3', + 'hierarchy.lvl4', + 'hierarchy.lvl5', + 'hierarchy.lvl6', + 'content', + 'type', + 'url' + ], + attributesToSnippet: [ + 'hierarchy.lvl1:10', + 'hierarchy.lvl2:10', + 'hierarchy.lvl3:10', + 'hierarchy.lvl4:10', + 'hierarchy.lvl5:10', + 'hierarchy.lvl6:10', + 'content:10' + ], + snippetEllipsisText: '…', highlightPreTag: ``, highlightPostTag: '', hitsPerPage: 20 @@ -29,13 +54,125 @@ export default function DocSearch() { 'support': 'Support' }; + function sectionTitlePredicate(hit) { + let sectionTitle; + for (const [path, title] of Object.entries(sectionTitles)) { + let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); + if (hit.url.match(regexp)) { + sectionTitle = title; + break; + } + } + if (!sectionTitle) { + sectionTitle = 'Documentation'; + } + return sectionTitle; + } + const [searchValue, setSearchValue] = useState(''); + const [loadingState, setLoadingState] = useState(null); const [predictions, setPredictions] = useState(null); const [suggestions, setSuggestions] = useState(null); let updatePredictions = ({hits}) => { setPredictions(hits); + + const groupedBySection = groupBy(hits, (hit) => sectionTitlePredicate(hit)); let sections = []; + for (const [title, hits] of Object.entries(groupedBySection)) { + const items = Object.values( + groupBy(hits, (hit) => hit.hierarchy.lvl1) + ) + .reverse() + .map( + groupedHits => + groupedHits.map((hit) => { + const hierarchy = hit.hierarchy; + const objectID = hit.objectID; + + return ( + + { + hierarchy[hit.type] && + hit.type === 'lvl1' && ( + <> + { + title === 'Blog' || title === 'Releases' ? + : + + } + + + + {hit.content && ( + + + + )} + + ) + } + + { + hierarchy[hit.type] && + ( + hit.type === 'lvl2' || + hit.type === 'lvl3' || + hit.type === 'lvl4' || + hit.type === 'lvl5' || + hit.type === 'lvl6' + ) && ( + <> + + + + + + + + + ) + } + + { + hit.type === 'content' && ( + <> + + + + + + + + + ) + } + + ); + } + )); + + sections.push({title, items}); + } + /* hits.forEach(prediction => { let hierarchy = prediction.hierarchy; let objectID = prediction.objectID; @@ -76,18 +213,23 @@ export default function DocSearch() { ); }); + */ let titles = Object.values(sectionTitles); sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); let suggestions = sections.map((section, index) =>
{section.items}
); setSuggestions(suggestions); + setLoadingState(null); }; let onInputChange = (query) => { + setSearchValue(query); if (!query && predictions) { setPredictions(null); setSuggestions(null); + setLoadingState(null); + return; } - setSearchValue(query); + setLoadingState('loading'); searchIndex .search( query, @@ -107,14 +249,16 @@ export default function DocSearch() { const searchAutocompleteRef = useRef(); const logoFragment = document.createElement('div'); logoFragment.className = docsStyle.docSearchFooter; - const logoRoot = ReactDOM.createRoot(logoFragment); + const logoRoot = createRoot(logoFragment); logoRoot.render(AlgoliaSearchLogo); let onOpenChange = (isOpen) => { if (isOpen) { requestAnimationFrame(() => { const listbox = document.querySelector('[role="listbox"]'); - if (listbox.nextElementSibling.innerHTML !== logoFragment.innerHTML) { + if (listbox && + listbox.nextElementSibling && + listbox.nextElementSibling.innerHTML !== logoFragment.innerHTML) { listbox.parentElement.insertBefore(logoFragment, listbox.nextElementSibling); } }); @@ -131,6 +275,7 @@ export default function DocSearch() { aria-label="Search" UNSAFE_className={docsStyle.docSearchBox} id="algolia-doc-search" + loadingState={loadingState} value={searchValue} onInputChange={onInputChange} onSubmit={onSubmit} @@ -153,3 +298,51 @@ const AlgoliaSearchLogo = ( ); + +function groupBy(values, predicate = (value) => value) { + return values.reduce((accumulator, item) => { + const key = predicate(item); + + if (!Object.prototype.hasOwnProperty.call(accumulator, key)) { + accumulator[key] = []; + } + + // We limit each section to show 20 hits maximum. + // This acts as a frontend alternative to `distinct`. + if (accumulator[key].length < 21) { + accumulator[key].push(item); + } + + return accumulator; + }, {}); +} + +function getPropertyByPath(object, path) { + const parts = path.split('.'); + + return parts.reduce((prev, current) => { + if (prev?.[current]) { + return prev[current]; + } + return null; + }, object); +} + +function Snippet({ + hit, + attribute, + tagName = 'span', + ...rest +}) { + return React.createElement(tagName, { + ...rest, + dangerouslySetInnerHTML: { + __html: + DOMPurify.sanitize( + getPropertyByPath(hit, `_snippetResult.${attribute}.value`) || + getPropertyByPath(hit, attribute) + ) + } + }); +} + From 9b2b7e503b5552193eb14af6887f060646875489 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Wed, 23 Nov 2022 14:26:13 -0500 Subject: [PATCH 22/46] fix(#1999): add indentation to express hierarchy - clean up commented code - use a # icon for "in page link" rather than the chain link icon --- packages/dev/docs/src/DocSearch.js | 74 +++++++++++------------------- packages/dev/docs/src/docs.css | 4 ++ 2 files changed, 31 insertions(+), 47 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 6a0fed0bc0b..7af7db14f21 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -1,14 +1,14 @@ import algoliasearch from 'algoliasearch/lite'; import {createRoot} from 'react-dom/client'; import docsStyle from './docs.css'; +import DocumentOutline from '@spectrum-icons/workflow/DocumentOutline'; import DOMPurify from 'dompurify'; +import {Icon} from '@react-spectrum/icon'; import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; -import Link from '@spectrum-icons/workflow/Link'; import News from '@spectrum-icons/workflow/News'; import React, {useRef, useState} from 'react'; import {Text, VisuallyHidden} from '@adobe/react-spectrum'; import {ThemeProvider} from './ThemeSwitcher'; -import WebPage from '@spectrum-icons/workflow/WebPage'; export default function DocSearch() { const client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); @@ -89,6 +89,13 @@ export default function DocSearch() { groupedHits.map((hit) => { const hierarchy = hit.hierarchy; const objectID = hit.objectID; + const docsearchParent = hit.type !== 'lvl1' && + groupedHits.find( + (siblingItem) => + siblingItem.type === 'lvl1' && + siblingItem.hierarchy.lvl1 === + hit.hierarchy.lvl1 + ); return ( @@ -99,7 +106,7 @@ export default function DocSearch() { { title === 'Blog' || title === 'Releases' ? : - + } - + - + { - let hierarchy = prediction.hierarchy; - let objectID = prediction.objectID; - let url = prediction.url; - let sectionTitle; - for (const [path, title] of Object.entries(sectionTitles)) { - let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); - if (url.match(regexp)) { - sectionTitle = title; - break; - } - } - if (!sectionTitle) { - sectionTitle = 'Documentation'; - } - let section = sections.find(section => section.title === sectionTitle); - if (!section) { - section = {title: sectionTitle, items: []}; - sections.push(section); - } - let text = []; - let textValue = []; - for (let i = 1; i < 6; i++) { - if (hierarchy[`lvl${i}`]) { - text.push(prediction._highlightResult.hierarchy[`lvl${i}`].value); - textValue.push(hierarchy[`lvl${i}`]); - } - } - section.items.push( - - - { - prediction.content && - - - - } - - ); - }); - */ let titles = Object.values(sectionTitles); sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); let suggestions = sections.map((section, index) =>
{section.items}
); @@ -287,6 +256,16 @@ export default function DocSearch() { ); } +function Hash(props) { + return ( + + + + + + ); +} + const AlgoliaSearchLogo = (
@@ -346,3 +325,4 @@ function Snippet({ }); } + diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index 98df3ba7a37..178094fe5c5 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -648,6 +648,10 @@ h2.sectionHeader { color: var(--spectrum-global-color-blue-700); } +.docSearchItemIndent { + margin-inline-start: var(--spectrum-global-dimension-size-300); +} + .docSearchFooter { align-items: center; display: flex; From d5aed9e2838b40d54ba7e7ba7a7a77f8fec212c6 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Wed, 23 Nov 2022 15:21:33 -0500 Subject: [PATCH 23/46] fix(#1999): fix search box width --- packages/dev/docs/src/docs.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index 178094fe5c5..02a1e8dedb2 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -636,7 +636,7 @@ h2.sectionHeader { .docSearchBox { margin-inline-start: auto; - width: var(--spectrum-global-dimension-size-3600); + width: var(--spectrum-global-dimension-size-3000) !important; > div { width: 100%; } @@ -649,7 +649,7 @@ h2.sectionHeader { } .docSearchItemIndent { - margin-inline-start: var(--spectrum-global-dimension-size-300); + margin-inline-start: var(--spectrum-global-dimension-size-325); } .docSearchFooter { From 879a5c0efc387c9c233471290d7fc80d2651ce4a Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 29 Nov 2022 11:26:02 -0500 Subject: [PATCH 24/46] fix(#1999): fix search autocomplete width for mobile improve css selector specificity so that autocomplete width is inherited correctly --- packages/dev/docs/src/docs.css | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index 02a1e8dedb2..a1bca266e68 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -632,13 +632,13 @@ h2.sectionHeader { .docSearchBoxThemeProvider { height: var(--spectrum-global-dimension-size-400); -} -.docSearchBox { - margin-inline-start: auto; - width: var(--spectrum-global-dimension-size-3000) !important; - > div { - width: 100%; + .docSearchBox { + margin-inline-start: auto; + width: var(--spectrum-global-dimension-size-3000); + > div { + width: 100%; + } } } @@ -802,9 +802,11 @@ h2.sectionHeader { } } -@media (max-width: 569px) { - .docSearchBox { - width: var(--spectrum-global-dimension-size-2400); +@media (max-width: 375px) { + .docSearchBoxThemeProvider { + .docSearchBox { + width: var(--spectrum-global-dimension-size-2400); + } } } From 151c95cb4092d0ae41cf5abe240a85fd79b4e787 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 29 Nov 2022 11:27:11 -0500 Subject: [PATCH 25/46] fix(#1999): fix querySelector for the themeSwitcher after adding search --- packages/dev/docs/src/client.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dev/docs/src/client.js b/packages/dev/docs/src/client.js index 98ff8ff4a3f..ccdd8ae688f 100644 --- a/packages/dev/docs/src/client.js +++ b/packages/dev/docs/src/client.js @@ -69,9 +69,9 @@ function Hamburger() { let hamburgerButtonRef = useRef(null); let onPress = (event) => { - let nav = document.querySelector('.' + docsStyle.nav); + let nav = document.querySelector(`.${docsStyle.nav}`); let main = document.querySelector('main'); - let themeSwitcher = event.target.parentElement.nextElementSibling; + let themeSwitcher = document.querySelector(`header.${docsStyle.pageHeader} > div:last-of-type`); nav.classList.toggle(docsStyle.visible); @@ -93,10 +93,10 @@ function Hamburger() { useEffect(() => { let mediaQueryList = window.matchMedia('(max-width: 1020px)'); - let nav = document.querySelector('.' + docsStyle.nav); + let nav = document.querySelector(`.${docsStyle.nav}`); let main = document.querySelector('main'); let hamburgerButton = hamburgerButtonRef.current; - let themeSwitcher = hamburgerRef.current.nextElementSibling; + let themeSwitcher = document.querySelector(`header.${docsStyle.pageHeader} > div:last-of-type`); let removeVisible = (isNotResponsive = false) => { setIsPressed(false); From 9aa2dc48a193fc1f19e033d1ea0f7475a990b109 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 29 Nov 2022 18:37:17 -0500 Subject: [PATCH 26/46] fix(#1999): fix search autocomplete width for mobile --- packages/dev/docs/src/docs.css | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index a1bca266e68..512a8d8bad7 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -635,8 +635,21 @@ h2.sectionHeader { .docSearchBox { margin-inline-start: auto; - width: var(--spectrum-global-dimension-size-3000); - > div { + max-width: var(--spectrum-global-dimension-size-3000); + width: calc( + 100vw + - 2 * var(--spectrum-actionbutton-min-width, var(--spectrum-global-dimension-size-400)) + - 4 * var(--spectrum-alias-border-size-thin) + - 4 * var(--spectrum-global-dimension-size-100) + ); + min-width: calc( + var(--spectrum-global-dimension-size-225) + + 2 * var(--spectrum-global-dimension-size-150) + + 2 * var(--spectrum-alias-border-size-thin) + ); + > div, + > div > div, + > div > div, div { width: 100%; } } @@ -805,8 +818,16 @@ h2.sectionHeader { @media (max-width: 375px) { .docSearchBoxThemeProvider { .docSearchBox { - width: var(--spectrum-global-dimension-size-2400); + max-width: unset; + > div, + > div > div, + > div > div > div { + min-width: unset; + padding-left: unset; + padding-right: unset; + } } + } } From 61c721c72d5c9570f39a4a82356da6905080b1bf Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 29 Nov 2022 12:33:44 -0500 Subject: [PATCH 27/46] fix(#3800): MobileSearchAutocomplete: focused style persists on blur --- .../autocomplete/src/MobileSearchAutocomplete.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx b/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx index 19057471192..f16e086f971 100644 --- a/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx +++ b/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx @@ -227,7 +227,7 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut return (
Date: Wed, 30 Nov 2022 10:48:47 -0500 Subject: [PATCH 28/46] fix(#1999): fix clear button position --- packages/dev/docs/src/docs.css | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index 512a8d8bad7..f5a2fb5fb23 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -647,11 +647,6 @@ h2.sectionHeader { + 2 * var(--spectrum-global-dimension-size-150) + 2 * var(--spectrum-alias-border-size-thin) ); - > div, - > div > div, - > div > div, div { - width: 100%; - } } } @@ -820,14 +815,16 @@ h2.sectionHeader { .docSearchBox { max-width: unset; > div, - > div > div, - > div > div > div { - min-width: unset; - padding-left: unset; - padding-right: unset; + > div > div { + min-width: calc( + var(--spectrum-global-dimension-size-150) + + var(--spectrum-global-dimension-size-225) + + var(--spectrum-global-dimension-size-65) + + var(--spectrum-global-dimension-size-350) + ); + width: 100%; } } - } } From 48900ee2c70cd0dec8b34cc1dd116e3ad4bdeb78 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Thu, 23 Feb 2023 09:08:14 -0800 Subject: [PATCH 29/46] Some fixes --- .../src/MobileSearchAutocomplete.tsx | 6 +- .../autocomplete/src/SearchAutocomplete.tsx | 5 + packages/dev/docs/package.json | 2 +- packages/dev/docs/src/DocSearch.js | 122 +++++++++--------- 4 files changed, 73 insertions(+), 62 deletions(-) diff --git a/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx b/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx index f16e086f971..f3a5c33bdd9 100644 --- a/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx +++ b/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx @@ -227,7 +227,7 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut return (
diff --git a/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx b/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx index 5065843a75d..d4d19ff25c1 100644 --- a/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx +++ b/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx @@ -225,6 +225,10 @@ function _SearchAutocompleteInput(props: SearchAutocompleteInputProps, ref classNames( styles, 'spectrum-InputGroup-input-circleLoader' + ), + classNames( + searchStyles, + 'spectrum-Search-circleLoader' ) )} /> ); @@ -307,6 +311,7 @@ function _SearchAutocompleteInput(props: SearchAutocompleteInputProps, ref classNames( searchStyles, 'spectrum-Search', + 'spectrum-Search--loadable', 'spectrum-Textfield', { 'is-disabled': isDisabled, diff --git a/packages/dev/docs/package.json b/packages/dev/docs/package.json index 211e2d86212..654f8f044c9 100644 --- a/packages/dev/docs/package.json +++ b/packages/dev/docs/package.json @@ -27,7 +27,7 @@ "clsx": "^1.1.1", "dompurify": "^2.3.10", "globals-docs": "^2.4.1", - "highlight.js": "^11.6.0", + "highlight.js": "9.18.1", "markdown-to-jsx": "^6.11.0", "quicklink": "^2.0.0", "react": "^18.0.0", diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 7af7db14f21..685d72d48bf 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -10,65 +10,66 @@ import React, {useRef, useState} from 'react'; import {Text, VisuallyHidden} from '@adobe/react-spectrum'; import {ThemeProvider} from './ThemeSwitcher'; -export default function DocSearch() { - const client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); - const searchIndex = client.initIndex('react-spectrum'); - const searchOptions = { - distinct: 1, - attributesToRetrieve: [ - 'hierarchy.lvl0', - 'hierarchy.lvl1', - 'hierarchy.lvl2', - 'hierarchy.lvl3', - 'hierarchy.lvl4', - 'hierarchy.lvl5', - 'hierarchy.lvl6', - 'content', - 'type', - 'url' - ], - attributesToSnippet: [ - 'hierarchy.lvl1:10', - 'hierarchy.lvl2:10', - 'hierarchy.lvl3:10', - 'hierarchy.lvl4:10', - 'hierarchy.lvl5:10', - 'hierarchy.lvl6:10', - 'content:10' - ], - snippetEllipsisText: '…', - highlightPreTag: ``, - highlightPostTag: '', - hitsPerPage: 20 - }; +let client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); +let searchIndex = client.initIndex('react-spectrum'); - const sectionTitles = { - 'react-aria': 'React Aria', - 'react-spectrum': 'React Spectrum', - 'react-stately': 'React Stately', - 'internationalized': 'Internationalized', - 'blog': 'Blog', - 'architecture': 'Architecture', - 'contribute': 'Contribute', - 'releases': 'Releases', - 'support': 'Support' - }; +const searchOptions = { + distinct: 1, + attributesToRetrieve: [ + 'hierarchy.lvl0', + 'hierarchy.lvl1', + 'hierarchy.lvl2', + 'hierarchy.lvl3', + 'hierarchy.lvl4', + 'hierarchy.lvl5', + 'hierarchy.lvl6', + 'content', + 'type', + 'url' + ], + attributesToSnippet: [ + 'hierarchy.lvl1:10', + 'hierarchy.lvl2:10', + 'hierarchy.lvl3:10', + 'hierarchy.lvl4:10', + 'hierarchy.lvl5:10', + 'hierarchy.lvl6:10', + 'content:10' + ], + snippetEllipsisText: '…', + highlightPreTag: ``, + highlightPostTag: '', + hitsPerPage: 20 +}; - function sectionTitlePredicate(hit) { - let sectionTitle; - for (const [path, title] of Object.entries(sectionTitles)) { - let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); - if (hit.url.match(regexp)) { - sectionTitle = title; - break; - } - } - if (!sectionTitle) { - sectionTitle = 'Documentation'; +const sectionTitles = { + 'react-aria': 'React Aria', + 'react-spectrum': 'React Spectrum', + 'react-stately': 'React Stately', + 'internationalized': 'Internationalized', + 'blog': 'Blog', + 'architecture': 'Architecture', + 'contribute': 'Contribute', + 'releases': 'Releases', + 'support': 'Support' +}; + +function sectionTitlePredicate(hit) { + let sectionTitle; + for (const [path, title] of Object.entries(sectionTitles)) { + let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); + if (hit.url.match(regexp)) { + sectionTitle = title; + break; } - return sectionTitle; } + if (!sectionTitle) { + sectionTitle = 'Documentation'; + } + return sectionTitle; +} +export default function DocSearch() { const [searchValue, setSearchValue] = useState(''); const [loadingState, setLoadingState] = useState(null); const [predictions, setPredictions] = useState(null); @@ -198,7 +199,7 @@ export default function DocSearch() { setLoadingState(null); return; } - setLoadingState('loading'); + setLoadingState('filtering'); searchIndex .search( query, @@ -216,14 +217,11 @@ export default function DocSearch() { }; const searchAutocompleteRef = useRef(); - const logoFragment = document.createElement('div'); - logoFragment.className = docsStyle.docSearchFooter; - const logoRoot = createRoot(logoFragment); - logoRoot.render(AlgoliaSearchLogo); let onOpenChange = (isOpen) => { if (isOpen) { requestAnimationFrame(() => { + // TODO: this could be broken if there is more than one listbox on the page... const listbox = document.querySelector('[role="listbox"]'); if (listbox && listbox.nextElementSibling && @@ -243,7 +241,6 @@ export default function DocSearch() { ref={searchAutocompleteRef} aria-label="Search" UNSAFE_className={docsStyle.docSearchBox} - id="algolia-doc-search" loadingState={loadingState} value={searchValue} onInputChange={onInputChange} @@ -278,6 +275,11 @@ const AlgoliaSearchLogo = (
); +const logoFragment = document.createElement('div'); +logoFragment.className = docsStyle.docSearchFooter; +const logoRoot = createRoot(logoFragment); +logoRoot.render(AlgoliaSearchLogo); + function groupBy(values, predicate = (value) => value) { return values.reduce((accumulator, item) => { const key = predicate(item); From 58e9d085c055a12c5a6bd2f297e1d235ccd5c8ac Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 23 Mar 2023 11:06:27 -0500 Subject: [PATCH 30/46] put logo as item in SearchAutocomplete --- packages/dev/docs/src/DocSearch.js | 57 ++++++++++-------------------- 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 685d72d48bf..6bb580596fb 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -1,5 +1,4 @@ import algoliasearch from 'algoliasearch/lite'; -import {createRoot} from 'react-dom/client'; import docsStyle from './docs.css'; import DocumentOutline from '@spectrum-icons/workflow/DocumentOutline'; import DOMPurify from 'dompurify'; @@ -7,7 +6,7 @@ import {Icon} from '@react-spectrum/icon'; import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; import News from '@spectrum-icons/workflow/News'; import React, {useRef, useState} from 'react'; -import {Text, VisuallyHidden} from '@adobe/react-spectrum'; +import {Text} from '@adobe/react-spectrum'; import {ThemeProvider} from './ThemeSwitcher'; let client = algoliasearch('1V1Q59JVTR', '44a7e2e7508ff185f25ac64c0a675f98'); @@ -187,6 +186,20 @@ export default function DocSearch() { let titles = Object.values(sectionTitles); sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); let suggestions = sections.map((section, index) =>
{section.items}
); + if (suggestions.length > 0) { + suggestions.push( +
+ + + + + + + +
+ ); + } + setSuggestions(suggestions); setLoadingState(null); }; @@ -209,7 +222,9 @@ export default function DocSearch() { }; let onSubmit = (value, key) => { - if (key) { + if (key === 'algolia-footer') { + window.open('https://www.algolia.com/ref/docsearch/?utm_source=react-spectrum.adobe.com&utm_medium=referral&utm_content=powered_by&utm_campaign=docsearch', '_blank'); + } else if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); let url = prediction.url; window.location.href = `${window.location.hostname === 'reactspectrum.blob.core.windows.net' ? window.location.href.replace(/(.+\/docs\/)(.+)/, '$1') : '/'}${url.replace('https://react-spectrum.adobe.com/', '')}`; @@ -218,22 +233,6 @@ export default function DocSearch() { const searchAutocompleteRef = useRef(); - let onOpenChange = (isOpen) => { - if (isOpen) { - requestAnimationFrame(() => { - // TODO: this could be broken if there is more than one listbox on the page... - const listbox = document.querySelector('[role="listbox"]'); - if (listbox && - listbox.nextElementSibling && - listbox.nextElementSibling.innerHTML !== logoFragment.innerHTML) { - listbox.parentElement.insertBefore(logoFragment, listbox.nextElementSibling); - } - }); - } else { - logoFragment.remove(); - } - }; - return ( @@ -244,8 +243,7 @@ export default function DocSearch() { loadingState={loadingState} value={searchValue} onInputChange={onInputChange} - onSubmit={onSubmit} - onOpenChange={onOpenChange}> + onSubmit={onSubmit}> {suggestions} @@ -263,23 +261,6 @@ function Hash(props) { ); } -const AlgoliaSearchLogo = ( -
-); - -const logoFragment = document.createElement('div'); -logoFragment.className = docsStyle.docSearchFooter; -const logoRoot = createRoot(logoFragment); -logoRoot.render(AlgoliaSearchLogo); - function groupBy(values, predicate = (value) => value) { return values.reduce((accumulator, item) => { const key = predicate(item); From 4c6ecda7a46a0cabaa1ab0e8f44580fa65a7ff78 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 23 Mar 2023 11:26:56 -0500 Subject: [PATCH 31/46] remove code changes --- .../autocomplete/src/MobileSearchAutocomplete.tsx | 4 ---- .../autocomplete/src/SearchAutocomplete.tsx | 7 +------ 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx b/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx index f3a5c33bdd9..19057471192 100644 --- a/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx +++ b/packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx @@ -270,10 +270,6 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut 'spectrum-Search--invalid': validationState === 'invalid' && !isDisabled, 'spectrum-Search--valid': validationState === 'valid' && !isDisabled } - ), - classNames( - styles, - 'spectrum-InputGroup-field' ) ) }> diff --git a/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx b/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx index d4d19ff25c1..e95dfa31e40 100644 --- a/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx +++ b/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx @@ -165,7 +165,7 @@ function _SearchAutocompleteBase(props: SpectrumSearchAutocomp isLoading={loadingState === 'loadingMore'} onLoadMore={onLoadMore} renderEmptyState={() => isAsync && ( - + {stringFormatter.format('noResults')} )} /> @@ -297,10 +297,6 @@ function _SearchAutocompleteInput(props: SearchAutocompleteInputProps, ref 'spectrum-InputGroup--invalid': validationState === 'invalid' && !isDisabled, 'is-hovered': isHovered }, - classNames( - searchAutocompleteStyles, - 'searchautocomplete' - ), className ) }> @@ -311,7 +307,6 @@ function _SearchAutocompleteInput(props: SearchAutocompleteInputProps, ref classNames( searchStyles, 'spectrum-Search', - 'spectrum-Search--loadable', 'spectrum-Textfield', { 'is-disabled': isDisabled, From fad1375b47740df1871d0637a71dbfa744390f28 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 23 Mar 2023 13:10:27 -0500 Subject: [PATCH 32/46] focus input on cmd/ctrl + K --- packages/dev/docs/src/DocSearch.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 6bb580596fb..c50eb14a173 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -5,7 +5,7 @@ import DOMPurify from 'dompurify'; import {Icon} from '@react-spectrum/icon'; import {Item, SearchAutocomplete, Section} from '@react-spectrum/autocomplete'; import News from '@spectrum-icons/workflow/News'; -import React, {useRef, useState} from 'react'; +import React, {useEffect, useRef, useState} from 'react'; import {Text} from '@adobe/react-spectrum'; import {ThemeProvider} from './ThemeSwitcher'; @@ -74,6 +74,24 @@ export default function DocSearch() { const [predictions, setPredictions] = useState(null); const [suggestions, setSuggestions] = useState(null); + const searchAutocompleteRef = useRef(); + + useEffect(() => { + // Focus search input when user presses Ctrl/Cmd + K + let handleKeyDown = (e) => { + if ((e.ctrlKey || e.metaKey) && e.key === 'k') { + e.preventDefault(); + searchAutocompleteRef.current.focus(); + } + }; + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, []); + let updatePredictions = ({hits}) => { setPredictions(hits); @@ -231,8 +249,6 @@ export default function DocSearch() { } }; - const searchAutocompleteRef = useRef(); - return ( From bdfac24b2e7473fe152073f66ba3a70da339bfdb Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 23 Mar 2023 13:57:59 -0500 Subject: [PATCH 33/46] remove rest of code changes --- .../autocomplete/src/SearchAutocomplete.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx b/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx index e95dfa31e40..0b5ac48ace4 100644 --- a/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx +++ b/packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx @@ -225,10 +225,6 @@ function _SearchAutocompleteInput(props: SearchAutocompleteInputProps, ref classNames( styles, 'spectrum-InputGroup-input-circleLoader' - ), - classNames( - searchStyles, - 'spectrum-Search-circleLoader' ) )} /> ); @@ -297,6 +293,10 @@ function _SearchAutocompleteInput(props: SearchAutocompleteInputProps, ref 'spectrum-InputGroup--invalid': validationState === 'invalid' && !isDisabled, 'is-hovered': isHovered }, + classNames( + searchAutocompleteStyles, + 'searchautocomplete' + ), className ) }> From 97d35ec8bffe212ef5fa346df67ae10dcd613975 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 29 Mar 2023 13:41:40 -0500 Subject: [PATCH 34/46] fix announcement of --- packages/dev/docs/src/DocSearch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index c50eb14a173..3ac3009f814 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -36,7 +36,7 @@ const searchOptions = { 'content:10' ], snippetEllipsisText: '…', - highlightPreTag: ``, + highlightPreTag: ``, highlightPostTag: '', hitsPerPage: 20 }; From 8dd79fe57534225aa8f73c6fed535c6dce2151c7 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 3 Apr 2023 17:04:44 -0500 Subject: [PATCH 35/46] switch to use dynamic items --- packages/dev/docs/src/DocSearch.js | 218 +++++++++++++++-------------- 1 file changed, 116 insertions(+), 102 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 3ac3009f814..f366bea6587 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -72,7 +72,7 @@ export default function DocSearch() { const [searchValue, setSearchValue] = useState(''); const [loadingState, setLoadingState] = useState(null); const [predictions, setPredictions] = useState(null); - const [suggestions, setSuggestions] = useState(null); + const [suggestions, setSuggestions] = useState([]); const searchAutocompleteRef = useRef(); @@ -115,110 +115,31 @@ export default function DocSearch() { hit.hierarchy.lvl1 ); - return ( - - { - hierarchy[hit.type] && - hit.type === 'lvl1' && ( - <> - { - title === 'Blog' || title === 'Releases' ? - : - - } - - - - {hit.content && ( - - - - )} - - ) - } - - { - hierarchy[hit.type] && - ( - hit.type === 'lvl2' || - hit.type === 'lvl3' || - hit.type === 'lvl4' || - hit.type === 'lvl5' || - hit.type === 'lvl6' - ) && ( - <> - - - - - - - - - ) - } - - { - hit.type === 'content' && ( - <> - - - - - - - - - ) - } - - ); + return { + key: objectID, + hit, + hierarchy, + docsearchParent, + textValue: hit.type === 'content' ? hit[hit.type] : hierarchy[hit.type] + }; } )); - sections.push({title, items}); + sections.push({title, items: items.flat()}); } let titles = Object.values(sectionTitles); sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); - let suggestions = sections.map((section, index) =>
{section.items}
); - if (suggestions.length > 0) { - suggestions.push( -
- - - - - - - -
- ); - } - - setSuggestions(suggestions); + let newSuggestions = sections.map((section, index) => ({key: `${index}-${section.title}`, title: section.title, children: section.items})); + newSuggestions.push({ + key: 'algolia-footer', + title: 'Search by', + children: [ + { + key: 'algolia-footer-logo' + } + ] + }); + setSuggestions(newSuggestions); setLoadingState(null); }; @@ -226,7 +147,7 @@ export default function DocSearch() { setSearchValue(query); if (!query && predictions) { setPredictions(null); - setSuggestions(null); + setSuggestions([]); setLoadingState(null); return; } @@ -240,7 +161,7 @@ export default function DocSearch() { }; let onSubmit = (value, key) => { - if (key === 'algolia-footer') { + if (key === 'algolia-footer-logo') { window.open('https://www.algolia.com/ref/docsearch/?utm_source=react-spectrum.adobe.com&utm_medium=referral&utm_content=powered_by&utm_campaign=docsearch', '_blank'); } else if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); @@ -256,11 +177,104 @@ export default function DocSearch() { ref={searchAutocompleteRef} aria-label="Search" UNSAFE_className={docsStyle.docSearchBox} + items={suggestions} loadingState={loadingState} value={searchValue} onInputChange={onInputChange} onSubmit={onSubmit}> - {suggestions} + {(section) => ( +
+ {(item) => ( + + {item.key === 'algolia-footer-logo' && ( + + + + + + )} + { + item.hierarchy && + item.hierarchy[item.hit.type] && + item.hit.type === 'lvl1' && ( + <> + { + section.title === 'Blog' || section.title === 'Releases' ? + : + + } + + + + {item.hit.content && ( + + + + )} + + ) + } + + { + item.hierarchy && item.hierarchy[item.hit.type] && + ( + item.hit.type === 'lvl2' || + item.hit.type === 'lvl3' || + item.hit.type === 'lvl4' || + item.hit.type === 'lvl5' || + item.hit.type === 'lvl6' + ) && ( + <> + + + + + + + + + ) + } + + { + item.hit && item.hit.type === 'content' && ( + <> + + + + + + + + + ) + } + + )} +
+ )}
From 83ff0ae69d1e7c35f359861216884d7fc7fcdb4e Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 3 Apr 2023 17:15:16 -0500 Subject: [PATCH 36/46] add textValue for algolia item --- packages/dev/docs/src/DocSearch.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index f366bea6587..4ce1c179483 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -135,7 +135,8 @@ export default function DocSearch() { title: 'Search by', children: [ { - key: 'algolia-footer-logo' + key: 'algolia-footer-logo', + textValue: 'Algolia' } ] }); From 7acdafc58705dd1a9fd51a400a653b267733fb7c Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 4 Apr 2023 14:26:46 -0500 Subject: [PATCH 37/46] show top-level result for a page --- packages/dev/docs/src/DocSearch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 4ce1c179483..0437a38436a 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -125,7 +125,7 @@ export default function DocSearch() { } )); - sections.push({title, items: items.flat()}); + sections.push({title, items: items.map((item) => item[0])}); } let titles = Object.values(sectionTitles); sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); From bf57013c7ad271a5655b4a77d7950bcffc2c82d1 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 4 Apr 2023 15:40:32 -0500 Subject: [PATCH 38/46] set overlay to position: fixed on open --- packages/dev/docs/src/DocSearch.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 0437a38436a..5cb0f94db11 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -171,6 +171,16 @@ export default function DocSearch() { } }; + let observer = new MutationObserver((mutationList) => { + let target = mutationList[0].target; + let top = (searchAutocompleteRef.current.UNSAFE_getDOMNode().getBoundingClientRect().height + 8) + 'px'; + if (target.style.top !== top) { + observer.disconnect(); + mutationList[0].target.style.top = top; + observer.observe(target, {attributes: true, attributeFilter: ['style']}); + } + }); + return ( @@ -182,6 +192,21 @@ export default function DocSearch() { loadingState={loadingState} value={searchValue} onInputChange={onInputChange} + onOpenChange={(isOpen) => { + if (isOpen) { + setTimeout(() => { + const listbox = document.querySelector('[role="listbox"][aria-label="Suggestions"]'); + if (listbox) { + listbox.parentElement.style.position = 'fixed'; + let top = (searchAutocompleteRef.current.UNSAFE_getDOMNode().getBoundingClientRect().height + 8) + 'px'; + listbox.parentElement.style.top = top; + observer.observe(listbox.parentElement, {attributes: true, attributeFilter: ['style']}); + } + }, 200); + } else { + observer.disconnect(); + } + }} onSubmit={onSubmit}> {(section) => (
From 2f060600eec3cccc1d05fec04ca230684a212e14 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 4 Apr 2023 16:47:31 -0500 Subject: [PATCH 39/46] use css class instead --- packages/dev/docs/src/DocSearch.js | 22 ++-------------------- packages/dev/docs/src/docs.css | 5 +++++ 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 5cb0f94db11..9365bf60721 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -171,16 +171,6 @@ export default function DocSearch() { } }; - let observer = new MutationObserver((mutationList) => { - let target = mutationList[0].target; - let top = (searchAutocompleteRef.current.UNSAFE_getDOMNode().getBoundingClientRect().height + 8) + 'px'; - if (target.style.top !== top) { - observer.disconnect(); - mutationList[0].target.style.top = top; - observer.observe(target, {attributes: true, attributeFilter: ['style']}); - } - }); - return ( @@ -194,17 +184,9 @@ export default function DocSearch() { onInputChange={onInputChange} onOpenChange={(isOpen) => { if (isOpen) { - setTimeout(() => { - const listbox = document.querySelector('[role="listbox"][aria-label="Suggestions"]'); - if (listbox) { - listbox.parentElement.style.position = 'fixed'; - let top = (searchAutocompleteRef.current.UNSAFE_getDOMNode().getBoundingClientRect().height + 8) + 'px'; - listbox.parentElement.style.top = top; - observer.observe(listbox.parentElement, {attributes: true, attributeFilter: ['style']}); - } - }, 200); + document.body.classList.add(docsStyle['docsearch-open']); } else { - observer.disconnect(); + document.body.classList.remove(docsStyle['docsearch-open']); } }} onSubmit={onSubmit}> diff --git a/packages/dev/docs/src/docs.css b/packages/dev/docs/src/docs.css index fb277c08f33..57521f946ea 100644 --- a/packages/dev/docs/src/docs.css +++ b/packages/dev/docs/src/docs.css @@ -713,6 +713,11 @@ h2.sectionHeader { margin-left: var(--spectrum-global-dimension-size-100); } +body.docsearch-open [data-testid="popover"] { + position: fixed !important; + top: 38px !important; +} + /* hamburger menu should be hidden so that it doesn't receive focus before the sidenav */ .hamburgerButton { display:none; From 13a5a25a8eb74ab24e4e834e7cdf9d5cd6c68054 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 6 Apr 2023 14:22:31 -0500 Subject: [PATCH 40/46] clear value on algolia select --- packages/dev/docs/src/DocSearch.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 9365bf60721..e4b29086e75 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -164,6 +164,7 @@ export default function DocSearch() { let onSubmit = (value, key) => { if (key === 'algolia-footer-logo') { window.open('https://www.algolia.com/ref/docsearch/?utm_source=react-spectrum.adobe.com&utm_medium=referral&utm_content=powered_by&utm_campaign=docsearch', '_blank'); + searchAutocompleteRef.current.UNSAFE_getDOMNode().querySelector('[role="button"]').click(); } else if (key) { let prediction = predictions.find(prediction => key === prediction.objectID); let url = prediction.url; From facc396e466646a39761e0d276b74b5d5e7ba3a7 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 11 Apr 2023 14:22:54 -0500 Subject: [PATCH 41/46] fix suggestion order --- packages/dev/docs/src/DocSearch.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index e4b29086e75..b93341b0fd5 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -101,7 +101,6 @@ export default function DocSearch() { const items = Object.values( groupBy(hits, (hit) => hit.hierarchy.lvl1) ) - .reverse() .map( groupedHits => groupedHits.map((hit) => { @@ -127,8 +126,6 @@ export default function DocSearch() { sections.push({title, items: items.map((item) => item[0])}); } - let titles = Object.values(sectionTitles); - sections = sections.sort((a, b) => titles.indexOf(a.title) < titles.indexOf(b.title) ? -1 : 1); let newSuggestions = sections.map((section, index) => ({key: `${index}-${section.title}`, title: section.title, children: section.items})); newSuggestions.push({ key: 'algolia-footer', From 9ef1739c4fc8722a3cf3d070b0a4d054481c26fa Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 11 Apr 2023 16:26:32 -0500 Subject: [PATCH 42/46] remove groupby --- packages/dev/docs/src/DocSearch.js | 51 +----------------------------- 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index b93341b0fd5..4ee8bb26cee 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -95,38 +95,7 @@ export default function DocSearch() { let updatePredictions = ({hits}) => { setPredictions(hits); - const groupedBySection = groupBy(hits, (hit) => sectionTitlePredicate(hit)); - let sections = []; - for (const [title, hits] of Object.entries(groupedBySection)) { - const items = Object.values( - groupBy(hits, (hit) => hit.hierarchy.lvl1) - ) - .map( - groupedHits => - groupedHits.map((hit) => { - const hierarchy = hit.hierarchy; - const objectID = hit.objectID; - const docsearchParent = hit.type !== 'lvl1' && - groupedHits.find( - (siblingItem) => - siblingItem.type === 'lvl1' && - siblingItem.hierarchy.lvl1 === - hit.hierarchy.lvl1 - ); - - return { - key: objectID, - hit, - hierarchy, - docsearchParent, - textValue: hit.type === 'content' ? hit[hit.type] : hierarchy[hit.type] - }; - } - )); - - sections.push({title, items: items.map((item) => item[0])}); - } - let newSuggestions = sections.map((section, index) => ({key: `${index}-${section.title}`, title: section.title, children: section.items})); + let newSuggestions = hits.map((section, index) => ({key: `${index}-${section.title}`, title: section.title, children: section.items})); newSuggestions.push({ key: 'algolia-footer', title: 'Search by', @@ -297,24 +266,6 @@ function Hash(props) { ); } -function groupBy(values, predicate = (value) => value) { - return values.reduce((accumulator, item) => { - const key = predicate(item); - - if (!Object.prototype.hasOwnProperty.call(accumulator, key)) { - accumulator[key] = []; - } - - // We limit each section to show 20 hits maximum. - // This acts as a frontend alternative to `distinct`. - if (accumulator[key].length < 21) { - accumulator[key].push(item); - } - - return accumulator; - }, {}); -} - function getPropertyByPath(object, path) { const parts = path.split('.'); From 033bfbfb7af9847853512be13aea536ee258711f Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 11 Apr 2023 16:34:49 -0500 Subject: [PATCH 43/46] lint --- packages/dev/docs/src/DocSearch.js | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 4ee8bb26cee..3784fc101a6 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -41,33 +41,6 @@ const searchOptions = { hitsPerPage: 20 }; -const sectionTitles = { - 'react-aria': 'React Aria', - 'react-spectrum': 'React Spectrum', - 'react-stately': 'React Stately', - 'internationalized': 'Internationalized', - 'blog': 'Blog', - 'architecture': 'Architecture', - 'contribute': 'Contribute', - 'releases': 'Releases', - 'support': 'Support' -}; - -function sectionTitlePredicate(hit) { - let sectionTitle; - for (const [path, title] of Object.entries(sectionTitles)) { - let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); - if (hit.url.match(regexp)) { - sectionTitle = title; - break; - } - } - if (!sectionTitle) { - sectionTitle = 'Documentation'; - } - return sectionTitle; -} - export default function DocSearch() { const [searchValue, setSearchValue] = useState(''); const [loadingState, setLoadingState] = useState(null); From 6ae6bb6301a46387f43c2e03c48537b86a1b8a2f Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 11 Apr 2023 16:57:57 -0500 Subject: [PATCH 44/46] Revert "remove groupby" This reverts commit 9ef1739c4fc8722a3cf3d070b0a4d054481c26fa. --- packages/dev/docs/src/DocSearch.js | 51 +++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 3784fc101a6..97866b23481 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -68,7 +68,38 @@ export default function DocSearch() { let updatePredictions = ({hits}) => { setPredictions(hits); - let newSuggestions = hits.map((section, index) => ({key: `${index}-${section.title}`, title: section.title, children: section.items})); + const groupedBySection = groupBy(hits, (hit) => sectionTitlePredicate(hit)); + let sections = []; + for (const [title, hits] of Object.entries(groupedBySection)) { + const items = Object.values( + groupBy(hits, (hit) => hit.hierarchy.lvl1) + ) + .map( + groupedHits => + groupedHits.map((hit) => { + const hierarchy = hit.hierarchy; + const objectID = hit.objectID; + const docsearchParent = hit.type !== 'lvl1' && + groupedHits.find( + (siblingItem) => + siblingItem.type === 'lvl1' && + siblingItem.hierarchy.lvl1 === + hit.hierarchy.lvl1 + ); + + return { + key: objectID, + hit, + hierarchy, + docsearchParent, + textValue: hit.type === 'content' ? hit[hit.type] : hierarchy[hit.type] + }; + } + )); + + sections.push({title, items: items.map((item) => item[0])}); + } + let newSuggestions = sections.map((section, index) => ({key: `${index}-${section.title}`, title: section.title, children: section.items})); newSuggestions.push({ key: 'algolia-footer', title: 'Search by', @@ -239,6 +270,24 @@ function Hash(props) { ); } +function groupBy(values, predicate = (value) => value) { + return values.reduce((accumulator, item) => { + const key = predicate(item); + + if (!Object.prototype.hasOwnProperty.call(accumulator, key)) { + accumulator[key] = []; + } + + // We limit each section to show 20 hits maximum. + // This acts as a frontend alternative to `distinct`. + if (accumulator[key].length < 21) { + accumulator[key].push(item); + } + + return accumulator; + }, {}); +} + function getPropertyByPath(object, path) { const parts = path.split('.'); From ac748eaf3282e1d21aa48c6de29740114c448f5f Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 11 Apr 2023 16:58:12 -0500 Subject: [PATCH 45/46] Revert "lint" This reverts commit 033bfbfb7af9847853512be13aea536ee258711f. --- packages/dev/docs/src/DocSearch.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/dev/docs/src/DocSearch.js b/packages/dev/docs/src/DocSearch.js index 97866b23481..b93341b0fd5 100644 --- a/packages/dev/docs/src/DocSearch.js +++ b/packages/dev/docs/src/DocSearch.js @@ -41,6 +41,33 @@ const searchOptions = { hitsPerPage: 20 }; +const sectionTitles = { + 'react-aria': 'React Aria', + 'react-spectrum': 'React Spectrum', + 'react-stately': 'React Stately', + 'internationalized': 'Internationalized', + 'blog': 'Blog', + 'architecture': 'Architecture', + 'contribute': 'Contribute', + 'releases': 'Releases', + 'support': 'Support' +}; + +function sectionTitlePredicate(hit) { + let sectionTitle; + for (const [path, title] of Object.entries(sectionTitles)) { + let regexp = new RegExp('^.+//.+/' + path + '[/.].+$', 'i'); + if (hit.url.match(regexp)) { + sectionTitle = title; + break; + } + } + if (!sectionTitle) { + sectionTitle = 'Documentation'; + } + return sectionTitle; +} + export default function DocSearch() { const [searchValue, setSearchValue] = useState(''); const [loadingState, setLoadingState] = useState(null); From f1e19a2953706058df3ecea6fe9d40ecdf727a07 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Tue, 11 Apr 2023 16:55:28 -0700 Subject: [PATCH 46/46] Revert ToC changes --- packages/dev/docs/src/attachToToC.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/dev/docs/src/attachToToC.js b/packages/dev/docs/src/attachToToC.js index 1b0065c6a9f..a53947b67a6 100644 --- a/packages/dev/docs/src/attachToToC.js +++ b/packages/dev/docs/src/attachToToC.js @@ -13,7 +13,6 @@ import sideNavStyles from '@adobe/spectrum-css-temp/components/sidenav/vars.css'; export function attachToToC() { - let main = document.getElementsByTagName('main')[0]; let tocLinks = document.querySelectorAll('#toc a'); let headers = []; for (let link of tocLinks) { @@ -27,7 +26,7 @@ export function attachToToC() { // this needs to be improved a little but the math hurts my head right now // right now it's impossible to select the last section if the last two heights combined are smaller than the viewport height headers.some((header, i) => { - if ((header.header.offsetTop + header.header.getBoundingClientRect().height) > main.scrollTop + main.offsetTop) { + if ((header.header.offsetTop + header.header.getBoundingClientRect().height) > document.documentElement.scrollTop) { let currentSelection = document.querySelectorAll(`#toc .${sideNavStyles['is-selected']}`); if (currentSelection) { currentSelection.forEach(node => { @@ -48,5 +47,5 @@ export function attachToToC() { updateToc(); - main.addEventListener('scroll', updateToc); + document.addEventListener('scroll', updateToc); }