generated from MetaMask/metamask-module-template
-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of github.com:MetaMask/metamask-design-system int…
…o dsrn/avatar-favicon
- Loading branch information
Showing
21 changed files
with
802 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
packages/design-system-react-native/src/components/AvatarIcon/AvatarIcon.constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import type { AvatarIconProps } from './AvatarIcon.types'; | ||
import { AvatarIconSeverity } from './AvatarIcon.types'; | ||
import { IconSize, IconColor } from '../Icon'; | ||
import { AvatarIconSize, AvatarBaseShape } from '../../shared/enums'; | ||
|
||
// Mappings | ||
export const TWCLASSMAP_AVATARICON_SEVERITY_BACKGROUNDCOLOR: Record< | ||
AvatarIconSeverity, | ||
string | ||
> = { | ||
[AvatarIconSeverity.Default]: 'bg-background-muted', | ||
[AvatarIconSeverity.Info]: 'bg-info-muted', | ||
[AvatarIconSeverity.Success]: 'bg-success-muted', | ||
[AvatarIconSeverity.Error]: 'bg-error-muted', | ||
[AvatarIconSeverity.Warning]: 'bg-warning-muted', | ||
}; | ||
export const MAP_AVATARICON_SIZE_ICONSIZE: Record<AvatarIconSize, IconSize> = { | ||
[AvatarIconSize.Xs]: IconSize.Xs, // 16px avatar -> 12px icon | ||
[AvatarIconSize.Sm]: IconSize.Sm, // 24px avatar -> 16px icon | ||
[AvatarIconSize.Md]: IconSize.Md, // 32px avatar -> 20px icon | ||
[AvatarIconSize.Lg]: IconSize.Lg, // 40px avatar -> 24px icon | ||
[AvatarIconSize.Xl]: IconSize.Xl, // 48px avatar -> 32px icon | ||
}; | ||
export const MAP_AVATARICON_SEVERITY_ICONCOLOR: Record< | ||
AvatarIconSeverity, | ||
IconColor | ||
> = { | ||
[AvatarIconSeverity.Default]: IconColor.IconAlternative, | ||
[AvatarIconSeverity.Info]: IconColor.InfoDefault, | ||
[AvatarIconSeverity.Success]: IconColor.SuccessDefault, | ||
[AvatarIconSeverity.Error]: IconColor.ErrorDefault, | ||
[AvatarIconSeverity.Warning]: IconColor.WarningDefault, | ||
}; | ||
|
||
// Defaults | ||
export const DEFAULT_AVATARICON_PROPS: Required< | ||
Pick<AvatarIconProps, 'size' | 'shape' | 'severity'> | ||
> = { | ||
size: AvatarIconSize.Md, | ||
shape: AvatarBaseShape.Circle, | ||
severity: AvatarIconSeverity.Default, | ||
}; |
74 changes: 74 additions & 0 deletions
74
packages/design-system-react-native/src/components/AvatarIcon/AvatarIcon.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import type { Meta, StoryObj } from '@storybook/react-native'; | ||
import { View } from 'react-native'; | ||
|
||
import AvatarIcon from './AvatarIcon'; | ||
import { DEFAULT_AVATARICON_PROPS } from './AvatarIcon.constants'; | ||
import type { AvatarIconProps } from './AvatarIcon.types'; | ||
import { AvatarSize } from '../../shared/enums'; | ||
import { IconName } from '../Icon'; | ||
import { AvatarIconSeverity } from './AvatarIcon.types'; | ||
|
||
const meta: Meta<AvatarIconProps> = { | ||
title: 'Components/AvatarIcon', | ||
component: AvatarIcon, | ||
argTypes: { | ||
size: { | ||
control: 'select', | ||
options: AvatarSize, | ||
}, | ||
severity: { | ||
control: 'select', | ||
options: AvatarIconSeverity, | ||
}, | ||
iconName: { | ||
control: 'select', | ||
options: IconName, | ||
}, | ||
twClassName: { | ||
control: 'text', | ||
}, | ||
}, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<AvatarIconProps>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
size: DEFAULT_AVATARICON_PROPS.size, | ||
severity: DEFAULT_AVATARICON_PROPS.severity, | ||
iconName: IconName.Arrow2UpRight, | ||
twClassName: '', | ||
}, | ||
}; | ||
|
||
export const Sizes: Story = { | ||
render: () => ( | ||
<View style={{ gap: 16 }}> | ||
{Object.keys(AvatarSize).map((sizeKey) => ( | ||
<AvatarIcon | ||
key={sizeKey} | ||
size={AvatarSize[sizeKey as keyof typeof AvatarSize]} | ||
iconName={IconName.Arrow2UpRight} | ||
/> | ||
))} | ||
</View> | ||
), | ||
}; | ||
|
||
export const Severities: Story = { | ||
render: () => ( | ||
<View style={{ gap: 16 }}> | ||
{Object.keys(AvatarIconSeverity).map((severityKey) => ( | ||
<AvatarIcon | ||
key={severityKey} | ||
severity={ | ||
AvatarIconSeverity[severityKey as keyof typeof AvatarIconSeverity] | ||
} | ||
iconName={IconName.Arrow2UpRight} | ||
/> | ||
))} | ||
</View> | ||
), | ||
}; |
83 changes: 83 additions & 0 deletions
83
packages/design-system-react-native/src/components/AvatarIcon/AvatarIcon.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { render } from '@testing-library/react-native'; | ||
|
||
import { IconName } from '../Icon'; | ||
import { AvatarIconSize } from '../../shared/enums'; | ||
import { generateAvatarIconContainerClassNames } from './AvatarIcon.utilities'; | ||
import { | ||
DEFAULT_AVATARICON_PROPS, | ||
TWCLASSMAP_AVATARICON_SEVERITY_BACKGROUNDCOLOR, | ||
} from './AvatarIcon.constants'; | ||
import AvatarIcon from './AvatarIcon'; | ||
import { AvatarIconSeverity } from './AvatarIcon.types'; | ||
|
||
describe('AvatarIcon', () => { | ||
describe('generateAvatarIconContainerClassNames', () => { | ||
it('returns correct class names for default state', () => { | ||
const classNames = generateAvatarIconContainerClassNames({}); | ||
expect(classNames).toStrictEqual( | ||
`${TWCLASSMAP_AVATARICON_SEVERITY_BACKGROUNDCOLOR[DEFAULT_AVATARICON_PROPS.severity]}`, | ||
); | ||
}); | ||
|
||
it('applies correct severity class', () => { | ||
Object.values(AvatarIconSeverity).forEach((severity) => { | ||
const expectedClass = | ||
TWCLASSMAP_AVATARICON_SEVERITY_BACKGROUNDCOLOR[severity]; | ||
const classNames = generateAvatarIconContainerClassNames({ severity }); | ||
expect(classNames).toStrictEqual(expectedClass); | ||
}); | ||
}); | ||
|
||
it('appends additional Tailwind class names', () => { | ||
const classNames = generateAvatarIconContainerClassNames({ | ||
twClassName: 'shadow-lg ring-2', | ||
}); | ||
expect(classNames).toStrictEqual( | ||
`${TWCLASSMAP_AVATARICON_SEVERITY_BACKGROUNDCOLOR[DEFAULT_AVATARICON_PROPS.severity]} shadow-lg ring-2`, | ||
); | ||
}); | ||
|
||
it('applies severity and additional classes together correctly', () => { | ||
const severity = AvatarIconSeverity.Success; | ||
const classNames = generateAvatarIconContainerClassNames({ | ||
severity, | ||
twClassName: 'border border-green-500', | ||
}); | ||
expect(classNames).toStrictEqual( | ||
`${TWCLASSMAP_AVATARICON_SEVERITY_BACKGROUNDCOLOR[severity]} border border-green-500`, | ||
); | ||
}); | ||
}); | ||
describe('AvatarIcon Component', () => { | ||
it('renders with default props', () => { | ||
const { getByTestId: getByTestIdIcon } = render( | ||
<AvatarIcon | ||
iconName={IconName.Add} | ||
iconProps={{ testID: 'inner-icon' }} | ||
/>, | ||
); | ||
const icon = getByTestIdIcon('inner-icon'); | ||
|
||
expect(icon.props.name).toStrictEqual(IconName.Add); | ||
}); | ||
|
||
it('renders with custom props', () => { | ||
const customSize = AvatarIconSize.Lg; | ||
const customSeverity = AvatarIconSeverity.Error; | ||
const customIconProps = { testID: 'custom-icon', extraProp: 'value' }; | ||
|
||
// Render separately to test the Icon props. | ||
const { getByTestId: getIcon } = render( | ||
<AvatarIcon | ||
iconName={IconName.Close} | ||
size={customSize} | ||
severity={customSeverity} | ||
iconProps={customIconProps} | ||
/>, | ||
); | ||
const icon = getIcon('custom-icon'); | ||
expect(icon.props.name).toStrictEqual(IconName.Close); | ||
expect(icon.props.extraProp).toStrictEqual('value'); | ||
}); | ||
}); | ||
}); |
51 changes: 51 additions & 0 deletions
51
packages/design-system-react-native/src/components/AvatarIcon/AvatarIcon.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ | ||
import { useTailwind } from '@metamask/design-system-twrnc-preset'; | ||
import React, { useMemo } from 'react'; | ||
|
||
import { | ||
DEFAULT_AVATARICON_PROPS, | ||
MAP_AVATARICON_SIZE_ICONSIZE, | ||
MAP_AVATARICON_SEVERITY_ICONCOLOR, | ||
} from './AvatarIcon.constants'; | ||
import type { AvatarIconProps } from './AvatarIcon.types'; | ||
import { generateAvatarIconContainerClassNames } from './AvatarIcon.utilities'; | ||
import Icon, { IconProps } from '../Icon'; | ||
import AvatarBase from '../../primitives/AvatarBase'; | ||
|
||
const AvatarIcon = ({ | ||
size = DEFAULT_AVATARICON_PROPS.size, | ||
shape = DEFAULT_AVATARICON_PROPS.shape, | ||
severity = DEFAULT_AVATARICON_PROPS.severity, | ||
iconName, | ||
iconProps, | ||
twClassName = '', | ||
style, | ||
...props | ||
}: AvatarIconProps) => { | ||
const tw = useTailwind(); | ||
const twContainerClassNames = useMemo(() => { | ||
return generateAvatarIconContainerClassNames({ | ||
severity, | ||
twClassName, | ||
}); | ||
}, [severity, twClassName]); | ||
|
||
return ( | ||
<AvatarBase | ||
size={size} | ||
shape={shape} | ||
style={[tw`${twContainerClassNames}`, style]} | ||
accessibilityRole="image" | ||
{...props} | ||
> | ||
<Icon | ||
name={iconName} | ||
size={MAP_AVATARICON_SIZE_ICONSIZE[size]} | ||
color={MAP_AVATARICON_SEVERITY_ICONCOLOR[severity]} | ||
{...iconProps} | ||
/> | ||
</AvatarBase> | ||
); | ||
}; | ||
|
||
export default AvatarIcon; |
29 changes: 29 additions & 0 deletions
29
packages/design-system-react-native/src/components/AvatarIcon/AvatarIcon.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { AvatarBaseProps } from '../../primitives/AvatarBase'; | ||
import { IconName, IconProps } from '../Icon'; | ||
|
||
export enum AvatarIconSeverity { | ||
Default = 'default', | ||
Info = 'info', | ||
Success = 'success', | ||
Error = 'error', | ||
Warning = 'warning', | ||
} | ||
|
||
/** | ||
* AvatarIcon component props. | ||
*/ | ||
export type AvatarIconProps = { | ||
/** | ||
* Optional prop to control the severity of the avatar | ||
* @default AvatarIconSeverity.Default | ||
*/ | ||
severity?: AvatarIconSeverity; | ||
/** | ||
* Optional prop to specify an icon to show | ||
*/ | ||
iconName: IconName; | ||
/** | ||
* Optional prop to pass additional properties to the icon | ||
*/ | ||
iconProps?: Omit<IconProps, 'name'>; | ||
} & Omit<AvatarBaseProps, 'children' | 'fallbackText' | 'fallbackTextProps'>; |
35 changes: 35 additions & 0 deletions
35
packages/design-system-react-native/src/components/AvatarIcon/AvatarIcon.utilities.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* eslint-disable jsdoc/check-param-names */ | ||
/* eslint-disable jsdoc/require-param */ | ||
import type { AvatarIconProps } from './AvatarIcon.types'; | ||
import { | ||
TWCLASSMAP_AVATARICON_SEVERITY_BACKGROUNDCOLOR, | ||
DEFAULT_AVATARICON_PROPS, | ||
} from './AvatarIcon.constants'; | ||
|
||
/** | ||
* Generates a Tailwind class name string for the background color of an avatar icon. | ||
* | ||
* This function constructs a class name string based on the icon's `severity` | ||
* and optional additional Tailwind class names. | ||
* | ||
* @param severity - The severity level of the avatar icon, affecting background color. | ||
* @param twClassName - Additional Tailwind class names for customization. | ||
* @returns A string of Tailwind class names representing the avatar icon's container styles. | ||
* | ||
* Example: | ||
* ``` | ||
* const classNames = generateAvatarIconContainerClassNames({ | ||
* severity: 'error', | ||
* twClassName: 'border border-red-500', | ||
* }); | ||
* | ||
* console.log(classNames); | ||
* // Output: "bg-error border border-red-500" | ||
* ``` | ||
*/ | ||
export const generateAvatarIconContainerClassNames = ({ | ||
severity = DEFAULT_AVATARICON_PROPS.severity, | ||
twClassName = '', | ||
}: Partial<AvatarIconProps>): string => { | ||
return `${TWCLASSMAP_AVATARICON_SEVERITY_BACKGROUNDCOLOR[severity]} ${twClassName}`.trim(); | ||
}; |
Oops, something went wrong.