Skip to content

Commit

Permalink
feat: add form banner component
Browse files Browse the repository at this point in the history
  • Loading branch information
SashaShostyr committed May 31, 2024
1 parent b479374 commit 8dc8ee2
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 6 deletions.
56 changes: 56 additions & 0 deletions system/core/src/components/FormBanner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { css } from '../utils';

export const element = 'div';
export const className = 'form-banner';

const variants = [
'success',
'info',
'error',
'warning',
'neutral',
'purple',
'orange'
] as const;

export type FormBannerVariant = (typeof variants)[number];

export interface Props {
'data-variant': FormBannerVariant;
}

export const fullStyles = css`
display: flex;
gap: var(--spacing-l2);
color: var(--neutral-text);
background: var(--neutral-surface);
border-radius: var(--border-radius-small);
padding: var(--spacing-l4) var(--spacing-l3);
align-items: flex-start;
width: 100%;
&[data-variant='error'] {
color: var(--error-text);
background: var(--error-surface);
}
&[data-variant='warning'] {
color: var(--warning-text);
background: var(--warning-surface);
}
&[data-variant='success'] {
color: var(--success-text);
background: var(--success-surface);
}
&[data-variant='info'] {
color: var(--info-text);
background: var(--info-surface);
}
&[data-variant='purple'] {
color: var(--purple-text);
background: var(--purple-surface);
}
&[data-variant='orange'] {
color: var(--orange-text);
background: var(--orange-surface);
}
`;
1 change: 1 addition & 0 deletions system/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * as checkboxRadioLabel from './components/CheckboxRadioLabel';
export * as childAnchors from './components/ChildAnchors';
export * as chip from './components/Chip';
export * as chipRow from './components/ChipRow';
export * as formBanner from './components/FormBanner';
export * as iconButton from './components/IconButton';
export * as inputAlert from './components/InputAlert';
export * as inputCore from './components/InputCore';
Expand Down
7 changes: 4 additions & 3 deletions system/react-css/src/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,19 @@ export function configureDefaults(defaults: CoreConfigDefaults): void {
}

export function getSentimentIcon(
variant: 'success' | 'error' | 'neutral' | 'info' | 'warning',
variant: 'success' | 'error' | 'neutral' | 'info' | 'warning' | 'default',
size = getConfigDefault('iconSize')
): JSX.Element {
switch (variant) {
case 'success':
return <CheckmarkFilled size={size} />;
case 'error':
return <WarningAltFilled size={size} />;
case 'warning':
return <WarningAlt size={size} />;
case 'neutral':
case 'info':
case 'default':
return <Information size={size} />;
case 'warning':
return <WarningAlt size={size} />;
}
}
7 changes: 7 additions & 0 deletions system/react-css/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ export {
export type { Props as CheckboxRadioLabelProps } from './structuredComponents/CheckboxRadioLabel';
export { Chip } from './structuredComponents/Chip';
export type { Props as ChipProps } from './structuredComponents/Chip';
export {
FormBannerCore,
FormBannerMessage,
FormBannerIconWrapper,
FormBanner
} from './structuredComponents/FormBanner';
export type { Props as FormBannerProps } from './structuredComponents/FormBanner';
export { Input } from './structuredComponents/Input';
export type { Props as InputProps } from './structuredComponents/Input';
export { InputAlert } from './structuredComponents/InputAlert';
Expand Down
69 changes: 69 additions & 0 deletions system/react-css/src/structuredComponents/FormBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { formBanner } from '@tablecheck/tablekit-core';
import * as React from 'react';

import { getSentimentIcon } from '../config';

export type Props = formBanner.Props;

export const FormBannerCore = React.forwardRef<
HTMLDivElement,
Props & React.HTMLAttributes<HTMLDivElement>
>((props, ref) => (
<div
{...props}
ref={ref}
className={`${props.className || ''} form-banner`}
/>
));

export const FormBannerMessage = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>((props, ref) => (
<div
{...props}
ref={ref}
style={{ ...(props.style || {}), font: 'var(--body-2)' }}
/>
));

export const FormBannerIconWrapper = React.forwardRef<
HTMLSpanElement,
React.HTMLAttributes<HTMLSpanElement>
>((props, ref) => (
<span
{...props}
ref={ref}
style={{ ...(props.style || {}), display: 'inline-flex', marginTop: '2px' }}
className={`${props.className || ''} form-banner-icon`}
/>
));

interface ComposedProps extends Props {
/**
* Icon to display in the form banner. Pass `null` to display sentiment one.
*/
icon?: React.ReactNode;
children: React.ReactNode;
}

function getIcon(variant: Props['data-variant']): JSX.Element {
if (variant === 'purple' || variant === 'orange')
return getSentimentIcon('default');
return getSentimentIcon(variant);
}

export const FormBanner = React.forwardRef<
HTMLDivElement,
ComposedProps & React.HTMLAttributes<HTMLDivElement>
>((props, ref) => {
const { icon, title, children, ...passThrough } = props;
return (
<FormBannerCore {...passThrough} ref={ref}>
<FormBannerIconWrapper>
{icon ?? getIcon(passThrough['data-variant'])}
</FormBannerIconWrapper>
<FormBannerMessage>{children}</FormBannerMessage>
</FormBannerCore>
);
});
7 changes: 4 additions & 3 deletions system/react/src/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,19 @@ export function configureDefaults(defaults: CoreConfigDefaults): void {
}

export function getSentimentIcon(
variant: 'success' | 'error' | 'neutral' | 'info' | 'warning',
variant: 'success' | 'error' | 'neutral' | 'info' | 'warning' | 'default',
size = getConfigDefault('iconSize')
): JSX.Element {
switch (variant) {
case 'success':
return <CheckmarkFilled size={size} />;
case 'error':
return <WarningAltFilled size={size} />;
case 'warning':
return <WarningAlt size={size} />;
case 'neutral':
case 'info':
case 'default':
return <Information size={size} />;
case 'warning':
return <WarningAlt size={size} />;
}
}
7 changes: 7 additions & 0 deletions system/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ export {
export type { Props as AlertProps } from './structuredComponents/Alert';
export { chipStyledComponents, Chip } from './structuredComponents/Chip';
export type { Props as ChipProps } from './structuredComponents/Chip';
export {
FormBannerCore,
FormBannerMessage,
FormBannerIconWrapper,
FormBanner
} from './structuredComponents/FormBanner';
export type { Props as FormBannerProps } from './structuredComponents/FormBanner';
export { Input } from './structuredComponents/Input';
export type { Props as InputProps } from './structuredComponents/Input';
export { InputAlertInner, InputAlert } from './structuredComponents/InputAlert';
Expand Down
56 changes: 56 additions & 0 deletions system/react/src/structuredComponents/FormBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import styled from '@emotion/styled';
import { formBanner } from '@tablecheck/tablekit-core';
import * as React from 'react';

import { getSentimentIcon } from '../config';

export type Props = formBanner.Props;

export const FormBannerCore = styled.div<Props>`
${formBanner.fullStyles}
`;

export const FormBannerMessage = styled.div`
font: var(--body-2);
`;

export const FormBannerIconWrapper = React.forwardRef<
HTMLSpanElement,
React.HTMLAttributes<HTMLSpanElement>
>((props, ref) => (
<span
{...props}
ref={ref}
style={{ ...(props.style || {}), display: 'inline-flex', marginTop: '2px' }}
className={`${props.className || ''} form-banner-icon`}
/>
));

interface ComposedProps extends Props {
/**
* Icon to display in the form banner. Pass `null` to display sentiment one.
*/
icon?: React.ReactNode;
children: React.ReactNode;
}

function getIcon(variant: Props['data-variant']): JSX.Element {
if (variant === 'purple' || variant === 'orange')
return getSentimentIcon('default');
return getSentimentIcon(variant);
}

export const FormBanner = React.forwardRef<
HTMLDivElement,
ComposedProps & React.HTMLAttributes<HTMLDivElement>
>((props, ref) => {
const { icon, children, ...passThrough } = props;
return (
<FormBannerCore {...passThrough} ref={ref}>
<FormBannerIconWrapper>
{icon ?? getIcon(passThrough['data-variant'])}
</FormBannerIconWrapper>
<FormBannerMessage>{children}</FormBannerMessage>
</FormBannerCore>
);
});
52 changes: 52 additions & 0 deletions system/stories/src/FormBanner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Meta, StoryFn } from '@storybook/react';
import { formBanner } from '@tablecheck/tablekit-core';
import * as emotion from '@tablecheck/tablekit-react';
import * as css from '@tablecheck/tablekit-react-css';
import * as React from 'react';

const contentVariants: emotion.FormBannerProps['data-variant'][] = [
'success',
'info',
'error',
'warning',
'neutral',
'purple',
'orange'
];

type LayoutComponents = Record<'FormBanner', React.ElementType>;

export default {
title: 'Components/FormBanner',
component: emotion.FormBanner,
parameters: {
...formBanner,
variants: contentVariants,
auxiliaryComponents: [
emotion.FormBannerIconWrapper,
emotion.FormBannerMessage
]
}
} as Meta;

const Template: StoryFn = ({ FormBanner }) => (
<>
{contentVariants.map((variant) => (
<FormBanner key={variant} data-variant={variant}>
Important message to explain a form or a section of a form.
</FormBanner>
))}
</>
);

export const Emotion: StoryFn = Template.bind({});
Emotion.args = {
FormBanner: emotion.FormBanner
} satisfies LayoutComponents;
Emotion.parameters = { useEmotion: true };

export const Class: StoryFn = Template.bind({});
Class.args = {
FormBanner: css.FormBanner
} satisfies LayoutComponents;
Class.parameters = { useEmotion: false };

0 comments on commit 8dc8ee2

Please sign in to comment.