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

Open all external links in a new tab #870

Merged
merged 11 commits into from
Apr 2, 2024
8 changes: 4 additions & 4 deletions app/scripts/components/common/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { variableBaseType, variableGlsp } from '$styles/variable-utils';
import { ElementInteractive } from '$components/common/element-interactive';
import { VarHeading } from '$styles/variable-components';
import { Figure } from '$components/common/figure';
import { getLinkProps } from '$utils/url';

type CardType = 'classic' | 'cover' | 'featured';

Expand Down Expand Up @@ -354,10 +355,9 @@ function CardComponent(props: CardComponentProps) {
onLinkClick
} = props;

const isExternalLink = linkTo.match(/^https?:\/\//);
const linkProps = isExternalLink
? { href: linkTo, onClick: onLinkClick }
: { as: Link, to: linkTo, onClick: onLinkClick };
const isExternalLink = /^https?:\/\//.test(linkTo);
const linkProps = getLinkProps(linkTo, Link, onLinkClick);


return (
<ElementInteractive
Expand Down
3 changes: 2 additions & 1 deletion app/scripts/components/common/mdx-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
LazyEmbed
} from '$components/common/blocks/lazy-components';
import { NotebookConnectCalloutBlock } from '$components/common/notebook-connect';
import SmartLink from '$components/common/smart-link';
import SmartLink, { CustomLink } from '$components/common/smart-link';

function MdxContent(props) {
const pageMdx = useMdxPageLoader(props.loader);
Expand All @@ -44,6 +44,7 @@ function MdxContent(props) {
CompareImage: LazyCompareImage,
NotebookConnectCallout: NotebookConnectCalloutBlock,
Link: SmartLink,
a: CustomLink,
Table: LazyTable,
Embed: LazyEmbed
}}
Expand Down
8 changes: 6 additions & 2 deletions app/scripts/components/common/page-footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { variableGlsp } from '../../styles/variable-utils';
import { Tip } from '$components/common/tip';
import { ComponentOverride } from '$components/common/page-overrides';

import SmartLink from '$components/common/smart-link';

const PageFooterSelf = styled.footer`
padding: ${variableGlsp(0.75, 1)};
background: ${themeVal('color.base-50')};
Expand Down Expand Up @@ -140,6 +142,8 @@ function PageFooter(props) {
variation='base-text'
size='small'
fitting='skinny'
target='_blank'
rel='noopener noreferrer'
>
<CollecticonBrandGithub title='Explore the code' meaningful />
GitHub
Expand All @@ -148,12 +152,12 @@ function PageFooter(props) {
</InfoList>
<FooterCredits>
<p>
<a href='https://earthdata.nasa.gov/'>
<SmartLink to='https://earthdata.nasa.gov/'>
<span>By</span> NASA <strong>Earthdata</strong> <span>on</span>{' '}
<time dateTime={nowDate.getFullYear()}>
{nowDate.getFullYear()}
</time>
</a>
</SmartLink>
{' • '}
<Tip
content={`Released on ${format(
Expand Down
28 changes: 25 additions & 3 deletions app/scripts/components/common/smart-link.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import { Link } from 'react-router-dom';

import { getLinkProps } from '$utils/url';

interface SmartLinkProps {
to: string;
}
Expand All @@ -10,10 +12,30 @@ interface SmartLinkProps {
*/
export default function SmartLink(props: SmartLinkProps) {
const { to, ...rest } = props;
const isExternalLink = /^https?:\/\//.test(to);
const linkProps = getLinkProps(to);
return isExternalLink ? (
<a {...linkProps} {...rest} />
) : (
<Link {...linkProps} {...rest} />
);
}


return /^https?:\/\//.test(to) ? (
<a href={to} {...rest} />
interface CustomLinkProps {
href: string;
}

/**
* For links defined as markdown, this component will open the external link in a new tab.
*/
export function CustomLink(props: CustomLinkProps) {
const { href, ...rest } = props;
const isExternalLink = /^https?:\/\//.test(href);
const linkProps = getLinkProps(href);
return isExternalLink ? (
<a {...linkProps} {...rest} />
) : (
<Link to={to} {...rest} />
<Link {...linkProps} {...rest} />
);
sandrahoang686 marked this conversation as resolved.
Show resolved Hide resolved
}
27 changes: 27 additions & 0 deletions app/scripts/utils/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { MouseEventHandler } from 'react';
import { getBoolean } from 'veda';
import { LinkProps } from 'react-router-dom';


export const getLinkProps = (
linkTo: string,
as?: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>,
onClick?: (() => void) | MouseEventHandler
) => {
const externalLinksInNewTab = getBoolean('externalLinksInNewTab');
const isExternalLink = /^https?:\/\//.test(linkTo);
return isExternalLink
? {
href: linkTo,
to: linkTo,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this work? I tested in the deploy preview and looks like the external link I clicked wasn't opening in a new tab?

Screenshot 2024-03-28 at 5 20 34 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sandrahoang686 that's because we also need externalLinksInNewTab set to true on veda-config/veda.config.js.
It is working on veda-config-ghg (US-GHG-Center/veda-config-ghg#326) and also on my local machine.
I had to set it this way to prevent typescript error which was expecting "to" prop from the function

...(externalLinksInNewTab
? {target: '_blank', rel: 'noopener noreferrer'}
: {}),
...(onClick ? {onClick: onClick} : {})
}
: {
...(as ? {as: as} : {}),
to: linkTo,
...(onClick ? {onClick: onClick} : {})
};
};
3 changes: 3 additions & 0 deletions parcel-resolver-veda/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ export interface LayerInfo {
one: string;
other: string;
};

export const getBoolean: (variable: string) => boolean;

/**
* List of custom user defined pages.
*/
Expand Down
4 changes: 3 additions & 1 deletion parcel-resolver-veda/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ module.exports = new Resolver({
root,
logger
)},
strings: ${JSON.stringify(withDefaultStrings(result.strings))}
strings: ${JSON.stringify(withDefaultStrings(result.strings))},
booleans: ${JSON.stringify(withDefaultStrings(result.booleans))}
};

export const theme = ${JSON.stringify(result.theme) || null};
Expand All @@ -212,6 +213,7 @@ module.exports = new Resolver({
.filter((k) => k.startsWith('/'));

export const getString = (variable) => config.strings[variable];
export const getBoolean = (variable) => config.booleans[variable];

export const datasets = ${generateMdxDataObject(datasetsImportData)};
export const stories = ${generateMdxDataObject(storiesImportData)};
Expand Down
Loading