Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve navbar menu #3088

Merged
merged 3 commits into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,18 @@ function NavItemDesktop({items, position, className, ...props}) {
<NavLink
className={navLinkClassNames(className)}
{...props}
onClick={(e) => e.preventDefault()}
onClick={props.to ? undefined : (e) => e.preventDefault()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
function toggle() {
((e.target as HTMLElement)
.parentNode as HTMLElement).classList.toggle('dropdown--show');
}
if (e.key === 'Enter' && !props.to) {
toggle();
}
if (e.key === 'Tab') {
toggle();
}
}}>
{props.label}
</NavLink>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import {
useActiveDocContext,
} from '@theme/hooks/useDocs';

const versionLabel = (version) =>
version.name === 'next' ? 'Next/Master' : version.name;
const versionLabel = (version, nextVersionLabel) =>
version.name === 'next' ? nextVersionLabel : version.name;

const getVersionMainDoc = (version) =>
version.docs.find((doc) => doc.id === version.mainDocId);

export default function DocsVersionDropdownNavbarItem({
mobile,
docsPluginId,
nextVersionLabel,
...props
}) {
const activeDocContext = useActiveDocContext(docsPluginId);
Expand All @@ -35,19 +37,28 @@ export default function DocsVersionDropdownNavbarItem({
getVersionMainDoc(version);
return {
isNavLink: true,
label: versionLabel(version),
label: versionLabel(version, nextVersionLabel),
to: versionDoc.path,
isActive: () => version === activeDocContext?.activeVersion,
};
});

const dropdownVersion = activeDocContext.activeVersion ?? latestVersion;

// Mobile is handled a bit differently
const dropdownLabel = mobile
? 'Versions'
: versionLabel(dropdownVersion, nextVersionLabel);
const dropdownTo = mobile
? undefined
: getVersionMainDoc(dropdownVersion).path;

return (
<DefaultNavbarItem
{...props}
label={versionLabel(dropdownVersion)}
to={getVersionMainDoc(dropdownVersion).path}
mobile={mobile}
label={dropdownLabel}
to={dropdownTo}
items={items}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ import {useActiveVersion, useLatestVersion} from '@theme/hooks/useDocs';
const getVersionMainDoc = (version) =>
version.docs.find((doc) => doc.id === version.mainDocId);

const versionLabel = (version, nextVersionLabel) =>
version.name === 'next' ? nextVersionLabel : version.name;

export default function DocsVersionNavbarItem({
label: staticLabel,
to: staticTo,
docsPluginId,
nextVersionLabel,
...props
}) {
const activeVersion = useActiveVersion(docsPluginId);
const latestVersion = useLatestVersion(docsPluginId);
const version = activeVersion ?? latestVersion;
const label = staticLabel ?? version.name;
const label = staticLabel ?? versionLabel(version, nextVersionLabel);
const path = staticTo ?? getVersionMainDoc(version).path;
return <DefaultNavbarItem {...props} label={label} to={path} />;
}
3 changes: 2 additions & 1 deletion packages/docusaurus-theme-classic/src/themeConfigSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const DefaultNavbarItemSchema = Joi.object({
items: Joi.array().optional().items(Joi.link('...')),
to: Joi.string(),
href: Joi.string().uri(),
prependBaseUrlToHref: Joi.bool().default(true),
label: Joi.string(),
position: NavbarItemPosition,
activeBasePath: Joi.string(),
Expand All @@ -28,12 +27,14 @@ const DocsVersionNavbarItemSchema = Joi.object({
label: Joi.string(),
to: Joi.string(),
docsPluginId: Joi.string(),
nextVersionLabel: Joi.string().default('Next'),
});

const DocsVersionDropdownNavbarItemSchema = Joi.object({
type: Joi.string().equal('docsVersionDropdown').required(),
position: NavbarItemPosition,
docsPluginId: Joi.string(),
nextVersionLabel: Joi.string().default('Next'),
});

// Can this be made easier? :/
Expand Down
8 changes: 5 additions & 3 deletions packages/docusaurus/src/client/exports/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface Props {
readonly isNavLink?: boolean;
readonly to?: string;
readonly activeClassName?: string;
readonly href: string;
readonly href?: string;
readonly children?: ReactNode;
}

Expand Down Expand Up @@ -89,14 +89,16 @@ function Link({isNavLink, activeClassName, ...props}: Props): JSX.Element {
const isAnchorLink = targetLink?.startsWith('#') ?? false;
const isRegularHtmlLink = !targetLink || !isInternal || isAnchorLink;

if (isInternal && !isAnchorLink) {
if (targetLink && isInternal && !isAnchorLink) {
if (targetLink && targetLink.startsWith('/http')) {
console.log('collectLink', props);
}
linksCollector.collectLink(targetLink);
}

return isRegularHtmlLink ? (
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
// @ts-expect-error: href specified twice needed to pass children and other user specified props
href={targetLink}
{...(!isInternal && {target: '_blank', rel: 'noopener noreferrer'})}
{...props}
Expand Down
12 changes: 12 additions & 0 deletions packages/docusaurus/src/client/exports/__tests__/isInternalUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import isInternalUrl from '../isInternalUrl';

describe('isInternalUrl', () => {
test('should be true for empty links', () => {
expect(isInternalUrl('')).toBeTruthy();
});

test('should be true for root relative links', () => {
expect(isInternalUrl('/foo/bar')).toBeTruthy();
});
Expand Down Expand Up @@ -35,4 +39,12 @@ describe('isInternalUrl', () => {
test('should be false for mailto links', () => {
expect(isInternalUrl('mailto:[email protected]')).toBeFalsy();
});

test('should be false for undefined links', () => {
expect(isInternalUrl(undefined)).toBeFalsy();
});

test('should be true for root relative links', () => {
expect(isInternalUrl('//reactjs.org')).toBeFalsy();
});
});
26 changes: 16 additions & 10 deletions packages/docusaurus/src/client/exports/__tests__/useBaseUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ jest.mock('../useDocusaurusContext', () => jest.fn(), {virtual: true});

const mockedContext = <jest.Mock>useDocusaurusContext;

const forcePrepend = {forcePrependBaseUrl: true};

describe('useBaseUrl', () => {
test('empty base URL', () => {
mockedContext.mockImplementation(() => ({
Expand All @@ -31,8 +33,9 @@ describe('useBaseUrl', () => {
expect(useBaseUrl('/hello/byebye/')).toEqual('/hello/byebye/');
expect(useBaseUrl('https://github.com')).toEqual('https://github.com');
expect(useBaseUrl('//reactjs.org')).toEqual('//reactjs.org');
expect(useBaseUrl('https://site.com', {forcePrependBaseUrl: true})).toEqual(
'/https://site.com',
expect(useBaseUrl('//reactjs.org', forcePrepend)).toEqual('//reactjs.org');
expect(useBaseUrl('https://site.com', forcePrepend)).toEqual(
'https://site.com',
);
expect(useBaseUrl('/hello/byebye', {absolute: true})).toEqual(
'https://v2.docusaurus.io/hello/byebye',
Expand All @@ -57,8 +60,9 @@ describe('useBaseUrl', () => {
expect(useBaseUrl('/hello/byebye/')).toEqual('/docusaurus/hello/byebye/');
expect(useBaseUrl('https://github.com')).toEqual('https://github.com');
expect(useBaseUrl('//reactjs.org')).toEqual('//reactjs.org');
expect(useBaseUrl('https://site.com', {forcePrependBaseUrl: true})).toEqual(
'/docusaurus/https://site.com',
expect(useBaseUrl('//reactjs.org', forcePrepend)).toEqual('//reactjs.org');
expect(useBaseUrl('https://site.com', forcePrepend)).toEqual(
'https://site.com',
);
expect(useBaseUrl('/hello/byebye', {absolute: true})).toEqual(
'https://v2.docusaurus.io/docusaurus/hello/byebye',
Expand Down Expand Up @@ -86,9 +90,10 @@ describe('useBaseUrlUtils().withBaseUrl()', () => {
expect(withBaseUrl('/hello/byebye/')).toEqual('/hello/byebye/');
expect(withBaseUrl('https://github.com')).toEqual('https://github.com');
expect(withBaseUrl('//reactjs.org')).toEqual('//reactjs.org');
expect(
withBaseUrl('https://site.com', {forcePrependBaseUrl: true}),
).toEqual('/https://site.com');
expect(withBaseUrl('//reactjs.org', forcePrepend)).toEqual('//reactjs.org');
expect(withBaseUrl('https://site.com', forcePrepend)).toEqual(
'https://site.com',
);
expect(withBaseUrl('/hello/byebye', {absolute: true})).toEqual(
'https://v2.docusaurus.io/hello/byebye',
);
Expand All @@ -113,9 +118,10 @@ describe('useBaseUrlUtils().withBaseUrl()', () => {
expect(withBaseUrl('/hello/byebye/')).toEqual('/docusaurus/hello/byebye/');
expect(withBaseUrl('https://github.com')).toEqual('https://github.com');
expect(withBaseUrl('//reactjs.org')).toEqual('//reactjs.org');
expect(
withBaseUrl('https://site.com', {forcePrependBaseUrl: true}),
).toEqual('/docusaurus/https://site.com');
expect(withBaseUrl('//reactjs.org', forcePrepend)).toEqual('//reactjs.org');
expect(withBaseUrl('https://site.com', forcePrepend)).toEqual(
'https://site.com',
);
expect(withBaseUrl('/hello/byebye', {absolute: true})).toEqual(
'https://v2.docusaurus.io/docusaurus/hello/byebye',
);
Expand Down
8 changes: 6 additions & 2 deletions packages/docusaurus/src/client/exports/isInternalUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/

export default function isInternalUrl(url: string): boolean {
return /^(\w*:|\/\/)/.test(url) === false;
export function hasProtocol(url: string) {
return /^(\w*:|\/\/)/.test(url) === true;
}

export default function isInternalUrl(url?: string): boolean {
return typeof url !== 'undefined' && !hasProtocol(url);
}
14 changes: 8 additions & 6 deletions packages/docusaurus/src/client/exports/useBaseUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import useDocusaurusContext from './useDocusaurusContext';
import isInternalUrl from './isInternalUrl';

type BaseUrlOptions = Partial<{
// note: if the url has a protocol, we never prepend it
// (it never makes any sense to do so)
forcePrependBaseUrl: boolean;
absolute: boolean;
}>;
Expand All @@ -23,21 +25,21 @@ function addBaseUrl(
return url;
}

if (forcePrependBaseUrl) {
return baseUrl + url;
}

if (!isInternalUrl(url)) {
return url;
}

if (forcePrependBaseUrl) {
return baseUrl + url;
}

const basePath = baseUrl + url.replace(/^\//, '');

return absolute ? siteUrl + basePath : basePath;
}

export type BaseUrlUtils = {
withBaseUrl: (url: string, options: BaseUrlOptions) => string;
withBaseUrl: (url: string, options?: BaseUrlOptions) => string;
};

export function useBaseUrlUtils(): BaseUrlUtils {
Expand All @@ -53,7 +55,7 @@ export function useBaseUrlUtils(): BaseUrlUtils {

export default function useBaseUrl(
url: string,
options: BaseUrlOptions,
options: BaseUrlOptions = {},
): string {
const {withBaseUrl} = useBaseUrlUtils();
return withBaseUrl(url, options);
Expand Down
13 changes: 8 additions & 5 deletions packages/docusaurus/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,6 @@ export async function load(
// Context.
const context: LoadContext = loadContext(siteDir, customOutDir);
const {generatedFilesDir, siteConfig, outDir, baseUrl} = context;
const genSiteConfig = generate(
generatedFilesDir,
CONFIG_FILE_NAME,
`export default ${JSON.stringify(siteConfig, null, 2)};`,
);

// Plugins.
const pluginConfigs: PluginConfig[] = loadPluginConfigs(context);
Expand All @@ -84,6 +79,14 @@ export async function load(
context,
});

// Site config must be generated after plugins
// We want the generated config to have been normalized by the plugins!
const genSiteConfig = generate(
generatedFilesDir,
CONFIG_FILE_NAME,
`export default ${JSON.stringify(siteConfig, null, 2)};`,
);

// Themes.
const fallbackTheme = path.resolve(__dirname, '../client/theme-fallback');
const pluginThemes: string[] = plugins
Expand Down
3 changes: 2 additions & 1 deletion website/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ module.exports = {
{
type: 'docsVersionDropdown',
position: 'left',
nextVersionLabel: '2.0.0-next',
},
{to: 'blog', label: 'Blog', position: 'left'},
{to: 'showcase', label: 'Showcase', position: 'left'},
Expand All @@ -188,8 +189,8 @@ module.exports = {
activeBaseRegex: `docs/next/(support|team|resources)`,
},
{
type: 'docsVersion',
to: 'versions',
label: 'All versions',
position: 'right',
},
{
Expand Down
8 changes: 8 additions & 0 deletions website/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ html[data-theme='dark'] .header-github-link:before {
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
no-repeat;
}

/*
TODO temporary, should be handled by infima next release
https://github.com/facebookincubator/infima/commit/7820399af53c182b1879aa6d7fceb4d296f78ce0
*/
.navbar__item.dropdown .navbar__link[href] {
pointer-events: all;
}