Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(badge): adding leading-icon to badge #2868

Merged
merged 21 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/gamut/src/Badge/helpers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const iconSizeMap = {
base: 16,
sm: 8,
} as const;

export const determineIconSize = (size: keyof typeof iconSizeMap) => {
return iconSizeMap[size];
};

const iconSpaceMap = {
base: 8,
sm: 4,
} as const;

export const determineIconSpacing = (size: keyof typeof iconSizeMap) => {
return iconSpaceMap[size];
};
43 changes: 38 additions & 5 deletions packages/gamut/src/Badge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { styledOptions, system, variant } from '@codecademy/gamut-styles';
import { StyleProps, variance } from '@codecademy/variance';
import styled from '@emotion/styled';

import { FlexBox } from '../Box';
import { IconComponentType, WithChildrenProp } from '../utils';
import { determineIconSize, determineIconSpacing } from './helpers';

const colorVariants = variant({
defaultVariant: 'primary',
base: {
Expand Down Expand Up @@ -34,12 +38,17 @@ const colorVariants = variant({
color: 'text-secondary',
bg: 'transparent',
},
tertiaryFill: {
border: 1,
borderColor: 'text-secondary',
color: 'text-secondary',
bg: 'background',
},
},
});

const sizeVariants = variant({
prop: 'size',
defaultVariant: 'base',
variants: {
base: {
height: '1.5rem',
Expand All @@ -59,14 +68,38 @@ const badgeProps = variance.compose(
system.layout,
system.typography
);

export interface BadgeProps
export interface BadgeBaseProps
extends StyleProps<typeof badgeProps>,
StyleProps<typeof colorVariants>,
StyleProps<typeof sizeVariants> {}
StyleProps<typeof sizeVariants>,
WithChildrenProp {}

export const Badge = styled('div', styledOptions)<BadgeProps>(
const BadgeBase = styled('div', styledOptions)<BadgeBaseProps>(
badgeProps,
colorVariants,
sizeVariants
);

export interface BadgeProps
extends Partial<IconComponentType>,
BadgeBaseProps {}

export const Badge: React.FC<BadgeProps> = ({
icon: Icon,
children,
...rest
}) => {
const iconSize = rest.size === 'sm' ? 'sm' : 'base';
const size = determineIconSize(iconSize);
const spacing = determineIconSpacing(iconSize);
return (
<BadgeBase {...rest}>
{Icon && (
<FlexBox pr={spacing}>
<Icon height={size} width={size} />
</FlexBox>
)}
{children}
Copy link
Contributor

@dreamwasp dreamwasp May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of the SB examples look great but noticed the portal example is very slightly wonky -
image

i think wrapping children in 100% width centered Flexbox should fix that! if it persists you may need to tweak lineHeight too 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhhh that's on me... probably a bad example to just tweak, there was some margin stuff in there that I didn't consider. Sorry about that :(
image

oddly enough, I don't see the text being shifted - admittedly, I removed the margin and put it back just as a sanity check and it looks fine to me
image
the badge seems to have a flex display and align-items: center.
image

I temporarily changed it in storybook and it looks good (just in case it was an issue with the icon itself)
image

maybe we can look at this together on Monday.
Thanks Cass :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that looks great now!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but now I'm paranoid about why you saw what you saw lol

</BadgeBase>
);
};
2 changes: 1 addition & 1 deletion packages/gamut/src/Button/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { ComponentProps, forwardRef } from 'react';

import { ButtonBaseElements } from '../ButtonBase/ButtonBase';
import { ToolTip, ToolTipProps } from '../Tip';
import { IconComponentType } from '../utils';
import {
createButtonComponent,
IconComponentType,
iconSizeVariants,
textButtonVariants,
} from './shared';
Expand Down
3 changes: 1 addition & 2 deletions packages/gamut/src/Button/shared/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { GamutIconProps } from '@codecademy/gamut-icons';
import { ColorModes } from '@codecademy/gamut-styles';
import { StyleProps } from '@codecademy/variance';
import { ComponentProps, HTMLProps } from 'react';

import { ButtonBase } from '../../ButtonBase';
import { IconComponentType } from '../../utils';
import { CTAButton } from '../CTAButton';
import { FillButton } from '../FillButton';
import { IconButton } from '../IconButton';
Expand All @@ -20,7 +20,6 @@ export interface ButtonBaseProps extends StyleProps<typeof buttonProps> {
}

export type ButtonProps = ButtonBaseProps & ComponentProps<typeof ButtonBase>;
export type IconComponentType = { icon: React.ComponentType<GamutIconProps> };

export type InlineIconButtonProps<
BaseButtonType extends
Expand Down
3 changes: 0 additions & 3 deletions packages/gamut/src/utils/childrenType.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/gamut/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from './childrenType';
export * from './createPromise';
export * from './generateResponsiveClassnames';
export * from './omitProps';
export * from './useIsMounted';
export * from './types';
7 changes: 7 additions & 0 deletions packages/gamut/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { GamutIconProps } from '@codecademy/gamut-icons';

export interface WithChildrenProp {
children?: React.ReactNode | React.ReactNode[];
}

export type IconComponentType = { icon: React.ComponentType<GamutIconProps> };
Comment on lines +1 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love ittt

Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Badge } from '@codecademy/gamut/src';
import {
MiniCheckCircleIcon,
MiniWarningTriangleIcon,
} from '@codecademy/gamut-icons';
import title from '@codecademy/macros/lib/title.macro';
import { PropsTable } from '@codecademy/storybook-addon-variance';
import { Canvas, Meta, Story } from '@storybook/addon-docs/blocks';
import LinkTo from '@storybook/addon-links/react';

import { TertiaryFillExample } from './examples';

<Meta
title={title}
component={Badge}
Expand All @@ -15,7 +21,7 @@ import LinkTo from '@storybook/addon-links/react';
design: {
type: 'figma',
url:
'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?type=design&node-id=60-3&mode=design&t=aRabEpDXVTIQicAe-0',
'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?type=design&node-id=29959-39721&mode=design&t=27y0ZBTRsAgBmZ9z-0',
},
}}
/>
Expand All @@ -26,6 +32,7 @@ Badges are generally found next to interface elements that need to be called out

- Use Badges to display read-only information like statuses, attributes, and other emphasized information.
- Use the `variant` and `size` properties to ensure your badges have the right visibility and visual hierarchy on your page.
- Use an <LinkTo kind='Atoms/Icons'>Icon</LinkTo> to draw attention to the badge using the `icon` prop.
- **Limit Badge text to 1–2 words if possible.** These elements are most effective when displaying short pieces of text.
- **Do not use Badges for elements that are meant to be clickable or interactive**. Badges are fully rounded to distinguish themselves from elements such as <LinkTo kind='Atoms/Button'>Buttons</LinkTo>.

Expand All @@ -35,12 +42,12 @@ export const BadgeTemplate = (args) => (

## Variants

We support the following variants of Badges: `primary`, `secondary`, `tertiary`, and `accent`.
We support the following variants of Badges: `primary`, `secondary`, `tertiary`, `tertiaryFill`, and `accent`.

- **Greyscale Badges** provide different levels of priority for the `primary`, `secondary`, and `tertiary` variants.
- **Greyscale Badges** provide different levels of priority for the `primary`, `secondary`, `tertiary`, and `tertiaryFill` variants.
- **Accent colors** like `accent` are meant to be used sparingly so they can stand out against other elements.

Additional variants should designed with both <LinkTo kind='Foundations/ColorMode'>ColorModes</LinkTo> in mind. For example, the `accent` and `secondary` variants work against both light and dark backgrounds, while the `primary` and `tertiary` variants both adapt to their ColorMode (which you can check out by using the toolbar at the top of this page!)
Additional variants should designed with both <LinkTo kind='Foundations/ColorMode'>ColorModes</LinkTo> in mind. For example, the `accent` and `secondary` variants work against both light and dark backgrounds, while the `primary` and `tertiary`, and `tertiaryFill` variants both adapt to their ColorMode (which you can check out by using the toolbar at the top of this page!)

<Canvas>
<Story name="primary" args={{ children: 'primary' }}>
Expand All @@ -52,11 +59,21 @@ Additional variants should designed with both <LinkTo kind='Foundations/ColorMod
<Story name="tertiary" args={{ variant: 'tertiary' }}>
{BadgeTemplate.bind({})}
</Story>
<Story name="tertiaryFill" args={{ variant: 'tertiaryFill' }}>
{BadgeTemplate.bind({})}
</Story>
<Story name="accent" args={{ variant: 'accent' }}>
{BadgeTemplate.bind({})}
</Story>
</Canvas>

In the example below that the background is set to `background-primary` (beige).
Notice that `tertiary` has a transparent background while `tertiaryFill` has a regular `background` color.

<Canvas>
<Story name="tertiaryFillSeparate">{() => <TertiaryFillExample />}</Story>
</Canvas>

## Size

We have two `size` options, which should be used according to their context:
Expand All @@ -76,6 +93,34 @@ We have two `size` options, which should be used according to their context:
</Story>
</Canvas>

## Leading Icon

Include the `icon` property when the icon reinforces the badge’s message and improves scannability. Since badges are inherently small, it's recommended to use Gamut's mini icons.

<Canvas>
<Story
name="sample icon"
args={{
children: 'sample icon',
variant: 'primary',
icon: MiniCheckCircleIcon,
}}
>
{BadgeTemplate.bind({})}
</Story>
<Story
name="small icon"
args={{
children: 'sm icon',
size: 'sm',
variant: 'accent',
icon: MiniWarningTriangleIcon,
}}
>
{BadgeTemplate.bind({})}
</Story>
</Canvas>

## Code Playground

<Canvas>
Expand Down
11 changes: 11 additions & 0 deletions packages/styleguide/stories/Atoms/Badge/examples.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Badge } from '@codecademy/gamut';
import { Background } from '@codecademy/gamut-styles';

export const TertiaryFillExample = () => {
return (
<Background bg="background-primary">
<Badge variant="tertiary">tertiary ex</Badge>
<Badge variant="tertiaryFill">tertiaryFill ex</Badge>
</Background>
);
};