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(InputField)!: introduce 2.0 component #1898

Merged
merged 1 commit into from
Mar 21, 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
1 change: 1 addition & 0 deletions src/components/Button/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { Button as default } from './Button';
export type { ButtonProps } from './Button';
export { Button as ButtonV2 } from './Button-v2';
52 changes: 52 additions & 0 deletions src/components/FieldNote/FieldNote-v2.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
@import '../../design-tokens/mixins.css';

/*------------------------------------*\
# FIELD NOTE
\*------------------------------------*/

/**
* Fieldnote
*/
.field-note {
font: var(--eds-theme-typography-body-sm);

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

/**
* Fieldnote icon
*/
.field-note__icon {
position: relative;
margin-right: 0.25rem;
}

/**
* Disabled variant
*/
.field-note--disabled {
/* TODO-AH: figure out field note treatment and tokens */
color: var(--eds-theme-color-text-utility-disabled-primary);
}

/**
* Error variant
*/
.field-note--error {
color: var(--eds-theme-color-text-utility-critical);

& > .field-note__icon {
/* TODO-AH: using the text token for this icon since they match and no icon tokens existed in the sheet */
color: var(--eds-theme-color-text-utility-critical);
}
}


.field-note--warning {
color: var(--eds-theme-color-text-utility-warning);

& > .field-note__icon {
/* TODO-AH: using the text token for this icon since they match and no icon tokens existed in the sheet */
color: var(--eds-theme-color-text-utility-warning);
}
}
58 changes: 58 additions & 0 deletions src/components/FieldNote/FieldNote-v2.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { StoryObj, Meta } from '@storybook/react';
import React from 'react';

import { FieldNote } from './FieldNote-v2';
import { LinkV2 as Link } from '../Link';
import Text from '../Text';

export default {
title: 'Components/V2/FieldNote',
component: FieldNote,
parameters: {
badges: ['intro-1.0', 'current-2.0'],
},
} as Meta<Args>;

type Args = React.ComponentProps<typeof FieldNote>;

export const Default: StoryObj<Args> = {
args: {
children: 'This is a fieldnote.',
id: 'field-1',
},
};

export const WithErrorIcon: StoryObj<Args> = {
args: {
children: 'This is a fieldnote.',
id: 'field-1',
icon: 'person-add',
isError: true,
},
};

export const WithIcon: StoryObj<Args> = {
args: {
children: 'This is a fieldnote.',
id: 'field-1',
icon: 'person-add',
},
};

export const WithText: StoryObj<Args> = {
args: {
children: (
<div className="max-w-xl">
<Text className="mb-6">Here is a field note that involves:</Text>
<ul className="ml-4 list-disc">
<li>Multiple lines</li>
<li>Arbitrary HTML text</li>
<li>
Even <Link href="#">text links</Link>
</li>
</ul>
</div>
),
id: 'field-1',
},
};
88 changes: 88 additions & 0 deletions src/components/FieldNote/FieldNote-v2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import clsx from 'clsx';
import type { ReactNode } from 'react';
import React from 'react';
import Icon, { type IconName } from '../Icon';
import styles from './FieldNote-v2.module.css';

export interface Props {
// Component API
/**
* Child node(s) that can be nested inside component
*/
children?: ReactNode;
/**
* CSS class names that can be appended to the component.
*/
className?: string;
/**
* HTML id for the component
*/
id?: string;
// Design API
/**
* Toggles disabled styling of the field note.
*/
disabled?: boolean;
/**
* Icon to use when an "icon" variant of the avatar. Default is "dangerous"
*/
icon?: IconName;
/**
* Whether there is an error state for the field note text (and icon)
*/
isError?: boolean;
/**
* Whether there is a warning state for the field note text (and icon)
*/
isWarning?: boolean;
}

/**
* `import {FieldNote} from "@chanzuckerberg/eds";`
*
* Fieldnote component wraps text to describe other components.
*/
export const FieldNote = ({
children,
className,
id,
disabled,
icon,
isError,
isWarning,
...other
}: Props) => {
const componentClassName = clsx(
styles['field-note'],
disabled && styles['field-note--disabled'],
isError && styles['field-note--error'],
isWarning && styles['field-note--warning'],
className,
);

let iconToUse = icon;
if (isError) {
iconToUse = 'dangerous';
} else if (isWarning) {
iconToUse = 'warning';
} else if (icon) {
iconToUse = icon;
}

return (
<div className={componentClassName} id={id} {...other}>
{(isError || isWarning || iconToUse) && (
<Icon
className={styles['field-note__icon']}
name={iconToUse}
purpose="informative"
size="1rem"
title={isError ? 'error' : 'warning'}
/>
)}
{children}
</div>
);
};

FieldNote.displayName = 'FieldNote';
1 change: 1 addition & 0 deletions src/components/FieldNote/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { FieldNote as default } from './FieldNote';
export { FieldNote as FieldNoteV2 } from './FieldNote-v2';
12 changes: 12 additions & 0 deletions src/components/Input/Input-v2.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@import '../../design-tokens/mixins.css';

/*------------------------------------*\
    # INPUT
\*------------------------------------*/

/**
* Default input styles
*/
.input {
@mixin inputStylesV2;
}
114 changes: 114 additions & 0 deletions src/components/Input/Input-v2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import clsx from 'clsx';
import type { ChangeEventHandler } from 'react';
import React, { forwardRef } from 'react';
import styles from './Input-v2.module.css';

export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
/**
* Aria-label to provide an accesible name for the text input if no visible label is provided.
*/
'aria-label'?: string;
/**
* CSS class names that can be appended to the component.
*/
className?: string;
/**
* Disables the input and prevents editing the contents
*/
disabled?: boolean;
/**
* HTML id for the component
*/
id?: string;
/**
* Gives a hint as to the type of data needed for text input
*/
inputMode?:
| 'text'
| 'email'
| 'url'
| 'search'
| 'tel'
| 'none'
| 'numeric'
| 'decimal';
/**
* Maximum number the input can take.
*/
max?: number | string;
/**
* Minimum number the input can take.
*/
min?: number | string;
/**
* HTML name attribute for the input
*/
name?: string;
/**
* Function that fires when field value has changed
*/
onChange?: ChangeEventHandler<HTMLInputElement>;
/**
* Placeholder attribute for input. Note: placeholder should be used sparingly
*/
placeholder?: string;
/**
* Toggles the form control's interactivity. When `readOnly` is set to `true`, the form control is not interactive
*/
readOnly?: boolean;
/**
* Indicates that field is required for form to be successfully submitted
*/
required?: boolean;
/**
* Title attribute on input
*/
title?: string;
/**
* HTML type attribute, allowing switching between text, password, and other HTML5 input field types
*/
type?: React.HTMLInputTypeAttribute;
/**
* The value of the input
*/
value?: string | number;
/**
* The default value of the input
*/
defaultValue?: string | number;
// Design API
/**
* Error state of the form field
*/
isError?: boolean;
/**
* Whether there is a warning state for the field note text (and icon)
*/
isWarning?: boolean;
};

/**
* Input component for one line of text.
*/
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ className, disabled, id, isError, isWarning, ...other }, ref) => {
const componentClassName = clsx(
styles['input'],
isError && styles['error'],
isWarning && styles['warning'],
className,
);

return (
<input
className={componentClassName}
disabled={disabled}
id={id}
ref={ref}
{...other}
/>
);
},
);

Input.displayName = 'Input';
1 change: 1 addition & 0 deletions src/components/Input/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { Input as default } from './Input';
export type { InputProps } from './Input';
export { Input as InputV2 } from './Input-v2';
Loading
Loading