summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ClipboardLabel.tsx12
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ContextMenu.tsx6
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/CustomDnsSettings.tsx10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/DeviceInfoButton.tsx7
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/InfoButton.tsx6
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx8
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/PageSlider.tsx18
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/SearchBar.tsx4
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/SettingsTextImport.tsx9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx6
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderAccountButton.tsx8
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderSettingsButton.tsx10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/app-navigation-header/components/AppNavigationHeaderBackButton.tsx9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx9
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionPanel.tsx6
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx6
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx71
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButtonContext.tsx24
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/IconButtonIcon.tsx43
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/index.ts1
21 files changed, 167 insertions, 120 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ClipboardLabel.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ClipboardLabel.tsx
index adc3ef8c1c..c7c675f584 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ClipboardLabel.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ClipboardLabel.tsx
@@ -55,7 +55,6 @@ export default function ClipboardLabel(props: IProps) {
<Flex $gap={Spacings.spacing5}>
{obscureValue !== false && (
<IconButton
- icon={obscured ? 'show' : 'hide'}
onClick={toggleObscured}
aria-label={
obscured
@@ -67,21 +66,22 @@ export default function ClipboardLabel(props: IProps) {
// TRANSLATORS: Provided to accessibility tools such as screenreaders to describe
// TRANSLATORS: the button which obscures the account number.
messages.pgettext('accessibility', 'Hide account number')
- }
- />
+ }>
+ {obscured ? <IconButton.Icon icon="show" /> : <IconButton.Icon icon="hide" />}
+ </IconButton>
)}
{justCopied ? (
<Icon icon="checkmark" color={Colors.green}></Icon>
) : (
<IconButton
- icon={'copy'}
onClick={onCopy}
aria-label={
// TRANSLATORS: Provided to accessibility tools such as screenreaders to describe a button
// TRANSLATORS: which copies the account number to the clipboard.
messages.pgettext('accessibility', 'Copy account number')
- }
- />
+ }>
+ <IconButton.Icon icon={'copy'} />
+ </IconButton>
)}
</Flex>
</StyledLabelContainer>
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ContextMenu.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ContextMenu.tsx
index c3d1e4845b..24a78472a2 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ContextMenu.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ContextMenu.tsx
@@ -90,7 +90,11 @@ export function ContextMenuContainer(props: React.PropsWithChildren) {
export function ContextMenuTrigger() {
const { toggleVisibility } = useContext(menuContext);
- return <IconButton icon="more-horizontal-circle" onClick={toggleVisibility} />;
+ return (
+ <IconButton onClick={toggleVisibility}>
+ <IconButton.Icon icon="more-horizontal-circle" />
+ </IconButton>
+ );
}
interface StyledMenuProps {
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/CustomDnsSettings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/CustomDnsSettings.tsx
index f264806aae..1be8d0be2e 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/CustomDnsSettings.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/CustomDnsSettings.tsx
@@ -251,7 +251,9 @@ export default function CustomDnsSettings() {
{messages.pgettext('vpn-settings-view', 'Add a server')}
</StyledAddCustomDnsLabel>
</StyledButton>
- <IconButton variant="secondary" icon="add-circle" onClick={showInput} />
+ <IconButton variant="secondary" onClick={showInput}>
+ <IconButton.Icon icon="add-circle" />
+ </IconButton>
</AddServerContainer>
</Accordion>
@@ -356,11 +358,11 @@ function CellListItem(props: ICellListItemProps) {
</StyledButton>
<AriaDescribed>
<IconButton
- icon="cross-circle"
variant="secondary"
onClick={onRemove}
- aria-label={messages.pgettext('accessibility', 'Remove item')}
- />
+ aria-label={messages.pgettext('accessibility', 'Remove item')}>
+ <IconButton.Icon icon="cross-circle" />
+ </IconButton>
</AriaDescribed>
</StyledItemContainer>
)}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/DeviceInfoButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/DeviceInfoButton.tsx
index fa1be49127..6100fe292c 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/DeviceInfoButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/DeviceInfoButton.tsx
@@ -10,10 +10,11 @@ export default function DeviceInfoButton() {
return (
<>
<IconButton
- icon="info-circle"
+ size="small"
onClick={showDeviceHelp}
- aria-label={messages.pgettext('accessibility', 'More information')}
- />
+ aria-label={messages.pgettext('accessibility', 'More information')}>
+ <IconButton.Icon icon="info-circle" />
+ </IconButton>
<ModalAlert
isOpen={deviceHelpVisible}
type={ModalAlertType.info}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/InfoButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/InfoButton.tsx
index 6b89a6c2f2..4c4b991829 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/InfoButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/InfoButton.tsx
@@ -15,11 +15,11 @@ export default function InfoButton({ title, message, children, ...props }: InfoB
return (
<>
<IconButton
- icon="info-circle"
onClick={show}
aria-label={messages.pgettext('accessibility', 'More information')}
- {...props}
- />
+ {...props}>
+ <IconButton.Icon icon="info-circle" />
+ </IconButton>
<ModalAlert
isOpen={isOpen}
title={title}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx
index 0ae7a65da4..9ce77d8e78 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationBanner.tsx
@@ -73,12 +73,12 @@ export function NotificationCloseAction(props: NotificationActionProps) {
return (
<IconButton
aria-describedby={NOTIFICATION_AREA_ID}
+ variant="secondary"
aria-label={messages.pgettext('accessibility', 'Close notification')}
onClick={props.onClick}
- icon="cross-circle"
- variant="secondary"
- size="small"
- />
+ size="small">
+ <IconButton.Icon icon="cross-circle" />
+ </IconButton>
);
}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/PageSlider.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/PageSlider.tsx
index f029c890d8..897297a243 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/PageSlider.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/PageSlider.tsx
@@ -185,18 +185,12 @@ function Controls(props: ControlsProps) {
))}
</StyledPageIndicators>
<StyledArrows>
- <IconButton
- icon="chevron-left"
- size="medium"
- disabled={!props.hasPrev}
- onClick={props.prev}
- />
- <IconButton
- icon="chevron-right"
- size="medium"
- disabled={!props.hasNext}
- onClick={props.next}
- />
+ <IconButton disabled={!props.hasPrev} onClick={props.prev}>
+ <IconButton.Icon icon="chevron-left" />
+ </IconButton>
+ <IconButton disabled={!props.hasNext} onClick={props.next}>
+ <IconButton.Icon icon="chevron-right" />
+ </IconButton>
</StyledArrows>
</StyledControlsContainer>
);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/SearchBar.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SearchBar.tsx
index 9a730ebb2c..5d4a672676 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/SearchBar.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/SearchBar.tsx
@@ -98,7 +98,9 @@ export default function SearchBar(props: ISearchBarProps) {
/>
<StyledSearchIcon icon="search" color={Colors.white60} />
{props.searchTerm.length > 0 && (
- <StyledClearButton icon="cross-circle" variant="secondary" onClick={onClear} />
+ <StyledClearButton variant="secondary" onClick={onClear}>
+ <IconButton.Icon icon="cross-circle" />
+ </StyledClearButton>
)}
</StyledSearchContainer>
);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/SettingsTextImport.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SettingsTextImport.tsx
index 7b2fc60cc2..70d774d702 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/SettingsTextImport.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/SettingsTextImport.tsx
@@ -3,6 +3,7 @@ import styled from 'styled-components';
import { messages } from '../../shared/gettext';
import useActions from '../lib/actionsHook';
+import { IconButton } from '../lib/components';
import { Colors } from '../lib/foundations';
import { useHistory } from '../lib/history';
import { useCombinedRefs, useRefCallback, useStyledRef } from '../lib/utility-hooks';
@@ -59,11 +60,9 @@ export default function SettingsTextImport() {
messages.pgettext('settings-import', 'Import via text')
}
titleVisible>
- <AppNavigationHeader.IconButton
- icon="checkmark"
- onClick={save}
- aria-label={messages.gettext('Save')}
- />
+ <AppNavigationHeader.IconButton onClick={save} aria-label={messages.gettext('Save')}>
+ <IconButton.Icon icon="checkmark" />
+ </AppNavigationHeader.IconButton>
</AppNavigationHeader>
<StyledTextArea ref={combinedTextAreaRef} />
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx
index 9257c1f8bb..f91504b746 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx
@@ -651,11 +651,19 @@ function ApplicationRow(props: IApplicationRowProps) {
<StyledCellLabel>{props.application.name}</StyledCellLabel>
<Flex $gap={Spacings.spacing3}>
{props.onDelete && (
- <IconButton icon="cross-circle" variant="secondary" onClick={onDelete} />
+ <IconButton variant="secondary" onClick={onDelete}>
+ <IconButton.Icon icon="cross-circle" />
+ </IconButton>
+ )}
+ {props.onAdd && (
+ <IconButton variant="secondary" onClick={onAdd}>
+ <IconButton.Icon icon="add-circle" />
+ </IconButton>
)}
- {props.onAdd && <IconButton icon="add-circle" variant="secondary" onClick={onAdd} />}
{props.onRemove && (
- <IconButton icon="remove-circle" variant="secondary" onClick={onRemove} />
+ <IconButton variant="secondary" onClick={onRemove}>
+ <IconButton.Icon icon="remove-circle" />
+ </IconButton>
)}
</Flex>
</StyledContainer>
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx
index 75460290c6..a020980def 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx
@@ -235,7 +235,6 @@ function Device(props: IDeviceProps) {
<Spinner />
) : (
<IconButton
- icon="cross-circle"
variant="secondary"
onClick={showConfirmation}
aria-label={sprintf(
@@ -245,8 +244,9 @@ function Device(props: IDeviceProps) {
// TRANSLATORS: %(deviceName)s - The device name to remove.
messages.pgettext('accessibility', 'Remove device named %(deviceName)s'),
{ deviceName: props.device.name },
- )}
- />
+ )}>
+ <IconButton.Icon icon="cross-circle" />
+ </IconButton>
)}
</Cell.Container>
<ModalAlert
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderAccountButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderAccountButton.tsx
index be10808816..7eee31e980 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderAccountButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderAccountButton.tsx
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { messages } from '../../../../shared/gettext';
-import { IconButtonProps, MainHeader } from '../../../lib/components';
+import { IconButton, IconButtonProps, MainHeader } from '../../../lib/components';
import { transitions, useHistory } from '../../../lib/history';
import { RoutePath } from '../../../lib/routes';
import { useSelector } from '../../../redux/store';
@@ -22,11 +22,11 @@ export const AppMainHeaderBarAccountButton = (props: MainHeaderBarAccountButtonP
return (
<MainHeader.IconButton
- icon="account-circle"
onClick={openAccount}
data-testid="account-button"
aria-label={messages.gettext('Account settings')}
- {...props}
- />
+ {...props}>
+ <IconButton.Icon icon="account-circle" />
+ </MainHeader.IconButton>
);
};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderSettingsButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderSettingsButton.tsx
index 9ecaab479e..f7bb352b04 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderSettingsButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/app-main-header/components/AppMainHeaderSettingsButton.tsx
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { messages } from '../../../../shared/gettext';
-import { IconButtonProps, MainHeader } from '../../../lib/components';
+import { IconButton, IconButtonProps, MainHeader } from '../../../lib/components';
import { transitions, useHistory } from '../../../lib/history';
import { RoutePath } from '../../../lib/routes';
@@ -17,10 +17,8 @@ export function AppMainHeaderSettingsButton(props: MainHeaderSettingsButtonProps
}, [history, props.disabled]);
return (
- <MainHeader.IconButton
- icon="settings-filled"
- onClick={openSettings}
- aria-label={messages.gettext('Settings')}
- />
+ <MainHeader.IconButton onClick={openSettings} aria-label={messages.gettext('Settings')}>
+ <IconButton.Icon icon="settings-filled" />{' '}
+ </MainHeader.IconButton>
);
}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/app-navigation-header/components/AppNavigationHeaderBackButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/app-navigation-header/components/AppNavigationHeaderBackButton.tsx
index 13e26f3444..73bcd69d46 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/app-navigation-header/components/AppNavigationHeaderBackButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/app-navigation-header/components/AppNavigationHeaderBackButton.tsx
@@ -21,11 +21,8 @@ export const AppNavigationHeaderBackButton = () => {
const ariaLabel = backIcon ? messages.gettext('Back') : messages.gettext('Close');
return (
- <IconButton
- variant="secondary"
- icon={iconSource}
- aria-label={ariaLabel}
- onClick={parentBackAction}
- />
+ <IconButton variant="secondary" aria-label={ariaLabel} onClick={parentBackAction}>
+ <IconButton.Icon icon={iconSource} />
+ </IconButton>
);
};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx
index c3fe16002f..7d219a533f 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx
@@ -399,12 +399,9 @@ export function RowInput(props: IRowInputProps) {
placeholder={props.placeholder}
/>
</StyledInputWrapper>
- <StyledIconButton
- variant="secondary"
- icon="checkmark-circle"
- onClick={submit}
- $disabled={value === ''}
- />
+ <StyledIconButton variant="secondary" onClick={submit} $disabled={value === ''}>
+ <IconButton.Icon icon="checkmark-circle" />
+ </StyledIconButton>
</StyledCellInputRowContainer>
</BackAction>
);
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionPanel.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionPanel.tsx
index f6d3af51ed..8a5685b35e 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionPanel.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/main-view/ConnectionPanel.tsx
@@ -97,10 +97,10 @@ export default function ConnectionPanel() {
<StyledConnectionPanel $expanded={expanded}>
{allowExpand && (
<StyledConnectionPanelChevron
- icon={expanded ? 'chevron-down' : 'chevron-up'}
onClick={toggleExpanded}
- data-testid="connection-panel-chevron"
- />
+ data-testid="connection-panel-chevron">
+ <IconButton.Icon icon={expanded ? 'chevron-down' : 'chevron-up'} />
+ </StyledConnectionPanelChevron>
)}
<StyledConnectionStatusContainer
$expanded={expanded}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx
index 549bba93c1..069197bb81 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx
@@ -133,11 +133,11 @@ export default function SelectLocation() {
}
titleVisible>
<IconButton
- icon="filter-circle"
variant="secondary"
onClick={onViewFilter}
- aria-label={messages.gettext('Filter')}
- />
+ aria-label={messages.gettext('Filter')}>
+ <IconButton.Icon icon="filter-circle" />
+ </IconButton>
</AppNavigationHeader>
<StyledNavigationBarAttachment>
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx
index 9bc022af8a..819151706a 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx
@@ -3,28 +3,15 @@ import styled from 'styled-components';
import { Colors } from '../../foundations';
import { buttonReset } from '../../styles';
-import { Icon, IconProps } from '../icon/Icon';
+import { IconProps } from '../icon/Icon';
+import { IconButtonIcon } from './components/IconButtonIcon';
+import { IconButtonProvider } from './IconButtonContext';
-export interface IconButtonProps
- extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
+export interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary';
size?: IconProps['size'];
- icon: IconProps['icon'];
}
-const variants = {
- primary: {
- background: Colors.white,
- hover: Colors.white60,
- disabled: Colors.white50,
- },
- secondary: {
- background: Colors.white60,
- hover: Colors.white80,
- disabled: Colors.white50,
- },
-} as const;
-
const sizes = {
tiny: 12,
small: 16,
@@ -46,41 +33,31 @@ const StyledButton = styled.button({
},
});
-const StyledIcon = styled(Icon)<IconProps & { $hoverColor: string; $disabled?: boolean }>(
- ({ $hoverColor, $disabled }) => ({
- ...(!$disabled && {
- '&&:hover': {
- backgroundColor: $hoverColor,
- },
- }),
- }),
-);
-
-export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
- ({ icon, variant = 'primary', size: sizeProp = 'medium', disabled, style, ...props }, ref) => {
- const styles = variants[variant];
+const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
+ ({ variant = 'primary', size: sizeProp = 'medium', disabled, style, ...props }, ref) => {
const size = sizes[sizeProp];
return (
- <StyledButton
- ref={ref}
- disabled={disabled}
- style={
- {
- '--size': `${size}px`,
- ...style,
- } as React.CSSProperties
- }
- {...props}>
- <StyledIcon
- icon={icon}
- color={disabled ? styles.disabled : styles.background}
- size={sizeProp}
- $hoverColor={styles.hover}
- $disabled={disabled}
+ <IconButtonProvider size={sizeProp} variant={variant} disabled={disabled}>
+ <StyledButton
+ ref={ref}
+ disabled={disabled}
+ style={
+ {
+ '--size': `${size}px`,
+ ...style,
+ } as React.CSSProperties
+ }
+ {...props}
/>
- </StyledButton>
+ </IconButtonProvider>
);
},
);
+const IconButtonNamespace = Object.assign(IconButton, {
+ Icon: IconButtonIcon,
+});
+
+export { IconButtonNamespace as IconButton };
+
IconButton.displayName = 'Button';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButtonContext.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButtonContext.tsx
new file mode 100644
index 0000000000..ebe100b1d5
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButtonContext.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+
+import { IconButtonProps } from './IconButton';
+interface IconButtonContextProps {
+ disabled?: boolean;
+ variant: IconButtonProps['variant'];
+ size: IconButtonProps['size'];
+}
+
+const IconButtonContext = React.createContext<IconButtonContextProps | undefined>(undefined);
+
+export const useIconButtonContext = (): IconButtonContextProps => {
+ const context = React.useContext(IconButtonContext);
+ if (!context) {
+ throw new Error('useButtonContext must be used within a IconButtonProvider');
+ }
+ return context;
+};
+interface IconButtonProviderProps extends IconButtonContextProps {
+ children: React.ReactNode;
+}
+export const IconButtonProvider = ({ children, ...props }: IconButtonProviderProps) => {
+ return <IconButtonContext.Provider value={props}>{children}</IconButtonContext.Provider>;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/IconButtonIcon.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/IconButtonIcon.tsx
new file mode 100644
index 0000000000..a65d887469
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/IconButtonIcon.tsx
@@ -0,0 +1,43 @@
+import styled from 'styled-components';
+
+import { Colors } from '../../../foundations';
+import { Icon, IconProps } from '../../icon/Icon';
+import { useIconButtonContext } from '../IconButtonContext';
+export type IconButtonIconProps = IconProps;
+
+const variants = {
+ primary: {
+ background: Colors.white,
+ hover: Colors.white60,
+ disabled: Colors.white50,
+ },
+ secondary: {
+ background: Colors.white60,
+ hover: Colors.white80,
+ disabled: Colors.white50,
+ },
+} as const;
+
+const StyledIcon = styled(Icon)<IconButtonIconProps & { $hoverColor: string; $disabled?: boolean }>(
+ ({ $hoverColor, $disabled }) => ({
+ ...(!$disabled && {
+ '&&:hover': {
+ backgroundColor: $hoverColor,
+ },
+ }),
+ }),
+);
+
+export const IconButtonIcon = (props: IconButtonIconProps) => {
+ const { variant = 'primary', size, disabled } = useIconButtonContext();
+ const styles = variants[variant];
+ return (
+ <StyledIcon
+ color={disabled ? styles.disabled : styles.background}
+ size={size}
+ $hoverColor={styles.hover}
+ $disabled={disabled}
+ {...props}
+ />
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/index.ts
index 1a85f0f725..cfe09746a9 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/index.ts
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/index.ts
@@ -1 +1,2 @@
export * from './IconButton';
+export * from './IconButtonContext';