diff --git a/package-lock.json b/package-lock.json index da7992bfa..a58d03c4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -131,7 +131,7 @@ }, "engines": { "node": "^18", - "npm": "8.*" + "npm": "9.*" } }, "node_modules/@adobe/css-tools": { diff --git a/src/components/Breadcrumbs/BreadcrumbContainer.js b/src/components/Breadcrumbs/BreadcrumbContainer.js index e304337a9..68e67936e 100644 --- a/src/components/Breadcrumbs/BreadcrumbContainer.js +++ b/src/components/Breadcrumbs/BreadcrumbContainer.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import styled from '@emotion/styled'; import { reportAnalytics } from '../../utils/report-analytics'; import { theme } from '../../theme/docsTheme'; +import { getFullBreadcrumbPath } from '../../utils/get-complete-breadcrumb-data'; import IndividualBreadcrumb from './IndividualBreadcrumb'; import CollapsedBreadcrumbs from './CollapsedBreadcrumbs'; @@ -67,7 +68,7 @@ const BreadcrumbContainer = ({ breadcrumbs }) => { setIsExcessivelyTruncated={collapseBreadcrumbs} onClick={() => reportAnalytics('BreadcrumbClick', { - breadcrumbClicked: crumb.url, + breadcrumbClicked: getFullBreadcrumbPath(crumb.path, true), }) } > @@ -81,7 +82,7 @@ const BreadcrumbContainer = ({ breadcrumbs }) => { const crumbObjectShape = { title: PropTypes.string.isRequired, - url: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, }; BreadcrumbContainer.propTypes = { diff --git a/src/components/Breadcrumbs/CollapsedBreadcrumbs.js b/src/components/Breadcrumbs/CollapsedBreadcrumbs.js index 509337031..7217a28ff 100644 --- a/src/components/Breadcrumbs/CollapsedBreadcrumbs.js +++ b/src/components/Breadcrumbs/CollapsedBreadcrumbs.js @@ -2,10 +2,27 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Menu, MenuItem } from '@leafygreen-ui/menu'; import IconButton from '@leafygreen-ui/icon-button'; +import { withPrefix } from 'gatsby'; +import { useLocation } from '@gatsbyjs/reach-router'; import Icon from '@leafygreen-ui/icon'; import { formatText } from '../../utils/format-text'; +import { isGatsbyPreview } from '../../utils/is-gatsby-preview'; +import { getGatsbyPreviewLink } from '../../utils/get-gatsby-preview-link'; const CollapsedBreadcrumbs = ({ crumbs }) => { + const location = useLocation(); + + const menuItems = crumbs.map((crumb, index) => { + let to = withPrefix(crumb.path); + if (isGatsbyPreview()) to = getGatsbyPreviewLink(to, location); + + return ( + + {formatText(crumb.title)} + + ); + }); + return ( { } > - {crumbs.map((crumb, index) => ( - - {formatText(crumb.title)} - - ))} + {menuItems} ); @@ -29,7 +42,7 @@ const CollapsedBreadcrumbs = ({ crumbs }) => { const crumbObjectShape = { title: PropTypes.string.isRequired, - url: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, }; CollapsedBreadcrumbs.propTypes = { diff --git a/src/components/Breadcrumbs/IndividualBreadcrumb.js b/src/components/Breadcrumbs/IndividualBreadcrumb.js index 9a741b396..b1e272545 100644 --- a/src/components/Breadcrumbs/IndividualBreadcrumb.js +++ b/src/components/Breadcrumbs/IndividualBreadcrumb.js @@ -9,6 +9,7 @@ import { theme } from '../../theme/docsTheme'; const linkStyling = LeafyCss` font-size: ${theme.fontSize.small}; vertical-align: middle; + line-height: unset; :hover, :focus { @@ -82,7 +83,7 @@ const IndividualBreadcrumb = ({ crumb, setIsExcessivelyTruncated, onClick }) => let result = (
21 ? ellipsisStyling : '')} ref={measuredRef}> - + {formatText(crumb.title)}
@@ -109,7 +110,7 @@ const IndividualBreadcrumb = ({ crumb, setIsExcessivelyTruncated, onClick }) => const crumbObjectShape = { title: PropTypes.oneOfType([PropTypes.string.isRequired, PropTypes.arrayOf(PropTypes.object)]), - url: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, }; IndividualBreadcrumb.propTypes = { diff --git a/src/components/Link.js b/src/components/Link.js index 7d64a24c7..0ddab664b 100644 --- a/src/components/Link.js +++ b/src/components/Link.js @@ -10,6 +10,7 @@ import { isRelativeUrl } from '../utils/is-relative-url'; import { joinClassNames } from '../utils/join-class-names'; import { isGatsbyPreview } from '../utils/is-gatsby-preview'; import { validateHTMAttributes } from '../utils/validate-element-attributes'; +import { getGatsbyPreviewLink } from '../utils/get-gatsby-preview-link'; /* * Note: This component is not suitable for internal page navigation: @@ -86,19 +87,7 @@ const Link = ({ // Ensure trailing slash to = to.replace(/\/?(\?|#|$)/, '/$1'); - if (isGatsbyPreview()) { - // If we're in preview mode, we build the pages of each project and branch of the site within - // its own namespace so each author can preview their own pages e.g. - // /project1/branch1/doc-path - // /project2/branch2/doc-path - // - // So to navigate with the namespaced site, we add to each link the current project and branch - // the user is browsing in. - const projectAndBranchPrefix = `/` + location.pathname.split(`/`).slice(1, 3).join(`/`); - if (!to.startsWith(projectAndBranchPrefix)) { - to = projectAndBranchPrefix + to; - } - } + if (isGatsbyPreview()) to = getGatsbyPreviewLink(to, location); return ( - breadcrumbs.map(({ url, title }, index) => ({ - '@type': 'ListItem', - position: index + 1, - name: title, - item: assertTrailingSlash(url), - })); + breadcrumbs.map(({ path, title }, index) => { + path = getFullBreadcrumbPath(path, true); + + return { + '@type': 'ListItem', + position: index + 1, + name: title, + item: path, + }; + }); const BreadcrumbSchema = ({ slug }) => { const { parentPaths, title: siteTitle } = useSnootyMetadata(); diff --git a/src/utils/get-complete-breadcrumb-data.js b/src/utils/get-complete-breadcrumb-data.js index 8b107fb1e..7b5131ba9 100644 --- a/src/utils/get-complete-breadcrumb-data.js +++ b/src/utils/get-complete-breadcrumb-data.js @@ -1,7 +1,9 @@ +import { withPrefix } from 'gatsby'; import { baseUrl } from './base-url'; import { assertTrailingSlash } from './assert-trailing-slash'; import { removeLeadingSlash } from './remove-leading-slash'; import { assertLeadingSlash } from './assert-leading-slash'; +import { isRelativeUrl } from './is-relative-url'; const nodesToString = (titleNodes) => { if (typeof titleNodes === 'string') { @@ -23,15 +25,25 @@ const nodesToString = (titleNodes) => { .join(''); }; +export const getFullBreadcrumbPath = (path, needsPrefix) => { + if (needsPrefix) { + path = withPrefix(path); + } + if (isRelativeUrl(path)) { + path = baseUrl() + removeLeadingSlash(path); + } + return assertTrailingSlash(path); +}; + export const getCompleteBreadcrumbData = ({ siteTitle, slug, queriedCrumbs, parentPaths }) => { //get intermediate breadcrumbs const intermediateCrumbs = (queriedCrumbs?.breadcrumbs ?? []).map((crumb) => { - return { ...crumb, url: assertTrailingSlash(baseUrl() + removeLeadingSlash(crumb.url)) }; + return { ...crumb, path: getFullBreadcrumbPath(crumb.path, false) }; }); const homeCrumb = { title: 'Docs Home', - url: baseUrl(), + path: baseUrl(), }; // If site is the property homepage, leave the propertyCrumb blank @@ -39,7 +51,7 @@ export const getCompleteBreadcrumbData = ({ siteTitle, slug, queriedCrumbs, pare if (slug !== '/') { propertyCrumb = { title: nodesToString(siteTitle), - url: '/', + path: '/', }; } @@ -49,7 +61,7 @@ export const getCompleteBreadcrumbData = ({ siteTitle, slug, queriedCrumbs, pare return { ...crumb, title: nodesToString(crumb.title), - url: assertLeadingSlash(crumb.path), + path: assertLeadingSlash(crumb.path), }; }); diff --git a/src/utils/get-gatsby-preview-link.js b/src/utils/get-gatsby-preview-link.js new file mode 100644 index 000000000..e1e2f8bdf --- /dev/null +++ b/src/utils/get-gatsby-preview-link.js @@ -0,0 +1,18 @@ +/** + * If we're in preview mode, we build the pages of each project and branch of the site within + * its own namespace so each author can preview their own pages e.g. + * /project1/branch1/doc-path + * /project2/branch2/doc-path + * + * So to navigate with the namespaced site, we add to each link the current project and branch + * the user is browsing in. + */ +const getGatsbyPreviewLink = (to, location) => { + const projectAndBranchPrefix = `/` + location.pathname.split(`/`).slice(1, 3).join(`/`); + if (!to.startsWith(projectAndBranchPrefix)) { + to = projectAndBranchPrefix + to; + } + return to; +}; + +module.exports = { getGatsbyPreviewLink }; diff --git a/tests/unit/BreadcrumbContainer.test.js b/tests/unit/BreadcrumbContainer.test.js index c0b53972f..f9a746656 100644 --- a/tests/unit/BreadcrumbContainer.test.js +++ b/tests/unit/BreadcrumbContainer.test.js @@ -13,30 +13,30 @@ jest.mock(`../../src/utils/use-snooty-metadata`, () => jest.fn()); const mockIntermediateCrumbs = { title: 'MongoDB Atlas', - url: 'https://www.mongodb.com/docs/atlas/', + path: 'https://www.mongodb.com/docs/atlas/', }; const mockPropertyCrumb = { title: 'MongoDB Atlas Device SDKs', - url: 'https://www.mongodb.com/docs/atlas/device-sdks/', + path: 'https://www.mongodb.com/docs/atlas/device-sdks/', }; describe('BreadcrumbContainer', () => { //home breadcrumb const mockHomeCrumb = { title: 'Docs Home', - url: 'https://www.mongodb.com/docs/', + path: 'https://www.mongodb.com/docs/', }; const mockParents = mockData; it('renders a driver site correctly with intermediate breadcrumb and with project parents', () => { const breadcrumbs = [ - { title: 'Docs Home', url: 'https://www.mongodb.com/docs/' }, - { title: 'Languages', url: 'https://www.mongodb.com/docs/languages' }, - { title: 'C#/.NET', url: 'https://www.mongodb.com/docs/languages/csharp/' }, - { title: 'C#/.NET Driver', url: 'https://www.mongodb.com/docs/languages/csharp/csharp-driver/' }, - { title: 'Usage Examples', url: 'https://www.mongodb.com/docs/languages/csharp/csharp-driver/usage-examples/' }, + { title: 'Docs Home', path: 'https://www.mongodb.com/docs/' }, + { title: 'Languages', path: 'https://www.mongodb.com/docs/languages' }, + { title: 'C#/.NET', path: 'https://www.mongodb.com/docs/languages/csharp/' }, + { title: 'C#/.NET Driver', path: 'https://www.mongodb.com/docs/languages/csharp/csharp-driver/' }, + { title: 'Usage Examples', path: 'https://www.mongodb.com/docs/languages/csharp/csharp-driver/usage-examples/' }, ]; const tree = mountBreadcrumbContainer(breadcrumbs); diff --git a/tests/unit/Breadcrumbs.test.js b/tests/unit/Breadcrumbs.test.js index 68cb2ee23..dc8357909 100644 --- a/tests/unit/Breadcrumbs.test.js +++ b/tests/unit/Breadcrumbs.test.js @@ -18,7 +18,7 @@ beforeAll(() => { const mockIntermediateCrumbs = [ { title: 'MongoDB Atlas', - url: '/atlas', + path: '/atlas', }, ]; const useStaticQuery = jest.spyOn(Gatsby, 'useStaticQuery'); diff --git a/tests/unit/__snapshots__/BreadcrumbContainer.test.js.snap b/tests/unit/__snapshots__/BreadcrumbContainer.test.js.snap index 193e86dca..35c866232 100644 --- a/tests/unit/__snapshots__/BreadcrumbContainer.test.js.snap +++ b/tests/unit/__snapshots__/BreadcrumbContainer.test.js.snap @@ -62,6 +62,7 @@ exports[`BreadcrumbContainer renders a driver site correctly with intermediate b display: inline; font-size: 13px; vertical-align: middle; + line-height: unset; } .emotion-3:hover, @@ -252,6 +253,7 @@ exports[`BreadcrumbContainer renders correctly as a docs homepage 1`] = ` display: inline; font-size: 13px; vertical-align: middle; + line-height: unset; } .emotion-3:hover, @@ -308,6 +310,54 @@ exports[`BreadcrumbContainer renders correctly as a docs homepage 1`] = ` } } +.emotion-11 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + cursor: pointer; + position: relative; + -webkit-text-decoration: none; + text-decoration: none; + text-decoration-color: transparent; + line-height: 13px; + color: #016BF8; +} + +.emotion-11>code { + color: #016BF8; +} + +.emotion-11:focus, +.emotion-11:hover { + text-decoration-line: underline; + -webkit-transition: text-decoration 150ms ease-in-out; + transition: text-decoration 150ms ease-in-out; + text-underline-offset: 4px; + text-decoration-thickness: 2px; +} + +.emotion-11:focus { + text-decoration-color: #016BF8; + outline: none; +} + +.emotion-11:hover { + text-decoration-color: #E8EDEB; +} + +.emotion-12 { + font-size: 13px; + vertical-align: middle; + line-height: unset; +} + +.emotion-12:hover, +.emotion-12:focus { + -webkit-text-decoration: underline; + text-decoration: underline; +} +
@@ -351,12 +401,10 @@ exports[`BreadcrumbContainer renders correctly as a docs homepage 1`] = ` class="emotion-2" > - - Atlas Device SDK for C++ - + Atlas Device SDK for C++
- - Application Services - C++ SDK - + Application Services - C++ SDK @@ -442,6 +488,7 @@ exports[`BreadcrumbContainer renders correctly with intermediate breadcrumb, no display: inline; font-size: 13px; vertical-align: middle; + line-height: unset; } .emotion-3:hover, @@ -616,6 +663,7 @@ exports[`BreadcrumbContainer renders correctly with no intermediate breadcrumbs, display: inline; font-size: 13px; vertical-align: middle; + line-height: unset; } .emotion-3:hover, @@ -672,6 +720,54 @@ exports[`BreadcrumbContainer renders correctly with no intermediate breadcrumbs, } } +.emotion-11 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + cursor: pointer; + position: relative; + -webkit-text-decoration: none; + text-decoration: none; + text-decoration-color: transparent; + line-height: 13px; + color: #016BF8; +} + +.emotion-11>code { + color: #016BF8; +} + +.emotion-11:focus, +.emotion-11:hover { + text-decoration-line: underline; + -webkit-transition: text-decoration 150ms ease-in-out; + transition: text-decoration 150ms ease-in-out; + text-underline-offset: 4px; + text-decoration-thickness: 2px; +} + +.emotion-11:focus { + text-decoration-color: #016BF8; + outline: none; +} + +.emotion-11:hover { + text-decoration-color: #E8EDEB; +} + +.emotion-12 { + font-size: 13px; + vertical-align: middle; + line-height: unset; +} + +.emotion-12:hover, +.emotion-12:focus { + -webkit-text-decoration: underline; + text-decoration: underline; +} +
@@ -715,12 +811,10 @@ exports[`BreadcrumbContainer renders correctly with no intermediate breadcrumbs, class="emotion-2" > - - Atlas Device SDK for C++ - + Atlas Device SDK for C++
- - Application Services - C++ SDK - + Application Services - C++ SDK diff --git a/tests/unit/__snapshots__/Breadcrumbs.test.js.snap b/tests/unit/__snapshots__/Breadcrumbs.test.js.snap index 37d958154..74b6114c5 100644 --- a/tests/unit/__snapshots__/Breadcrumbs.test.js.snap +++ b/tests/unit/__snapshots__/Breadcrumbs.test.js.snap @@ -70,6 +70,7 @@ exports[`renders correctly as a homepage 1`] = ` display: inline; font-size: 13px; vertical-align: middle; + line-height: unset; } .emotion-4:hover, @@ -218,6 +219,7 @@ exports[`renders correctly with siteTitle 1`] = ` display: inline; font-size: 13px; vertical-align: middle; + line-height: unset; } .emotion-4:hover, @@ -293,6 +295,7 @@ exports[`renders correctly with siteTitle 1`] = ` .emotion-13 { font-size: 13px; vertical-align: middle; + line-height: unset; } .emotion-13:hover,