-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(NumberIcon)!: introduce 2.0 component
- Loading branch information
1 parent
d5782cd
commit 5d5c2d2
Showing
3 changed files
with
282 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
@import '../../design-tokens/mixins.css'; | ||
/*------------------------------------*\ | ||
# NUMBER ICON | ||
\*------------------------------------*/ | ||
|
||
/** | ||
* Number Icon displays a number enclosed in a circle. | ||
* | ||
* Centers the number text in the circle. | ||
*/ | ||
.number-icon { | ||
/* Line height set to 1 here since this should only ever be on 1 line and it evens out padding in circle. */ | ||
line-height: 1; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
|
||
/* The circle part of the icon, made with borders. */ | ||
border: var(--eds-border-width-sm) solid; | ||
border-color: inherit; | ||
border-radius: var(--eds-border-radius-full); | ||
|
||
cursor: pointer; | ||
} | ||
|
||
/** | ||
* Size Variants. | ||
*/ | ||
.number-icon--size-sm { | ||
/* Line height set to 1 here since this should only ever be on 1 line and it evens out padding in circle. */ | ||
line-height: 1; | ||
height: 1rem; | ||
width: 1rem; | ||
min-width: 1rem; | ||
} | ||
|
||
.number-icon--size-md { | ||
height: 1.5rem; | ||
width: 1.5rem; | ||
min-width: 1.5rem; | ||
} | ||
|
||
.number-icon--size-lg { | ||
height: 2rem; | ||
width: 2rem; | ||
min-width: 2rem; | ||
} | ||
|
||
/* Colors & Theme */ | ||
|
||
/** | ||
* Interactive States | ||
*/ | ||
.number-icon--status-default { | ||
color: var(--eds-theme-color-text-utility-interactive-primary); | ||
border-color: var(--eds-theme-color-border-utility-interactive); | ||
background-color: var(--eds-theme-color-background-utility-interactive-no-emphasis); | ||
|
||
&:hover { | ||
border-color: var(--eds-theme-color-border-utility-interactive-hover); | ||
background-color: var(--eds-theme-color-background-utility-interactive-no-emphasis-hover); | ||
} | ||
|
||
&:active { | ||
border-color: var(--eds-theme-color-border-utility-interactive-active); | ||
background-color: var(--eds-theme-color-background-utility-interactive-no-emphasis-active); | ||
} | ||
} | ||
|
||
.number-icon--status-completed { | ||
color: var(--eds-theme-color-text-utility-inverse); | ||
border-color: var(--eds-theme-color-background-utility-favorable-high-emphasis); | ||
background-color: var(--eds-theme-color-background-utility-favorable-high-emphasis); | ||
|
||
&:hover { | ||
border-color: var(--eds-theme-color-background-utility-favorable-high-emphasis-hover); | ||
background-color: var(--eds-theme-color-background-utility-favorable-high-emphasis-hover); | ||
} | ||
|
||
&:active { | ||
border-color: var(--eds-theme-color-background-utility-favorable-high-emphasis-active); | ||
background-color: var(--eds-theme-color-background-utility-favorable-high-emphasis-active); | ||
} | ||
} | ||
|
||
.number-icon--status-incomplete { | ||
color: var(--eds-theme-color-text-utility-neutral-secondary); | ||
border-color: var(--eds-theme-color-border-utility-neutral-medium-emphasis); | ||
|
||
border-style: dashed; | ||
pointer-events: none; | ||
} |
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,115 @@ | ||
import type { StoryObj, Meta } from '@storybook/react'; | ||
import React from 'react'; | ||
|
||
import { NumberIcon } from './NumberIcon-v2'; | ||
|
||
export default { | ||
title: 'Components/NumberIcon (v2)', | ||
component: NumberIcon, | ||
parameters: { | ||
badges: ['intro-1.0', 'current-2.0'], | ||
}, | ||
args: { | ||
'aria-label': 'number icon example', | ||
number: 1, | ||
}, | ||
decorators: [(Story) => <div className="p-8">{Story()}</div>], | ||
} as Meta<Args>; | ||
|
||
type Args = React.ComponentProps<typeof NumberIcon>; | ||
type Story = StoryObj<Args>; | ||
|
||
export const Default: Story = {}; | ||
|
||
export const Sizes: Story = { | ||
args: { | ||
status: 'default', | ||
}, | ||
render: (args) => { | ||
return ( | ||
<> | ||
<NumberIcon number={1} size="sm" {...args} /> | ||
<NumberIcon number={2} size="md" {...args} /> | ||
<NumberIcon number={3} size="lg" {...args} /> | ||
</> | ||
); | ||
}, | ||
decorators: [ | ||
(Story) => <div className="flex flex-wrap gap-1">{Story()}</div>, | ||
], | ||
}; | ||
|
||
export const Completed: Story = { | ||
args: { | ||
...Sizes.args, | ||
status: 'completed', | ||
}, | ||
render: Sizes.render, | ||
decorators: Sizes.decorators, | ||
}; | ||
|
||
export const Incomplete: Story = { | ||
args: { | ||
...Sizes.args, | ||
status: 'incomplete', | ||
}, | ||
render: Sizes.render, | ||
decorators: Sizes.decorators, | ||
}; | ||
|
||
/** | ||
* `NumberIcon` supports individual digits, with a maximum of two digits. By default, | ||
* they are positioned as block-level elements. use `flex` or `display` to update positioning. | ||
*/ | ||
export const DifferentNumbers: Story = { | ||
/** | ||
* Disables controls for args that have no affect on this story | ||
*/ | ||
argTypes: { | ||
number: { | ||
table: { | ||
disable: true, | ||
}, | ||
}, | ||
'aria-label': { | ||
table: { | ||
disable: true, | ||
}, | ||
}, | ||
}, | ||
render: (args) => ( | ||
<div> | ||
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 32, 43, 54, 65, 76, 87, 98].map( | ||
(number) => ( | ||
<NumberIcon | ||
key={number} | ||
{...args} | ||
aria-label={`Step ${number}`} | ||
number={number} | ||
/> | ||
), | ||
)} | ||
</div> | ||
), | ||
}; | ||
|
||
/** | ||
* This Implementation example shows how to use Number Icon to build a stepper-like component. | ||
* | ||
* - incomplete rows are aligned with each number icon to show progress | ||
*/ | ||
export const NumberIconList: Story = { | ||
parameters: { | ||
badges: ['intro-1.0', 'implementationExample'], | ||
}, | ||
render: () => ( | ||
<div className="flex flex-wrap gap-1"> | ||
<NumberIcon aria-label="Item 1" number={1} size="sm" /> | ||
<NumberIcon aria-label="Item 2" number={2} size="sm" /> | ||
<NumberIcon aria-label="Item 3" number={3} size="sm" /> | ||
<NumberIcon aria-label="Item 4" number={4} size="sm" /> | ||
<NumberIcon aria-label="Item 5" number={5} size="sm" /> | ||
<NumberIcon aria-label="Item 6" number={6} size="sm" /> | ||
</div> | ||
), | ||
}; |
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,75 @@ | ||
import clsx from 'clsx'; | ||
import React from 'react'; | ||
|
||
import type { Size } from '../../util/variant-types'; | ||
|
||
import Text from '../Text'; | ||
import styles from './NumberIcon-v2.module.css'; | ||
|
||
export interface Props { | ||
// Component API | ||
/** | ||
* (Required) Screen-reader text for the number icon. | ||
*/ | ||
'aria-label': string; | ||
/** | ||
* CSS class names that can be appended to the component. | ||
*/ | ||
className?: string; | ||
// Design API | ||
/** | ||
* Whether `NumberIcon` can be focused on, clicked, etc. | ||
*/ | ||
isInteractive?: boolean; | ||
/** | ||
* Number to be shown as the icon. Maximum of two digits. | ||
*/ | ||
number?: number; | ||
/** | ||
* The size of the icon. | ||
* | ||
* **Default is `"lg"`**. | ||
*/ | ||
size?: Extract<Size, 'sm' | 'md' | 'lg'>; | ||
/** | ||
* Indication of the status of the referenced item | ||
*/ | ||
status?: 'completed' | 'incomplete' | 'default'; | ||
} | ||
|
||
/** | ||
* `import {NumberIcon} from "@chanzuckerberg/eds";` | ||
* | ||
* Treats a numeral as an icon by wrapping it in a container and adding color/spacing. | ||
* | ||
*/ | ||
export const NumberIcon = ({ | ||
className, | ||
isInteractive = false, | ||
number, | ||
status = 'default', | ||
size = 'lg', | ||
...other | ||
}: Props) => { | ||
const componentClassName = clsx( | ||
className, | ||
styles['number-icon'], | ||
size && styles[`number-icon--size-${size}`], | ||
status && styles[`number-icon--status-${status}`], | ||
); | ||
|
||
return ( | ||
<Text | ||
as="span" | ||
className={componentClassName} | ||
preset={['sm', 'md'].includes(size) ? 'caption-sm' : 'caption-lg'} | ||
role="img" | ||
tabIndex={isInteractive ? 0 : -1} | ||
{...other} | ||
> | ||
{number} | ||
</Text> | ||
); | ||
}; | ||
|
||
NumberIcon.displayName = 'NumberIcon'; |