mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat: swap panel component (#51687)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { Panel, Button } from '@freecodecamp/react-bootstrap';
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import type { RouteComponentProps } from '@reach/router';
|
||||
import React from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Container, Panel } from '@freecodecamp/ui';
|
||||
|
||||
import { Container } from '@freecodecamp/ui';
|
||||
import envData from '../../config/env.json';
|
||||
import { Spacer } from '../components/helpers';
|
||||
import FullWidthRow from '../components/helpers/full-width-row';
|
||||
@@ -28,7 +28,7 @@ function ShowUnsubscribed({
|
||||
<main>
|
||||
<FullWidthRow>
|
||||
<Spacer size='large' />
|
||||
<Panel bsStyle='primary' className='text-center'>
|
||||
<Panel variant='primary' className='text-center'>
|
||||
<Spacer size='medium' />
|
||||
<h2 data-playwright-test-label='main-heading'>
|
||||
{t('misc.unsubscribed')}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
Panel,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
ControlLabel,
|
||||
@@ -11,7 +10,7 @@ import type { TFunction } from 'i18next';
|
||||
import { Trans, withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { Col, Row } from '@freecodecamp/ui';
|
||||
import { Col, Row, Panel } from '@freecodecamp/ui';
|
||||
|
||||
import Login from '../components/Header/components/login';
|
||||
|
||||
@@ -84,11 +83,9 @@ function ShowUser({
|
||||
<main>
|
||||
<FullWidthRow>
|
||||
<Spacer size='large' />
|
||||
<Panel bsStyle='info' className='text-center'>
|
||||
<Panel variant='primary' className='text-center'>
|
||||
<Panel.Heading>
|
||||
<Panel.Title componentClass='h3'>
|
||||
{t('report.sign-in')}
|
||||
</Panel.Title>
|
||||
<Panel.Title>{t('report.sign-in')}</Panel.Title>
|
||||
</Panel.Heading>
|
||||
<Panel.Body className='text-center'>
|
||||
<Spacer size='large' />
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Panel } from '@freecodecamp/react-bootstrap';
|
||||
import Prism from 'prismjs';
|
||||
import React from 'react';
|
||||
import { ChallengeFile } from '../../redux/prop-types';
|
||||
import { Panel } from '@freecodecamp/ui';
|
||||
|
||||
import type { ChallengeFile } from '../../redux/prop-types';
|
||||
|
||||
type Props = {
|
||||
challengeFiles: Solution[] | null;
|
||||
@@ -25,7 +26,7 @@ function SolutionViewer({ challengeFiles, solution }: Props): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
{solutions.map(({ fileKey, ext, contents }) => (
|
||||
<Panel bsStyle='primary' className='solution-viewer' key={fileKey}>
|
||||
<Panel variant='primary' className='solution-viewer' key={fileKey}>
|
||||
<Panel.Heading>{ext.toUpperCase()}</Panel.Heading>
|
||||
<Panel.Body>
|
||||
<pre>
|
||||
|
||||
@@ -470,11 +470,6 @@ code {
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.help-block em {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
@@ -519,10 +514,6 @@ hr {
|
||||
color: var(--quaternary-color);
|
||||
}
|
||||
|
||||
.panel-default > .panel-heading {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.challenge-output span {
|
||||
font-size: 1rem;
|
||||
}
|
||||
@@ -544,12 +535,6 @@ pre {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.panel-primary > .panel-heading {
|
||||
background-color: transparent;
|
||||
border-color: var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.solution-viewer,
|
||||
.solution-viewer pre {
|
||||
margin-bottom: 0;
|
||||
@@ -593,10 +578,6 @@ pre {
|
||||
border-color: var(--tertiary-color);
|
||||
}
|
||||
|
||||
.panel-primary {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
blockquote footer,
|
||||
blockquote small,
|
||||
blockquote .small {
|
||||
|
||||
@@ -8,7 +8,7 @@ exports[`<Honesty /> <Honesty /> snapshot when isHonest is false: Honesty 1`] =
|
||||
settings.headings.honesty
|
||||
</SectionHeader>
|
||||
<FullWidthRow>
|
||||
<Uncontrolled(Panel)
|
||||
<d
|
||||
className="honesty-panel"
|
||||
>
|
||||
<p>
|
||||
@@ -42,7 +42,7 @@ exports[`<Honesty /> <Honesty /> snapshot when isHonest is false: Honesty 1`] =
|
||||
</a>
|
||||
</Trans>
|
||||
</p>
|
||||
</Uncontrolled(Panel)>
|
||||
</d>
|
||||
<Button
|
||||
active={false}
|
||||
aria-disabled={false}
|
||||
@@ -66,7 +66,7 @@ exports[`<Honesty /> <Honesty /> snapshot when isHonest is true: HonestyAccepted
|
||||
settings.headings.honesty
|
||||
</SectionHeader>
|
||||
<FullWidthRow>
|
||||
<Uncontrolled(Panel)
|
||||
<d
|
||||
className="honesty-panel"
|
||||
>
|
||||
<p>
|
||||
@@ -100,7 +100,7 @@ exports[`<Honesty /> <Honesty /> snapshot when isHonest is true: HonestyAccepted
|
||||
</a>
|
||||
</Trans>
|
||||
</p>
|
||||
</Uncontrolled(Panel)>
|
||||
</d>
|
||||
<Button
|
||||
active={false}
|
||||
aria-disabled={true}
|
||||
|
||||
@@ -10,18 +10,6 @@
|
||||
background-color: var(--danger-background);
|
||||
border-color: var(--danger-background);
|
||||
}
|
||||
|
||||
.danger-zone .panel-heading {
|
||||
color: var(--danger-color);
|
||||
background-color: var(--danger-background);
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.danger-zone .panel-danger {
|
||||
border-color: var(--danger-background);
|
||||
}
|
||||
|
||||
.danger-zone p {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Button, Panel } from '@freecodecamp/react-bootstrap';
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import React, { useState } from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
import { Panel } from '@freecodecamp/ui';
|
||||
|
||||
import { deleteAccount, resetProgress } from '../../redux/settings/actions';
|
||||
import { FullWidthRow, Spacer } from '../helpers';
|
||||
@@ -44,7 +45,7 @@ function DangerZone({ deleteAccount, resetProgress, t }: DangerZoneProps) {
|
||||
|
||||
return (
|
||||
<FullWidthRow className='danger-zone text-center'>
|
||||
<Panel bsStyle='danger' id='danger-zone'>
|
||||
<Panel variant='danger' id='danger-zone'>
|
||||
<Panel.Heading>{t('settings.danger.heading')}</Panel.Heading>
|
||||
<Spacer size='medium' />
|
||||
<p>{t('settings.danger.be-careful')}</p>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Button, Panel } from '@freecodecamp/react-bootstrap';
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Panel } from '@freecodecamp/ui';
|
||||
|
||||
import { FullWidthRow } from '../helpers';
|
||||
import SectionHeader from './section-header';
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
.user-panel {
|
||||
border-color: var(--highlight-background);
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background-color: var(--highlight-color);
|
||||
color: var(--highlight-background);
|
||||
@@ -15,17 +11,6 @@
|
||||
border-color: var(--highlight-background);
|
||||
}
|
||||
|
||||
.user-token .panel-heading {
|
||||
color: var(--highlight-color);
|
||||
background-color: var(--highlight-background);
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.user-token .panel-info {
|
||||
border-color: var(--highlight-background);
|
||||
}
|
||||
|
||||
.user-token p {
|
||||
color: var(--highlight-color);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import { Button, Panel } from '@freecodecamp/react-bootstrap';
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import React, { Component } from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { Panel } from '@freecodecamp/ui';
|
||||
|
||||
import { deleteUserToken } from '../../redux/actions';
|
||||
import { FullWidthRow, Spacer } from '../helpers';
|
||||
@@ -32,7 +32,7 @@ class UserToken extends Component<UserTokenProps> {
|
||||
return (
|
||||
<div data-cy='user-token' className='user-token text-center'>
|
||||
<FullWidthRow>
|
||||
<Panel className='user-panel'>
|
||||
<Panel variant='info'>
|
||||
<Panel.Heading>{t('user-token.title')}</Panel.Heading>
|
||||
<Spacer size='medium' />
|
||||
<p>{t('user-token.delete-p1')}</p>
|
||||
|
||||
@@ -4,6 +4,7 @@ export { Alert, type AlertProps } from './alert';
|
||||
export { CloseButton } from './close-button';
|
||||
// export { Image } from './image';
|
||||
export { Table } from './table';
|
||||
export { Panel } from './panel';
|
||||
// export { ToggleButton } from './toggle-button';
|
||||
export { Dropdown } from './drop-down';
|
||||
export { MenuItem } from './drop-down/menu-item';
|
||||
|
||||
@@ -7,12 +7,12 @@ const story = {
|
||||
component: Panel,
|
||||
parameters: {
|
||||
controls: {
|
||||
include: ['className', 'bsStyle']
|
||||
include: ['className', 'variant']
|
||||
}
|
||||
},
|
||||
argType: {
|
||||
className: { control: { type: 'text' } },
|
||||
bsStyle: { option: ['primary', 'danger', 'info', undefined] }
|
||||
variant: { option: ['primary', 'danger', 'info', undefined] }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,19 +37,19 @@ Default.args = {
|
||||
export const Primary = Template.bind({});
|
||||
Primary.args = {
|
||||
children: <Child />,
|
||||
bsStyle: 'primary'
|
||||
variant: 'primary'
|
||||
};
|
||||
|
||||
export const Info = Template.bind({});
|
||||
Info.args = {
|
||||
children: <Child />,
|
||||
bsStyle: 'info'
|
||||
variant: 'info'
|
||||
};
|
||||
|
||||
export const Danger = Template.bind({});
|
||||
Danger.args = {
|
||||
children: <Child />,
|
||||
bsStyle: 'danger'
|
||||
variant: 'danger'
|
||||
};
|
||||
|
||||
export default story;
|
||||
|
||||
@@ -11,14 +11,14 @@ describe('<Panel />', () => {
|
||||
});
|
||||
it('PanelHead should inherit bsStyle', () => {
|
||||
render(
|
||||
<Panel role='article' bsStyle='primary'>
|
||||
<Panel role='article' variant='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'
|
||||
'outline-[1px] outline outline-foreground-primary text-foreground-primary px-3.5 py-2.5'
|
||||
);
|
||||
expect(screen.getByText('TestBody')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -2,34 +2,30 @@ import React, { createContext, useContext } from 'react';
|
||||
|
||||
import { PanelProps } from './types';
|
||||
|
||||
type PanelContextProps = Pick<PanelProps, 'bsStyle'>;
|
||||
type PanelContextProps = Pick<PanelProps, 'variant'>;
|
||||
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 ';
|
||||
const border = {
|
||||
primary: 'border-foreground-primary',
|
||||
danger: 'border-foreground-danger',
|
||||
info: 'border-foreground-info'
|
||||
};
|
||||
|
||||
let bsStyleClass = defaultBorder;
|
||||
let headingStyles = headingPadding + defaultHeadingStyle;
|
||||
const heading = {
|
||||
primary:
|
||||
'outline-[1px] outline outline-foreground-primary text-foreground-primary',
|
||||
danger: 'bg-foreground-danger text-background-danger',
|
||||
info: 'bg-foreground-info text-background-info'
|
||||
};
|
||||
|
||||
const Body = ({
|
||||
children,
|
||||
props
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
props?: React.ComponentProps<'div'>;
|
||||
}): JSX.Element => {
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'div'>): JSX.Element => {
|
||||
const classes = [className, 'p-3.5'].join(' ');
|
||||
return (
|
||||
<div className='p-3.5' {...props}>
|
||||
<div className={classes} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -37,18 +33,18 @@ const Body = ({
|
||||
|
||||
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;
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<'div'>): JSX.Element => {
|
||||
const { variant } = useContext(PanelContext);
|
||||
|
||||
const headingStyles = variant
|
||||
? heading[variant]
|
||||
: 'outline outline-[1px] outline-background-tertiary';
|
||||
const classes = [className, headingStyles, 'px-3.5 py-2.5'].join(' ');
|
||||
|
||||
return (
|
||||
<div className={headingStyles} {...props}>
|
||||
<div className={classes} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -56,11 +52,8 @@ export const Heading = ({
|
||||
|
||||
export const Title = ({
|
||||
children,
|
||||
props
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
props?: React.ComponentProps<'h3'>;
|
||||
}): JSX.Element => {
|
||||
...props
|
||||
}: React.ComponentProps<'h3'>): JSX.Element => {
|
||||
return (
|
||||
<h3 className='text-inherit mb-0 text-xl' {...props}>
|
||||
{children}
|
||||
@@ -71,17 +64,18 @@ export const Title = ({
|
||||
export const Panel = ({
|
||||
children,
|
||||
className,
|
||||
bsStyle,
|
||||
variant,
|
||||
...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(' ');
|
||||
const variantClass = variant ? border[variant] : 'border-background-tertiary';
|
||||
const panelClassed = [
|
||||
'border-1 border-solid shadow-sm mb-6',
|
||||
variantClass,
|
||||
className
|
||||
].join(' ');
|
||||
|
||||
return (
|
||||
<PanelContext.Provider value={{ bsStyle }}>
|
||||
<PanelContext.Provider value={{ variant }}>
|
||||
<div className={panelClassed} {...restProps}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
bsStyle?: 'primary' | 'info' | 'danger';
|
||||
variant?: 'primary' | 'info' | 'danger';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user