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(InputChip): add 1.0 component #2097

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
"typescript.validate.enable": true,
"javascript.validate.enable": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "esbenp.prettier-vscode",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note: this tells VSCode to treat the *.css files as css language files. Somehow, it was thinking they were "tailwind" files 👎

"[css]": {
"editor.defaultFormatter": "vscode.css-language-features"
}
}
72 changes: 72 additions & 0 deletions src/components/InputChip/InputChip.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*------------------------------------*\
# INPUT CHIP
\*------------------------------------*/

/**
* InputChip
*/

.input-chip {
display: inline-flex;
}

.input-chip__label {
display: inline-flex;
padding: calc(var(--eds-size-1) / 16 * 1rem);
padding-right: 0;
gap: calc(var(--eds-size-1) / 16 * 1rem);
align-items: center;
justify-content: center;

border: 1px solid var(--eds-theme-color-border-utility-default-low-emphasis);
border-right: none;

border-radius: calc(var(--eds-border-radius-full) * 1px);
border-top-right-radius: 0;
border-bottom-right-radius: 0;

color: var(--eds-theme-color-text-utility-default-primary);
}

.input-chip .input-chip__action-button {
display: flex;
align-items: center;
padding: calc(var(--eds-size-1) / 16 * 1rem);

border: 1px solid var(--eds-theme-color-border-utility-default-low-emphasis);
border-left: none;
border-radius: calc(var(--eds-border-radius-full) * 1px);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
outline: none;

color: var(--eds-theme-color-text-utility-default-primary);
}

.input-chip--disabled {
pointer-events: none;
}

/**
* Theme tokens
*/

.input-chip__action-button:hover {
background-color: var(--eds-theme-color-background-utility-default-no-emphasis-hover);
}

.input-chip__action-button:active {
background-color: var(--eds-theme-color-background-utility-default-no-emphasis-active);
}

.input-chip__action-button:focus-visible {
border: none;
box-shadow: inset 0 0 0 2px var(--eds-theme-color-border-utility-focus);
}

@supports not selector(:focus-visible) {
.input-chip__action-button:focus {
border: none;
box-shadow: inset 0 0 0 2px var(--eds-theme-color-border-utility-focus);
}
}
43 changes: 43 additions & 0 deletions src/components/InputChip/InputChip.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { StoryObj, Meta } from '@storybook/react';
import type React from 'react';

import { InputChip } from './InputChip';

export default {
title: 'Components/InputChip',
component: InputChip,
parameters: {
badges: ['intro-1.0', 'current-1.0'],
},
argTypes: {
onClick: {
control: false,
},
leadingComponent: {
control: false,
},
},
} as Meta<Args>;

type Args = React.ComponentProps<typeof InputChip>;

export const Default: StoryObj<Args> = {
args: {
label: 'Chip Label',
onClick: () => {},
},
};

export const WithLeadingIcon: StoryObj<Args> = {
args: {
...Default.args,
leadingComponent: 'person-encircled',
},
};

export const Disabled: StoryObj<Args> = {
args: {
...Default.args,
isDisabled: true,
},
};
7 changes: 7 additions & 0 deletions src/components/InputChip/InputChip.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { generateSnapshots } from '@chanzuckerberg/story-utils';
import * as stories from './InputChip.stories';
import type { StoryFile } from '../../util/utility-types';

describe('<InputChip />', () => {
generateSnapshots(stories as StoryFile);
});
81 changes: 81 additions & 0 deletions src/components/InputChip/InputChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import clsx from 'clsx';
import React, { type MouseEventHandler } from 'react';
import type { Size } from '../../util/variant-types';
import Icon, { type IconName } from '../Icon';
import Text from '../Text';

import styles from './InputChip.module.css';

export interface Props {
// Component API
/**
* CSS class names that can be appended to the component.
*/
className?: string;
// Design API
/**
* Whether the chip is in a non-interactive, disabled state
*/
isDisabled?: boolean;
/**
* Text used in the chip to give it a description
*/
label: string;
/**
* Leading glyph (icon) for the chip
*/
leadingComponent: IconName | React.ReactNode; // TODO: check that it only allows Avatar
/**
* click handler for the action button on the chip (ex: to dismiss or remove the chip from the screen)
*/
onClick?: MouseEventHandler;
/**
* The display size of the chip
*/
size?: Extract<Size, 'sm' | 'md'>;
}

/**
* `import {InputChip} from "@chanzuckerberg/eds";`
*
* Compact, interactive elements used to display user-generated information.
*/
export const InputChip = ({
className,
isDisabled,
label,
leadingComponent,
onClick,
size = 'md',
...other
}: Props) => {
const componentClassName = clsx(
styles['input-chip'],
isDisabled && styles[`input-chip--disabled`],
className,
);

return (
<div className={componentClassName} {...other}>
<div className={styles['input-chip__label']}>
{leadingComponent && typeof leadingComponent === 'string' && (
<Icon
className={styles['input-chip__leading-component']}
name={leadingComponent as IconName}
purpose="decorative"
/>
)}
<Text as="span" preset="body-xs">
{label}
</Text>
</div>
<button
className={styles['input-chip__action-button']}
disabled={isDisabled}
onClick={onClick}
>
<Icon name="close" purpose="informative" title={`remove ${label}`} />
</button>
</div>
);
};
115 changes: 115 additions & 0 deletions src/components/InputChip/__snapshots__/InputChip.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<InputChip /> Default story renders snapshot 1`] = `
<div
class="input-chip"
>
<div
class="input-chip__label"
>
<span
class="text text--body-xs"
>
Chip Label
</span>
</div>
<button
class="input-chip__action-button"
>
<svg
class="icon"
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<title>
remove Chip Label
</title>
<path
d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"
/>
</svg>
</button>
</div>
`;

exports[`<InputChip /> Disabled story renders snapshot 1`] = `
<div
class="input-chip input-chip--disabled"
>
<div
class="input-chip__label"
>
<span
class="text text--body-xs"
>
Chip Label
</span>
</div>
<button
class="input-chip__action-button"
disabled=""
>
<svg
class="icon"
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<title>
remove Chip Label
</title>
<path
d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"
/>
</svg>
</button>
</div>
`;

exports[`<InputChip /> WithLeadingIcon story renders snapshot 1`] = `
<div
class="input-chip"
>
<div
class="input-chip__label"
>
<svg
aria-hidden="true"
class="icon input-chip__leading-component"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.85 17.1C6.7 16.45 7.65 15.9375 8.7 15.5625C9.75 15.1875 10.85 15 12 15C13.15 15 14.25 15.1875 15.3 15.5625C16.35 15.9375 17.3 16.45 18.15 17.1C18.7333 16.4167 19.1875 15.6417 19.5125 14.775C19.8375 13.9083 20 12.9833 20 12C20 9.78333 19.2208 7.89583 17.6625 6.3375C16.1042 4.77917 14.2167 4 12 4C9.78333 4 7.89583 4.77917 6.3375 6.3375C4.77917 7.89583 4 9.78333 4 12C4 12.9833 4.1625 13.9083 4.4875 14.775C4.8125 15.6417 5.26667 16.4167 5.85 17.1ZM12 13C11.0167 13 10.1875 12.6625 9.5125 11.9875C8.8375 11.3125 8.5 10.4833 8.5 9.5C8.5 8.51667 8.8375 7.6875 9.5125 7.0125C10.1875 6.3375 11.0167 6 12 6C12.9833 6 13.8125 6.3375 14.4875 7.0125C15.1625 7.6875 15.5 8.51667 15.5 9.5C15.5 10.4833 15.1625 11.3125 14.4875 11.9875C13.8125 12.6625 12.9833 13 12 13ZM12 22C10.6167 22 9.31667 21.7375 8.1 21.2125C6.88333 20.6875 5.825 19.975 4.925 19.075C4.025 18.175 3.3125 17.1167 2.7875 15.9C2.2625 14.6833 2 13.3833 2 12C2 10.6167 2.2625 9.31667 2.7875 8.1C3.3125 6.88333 4.025 5.825 4.925 4.925C5.825 4.025 6.88333 3.3125 8.1 2.7875C9.31667 2.2625 10.6167 2 12 2C13.3833 2 14.6833 2.2625 15.9 2.7875C17.1167 3.3125 18.175 4.025 19.075 4.925C19.975 5.825 20.6875 6.88333 21.2125 8.1C21.7375 9.31667 22 10.6167 22 12C22 13.3833 21.7375 14.6833 21.2125 15.9C20.6875 17.1167 19.975 18.175 19.075 19.075C18.175 19.975 17.1167 20.6875 15.9 21.2125C14.6833 21.7375 13.3833 22 12 22ZM12 20C12.8833 20 13.7167 19.8708 14.5 19.6125C15.2833 19.3542 16 18.9833 16.65 18.5C16 18.0167 15.2833 17.6458 14.5 17.3875C13.7167 17.1292 12.8833 17 12 17C11.1167 17 10.2833 17.1292 9.5 17.3875C8.71667 17.6458 8 18.0167 7.35 18.5C8 18.9833 8.71667 19.3542 9.5 19.6125C10.2833 19.8708 11.1167 20 12 20ZM12 11C12.4333 11 12.7917 10.8583 13.075 10.575C13.3583 10.2917 13.5 9.93333 13.5 9.5C13.5 9.06667 13.3583 8.70833 13.075 8.425C12.7917 8.14167 12.4333 8 12 8C11.5667 8 11.2083 8.14167 10.925 8.425C10.6417 8.70833 10.5 9.06667 10.5 9.5C10.5 9.93333 10.6417 10.2917 10.925 10.575C11.2083 10.8583 11.5667 11 12 11Z"
/>
</svg>
<span
class="text text--body-xs"
>
Chip Label
</span>
</div>
<button
class="input-chip__action-button"
>
<svg
class="icon"
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<title>
remove Chip Label
</title>
<path
d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"
/>
</svg>
</button>
</div>
`;
1 change: 1 addition & 0 deletions src/components/InputChip/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { InputChip as default } from './InputChip';
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export { default as FieldNote } from './components/FieldNote';
export { default as Fieldset } from './components/Fieldset';
export { default as Icon } from './components/Icon';
export { default as InlineNotification } from './components/InlineNotification';
export { default as InputChip } from './components/InputChip';
export { default as InputField } from './components/InputField';
export { default as Link } from './components/Link';
export { default as LoadingIndicator } from './components/LoadingIndicator';
Expand Down
Loading