Skip to content

Commit

Permalink
feat: avatar group: add the avatar group component (#422)
Browse files Browse the repository at this point in the history
* feat: avatar group: add the avatar group component

* chore: avatar group: address pr comments

* chore: avatar group: updates border color to background color var
  • Loading branch information
dkilgore-eightfold authored Oct 19, 2022
1 parent 5c6beed commit 535cfe6
Show file tree
Hide file tree
Showing 13 changed files with 966 additions and 19 deletions.
4 changes: 0 additions & 4 deletions src/components/Avatar/Avatar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ const imageProps = {
alt: 'random profile image',
};

const iconProps = {
path: IconName.mdiBell,
};

const Avatar_Default_Story: ComponentStory<typeof Avatar> = (args) => (
<Avatar {...args} />
);
Expand Down
105 changes: 105 additions & 0 deletions src/components/Avatar/Avatar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from 'react';
import Enzyme, { mount } from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import MatchMediaMock from 'jest-matchmedia-mock';
import { Avatar, AvatarGroup } from './';

Enzyme.configure({ adapter: new Adapter() });

let matchMedia: any;

describe('Avatar', () => {
beforeAll(() => {
matchMedia = new MatchMediaMock();
});

afterEach(() => {
matchMedia.clear();
});

test('Should render', () => {
const wrapper = mount(<Avatar>AB</Avatar>);
expect(wrapper.children().length).toEqual(1);
});

test('Avatar type square is default', () => {
const wrapper = mount(<Avatar>AB</Avatar>);
expect(wrapper.find('.avatar').hasClass('round-image')).toBeFalsy();
expect(wrapper.render()).toMatchSnapshot();
});

test('Avatar type is round', () => {
const wrapper = mount(<Avatar type={'round'}>AB</Avatar>);
expect(wrapper.find('.round-image')).toBeTruthy();
expect(wrapper.render()).toMatchSnapshot();
});

test('Avatar size is 40px', () => {
const wrapper = mount(<Avatar size={'40px'}>AB</Avatar>);
expect(wrapper.render()).toMatchSnapshot();
});

test('Should render a group', () => {
const wrapper = mount(
<AvatarGroup>
<Avatar>AB</Avatar>
<Avatar>CD</Avatar>
<Avatar>EF</Avatar>
<Avatar>GH</Avatar>
</AvatarGroup>
);
expect(wrapper.children().length).toEqual(1);
});

test('Should render a list group', () => {
const imageProps = {
src: 'https://images.pexels.com/photos/771742/pexels-photo-771742.jpeg',
alt: 'random profile image',
};

interface User {
'data-test-id': string;
alt: string;
children: React.ReactNode;
classNames: string;
img: string;
key: string;
name: string;
randomiseTheme: boolean;
}

const sampleList: User[] = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20,
].map((i) => ({
'data-test-id': `my-avatar-test-id-${i}`,
alt: i === 1 ? imageProps.alt : null,
children: `U${i}`,
classNames: `my-avatar-class-${i}`,
img: i === 1 ? imageProps.src : null,
key: `key-${i}`,
name: `User ${i}`,
randomiseTheme: true,
}));
const wrapper = mount(
<AvatarGroup
avatarListProps={{
items: sampleList,
renderItem: (item: User) => (
<Avatar
alt={item.alt}
classNames={item.classNames}
data-test-id={item['data-test-id']}
hashingFunction={() => 3}
randomiseTheme={item.randomiseTheme}
src={item.img}
>
{item.children}
</Avatar>
),
}}
/>
);
expect(wrapper.children().length).toEqual(1);
});
});
3 changes: 3 additions & 0 deletions src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const AvatarFallback: FC<AvatarFallbackProps> = React.forwardRef(

const avatarClasses: string = mergeClasses([
styles.wrapperStyle,
styles.avatar,
classNames,
{ [styles.red]: theme === 'red' },
{ [styles.redOrange]: theme === 'redOrange' },
Expand Down Expand Up @@ -68,6 +69,7 @@ const AvatarIcon: FC<AvatarIconProps> = React.forwardRef(
({ iconProps, fontSize, classNames, style }, ref: Ref<HTMLDivElement>) => {
const wrapperClasses: string = mergeClasses([
styles.wrapperStyle,
styles.avatar,
classNames,
]);

Expand Down Expand Up @@ -98,6 +100,7 @@ export const Avatar: FC<AvatarProps> = React.forwardRef(
ref: Ref<HTMLDivElement>
) => {
const imageClasses: string = mergeClasses([
styles.avatar,
styles.imageStyle,
{ [styles.roundImage]: type === 'round' },
]);
Expand Down
96 changes: 81 additions & 15 deletions src/components/Avatar/Avatar.types.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
import { Ref } from 'react';
import React, { ReactNode, Ref } from 'react';
import { OcThemeNames } from '../ConfigProvider';
import { IconProps } from '../Icon';
import { ListProps } from '../List';
import { TooltipProps } from '../Tooltip';
import { OcBaseProps } from '../OcBase';

interface BaseAvatarProps extends OcBaseProps<HTMLSpanElement> {
/**
* Avatar size
* @default 32
*/
size?: string;
/**
* Avatar fallback font size
* @default 18
* @default '18px'
*/
fontSize?: string;
/**
* Type of avatar style
* @default 'square'
* Function that returns avatar index
*/
type?: 'round' | 'square';
hashingFunction?: () => number;
/**
* Should randomise theme
* @default false
*/
randomiseTheme?: boolean;
/**
* Ref of the container div
*/
ref?: Ref<HTMLDivElement>;
/**
* Function that returns avatar index
* Avatar size
* @default '32px'
*/
hashingFunction?: () => number;
size?: string;
/**
* theme of the fallback avatar
* @default ''
*/
theme?: OcThemeNames;
/**
* Should randomise theme
* @default false
* Type of avatar style
* @default 'square'
*/
randomiseTheme?: boolean;
type?: 'round' | 'square';
}

export interface AvatarIconProps extends BaseAvatarProps {
Expand All @@ -60,3 +62,67 @@ export interface AvatarProps
*/
alt?: string;
}

interface MaxAvatarProps extends BaseAvatarProps {
/**
* Max Avatar class names.
*/
classNames?: string;
/**
* Shown Avatars max count.
*/
count?: number;
/**
* Max Avatar style.
*/
style?: React.CSSProperties;
/**
* Max tooltip props.
*/
tooltipProps?: TooltipProps;
/**
* Max Avatar custom value.
*/
value?: ReactNode;
/**
* Unique id used to target element for testing.
*/
'data-test-id'?: string;
/**
* Max Avatar ref.
*/
ref?: Ref<HTMLDivElement>;
}

interface AvatarListProps
extends Omit<ListProps<ReactNode>, 'footer' | 'header' | 'layout'> {}

export interface AvatarGroupProps extends OcBaseProps<HTMLDivElement> {
/**
* Avatar group List props.
*/
avatarListProps?: AvatarListProps;
/**
* Avatar group children.
*/
children?: ReactNode;
/**
* Avatar group fallback avatar font sizes.
* @default '18px'
*/
fontSize?: string;
/**
* Avatar group max props.
*/
maxProps?: MaxAvatarProps;
/**
* Avatar group avatar sizes.
* @default '32px'
*/
size?: string;
/**
* Type of avatar group style
* @default 'square'
*/
type?: 'round' | 'square';
}
Loading

0 comments on commit 535cfe6

Please sign in to comment.