feat(tools): add panel component (#49398)

* run panel gen code

* add the default style

* add tthe styles for the panel

* create context of bsStyle

* add panel heading

* add panel title

* add panel body

* create the story for the panel

* typo hiding the border

* border doesn't work for some reason

* only border-3 works in tailwind

* change for border-1 for better visual indication

* fix: bsStyle not applying to the panel

* add html props to the elements

* remove the info colors for our info colors

* fix heading style

* add Panel test

* use to have Class instead

* fix type

* component disregarding default attributes?

* remove duplication in the test

* use compound components

* clean the old logic for the bsStyle

* clean the exports

* add heading and bsstyle control

* fix the classes

* check for title body render

* remove extra types

---------

Co-authored-by: Ahmad Abdolsaheb <ahmad.abdolsaheb@gmail.com>
This commit is contained in:
Muhammed Mustafa
2023-04-19 16:37:13 +02:00
committed by GitHub
parent 7dcfc402dd
commit 6b869ec375
5 changed files with 181 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
export { Panel } from './panel';
export type { PanelProps } from './types';

View File

@@ -0,0 +1,55 @@
import React from 'react';
import { Story } from '@storybook/react';
import { Panel, PanelProps } from '.';
const story = {
title: 'Example/Panel',
component: Panel,
parameters: {
controls: {
include: ['className', 'bsStyle']
}
},
argType: {
className: { control: { type: 'text' } },
bsStyle: { option: ['primary', 'danger', 'info', undefined] }
}
};
const Child = () => {
return (
<>
<Panel.Heading>
<Panel.Title>Here is panel Heading</Panel.Title>
</Panel.Heading>
<Panel.Body>Here is Panel body</Panel.Body>
</>
);
};
const Template: Story<PanelProps> = args => <Panel {...args} />;
export const Default = Template.bind({});
Default.args = {
children: <Child />
};
export const Primary = Template.bind({});
Primary.args = {
children: <Child />,
bsStyle: 'primary'
};
export const Info = Template.bind({});
Info.args = {
children: <Child />,
bsStyle: 'info'
};
export const Danger = Template.bind({});
Danger.args = {
children: <Child />,
bsStyle: 'danger'
};
export default story;

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { Panel } from '.';
describe('<Panel />', () => {
it('PanelTitle should render', () => {
render(<Panel.Title>Test Title</Panel.Title>);
const title = screen.getByText('Test Title');
expect(title).toBeInTheDocument();
});
it('PanelHead should inherit bsStyle', () => {
render(
<Panel role='article' bsStyle='primary'>
<Panel.Heading>Test</Panel.Heading>
<Panel.Body>TestBody</Panel.Body>
</Panel>
);
expect(screen.getByRole('article')).toBeInTheDocument();
expect(screen.getByText('Test')).toHaveClass(
'border-b-1 border-solid border-foreground-primary text-foreground-primary'
);
expect(screen.getByText('TestBody')).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,94 @@
import React, { createContext, useContext } from 'react';
import { PanelProps } from './types';
type PanelContextProps = Pick<PanelProps, 'bsStyle'>;
const PanelContext = createContext<PanelContextProps>({});
const styles = 'border-1 border-solid shadow-sm mb-6';
const defaultBorder = 'border-background-tertiary';
const primaryBorder = 'border-foreground-primary';
const dangerBorder = 'border-foreground-danger';
const infoBorder = 'border-foreground-info';
const defaultHeadingStyle =
'border-b-1 border-solid border-background-tertiary';
const primaryHeadingStyle =
'border-b-1 border-solid border-foreground-primary text-foreground-primary';
const infoHeadingStyle = 'text-background-info bg-foreground-info';
const dangerHeadingStyle = 'text-background-danger bg-foreground-danger';
const headingPadding = 'px-2.5 py-3.5 ';
let bsStyleClass = defaultBorder;
let headingStyles = headingPadding + defaultHeadingStyle;
const Body = ({
children,
props
}: {
children?: React.ReactNode;
props?: React.ComponentProps<'div'>;
}): JSX.Element => {
return (
<div className='p-3.5' {...props}>
{children}
</div>
);
};
export const Heading = ({
children,
props
}: {
children?: React.ReactNode;
props?: React.ComponentProps<'div'>;
}): JSX.Element => {
const { bsStyle } = useContext(PanelContext);
if (bsStyle === 'primary') headingStyles = primaryHeadingStyle;
else if (bsStyle === 'danger') headingStyles = dangerHeadingStyle;
else if (bsStyle === 'info') headingStyles = infoHeadingStyle;
return (
<div className={headingStyles} {...props}>
{children}
</div>
);
};
export const Title = ({
children,
props
}: {
children?: React.ReactNode;
props?: React.ComponentProps<'h3'>;
}): JSX.Element => {
return (
<h3 className='text-inherit mb-0 text-xl' {...props}>
{children}
</h3>
);
};
export const Panel = ({
children,
className,
bsStyle,
...restProps
}: PanelProps): JSX.Element => {
if (bsStyle === 'primary') bsStyleClass = primaryBorder;
else if (bsStyle === 'danger') bsStyleClass = dangerBorder;
else if (bsStyle === 'info') bsStyleClass = infoBorder;
const panelClassed = [styles, bsStyleClass, className].join(' ');
return (
<PanelContext.Provider value={{ bsStyle }}>
<div className={panelClassed} {...restProps}>
{children}
</div>
</PanelContext.Provider>
);
};
Panel.Body = Body;
Panel.Heading = Heading;
Panel.Title = Title;

View File

@@ -0,0 +1,5 @@
import React from 'react';
export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> {
bsStyle?: 'primary' | 'info' | 'danger';
}