Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nice-wings-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@skillsoft/gamut': minor
---

Migrates a batch of components out of the retiring packages/styleguide into co-located .stories.tsx files in packages/gamut/src, as stories-as-tests under @storybook/addon-vitest. Stories are regrouped from the old Atoms/Molecules taxonomy into semantic sidebar categories that match the new docs site, behavioral coverage from the old Jest tests is folded into play functions, and node-only tests are kept co-located for concerns that can't run in the browser.
5 changes: 5 additions & 0 deletions packages/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ const preview: Preview = {
disable: true,
},
deepControls: { enabled: true },
options: {
storySort: {
order: ['Actions', ['Buttons', ['Button Examples', '*']], '*'],
},
},
docs: {
container: DocsContainer,
theme,
Expand Down
59 changes: 59 additions & 0 deletions packages/gamut/src/Animation/ExpandInCollapseOut.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { Meta, StoryObj } from '@storybook/react';
import { AnimatePresence } from 'framer-motion';
import { useState } from 'react';
import { expect, waitFor } from 'storybook/test';

import { Box, FlexBox } from '../Box';
import { FillButton } from '../Button';
import { ExpandInCollapseOut } from './ExpandInCollapseOut';

const meta: Meta<typeof ExpandInCollapseOut> = {
title: 'Media & Assets/Animations/ExpandInCollapseOut',
component: ExpandInCollapseOut,
args: {
children:
"I got bored one day - and I put everything on a bagel. Everything. All my hopes and dreams, my old report cards, every breed of dog, every last personal ad on craigslist. Sesame. Poppy seed. Salt. And it collapsed in on itself. 'Cause, you see, when you really put everything on a bagel, it becomes this.",
},
};

export default meta;

type Story = StoryObj<typeof ExpandInCollapseOut>;

export const Default: Story = {
render: function ExpandInCollapseOutDemo({ children }) {
const [isExpanded, setExpanded] = useState(true);

return (
<FlexBox column>
<Box>
<FillButton onClick={() => setExpanded(!isExpanded)}>
Look, the text expands in and collapses out!
</FillButton>
</Box>
<Box mt={8}>
<AnimatePresence>
{isExpanded && (
<ExpandInCollapseOut>{children}</ExpandInCollapseOut>
)}
</AnimatePresence>
</Box>
</FlexBox>
);
},
play: async ({ canvas, userEvent }) => {
const toggle = canvas.getByRole('button', {
name: 'Look, the text expands in and collapses out!',
});

canvas.getByText(/put everything on a bagel/i);

await userEvent.click(toggle);
await waitFor(() =>
expect(canvas.queryByText(/put everything on a bagel/i)).toBeNull()
);

await userEvent.click(toggle);
await canvas.findByText(/put everything on a bagel/i);
},
};
53 changes: 53 additions & 0 deletions packages/gamut/src/Animation/FadeInSlideOut.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { Meta, StoryObj } from '@storybook/react';
import { AnimatePresence } from 'framer-motion';
import { useState } from 'react';
import { expect } from 'storybook/test';

import { Box, FlexBox } from '../Box';
import { FillButton } from '../Button';
import { FadeInSlideOut } from './FadeInSlideOut';

const meta: Meta<typeof FadeInSlideOut> = {
title: 'Media & Assets/Animations/FadeInSlideOut',
component: FadeInSlideOut,
};

export default meta;

type Story = StoryObj<typeof FadeInSlideOut>;

export const Default: Story = {
render: function FadeInSlideOutDemo() {
const [isVisible, setIsVisible] = useState(false);

return (
<FlexBox column>
<Box>
<FillButton onClick={() => setIsVisible(!isVisible)}>
{isVisible ? 'Thanks!' : 'Click me.'}
</FillButton>
</Box>
<Box height="50px" mt={8}>
<AnimatePresence>
{isVisible && (
<FadeInSlideOut>
<Box border={1} p={8}>
Even in a stupid, stupid universe where we have hot dogs for
fingers, we get very good with our feet.
</Box>
</FadeInSlideOut>
)}
</AnimatePresence>
</Box>
</FlexBox>
);
},
play: async ({ canvas, userEvent }) => {
await expect(canvas.queryByText(/hot dogs for/i)).toBeNull();

await userEvent.click(canvas.getByRole('button', { name: 'Click me.' }));

await canvas.findByText(/hot dogs for/i);
canvas.getByRole('button', { name: 'Thanks!' });
},
};
65 changes: 65 additions & 0 deletions packages/gamut/src/Animation/Rotation.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { MiniChevronDownIcon } from '@skillsoft/gamut-icons';
import type { Meta, StoryObj } from '@storybook/react';
import { useEffect, useState } from 'react';

import { StrokeButton } from '../Button';
import { Rotation } from './Rotation';

const meta: Meta<typeof Rotation> = {
title: 'Media & Assets/Animations/Rotation',
component: Rotation,
args: {
rotated: false,
},
argTypes: {
degrees: {
table: {
type: { summary: 'number' },
defaultValue: { summary: '180' },
},
},
height: {
table: {
type: { summary: 'number' },
defaultValue: { summary: '16' },
},
},
width: {
table: {
type: { summary: 'number' },
defaultValue: { summary: '16' },
},
},
},
};

export default meta;

type Story = StoryObj<typeof Rotation>;

export const Default: Story = {
render: function RotationDemo(args) {
const [isRotated, setRotated] = useState(args.rotated);

useEffect(() => {
setRotated(args.rotated);
}, [args.rotated]);

return (
<StrokeButton
aria-label="Toggle rotation demo"
onClick={() => setRotated(!isRotated)}
>
<Rotation {...args} rotated={isRotated}>
<MiniChevronDownIcon />
</Rotation>
</StrokeButton>
);
},
play: async ({ canvas, userEvent }) => {
await userEvent.click(
canvas.getByRole('button', { name: 'Toggle rotation demo' })
);
canvas.getByRole('img', { hidden: true });
},
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Badge, FlexBox } from '@skillsoft/gamut';
import * as icons from '@skillsoft/gamut-icons';
import type { Meta, StoryObj } from '@storybook/react';

import { TertiaryFillExample } from './examples';
import { Box, FlexBox } from '../Box';
import { Badge } from '.';

const meta: Meta<typeof Badge> = {
title: 'Status/Badge',
component: Badge,
args: { children: 'Badge' },
argTypes: {
Expand All @@ -21,6 +22,9 @@ type Story = StoryObj<typeof Badge>;

export const Default: Story = {
args: {},
play: ({ canvas }) => {
canvas.getByText('Badge');
},
};

export const Secondary: Story = {
Expand All @@ -36,9 +40,20 @@ export const Tertiary: Story = {
};

export const TertiaryFill: Story = {
render: () => {
return <TertiaryFillExample />;
args: {
icon: icons.MiniStarIcon,
variant: 'tertiaryFill',
},
decorators: [
(Story) => (
<Box bg="background-primary" height="30px" pt={16}>
<Box left={12} position="absolute" top={12}>
<Story />
</Box>
<Box bg="text" height="100%" width="100%" />
</Box>
),
],
};

export const Accent: Story = {
Expand Down
14 changes: 0 additions & 14 deletions packages/gamut/src/Badge/__tests__/Badge.test.tsx

This file was deleted.

133 changes: 133 additions & 0 deletions packages/gamut/src/Button/ButtonExamples.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import {
MiniArrowLeftIcon,
MiniArrowRightIcon,
MiniDeleteIcon,
MiniRibbonIcon,
SearchIcon,
} from '@skillsoft/gamut-icons';
import type { Meta, StoryObj } from '@storybook/react';

import { Box, FlexBox, GridBox } from '../Box';
import { FillButton } from './FillButton';
import { IconButton } from './IconButton';
import { buttonVariants } from './shared';
import { StrokeButton } from './StrokeButton';
import { TextButton } from './TextButton';

const meta: Meta<typeof FillButton> = {
title: 'Actions/Buttons/Button Examples',
component: FillButton,
args: {},
};

export default meta;
type Story = StoryObj<typeof FillButton>;

export const InlineIcons: Story = {
render: () => (
<FlexBox gap={16} row>
<FillButton icon={MiniArrowLeftIcon} maxWidth="fit-content">
FillButton
</FillButton>
<StrokeButton icon={MiniRibbonIcon} maxWidth="fit-content">
Leading icon
</StrokeButton>
<TextButton
icon={MiniArrowRightIcon}
iconPosition="right"
maxWidth="fit-content"
>
TextButton
</TextButton>
</FlexBox>
),
play: ({ canvas }) => {
canvas.getByRole('button', { name: 'FillButton' });
canvas.getByRole('button', { name: 'Leading icon' });
canvas.getByRole('button', { name: 'TextButton' });
},
};

const buttonScaleItems = [
{ name: 'FillButton', Component: FillButton },
{ name: 'IconButton', Component: IconButton },
{ name: 'StrokeButton', Component: StrokeButton },
{ name: 'TextButton', Component: TextButton },
] as const;

const sizes = ['normal', 'small', 'large'] as const;

const ButtonScale = ({ mode }: { mode: 'dark' | 'light' }) => {
const grid: React.ReactNode[] = [
<Box key="corner" />,
...buttonScaleItems.map(({ name }) => (
<Box fontWeight="title" key={`${name}-header`}>
{name}
</Box>
)),
];

buttonVariants.forEach((variant) => {
sizes.forEach((size) => {
grid.push(
<Box fontSize={14} key={`${mode}-${variant}-${size}-label`}>
{variant} · {size}
</Box>
);

buttonScaleItems.forEach(({ name, Component }) => {
const key = `${name}-${mode}-${variant}-${size}`;

if (name === 'IconButton') {
grid.push(
<IconButton
icon={size === 'small' ? MiniDeleteIcon : SearchIcon}
key={key}
mode={mode}
size={size}
tip="Here's a tip!"
variant={variant}
/>
);
return;
}

grid.push(
<Component
icon={MiniDeleteIcon}
key={key}
mode={mode}
size={size}
variant={variant}
>
{mode}
</Component>
);
});
});
});

return (
<GridBox
alignItems="center"
bg={mode === 'dark' ? 'navy' : 'white'}
columnGap={32}
gridAutoRows="3rem"
gridTemplateColumns="max-content repeat(4, minmax(50px, max-content))"
justifyItems="start"
p={32}
rowGap={16}
textColor={mode === 'dark' ? 'white' : 'navy'}
>
{grid}
</GridBox>
);
};

export const ButtonsLightMode: Story = {
render: () => <ButtonScale mode="light" />,
};

export const ButtonsDarkMode: Story = {
render: () => <ButtonScale mode="dark" />,
};
Loading
Loading