mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-13 06:04:13 -04:00
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:
2
tools/ui-components/src/panel/index.ts
Normal file
2
tools/ui-components/src/panel/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { Panel } from './panel';
|
||||
export type { PanelProps } from './types';
|
||||
55
tools/ui-components/src/panel/panel.stories.tsx
Normal file
55
tools/ui-components/src/panel/panel.stories.tsx
Normal 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;
|
||||
25
tools/ui-components/src/panel/panel.test.tsx
Normal file
25
tools/ui-components/src/panel/panel.test.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
94
tools/ui-components/src/panel/panel.tsx
Normal file
94
tools/ui-components/src/panel/panel.tsx
Normal 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;
|
||||
5
tools/ui-components/src/panel/types.ts
Normal file
5
tools/ui-components/src/panel/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
bsStyle?: 'primary' | 'info' | 'danger';
|
||||
}
|
||||
Reference in New Issue
Block a user