Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storybook: Use satisfies operator to get helpful errors about missing props #90570

Merged
merged 1 commit into from
May 15, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import {
getPlanFeaturesGrouped,
setTrailMapExperiment,
} from '@automattic/calypso-products';
import { Meta, StoryObj } from '@storybook/react';
import { ComparisonGrid, ComparisonGridExternalProps, useGridPlansForComparisonGrid } from '../..';
import type { Meta, StoryObj } from '@storybook/react';

const ComponentWrapper = (
props: ComparisonGridExternalProps & {
trailMapVariant: TrailMapVariantType;
props: Omit< ComparisonGridExternalProps, 'gridPlans' > & {
trailMapVariant?: TrailMapVariantType;
}
) => {
const gridPlans = useGridPlansForComparisonGrid( {
Expand Down Expand Up @@ -45,7 +45,7 @@ const ComponentWrapper = (
);
};

const defaultProps: Omit< ComparisonGridExternalProps, 'gridPlans' > = {
const defaultProps = {
allFeaturesList: getFeaturesList(),
coupon: undefined,
currentSitePlanSlug: undefined,
Expand All @@ -67,61 +67,61 @@ const defaultProps: Omit< ComparisonGridExternalProps, 'gridPlans' > = {
primary: {
text: 'test',
callback: () => {},
status: 'enabled',
status: 'enabled' as const,
},
postButtonText: '',
} ),
};

type Story = StoryObj< typeof ComponentWrapper >;
const meta = {
title: 'ComparisonGrid',
component: ComponentWrapper,
decorators: [
( Story, { args: { trailMapVariant } } ) => {
trailMapVariant && setTrailMapExperiment( trailMapVariant );
return <Story />;
},
],
} satisfies Meta< typeof ComponentWrapper >;
Comment on lines +76 to +85
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting TIL!

The docs explains it as a way to enforce the type
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html

I was curious and did some playing around with this feature
image.

The error is more prominent!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sharing this post which I found helpful: https://www.totaltypescript.com/clarifying-the-satisfies-operator

"When you use a colon, the type BEATS the value ... When you use satisfies, the value BEATS the type"

So in your example if we make newProp optional, satisfies narrows the type and provides a nice warning:

CleanShot 2024-05-15 at 13 24 07@2x


export const Start: Story = {
export default meta;

type Story = StoryObj< typeof meta >;

export const Start = {
name: '/start',
args: {
...defaultProps,
intent: 'plans-default-wpcom',
},
};
} satisfies Story;

export const TrailMapControl: Story = {
export const TrailMapControl = {
args: {
...Start.args,
trailMapVariant: 'control',
},
};
} satisfies Story;

export const TrailMapStructure: Story = {
export const TrailMapStructure = {
args: {
...TrailMapControl.args,
trailMapVariant: 'treatment_structure',
hideUnsupportedFeatures: true,
},
};
} satisfies Story;

export const TrailMapCopy: Story = {
export const TrailMapCopy = {
args: {
...TrailMapControl.args,
trailMapVariant: 'treatment_copy',
},
};
} satisfies Story;

export const TrailMapCopyAndStructure: Story = {
export const TrailMapCopyAndStructure = {
args: {
...TrailMapControl.args,
trailMapVariant: 'treatment_copy_and_structure',
hideUnsupportedFeatures: true,
},
};

const meta: Meta< typeof ComponentWrapper > = {
title: 'ComparisonGrid',
component: ComponentWrapper,
decorators: [
( Story, storyContext ) => {
setTrailMapExperiment( storyContext.args.trailMapVariant );
return <Story />;
},
],
};

export default meta;
} satisfies Story;
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import {
getPlanFeaturesGrouped,
setTrailMapExperiment,
} from '@automattic/calypso-products';
import { Meta, StoryObj } from '@storybook/react';
import {
FeaturesGrid,
FeaturesGridExternalProps,
useGridPlanForSpotlight,
useGridPlansForFeaturesGrid,
} from '../..';
import type { Meta, StoryObj } from '@storybook/react';

const ComponentWrapper = (
props: FeaturesGridExternalProps & {
trailMapVariant: TrailMapVariantType;
props: Omit< FeaturesGridExternalProps, 'gridPlans' > & {
trailMapVariant?: TrailMapVariantType;
}
) => {
const gridPlans = useGridPlansForFeaturesGrid( {
Expand Down Expand Up @@ -60,7 +60,7 @@ const ComponentWrapper = (
);
};

const defaultProps: Omit< FeaturesGridExternalProps, 'gridPlans' > = {
const defaultProps = {
allFeaturesList: getFeaturesList(),
coupon: undefined,
currentSitePlanSlug: undefined,
Expand All @@ -87,15 +87,28 @@ const defaultProps: Omit< FeaturesGridExternalProps, 'gridPlans' > = {
primary: {
text: 'test',
callback: () => {},
status: 'enabled',
status: 'enabled' as const,
},
postButtonText: '',
} ),
};

type Story = StoryObj< typeof ComponentWrapper >;
const meta = {
title: 'FeaturesGrid',
component: ComponentWrapper,
decorators: [
( Story, { args: { trailMapVariant } } ) => {
trailMapVariant && setTrailMapExperiment( trailMapVariant );
return <Story />;
},
],
} satisfies Meta< typeof ComponentWrapper >;

export const Plans: Story = {
export default meta;

type Story = StoryObj< typeof meta >;

export const Plans = {
name: '/plans',
args: {
...defaultProps,
Expand All @@ -104,55 +117,43 @@ export const Plans: Story = {
isInAdmin: true,
isInSignup: false,
},
};
} satisfies Story;

export const Newsletter: Story = {
export const Newsletter = {
name: '/setup/newsletter',
args: {
...defaultProps,
intent: 'plans-newsletter',
},
};
} satisfies Story;

export const TrailMapControl: Story = {
export const TrailMapControl = {
args: {
...Plans.args,
trailMapVariant: 'control',
gridPlanForSpotlight: undefined,
},
};
} satisfies Story;

export const TrailMapStructure: Story = {
export const TrailMapStructure = {
args: {
...TrailMapControl.args,
trailMapVariant: 'treatment_structure',
enableCategorisedFeatures: true,
},
};
} satisfies Story;

export const TrailMapCopy: Story = {
export const TrailMapCopy = {
args: {
...TrailMapControl.args,
trailMapVariant: 'treatment_copy',
},
};
export const TrailMapCopyAndStructure: Story = {
} satisfies Story;

export const TrailMapCopyAndStructure = {
args: {
...TrailMapControl.args,
trailMapVariant: 'treatment_copy_and_structure',
enableCategorisedFeatures: true,
},
};

const meta: Meta< typeof ComponentWrapper > = {
title: 'FeaturesGrid',
component: ComponentWrapper,
decorators: [
( Story, storyContext ) => {
setTrailMapExperiment( storyContext.args.trailMapVariant );
return <Story />;
},
],
};

export default meta;
} satisfies Story;