diff --git a/packages/manager/.changeset/pr-10015-tech-stories-1703101491856.md b/packages/manager/.changeset/pr-10015-tech-stories-1703101491856.md new file mode 100644 index 00000000000..9c8fc08bc74 --- /dev/null +++ b/packages/manager/.changeset/pr-10015-tech-stories-1703101491856.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +ColorPalette and CircleProgress v7 storybook migration ([#10015](https://github.com/linode/manager/pull/10015)) diff --git a/packages/manager/src/components/CircleProgress/CircleProgress.stories.mdx b/packages/manager/src/components/CircleProgress/CircleProgress.stories.mdx deleted file mode 100644 index cb82e380a0b..00000000000 --- a/packages/manager/src/components/CircleProgress/CircleProgress.stories.mdx +++ /dev/null @@ -1,35 +0,0 @@ -import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs'; -import { CircleProgress } from './CircleProgress'; - - - -# Circle Progress - -Use for short, indeterminate activities requiring user attention. - -export const Template = (args) => ; - - - - {Template.bind()} - - - - diff --git a/packages/manager/src/components/CircleProgress/CircleProgress.stories.tsx b/packages/manager/src/components/CircleProgress/CircleProgress.stories.tsx new file mode 100644 index 00000000000..28b1e392d38 --- /dev/null +++ b/packages/manager/src/components/CircleProgress/CircleProgress.stories.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import { CircleProgress } from './CircleProgress'; + +import type { Meta, StoryObj } from '@storybook/react'; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => , +}; + +const meta: Meta = { + component: CircleProgress, + title: 'Components/Loading States/Circle Progress', +}; + +export default meta; diff --git a/packages/manager/src/components/CircleProgress/CircleProgress.test.tsx b/packages/manager/src/components/CircleProgress/CircleProgress.test.tsx new file mode 100644 index 00000000000..b9416f0f1e6 --- /dev/null +++ b/packages/manager/src/components/CircleProgress/CircleProgress.test.tsx @@ -0,0 +1,54 @@ +import React from 'react'; + +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { CircleProgress } from './CircleProgress'; + +const CONTENT_LOADING = 'Content is loading'; + +describe('CircleProgress', () => { + it('renders a CircleProgress properly', () => { + const screen = renderWithTheme(); + + const circleProgress = screen.getByLabelText(CONTENT_LOADING); + expect(circleProgress).toBeVisible(); + const circle = screen.getByTestId('circle-progress'); + expect(circle).toBeInTheDocument(); + expect(circle).toHaveStyle('width: 124px; height: 124px;'); + const innerCircle = screen.getByTestId('inner-circle-progress'); + expect(innerCircle).toBeInTheDocument(); + }); + + it('renders a mini CircleProgress', () => { + const screen = renderWithTheme(); + + const circleProgress = screen.getByLabelText(CONTENT_LOADING); + expect(circleProgress).toBeVisible(); + expect(circleProgress).toHaveStyle('width: 40px; height: 40px;'); + }); + + it('sets a mini CircleProgress with no padding', () => { + const screen = renderWithTheme(); + + const circleProgress = screen.getByLabelText(CONTENT_LOADING); + expect(circleProgress).toBeVisible(); + expect(circleProgress).toHaveStyle('width: 22px; height: 22px;'); + }); + + it('sets a mini CircleProgress with a custom size', () => { + const screen = renderWithTheme(); + + const circleProgress = screen.getByLabelText(CONTENT_LOADING); + expect(circleProgress).toBeVisible(); + expect(circleProgress).toHaveStyle('width: 25px; height: 25px;'); + }); + + it('renders a CircleProgress without the inner circle', () => { + const screen = renderWithTheme(); + + const circleProgress = screen.getByLabelText(CONTENT_LOADING); + expect(circleProgress).toBeVisible(); + const innerCircle = screen.queryByTestId('inner-circle-progress'); + expect(innerCircle).not.toBeInTheDocument(); + }); +}); diff --git a/packages/manager/src/components/CircleProgress/CircleProgress.tsx b/packages/manager/src/components/CircleProgress/CircleProgress.tsx index 308a5d61219..24fe6e240fb 100644 --- a/packages/manager/src/components/CircleProgress/CircleProgress.tsx +++ b/packages/manager/src/components/CircleProgress/CircleProgress.tsx @@ -9,26 +9,37 @@ import { import { omittedProps } from 'src/utilities/omittedProps'; interface CircleProgressProps extends CircularProgressProps { + /** + * Additional child elements to pass in + */ children?: JSX.Element; - className?: string; + /** + * Displays a smaller version of the circle progress. + */ mini?: boolean; + /** + * If true, will not show an inner circle beneath the spinning circle + */ noInner?: boolean; + /** + * Removes the padding for `mini` circle progresses only. + */ noPadding?: boolean; + /** + * To be primarily used with mini and noPadding. Set spinner to a custom size. + */ size?: number; + /** + * Additional styles to apply to the root element. + */ sx?: SxProps; } +/** + * Use for short, indeterminate activities requiring user attention. + */ const CircleProgress = (props: CircleProgressProps) => { - const { - children, - className, - mini, - noInner, - noPadding, - size, - sx, - ...rest - } = props; + const { children, mini, noInner, noPadding, size, sx, ...rest } = props; const variant = typeof props.value === 'number' ? 'determinate' : 'indeterminate'; @@ -48,16 +59,12 @@ const CircleProgress = (props: CircleProgressProps) => { } return ( - + {children !== undefined && ( {children} )} {noInner !== true && ( - + )} diff --git a/packages/manager/src/components/ColorPalette/ColorPalette.stories.mdx b/packages/manager/src/components/ColorPalette/ColorPalette.stories.mdx deleted file mode 100644 index 4e741748053..00000000000 --- a/packages/manager/src/components/ColorPalette/ColorPalette.stories.mdx +++ /dev/null @@ -1,21 +0,0 @@ -import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs'; -import { ColorPalette } from './ColorPalette'; - - - -# Color Palette - -Add a new color to the palette, especially another tint of gray or blue, only after exhausting the option of using an existing color. - -- Colors used in light mode are located in `foundations/light.ts` -- Colors used in dark mode are located in `foundations/dark.ts` - -If a color does not exist in the current palette and is only used once, consider applying the color conditionally: - -`theme.name === 'light' ? '#fff' : '#000'` - - - - - - diff --git a/packages/manager/src/components/ColorPalette/ColorPalette.stories.tsx b/packages/manager/src/components/ColorPalette/ColorPalette.stories.tsx new file mode 100644 index 00000000000..ea406d68902 --- /dev/null +++ b/packages/manager/src/components/ColorPalette/ColorPalette.stories.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +import { ColorPalette } from './ColorPalette'; + +import type { Meta, StoryObj } from '@storybook/react'; + +export const Default: StoryObj = { + render: () => , +}; + +const meta: Meta = { + component: ColorPalette, + title: 'Design System/Color Palette', +}; + +export default meta; diff --git a/packages/manager/src/components/ColorPalette/ColorPalette.test.tsx b/packages/manager/src/components/ColorPalette/ColorPalette.test.tsx new file mode 100644 index 00000000000..a9f6024520e --- /dev/null +++ b/packages/manager/src/components/ColorPalette/ColorPalette.test.tsx @@ -0,0 +1,132 @@ +import React from 'react'; + +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { ColorPalette } from './ColorPalette'; + +describe('Color Palette', () => { + it('renders the Color Palette', () => { + const { getAllByText, getByText } = renderWithTheme(); + + // primary colors + getByText('Primary Colors'); + getByText('theme.palette.primary.main'); + const mainHash = getAllByText('#3683dc'); + expect(mainHash).toHaveLength(2); + getByText('theme.palette.primary.light'); + getByText('#4d99f1'); + getByText('theme.palette.primary.dark'); + getByText('#2466b3'); + getByText('theme.palette.text.primary'); + const primaryHash = getAllByText('#606469'); + expect(primaryHash).toHaveLength(3); + getByText('theme.color.headline'); + const headlineHash = getAllByText('#32363c'); + expect(headlineHash).toHaveLength(2); + getByText('theme.palette.divider'); + const dividerHash = getAllByText('#f4f4f4'); + expect(dividerHash).toHaveLength(2); + const whiteColor = getAllByText('theme.color.white'); + expect(whiteColor).toHaveLength(2); + const whiteHash = getAllByText('#fff'); + expect(whiteHash).toHaveLength(3); + + // etc + getByText('Etc.'); + getByText('theme.color.red'); + getByText('#ca0813'); + getByText('theme.color.orange'); + getByText('#ffb31a'); + getByText('theme.color.yellow'); + getByText('#fecf2f'); + getByText('theme.color.green'); + getByText('#00b159'); + getByText('theme.color.teal'); + getByText('#17cf73'); + getByText('theme.color.border2'); + getByText('#c5c6c8'); + getByText('theme.color.border3'); + getByText('#eee'); + getByText('theme.color.grey1'); + getByText('#abadaf'); + getByText('theme.color.grey2'); + getByText('#e7e7e7'); + getByText('theme.color.grey3'); + getByText('#ccc'); + getByText('theme.color.grey4'); + getByText('#8C929D'); + getByText('theme.color.grey5'); + getByText('#f5f5f5'); + getByText('theme.color.grey6'); + const borderGreyHash = getAllByText('#e3e5e8'); + expect(borderGreyHash).toHaveLength(3); + getByText('theme.color.grey7'); + getByText('#e9eaef'); + getByText('theme.color.grey8'); + getByText('#dbdde1'); + getByText('theme.color.grey9'); + const borderGrey9Hash = getAllByText('#f4f5f6'); + expect(borderGrey9Hash).toHaveLength(3); + getByText('theme.color.black'); + getByText('#222'); + getByText('theme.color.offBlack'); + getByText('#444'); + getByText('theme.color.boxShadow'); + getByText('#ddd'); + getByText('theme.color.boxShadowDark'); + getByText('#aaa'); + getByText('theme.color.blueDTwhite'); + getByText('theme.color.tableHeaderText'); + getByText('rgba(0, 0, 0, 0.54)'); + getByText('theme.color.drawerBackdrop'); + getByText('rgba(255, 255, 255, 0.5)'); + getByText('theme.color.label'); + getByText('#555'); + getByText('theme.color.disabledText'); + getByText('#c9cacb'); + getByText('theme.color.tagButton'); + getByText('#f1f7fd'); + getByText('theme.color.tagIcon'); + getByText('#7daee8'); + + // background colors + getByText('Background Colors'); + getByText('theme.bg.app'); + getByText('theme.bg.main'); + getByText('theme.bg.offWhite'); + getByText('#fbfbfb'); + getByText('theme.bg.lightBlue1'); + getByText('#f0f7ff'); + getByText('theme.bg.lightBlue2'); + getByText('#e5f1ff'); + getByText('theme.bg.white'); + getByText('theme.bg.tableHeader'); + getByText('#f9fafa'); + getByText('theme.bg.primaryNavPaper'); + getByText('#3a3f46'); + getByText('theme.bg.mainContentBanner'); + getByText('#33373d'); + getByText('theme.bg.bgPaper'); + getByText('#ffffff'); + getByText('theme.bg.bgAccessRow'); + getByText('#fafafa'); + getByText('theme.bg.bgAccessRowTransparentGradient'); + getByText('rgb(255, 255, 255, .001)'); + + // typography colors + getByText('Typography Colors'); + getByText('theme.textColors.linkActiveLight'); + getByText('#2575d0'); + getByText('theme.textColors.headlineStatic'); + getByText('theme.textColors.tableHeader'); + getByText('#888f91'); + getByText('theme.textColors.tableStatic'); + getByText('theme.textColors.textAccessTable'); + + // border colors + getByText('Border Colors'); + getByText('theme.borderColors.borderTypography'); + getByText('theme.borderColors.borderTable'); + getByText('theme.borderColors.divider'); + }); +}); diff --git a/packages/manager/src/components/ColorPalette/ColorPalette.tsx b/packages/manager/src/components/ColorPalette/ColorPalette.tsx index a6f04adfa4d..a3bfaadb121 100644 --- a/packages/manager/src/components/ColorPalette/ColorPalette.tsx +++ b/packages/manager/src/components/ColorPalette/ColorPalette.tsx @@ -42,6 +42,16 @@ const useStyles = makeStyles()((theme: Theme) => ({ }, })); +/** + * Add a new color to the palette, especially another tint of gray or blue, only after exhausting the option of using an existing color. + * + * - Colors used in light mode are located in `foundations/light.ts + * - Colors used in dark mode are located in `foundations/dark.ts` + * + * If a color does not exist in the current palette and is only used once, consider applying the color conditionally: + * + * `theme.name === 'light' ? '#fff' : '#000'` + */ export const ColorPalette = () => { const { classes } = useStyles(); const theme = useTheme();