Merged in feature/storybook-tests (pull request #2623)

Feature/storybook tests

* feature: add interaction tests for storybook and upgrade storybook@9

* add a11y testing for storybook

* Merge branch 'master' of bitbucket.org:scandic-swap/web into feature/storybook-tests

* Test and build only required packages

* .

* .

* .

* .

* .

* .

* .

* disable playwright tests in netlify ci

* .

* debug out process.env

* don't run playwright on CI

* remove unused netlify-plugin-playwright-cache

* .

* .

* .

* .

* .

* .

* remove turbo dependancy to design-system#test

* merge

* merge


Approved-by: Anton Gunnarsson
This commit is contained in:
Joakim Jäderberg
2025-08-14 06:25:08 +00:00
parent b2148cd12e
commit f531c7a49f
43 changed files with 1132 additions and 2120 deletions

View File

@@ -1,14 +1,14 @@
[build]
command = "yarn test && yarn build:sas"
command = "yarn test --filter=@scandic-hotels/partner-sas && yarn build:sas"
publish = "apps/partner-sas/.next"
ignore = "if [ -z ${CACHED_COMMIT_REF+x} ] ; then echo 'no CACHED_COMMIT_REF found' && false ; else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF apps/partner-sas packages/common packages/trpc packages/design-system packages/typescript-config ; fi"
[context.branch-deploy]
command = "yarn test && yarn build:sas"
command = "yarn test --filter=@scandic-hotels/partner-sas && yarn build:sas"
[context.deploy-preview]
command = "yarn test && yarn build:sas"
command = "yarn test --filter=@scandic-hotels/partner-sas && yarn build:sas"
[build.environment]
# set TERM variable for terminal output
@@ -17,6 +17,7 @@ TERM = "xterm"
[[plugins]]
package = "@netlify/plugin-nextjs"
# [images]
# remote_images = [
# "https://imagevault-stage.scandichotels.com.*",

View File

@@ -1,9 +1,14 @@
{
"extends": ["//"],
"tasks": {
"dev": {},
"build": {},
"test": {},
"lint": {}
"lint": { "dependsOn": [] },
"build": { "dependsOn": [] },
"test": {
"dependsOn": [
"@scandic-hotels/trpc#test",
"@scandic-hotels/common#test",
"@scandic-hotels/booking-flow#test"
]
}
}
}

View File

@@ -1,14 +1,14 @@
[build]
command = "yarn test && yarn build:web"
command = "yarn test --filter=@scandic-hotels/scandic-web && yarn build:web"
publish = "apps/scandic-web/.next"
ignore = "if [ -z ${CACHED_COMMIT_REF+x} ] ; then echo 'no CACHED_COMMIT_REF found' && false ; else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF apps/scandic-web packages/common packages/trpc packages/design-system packages/typescript-config ; fi"
[context.branch-deploy]
command = "yarn test && yarn build:web"
command = "yarn test --filter=@scandic-hotels/scandic-web && yarn build:web"
[context.deploy-preview]
command = "yarn test && yarn build:web"
command = "yarn test --filter=@scandic-hotels/scandic-web && yarn build:web"
# [[plugins]]
# package = "netlify-plugin-cypress"

View File

@@ -120,7 +120,7 @@
"@types/react-dom": "19.1.0",
"@typescript-eslint/eslint-plugin": "^8.32.0",
"@typescript-eslint/parser": "^8.32.0",
"@vitejs/plugin-react": "^4.6.0",
"@vitejs/plugin-react": "^5.0.0",
"adm-zip": "^0.5.16",
"babel-plugin-formatjs": "^10.5.39",
"cypress": "^14.3.3",

View File

@@ -1,9 +1,14 @@
{
"extends": ["//"],
"tasks": {
"dev": {},
"build": {},
"test": {},
"lint": {}
"lint": { "dependsOn": [] },
"build": { "dependsOn": [] },
"test": {
"dependsOn": [
"@scandic-hotels/trpc#test",
"@scandic-hotels/common#test",
"@scandic-hotels/booking-flow#test"
]
}
}
}

View File

@@ -1,46 +1,49 @@
{
"name": "scandic",
"packageManager": "yarn@4.6.0",
"scripts": {
"build": "turbo run build --env-mode=loose",
"build:web": "turbo run build --filter=@scandic-hotels/scandic-web --env-mode=loose",
"build:sas": "turbo run build --filter=@scandic-hotels/partner-sas --env-mode=loose",
"lint": "turbo run lint",
"dev": "turbo run dev --output-logs new-only",
"dev:web": "turbo run dev --filter=@scandic-hotels/scandic-web --output-logs new-only",
"dev:ds": "turbo run dev --filter=@scandic-hotels/design-system --output-logs new-only",
"dev:sas": "turbo run dev --filter=@scandic-hotels/partner-sas --output-logs new-only",
"test": "turbo run test",
"postinstall": "husky",
"icons:update": "node scripts/material-symbols-update.mjs",
"check-types": "turbo run check-types",
"env:web": "node scripts/show-env.mjs scandic-web --missing",
"env:sas": "node scripts/show-env.mjs partner-sas --missing",
"i18n:extract": "formatjs extract \"{apps/scandic-web,apps/partner-sas,packages/booking-flow,packages/design-system}/{actions,app,components,constants,contexts,env,hooks,i18n,lib,middlewares,netlify,providers,server,services,stores,utils}/**/*.{ts,tsx}\" --format scripts/i18n/formatter.mjs --out-file scripts/i18n/extracted.json",
"i18n:upload": "jiti scripts/i18n/upload.ts",
"i18n:download": "jiti scripts/i18n/download.ts",
"i18n:compile": "formatjs compile-folder --ast --format scripts/i18n/formatter.mjs scripts/i18n/translations-all scripts/i18n/dictionaries",
"i18n:diff": "node scripts/i18n/diff.mjs",
"i18n:clean": "jiti scripts/i18n/clean.ts",
"i18n:distribute": "jiti scripts/i18n/distribute.ts scandic-web partner-sas",
"i18n:push": "yarn i18n:extract && yarn i18n:upload",
"i18n:pull": "yarn i18n:download && yarn i18n:compile && yarn i18n:distribute",
"i18n:sync": "yarn i18n:push && yarn i18n:pull"
},
"workspaces": [
"apps/*",
"packages/*"
],
"devDependencies": {
"@eslint/compat": "^1.2.9",
"@formatjs/cli": "^6.7.1",
"@types/react": "19.1.0",
"@types/react-dom": "19.1.0",
"@yarnpkg/types": "^4.0.1",
"husky": "^9.1.7",
"jiti": "^1.21.0",
"lint-staged": "^15.2.2",
"ts-node": "^10.9.2",
"turbo": "^2.5.2"
}
"name": "scandic",
"packageManager": "yarn@4.6.0",
"scripts": {
"build": "turbo run build --env-mode=loose",
"build:web": "turbo run build --filter=@scandic-hotels/scandic-web --env-mode=loose",
"build:sas": "turbo run build --filter=@scandic-hotels/partner-sas --env-mode=loose",
"lint": "turbo run lint",
"dev": "turbo run dev --output-logs new-only",
"dev:web": "turbo run dev --filter=@scandic-hotels/scandic-web --output-logs new-only",
"dev:ds": "turbo run dev --filter=@scandic-hotels/design-system --output-logs new-only",
"dev:sas": "turbo run dev --filter=@scandic-hotels/partner-sas --output-logs new-only",
"test": "turbo run test",
"postinstall": "husky",
"icons:update": "node scripts/material-symbols-update.mjs",
"check-types": "turbo run check-types",
"env:web": "node scripts/show-env.mjs scandic-web --missing",
"env:sas": "node scripts/show-env.mjs partner-sas --missing",
"i18n:extract": "formatjs extract \"{apps/scandic-web,apps/partner-sas,packages/booking-flow,packages/design-system}/{actions,app,components,constants,contexts,env,hooks,i18n,lib,middlewares,netlify,providers,server,services,stores,utils}/**/*.{ts,tsx}\" --format scripts/i18n/formatter.mjs --out-file scripts/i18n/extracted.json",
"i18n:upload": "jiti scripts/i18n/upload.ts",
"i18n:download": "jiti scripts/i18n/download.ts",
"i18n:compile": "formatjs compile-folder --ast --format scripts/i18n/formatter.mjs scripts/i18n/translations-all scripts/i18n/dictionaries",
"i18n:diff": "node scripts/i18n/diff.mjs",
"i18n:clean": "jiti scripts/i18n/clean.ts",
"i18n:distribute": "jiti scripts/i18n/distribute.ts scandic-web partner-sas",
"i18n:push": "yarn i18n:extract && yarn i18n:upload",
"i18n:pull": "yarn i18n:download && yarn i18n:compile && yarn i18n:distribute",
"i18n:sync": "yarn i18n:push && yarn i18n:pull"
},
"workspaces": [
"apps/*",
"packages/*"
],
"devDependencies": {
"@eslint/compat": "^1.2.9",
"@formatjs/cli": "^6.7.1",
"@types/react": "19.1.0",
"@types/react-dom": "19.1.0",
"@yarnpkg/types": "^4.0.1",
"husky": "^9.1.7",
"jiti": "^1.21.0",
"lint-staged": "^15.2.2",
"ts-node": "^10.9.2",
"turbo": "^2.5.2"
},
"resolutions": {
"vite": "^6.3.5"
}
}

View File

@@ -4,10 +4,11 @@ import type { StorybookConfig } from '@storybook/react-vite'
const config: StorybookConfig = {
stories: ['../lib/**/*.mdx', '../lib/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@storybook/addon-interactions'),
getAbsolutePath('@storybook/addon-links'),
getAbsolutePath('@storybook/addon-themes'),
getAbsolutePath('@storybook/addon-vitest'),
getAbsolutePath('@storybook/addon-docs'),
getAbsolutePath('@storybook/addon-a11y'),
],
framework: {
name: getAbsolutePath('@storybook/react-vite'),

View File

@@ -1,6 +1,6 @@
import { withThemeByClassName } from '@storybook/addon-themes'
import type { Preview, ReactRenderer } from '@storybook/react'
import type { Preview, ReactRenderer } from '@storybook/react-vite'
import '../lib/fonts.css'
import '../lib/style.css'

View File

@@ -0,0 +1,4 @@
import { setProjectAnnotations } from '@storybook/react-vite'
import * as previewAnnotations from './preview'
const annotations = setProjectAnnotations([previewAnnotations])

View File

@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { fn } from '@storybook/test'
import { expect, fn } from 'storybook/test'
import { MaterialIcon } from '../Icons/MaterialIcon'
import { config as typographyConfig } from '../Typography/variants'
@@ -58,6 +58,12 @@ export const PrimaryDefault: Story = {
children: 'Primary button',
typography: 'Body/Paragraph/mdBold',
variant: 'Primary',
isDisabled: false,
isPending: false,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
@@ -66,6 +72,10 @@ export const PrimaryDisabled: Story = {
...PrimaryDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const PrimaryLoading: Story = {
@@ -73,6 +83,10 @@ export const PrimaryLoading: Story = {
...PrimaryDefault.args,
isPending: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const PrimaryLarge: Story = {
@@ -80,6 +94,10 @@ export const PrimaryLarge: Story = {
...PrimaryDefault.args,
size: 'Large',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const PrimaryMedium: Story = {
@@ -87,6 +105,10 @@ export const PrimaryMedium: Story = {
...PrimaryDefault.args,
size: 'Medium',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const PrimarySmall: Story = {
@@ -94,6 +116,10 @@ export const PrimarySmall: Story = {
...PrimaryDefault.args,
size: 'Small',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const PrimaryInvertedDefault: Story = {
@@ -104,6 +130,10 @@ export const PrimaryInvertedDefault: Story = {
variant: 'Primary',
color: 'Inverted',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const PrimaryInvertedDisabled: Story = {
@@ -111,6 +141,10 @@ export const PrimaryInvertedDisabled: Story = {
...PrimaryInvertedDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const PrimaryInvertedLoading: Story = {
@@ -118,6 +152,10 @@ export const PrimaryInvertedLoading: Story = {
...PrimaryInvertedDefault.args,
isPending: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const PrimaryInvertedLarge: Story = {
@@ -125,6 +163,11 @@ export const PrimaryInvertedLarge: Story = {
...PrimaryInvertedDefault.args,
size: 'Large',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const PrimaryInvertedMedium: Story = {
@@ -132,6 +175,11 @@ export const PrimaryInvertedMedium: Story = {
...PrimaryInvertedDefault.args,
size: 'Medium',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const PrimaryInvertedSmall: Story = {
@@ -139,6 +187,11 @@ export const PrimaryInvertedSmall: Story = {
...PrimaryInvertedDefault.args,
size: 'Small',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const SecondaryDefault: Story = {
@@ -148,6 +201,11 @@ export const SecondaryDefault: Story = {
typography: 'Body/Paragraph/mdBold',
variant: 'Secondary',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const SecondaryDisabled: Story = {
@@ -155,6 +213,10 @@ export const SecondaryDisabled: Story = {
...SecondaryDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const SecondaryLoading: Story = {
@@ -162,6 +224,11 @@ export const SecondaryLoading: Story = {
...SecondaryDefault.args,
isPending: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const SecondaryLarge: Story = {
@@ -169,6 +236,11 @@ export const SecondaryLarge: Story = {
...SecondaryDefault.args,
size: 'Large',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const SecondaryMedium: Story = {
@@ -176,6 +248,11 @@ export const SecondaryMedium: Story = {
...SecondaryDefault.args,
size: 'Medium',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const SecondarySmall: Story = {
@@ -183,6 +260,11 @@ export const SecondarySmall: Story = {
...SecondaryDefault.args,
size: 'Small',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const SecondaryInvertedDefault: Story = {
@@ -193,6 +275,11 @@ export const SecondaryInvertedDefault: Story = {
variant: 'Secondary',
color: 'Inverted',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const SecondaryInvertedDisabled: Story = {
@@ -200,6 +287,11 @@ export const SecondaryInvertedDisabled: Story = {
...SecondaryInvertedDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const SecondaryInvertedLoading: Story = {
@@ -207,6 +299,11 @@ export const SecondaryInvertedLoading: Story = {
...SecondaryInvertedDefault.args,
isPending: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const SecondaryInvertedLarge: Story = {
@@ -214,6 +311,11 @@ export const SecondaryInvertedLarge: Story = {
...SecondaryInvertedDefault.args,
size: 'Large',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const SecondaryInvertedMedium: Story = {
@@ -221,6 +323,11 @@ export const SecondaryInvertedMedium: Story = {
...SecondaryInvertedDefault.args,
size: 'Medium',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const SecondaryInvertedSmall: Story = {
@@ -228,6 +335,11 @@ export const SecondaryInvertedSmall: Story = {
...SecondaryInvertedDefault.args,
size: 'Small',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TertiaryDefault: Story = {
@@ -237,6 +349,11 @@ export const TertiaryDefault: Story = {
typography: 'Body/Paragraph/mdBold',
variant: 'Tertiary',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TertiaryDisabled: Story = {
@@ -244,6 +361,11 @@ export const TertiaryDisabled: Story = {
...TertiaryDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const TertiaryLoading: Story = {
@@ -251,12 +373,22 @@ export const TertiaryLoading: Story = {
...TertiaryDefault.args,
isPending: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const TertiaryLarge: Story = {
args: {
...TertiaryDefault.args,
size: 'Large',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TertiaryMedium: Story = {
@@ -264,6 +396,11 @@ export const TertiaryMedium: Story = {
...TertiaryDefault.args,
size: 'Medium',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TertiarySmall: Story = {
@@ -271,6 +408,11 @@ export const TertiarySmall: Story = {
...TertiaryDefault.args,
size: 'Small',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextDefault: Story = {
@@ -280,6 +422,11 @@ export const TextDefault: Story = {
typography: 'Body/Paragraph/mdBold',
variant: 'Text',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextDisabled: Story = {
@@ -287,6 +434,11 @@ export const TextDisabled: Story = {
...TextDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const TextLarge: Story = {
@@ -294,6 +446,11 @@ export const TextLarge: Story = {
...TextDefault.args,
size: 'Large',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextMedium: Story = {
@@ -301,6 +458,11 @@ export const TextMedium: Story = {
...TextDefault.args,
size: 'Medium',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextSmall: Story = {
@@ -308,6 +470,11 @@ export const TextSmall: Story = {
...TextDefault.args,
size: 'Small',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextNoWrapping: Story = {
@@ -316,6 +483,10 @@ export const TextNoWrapping: Story = {
children: 'Text button with wrapping false',
wrapping: false,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextInvertedDefault: Story = {
@@ -326,6 +497,11 @@ export const TextInvertedDefault: Story = {
variant: 'Text',
color: 'Inverted',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextInvertedDisabled: Story = {
@@ -333,6 +509,11 @@ export const TextInvertedDisabled: Story = {
...TextInvertedDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const TextInvertedLarge: Story = {
@@ -340,6 +521,11 @@ export const TextInvertedLarge: Story = {
...TextInvertedDefault.args,
size: 'Large',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextInvertedMedium: Story = {
@@ -347,6 +533,11 @@ export const TextInvertedMedium: Story = {
...TextInvertedDefault.args,
size: 'Medium',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextInvertedSmall: Story = {
@@ -354,6 +545,11 @@ export const TextInvertedSmall: Story = {
...TextInvertedDefault.args,
size: 'Small',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TextWithIcon: Story = {
@@ -368,6 +564,13 @@ export const TextWithIcon: Story = {
typography: 'Body/Paragraph/mdBold',
variant: 'Text',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
expect(canvas.getByText('Text with icon')).toBeDefined()
expect(canvas.getByTestId('MaterialIcon')).toBeDefined()
},
}
export const TextWithIconInverted: Story = {
@@ -383,4 +586,12 @@ export const TextWithIconInverted: Story = {
variant: 'Text',
color: 'Inverted',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(await canvas.findByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
expect(canvas.getByText('Text with icon')).toBeDefined()
expect(canvas.getByTestId('MaterialIcon')).toBeDefined()
},
}

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { Card } from './Card.tsx'

View File

@@ -1,5 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react'
import { fn } from '@storybook/test'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { fn } from 'storybook/test'
import { themes } from '../../../../.storybook/preview'

View File

@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { fn } from '@storybook/test'
import { fn } from 'storybook/test'
import { MaterialIcon } from '../Icons/MaterialIcon/MaterialIcon.tsx'
import { ChipButton } from './ChipButton.tsx'

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { MaterialIcon } from '../Icons/MaterialIcon'
import { ChipLink } from './ChipLink.tsx'

View File

@@ -1,5 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react'
import { fn } from '@storybook/test'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { fn } from 'storybook/test'
import { Chips } from './Chips.tsx'
import { ChipLink } from '../ChipLink/ChipLink.tsx'

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { Divider } from './Divider'

View File

@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { fn } from '@storybook/test'
import { expect, fn } from 'storybook/test'
import { MaterialIcon } from '../Icons/MaterialIcon'
import { IconButton } from './IconButton'
@@ -40,6 +40,10 @@ export const PrimaryDefault: Story = {
children: <MaterialIcon icon="search" size={24} color="CurrentColor" />,
theme: 'Primary',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const PrimaryDisabled: Story = {
@@ -47,6 +51,10 @@ export const PrimaryDisabled: Story = {
...PrimaryDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const InvertedDefault: Story = {
@@ -57,6 +65,10 @@ export const InvertedDefault: Story = {
),
theme: 'Inverted',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const InvertedDisabled: Story = {
@@ -64,6 +76,10 @@ export const InvertedDisabled: Story = {
...InvertedDefault.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const InvertedElevated: Story = {
@@ -71,6 +87,10 @@ export const InvertedElevated: Story = {
...InvertedDefault.args,
style: 'Elevated',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const InvertedElevatedDisabled: Story = {
@@ -78,6 +98,10 @@ export const InvertedElevatedDisabled: Story = {
...InvertedElevated.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const InvertedMuted: Story = {
@@ -86,6 +110,11 @@ export const InvertedMuted: Story = {
children: <MaterialIcon icon="close" size={24} color="CurrentColor" />,
style: 'Muted',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const InvertedMutedDisabled: Story = {
@@ -93,6 +122,11 @@ export const InvertedMutedDisabled: Story = {
...InvertedMuted.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const InvertedFaded: Story = {
@@ -100,6 +134,10 @@ export const InvertedFaded: Story = {
...InvertedDefault.args,
style: 'Faded',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const InvertedFadedDisabled: Story = {
@@ -107,6 +145,10 @@ export const InvertedFadedDisabled: Story = {
...InvertedFaded.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const TertiaryElevated: Story = {
@@ -116,6 +158,10 @@ export const TertiaryElevated: Story = {
theme: 'Tertiary',
style: 'Elevated',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const TertiaryDisabled: Story = {
@@ -123,6 +169,10 @@ export const TertiaryDisabled: Story = {
...TertiaryElevated.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}
export const BlackMuted: Story = {
@@ -131,6 +181,10 @@ export const BlackMuted: Story = {
children: <MaterialIcon icon="close" size={24} color="CurrentColor" />,
theme: 'Black',
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(1)
},
}
export const BlackMutedDisabled: Story = {
@@ -138,4 +192,8 @@ export const BlackMutedDisabled: Story = {
...BlackMuted.args,
isDisabled: true,
},
play: async ({ canvas, userEvent, args }) => {
await userEvent.click(canvas.getByRole('button'))
expect(args.onPress).toHaveBeenCalledTimes(0)
},
}

View File

@@ -23,6 +23,7 @@ export function MaterialIcon({
<span>
<MaterialSymbol
className={classNames}
data-testid="MaterialIcon"
size={size}
{...props}
fill={isFilled}

View File

@@ -1,10 +1,13 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { expect } from 'storybook/test'
import { Input } from './Input'
import { TextField } from 'react-aria-components'
const meta: Meta<typeof Input> = {
title: 'Components/Input',
// @ts-expect-error Input does not support this, but wrapping <TextField> does
component: ({ isInvalid, ...props }) => (
<TextField isInvalid={isInvalid}>
<Input {...props} />
@@ -23,6 +26,19 @@ export const Default: Story = {
name: 'foo',
required: false,
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).not.toBeDisabled()
expect(textbox).toHaveValue('')
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('Hello World')
await userEvent.clear(textbox)
expect(textbox).toHaveValue('')
},
}
export const Filled: Story = {
@@ -31,6 +47,13 @@ export const Filled: Story = {
name: 'foo',
value: 'Value',
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).not.toBeDisabled()
},
}
export const Error: Story = {
@@ -40,6 +63,12 @@ export const Error: Story = {
// @ts-expect-error Input does not support this, but wrapping <TextField> does
isInvalid: true,
},
play: async ({ canvas }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveAttribute('aria-invalid', 'true')
expect(textbox).not.toBeDisabled()
},
}
export const Disabled: Story = {
@@ -48,6 +77,15 @@ export const Disabled: Story = {
name: 'foo',
disabled: true,
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('')
expect(textbox).toBeDisabled()
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('')
},
}
export const DisabledFilled: Story = {
@@ -57,4 +95,13 @@ export const DisabledFilled: Story = {
disabled: true,
value: 'Value',
},
play: async ({ canvas, userEvent }) => {
const textbox = canvas.getByRole('textbox')
expect(textbox).toHaveValue('Value')
expect(textbox).toBeDisabled()
await userEvent.type(textbox, 'Hello World')
expect(textbox).toHaveValue('Value')
},
}

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { Label } from './Label'

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { Loading } from './Loading'
import { config } from './variants'

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import CampaignRateCard from '.'
const meta: Meta<typeof CampaignRateCard> = {

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import CodeRateCard from '.'
const meta: Meta<typeof CodeRateCard> = {

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import NoRateAvailableCard from '.'
const meta: Meta<typeof NoRateAvailableCard> = {

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import PointsRateCard from '.'

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import RegularRateCard from '.'
const meta: Meta<typeof RegularRateCard> = {

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { Select } from './Select'

View File

@@ -5,7 +5,7 @@ import {
Description,
Controls,
Stories,
} from '@storybook/blocks'
} from '@storybook/addon-docs/blocks'
import * as TypographyStories from './Typography.stories.tsx'

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from '@storybook/react'
import type { Meta, StoryObj } from '@storybook/react-vite'
import { Typography } from './Typography.tsx'

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
<Meta title="Introduction" />

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
import { Colors } from './Colors'

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
import { Colors } from './Colors'

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
import { Colors } from './Colors'

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
import { Colors } from './Colors'

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
import { Colors } from './Colors'

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
import { Colors } from './Colors'

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
import { Colors } from './Colors'

View File

@@ -1,4 +1,4 @@
import { Meta } from '@storybook/blocks'
import { Meta } from '@storybook/addon-docs/blocks'
import { Colors } from './Colors'

View File

@@ -1,2 +1,3 @@
[build]
ignore = "if [ -z ${CACHED_COMMIT_REF+x} ] ; then echo 'no CACHED_COMMIT_REF found' && false ; else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF packages/design-system packages/typescript-config ; fi"
command = "npx playwright install chromium && (yarn test --filter=@scandic-hotels/design-system > /dev/null 2>&1 || yarn test --filter=@scandic-hotels/design-system) && yarn build --filter=@scandic-hotels/design-system"
ignore = "if [ -z ${CACHED_COMMIT_REF+x} ] ; then echo 'no CACHED_COMMIT_REF found' && false ; else git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF packages/design-system packages/typescript-config ; fi"

View File

@@ -164,10 +164,12 @@
"storybook": "yarn run generate && storybook dev -p 6006",
"build-storybook": "storybook build",
"test": "vitest run --passWithNoTests",
"test:s": "vitest --project=storybook",
"test:watch": "vitest",
"prepack": "yarn run build",
"prepare": "husky && yarn run build",
"check-types": "tsc --noEmit"
"check-types": "tsc --noEmit",
"test:browser": "vitest --config=vitest.browser.config.ts"
},
"dependencies": {
"@scandic-hotels/common": "workspace:*"
@@ -187,21 +189,20 @@
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.26.0",
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-links": "^8.6.12",
"@storybook/addon-themes": "^8.6.12",
"@storybook/blocks": "^8.6.12",
"@storybook/react": "^8.6.12",
"@storybook/react-vite": "^8.6.12",
"@storybook/test": "^8.6.12",
"@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/react-vite": "^9.1.2",
"@types/css-modules": "^1.0.5",
"@types/node": "^20.17.17",
"@types/react": "^19",
"@types/react-dom": "^19",
"@typescript-eslint/eslint-plugin": "^8.32.0",
"@typescript-eslint/parser": "^8.32.0",
"@vitejs/plugin-react": "^4.4.1",
"@vitejs/plugin-react": "^5.0.0",
"@vitest/browser": "^3.2.4",
"class-variance-authority": "^0.7.1",
"colord": "^2.9.3",
"copy-to-clipboard": "^3.3.3",
@@ -210,23 +211,25 @@
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"eslint-plugin-storybook": "^0.12.0",
"eslint-plugin-storybook": "^9.1.2",
"glob": "^11.0.2",
"globals": "^16.1.0",
"husky": "^9.1.7",
"jiti": "^1.21.0",
"lint-staged": "^15.5.2",
"motion": "^12.10.0",
"playwright": "^1.54.2",
"prettier": "^3.5.3",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"rollup": "^4.40.2",
"rollup-preserve-directives": "^1.1.3",
"storybook": "^8.6.12",
"storybook": "^9.1.2",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"vite-plugin-dts": "^4.5.3",
"vite-plugin-lib-inject-css": "^2.2.2",
"vitest": "^3.2.4"
"vitest": "^3.2.4",
"vitest-browser-react": "^1.0.1"
}
}

View File

@@ -13,6 +13,9 @@ import preserveDirectives from 'rollup-preserve-directives'
// https://vitejs.dev/config/
export default defineConfig({
optimizeDeps: {
include: ['react/jsx-dev-runtime'],
},
plugins: [
react(),
libInjectCss(),
@@ -30,7 +33,8 @@ export default defineConfig({
// rollupTypes: true,
bundledPackages: ['class-variance-authority', 'clsx'],
}),
preserveDirectives(),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
preserveDirectives() as any,
],
build: {
cssCodeSplit: true,

View File

@@ -0,0 +1,52 @@
import { defineConfig, mergeConfig } from 'vitest/config'
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const dirname =
typeof __dirname !== 'undefined'
? __dirname
: path.dirname(fileURLToPath(import.meta.url))
import viteConfig from './vite.config'
const isCI = process.env.CI === 'true'
const browserInstances = isCI
? [{ browser: 'chromium' }]
: [{ browser: 'chromium' }, { browser: 'firefox' }, { browser: 'webkit' }]
export default mergeConfig(
viteConfig,
defineConfig({
// !isCI ?
test: {
projects: [
{
plugins: [
storybookTest({
// The location of your Storybook config, main.js|ts
configDir: path.join(dirname, '.storybook'),
// This should match your package.json script to run Storybook
// The --ci flag will skip prompts and not open a browser
storybookScript: 'yarn storybook --ci',
}),
],
test: {
name: 'storybook',
// Enable browser mode
browser: {
enabled: true,
// Make sure to install Playwright
provider: 'playwright',
headless: true,
instances: browserInstances,
},
setupFiles: ['./.storybook/vitest.setup.ts'],
},
},
],
},
//: {}, // Netlify CI fails to run playwright tests. Only supported locally for now
})
)

2636
yarn.lock

File diff suppressed because it is too large Load Diff