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) => ;
-
-
-
-
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();