summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-05-27 21:34:13 +0200
committerTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-05-27 21:34:13 +0200
commitfb07661ff91ecfb45e25361a3aee9a0d8430dcc1 (patch)
treed94ee5ce03fbf3c79c4c72a7165d8d2105c30abb
parent2a28c60c910976f5d18e6d56bf6224bf39b2fc5b (diff)
parent235949cde68308c97ec3c59fc539b8f62b25297d (diff)
downloadmullvadvpn-fb07661ff91ecfb45e25361a3aee9a0d8430dcc1.tar.xz
mullvadvpn-fb07661ff91ecfb45e25361a3aee9a0d8430dcc1.zip
Merge branch 'fix-link-icon-alignment-when-spanning-multiple-lines-des-2170'
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ExternalLink.tsx11
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/InternalLink.tsx11
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/NotificationSubtitle.tsx15
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx12
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/link/Link.tsx94
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/link/LinkContext.tsx28
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkIcon.tsx5
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkText.tsx13
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/link/hooks/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/link/hooks/use-hover-color.ts10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/link/index.ts1
12 files changed, 130 insertions, 72 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ExternalLink.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ExternalLink.tsx
index 0acba13623..4f160ad64d 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ExternalLink.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ExternalLink.tsx
@@ -1,19 +1,13 @@
import { useCallback } from 'react';
-import styled from 'styled-components';
import { Url } from '../../shared/constants';
import { useAppContext } from '../context';
import { Link, LinkProps } from '../lib/components';
-export type ExternalLinkProps = Omit<LinkProps<'a'>, 'href' | 'as'> & {
+export type ExternalLinkProps = Omit<LinkProps, 'href' | 'as'> & {
to: Url;
};
-const StyledLink = styled(Link)`
- display: inline-flex;
- width: fit-content;
-`;
-
function ExternalLink({ to, onClick, ...props }: ExternalLinkProps) {
const { openUrl } = useAppContext();
const navigate = useCallback(
@@ -26,10 +20,11 @@ function ExternalLink({ to, onClick, ...props }: ExternalLinkProps) {
},
[onClick, openUrl, to],
);
- return <StyledLink href="" onClick={navigate} {...props} />;
+ return <Link href="" onClick={navigate} {...props} />;
}
const ExternalLinkNamespace = Object.assign(ExternalLink, {
+ Text: Link.Text,
Icon: Link.Icon,
});
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/InternalLink.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/InternalLink.tsx
index 70f8b7c496..ba6b32280e 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/InternalLink.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/InternalLink.tsx
@@ -4,11 +4,11 @@ import { Link, LinkProps } from '../lib/components';
import { useHistory } from '../lib/history';
import { RoutePath } from '../lib/routes';
-export type InternalLinkProps = Omit<LinkProps<'a'>, 'href' | 'as'> & {
+export type InternalLinkProps = Omit<LinkProps, 'href' | 'as'> & {
to: RoutePath;
};
-export function InternalLink({ to, onClick, ...props }: InternalLinkProps) {
+function InternalLink({ to, onClick, ...props }: InternalLinkProps) {
const history = useHistory();
const navigate = useCallback(
(e: React.MouseEvent<HTMLAnchorElement>) => {
@@ -22,3 +22,10 @@ export function InternalLink({ to, onClick, ...props }: InternalLinkProps) {
);
return <Link href="" onClick={navigate} {...props} />;
}
+
+const InternalLinkNamespace = Object.assign(InternalLink, {
+ Text: Link.Text,
+ Icon: Link.Icon,
+});
+
+export { InternalLinkNamespace as InternalLink };
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationSubtitle.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationSubtitle.tsx
index 0a43452513..71045a2bfd 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationSubtitle.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationSubtitle.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-import styled from 'styled-components';
import { InAppNotificationSubtitle } from '../../shared/notifications';
import { LabelTiny } from '../lib/components';
@@ -11,10 +10,6 @@ export type NotificationSubtitleProps = {
subtitle?: string | InAppNotificationSubtitle[];
};
-const StyledExternalLink = styled(ExternalLink)`
- display: flex;
-`;
-
const formatSubtitle = (subtitle: InAppNotificationSubtitle) => {
const content = formatHtml(subtitle.content);
if (subtitle.action) {
@@ -22,15 +17,15 @@ const formatSubtitle = (subtitle: InAppNotificationSubtitle) => {
case 'navigate-internal':
return (
<InternalLink variant="labelTiny" {...subtitle.action.link}>
- {content}
+ <InternalLink.Text>{content}</InternalLink.Text>
</InternalLink>
);
case 'navigate-external':
return (
- <StyledExternalLink variant="labelTiny" {...subtitle.action.link}>
- {content}
- <ExternalLink.Icon icon="external" size="small" />
- </StyledExternalLink>
+ <ExternalLink variant="labelTiny" {...subtitle.action.link}>
+ <ExternalLink.Text>{content}</ExternalLink.Text>
+ <ExternalLink.Icon icon="external" />
+ </ExternalLink>
);
default:
break;
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx
index 3de932bc86..9e3acf985e 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/VpnSettings.tsx
@@ -758,11 +758,13 @@ function TunnelProtocolSetting() {
</Cell.CellFooterText>
</AriaDescription>
<ExternalLink variant="labelTiny" to={urls.removingOpenVpnBlog}>
- {sprintf(
- // TRANSLATORS: Link in tunnel protocol selector footer to blog post
- // TRANSLATORS: about OpenVPN support ending.
- messages.pgettext('vpn-settings-view', 'Read more'),
- )}
+ <ExternalLink.Text>
+ {sprintf(
+ // TRANSLATORS: Link in tunnel protocol selector footer to blog post
+ // TRANSLATORS: about OpenVPN support ending.
+ messages.pgettext('vpn-settings-view', 'Read more'),
+ )}
+ </ExternalLink.Text>
<ExternalLink.Icon icon="external" size="small" />
</ExternalLink>
</Cell.CellFooter>
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/Link.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/Link.tsx
index 40a598b666..5e1bef8d5f 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/Link.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/Link.tsx
@@ -1,62 +1,66 @@
import React from 'react';
-import styled from 'styled-components';
+import styled, { css } from 'styled-components';
-import { Colors, colors, Radius } from '../../foundations';
-import { Text, TextProps } from '../typography';
-import { LinkIcon } from './components';
+import { Colors, colors, Radius, Typography } from '../../foundations';
+import { TransientProps } from '../../types';
+import { LinkIcon, LinkText, StyledIcon as StyledLinkIcon, StyledLinkText } from './components';
+import { useHoverColor } from './hooks';
+import { LinkProvider } from './LinkContext';
-export type LinkProps<T extends React.ElementType = 'a'> = TextProps<T> & {
+type LinkBaseProps = {
+ variant?: Typography;
+ color?: Colors;
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
};
-const StyledText = styled(Text)<{
- $hoverColor: Colors | undefined;
-}>((props) => ({
- background: colors.transparent,
- cursor: 'default',
- textDecoration: 'none',
- display: 'inline',
+export type LinkProps = LinkBaseProps &
+ Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkBaseProps>;
- '&&:hover': {
- textDecorationLine: 'underline',
- textUnderlineOffset: '2px',
- color: props.$hoverColor,
- },
- '&&:focus-visible': {
- borderRadius: Radius.radius4,
- outline: `2px solid ${colors.whiteAlpha60}`,
- outlineOffset: '2px',
- },
-}));
-
-const getHoverColor = (color: Colors | undefined) => {
- switch (color) {
- case 'whiteAlpha60':
- return 'white';
- default:
- return undefined;
+const StyledLink = styled.a<
+ TransientProps<LinkProps> & {
+ $hoverColor?: Colors;
}
-};
+>(({ $hoverColor }) => {
+ return css`
+ cursor: default;
+ text-decoration: none;
+ display: inline;
+ width: fit-content;
+
+ &&:hover > ${StyledLinkText} {
+ text-decoration-line: underline;
+ text-underline-offset: 2px;
+ color: ${$hoverColor};
+ }
+
+ &&:focus-visible > ${StyledLinkText} {
+ border-radius: ${Radius.radius4};
+ outline: 2px solid ${colors.white};
+ outline-offset: 2px;
+ }
+
+ > ${StyledLinkIcon}:first-child:not(:only-child) {
+ margin-right: 2px;
+ }
+ > ${StyledLinkIcon}:last-child:not(:only-child) {
+ margin-left: 2px;
+ }
+ `;
+});
-function Link<T extends React.ElementType = 'a'>({
- as: forwardedAs,
- color,
- ...props
-}: LinkProps<T>) {
- // If `as` is provided we need to pass it as `forwardedAs` for it to
- // be correctly passed to the `Text` component.
- const componentProps = forwardedAs ? { ...props, forwardedAs } : props;
+function Link({ color, variant, children, ...props }: LinkProps) {
+ const hoverColor = useHoverColor(color);
return (
- <StyledText
- forwardedAs="a"
- color={color}
- $hoverColor={getHoverColor(color)}
- {...componentProps}
- />
+ <LinkProvider variant={variant} color={color}>
+ <StyledLink $hoverColor={hoverColor} {...props}>
+ {children}
+ </StyledLink>
+ </LinkProvider>
);
}
const LinkNamespace = Object.assign(Link, {
+ Text: LinkText,
Icon: LinkIcon,
});
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/LinkContext.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/LinkContext.tsx
new file mode 100644
index 0000000000..128428c081
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/LinkContext.tsx
@@ -0,0 +1,28 @@
+import React from 'react';
+
+import { LinkProps } from './Link';
+
+interface LinkContextProps {
+ color?: LinkProps['color'];
+ variant?: LinkProps['variant'];
+}
+
+const LinkContext = React.createContext<LinkContextProps | undefined>(undefined);
+
+export const useLinkContext = (): LinkContextProps => {
+ const context = React.useContext(LinkContext);
+ if (!context) {
+ throw new Error('useLinkContext must be used within a LinkProvider');
+ }
+ return context;
+};
+
+interface LinkProviderProps {
+ color?: LinkContextProps['color'];
+ variant?: LinkContextProps['variant'];
+ children: React.ReactNode;
+}
+
+export function LinkProvider({ color, variant, children }: LinkProviderProps) {
+ return <LinkContext.Provider value={{ color, variant }}>{children}</LinkContext.Provider>;
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkIcon.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkIcon.tsx
index 7af4b28045..3a82f11a4d 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkIcon.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkIcon.tsx
@@ -5,8 +5,9 @@ import { Icon, IconProps } from '../../icon';
type LinkIconProps = IconProps;
export const StyledIcon = styled(Icon)`
- vertical-align: middle;
- display: inline-flex;
+ vertical-align: text-bottom;
+ display: inline-block;
+ flex-shrink: 0;
`;
export function LinkIcon({ ...props }: LinkIconProps) {
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkText.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkText.tsx
new file mode 100644
index 0000000000..c8c1ff8baa
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/LinkText.tsx
@@ -0,0 +1,13 @@
+import styled from 'styled-components';
+
+import { Text, TextProps } from '../../typography';
+import { useLinkContext } from '../LinkContext';
+
+export type LinkTextProps = TextProps;
+
+export const StyledLinkText = styled(Text)``;
+
+export function LinkText(props: LinkTextProps) {
+ const { variant, color } = useLinkContext();
+ return <StyledLinkText variant={variant} color={color} {...props}></StyledLinkText>;
+}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/index.ts
index 1718f57d55..efe33915d2 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/index.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/components/index.ts
@@ -1 +1,2 @@
export * from './LinkIcon';
+export * from './LinkText';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/hooks/index.ts
new file mode 100644
index 0000000000..4f17d940a4
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/hooks/index.ts
@@ -0,0 +1 @@
+export * from './use-hover-color';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/hooks/use-hover-color.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/hooks/use-hover-color.ts
new file mode 100644
index 0000000000..f62f26a6fe
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/hooks/use-hover-color.ts
@@ -0,0 +1,10 @@
+import { Colors } from '../../../foundations';
+
+export const useHoverColor = (color: Colors | undefined) => {
+ switch (color) {
+ case 'whiteAlpha60':
+ return 'white';
+ default:
+ return undefined;
+ }
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/index.ts
new file mode 100644
index 0000000000..3db78f51f0
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/link/index.ts
@@ -0,0 +1 @@
+export * from './Link';