From 4cd31104f19ff89908862f9f191e892407c55d4e Mon Sep 17 00:00:00 2001 From: Gert-Jan Vercauteren Date: Thu, 26 Mar 2026 13:26:34 +0800 Subject: [PATCH 1/5] [BpkButton,BpkBadge,BpkCard] Colocate stories with components and convert to CSF3 Migrate stories from examples/ to packages/*/src/ for Button, Badge, and Card as a pilot for full story colocation. Convert from CSF2 to CSF3 format. Add build safeguards (babel ignore, .npmignore, SCSS skip) to prevent stories from shipping in the npm package. Update storybook config with dual-glob discovery to support incremental migration. Co-Authored-By: Claude Opus 4.6 --- .storybook/main.ts | 1 + babel.config.js | 1 + examples/bpk-component-badge/stories.tsx | 67 --- examples/bpk-component-button/stories.tsx | 78 --- examples/bpk-component-card/stories.js | 70 --- packages/.npmignore | 10 + .../src/BpkBadge.stories.module.scss | 35 ++ .../src/BpkBadge.stories.tsx | 398 ++++++++++++ .../src/BpkButton.stories.module.scss | 23 + .../src/BpkButton.stories.tsx | 568 ++++++++++++++++++ .../src/BpkCard.stories.module.scss | 34 ++ .../src/BpkCard.stories.tsx | 302 ++++++++++ scripts/scss/styles-prod.js | 4 +- 13 files changed, 1375 insertions(+), 216 deletions(-) delete mode 100644 examples/bpk-component-badge/stories.tsx delete mode 100644 examples/bpk-component-button/stories.tsx delete mode 100644 examples/bpk-component-card/stories.js create mode 100644 packages/bpk-component-badge/src/BpkBadge.stories.module.scss create mode 100644 packages/bpk-component-badge/src/BpkBadge.stories.tsx create mode 100644 packages/bpk-component-button/src/BpkButton.stories.module.scss create mode 100644 packages/bpk-component-button/src/BpkButton.stories.tsx create mode 100644 packages/bpk-component-card/src/BpkCard.stories.module.scss create mode 100644 packages/bpk-component-card/src/BpkCard.stories.tsx diff --git a/.storybook/main.ts b/.storybook/main.ts index 8d181c8db4..1dff5b43c7 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -20,6 +20,7 @@ import type { StorybookConfig } from '@storybook/react-webpack5'; const config: StorybookConfig = { stories: [ + '../packages/*/src/**/*.stories.@(ts|tsx)', '../examples/**/{stories,*.stories}.@(ts|tsx|js|jsx)', ], addons: [ diff --git a/babel.config.js b/babel.config.js index ec84fa13e5..4027f69376 100644 --- a/babel.config.js +++ b/babel.config.js @@ -60,6 +60,7 @@ module.exports = { !!( fileName.includes('-test') || fileName.includes('-Test') || + fileName.includes('.stories') || /\/node_modules\//.test(fileName) || fileName.includes('.d.ts') || fileName.includes('.figma.tsx') diff --git a/examples/bpk-component-badge/stories.tsx b/examples/bpk-component-badge/stories.tsx deleted file mode 100644 index c01e5f8369..0000000000 --- a/examples/bpk-component-badge/stories.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import BpkBadge from '../../packages/bpk-component-badge/src/BpkBadge'; - -import { - DefaultExample, - WarningExample, - SuccessExample, - CriticalExample, - InverseExample, - OutlineExample, - StrongExample, - BrandExample, - CenteredExample, - DockedLeadingExample, - DockedTrailingExample, - MixedExample, - ThemedCornerRadiusExample, - ThemedBackgroundColorExample, - ThemedIconColorExample, - ThemedTypographyExample, -} from './examples'; - -export default { - title: 'bpk-component-badge', - component: BpkBadge, -}; - -export const Default = DefaultExample; -export const Warning = WarningExample; -export const Success = SuccessExample; -export const Critical = CriticalExample; -export const Strong = StrongExample; -export const Brand = BrandExample; -export const Inverse = InverseExample; -export const Outline = OutlineExample; -export const Centered = CenteredExample; -export const DockedRight = DockedLeadingExample; -export const DockedLeft = DockedTrailingExample; -export const ThemedCornerRadius = ThemedCornerRadiusExample; -export const ThemedBackgroundColor = ThemedBackgroundColorExample; -export const ThemedIconColor = ThemedIconColorExample; -export const ThemedTypography = ThemedTypographyExample; - -export const VisualTest = MixedExample; -export const VisualTestWithZoom = { - render: VisualTest, - args: { - zoomEnabled: true, - }, -}; diff --git a/examples/bpk-component-button/stories.tsx b/examples/bpk-component-button/stories.tsx deleted file mode 100644 index 7b64c21fb7..0000000000 --- a/examples/bpk-component-button/stories.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import BpkButton from '../../packages/bpk-component-button'; - -import { - PrimaryExample, - PrimaryOnDarkExample, - PrimaryOnLightExample, - SecondaryExample, - SecondaryOnDarkExample, - DestructiveExample, - FeaturedExample, - LinkExample, - LinkOnDarkExample, - LinksExamples, - MixedExample, - AnchorTagsExample, - FullWidthExample, - SubmitButtonExample, - ThemedBorderRadiusExample, -} from './examples'; - -export default { - title: 'bpk-component-button', - component: BpkButton, -}; - -export const BpkButtonPrimary = () => ; - -export const BpkButtonPrimaryOnDark = () => ; - -export const BpkButtonPrimaryOnLight = () => ; - -export const BpkButtonSecondary = () => ; - -export const BpkButtonSecondaryOnDark = () => ; - -export const BpkButtonDestructive = () => ; - -export const BpkButtonFeatured = () => ; - -export const BpkButtonLinkButton = () => ; - -export const BpkButtonLinkOnDarkButton = () => ; - -export const BpkButtonLinks = () => ; - -export const Mixture = () => ; -export const AnchorTags = () => ; - -export const VisualTest = () => ; -export const VisualTestWithZoom = { - render: VisualTest, - args: { - zoomEnabled: true, - }, -}; - -export const SubmitButton = () => ; -export const FullWidth = () => ; - -export const ThemedCornerRadius = () => ; diff --git a/examples/bpk-component-card/stories.js b/examples/bpk-component-card/stories.js deleted file mode 100644 index 97f7ab69ae..0000000000 --- a/examples/bpk-component-card/stories.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import BpkCard from '../../packages/bpk-component-card/src/BpkCard'; -import BpkCardWrapper from '../../packages/bpk-component-card/src/BpkCardWrapper'; -import BpkDividedCard from '../../packages/bpk-component-card/src/BpkDividedCard'; - -import { - DefaultExample, - WithHrefExample, - WithoutPaddingExample, - NonAtomicExample, - NonAtomicHrefExample, - DefaultDividedCardExample, - VerticalDividedCardExample, - WithHrefDividedCardExample, - NonElevatedDividedCardExample, - CardWrapperExample, - DividedCardWrapperExample, - WithClassNameWrapperExample, - MixedExample, -} from './examples'; - -export default { - title: 'bpk-component-card', - component: BpkCard, - subcomponents: { - BpkDividedCard, - BpkCardWrapper, - }, -}; - -export const Default = DefaultExample; -export const WithHref = WithHrefExample; - -export const WithoutPadding = WithoutPaddingExample; - -export const NonAtomic = NonAtomicExample; - -export const NonAtomicWithHref = NonAtomicHrefExample; - -export const DefaultDividedCard = DefaultDividedCardExample; -export const VerticalDividedCard = VerticalDividedCardExample; -export const WithHrefDividedCard = WithHrefDividedCardExample; -export const NonElevatedDividedCard = NonElevatedDividedCardExample; - -export const CardWrapper = CardWrapperExample; -export const DividedCardWrapper = DividedCardWrapperExample; -export const WithClassNameWrapper = WithClassNameWrapperExample; - -export const VisualTest = MixedExample; -export const VisualTestWithZoom = VisualTest.bind({}); -VisualTestWithZoom.args = { - zoomEnabled: true -}; \ No newline at end of file diff --git a/packages/.npmignore b/packages/.npmignore index 25bf1363ee..e0226ad067 100644 --- a/packages/.npmignore +++ b/packages/.npmignore @@ -1,7 +1,17 @@ bpk-component-boilerplate bpk-storybook-utils *-test.js +*-test.tsx +*.test.js +*.test.tsx stories.js +stories.tsx +*.stories.js +*.stories.jsx +*.stories.ts +*.stories.tsx +*.stories.module.scss +*.stories.module.css __snapshots__ docs tasks diff --git a/packages/bpk-component-badge/src/BpkBadge.stories.module.scss b/packages/bpk-component-badge/src/BpkBadge.stories.module.scss new file mode 100644 index 0000000000..87faa70c6e --- /dev/null +++ b/packages/bpk-component-badge/src/BpkBadge.stories.module.scss @@ -0,0 +1,35 @@ +/* + * Backpack - Skyscanner's Design System + * + * Copyright 2016 Skyscanner Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@use '../../bpk-mixins/tokens'; +@use '../../bpk-mixins/radii'; + +.bpk-badge-layout { + &__container { + position: relative; + display: flex; + min-height: tokens.bpk-spacing-xxl(); + padding: tokens.bpk-spacing-lg(); + + &--light { + background-color: tokens.$bpk-canvas-contrast-day; + + @include radii.bpk-border-radius-xs; + } + } +} diff --git a/packages/bpk-component-badge/src/BpkBadge.stories.tsx b/packages/bpk-component-badge/src/BpkBadge.stories.tsx new file mode 100644 index 0000000000..1a02781c47 --- /dev/null +++ b/packages/bpk-component-badge/src/BpkBadge.stories.tsx @@ -0,0 +1,398 @@ +/* + * Backpack - Skyscanner's Design System + * + * Copyright 2016 Skyscanner Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ReactNode } from 'react'; + +import { + coreAccentDay, + coreEcoDay, + corePrimaryDay, + fontSizeBase, + fontWeightBold, + lineHeightBase, + statusDangerFillDay, + statusDangerSpotDay, + statusSuccessFillDay, + statusSuccessSpotDay, + statusWarningFillDay, + statusWarningSpotDay, +} from '@skyscanner/bpk-foundations-web/tokens/base.es6'; + +import { BpkDarkExampleWrapper } from '../../../examples/bpk-storybook-utils'; +import BpkSmallExclamationIcon from '../../bpk-component-icon/sm/exclamation'; +import BpkSmallHelpCircleIcon from '../../bpk-component-icon/sm/help-circle'; +import BpkSmallTickIcon from '../../bpk-component-icon/sm/tick-circle'; +import { cssModules } from '../../bpk-react-utils'; +// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. +import BpkThemeProvider from '../../bpk-theming'; +// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. + +import BpkBadge, { BADGE_TYPES } from './BpkBadge'; + +import type { Meta, StoryObj } from '@storybook/react'; + +import LAYOUT_STYLES from './BpkBadge.stories.module.scss'; + +const getClassName = cssModules(LAYOUT_STYLES); + +type BadgeLayoutProps = { + docked?: string | null; + children: ReactNode; +}; + +const BadgeLayout = ({ children, docked = null }: BadgeLayoutProps) => { + const classNames = getClassName( + 'bpk-badge-layout__container', + docked && 'bpk-badge-layout__container--light', + ); + + return
{children}
; +}; + +const DefaultExample = () => ( + + Normal +   + +  Normal + + +); + +const WarningExample = () => ( + + Warning +   + +  Warning + + +); + +const SuccessExample = () => ( + + Success +   + + +  Success + + +); + +const CriticalExample = () => ( + + Critical +   + + +  Critical + + +); + +const InverseExample = () => ( + + + Inverse +   + + +  Inverse + + + +); + +const OutlineExample = () => ( + + + Outline +   + + +  Outline + + + +); + +const StrongExample = () => ( + + Strong +   + + +  Strong + + +); + +const BrandExample = () => ( + + Brand +   + + +  Brand + + +); + +const CenteredExample = () => ( + +
+ The badge is aligned to the centre of this text.{' '} + Centered +
+
+); + +const DockedLeadingExample = () => ( + + Advert + +); + +const DockedTrailingExample = () => ( + + Advert + +); + +const ThemedCornerRadiusExample = () => ( + + + Normal +   + Strong +   + Brand + + +); + +const ThemedBackgroundColorExample = () => ( + + + Normal + +   + + Warning + +   + + Success + +   + + Critical + +   + + Strong + +   + + Brand + + +); + +const ThemedIconColorExample = () => ( + + + + +  Normal + + +   + + + +  Warning + + +   + + + +  Success + + +   + + + +  Critical + + + +); + +const ThemedTypographyExample = () => ( + + + Normal +   + + +  Warning + +   + + +  Success + + + +); + +const MixedExample = () => ( +
+ + + + + + + + +
+); + +const meta = { + title: 'bpk-component-badge', + component: BpkBadge, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => , +}; + +export const Warning: Story = { + render: () => , +}; + +export const Success: Story = { + render: () => , +}; + +export const Critical: Story = { + render: () => , +}; + +export const Strong: Story = { + render: () => , +}; + +export const Brand: Story = { + render: () => , +}; + +export const Inverse: Story = { + render: () => , +}; + +export const Outline: Story = { + render: () => , +}; + +export const Centered: Story = { + render: () => , +}; + +export const DockedRight: Story = { + render: () => , +}; + +export const DockedLeft: Story = { + render: () => , +}; + +export const ThemedCornerRadius: Story = { + render: () => , +}; + +export const ThemedBackgroundColor: Story = { + render: () => , +}; + +export const ThemedIconColor: Story = { + render: () => , +}; + +export const ThemedTypography: Story = { + render: () => , +}; + +export const VisualTest: Story = { + render: () => , +}; + +export const VisualTestWithZoom: Story = { + render: () => , + args: { + zoomEnabled: true, + }, +}; diff --git a/packages/bpk-component-button/src/BpkButton.stories.module.scss b/packages/bpk-component-button/src/BpkButton.stories.module.scss new file mode 100644 index 0000000000..6deeb82c1e --- /dev/null +++ b/packages/bpk-component-button/src/BpkButton.stories.module.scss @@ -0,0 +1,23 @@ +/* + * Backpack - Skyscanner's Design System + * + * Copyright 2016 Skyscanner Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@use '../../bpk-mixins/tokens'; + +.bpk-button-story-wrapper { + padding: tokens.bpk-spacing-sm() 0; +} diff --git a/packages/bpk-component-button/src/BpkButton.stories.tsx b/packages/bpk-component-button/src/BpkButton.stories.tsx new file mode 100644 index 0000000000..1ef8d6c306 --- /dev/null +++ b/packages/bpk-component-button/src/BpkButton.stories.tsx @@ -0,0 +1,568 @@ +/* + * Backpack - Skyscanner's Design System + * + * Copyright 2016 Skyscanner Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import { action, BpkDarkExampleWrapper } from '../../../examples/bpk-storybook-utils'; +import { + withButtonAlignment, + withLargeButtonAlignment, + withRtlSupport, +} from '../../bpk-component-icon'; +import LargeLightningIcon from '../../bpk-component-icon/lg/lightning'; +import LargeLongArrowRightIcon from '../../bpk-component-icon/lg/long-arrow-right'; +import SmallLightningIcon from '../../bpk-component-icon/sm/lightning'; +import SmallLongArrowRightIcon from '../../bpk-component-icon/sm/long-arrow-right'; +import { cssModules } from '../../bpk-react-utils'; +// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. +import BpkThemeProvider from '../../bpk-theming'; +// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. + +import BpkButton from './BpkButton'; +import { BUTTON_TYPES, SIZE_TYPES } from './common-types'; + +import type { Meta, StoryObj } from '@storybook/react'; + +import STYLES from './BpkButton.stories.module.scss'; + +const RtlSmallLongArrowRightIcon = withRtlSupport(SmallLongArrowRightIcon); +const RtlLargeLongArrowRightIcon = withRtlSupport(LargeLongArrowRightIcon); + +const AlignedSmallLongArrowRightIcon = withButtonAlignment( + withRtlSupport(SmallLongArrowRightIcon), +); +const AlignedLargeLongArrowRightIcon = withLargeButtonAlignment( + withRtlSupport(LargeLongArrowRightIcon), +); + +const getClassName = cssModules(STYLES); + +type StoryProps = Omit[0], 'children'> & { + className?: string; + wrapped: typeof BpkButton; +}; + +const ButtonStory = ({ className, wrapped, ...rest }: StoryProps) => { + const Wrapped = wrapped; + return ( +
+   + + Button + +   + } onClick={action('Button clicked')} {...rest}> + Button + +   + } onClick={action('Button clicked')} {...rest}> + Button + +   + } trailingIcon={} onClick={action('Button clicked')} {...rest}> + Button + +   + + Button + +   + + Disabled + +   + + Button + +   + + Button + +   + } + onClick={action('Button clicked')} + {...rest} + > + Button + +   + } + onClick={action('Button clicked')} + {...rest} + > + Button + +   + } + trailingIcon={} + onClick={action('Button clicked')} + {...rest} + > + Button + +   + + Disabled + +   + + + +   + + + +   + + + +   + + + +   +
+ ); +}; + +ButtonStory.defaultProps = { className: null }; + +const PrimaryExample = (props: any) => ( + +); +const PrimaryOnDarkExample = (props: any) => ( + + + +); +const PrimaryOnLightExample = (props: any) => ( + +); +const SecondaryExample = (props: any) => ( + +); +const SecondaryOnDarkExample = (props: any) => ( + + + +); +const DestructiveExample = (props: any) => ( + +); +const FeaturedExample = (props: any) => ( + +); +const LinkExample = (props: any) => ( +
+ {/* Default Link */} + + Button + +   + {/* Link with trailing icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Link with leading icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Link with leading and trailing icon */} + } trailingIcon={} onClick={action('Link clicked')} {...props}> + Button + +   + {/* Loading Link */} + + Button + +   + {/* Implicit Link */} + + Button + +   + {/* Implicit Link with trailing icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Implicit Link with leading icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Implicit Link with leading and trailing icon */} + } trailingIcon={} onClick={action('Link clicked')} {...props}> + Button + +   + {/* Disabled Link */} + + Disabled + +   + {/* Large Link */} + + Button + +   + {/* Large Link with trailing icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Large Loading Link */} + + Button + +   + {/* Large Link with leading icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Large Link with leading and trailing icon */} + } trailingIcon={} onClick={action('Link clicked')} {...props}> + Button + +   + {/* iconOnly Link */} + + + +   + {/* Loading iconOnly Link */} + + + +   + {/* Large iconOnly Link */} + + + +   + {/* Large loading iconOnly Link */} + + + +
+); + +const LinkOnDarkExample = (props: any) => ( + +
+ {/* Default LinkOnDark */} + + Button + +   + {/* LinkOnDark with trailing icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* LinkOnDark with leading icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* LinkOnDark with leading and trailing icon */} + } trailingIcon={} onClick={action('Link clicked')} {...props}> + Button + +   + {/* Loading LinkOnDark */} + + Button + +   + {/* Implicit LinkOnDark */} + + Button + +   + {/* Implicit LinkOnDark with trailing icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Implicit LinkOnDark with leading icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Implicit LinkOnDark with leading and trailing icon */} + } trailingIcon={} onClick={action('Link clicked')} {...props}> + Button + +   + {/* Disabled LinkOnDark */} + + Disabled + +   + {/* Large LinkOnDark */} + + Button + +   + {/* Large LinkOnDark with trailing icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Large Loading LinkOnDark */} + + Button + +   + {/* Large LinkOnDark with leading icon */} + } onClick={action('Link clicked')} {...props}> + Button + +   + {/* Large LinkOnDark with leading and trailing icon */} + } trailingIcon={} onClick={action('Link clicked')} {...props}> + Button + +   + {/* iconOnly LinkOnDark */} + + + +   + {/* Loading iconOnly LinkOnDark */} + + + +   + {/* Large iconOnly LinkOnDark */} + + + +   + {/* Large loading iconOnly LinkOnDark */} + + + +
+
+); + +const FullWidthExample = (props: any) => ( + + Full Width Button + +); + +const SubmitButtonExample = (props: any) => ( + + Submit Button + +); + +const ThemedBorderRadiusExample = () => ( + + + +); + +const LinksExamples = () => ( + <> + + + +); + +const MixedExample = () => ( + <> + + + + + + + + + + + + +); + +const AnchorTagsExample = () => ( + <> + + + + + + + + + + +); + +const meta = { + title: 'bpk-component-button', + component: BpkButton, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const BpkButtonPrimary: Story = { + render: () => , +}; + +export const BpkButtonPrimaryOnDark: Story = { + render: () => , +}; + +export const BpkButtonPrimaryOnLight: Story = { + render: () => , +}; + +export const BpkButtonSecondary: Story = { + render: () => , +}; + +export const BpkButtonSecondaryOnDark: Story = { + render: () => , +}; + +export const BpkButtonDestructive: Story = { + render: () => , +}; + +export const BpkButtonFeatured: Story = { + render: () => , +}; + +export const BpkButtonLinkButton: Story = { + render: () => , +}; + +export const BpkButtonLinkOnDarkButton: Story = { + render: () => , +}; + +export const BpkButtonLinks: Story = { + render: () => , +}; + +export const Mixture: Story = { + render: () => , +}; + +export const AnchorTags: Story = { + render: () => , +}; + +export const VisualTest: Story = { + render: () => , +}; + +export const VisualTestWithZoom: Story = { + render: () => , + args: { + zoomEnabled: true, + }, +}; + +export const SubmitButton: Story = { + render: () => , +}; + +export const FullWidth: Story = { + render: () => , +}; + +export const ThemedCornerRadius: Story = { + render: () => , +}; diff --git a/packages/bpk-component-card/src/BpkCard.stories.module.scss b/packages/bpk-component-card/src/BpkCard.stories.module.scss new file mode 100644 index 0000000000..edb9b3856b --- /dev/null +++ b/packages/bpk-component-card/src/BpkCard.stories.module.scss @@ -0,0 +1,34 @@ +/* + * Backpack - Skyscanner's Design System + * + * Copyright 2016 Skyscanner Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@use '../../bpk-mixins/tokens'; + +.bpk-card-examples { + &__header { + display: flex; + height: tokens.bpk-spacing-xxl(); + padding: tokens.bpk-spacing-md(); + justify-content: space-between; + align-items: center; + color: tokens.$bpk-text-on-dark-day; + } + + &__wrapper { + width: tokens.bpk-spacing-md() * 41; + } +} diff --git a/packages/bpk-component-card/src/BpkCard.stories.tsx b/packages/bpk-component-card/src/BpkCard.stories.tsx new file mode 100644 index 0000000000..05015c816a --- /dev/null +++ b/packages/bpk-component-card/src/BpkCard.stories.tsx @@ -0,0 +1,302 @@ +/* + * Backpack - Skyscanner's Design System + * + * Copyright 2016 Skyscanner Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Fragment } from 'react'; + + +import { + coreAccentDay, + surfaceHighlightDay, +} from '@skyscanner/bpk-foundations-web/tokens/base.es6'; + +import BpkText, { TEXT_STYLES } from '../../bpk-component-text'; +import { cssModules } from '../../bpk-react-utils'; + +import BpkCard from './BpkCard'; +import BpkCardWrapper from './BpkCardWrapper'; +import BpkDividedCard, { ORIENTATION } from './BpkDividedCard'; + +import type { Meta, StoryObj } from '@storybook/react'; + +import STYLES from './BpkCard.stories.module.scss'; + +const getClassName = cssModules(STYLES); + +const shortContent = 'Book your next trip on skyscanner.net.'; +const longMessage = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque sagittis sagittis purus, id +blandit ipsum. Pellentesque nec diam nec erat condimentum dapibus. Nunc diam augue, egestas id egestas ut, facilisis +nec mi. Donec et congue odio, nec laoreet est. Integer rhoncus varius arcu, a fringilla libero laoreet at. Mauris +porta varius ullamcorper. Sed laoreet libero mauris, non pretium lectus accumsan et. Suspendisse vehicula ullamcorper +sapien, et dapibus mi aliquet non. Pellentesque auctor sagittis lectus vitae rhoncus. Fusce id enim porttitor, mattis +ante in, vestibulum nulla.`; + +const headerContent = ( +
+ + Wrapper title + +
+); + +const longContent = ( + + + Let's explore + + + It's your world and we'll help you explore it. Find the best + prices across millions of flights, hotels and car hire options to create + your perfect trip. +
+
+
+); + +const meta = { + title: 'bpk-component-card', + component: BpkCard, + subcomponents: { + BpkDividedCard, + BpkCardWrapper, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + window.open('https://www.skyscanner.net/')}> + {shortContent} + + ), +}; + +export const WithHref: Story = { + render: () => ( + {shortContent} + ), +}; + +export const WithoutPadding: Story = { + render: () => ( + window.open('https://www.skyscanner.net/')} + > + {shortContent} + + ), +}; + +export const NonAtomic: Story = { + render: () => ( + window.open('https://www.skyscanner.net/')} + > + {longContent} + + ), +}; + +export const NonAtomicWithHref: Story = { + render: () => ( + + {longContent} + + ), +}; + +export const DefaultDividedCard: Story = { + render: () => ( + + ), +}; + +export const VerticalDividedCard: Story = { + render: () => ( + + ), +}; + +export const WithHrefDividedCard: Story = { + render: () => ( + + ), +}; + +export const NonElevatedDividedCard: Story = { + render: () => ( + + ), +}; + +export const CardWrapper: Story = { + render: () => ( + window.open('https://www.skyscanner.net/')} + > + {longContent} + + } + header={headerContent} + /> + ), +}; + +export const DividedCardWrapper: Story = { + render: () => ( + + } + header={headerContent} + /> + ), +}; + +export const WithClassNameWrapper: Story = { + render: () => ( + window.open('https://www.skyscanner.net/')} + > + {longContent} + + } + header={headerContent} + // eslint-disable-next-line @skyscanner/rules/forbid-component-props + className={getClassName('bpk-card-examples__wrapper')} + /> + ), +}; + +export const VisualTest: Story = { + render: () => ( +
+ window.open('https://www.skyscanner.net/')}> + {shortContent} + +
+ window.open('https://www.skyscanner.net/')} + > + {shortContent} + +
+ +
+ +
+ +
+ window.open('https://www.skyscanner.net/')} + > + {longContent} + + } + header={headerContent} + /> +
+ + } + header={headerContent} + /> +
+ window.open('https://www.skyscanner.net/')} + > + {longContent} + + } + header={headerContent} + // eslint-disable-next-line @skyscanner/rules/forbid-component-props + className={getClassName('bpk-card-examples__wrapper')} + /> +
+ ), +}; + +export const VisualTestWithZoom: Story = { + ...VisualTest, + args: { + zoomEnabled: true, + }, +}; diff --git a/scripts/scss/styles-prod.js b/scripts/scss/styles-prod.js index 736fa099a2..096423ac7e 100644 --- a/scripts/scss/styles-prod.js +++ b/scripts/scss/styles-prod.js @@ -26,7 +26,9 @@ const files = new Glob(DIR_GLOB, {}); try { for (const file of files) { - compile(file); + if (!file.includes('.stories.')) { + compile(file); + } } } catch (err) { // eslint-disable-next-line no-console From 29d9b47174dd879888ce82f4dfaf44a39f34197a Mon Sep 17 00:00:00 2001 From: Gert-Jan Vercauteren Date: Thu, 26 Mar 2026 13:33:46 +0800 Subject: [PATCH 2/5] [BpkButton,BpkBadge,BpkCard] Remove leftover example files for migrated components Clean up the remaining examples/ files (examples.tsx, SCSS, CSS) for the three components whose stories were colocated in the previous commit. Co-Authored-By: Claude Opus 4.6 --- examples/bpk-component-badge/BadgeLayout.tsx | 41 -- .../BpkBadgeLayout.module.scss | 35 -- examples/bpk-component-badge/examples.tsx | 316 ----------- .../BpkButtonStory.module.scss | 23 - examples/bpk-component-button/examples.tsx | 506 ------------------ examples/bpk-component-card/examples.js | 215 -------- .../bpk-component-card/examples.module.scss | 34 -- 7 files changed, 1170 deletions(-) delete mode 100644 examples/bpk-component-badge/BadgeLayout.tsx delete mode 100644 examples/bpk-component-badge/BpkBadgeLayout.module.scss delete mode 100644 examples/bpk-component-badge/examples.tsx delete mode 100644 examples/bpk-component-button/BpkButtonStory.module.scss delete mode 100644 examples/bpk-component-button/examples.tsx delete mode 100644 examples/bpk-component-card/examples.js delete mode 100644 examples/bpk-component-card/examples.module.scss diff --git a/examples/bpk-component-badge/BadgeLayout.tsx b/examples/bpk-component-badge/BadgeLayout.tsx deleted file mode 100644 index addf2a3fc0..0000000000 --- a/examples/bpk-component-badge/BadgeLayout.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ReactNode } from 'react'; - -import { cssModules } from '../../packages/bpk-react-utils'; - -import STYLES from './BpkBadgeLayout.module.scss'; - -const getClassName = cssModules(STYLES); - -type Props = { - docked?: string | null; - children: ReactNode; -}; - -const BadgeLayout = ({ children, docked = null }: Props) => { - const classNames = getClassName( - 'bpk-badge-layout__container', - docked && 'bpk-badge-layout__container--light', - ); - - return
{children}
; -}; - -export default BadgeLayout; diff --git a/examples/bpk-component-badge/BpkBadgeLayout.module.scss b/examples/bpk-component-badge/BpkBadgeLayout.module.scss deleted file mode 100644 index 102d056ac4..0000000000 --- a/examples/bpk-component-badge/BpkBadgeLayout.module.scss +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@use '../../packages/bpk-mixins/tokens'; -@use '../../packages/bpk-mixins/radii'; - -.bpk-badge-layout { - &__container { - position: relative; - display: flex; - min-height: tokens.bpk-spacing-xxl(); - padding: tokens.bpk-spacing-lg(); - - &--light { - background-color: tokens.$bpk-canvas-contrast-day; - - @include radii.bpk-border-radius-xs; - } - } -} diff --git a/examples/bpk-component-badge/examples.tsx b/examples/bpk-component-badge/examples.tsx deleted file mode 100644 index 40292e82a4..0000000000 --- a/examples/bpk-component-badge/examples.tsx +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - coreAccentDay, - coreEcoDay, - corePrimaryDay, - fontSizeBase, - fontWeightBold, - lineHeightBase, - statusDangerFillDay, - statusDangerSpotDay, - statusSuccessFillDay, - statusSuccessSpotDay, - statusWarningFillDay, - statusWarningSpotDay, -} from '@skyscanner/bpk-foundations-web/tokens/base.es6'; - -import BpkBadge, { BADGE_TYPES } from '../../packages/bpk-component-badge'; -import BpkSmallExclamationIcon from '../../packages/bpk-component-icon/sm/exclamation'; -import BpkSmallHelpCircleIcon from '../../packages/bpk-component-icon/sm/help-circle'; -import BpkSmallTickIcon from '../../packages/bpk-component-icon/sm/tick-circle'; -// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. -import BpkThemeProvider from '../../packages/bpk-theming'; -// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. -import { BpkDarkExampleWrapper } from '../bpk-storybook-utils'; - -import BadgeLayout from './BadgeLayout'; - -const DefaultExample = () => ( - - Normal -   - -  Normal - - -); - -const WarningExample = () => ( - - Warning -   - -  Warning - - -); - -const SuccessExample = () => ( - - Success -   - - -  Success - - -); - -const CriticalExample = () => ( - - Critical -   - - -  Critical - - -); - -const InverseExample = () => ( - - - Inverse -   - - -  Inverse - - - -); - -const OutlineExample = () => ( - - - Outline -   - - -  Outline - - - -); - -const StrongExample = () => ( - - Strong -   - - -  Strong - - -); - -const BrandExample = () => ( - - Brand -   - - -  Brand - - -); - -const CenteredExample = () => ( - -
- The badge is aligned to the centre of this text.{' '} - Centered -
-
-); - -const DockedLeadingExample = () => ( - - Advert - -); - -const DockedTrailingExample = () => ( - - Advert - -); - -const ThemedCornerRadiusExample = () => ( - - - Normal -   - Strong -   - Brand - - -); - -const ThemedBackgroundColorExample = () => ( - - - Normal - -   - - Warning - -   - - Success - -   - - Critical - -   - - Strong - -   - - Brand - - -); - -const ThemedIconColorExample = () => ( - - - - -  Normal - - -   - - - -  Warning - - -   - - - -  Success - - -   - - - -  Critical - - - -); - -const ThemedTypographyExample = () => ( - - - Normal -   - - -  Warning - -   - - -  Success - - - -); - -const MixedExample = () => ( -
- - - - - - - - -
-); - -export { - DefaultExample, - WarningExample, - SuccessExample, - CriticalExample, - InverseExample, - OutlineExample, - StrongExample, - BrandExample, - CenteredExample, - DockedLeadingExample, - DockedTrailingExample, - MixedExample, - ThemedCornerRadiusExample, - ThemedBackgroundColorExample, - ThemedIconColorExample, - ThemedTypographyExample, -}; diff --git a/examples/bpk-component-button/BpkButtonStory.module.scss b/examples/bpk-component-button/BpkButtonStory.module.scss deleted file mode 100644 index 303ff7890a..0000000000 --- a/examples/bpk-component-button/BpkButtonStory.module.scss +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@use '../../packages/bpk-mixins/tokens'; - -.bpk-button-story-wrapper { - padding: tokens.bpk-spacing-sm() 0; -} diff --git a/examples/bpk-component-button/examples.tsx b/examples/bpk-component-button/examples.tsx deleted file mode 100644 index 3d358728d0..0000000000 --- a/examples/bpk-component-button/examples.tsx +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import BpkButton, { - BUTTON_TYPES, - SIZE_TYPES, -} from '../../packages/bpk-component-button'; -import { - withButtonAlignment, - withLargeButtonAlignment, - withRtlSupport, -} from '../../packages/bpk-component-icon'; -import LargeLightningIcon from '../../packages/bpk-component-icon/lg/lightning'; -import LargeLongArrowRightIcon from '../../packages/bpk-component-icon/lg/long-arrow-right'; -import SmallLightningIcon from '../../packages/bpk-component-icon/sm/lightning'; -import SmallLongArrowRightIcon from '../../packages/bpk-component-icon/sm/long-arrow-right'; -import { cssModules } from '../../packages/bpk-react-utils'; -// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. -import BpkThemeProvider from '../../packages/bpk-theming'; -// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. -import { action, BpkDarkExampleWrapper } from '../bpk-storybook-utils'; - -import STYLES from './BpkButtonStory.module.scss'; - -const RtlSmallLongArrowRightIcon = withRtlSupport(SmallLongArrowRightIcon); -const RtlLargeLongArrowRightIcon = withRtlSupport(LargeLongArrowRightIcon); - -const AlignedSmallLongArrowRightIcon = withButtonAlignment( - withRtlSupport(SmallLongArrowRightIcon), -); -const AlignedLargeLongArrowRightIcon = withLargeButtonAlignment( - withRtlSupport(LargeLongArrowRightIcon), -); - -const getClassName = cssModules(STYLES); - -type StoryProps = Omit[0], 'children'> & { - className?: string; - wrapped: typeof BpkButton; -}; - -const ButtonStory = ({ className, wrapped, ...rest }: StoryProps) => { - const Wrapped = wrapped; - return ( -
-   - - Button - -   - } onClick={action('Button clicked')} {...rest}> - Button - -   - } onClick={action('Button clicked')} {...rest}> - Button - -   - } trailingIcon={} onClick={action('Button clicked')} {...rest}> - Button - -   - - Button - -   - - Disabled - -   - - Button - -   - - Button - -   - } - onClick={action('Button clicked')} - {...rest} - > - Button - -   - } - onClick={action('Button clicked')} - {...rest} - > - Button - -   - } - trailingIcon={} - onClick={action('Button clicked')} - {...rest} - > - Button - -   - - Disabled - -   - - - -   - - - -   - - - -   - - - -   -
- ); -}; - -ButtonStory.defaultProps = { className: null }; - - -const PrimaryExample = (props: any) => ( - -); -const PrimaryOnDarkExample = (props: any) => ( - - - -); -const PrimaryOnLightExample = (props: any) => ( - -); -const SecondaryExample = (props: any) => ( - -); -const SecondaryOnDarkExample = (props: any) => ( - - - -); -const DestructiveExample = (props: any) => ( - -); -const FeaturedExample = (props: any) => ( - -); -const LinkExample = (props: any) => ( -
- {/* Default Link */} - - Button - -   - {/* Link with trailing icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Link with leading icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Link with leading and trailing icon */} - } trailingIcon={} onClick={action('Link clicked')} {...props}> - Button - -   - {/* Loading Link */} - - Button - -   - {/* Implicit Link */} - - Button - -   - {/* Implicit Link with trailing icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Implicit Link with leading icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Implicit Link with leading and trailing icon */} - } trailingIcon={} onClick={action('Link clicked')} {...props}> - Button - -   - {/* Disabled Link */} - - Disabled - -   - {/* Large Link */} - - Button - -   - {/* Large Link with trailing icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Large Loading Link */} - - Button - -   - {/* Large Link with leading icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Large Link with leading and trailing icon */} - } trailingIcon={} onClick={action('Link clicked')} {...props}> - Button - -   - {/* iconOnly Link */} - - - -   - {/* Loading iconOnly Link */} - - - -   - {/* Large iconOnly Link */} - - - -   - {/* Large loading iconOnly Link */} - - - -
-); - -const LinkOnDarkExample = (props: any) => ( - -
- {/* Default LinkOnDark */} - - Button - -   - {/* LinkOnDark with trailing icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* LinkOnDark with leading icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* LinkOnDark with leading and trailing icon */} - } trailingIcon={} onClick={action('Link clicked')} {...props}> - Button - -   - {/* Loading LinkOnDark */} - - Button - -   - {/* Implicit LinkOnDark */} - - Button - -   - {/* Implicit LinkOnDark with trailing icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Implicit LinkOnDark with leading icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Implicit LinkOnDark with leading and trailing icon */} - } trailingIcon={} onClick={action('Link clicked')} {...props}> - Button - -   - {/* Disabled LinkOnDark */} - - Disabled - -   - {/* Large LinkOnDark */} - - Button - -   - {/* Large LinkOnDark with trailing icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Large Loading LinkOnDark */} - - Button - -   - {/* Large LinkOnDark with leading icon */} - } onClick={action('Link clicked')} {...props}> - Button - -   - {/* Large LinkOnDark with leading and trailing icon */} - } trailingIcon={} onClick={action('Link clicked')} {...props}> - Button - -   - {/* iconOnly LinkOnDark */} - - - -   - {/* Loading iconOnly LinkOnDark */} - - - -   - {/* Large iconOnly LinkOnDark */} - - - -   - {/* Large loading iconOnly LinkOnDark */} - - - -
-
-); - -const FullWidthExample = (props: any) => ( - - Full Width Button - -); - -const SubmitButtonExample = (props: any) => ( - - Submit Button - -); - -const ThemedBorderRadiusExample = () => ( - - - -); - -const LinksExamples = () => ( - <> - - - -); - -const MixedExample = () => ( - <> - - - - - - - - - - - - -); - -const AnchorTagsExample = () => ( - <> - - - - - - - - - - -); - -export { - PrimaryExample, - PrimaryOnDarkExample, - PrimaryOnLightExample, - SecondaryExample, - SecondaryOnDarkExample, - DestructiveExample, - FeaturedExample, - LinkExample, - LinkOnDarkExample, - LinksExamples, - MixedExample, - AnchorTagsExample, - FullWidthExample, - SubmitButtonExample, - ThemedBorderRadiusExample, -}; diff --git a/examples/bpk-component-card/examples.js b/examples/bpk-component-card/examples.js deleted file mode 100644 index fc3c446ef6..0000000000 --- a/examples/bpk-component-card/examples.js +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* @flow strict */ - -import { Fragment } from 'react'; - -import { - coreAccentDay, - surfaceHighlightDay, -} from '@skyscanner/bpk-foundations-web/tokens/base.es6'; - -import BpkCard, { - BpkDividedCard, - BpkCardWrapper, - ORIENTATION, -} from '../../packages/bpk-component-card'; -import BpkText, { TEXT_STYLES } from '../../packages/bpk-component-text'; -import { cssModules } from '../../packages/bpk-react-utils'; - -import STYLES from './examples.module.scss'; - -const getClassName = cssModules(STYLES); - -const shortContent = 'Book your next trip on skyscanner.net.'; -const longMessage = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque sagittis sagittis purus, id -blandit ipsum. Pellentesque nec diam nec erat condimentum dapibus. Nunc diam augue, egestas id egestas ut, facilisis -nec mi. Donec et congue odio, nec laoreet est. Integer rhoncus varius arcu, a fringilla libero laoreet at. Mauris -porta varius ullamcorper. Sed laoreet libero mauris, non pretium lectus accumsan et. Suspendisse vehicula ullamcorper -sapien, et dapibus mi aliquet non. Pellentesque auctor sagittis lectus vitae rhoncus. Fusce id enim porttitor, mattis -ante in, vestibulum nulla.`; -const headerContent = ( -
- - Wrapper title - -
-); -const longContent = ( - - - Let's explore - - - It's your world and we'll help you explore it. Find the best - prices across millions of flights, hotels and car hire options to create - your perfect trip. -
-
-
-); - -const DefaultExample = () => ( - window.open('https://www.skyscanner.net/')}> - {shortContent} - -); - -const WithHrefExample = () => ( - {shortContent} -); - -const WithoutPaddingExample = () => ( - window.open('https://www.skyscanner.net/')} - > - {shortContent} - -); - -const NonAtomicExample = () => ( - window.open('https://www.skyscanner.net/')} - > - {longContent} - -); - -const NonAtomicHrefExample = () => ( - - {longContent} - -); - -const DefaultDividedCardExample = () => ( - -); - -const VerticalDividedCardExample = () => ( - -); -const WithHrefDividedCardExample = () => ( - -); - -const NonElevatedDividedCardExample = () => ( - -); - -const CardWrapperExample = () => ( - window.open('https://www.skyscanner.net/')} - > - {longContent} - - } - header={headerContent} - /> -); - -const DividedCardWrapperExample = () => ( - - } - header={headerContent} - /> -); - -const WithClassNameWrapperExample = () => ( - window.open('https://www.skyscanner.net/')} - > - {longContent} - - } - header={headerContent} - className={getClassName('bpk-card-examples__wrapper')} - /> -); - -const MixedExample = () => ( -
- -
- -
- -
- -
- -
- -
- -
- -
-); - -export { - DefaultExample, - WithHrefExample, - WithoutPaddingExample, - NonAtomicExample, - NonAtomicHrefExample, - DefaultDividedCardExample, - VerticalDividedCardExample, - WithHrefDividedCardExample, - NonElevatedDividedCardExample, - CardWrapperExample, - DividedCardWrapperExample, - WithClassNameWrapperExample, - MixedExample, -}; diff --git a/examples/bpk-component-card/examples.module.scss b/examples/bpk-component-card/examples.module.scss deleted file mode 100644 index 358bbe621f..0000000000 --- a/examples/bpk-component-card/examples.module.scss +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Backpack - Skyscanner's Design System - * - * Copyright 2016 Skyscanner Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@use '../../packages/bpk-mixins/tokens'; - -.bpk-card-examples { - &__header { - display: flex; - height: tokens.bpk-spacing-xxl(); - padding: tokens.bpk-spacing-md(); - justify-content: space-between; - align-items: center; - color: tokens.$bpk-text-on-dark-day; - } - - &__wrapper { - width: tokens.bpk-spacing-md() * 41; - } -} From f3449357eb37e96f90ae0fb119713ade47360b1d Mon Sep 17 00:00:00 2001 From: Gert-Jan Vercauteren Date: Thu, 26 Mar 2026 13:44:13 +0800 Subject: [PATCH 3/5] [BpkButton,BpkBadge,BpkCard] Fix TypeScript errors in colocated stories Remove Story type annotations from render-only stories to avoid TS errors where StoryObj requires args for required component props. Remove unused StoryObj imports. Add @ts-ignore for untyped bpk-storybook-utils module. Co-Authored-By: Claude Opus 4.6 --- .../src/BpkBadge.stories.tsx | 44 +++++++++---------- .../src/BpkButton.stories.tsx | 42 +++++++++--------- .../src/BpkCard.stories.tsx | 34 +++++++------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/packages/bpk-component-badge/src/BpkBadge.stories.tsx b/packages/bpk-component-badge/src/BpkBadge.stories.tsx index 1a02781c47..7d839742dc 100644 --- a/packages/bpk-component-badge/src/BpkBadge.stories.tsx +++ b/packages/bpk-component-badge/src/BpkBadge.stories.tsx @@ -33,18 +33,18 @@ import { statusWarningSpotDay, } from '@skyscanner/bpk-foundations-web/tokens/base.es6'; +// @ts-ignore Untyped import. See `decisions/imports-ts-suppressions.md`. import { BpkDarkExampleWrapper } from '../../../examples/bpk-storybook-utils'; import BpkSmallExclamationIcon from '../../bpk-component-icon/sm/exclamation'; import BpkSmallHelpCircleIcon from '../../bpk-component-icon/sm/help-circle'; import BpkSmallTickIcon from '../../bpk-component-icon/sm/tick-circle'; import { cssModules } from '../../bpk-react-utils'; -// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. +// @ts-ignore Untyped import. See `decisions/imports-ts-suppressions.md`. import BpkThemeProvider from '../../bpk-theming'; -// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. import BpkBadge, { BADGE_TYPES } from './BpkBadge'; -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta } from '@storybook/react'; import LAYOUT_STYLES from './BpkBadge.stories.module.scss'; @@ -321,76 +321,76 @@ const MixedExample = () => ( const meta = { title: 'bpk-component-badge', component: BpkBadge, -} satisfies Meta; +} satisfies Meta; export default meta; -type Story = StoryObj; -export const Default: Story = { + +export const Default = { render: () => , }; -export const Warning: Story = { +export const Warning = { render: () => , }; -export const Success: Story = { +export const Success = { render: () => , }; -export const Critical: Story = { +export const Critical = { render: () => , }; -export const Strong: Story = { +export const Strong = { render: () => , }; -export const Brand: Story = { +export const Brand = { render: () => , }; -export const Inverse: Story = { +export const Inverse = { render: () => , }; -export const Outline: Story = { +export const Outline = { render: () => , }; -export const Centered: Story = { +export const Centered = { render: () => , }; -export const DockedRight: Story = { +export const DockedRight = { render: () => , }; -export const DockedLeft: Story = { +export const DockedLeft = { render: () => , }; -export const ThemedCornerRadius: Story = { +export const ThemedCornerRadius = { render: () => , }; -export const ThemedBackgroundColor: Story = { +export const ThemedBackgroundColor = { render: () => , }; -export const ThemedIconColor: Story = { +export const ThemedIconColor = { render: () => , }; -export const ThemedTypography: Story = { +export const ThemedTypography = { render: () => , }; -export const VisualTest: Story = { +export const VisualTest = { render: () => , }; -export const VisualTestWithZoom: Story = { +export const VisualTestWithZoom = { render: () => , args: { zoomEnabled: true, diff --git a/packages/bpk-component-button/src/BpkButton.stories.tsx b/packages/bpk-component-button/src/BpkButton.stories.tsx index 1ef8d6c306..22364d6dc9 100644 --- a/packages/bpk-component-button/src/BpkButton.stories.tsx +++ b/packages/bpk-component-button/src/BpkButton.stories.tsx @@ -17,6 +17,7 @@ */ +// @ts-ignore Untyped import. See `decisions/imports-ts-suppressions.md`. import { action, BpkDarkExampleWrapper } from '../../../examples/bpk-storybook-utils'; import { withButtonAlignment, @@ -30,12 +31,11 @@ import SmallLongArrowRightIcon from '../../bpk-component-icon/sm/long-arrow-righ import { cssModules } from '../../bpk-react-utils'; // @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. import BpkThemeProvider from '../../bpk-theming'; -// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. import BpkButton from './BpkButton'; import { BUTTON_TYPES, SIZE_TYPES } from './common-types'; -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta } from '@storybook/react'; import STYLES from './BpkButton.stories.module.scss'; @@ -491,78 +491,78 @@ const AnchorTagsExample = () => ( const meta = { title: 'bpk-component-button', component: BpkButton, -} satisfies Meta; +} satisfies Meta; export default meta; -type Story = StoryObj; -export const BpkButtonPrimary: Story = { + +export const BpkButtonPrimary = { render: () => , }; -export const BpkButtonPrimaryOnDark: Story = { +export const BpkButtonPrimaryOnDark = { render: () => , }; -export const BpkButtonPrimaryOnLight: Story = { +export const BpkButtonPrimaryOnLight = { render: () => , }; -export const BpkButtonSecondary: Story = { +export const BpkButtonSecondary = { render: () => , }; -export const BpkButtonSecondaryOnDark: Story = { +export const BpkButtonSecondaryOnDark = { render: () => , }; -export const BpkButtonDestructive: Story = { +export const BpkButtonDestructive = { render: () => , }; -export const BpkButtonFeatured: Story = { +export const BpkButtonFeatured = { render: () => , }; -export const BpkButtonLinkButton: Story = { +export const BpkButtonLinkButton = { render: () => , }; -export const BpkButtonLinkOnDarkButton: Story = { +export const BpkButtonLinkOnDarkButton = { render: () => , }; -export const BpkButtonLinks: Story = { +export const BpkButtonLinks = { render: () => , }; -export const Mixture: Story = { +export const Mixture = { render: () => , }; -export const AnchorTags: Story = { +export const AnchorTags = { render: () => , }; -export const VisualTest: Story = { +export const VisualTest = { render: () => , }; -export const VisualTestWithZoom: Story = { +export const VisualTestWithZoom = { render: () => , args: { zoomEnabled: true, }, }; -export const SubmitButton: Story = { +export const SubmitButton = { render: () => , }; -export const FullWidth: Story = { +export const FullWidth = { render: () => , }; -export const ThemedCornerRadius: Story = { +export const ThemedCornerRadius = { render: () => , }; diff --git a/packages/bpk-component-card/src/BpkCard.stories.tsx b/packages/bpk-component-card/src/BpkCard.stories.tsx index 05015c816a..b93a34e9b9 100644 --- a/packages/bpk-component-card/src/BpkCard.stories.tsx +++ b/packages/bpk-component-card/src/BpkCard.stories.tsx @@ -31,7 +31,7 @@ import BpkCard from './BpkCard'; import BpkCardWrapper from './BpkCardWrapper'; import BpkDividedCard, { ORIENTATION } from './BpkDividedCard'; -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta } from '@storybook/react'; import STYLES from './BpkCard.stories.module.scss'; @@ -78,12 +78,12 @@ const meta = { BpkDividedCard, BpkCardWrapper, }, -} satisfies Meta; +} satisfies Meta; export default meta; -type Story = StoryObj; -export const Default: Story = { + +export const Default = { render: () => ( window.open('https://www.skyscanner.net/')}> {shortContent} @@ -91,13 +91,13 @@ export const Default: Story = { ), }; -export const WithHref: Story = { +export const WithHref = { render: () => ( {shortContent} ), }; -export const WithoutPadding: Story = { +export const WithoutPadding = { render: () => ( ( ( {longContent} @@ -127,7 +127,7 @@ export const NonAtomicWithHref: Story = { ), }; -export const DefaultDividedCard: Story = { +export const DefaultDividedCard = { render: () => ( ( ( ( ( ( ( (
window.open('https://www.skyscanner.net/')}> @@ -294,7 +294,7 @@ export const VisualTest: Story = { ), }; -export const VisualTestWithZoom: Story = { +export const VisualTestWithZoom = { ...VisualTest, args: { zoomEnabled: true, From e18735e553fe76f797ba609b75e26dedc91845db Mon Sep 17 00:00:00 2001 From: Gert-Jan Vercauteren Date: Thu, 26 Mar 2026 21:00:05 +0800 Subject: [PATCH 4/5] [BpkButton,BpkBadge,BpkCard] Address PR review comments - Fix swapped DockedRight/DockedLeft story names in badge stories - Use https:// instead of http:// in card story href - Remove legacy defaultProps pattern from ButtonStory, use default parameter Co-Authored-By: Claude Opus 4.6 --- packages/bpk-component-badge/src/BpkBadge.stories.tsx | 4 ++-- packages/bpk-component-button/src/BpkButton.stories.tsx | 4 ++-- packages/bpk-component-card/src/BpkCard.stories.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/bpk-component-badge/src/BpkBadge.stories.tsx b/packages/bpk-component-badge/src/BpkBadge.stories.tsx index 7d839742dc..7606fae0a0 100644 --- a/packages/bpk-component-badge/src/BpkBadge.stories.tsx +++ b/packages/bpk-component-badge/src/BpkBadge.stories.tsx @@ -363,11 +363,11 @@ export const Centered = { }; export const DockedRight = { - render: () => , + render: () => , }; export const DockedLeft = { - render: () => , + render: () => , }; export const ThemedCornerRadius = { diff --git a/packages/bpk-component-button/src/BpkButton.stories.tsx b/packages/bpk-component-button/src/BpkButton.stories.tsx index 22364d6dc9..ac49c8bc53 100644 --- a/packages/bpk-component-button/src/BpkButton.stories.tsx +++ b/packages/bpk-component-button/src/BpkButton.stories.tsx @@ -56,7 +56,7 @@ type StoryProps = Omit[0], 'children'> & { wrapped: typeof BpkButton; }; -const ButtonStory = ({ className, wrapped, ...rest }: StoryProps) => { +const ButtonStory = ({ className = undefined, wrapped, ...rest }: StoryProps) => { const Wrapped = wrapped; return (
{ ); }; -ButtonStory.defaultProps = { className: null }; + const PrimaryExample = (props: any) => ( diff --git a/packages/bpk-component-card/src/BpkCard.stories.tsx b/packages/bpk-component-card/src/BpkCard.stories.tsx index b93a34e9b9..a592f16fb3 100644 --- a/packages/bpk-component-card/src/BpkCard.stories.tsx +++ b/packages/bpk-component-card/src/BpkCard.stories.tsx @@ -151,7 +151,7 @@ export const WithHrefDividedCard = { ), }; From 93f9f8b6e65a6b0d7f5003498bb8aae33b0e3b28 Mon Sep 17 00:00:00 2001 From: Gert-Jan Vercauteren Date: Fri, 27 Mar 2026 12:04:10 +0800 Subject: [PATCH 5/5] [BpkButton,BpkBadge] Use @ts-expect-error instead of @ts-ignore Per decisions/imports-ts-suppressions.md, use @ts-expect-error for untyped imports so suppressions fail loudly if the underlying issue is fixed. Co-Authored-By: Claude Opus 4.6 --- packages/bpk-component-badge/src/BpkBadge.stories.tsx | 4 ++-- packages/bpk-component-button/src/BpkButton.stories.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/bpk-component-badge/src/BpkBadge.stories.tsx b/packages/bpk-component-badge/src/BpkBadge.stories.tsx index 7606fae0a0..a163766cf6 100644 --- a/packages/bpk-component-badge/src/BpkBadge.stories.tsx +++ b/packages/bpk-component-badge/src/BpkBadge.stories.tsx @@ -33,13 +33,13 @@ import { statusWarningSpotDay, } from '@skyscanner/bpk-foundations-web/tokens/base.es6'; -// @ts-ignore Untyped import. See `decisions/imports-ts-suppressions.md`. +// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. import { BpkDarkExampleWrapper } from '../../../examples/bpk-storybook-utils'; import BpkSmallExclamationIcon from '../../bpk-component-icon/sm/exclamation'; import BpkSmallHelpCircleIcon from '../../bpk-component-icon/sm/help-circle'; import BpkSmallTickIcon from '../../bpk-component-icon/sm/tick-circle'; import { cssModules } from '../../bpk-react-utils'; -// @ts-ignore Untyped import. See `decisions/imports-ts-suppressions.md`. +// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. import BpkThemeProvider from '../../bpk-theming'; import BpkBadge, { BADGE_TYPES } from './BpkBadge'; diff --git a/packages/bpk-component-button/src/BpkButton.stories.tsx b/packages/bpk-component-button/src/BpkButton.stories.tsx index ac49c8bc53..735be2f3e4 100644 --- a/packages/bpk-component-button/src/BpkButton.stories.tsx +++ b/packages/bpk-component-button/src/BpkButton.stories.tsx @@ -17,7 +17,7 @@ */ -// @ts-ignore Untyped import. See `decisions/imports-ts-suppressions.md`. +// @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`. import { action, BpkDarkExampleWrapper } from '../../../examples/bpk-storybook-utils'; import { withButtonAlignment,