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:
@@ -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()
|
||||
},
|
||||
}
|
||||
34
packages/design-system/lib/components/TextLink/TextLink.tsx
Normal file
34
packages/design-system/lib/components/TextLink/TextLink.tsx
Normal 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,
|
||||
})}
|
||||
/>
|
||||
)
|
||||
}
|
||||
1
packages/design-system/lib/components/TextLink/index.tsx
Normal file
1
packages/design-system/lib/components/TextLink/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { TextLink } from './TextLink'
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
12
packages/design-system/lib/components/TextLink/types.ts
Normal file
12
packages/design-system/lib/components/TextLink/types.ts
Normal 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
|
||||
}
|
||||
38
packages/design-system/lib/components/TextLink/variants.ts
Normal file
38
packages/design-system/lib/components/TextLink/variants.ts
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user