Merged in feat/BOOK-61-refactor-hotel-page-css-variables (pull request #3014)

Feat/BOOK-61 refactor hotel page css variables

* feat(BOOK-61): Breadcrumbs

* feat(BOOK-61): intro section

* feat(BOOK-61): show more button

* feat(BOOK-61): rooms section

* feat(BOOK-61): sidepeeks

* feat(BOOK-61): deprecated old Link component

* feat(BOOK-61): added new TextLink component to the design-system

* feat(BOOK-61): replaced deprecated links with new TextLink component

* feat(BOOK-61): miscellaneous changes


Approved-by: Bianca Widstam
Approved-by: Christel Westerberg
This commit is contained in:
Erik Tiekstra
2025-10-29 09:15:03 +00:00
parent bfe5c5f8bb
commit 333636c81a
122 changed files with 782 additions and 498 deletions

View File

@@ -0,0 +1,143 @@
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
import { expect } from 'storybook/test'
import { TextLink } from '.'
import { MaterialIcon } from '../Icons/MaterialIcon'
import { Typography } from '../Typography'
import { config } from './variants'
const meta: Meta<typeof TextLink> = {
title: 'Components/TextLink',
component: TextLink,
argTypes: {
theme: {
control: 'select',
options: Object.keys(config.variants.theme),
default: config.defaultVariants.theme,
},
isInline: {
control: 'boolean',
default: false,
description:
'Should be used when the link is placed inside a text block, removes the padding.',
},
isDisabled: {
control: 'boolean',
default: false,
description: 'Disables the link and makes it non-interactive.',
},
},
}
export default meta
type Story = StoryObj<typeof TextLink>
export const Default: Story = {
args: {
href: 'https://www.scandichotels.com/en',
},
render: (args) => <TextLink {...args}>Default link</TextLink>,
play: async ({ canvasElement }) => {
const link = canvasElement.querySelector('a')
if (!link) throw new Error('Link not found')
expect(link).toBeInTheDocument()
},
}
export const Inverted: Story = {
args: {
...Default.args,
theme: 'Inverted',
},
render: (args) => <TextLink {...args}>Inverted link</TextLink>,
play: async ({ canvasElement }) => {
const link = canvasElement.querySelector('a')
if (!link) throw new Error('Link not found')
expect(link).toBeInTheDocument()
},
}
export const Disabled: Story = {
args: {
...Default.args,
isDisabled: true,
},
render: (args) => <TextLink {...args}>Disabled link</TextLink>,
play: async ({ canvasElement }) => {
const link = canvasElement.querySelector('a')
if (!link) throw new Error('Link not found')
expect(link).toBeInTheDocument()
},
}
export const WithIcon: Story = {
args: {
...Default.args,
},
render: (args) => (
<TextLink {...args}>
Link with icon
<MaterialIcon icon="arrow_forward" size={24} color="CurrentColor" />
</TextLink>
),
play: async ({ canvasElement }) => {
const link = canvasElement.querySelector('a')
if (!link) throw new Error('Link not found')
expect(link).toBeInTheDocument()
},
}
export const Small: Story = {
args: {
...Default.args,
typography: 'Link/sm',
},
render: (args) => <TextLink {...args}>Small link</TextLink>,
play: async ({ canvasElement }) => {
const link = canvasElement.querySelector('a')
if (!link) throw new Error('Link not found')
expect(link).toBeInTheDocument()
},
}
export const SmallWithIcon: Story = {
args: {
...Default.args,
typography: 'Link/sm',
},
render: (args) => (
<TextLink {...args}>
Small link with icon
<MaterialIcon icon="arrow_forward" size={20} color="CurrentColor" />
</TextLink>
),
play: async ({ canvasElement }) => {
const link = canvasElement.querySelector('a')
if (!link) throw new Error('Link not found')
expect(link).toBeInTheDocument()
},
}
export const Inline: Story = {
args: {
...Default.args,
isInline: true,
},
render: (args) => (
<Typography variant="Body/Paragraph/mdRegular">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.{' '}
<TextLink {...args}>Inline link</TextLink> Curabitur vitae neque non
ipsum efficitur hendrerit at ut nulla. Cras in tellus et ligula posuere
ullamcorper. Praesent pulvinar rutrum metus ut gravida.
</p>
</Typography>
),
play: async ({ canvasElement }) => {
const link = canvasElement.querySelector('a')
if (!link) throw new Error('Link not found')
expect(link).toBeInTheDocument()
},
}

View File

@@ -0,0 +1,34 @@
import { cx } from 'class-variance-authority'
import NextLink from 'next/link'
import { TextLinkProps } from './types'
import { variants } from './variants'
import styles from './textLink.module.css'
export function TextLink({
theme,
className,
isDisabled,
tabIndex,
isInline,
typography,
...props
}: TextLinkProps) {
const classNames = variants({
theme,
typography,
className,
})
return (
<NextLink
{...props}
tabIndex={isDisabled ? -1 : tabIndex}
aria-disabled={isDisabled}
className={cx(classNames, {
[styles.disabled]: isDisabled,
[styles.inline]: isInline,
})}
/>
)
}

View File

@@ -0,0 +1 @@
export { TextLink } from './TextLink'

View File

@@ -0,0 +1,36 @@
.textLink {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--Space-x05);
padding: var(--Space-x025) 0;
}
.disabled {
color: var(--Text-Interactive-Disabled);
text-decoration: none;
cursor: default;
pointer-events: none;
}
.inline {
padding: 0;
}
.theme-primary:not(.disabled) {
color: var(--Text-Interactive-Secondary);
text-decoration: underline;
&:hover {
color: var(--Text-Interactive-Secondary-Hover);
}
}
.theme-inverted:not(.disabled) {
color: var(--Text-Inverted);
text-decoration: underline;
&:hover {
opacity: 0.7;
}
}

View File

@@ -0,0 +1,12 @@
import type { VariantProps } from 'class-variance-authority'
import NextLink from 'next/link'
import type { ComponentProps } from 'react'
import type { variants } from './variants'
export interface TextLinkProps
extends ComponentProps<typeof NextLink>,
VariantProps<typeof variants> {
isDisabled?: boolean
isInline?: boolean
}

View File

@@ -0,0 +1,38 @@
import { cva } from 'class-variance-authority'
import {
config as typographyConfig,
withTypography,
} from '../Typography/variants'
import { deepmerge } from 'deepmerge-ts'
import styles from './textLink.module.css'
export const config = {
variants: {
theme: {
Primary: styles['theme-primary'],
Inverted: styles['theme-inverted'],
},
},
defaultVariants: {
theme: 'Primary',
},
} as const
const textLinkConfig = {
variants: {
...config.variants,
typography: typographyConfig.variants.variant,
},
defaultVariants: {
...config.defaultVariants,
typography: 'Link/md',
},
} as const
export const variants = cva(styles.textLink, withTypography(textLinkConfig))
export function withTextLink<T>(config: T) {
return deepmerge(textLinkConfig, config)
}