Skip to content

Commit

Permalink
Merge pull request #2482 from innovaccer/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
anuradha9712 authored Jan 16, 2025
2 parents 409c83b + 36c9072 commit 4f9c7c5
Show file tree
Hide file tree
Showing 53 changed files with 11,148 additions and 5,752 deletions.
33 changes: 25 additions & 8 deletions core/components/atoms/avatarGroup/AvatarGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ interface AvatarPopoverProps {
maxHeight?: number | string;
minHeight?: number | string;
width?: number | string;
withSearch?: boolean;
searchPlaceholder?: string;
searchComparator?: (searchValue: string, avatarData: AvatarData) => boolean;
}

export interface AvatarGroupProps extends BaseProps {
Expand Down Expand Up @@ -74,6 +77,9 @@ export interface AvatarGroupProps extends BaseProps {
* minHeight?: number | string;
* width?: number | string;
* popperClassName?: string;
* withSearch?: boolean;
* searchPlaceholder?: string;
* searchComparator?: (searchValue: string, avatarData: AvatarData) => boolean;
* }
* </pre>
*
Expand All @@ -87,6 +93,9 @@ export interface AvatarGroupProps extends BaseProps {
* | minHeight | Min height of `Popover Text Wrapper` (does not work in case of custom popperRenderer) | |
* | width | width of `Popover Text Wrapper` (does not work in case of custom popperRenderer) | 176 |
* | popperClassName | Custom className added to `Popover` | |
* | withSearch | Adds search input in `Popover` | false | |
* | searchPlaceholder | Placeholder for search input | | |
* | searchComparator | Comparator function to search inside popover | |
*
*/
popoverOptions: AvatarPopoverProps;
Expand All @@ -107,6 +116,9 @@ export const AvatarGroup = (props: AvatarGroupProps) => {
position = 'bottom',
on = 'hover',
appendToBody = true,
withSearch,
searchPlaceholder,
searchComparator,
popperClassName = '',
} = popoverOptions;

Expand Down Expand Up @@ -135,6 +147,18 @@ export const AvatarGroup = (props: AvatarGroupProps) => {
className
);

const avatarPopperBodyProps = {
hiddenAvatarList: [...list].slice(max, list.length),
popperRenderer,
maxHeight,
minHeight,
width,
popperClassName,
withSearch,
searchPlaceholder,
searchComparator,
};

return (
<div data-test="DesignSystem-AvatarGroup" {...baseProps} className={AvatarGroupClass}>
<Avatars size={size} avatarList={avatarList} avatarStyle={avatarStyle} tooltipPosition={tooltipPosition} />
Expand All @@ -146,14 +170,7 @@ export const AvatarGroup = (props: AvatarGroupProps) => {
appendToBody={appendToBody}
offset="medium"
>
<AvatarPopperBody
hiddenAvatarList={list.slice(max, list.length)}
popperRenderer={popperRenderer}
maxHeight={maxHeight}
minHeight={minHeight}
width={width}
popperClassName={popperClassName}
/>
<AvatarPopperBody {...avatarPopperBodyProps} />
</Popover>
)}
</div>
Expand Down
41 changes: 41 additions & 0 deletions core/components/atoms/avatarGroup/AvatarGroupEmptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from 'react';
import { Text } from '@/index';

export interface AvatarEmptyStateProps {
/**
* Specify height for empty state content
*/
height?: string | number;
/**
* Describe title for empty state
*/
title?: React.ReactText;
/**
* Specify description for empty state
*/
description?: string;
}

export const AvatarGroupEmptyState = (props: AvatarEmptyStateProps) => {
const { height, title, description } = props;
return (
<div
className="d-flex flex-column justify-content-center align-items-center"
style={{ height: height ? (height as number) - 4 : '' }}
data-test="DesignSystem-AvatarGroup--EmptyState"
>
{title && (
<Text className="text-align-center mb-3" weight="strong">
{title}
</Text>
)}
{description && (
<Text className="text-align-center mb-6" weight="medium" size="small" appearance="subtle">
{description}
</Text>
)}
</div>
);
};

export default AvatarGroupEmptyState;
20 changes: 20 additions & 0 deletions core/components/atoms/avatarGroup/AvatarInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import { Input } from '@/index';
import { InputProps } from '@/index.type';
import styles from '@css/components/avatarGroup.module.css';
import classNames from 'classnames';

export const AvatarGroupInput = (props: InputProps) => {
const inputClassName = classNames({
'w-100': true,
[styles['AvatarGroup-input']]: true,
});

return (
<div className={styles['AvatarGroup-inputWrapper']}>
<Input icon="search" className={inputClassName} data-test="DesignSystem-AvatarGroup--Input" {...props} />
</div>
);
};

export default AvatarGroupInput;
43 changes: 43 additions & 0 deletions core/components/atoms/avatarGroup/AvatarOptionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as React from 'react';
import { Text, Avatar, Tooltip, Listbox } from '@/index';
import { AvatarData } from './AvatarGroup';
import classNames from 'classnames';

interface AvatarOptionItemProps {
avatarData: AvatarData;
}

const AvatarOptionItem = (props: AvatarOptionItemProps) => {
const { firstName = '', lastName = '', tooltipSuffix = '', disabled, image, icon, ...rest } = props.avatarData;
const name = `${firstName} ${lastName} ${tooltipSuffix}`;
const elementRef = React.useRef(null);

const triggerClassName = classNames({
['cursor-not-allowed']: disabled,
});

const itemClassName = classNames({
['AvatarGroup-listItem--disabled']: disabled,
['cursor-default']: !disabled,
});

return (
<Tooltip showOnTruncation={true} tooltip={name} elementRef={elementRef} triggerClass={triggerClassName}>
<Listbox.Item
disabled={disabled}
className={itemClassName}
tagName="li"
data-test="DesignSystem-AvatarGroup--Item"
>
<Avatar firstName={firstName} lastName={lastName} className="mr-4" withTooltip={false} {...rest}>
{image || icon}
</Avatar>
<Text ref={elementRef} data-test="DesignSystem-AvatarGroup--Text" className="ellipsis--noWrap">
{name}
</Text>
</Listbox.Item>
</Tooltip>
);
};

export default AvatarOptionItem;
124 changes: 89 additions & 35 deletions core/components/atoms/avatarGroup/AvatarPopperBody.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as React from 'react';
import { Text, Listbox, Tooltip } from '@/index';
import { Listbox } from '@/index';
import classNames from 'classnames';
import { AvatarData } from './AvatarGroup';
import styles from '@css/components/avatarGroup.module.css';
import AvatarGroupEmptyState from './AvatarGroupEmptyState';
import AvatarInput from './AvatarInput';
import AvatarOptionItem from './AvatarOptionItem';

interface AvatarPopperProps {
popperRenderer?: (names: AvatarData[]) => JSX.Element;
Expand All @@ -11,53 +14,104 @@ interface AvatarPopperProps {
width?: number | string;
popperClassName?: string;
hiddenAvatarList: AvatarData[];
withSearch?: boolean;
searchPlaceholder?: string;
searchComparator?: (searchValue: string, avatarData: AvatarData) => boolean;
}

const AvatarPopperBody = (props: AvatarPopperProps) => {
const { hiddenAvatarList, popperRenderer, maxHeight, minHeight, width, popperClassName } = props;
const {
hiddenAvatarList,
popperRenderer,
maxHeight,
minHeight,
width,
popperClassName,
withSearch,
searchPlaceholder,
searchComparator,
} = props;

if (popperRenderer) {
return popperRenderer(hiddenAvatarList);
}
const [searchValue, setSearchValue] = React.useState<string>('');
const [searchList, setSearchList] = React.useState<AvatarData[]>(hiddenAvatarList);

const onSearchHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = event.target.value;
setSearchValue(inputValue);
const list = hiddenAvatarList.filter((avatarData: AvatarData) => {
const { firstName, lastName } = avatarData;

if (searchComparator) {
return searchComparator(inputValue, avatarData);
}
return (
firstName?.toLowerCase()?.startsWith(inputValue.toLowerCase()) ||
lastName?.toLowerCase()?.startsWith(inputValue.toLowerCase())
);
});

setSearchList(list);
};

const onClearHandler = () => {
setSearchValue('');
setSearchList(hiddenAvatarList);
};

const popperClass = classNames(
{
[styles['AvatarGroup-Popper']]: true,
['py-3']: true,
['py-3']: !withSearch,
['pb-3']: withSearch,
},
popperClassName
);

return (
<div style={{ width, minHeight, maxHeight }} className={popperClass} data-test="DesignSystem-AvatarGroup--Popover">
<Listbox
showDivider={false}
type="description"
size="compressed"
tagName="ul"
data-test="DesignSystem-AvatarGroup--List"
>
{hiddenAvatarList.map((item: AvatarData, index: number) => {
const { firstName = '', lastName = '', tooltipSuffix = '', disabled } = item;
const name = `${firstName} ${lastName} ${tooltipSuffix}`;
const elementRef = React.useRef(null);
const searchInputHeight = 36;
const searchBorder = 1;

const customStyle = {
width,
minHeight: minHeight,
maxHeight: withSearch ? (maxHeight as number)! - searchInputHeight - searchBorder : maxHeight,
};

return (
<Tooltip key={index} showOnTruncation={true} tooltip={name} elementRef={elementRef}>
<Listbox.Item
disabled={disabled}
className="cursor-default"
tagName="li"
data-test="DesignSystem-AvatarGroup--Item"
>
<Text ref={elementRef} data-test="DesignSystem-AvatarGroup--Text" className="ellipsis--noWrap">
{name}
</Text>
</Listbox.Item>
</Tooltip>
);
})}
</Listbox>
if (popperRenderer) {
return popperRenderer(hiddenAvatarList);
}

return (
<div style={{ width: customStyle.width }} data-test="DesignSystem-AvatarGroup--Popover">
{withSearch && (
<AvatarInput
value={searchValue}
placeholder={searchPlaceholder}
onChange={onSearchHandler}
onClear={onClearHandler}
/>
)}
<div style={customStyle} className={popperClass}>
{searchList.length === 0 && (
<AvatarGroupEmptyState
height={customStyle.maxHeight}
title="No users found"
description="Try modifying your search to find what you are looking for."
/>
)}
{!!searchList.length && (
<Listbox
tagName="ul"
showDivider={false}
type="description"
size="compressed"
data-test="DesignSystem-AvatarGroup--List"
>
{searchList.map((item: AvatarData, index: number) => {
return <AvatarOptionItem key={index} avatarData={item} />;
})}
</Listbox>
)}
</div>
</div>
);
};
Expand Down
5 changes: 4 additions & 1 deletion core/components/atoms/avatarGroup/Avatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ const Avatars = (props: any) => {

const avatars = avatarList.map((item: any, index: any) => {
const { appearance, firstName, lastName, icon, image, disabled, tooltipSuffix } = item;

const newAvatarStyle = { ...avatarStyle, zIndex: avatarList.length - index };

return (
<div data-test="DesignSystem-AvatarGroup--Avatar" className={GroupClass} style={avatarStyle} key={index}>
<div data-test="DesignSystem-AvatarGroup--Avatar" className={GroupClass} style={newAvatarStyle} key={index}>
<Avatar
size={size}
appearance={appearance}
Expand Down
25 changes: 14 additions & 11 deletions core/components/atoms/avatarGroup/__stories__/index.story.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { list } from './AvatarList';

export const all = () => {
const position = 'bottom';
const on = 'hover';
const max = 2;
const options = {
max,
popoverOptions: {
on,
position,
withSearch: true,
searchPlaceholder: 'Search User',
on: 'click',
},
list: list.slice(0, 4),
};
Expand All @@ -33,23 +34,25 @@ const customCode = `() => {
lastName: 'Wheeler'
},
{
firstName: 'Monica',
lastName: 'Geller'
firstName: 'Rachel',
lastName: 'Green',
icon: <Avatar.Icon name="person" />
},
{
firstName: 'Arya',
lastName: 'Stark',
firstName: 'Anuradha',
lastName: 'Aggarwal',
image: <Avatar.Image src="https://design.innovaccer.com/images/avatar2.jpeg" />,
},
{
firstName: 'Rachel',
lastName: 'Green',
firstName: 'Monica',
lastName: 'Geller'
},
{
firstName: 'Walter',
lastName: 'Wheeler'
firstName: 'Arya',
lastName: 'Stark',
},
];
return <AvatarGroup list={list} popoverOptions={{ position: 'bottom'}} />;
return <AvatarGroup list={list} popoverOptions={{ position: 'bottom', withSearch: true, on: 'click', searchPlaceholder: 'Search User',}} />;
}`;

export default {
Expand Down
Loading

0 comments on commit 4f9c7c5

Please sign in to comment.