Skip to content

Commit

Permalink
feat(Icons): make components with icons headless, pt 1
Browse files Browse the repository at this point in the history
- part one: using existing icons with current selection as default
- this will let teams choose from the possible set of icons when overrides are needed
- part two will enable using a ReactNode or string, so teams can use custom icons
- update any new/existing snapshots
  • Loading branch information
booc0mtaco committed Sep 21, 2023
1 parent f56983a commit 8ce1d63
Show file tree
Hide file tree
Showing 27 changed files with 357 additions and 762 deletions.
9 changes: 7 additions & 2 deletions src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ENTER_KEYCODE, SPACEBAR_KEYCODE } from '../../util/keycodes';
import type { Size } from '../../util/variant-types';
import Button from '../Button';
import Heading, { type HeadingElement } from '../Heading';
import Icon from '../Icon';
import Icon, { type IconName } from '../Icon';
import styles from './Accordion.module.css';

type AccordionProps = {
Expand Down Expand Up @@ -40,6 +40,10 @@ type AccordionButtonProps = {
* Additional classnames passed in for styling
*/
className?: string;
/**
* Icon override for component. Default is 'expand-more'
*/
icon?: Extract<IconName, 'expand-more'>;
/**
* Used to specify which heading element should be rendered for the title.
* If provided, overrides parent <Accordion> headingAs prop.
Expand Down Expand Up @@ -142,6 +146,7 @@ const AccordionButton = ({
children,
className,
headingAs,
icon = 'expand-more',
onClose,
onOpen,
...other
Expand Down Expand Up @@ -203,7 +208,7 @@ const AccordionButton = ({
styles['accordion-button__icon'],
open && styles['accordion-button__icon--open'],
)}
name="expand-more"
name={icon}
purpose="informative"
size="1.625rem"
title={open ? 'hide content' : 'show content'}
Expand Down
10 changes: 10 additions & 0 deletions src/components/Avatar/Avatar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ export const WithCustomLabel: Story = {
},
};

export const WithCustomIcon: Story = {
args: {
ariaLabel: 'Custom label for avatar',
size: 'md',
shape: 'circle',
variant: 'icon',
icon: 'person-add',
},
};

export const UsingInitials: Story = {
args: {
size: 'md',
Expand Down
9 changes: 7 additions & 2 deletions src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Graphemer from 'graphemer';

import React from 'react';
import type { Size } from '../../util/variant-types';
import Icon from '../Icon';
import Icon, { type IconName } from '../Icon';
import styles from './Avatar.module.css';

export type UserData = {
Expand Down Expand Up @@ -34,6 +34,10 @@ export interface Props {
* CSS class names that can be appended to the component.
*/
className?: string;
/**
* Icon to use when an "icon" variant of the avatar. Default is "person"
*/
icon?: IconName;
/**
* The shape of the avatar
*/
Expand Down Expand Up @@ -92,6 +96,7 @@ export function getInitials(fromName: string): string {
export const Avatar = ({
ariaLabel,
className,
icon = 'person',
shape = 'circle',
size = 'md',
user,
Expand Down Expand Up @@ -126,7 +131,7 @@ export const Avatar = ({
{...other}
>
{variant === 'initials' && avatarDisplayName}
{variant === 'icon' && <Icon name="person" purpose="decorative" />}
{variant === 'icon' && <Icon name={icon} purpose="decorative" />}
{variant === 'image' && src && (
<img alt="user" className={styles['avatar__image']} src={src} />
)}
Expand Down
20 changes: 20 additions & 0 deletions src/components/Avatar/__snapshots__/Avatar.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,26 @@ exports[`<Avatar /> WhenImageVariantMissingSource story renders snapshot 1`] = `
</div>
`;

exports[`<Avatar /> WithCustomIcon story renders snapshot 1`] = `
<div
aria-label="Custom label for avatar"
class="avatar avatar--circle avatar--md avatar--icon"
role="img"
>
<svg
aria-hidden="true"
class="icon"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 14C18.7167 14 18.4792 13.9042 18.2875 13.7125C18.0958 13.5208 18 13.2833 18 13V11H16C15.7167 11 15.4792 10.9042 15.2875 10.7125C15.0958 10.5208 15 10.2833 15 10C15 9.71667 15.0958 9.47917 15.2875 9.2875C15.4792 9.09583 15.7167 9 16 9H18V7C18 6.71667 18.0958 6.47917 18.2875 6.2875C18.4792 6.09583 18.7167 6 19 6C19.2833 6 19.5208 6.09583 19.7125 6.2875C19.9042 6.47917 20 6.71667 20 7V9H22C22.2833 9 22.5208 9.09583 22.7125 9.2875C22.9042 9.47917 23 9.71667 23 10C23 10.2833 22.9042 10.5208 22.7125 10.7125C22.5208 10.9042 22.2833 11 22 11H20V13C20 13.2833 19.9042 13.5208 19.7125 13.7125C19.5208 13.9042 19.2833 14 19 14ZM9 12C7.9 12 6.95833 11.6083 6.175 10.825C5.39167 10.0417 5 9.1 5 8C5 6.9 5.39167 5.95833 6.175 5.175C6.95833 4.39167 7.9 4 9 4C10.1 4 11.0417 4.39167 11.825 5.175C12.6083 5.95833 13 6.9 13 8C13 9.1 12.6083 10.0417 11.825 10.825C11.0417 11.6083 10.1 12 9 12ZM2 20C1.71667 20 1.47917 19.9042 1.2875 19.7125C1.09583 19.5208 1 19.2833 1 19V17.2C1 16.6333 1.14583 16.1125 1.4375 15.6375C1.72917 15.1625 2.11667 14.8 2.6 14.55C3.63333 14.0333 4.68333 13.6458 5.75 13.3875C6.81667 13.1292 7.9 13 9 13C10.1 13 11.1833 13.1292 12.25 13.3875C13.3167 13.6458 14.3667 14.0333 15.4 14.55C15.8833 14.8 16.2708 15.1625 16.5625 15.6375C16.8542 16.1125 17 16.6333 17 17.2V19C17 19.2833 16.9042 19.5208 16.7125 19.7125C16.5208 19.9042 16.2833 20 16 20H2ZM3 18H15V17.2C15 17.0167 14.9542 16.85 14.8625 16.7C14.7708 16.55 14.65 16.4333 14.5 16.35C13.6 15.9 12.6917 15.5625 11.775 15.3375C10.8583 15.1125 9.93333 15 9 15C8.06667 15 7.14167 15.1125 6.225 15.3375C5.30833 15.5625 4.4 15.9 3.5 16.35C3.35 16.4333 3.22917 16.55 3.1375 16.7C3.04583 16.85 3 17.0167 3 17.2V18ZM9 10C9.55 10 10.0208 9.80417 10.4125 9.4125C10.8042 9.02083 11 8.55 11 8C11 7.45 10.8042 6.97917 10.4125 6.5875C10.0208 6.19583 9.55 6 9 6C8.45 6 7.97917 6.19583 7.5875 6.5875C7.19583 6.97917 7 7.45 7 8C7 8.55 7.19583 9.02083 7.5875 9.4125C7.97917 9.80417 8.45 10 9 10Z"
/>
</svg>
</div>
`;

exports[`<Avatar /> WithCustomLabel story renders snapshot 1`] = `
<div
aria-label="Custom label for avatar"
Expand Down
18 changes: 18 additions & 0 deletions src/components/Badge/Badge.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,24 @@ export const IconBadge: StoryObj<Args> = {
},
};

export const IconBadgeUsingIcon: StoryObj<Args> = {
args: {
children: (
<>
<div
aria-label="Ava alert"
className="fpo flex h-8 w-8 items-center justify-center"
>
Ava
</div>
<Badge.Icon icon="alarm" />
</>
),
},
};

// TODO-AH: add in test to see if error is thrown

export const LargeBadgeableObject: StoryObj<Args> = {
args: {
children: (
Expand Down
39 changes: 31 additions & 8 deletions src/components/Badge/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import clsx from 'clsx';
import React from 'react';
import type { EitherExclusive } from '../../util/utility-types';
import Icon from '../Icon';
import type { IconName, IconProps } from '../Icon';
import styles from './Badge.module.css';
Expand All @@ -16,12 +17,23 @@ type BadgeProps = {
className?: string;
};

type BadgeIconProps = Omit<IconProps, 'purpose' | 'name'> & {
/**
* Name of icon to render.
*/
name: IconName;
};
type BadgeIconProps = Omit<IconProps, 'purpose' | 'name'> &
EitherExclusive<
{
/**
* Name of icon to render. Please use `icon` instead, which has the same behavior.
* @deprecated
*/
name: IconName;
},
{
/**
* Name of icon to render.
*/

icon: IconName;
}
>;

type BadgeTextProps = {
/**
Expand All @@ -34,11 +46,22 @@ type BadgeTextProps = {
className?: string;
};

const BadgeIcon = ({ className, ...other }: BadgeIconProps) => {
const BadgeIcon = ({ className, name, icon, ...other }: BadgeIconProps) => {
const componentClassName = clsx(styles['badge'], className);
let iconName: IconName;

if (icon) {
iconName = icon;
} else if (name) {
iconName = name;
} else {
// both name and icon are undefined. throw an error
throw new Error('Name or Icon must be passed to the Badge component');
}

return (
<div aria-hidden className={componentClassName}>
<Icon purpose="decorative" size="1rem" {...other} />
<Icon name={iconName} purpose="decorative" size="1rem" {...other} />
</div>
);
};
Expand Down
32 changes: 32 additions & 0 deletions src/components/Badge/__snapshots__/Badge.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,38 @@ exports[`<Badge /> IconBadge story renders snapshot 1`] = `
</div>
`;

exports[`<Badge /> IconBadgeUsingIcon story renders snapshot 1`] = `
<div
class="badge__wrapper"
>
<div
aria-label="Ava alert"
class="fpo flex h-8 w-8 items-center justify-center"
>
Ava
</div>
<div
aria-hidden="true"
class="badge"
>
<svg
aria-hidden="true"
class="icon"
fill="currentColor"
height="1rem"
style="--icon-size: 1rem;"
viewBox="0 0 24 24"
width="1rem"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.87 15.25l-3.37-2V8.72c0-.4-.32-.72-.72-.72h-.06c-.4 0-.72.32-.72.72v4.72c0 .35.18.68.49.86l3.65 2.19c.34.2.78.1.98-.24.21-.35.1-.8-.25-1zm5.31-10.24L18.1 2.45c-.42-.35-1.05-.3-1.41.13-.35.42-.29 1.05.13 1.41l3.07 2.56c.42.35 1.05.3 1.41-.13.36-.42.3-1.05-.12-1.41zM4.1 6.55l3.07-2.56c.43-.36.49-.99.13-1.41-.35-.43-.98-.48-1.4-.13L2.82 5.01c-.42.36-.48.99-.12 1.41.35.43.98.48 1.4.13zM12 4c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9-4.03-9-9-9zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7z"
/>
</svg>
</div>
</div>
`;

exports[`<Badge /> LargeBadgeableObject story renders snapshot 1`] = `
<div
class="badge__wrapper"
Expand Down
9 changes: 7 additions & 2 deletions src/components/Breadcrumbs/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import clsx from 'clsx';
import { debounce } from 'lodash';
import React, { createContext, useContext, type ReactNode } from 'react';
import { flattenReactChildren } from '../../util/flattenReactChildren';
import Icon from '../Icon';
import Icon, { type IconName } from '../Icon';
import Menu from '../Menu';
import styles from './Breadcrumbs.module.css';

Expand Down Expand Up @@ -193,6 +193,10 @@ type BreadcrumbItemProps = {
* Null case is used for the collapsed variant, which uses Menu Items which has hrefs.
*/
href: string | null;
/**
* Icon override for component. Default is 'chevron-left'
*/
icon?: Extract<IconName, 'chevron-left'>;
/**
* URLs for the collapsed breadcrumbs variant.
* Should be <Menu.Item href={href}>{text}</Menu.Item>.
Expand Down Expand Up @@ -223,6 +227,7 @@ type BreadcrumbItemProps = {
export const BreadcrumbsItem = ({
className,
href,
icon = 'chevron-left',
menuItems,
separator = '/',
text,
Expand Down Expand Up @@ -260,7 +265,7 @@ export const BreadcrumbsItem = ({
<a className={styles['breadcrumbs__link']} href={href as string}>
<Icon
className={styles['breadcrumbs__back-icon']}
name="chevron-left"
name={icon}
purpose="informative"
title={text as string}
/>
Expand Down
8 changes: 4 additions & 4 deletions src/components/DragDrop/DragDrop.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@
display: flex;
flex-shrink: 0;
flex-direction: column;
margin-left: var(--eds-size-2);
margin-right: var(--eds-size-2);
width: 20rem;
/* Padding added to the top to account for outline of button. */
padding-top: var(--eds-size-half);
/**
* First drag drop container.
* Last drag drop container.
*/
&:first-of-type {
&:last-of-type {
/* Don't add left margin to left align with the rest of the content. */
margin-left: 0;
margin-right: 0;
}
}

Expand Down
14 changes: 1 addition & 13 deletions src/components/DragDrop/DragDrop.stories.module.css
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
.example-card {
.draggable-card {
padding-left: var(--eds-size-6);
}

.bg-yellow {
background-color: yellow;
}

.bg-white {
background-color: white;
}

.grid-square {
width: fit-content;
display: grid;
grid-template-areas: 'a a' 'a a';
gap: var(--eds-size-6);
}

.margin-0 {
margin: 0;
}
Loading

0 comments on commit 8ce1d63

Please sign in to comment.