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

Add support for external links in link component #7784

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
@@ -0,0 +1,24 @@
import { useCallback } from 'react';

import { Url } from '../../shared/constants';
import { useAppContext } from '../context';
import { Link, LinkProps } from '../lib/components';

export type ExternalLinkProps = Omit<LinkProps<'a'>, 'href' | 'as'> & {
to: Url;
};

export const ExternalLink = ({ to, onClick, ...props }: ExternalLinkProps) => {
const { openUrl } = useAppContext();
const navigate = useCallback(
(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault();
if (onClick) {
onClick(e);
}
return openUrl(to);
},
[onClick, openUrl, to],
);
return <Link href="" onClick={navigate} {...props} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useCallback } from 'react';

import { Link, LinkProps } from '../lib/components';
import { useHistory } from '../lib/history';
import { RoutePath } from '../lib/routes';

export type InternalLinkProps = Omit<LinkProps<'a'>, 'href' | 'as'> & {
to: RoutePath;
};

export const InternalLink = ({ to, onClick, ...props }: InternalLinkProps) => {
const history = useHistory();
const navigate = useCallback(
(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault();
if (onClick) {
onClick(e);
}
return history.push(to);
},
[history, to, onClick],
);
return <Link href="" onClick={navigate} {...props} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
} from '../../shared/notifications';
import { useAppContext } from '../context';
import useActions from '../lib/actionsHook';
import { Link } from '../lib/components';
import { Colors } from '../lib/foundations';
import { transitions, useHistory } from '../lib/history';
import { formatHtml } from '../lib/html-formatter';
Expand All @@ -28,6 +27,7 @@ import { RoutePath } from '../lib/routes';
import accountActions from '../redux/account/actions';
import { IReduxState, useSelector } from '../redux/store';
import * as AppButton from './AppButton';
import { InternalLink } from './InternalLink';
import { ModalAlert, ModalAlertType, ModalMessage, ModalMessageList } from './Modal';
import {
NotificationActions,
Expand Down Expand Up @@ -141,13 +141,13 @@ export default function NotificationArea(props: IProps) {
{notification.title}
</NotificationTitle>
<NotificationSubtitle data-testid="notificationSubTitle">
{notification.subtitleAction?.type === 'navigate' ? (
<Link
{notification.subtitleAction?.type === 'navigate-internal' ? (
<InternalLink
variant="labelTiny"
color={Colors.white60}
{...notification.subtitleAction.link}>
{formatHtml(notification.subtitle ?? '')}
</Link>
</InternalLink>
) : (
formatHtml(notification.subtitle ?? '')
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export const StyledText = styled(TitleMedium)<{ $visible?: boolean }>(({ $visibl
textOverflow: 'ellipsis',
}));

export const NavigationHeaderTitle = ({ children }: NavigationHeaderTitleProps) => {
export const NavigationHeaderTitle = ({ children, ...props }: NavigationHeaderTitleProps) => {
const { titleVisible } = useNavigationHeader();
return (
<StyledText tag="h1" $visible={titleVisible}>
<StyledText forwardedAs="h1" $visible={titleVisible} {...props}>
{children}
</StyledText>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { KnownTarget } from 'styled-components/dist/types';

import { Text, TextProps } from './Text';

export type BodySmallProps = Omit<TextProps, 'variant'>;
export type BodySmallProps = Omit<TextProps<KnownTarget>, 'variant'>;

export const BodySmall = ({ children, ...props }: BodySmallProps) => (
<Text variant="bodySmall" {...props}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { KnownTarget } from 'styled-components/dist/types';

import { Text, TextProps } from './Text';

export type BodySmallSemiBoldProps = Omit<TextProps, 'variant'>;
export type BodySmallSemiBoldProps = Omit<TextProps<KnownTarget>, 'variant'>;

export const BodySmallSemiBold = ({ children, ...props }: BodySmallSemiBoldProps) => (
<Text variant="bodySmallSemibold" {...props}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { KnownTarget } from 'styled-components/dist/types';

import { Text, TextProps } from './Text';

export type FoonoteMiniProps = Omit<TextProps, 'variant'>;
export type FoonoteMiniProps = Omit<TextProps<KnownTarget>, 'variant'>;

export const FootnoteMini = ({ children, ...props }: FoonoteMiniProps) => (
<Text variant="footnoteMini" {...props}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Text } from './Text';
import { TextProps } from './Text';

export type LabelProps = TextProps & React.LabelHTMLAttributes<HTMLLabelElement>;
export type LabelProps = TextProps<'label'>;

export const Label = ({ children, ...props }: LabelProps) => {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { KnownTarget } from 'styled-components/dist/types';

import { Text, TextProps } from './Text';
export type LabelTinyProps = Omit<TextProps, 'variant'>;
export type LabelTinyProps = Omit<TextProps<KnownTarget>, 'variant'>;

export const LabelTiny = ({ children, ...props }: LabelTinyProps) => (
<Text variant="labelTiny" {...props}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import React, { useCallback } from 'react';
import styled from 'styled-components';
import { KnownTarget } from 'styled-components/dist/types';

import { Colors, Radius } from '../../foundations';
import { useHistory } from '../../history';
import { RoutePath } from '../../routes';
import { buttonReset } from '../../styles';
import { Text, TextProps } from './Text';

export interface LinkProps extends TextProps, Omit<React.HtmlHTMLAttributes<'button'>, 'color'> {
to: RoutePath;
}
export type LinkProps<T extends KnownTarget = 'a'> = TextProps<T> & {
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
};

const StyledText = styled(Text)<{
$hoverColor: Colors | undefined;
}>((props) => ({
...buttonReset,
background: 'transparent',
cursor: 'default',
textDecoration: 'none',
display: 'inline-flex',
width: 'fit-content',

'&:hover': {
'&&:hover': {
textDecorationLine: 'underline',
textUnderlineOffset: '2px',
color: props.$hoverColor,
},
'&:focus-visible': {
'&&:focus-visible': {
borderRadius: Radius.radius4,
outline: `2px solid ${Colors.white}`,
outlineOffset: '2px',
Expand All @@ -38,24 +38,14 @@ const getHoverColor = (color: Colors | undefined) => {
}
};

export const Link = ({ to, children, color, onClick, ...props }: LinkProps) => {
const history = useHistory();
const navigate = useCallback(
(e: React.MouseEvent<'button'>) => {
if (onClick) {
onClick(e);
}
return history.push(to);
},
[history, to, onClick],
);
export const Link = <T extends KnownTarget = 'a'>({
as = 'a' as T,
children,
color,
...props
}: LinkProps<T>) => {
return (
<StyledText
onClick={navigate}
as={'button'}
color={color}
$hoverColor={getHoverColor(color)}
{...props}>
<StyledText forwardedAs={as} color={color} $hoverColor={getHoverColor(color)} {...props}>
{children}
</StyledText>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,50 @@
import { createElement, forwardRef } from 'react';
import styled, { WebTarget } from 'styled-components';
import React from 'react';
import styled, { PolymorphicComponentProps } from 'styled-components';
import { KnownTarget } from 'styled-components/dist/types';

import { Colors, Typography, typography, TypographyProperties } from '../../foundations';
import { Colors, Typography, typography } from '../../foundations';
import { TransientProps } from '../../types';

export type TextProps = React.PropsWithChildren<{
type TextBaseProps = {
variant?: Typography;
color?: Colors;
tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';
as?: WebTarget;
style?: React.CSSProperties;
}>;
children?: React.ReactNode;
};

const StyledText = styled(
({ tag = 'span', ...props }: { tag: TextProps['tag'] } & TransientProps<TypographyProperties>) =>
createElement(tag, props),
)((props) => ({
color: 'var(--color)',
fontFamily: props.$fontFamily,
fontWeight: props.$fontWeight,
fontSize: props.$fontSize,
lineHeight: props.$lineHeight,
}));
export type TextProps<T extends KnownTarget = 'span'> = PolymorphicComponentProps<
'web',
TextBaseProps,
T,
T
>;

export const Text = forwardRef(
(
{
tag = 'span',
variant = 'bodySmall',
color = Colors.white,
children,
style,
...props
}: TextProps,
ref,
) => {
const { fontFamily, fontSize, fontWeight, lineHeight } = typography[variant];
return (
<StyledText
ref={ref}
tag={tag}
style={
{
'--color': color,
...style,
} as React.CSSProperties
}
$fontFamily={fontFamily}
$fontWeight={fontWeight}
$fontSize={fontSize}
$lineHeight={lineHeight}
{...props}>
{children}
</StyledText>
);
const StyledText = styled.span<TransientProps<TextBaseProps>>(
({ $variant = 'bodySmall', $color = Colors.white }) => {
const { fontFamily, fontSize, fontWeight, lineHeight } = typography[$variant];
return `
--color: ${$color};

color: var(--color);
font-family: ${fontFamily};
font-size: ${fontSize};
font-weight: ${fontWeight};
line-height: ${lineHeight};
`;
},
);

export const Text = <T extends KnownTarget = 'span'>({
variant,
color,
children,
style,
...props
}: TextProps<T>) => {
return (
<StyledText $variant={variant} $color={color} {...props}>
{children}
</StyledText>
);
};

Text.displayName = 'Text';
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { KnownTarget } from 'styled-components/dist/types';

import { Text, TextProps } from './Text';
export type TitleBigProps = Omit<TextProps, 'variant'>;
export type TitleBigProps<T extends KnownTarget> = TextProps<T>;

export const TitleBig = ({ children, ...props }: TitleBigProps) => (
<Text variant="titleBig" {...props}>
{children}
</Text>
export const TitleBig = <T extends KnownTarget>({ ...props }: TitleBigProps<T>) => (
<Text variant="titleBig" {...props} />
);
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { KnownTarget } from 'styled-components/dist/types';

import { Text, TextProps } from './Text';
export type TitleLargeProps = Omit<TextProps, 'variant'>;
export type TitleLargeProps = Omit<TextProps<KnownTarget>, 'variant'>;

export const TitleLarge = ({ children, ...props }: TitleLargeProps) => (
<Text variant="titleLarge" {...props}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { KnownTarget } from 'styled-components/dist/types';

import { Text, TextProps } from './Text';
export type TitleMediumProps = Omit<TextProps, 'variant'>;
export type TitleMediumProps = Omit<TextProps<KnownTarget>, 'variant'>;

export const TitleMedium = ({ children, ...props }: TitleMediumProps) => (
<Text variant="titleMedium" {...props}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class NewVersionNotificationProvider implements InAppNotificationProvider
title,
subtitle,
subtitleAction: {
type: 'navigate',
type: 'navigate-internal',
link: {
to: RoutePath.changelog,
onClick: this.context.close,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LinkProps } from '../../renderer/lib/components';
import { InternalLinkProps } from '../../renderer/components/InternalLink';
import { Url } from '../constants';

export type NotificationAction = {
Expand Down Expand Up @@ -31,8 +31,8 @@ export type InAppNotificationAction =
close: () => void;
}
| {
type: 'navigate';
link: Pick<LinkProps, 'to' | 'onClick' | 'aria-label'>;
type: 'navigate-internal';
link: Pick<InternalLinkProps, 'to' | 'onClick' | 'aria-label'>;
};

export type InAppNotificationIndicatorType = 'success' | 'warning' | 'error';
Expand Down