Merged in feat/SW-3644-storybook-v10 (pull request #3240)
feat(SW-3644): Storybook v10 * Auto update to Storybook v10 * Add scandic theme and logo * Update yarn.lock * Update formatting of package.json * Update vitest config and playwright plugin * Remove vitest 4 update * Re-added comment * Update the Typography component to explicitly return React.ReactNode * Add an explicit type assertion to the export * Add an explicit type assertion to the export for Checkbox * Explicit return type assertion * Add an explicit type assertion to the export * Update @types/react and fix ts warnings * Updated typings Approved-by: Linus Flood Approved-by: Matilda Landström
This commit is contained in:
@@ -4,3 +4,6 @@ packageExtensions:
|
||||
eslint-config-next@*:
|
||||
dependencies:
|
||||
next: "*"
|
||||
storybook@*:
|
||||
dependencies:
|
||||
"@storybook/nextjs-vite": "*"
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
"@scandic-hotels/typescript-config": "workspace:*",
|
||||
"@swc/plugin-formatjs": "^3.2.2",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "^19.2.3",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
||||
"@typescript-eslint/parser": "^8.32.0",
|
||||
"babel-plugin-formatjs": "^10.5.39",
|
||||
|
||||
@@ -162,7 +162,7 @@ const variants = {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.4, ease: "easeInOut" },
|
||||
},
|
||||
},
|
||||
} as const,
|
||||
|
||||
slideInOut: {
|
||||
hidden: {
|
||||
@@ -176,7 +176,7 @@ const variants = {
|
||||
transition: { duration: 0.4, ease: "easeInOut" },
|
||||
},
|
||||
},
|
||||
}
|
||||
} as const
|
||||
|
||||
function getRedeemFlow(reward: Reward, membershipNumber: string) {
|
||||
const { rewardType } = reward
|
||||
|
||||
@@ -224,7 +224,11 @@ export default function SurprisesNotification({
|
||||
animate="center"
|
||||
exit="exit"
|
||||
transition={{
|
||||
x: { type: "ease", duration: 0.5 },
|
||||
x: {
|
||||
type: "tween",
|
||||
ease: "easeInOut",
|
||||
duration: 0.5,
|
||||
},
|
||||
opacity: { duration: 0.2 },
|
||||
}}
|
||||
layout
|
||||
@@ -277,4 +281,4 @@ const variants = {
|
||||
opacity: 0,
|
||||
}
|
||||
},
|
||||
}
|
||||
} as const
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"dev": "NODE_OPTIONS=--openssl-legacy-provider PORT=3000 NEXT_PUBLIC_PORT=3000 next dev --turbo",
|
||||
"lint": "yarn clean && next lint --max-warnings 0 && tsc",
|
||||
"lint:fix": "yarn clean && next lint --fix --max-warnings 0 && tsc",
|
||||
"lint": "next typegen && next lint --max-warnings 0 && tsc",
|
||||
"lint:fix": "next typegen && next lint --fix --max-warnings 0 && tsc",
|
||||
"start": "node .next/standalone/server.js",
|
||||
"test:setup": "yarn build && yarn start",
|
||||
"preinstall": "/bin/sh -c \"export $(cat .env.local | grep -v '^#' | xargs)\"",
|
||||
@@ -99,8 +99,8 @@
|
||||
"@types/json-stable-stringify-without-jsonify": "^1.0.2",
|
||||
"@types/jsonwebtoken": "^9",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "^19.2.3",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
||||
"@typescript-eslint/parser": "^8.32.0",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
@@ -122,9 +122,5 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": "22"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.2.9",
|
||||
"@formatjs/cli": "^6.7.1",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.0",
|
||||
"@types/react": "^19.2.3",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20251104.1",
|
||||
"@yarnpkg/types": "^4.0.1",
|
||||
"commander": "^14.0.0",
|
||||
@@ -48,7 +48,7 @@
|
||||
"turbo": "^2.6.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"vite": "^6.3.5",
|
||||
"vite": "^7.2.4",
|
||||
"import-in-the-middle": "^1.14.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@scandic-hotels/typescript-config": "workspace:*",
|
||||
"@t3-oss/env-nextjs": "^0.13.4",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
||||
"@typescript-eslint/parser": "^8.32.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import type { StorybookConfig } from '@storybook/nextjs-vite'
|
||||
import { dirname, join } from 'path'
|
||||
import { mergeConfig } from 'vite'
|
||||
|
||||
const config: StorybookConfig = {
|
||||
framework: '@storybook/nextjs-vite',
|
||||
stories: ['../lib/**/*.mdx', '../lib/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
||||
addons: [
|
||||
getAbsolutePath('@storybook/addon-links'),
|
||||
getAbsolutePath('@storybook/addon-themes'),
|
||||
getAbsolutePath('@storybook/addon-vitest'),
|
||||
getAbsolutePath('@storybook/addon-docs'),
|
||||
getAbsolutePath('@storybook/addon-a11y'),
|
||||
getAbsolutePath('storybook-react-intl'),
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-themes',
|
||||
'@storybook/addon-vitest',
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-a11y',
|
||||
'storybook-react-intl',
|
||||
],
|
||||
framework: {
|
||||
name: getAbsolutePath('@storybook/nextjs-vite'),
|
||||
options: {},
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
},
|
||||
async viteFinal(config) {
|
||||
return mergeConfig(config, {
|
||||
@@ -48,7 +47,3 @@ const config: StorybookConfig = {
|
||||
},
|
||||
}
|
||||
export default config
|
||||
|
||||
function getAbsolutePath(value: string) {
|
||||
return dirname(require.resolve(join(value, 'package.json')))
|
||||
}
|
||||
|
||||
6
packages/design-system/.storybook/manager.ts
Normal file
6
packages/design-system/.storybook/manager.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { addons } from 'storybook/manager-api'
|
||||
import scandicTheme from './scandic-theme'
|
||||
|
||||
addons.setConfig({
|
||||
theme: scandicTheme,
|
||||
})
|
||||
@@ -47,18 +47,23 @@ const preview: Preview = {
|
||||
},
|
||||
parameters: {
|
||||
reactIntl,
|
||||
|
||||
nextjs: {
|
||||
appDirectory: true,
|
||||
},
|
||||
|
||||
docs: {
|
||||
toc: true,
|
||||
},
|
||||
|
||||
controls: { matchers: { color: /(background|color)$/i, date: /Date$/i } },
|
||||
|
||||
options: {
|
||||
storySort: {
|
||||
order: ['Introduction', 'Global', 'Components', 'Compositions', '*'],
|
||||
},
|
||||
},
|
||||
|
||||
backgrounds: {
|
||||
options: {
|
||||
// 👇 Scandic
|
||||
@@ -70,6 +75,13 @@ const preview: Preview = {
|
||||
storybookLight: { name: 'Storybook Light', value: '#F7F9F2' },
|
||||
},
|
||||
},
|
||||
|
||||
a11y: {
|
||||
// 'todo' - show a11y violations in the test UI only
|
||||
// 'error' - fail CI on a11y violations
|
||||
// 'off' - skip a11y checks entirely
|
||||
test: 'todo',
|
||||
},
|
||||
},
|
||||
|
||||
tags: ['autodocs'],
|
||||
|
||||
8
packages/design-system/.storybook/scandic-theme.ts
Normal file
8
packages/design-system/.storybook/scandic-theme.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { create } from 'storybook/theming'
|
||||
|
||||
export default create({
|
||||
base: 'dark',
|
||||
brandTitle: 'Scandic Design System',
|
||||
brandUrl: 'https://www.scandichotels.com/',
|
||||
brandImage: 'http://scandichotels.com/_static/img/scandic-logotype.png',
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview'
|
||||
import { setProjectAnnotations } from '@storybook/nextjs-vite'
|
||||
import * as previewAnnotations from './preview'
|
||||
|
||||
setProjectAnnotations([previewAnnotations])
|
||||
setProjectAnnotations([a11yAddonAnnotations, previewAnnotations])
|
||||
|
||||
@@ -16,8 +16,10 @@ import styles from './select.module.css'
|
||||
import Body from '../Body'
|
||||
import { Label } from '../Label'
|
||||
|
||||
interface SelectProps
|
||||
extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'onSelect'> {
|
||||
interface SelectProps extends Omit<
|
||||
React.SelectHTMLAttributes<HTMLSelectElement>,
|
||||
'onSelect'
|
||||
> {
|
||||
defaultSelectedKey?: Key
|
||||
items: { label: string; value: Key }[]
|
||||
label: string
|
||||
@@ -67,9 +69,11 @@ export default function Select({
|
||||
}
|
||||
}
|
||||
|
||||
function handleOnSelect(key: Key) {
|
||||
function handleOnSelect(key: Key | null) {
|
||||
if (key !== null) {
|
||||
onSelect(key)
|
||||
}
|
||||
}
|
||||
|
||||
let chevronProps = {}
|
||||
|
||||
@@ -141,7 +145,7 @@ export default function Select({
|
||||
key={`${item.value}_${item.label}`}
|
||||
data-testid={item.label}
|
||||
>
|
||||
{optionsIcon ? optionsIcon : null}
|
||||
{optionsIcon}
|
||||
{item.label}
|
||||
</ListBoxItem>
|
||||
))}
|
||||
|
||||
@@ -20,7 +20,7 @@ interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
errorCodeMessages?: Record<string, string>
|
||||
}
|
||||
|
||||
const Checkbox = forwardRef<
|
||||
const CheckboxComponent = forwardRef<
|
||||
HTMLInputElement,
|
||||
React.PropsWithChildren<CheckboxProps>
|
||||
>(function Checkbox(
|
||||
@@ -85,4 +85,8 @@ const Checkbox = forwardRef<
|
||||
)
|
||||
})
|
||||
|
||||
const Checkbox = CheckboxComponent as React.ForwardRefExoticComponent<
|
||||
React.PropsWithChildren<CheckboxProps> & React.RefAttributes<HTMLInputElement>
|
||||
>
|
||||
|
||||
export default Checkbox
|
||||
|
||||
@@ -122,7 +122,7 @@ export type HotelCardProps = {
|
||||
onAddressClick: () => void
|
||||
}
|
||||
|
||||
export const HotelCard = memo(
|
||||
export const HotelCardComponent = memo(
|
||||
({
|
||||
prices,
|
||||
hotel,
|
||||
@@ -385,6 +385,10 @@ export const HotelCard = memo(
|
||||
}
|
||||
)
|
||||
|
||||
export const HotelCard = HotelCardComponent as React.MemoExoticComponent<
|
||||
(props: HotelCardProps) => React.ReactElement
|
||||
>
|
||||
|
||||
interface PricesWrapperProps {
|
||||
children: React.ReactNode
|
||||
isClickable?: boolean
|
||||
|
||||
@@ -97,4 +97,8 @@ function ImageGallery({
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(ImageGallery)
|
||||
const ImageGalleryComponent = memo(ImageGallery)
|
||||
|
||||
export default ImageGalleryComponent as React.MemoExoticComponent<
|
||||
(props: ImageGalleryProps) => React.ReactElement
|
||||
>
|
||||
|
||||
@@ -15,7 +15,7 @@ import styles from './input.module.css'
|
||||
import type { InputProps } from './types'
|
||||
import { Typography } from '../Typography'
|
||||
|
||||
export const Input = forwardRef(function AriaInputWithLabelComponent(
|
||||
const InputComponent = forwardRef(function AriaInputWithLabelComponent(
|
||||
{ label, ...props }: InputProps,
|
||||
forwardedRef: ForwardedRef<HTMLInputElement>
|
||||
) {
|
||||
@@ -44,3 +44,7 @@ export const Input = forwardRef(function AriaInputWithLabelComponent(
|
||||
</AriaLabel>
|
||||
)
|
||||
})
|
||||
|
||||
export const Input = InputComponent as React.ForwardRefExoticComponent<
|
||||
InputProps & React.RefAttributes<HTMLInputElement>
|
||||
>
|
||||
|
||||
@@ -82,7 +82,7 @@ export default function FullView({
|
||||
opacity: 0,
|
||||
x: animateLeft ? -300 : 300,
|
||||
}),
|
||||
}
|
||||
} as const
|
||||
|
||||
return (
|
||||
<div className={styles.fullView}>
|
||||
|
||||
@@ -82,7 +82,7 @@ export default function Gallery({
|
||||
opacity: 0,
|
||||
x: animateLeft ? -300 : 300,
|
||||
}),
|
||||
}
|
||||
} as const
|
||||
|
||||
return (
|
||||
<div className={styles.gallery}>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export const fade = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
transition: { duration: 0.4, ease: 'easeInOut' },
|
||||
transition: { duration: 0.4, ease: 'easeInOut' as const },
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.4, ease: 'easeInOut' },
|
||||
transition: { duration: 0.4, ease: 'easeInOut' as const },
|
||||
},
|
||||
}
|
||||
} as const
|
||||
|
||||
export const slideInOut = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 32,
|
||||
transition: { duration: 0.4, ease: 'easeInOut' },
|
||||
transition: { duration: 0.4, ease: 'easeInOut' as const },
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.4, ease: 'easeInOut' },
|
||||
transition: { duration: 0.4, ease: 'easeInOut' as const },
|
||||
},
|
||||
}
|
||||
} as const
|
||||
|
||||
@@ -7,7 +7,7 @@ export const fade = {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.4, ease: 'easeInOut' },
|
||||
},
|
||||
}
|
||||
} as const
|
||||
|
||||
export const slideInOut = {
|
||||
hidden: {
|
||||
@@ -20,7 +20,7 @@ export const slideInOut = {
|
||||
y: 0,
|
||||
transition: { duration: 0.4, ease: 'easeInOut' },
|
||||
},
|
||||
}
|
||||
} as const
|
||||
|
||||
export const slideFromTop = {
|
||||
hidden: {
|
||||
@@ -33,4 +33,4 @@ export const slideFromTop = {
|
||||
y: 0,
|
||||
transition: { duration: 0.4, ease: 'easeInOut' },
|
||||
},
|
||||
}
|
||||
} as const
|
||||
|
||||
@@ -4,7 +4,11 @@ import { variants } from './variants'
|
||||
|
||||
import type { TypographyProps } from './types'
|
||||
|
||||
export function Typography({ variant, className, children }: TypographyProps) {
|
||||
export function Typography({
|
||||
variant,
|
||||
className,
|
||||
children,
|
||||
}: TypographyProps): React.ReactNode {
|
||||
if (!isValidElement(children)) return null
|
||||
|
||||
const classNames = variants({
|
||||
|
||||
@@ -246,19 +246,19 @@
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@storybook/addon-a11y": "^9.1.2",
|
||||
"@storybook/addon-docs": "^9.1.2",
|
||||
"@storybook/addon-links": "^9.1.2",
|
||||
"@storybook/addon-themes": "^9.1.2",
|
||||
"@storybook/addon-vitest": "^9.1.2",
|
||||
"@storybook/nextjs-vite": "^9.1.2",
|
||||
"@storybook/addon-a11y": "^10.0.8",
|
||||
"@storybook/addon-docs": "^10.0.8",
|
||||
"@storybook/addon-links": "^10.0.8",
|
||||
"@storybook/addon-themes": "^10.0.8",
|
||||
"@storybook/addon-vitest": "^10.0.8",
|
||||
"@storybook/nextjs-vite": "^10.0.8",
|
||||
"@types/css-modules": "^1.0.5",
|
||||
"@types/node": "^20.17.17",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/react": "^19.2.3",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
||||
"@typescript-eslint/parser": "^8.32.0",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"babel-plugin-formatjs": "^10.5.10",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@@ -270,7 +270,7 @@
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"eslint-plugin-storybook": "^9.1.2",
|
||||
"eslint-plugin-storybook": "^10.0.8",
|
||||
"glob": "^11.0.2",
|
||||
"globals": "^16.1.0",
|
||||
"husky": "^9.1.7",
|
||||
@@ -282,11 +282,11 @@
|
||||
"react-dom": "^19.1.0",
|
||||
"rollup": "^4.40.2",
|
||||
"rollup-preserve-directives": "^1.1.3",
|
||||
"storybook": "^9.1.2",
|
||||
"storybook-react-intl": "^4.0.7",
|
||||
"storybook": "^10.0.8",
|
||||
"storybook-react-intl": "^10.0.1",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-dts": "^4.5.3",
|
||||
"vite": "^7.2.4",
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-lib-inject-css": "^2.2.2",
|
||||
"vitest": "^3.2.4",
|
||||
"vitest-browser-react": "^1.0.1"
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type Control,
|
||||
type FieldValues,
|
||||
useFormState,
|
||||
type UseFromSubscribe,
|
||||
type UseFormSubscribe,
|
||||
} from "react-hook-form"
|
||||
|
||||
import {
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
|
||||
export function useFormTracking<T extends FieldValues>(
|
||||
formType: FormType,
|
||||
subscribe: UseFromSubscribe<T>,
|
||||
subscribe: UseFormSubscribe<T>,
|
||||
control: Control<T>,
|
||||
nameSuffix: string = ""
|
||||
) {
|
||||
|
||||
@@ -3,6 +3,8 @@ import "server-only"
|
||||
import deepmerge from "deepmerge"
|
||||
import merge from "deepmerge"
|
||||
|
||||
import type { DocumentNode } from "graphql"
|
||||
|
||||
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
|
||||
|
||||
import { request } from "./request"
|
||||
@@ -24,7 +26,11 @@ export async function batchRequest<T>(
|
||||
try {
|
||||
const response = await Promise.allSettled(
|
||||
queries.map((query) =>
|
||||
request<T>(query.document, query.variables, query.cacheOptions)
|
||||
request<T>(
|
||||
query.document as string | DocumentNode,
|
||||
query.variables,
|
||||
query.cacheOptions
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ function internalRequest<T>(
|
||||
...params,
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
})
|
||||
}),
|
||||
}) as unknown as typeof fetch,
|
||||
})
|
||||
|
||||
const mergedParams =
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@scandic-hotels/typescript-config": "workspace:*",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react": "^19.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
||||
"@typescript-eslint/parser": "^8.32.0",
|
||||
"dotenv": "^16.5.0",
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"esModuleInterop": true,
|
||||
"incremental": false,
|
||||
"isolatedModules": true,
|
||||
"lib": ["es2022", "DOM", "DOM.Iterable"],
|
||||
"lib": ["es2023", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"target": "ES2022"
|
||||
"target": "ES2023"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user