diff --git a/src/components/NumberIcon/NumberIcon-v2.module.css b/src/components/NumberIcon/NumberIcon-v2.module.css new file mode 100644 index 000000000..4b1559188 --- /dev/null +++ b/src/components/NumberIcon/NumberIcon-v2.module.css @@ -0,0 +1,84 @@ +@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-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; +} \ No newline at end of file diff --git a/src/components/NumberIcon/NumberIcon-v2.stories.tsx b/src/components/NumberIcon/NumberIcon-v2.stories.tsx new file mode 100644 index 000000000..ddc4deba3 --- /dev/null +++ b/src/components/NumberIcon/NumberIcon-v2.stories.tsx @@ -0,0 +1,114 @@ +import type { StoryObj, Meta } from '@storybook/react'; +import React from 'react'; + +import { NumberIcon } from './NumberIcon-v2'; + +export default { + title: 'Components/V2/NumberIcon', + component: NumberIcon, + parameters: { + badges: ['intro-1.0', 'current-2.0'], + }, + args: { + 'aria-label': 'number icon example', + number: 1, + }, + decorators: [(Story) =>
{Story()}
], +} as Meta; + +type Args = React.ComponentProps; +type Story = StoryObj; + +export const Default: Story = {}; + +export const Sizes: Story = { + args: { + status: 'default', + }, + render: (args) => { + return ( + <> + + + + ); + }, + decorators: [ + (Story) =>
{Story()}
, + ], +}; + +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) => ( +
+ {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 32, 43, 54, 65, 76, 87, 98].map( + (number) => ( + + ), + )} +
+ ), +}; + +/** + * 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', 'current-2.0', 'implementationExample'], + }, + render: () => ( +
+ + + + + + +
+ ), +}; diff --git a/src/components/NumberIcon/NumberIcon-v2.tsx b/src/components/NumberIcon/NumberIcon-v2.tsx new file mode 100644 index 000000000..a08d35289 --- /dev/null +++ b/src/components/NumberIcon/NumberIcon-v2.tsx @@ -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; + /** + * 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 ( + + {number} + + ); +}; + +NumberIcon.displayName = 'NumberIcon'; diff --git a/src/components/NumberIcon/index.ts b/src/components/NumberIcon/index.ts index 27abe5f81..096c360ce 100644 --- a/src/components/NumberIcon/index.ts +++ b/src/components/NumberIcon/index.ts @@ -1 +1,2 @@ export { NumberIcon as default } from './NumberIcon'; +export { NumberIcon as NumberIconV2 } from './NumberIcon-v2';