From ccc642da4438ba5e60797a5e462e4f0c3bfea003 Mon Sep 17 00:00:00 2001 From: slorber Date: Wed, 7 Oct 2020 20:22:35 +0200 Subject: [PATCH 1/8] POC of contextual search dynamic filters --- .../src/index.ts | 6 +- .../src/plugin-content-docs.d.ts | 1 + .../src/props.ts | 2 + .../src/theme/hooks/useDocs.ts | 17 ++++- .../src/theme/DocPage/index.tsx | 74 ++++++++++++------- .../src/theme/Layout/index.tsx | 6 ++ .../src/theme/hooks/useDocsearchParameters.ts | 46 ++++++++++++ .../src/theme/SearchBar/index.js | 5 ++ website/docusaurus.config.js | 3 - 9 files changed, 126 insertions(+), 34 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/theme/hooks/useDocsearchParameters.ts diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 08b95a73ee2a..794527817937 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -267,7 +267,11 @@ export default function pluginContentDocs( `${docuHash( `version-${loadedVersion.versionName}-metadata-prop`, )}.json`, - JSON.stringify(toVersionMetadataProp(loadedVersion), null, 2), + JSON.stringify( + toVersionMetadataProp(pluginId, loadedVersion), + null, + 2, + ), ); addRoute({ diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index d4c8edd112cb..77d91c9008e2 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -13,6 +13,7 @@ declare module '@docusaurus/plugin-content-docs-types' { }; export type PropVersionMetadata = { + pluginId: string; version: string; label: string; isLast: boolean; diff --git a/packages/docusaurus-plugin-content-docs/src/props.ts b/packages/docusaurus-plugin-content-docs/src/props.ts index 5ff9bdb2eba6..f1d6914ef710 100644 --- a/packages/docusaurus-plugin-content-docs/src/props.ts +++ b/packages/docusaurus-plugin-content-docs/src/props.ts @@ -62,9 +62,11 @@ Available document ids= } export function toVersionMetadataProp( + pluginId: string, loadedVersion: LoadedVersion, ): PropVersionMetadata { return { + pluginId, version: loadedVersion.versionName, label: loadedVersion.versionLabel, isLast: loadedVersion.isLast, diff --git a/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts b/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts index eea342cbeb6d..898302ba2345 100644 --- a/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts +++ b/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts @@ -21,10 +21,10 @@ import { GetActivePluginOptions, } from '../../client/docsClientUtils'; -const useAllDocsData = (): Record => +export const useAllDocsData = (): Record => useAllPluginInstancesData('docusaurus-plugin-content-docs'); -const useDocsData = (pluginId: string | undefined) => +export const useDocsData = (pluginId: string | undefined) => usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData; export const useActivePlugin = (options: GetActivePluginOptions = {}) => { @@ -33,6 +33,19 @@ export const useActivePlugin = (options: GetActivePluginOptions = {}) => { return getActivePlugin(data, pathname, options); }; +export const useActivePluginAndVersion = ( + options: GetActivePluginOptions = {}, +) => { + const {pathname} = useLocation(); + const activePlugin = useActivePlugin(options); + return activePlugin + ? { + activePlugin, + activeVersion: getActiveVersion(activePlugin.pluginData, pathname), + } + : undefined; +}; + // versions are returned ordered (most recent first) export const useVersions = (pluginId: string | undefined): GlobalVersion[] => { const data = useDocsData(pluginId); diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx index bc2a44f64ddf..08ac1be7ec56 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx @@ -32,9 +32,11 @@ type DocPageContentProps = { // Note the last version is also indexed with "last", to avoid breaking search on new releases // See https://github.com/facebook/docusaurus/issues/3391 function DocSearchVersionHeader({ + pluginId, version, isLast, }: { + pluginId: string; version: string; isLast: boolean; }) { @@ -45,7 +47,13 @@ function DocSearchVersionHeader({ name="docsearch:version" content={ // See https://github.com/facebook/docusaurus/issues/3391#issuecomment-685594160 - versions.join(',') + versions + .map( + (v) => + // TODO likely temporary as version must follow semver + `${pluginId}-${v}`, + ) + .join(',') } /> @@ -58,36 +66,46 @@ function DocPageContent({ children, }: DocPageContentProps): JSX.Element { const {siteConfig, isClient} = useDocusaurusContext(); - const {permalinkToSidebar, docsSidebars, version, isLast} = versionMetadata; + const { + pluginId, + permalinkToSidebar, + docsSidebars, + version, + isLast, + } = versionMetadata; const sidebarName = permalinkToSidebar[currentDocRoute.path]; const sidebar = docsSidebars[sidebarName]; return ( - <> - - -
- {sidebar && ( -
- -
- )} -
- {children} -
-
-
- + + +
+ {sidebar && ( +
+ +
+ )} +
+ {children} +
+
+
); } diff --git a/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx b/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx index 452fa26a3631..b0b0068cd7fa 100644 --- a/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx @@ -76,6 +76,12 @@ function Layout(props: Props): JSX.Element { {permalink && } {permalink && } + + v.isLast, + ); + + const facetVersion = activeVersion ?? fallbackVersion; + + return `version:${docPluginId}-${facetVersion.name}`; + } + + const facetFilters = [ + // `language:en`, // TODO + [ + `default`, + ...Object.keys(allDocsData).map(getDocPluginFacetFilters), + /* + `version:ios-1.0`, + `version:android-3.0`, + `version:js-unreleased`, + + */ + ], + ]; + + return { + facetFilters, + }; +} diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js index d7a6eeeced7c..44f155c9f960 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js @@ -13,6 +13,7 @@ import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; import Link from '@docusaurus/Link'; import Head from '@docusaurus/Head'; import useSearchQuery from '@theme/hooks/useSearchQuery'; +import useDocsearchParameters from '@theme/hooks/useDocsearchParameters'; import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react'; let DocSearchModal = null; @@ -33,6 +34,9 @@ function ResultsFooter({state, onClose}) { function DocSearch(props) { const {siteMetadata} = useDocusaurusContext(); + const searchParameters = useDocsearchParameters(); + console.log('docsearchParameters SearchBar', {searchParameters}); + const {withBaseUrl} = useBaseUrlUtils(); const history = useHistory(); const searchButtonRef = useRef(null); @@ -151,6 +155,7 @@ function DocSearch(props) { hitComponent={Hit} resultsFooterComponent={resultsFooterComponent} transformSearchClient={transformSearchClient} + searchParameters={searchParameters} {...props} />, document.body, diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 09b2b8c0d993..536aa56be4da 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -249,9 +249,6 @@ module.exports = { algolia: { apiKey: '47ecd3b21be71c5822571b9f59e52544', indexName: 'docusaurus-2', - searchParameters: { - facetFilters: [`version:latest`], - }, }, navbar: { hideOnScroll: true, From c6773238eecb0462e1a46bf5eeb3215fd6a46a20 Mon Sep 17 00:00:00 2001 From: slorber Date: Thu, 8 Oct 2020 19:51:22 +0200 Subject: [PATCH 2/8] fix useSearchTags bugs --- .../src/theme/hooks/useDocs.ts | 21 +++++++----- ...ocsearchParameters.ts => useSearchTags.ts} | 32 +++++++++---------- .../src/theme/SearchBar/index.js | 15 ++++++--- 3 files changed, 39 insertions(+), 29 deletions(-) rename packages/docusaurus-theme-classic/src/theme/hooks/{useDocsearchParameters.ts => useSearchTags.ts} (60%) diff --git a/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts b/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts index 898302ba2345..6e14f3106c84 100644 --- a/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts +++ b/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts @@ -19,6 +19,7 @@ import { getActiveDocContext, getDocVersionSuggestions, GetActivePluginOptions, + ActivePlugin, } from '../../client/docsClientUtils'; export const useAllDocsData = (): Record => @@ -35,15 +36,19 @@ export const useActivePlugin = (options: GetActivePluginOptions = {}) => { export const useActivePluginAndVersion = ( options: GetActivePluginOptions = {}, -) => { - const {pathname} = useLocation(); +): + | undefined + | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} => { const activePlugin = useActivePlugin(options); - return activePlugin - ? { - activePlugin, - activeVersion: getActiveVersion(activePlugin.pluginData, pathname), - } - : undefined; + const {pathname} = useLocation(); + if (activePlugin) { + const activeVersion = getActiveVersion(activePlugin.pluginData, pathname); + return { + activePlugin, + activeVersion, + }; + } + return undefined; }; // versions are returned ordered (most recent first) diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useDocsearchParameters.ts b/packages/docusaurus-theme-classic/src/theme/hooks/useSearchTags.ts similarity index 60% rename from packages/docusaurus-theme-classic/src/theme/hooks/useDocsearchParameters.ts rename to packages/docusaurus-theme-classic/src/theme/hooks/useSearchTags.ts index 5d9e56acf4eb..b1be10110d4a 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useDocsearchParameters.ts +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useSearchTags.ts @@ -6,17 +6,18 @@ */ import {useAllDocsData, useActivePluginAndVersion} from '@theme/hooks/useDocs'; -export default function useDocsearchParameters() { +// TODO decouple this from DocSearch? +// Maybe users will want to use its own search engine solution +export default function useSearchTags() { const allDocsData = useAllDocsData(); const activePluginAndVersion = useActivePluginAndVersion(); - function getDocPluginFacetFilters(docPluginId: string) { + function getDocPluginTags(docPluginId: string) { const activeVersion = - activePluginAndVersion && - activePluginAndVersion.pluginId === docPluginId && - activePluginAndVersion.version - ? activePluginAndVersion.version + activePluginAndVersion?.activePlugin?.pluginId === docPluginId + ? activePluginAndVersion.activeVersion : undefined; + const fallbackVersion = allDocsData[docPluginId].versions.find( (v) => v.isLast, ); @@ -26,21 +27,18 @@ export default function useDocsearchParameters() { return `version:${docPluginId}-${facetVersion.name}`; } - const facetFilters = [ - // `language:en`, // TODO - [ - `default`, - ...Object.keys(allDocsData).map(getDocPluginFacetFilters), - /* + const tags = [ + // `language:en`, // TODO on i18n branch, later + // [ + `default`, + ...Object.keys(allDocsData).map(getDocPluginTags), + /* `version:ios-1.0`, `version:android-3.0`, `version:js-unreleased`, - */ - ], + // ], ]; - return { - facetFilters, - }; + return tags; } diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js index 44f155c9f960..2eb9f8d223a7 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js @@ -13,7 +13,7 @@ import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; import Link from '@docusaurus/Link'; import Head from '@docusaurus/Head'; import useSearchQuery from '@theme/hooks/useSearchQuery'; -import useDocsearchParameters from '@theme/hooks/useDocsearchParameters'; +import useSearchTags from '@theme/hooks/useSearchTags'; import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react'; let DocSearchModal = null; @@ -34,8 +34,15 @@ function ResultsFooter({state, onClose}) { function DocSearch(props) { const {siteMetadata} = useDocusaurusContext(); - const searchParameters = useDocsearchParameters(); - console.log('docsearchParameters SearchBar', {searchParameters}); + const searchTags = useSearchTags(); + + // We merge user-provided searchParameters, and let user override default facetFilters if he wants to + const searchParameters = { + facetFilters: searchTags, + ...props.searchParameters, + }; + + console.log('searchParameters', searchParameters); const {withBaseUrl} = useBaseUrlUtils(); const history = useHistory(); @@ -155,8 +162,8 @@ function DocSearch(props) { hitComponent={Hit} resultsFooterComponent={resultsFooterComponent} transformSearchClient={transformSearchClient} - searchParameters={searchParameters} {...props} + searchParameters={searchParameters} />, document.body, )} From 71d534966634c9f14565ffa54605aaeb26678872 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 12 Oct 2020 19:56:12 +0200 Subject: [PATCH 3/8] contextual search should use preferred version (persisted in storage) --- .../src/theme/hooks/useSearchTags.ts | 16 +++++----- .../useDocsPreferredVersion.ts | 30 ++++++++++++++++++- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useSearchTags.ts b/packages/docusaurus-theme-classic/src/theme/hooks/useSearchTags.ts index b1be10110d4a..12d1978394e4 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useSearchTags.ts +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useSearchTags.ts @@ -5,26 +5,28 @@ * LICENSE file in the root directory of this source tree. */ import {useAllDocsData, useActivePluginAndVersion} from '@theme/hooks/useDocs'; +import {useDocsPreferredVersionByPluginId} from '../../utils/docsPreferredVersion/useDocsPreferredVersion'; // TODO decouple this from DocSearch? // Maybe users will want to use its own search engine solution export default function useSearchTags() { const allDocsData = useAllDocsData(); const activePluginAndVersion = useActivePluginAndVersion(); + const docsPreferredVersionByPluginId = useDocsPreferredVersionByPluginId(); - function getDocPluginTags(docPluginId: string) { + function getDocPluginTags(pluginId: string) { const activeVersion = - activePluginAndVersion?.activePlugin?.pluginId === docPluginId + activePluginAndVersion?.activePlugin?.pluginId === pluginId ? activePluginAndVersion.activeVersion : undefined; - const fallbackVersion = allDocsData[docPluginId].versions.find( - (v) => v.isLast, - ); + const preferredVersion = docsPreferredVersionByPluginId[pluginId]; - const facetVersion = activeVersion ?? fallbackVersion; + const latestVersion = allDocsData[pluginId].versions.find((v) => v.isLast); - return `version:${docPluginId}-${facetVersion.name}`; + const facetVersion = activeVersion ?? preferredVersion ?? latestVersion; + + return `version:${pluginId}-${facetVersion.name}`; } const tags = [ diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts index a6a14778b2a5..6708c782b1f2 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts @@ -6,7 +6,7 @@ */ import {useCallback} from 'react'; import {useDocsPreferredVersionContext} from './DocsPreferredVersionProvider'; -import {useDocsData} from '@theme/hooks/useDocs'; +import {useAllDocsData, useDocsData} from '@theme/hooks/useDocs'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; @@ -32,3 +32,31 @@ export default function useDocsPreferredVersion( return {preferredVersion, savePreferredVersionName} as const; } + +export function useDocsPreferredVersionByPluginId() { + const allDocsData = useAllDocsData(); + const [state] = useDocsPreferredVersionContext(); + + function getPluginIdPreferredVersion(pluginId: string) { + const docsData = allDocsData[pluginId]; + const {preferredVersionName} = state[pluginId]; + + return preferredVersionName + ? docsData.versions.find( + (version) => version.name === preferredVersionName, + ) + : null; + } + + const pluginIds = Object.keys(allDocsData); + + const result: Record< + string, + any // TODO find a way to type this properly! + > = {}; + pluginIds.forEach((pluginId) => { + result[pluginId] = getPluginIdPreferredVersion(pluginId); + }); + + return result; +} From ece9510190ded3fbba46513e60f887d433ced88a Mon Sep 17 00:00:00 2001 From: slorber Date: Tue, 13 Oct 2020 17:38:39 +0200 Subject: [PATCH 4/8] Contextual search: make system decoupled from algolia + wire proper meta tags and facet filters --- .../src/theme/DocPage/index.tsx | 56 ++--------- .../src/theme/Layout/index.tsx | 93 ++----------------- .../src/theme/LayoutHead/index.tsx | 83 +++++++++++++++++ .../src/theme/LayoutProviders/index.tsx | 23 +++++ .../src/theme/SearchMetadatas/index.tsx | 33 +++++++ .../src/theme/hooks/useSearchFilters.ts | 8 ++ .../docusaurus-theme-classic/src/types.d.ts | 4 + .../useSearchTags.ts => utils/searchUtils.ts} | 39 +++++--- .../src/theme/SearchBar/index.js | 9 +- .../src/theme/SearchMetadatas/index.js | 21 +++++ .../src/utils/algoliaSearchUtils.js | 19 ++++ website/docusaurus.config.js | 6 +- 12 files changed, 238 insertions(+), 156 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/theme/LayoutHead/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/LayoutProviders/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/SearchMetadatas/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/hooks/useSearchFilters.ts rename packages/docusaurus-theme-classic/src/{theme/hooks/useSearchTags.ts => utils/searchUtils.ts} (57%) create mode 100644 packages/docusaurus-theme-search-algolia/src/theme/SearchMetadatas/index.js create mode 100644 packages/docusaurus-theme-search-algolia/src/utils/algoliaSearchUtils.js diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx index 08ac1be7ec56..8d122c58a24e 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx @@ -18,9 +18,9 @@ import NotFound from '@theme/NotFound'; import type {DocumentRoute} from '@theme/DocItem'; import type {Props} from '@theme/DocPage'; import {matchPath} from '@docusaurus/router'; -import Head from '@docusaurus/Head'; import styles from './styles.module.css'; +import {docVersionSearchTag} from '../../utils/searchUtils'; type DocPageContentProps = { readonly currentDocRoute: DocumentRoute; @@ -28,62 +28,22 @@ type DocPageContentProps = { readonly children: ReactNode; }; -// This theme is not coupled to Algolia, but can we do something else? -// Note the last version is also indexed with "last", to avoid breaking search on new releases -// See https://github.com/facebook/docusaurus/issues/3391 -function DocSearchVersionHeader({ - pluginId, - version, - isLast, -}: { - pluginId: string; - version: string; - isLast: boolean; -}) { - const versions = isLast ? [version, 'latest'] : [version]; - return ( - - - // TODO likely temporary as version must follow semver - `${pluginId}-${v}`, - ) - .join(',') - } - /> - - ); -} - function DocPageContent({ currentDocRoute, versionMetadata, children, }: DocPageContentProps): JSX.Element { const {siteConfig, isClient} = useDocusaurusContext(); - const { - pluginId, - permalinkToSidebar, - docsSidebars, - version, - isLast, - } = versionMetadata; + const {pluginId, permalinkToSidebar, docsSidebars, version} = versionMetadata; const sidebarName = permalinkToSidebar[currentDocRoute.path]; const sidebar = docsSidebars[sidebarName]; return ( - - +
{sidebar && (
diff --git a/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx b/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx index d4f998731663..108f03e47d77 100644 --- a/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx @@ -7,105 +7,26 @@ import React from 'react'; import clsx from 'clsx'; -import Head from '@docusaurus/Head'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import useBaseUrl from '@docusaurus/useBaseUrl'; - -import ThemeProvider from '@theme/ThemeProvider'; -import UserPreferencesProvider from '@theme/UserPreferencesProvider'; import AnnouncementBar from '@theme/AnnouncementBar'; import Navbar from '@theme/Navbar'; import Footer from '@theme/Footer'; +import LayoutProviders from '@theme/LayoutProviders'; +import LayoutHead from '@theme/LayoutHead'; import type {Props} from '@theme/Layout'; - import './styles.css'; -import DocsPreferredVersionContextProvider from '../../utils/docsPreferredVersion/DocsPreferredVersionProvider'; - -function Providers({children}) { - return ( - - - - {children} - - - - ); -} function Layout(props: Props): JSX.Element { - const {siteConfig} = useDocusaurusContext(); - const { - favicon, - title: siteTitle, - themeConfig: {image: defaultImage, metadatas}, - url: siteUrl, - titleDelimiter, - } = siteConfig; - const { - children, - title, - noFooter, - description, - image, - keywords, - permalink, - wrapperClassName, - } = props; - const metaTitle = title - ? `${title} ${titleDelimiter} ${siteTitle}` - : siteTitle; - const metaImage = image || defaultImage; - const metaImageUrl = useBaseUrl(metaImage, {absolute: true}); - const faviconUrl = useBaseUrl(favicon); + const {children, noFooter, wrapperClassName} = props; return ( - - - {/* TODO: Do not assume that it is in english language */} - - {metaTitle && {metaTitle}} - {metaTitle && } - {favicon && } - {description && } - {description && ( - - )} - {keywords && keywords.length && ( - - )} - {metaImage && } - {metaImage && } - {metaImage && ( - - )} - {permalink && } - {permalink && } - - - - - - element here, - // as it allows react-helmet to override values set in previous - // ie we can override default metadatas such as "twitter:card" - // In same Head, the same meta would appear twice instead of overriding - // See react-helmet doc - > - {metadatas.map((metadata, i) => ( - - ))} - + +
{children}
+ {!noFooter &&