diff --git a/.changeset/dirty-beds-agree.md b/.changeset/dirty-beds-agree.md new file mode 100644 index 0000000000..2164ac0977 --- /dev/null +++ b/.changeset/dirty-beds-agree.md @@ -0,0 +1,5 @@ +--- +"@ultraviolet/ui": minor +--- + +`Link`: update the decoration and sentiment behavior, fix styles for the different sizes diff --git a/packages/ui/src/components/Banner/__tests__/__snapshots__/index.test.tsx.snap b/packages/ui/src/components/Banner/__tests__/__snapshots__/index.test.tsx.snap index 6ec1669a6e..36abf58eea 100644 --- a/packages/ui/src/components/Banner/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/ui/src/components/Banner/__tests__/__snapshots__/index.test.tsx.snap @@ -113,33 +113,28 @@ exports[`banner > renders correctly with a link 1`] = ` class="styles__toi52u0 styles_alignItems_center_xxsmall__toi52ud styles_flexDirection_row_xxsmall__toi52u2j styles_gap_1rem_xxsmall__toi52u3v" > Link - - - - OpenInNewIcon - - - - - + + OpenInNewIcon + + + + @@ -375,33 +370,28 @@ exports[`banner > should render correctly with dark theme 1`] = ` class="styles__toi52u0 styles_alignItems_center_xxsmall__toi52ud styles_flexDirection_row_xxsmall__toi52u2j styles_gap_1rem_xxsmall__toi52u3v" > Learn more - - - - OpenInNewIcon - - - - - + + OpenInNewIcon + + + + diff --git a/packages/ui/src/components/Banner/index.tsx b/packages/ui/src/components/Banner/index.tsx index 21ad8b7410..f21dc586be 100644 --- a/packages/ui/src/components/Banner/index.tsx +++ b/packages/ui/src/components/Banner/index.tsx @@ -126,7 +126,7 @@ export const Banner = ({ ) : null} {linkText ? ( - + {linkText} ) : null} diff --git a/packages/ui/src/components/Breadcrumbs/__tests__/__snapshots__/index.test.tsx.snap b/packages/ui/src/components/Breadcrumbs/__tests__/__snapshots__/index.test.tsx.snap index 18f4c2d82a..5f02bb248b 100644 --- a/packages/ui/src/components/Breadcrumbs/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/ui/src/components/Breadcrumbs/__tests__/__snapshots__/index.test.tsx.snap @@ -15,8 +15,7 @@ exports[`breadcrumbs > click on middle item 1`] = ` class="styles__1yxbbx44 styles__1yxbbx47" > Step 1 @@ -62,8 +61,7 @@ exports[`breadcrumbs > last item should no be clickable 1`] = ` class="styles__1yxbbx44 styles__1yxbbx47" > Step 1 @@ -73,8 +71,7 @@ exports[`breadcrumbs > last item should no be clickable 1`] = ` class="styles__1yxbbx44 styles__1yxbbx47" > I'm a very long long long long long long long long long long long long step @@ -111,8 +108,7 @@ exports[`breadcrumbs > renders correctly with default values 1`] = ` class="styles__1yxbbx44 styles__1yxbbx47" > Step 1 @@ -122,8 +118,7 @@ exports[`breadcrumbs > renders correctly with default values 1`] = ` class="styles__1yxbbx44 styles__1yxbbx47" > I'm a very long long long long long long long long long long long long step @@ -177,8 +172,7 @@ exports[`breadcrumbs > renders correctly with minWidth and maxWidth 1`] = ` class="styles__1yxbbx44 styles__1yxbbx47" > Step 1 @@ -189,8 +183,7 @@ exports[`breadcrumbs > renders correctly with minWidth and maxWidth 1`] = ` style="--_1yxbbx41: 100px; --_1yxbbx40: 200px;" > I'm a very long long long long long long long long long long long long step diff --git a/packages/ui/src/components/EmptyState/__tests__/__snapshots__/index.test.tsx.snap b/packages/ui/src/components/EmptyState/__tests__/__snapshots__/index.test.tsx.snap index 373b42a195..279d85a3e1 100644 --- a/packages/ui/src/components/EmptyState/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/ui/src/components/EmptyState/__tests__/__snapshots__/index.test.tsx.snap @@ -136,8 +136,7 @@ exports[`emptySpace > should work with learn more 1`] = ` class="styles__toi52u0 styles_alignItems_center_xxsmall__toi52ud styles_flexDirection_column_xxsmall__toi52u2d styles_gap_1rem_xxsmall__toi52u3v styles_justifyContent_center_xxsmall__toi52u61" > Learn more diff --git a/packages/ui/src/components/GlobalAlert/__tests__/__snapshots__/index.test.tsx.snap b/packages/ui/src/components/GlobalAlert/__tests__/__snapshots__/index.test.tsx.snap index 3371069ff5..e4449bd24d 100644 --- a/packages/ui/src/components/GlobalAlert/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/ui/src/components/GlobalAlert/__tests__/__snapshots__/index.test.tsx.snap @@ -310,8 +310,7 @@ exports[`globalAlert > renders correctly with link 1`] = ` > This is a  Global Alert diff --git a/packages/ui/src/components/Link/__stories__/Examples.stories.tsx b/packages/ui/src/components/Link/__stories__/Examples.stories.tsx index a8df8a798c..08a5a9ce24 100644 --- a/packages/ui/src/components/Link/__stories__/Examples.stories.tsx +++ b/packages/ui/src/components/Link/__stories__/Examples.stories.tsx @@ -1,4 +1,5 @@ import type { Decorator } from '@storybook/react-vite' +import { DocPaperIcon } from '@ultraviolet/icons/DocPaperIcon' import type { ComponentProps } from 'react' import { Link } from '..' import { Stack } from '../../Stack' @@ -11,24 +12,20 @@ export const Examples = (args: ComponentProps) => { <> To know more about that feature please visit{' '} - + our website - {' '} - that is available any time. + + . - To know more about that feature please visit{' '} - - our website - {' '} - that is available any time. + Update the view using the{' '} + + filters panel + + . - To know more about that feature please visit{' '} - - our website - {' '} - that is available any time. + Go to your profile page to update your preferences. ) diff --git a/packages/ui/src/components/Link/__stories__/Icons.stories.tsx b/packages/ui/src/components/Link/__stories__/Icons.stories.tsx new file mode 100644 index 0000000000..47903056c2 --- /dev/null +++ b/packages/ui/src/components/Link/__stories__/Icons.stories.tsx @@ -0,0 +1,30 @@ +import type { ComponentProps } from 'react' +import { Link } from '..' +import { Stack } from '../../Stack' + +const sizes = ['large', 'small', 'xsmall'] as const + +export const Icons = (props: ComponentProps) => ( + + + {sizes.map(size => ( + + {size} + + ))} + + + {sizes.map(size => ( + + {size} + + ))} + + +) + +Icons.parameters = { + docs: { + description: { story: 'Add an arrow icon on the left or right with the `iconPosition` property.' }, + }, +} diff --git a/packages/ui/src/components/Link/__stories__/OneLine.stories.tsx b/packages/ui/src/components/Link/__stories__/OneLine.stories.tsx index 596847fcc8..a10cdae5d9 100644 --- a/packages/ui/src/components/Link/__stories__/OneLine.stories.tsx +++ b/packages/ui/src/components/Link/__stories__/OneLine.stories.tsx @@ -27,7 +27,7 @@ OneLine.parameters = { docs: { description: { story: - ' `oneLine` prop will force link to be display on a single line by adding `...` after cropped text and will display a tooltip with full text when hovered.', + 'The `oneLine` property will force the link to be display on a single line by adding an ellipsis `...` after the cropped text and will display a tooltip with the full text when hovered.', }, }, } diff --git a/packages/ui/src/components/Link/__stories__/Primary.stories.tsx b/packages/ui/src/components/Link/__stories__/Primary.stories.tsx deleted file mode 100644 index 3540f8ff90..0000000000 --- a/packages/ui/src/components/Link/__stories__/Primary.stories.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import type { Decorator } from '@storybook/react-vite' -import type { ComponentProps } from 'react' -import { Link } from '..' -import { Stack } from '../../Stack' - -export const Primary = (props: ComponentProps) => ( - - - Primary link - - Default link - -) - -Primary.decorators = [ - StoryComponent => ( - - - - ), -] as Decorator[] - -Primary.parameters = { - docs: { - description: { - story: - 'The `sentiment` prop should only be used with the `primary` value. Other values are deprecated and will be removed in the next major version.', - }, - }, -} diff --git a/packages/ui/src/components/Link/__stories__/Prominence.stories.tsx b/packages/ui/src/components/Link/__stories__/Prominence.stories.tsx index d36b05bb91..bb43e6d728 100644 --- a/packages/ui/src/components/Link/__stories__/Prominence.stories.tsx +++ b/packages/ui/src/components/Link/__stories__/Prominence.stories.tsx @@ -1,21 +1,37 @@ -import type { Decorator } from '@storybook/react-vite' +import { useTheme } from '@ultraviolet/themes' import type { ComponentProps } from 'react' import { Link } from '..' -import type { ProminenceProps } from '..' import { Stack } from '../../Stack' -import { PROMINENCES } from '../constants' -export const Prominence = (props: ComponentProps) => - Object.keys(PROMINENCES).map(prominence => ( - - {prominence} - - )) +export const Prominence = (props: ComponentProps) => { + const theme = useTheme() -Prominence.decorators = [ - StoryComponent => ( - - + return ( + + + + Default + + + External + + + + + Default + + + External + + - ), -] as Decorator[] + ) +} + +Prominence.parameters = { + docs: { + description: { + story: 'On a dark background, use `prominence="strong"` to make the link visible.', + }, + }, +} diff --git a/packages/ui/src/components/Link/__stories__/Sentiment.stories.tsx b/packages/ui/src/components/Link/__stories__/Sentiment.stories.tsx new file mode 100644 index 0000000000..b45f711ac0 --- /dev/null +++ b/packages/ui/src/components/Link/__stories__/Sentiment.stories.tsx @@ -0,0 +1,33 @@ +import type { Decorator } from '@storybook/react-vite' +import type { ComponentProps } from 'react' +import { Link } from '..' +import { Stack } from '../../Stack' + +export const Sentiment = (props: ComponentProps) => ( + <> + Link to an internal page + + Link that opens in a new tab + + + Link to an internal page that opens in new tab + + +) + +Sentiment.decorators = [ + StoryComponent => ( + + + + ), +] as Decorator[] + +Sentiment.parameters = { + docs: { + description: { + story: + 'By default the sentiment of a link is `info` for a link with `target="_blank"`, and `primary` otherwise. For particular cases where you want to modify the sentiment (i.e. a link with `target="_blank"` but to an internal page so it should be primary), you can choose it with the `sentiment` property.', + }, + }, +} diff --git a/packages/ui/src/components/Link/__stories__/Size.stories.tsx b/packages/ui/src/components/Link/__stories__/Size.stories.tsx index be5cd59615..6911a8ad2a 100644 --- a/packages/ui/src/components/Link/__stories__/Size.stories.tsx +++ b/packages/ui/src/components/Link/__stories__/Size.stories.tsx @@ -14,13 +14,13 @@ export const Size = (props: ComponentProps) => Size.parameters = { docs: { - description: { story: 'Edit `size` prop to change the size of the text' }, + description: { story: 'Edit the `size` property to change the size of the text' }, }, } Size.decorators = [ Story => ( - + ), diff --git a/packages/ui/src/components/Link/__stories__/Target.stories.tsx b/packages/ui/src/components/Link/__stories__/Target.stories.tsx index 822d7dd53e..12e9739adb 100644 --- a/packages/ui/src/components/Link/__stories__/Target.stories.tsx +++ b/packages/ui/src/components/Link/__stories__/Target.stories.tsx @@ -1,12 +1,33 @@ -import { Template } from './Template.stories' +import type { ComponentProps } from 'react' +import { Link } from '..' +import { Stack } from '../../Stack' -export const Target = Template.bind({}) +const sizes = ['large', 'small', 'xsmall'] as const + +export const Target = (props: ComponentProps) => ( + + + {sizes.map(size => ( + + {size} + + ))} + + + {sizes.map(size => ( + + {size} + + ))} + + +) Target.parameters = { docs: { description: { story: - 'Edit the `target` prop to specify the target you want for your link. By using `_blank`, an icon is added to show that it is an external link', + 'Edit the `target` property to specify the target you want for your link. External links (`target="_blank"`) have an arrow icon automatically added, they have an `info` sentiment by default, and can have a visited state.', }, }, } diff --git a/packages/ui/src/components/Link/__stories__/Variants.stories.tsx b/packages/ui/src/components/Link/__stories__/Variants.stories.tsx deleted file mode 100644 index c382a4690c..0000000000 --- a/packages/ui/src/components/Link/__stories__/Variants.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { Decorator } from '@storybook/react-vite' -import type { ComponentProps } from 'react' -import { Link } from '..' -import { Stack } from '../../Stack' - -export const Variants = (props: ComponentProps) => - (['inline', 'standalone'] as const).map(variant => ( - - {variant} - - )) - -Variants.decorators = [ - StoryComponent => ( - - - - ), -] as Decorator[] diff --git a/packages/ui/src/components/Link/__stories__/index.stories.tsx b/packages/ui/src/components/Link/__stories__/index.stories.tsx index 6f36bbd2af..4551105431 100644 --- a/packages/ui/src/components/Link/__stories__/index.stories.tsx +++ b/packages/ui/src/components/Link/__stories__/index.stories.tsx @@ -17,8 +17,8 @@ export default { } as Meta export { Playground } from './Playground.stories' -export { Variants } from './Variants.stories' -export { Primary } from './Primary.stories' +export { Sentiment } from './Sentiment.stories' +export { Icons } from './Icons.stories' export { Target } from './Target.stories' export { Size } from './Size.stories' export { OneLine } from './OneLine.stories' diff --git a/packages/ui/src/components/Link/__tests__/__snapshots__/index.test.tsx.snap b/packages/ui/src/components/Link/__tests__/__snapshots__/index.test.tsx.snap index b61bcbb693..d599545a30 100644 --- a/packages/ui/src/components/Link/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/ui/src/components/Link/__tests__/__snapshots__/index.test.tsx.snap @@ -6,8 +6,7 @@ exports[`link > prominence > render prominence default 1`] = ` data-testid="testing" > Hello @@ -22,8 +21,7 @@ exports[`link > prominence > render prominence strong 1`] = ` data-testid="testing" > Hello @@ -38,8 +36,7 @@ exports[`link > prominence > render prominence stronger 1`] = ` data-testid="testing" > Hello @@ -54,8 +51,7 @@ exports[`link > prominence > render prominence weak 1`] = ` data-testid="testing" > Hello @@ -70,8 +66,7 @@ exports[`link > render correctly prop primary 1`] = ` data-testid="testing" > Hello @@ -86,8 +81,7 @@ exports[`link > render correctly with bad sentiment 1`] = ` data-testid="testing" > Hello @@ -102,8 +96,7 @@ exports[`link > render correctly with href props 1`] = ` data-testid="testing" > render correctly with href props 1`] = ` Hello Hello @@ -146,62 +138,52 @@ exports[`link > render correctly with href props 1`] = ` Hello - - - - OpenInNewIcon - - - - - + + OpenInNewIcon + + + + Hello - - - - OpenInNewIcon - - - - - + + OpenInNewIcon + + + + @@ -213,8 +195,7 @@ exports[`link > render correctly with no sentiment 1`] = ` data-testid="testing" > Hello @@ -232,8 +213,7 @@ exports[`link > render correctly with oneLine 1`] = ` style="margin-bottom: 16px; margin-top: 8px; width: 200px;" > Hello this is a very long text that should be truncated @@ -249,24 +229,21 @@ exports[`link > render correctly with sizes 1`] = ` data-testid="testing" > Hello , Hello , Hello @@ -281,33 +258,28 @@ exports[`link > render correctly with target blank 1`] = ` data-testid="testing" > Hello - - - - OpenInNewIcon - - - - - + + OpenInNewIcon + + + + @@ -319,15 +291,13 @@ exports[`link > render correctly with variants props 1`] = ` data-testid="testing" > Hello Hello @@ -342,8 +312,7 @@ exports[`link > render prop > element form > render correctly with render prop 1 data-testid="testing" > About @@ -358,15 +327,13 @@ exports[`link > render prop > element form > render correctly with render prop a data-testid="testing" > Primary Info @@ -381,22 +348,19 @@ exports[`link > render prop > element form > render correctly with render prop a data-testid="testing" > Large Small XSmall @@ -411,8 +375,7 @@ exports[`link > render prop > function form > render correctly with render funct data-testid="testing" > About @@ -427,8 +390,7 @@ exports[`link > sentiment > render info 1`] = ` data-testid="testing" > Hello @@ -443,8 +405,7 @@ exports[`link > sentiment > render primary 1`] = ` data-testid="testing" > Hello diff --git a/packages/ui/src/components/Link/__tests__/index.test.tsx b/packages/ui/src/components/Link/__tests__/index.test.tsx index 0e07425517..cd32f10639 100644 --- a/packages/ui/src/components/Link/__tests__/index.test.tsx +++ b/packages/ui/src/components/Link/__tests__/index.test.tsx @@ -4,8 +4,7 @@ import { renderWithTheme, shouldMatchSnapshot } from '@utils/test' import { forwardRef } from 'react' import { describe, expect, it, vi } from 'vitest' import { Link } from '..' -import type { ProminenceProps } from '..' -import { PROMINENCES } from '../constants' +import { PROMINENCE_VALUES } from '../constants' // Mock component simulating Next.js Link const MockNextLink = forwardRef & { href: string }>( @@ -31,11 +30,11 @@ describe('link', () => { }) describe('prominence', () => { - it.each(Object.keys(PROMINENCES).map(prominence => [`render prominence ${prominence}`, prominence]))( + it.each(PROMINENCE_VALUES.map(prominence => [`render prominence ${prominence}`, prominence]))( '%s', (_, prominence) => shouldMatchSnapshot( - + Hello , ), @@ -74,18 +73,6 @@ describe('link', () => { , )) - it('render correctly with variants props', () => - shouldMatchSnapshot( - <> - - Hello - - - Hello - - , - )) - it('render correctly with bad sentiment', () => shouldMatchSnapshot( // @ts-expect-error Use a wrong sentiment diff --git a/packages/ui/src/components/Link/constants.ts b/packages/ui/src/components/Link/constants.ts index 89c3fd212b..f85c7a6bad 100644 --- a/packages/ui/src/components/Link/constants.ts +++ b/packages/ui/src/components/Link/constants.ts @@ -1,6 +1,9 @@ -export const PROMINENCES = { +export const PROMINENCE_VALUES = ['default', 'strong', 'stronger', 'weak'] as const +export type ProminenceType = (typeof PROMINENCE_VALUES)[number] + +export const PROMINENCES: Record = { default: '', strong: 'strong', - stronger: 'stronger', - weak: 'weak', -} as const + stronger: 'strong', + weak: '', +} diff --git a/packages/ui/src/components/Link/index.tsx b/packages/ui/src/components/Link/index.tsx index 90c56f886a..ac602e1b22 100644 --- a/packages/ui/src/components/Link/index.tsx +++ b/packages/ui/src/components/Link/index.tsx @@ -5,7 +5,7 @@ import { ArrowRightIcon } from '@ultraviolet/icons/ArrowRightIcon' import { OpenInNewIcon } from '@ultraviolet/icons/OpenInNewIcon' import { cn, renderElement } from '@ultraviolet/utils' import type { RenderProp } from '@ultraviolet/utils' -import { forwardRef, useEffect, useMemo, useRef, useState } from 'react' +import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' import type { AnchorHTMLAttributes, CSSProperties, @@ -14,15 +14,12 @@ import type { KeyboardEventHandler, MouseEventHandler, ReactNode, - RefObject, } from 'react' import recursivelyGetChildrenString from '../../helpers/recursivelyGetChildrenString' import { Tooltip } from '../Tooltip' -import type { PROMINENCES } from './constants' +import type { ProminenceType } from './constants' import { linkStyle } from './styles.css' -export type ProminenceProps = keyof typeof PROMINENCES - type LinkSizes = 'large' | 'small' | 'xsmall' type LinkIconPosition = 'left' | 'right' @@ -30,8 +27,13 @@ type LinkProps = { children: ReactNode target?: HTMLAttributeAnchorTarget download?: string | boolean + /** + * - "primary" is for a link to an internal page + * - "info" is for a link to an external page (which should also have `target="_blank"`) + * @default "info" if target="_blank", "primary" otherwise. + */ sentiment?: 'primary' | 'info' - prominence?: ProminenceProps + prominence?: ProminenceType size?: LinkSizes iconPosition?: LinkIconPosition rel?: AnchorHTMLAttributes['rel'] @@ -44,6 +46,9 @@ type LinkProps = { 'aria-keyshortcuts'?: string oneLine?: boolean 'data-testid'?: string + /** + * @deprecated the "variant" property has been removed. + */ variant?: 'inline' | 'standalone' style?: CSSProperties } & XOR< @@ -84,7 +89,7 @@ export const Link = forwardRef( href, target, download, - sentiment = 'info', + sentiment = target === '_blank' ? 'info' : 'primary', prominence, size = 'large', iconPosition, @@ -97,20 +102,19 @@ export const Link = forwardRef( 'aria-keyshortcuts': ariaKeyshortcuts, oneLine = false, 'data-testid': dataTestId, - variant = 'standalone', style, render, }: LinkProps, ref: ForwardedRef, ) => { const isBlank = target === '_blank' - const computedRel = rel ?? (isBlank ? 'noopener noreferrer' : undefined) const [isTruncated, setIsTruncated] = useState(false) - const elementRef = useRef(null) - const usedRef = (ref as RefObject) ?? elementRef + const elementRef = useRef(null) + useImperativeHandle(ref, () => elementRef.current!) const finalStringChildren = recursivelyGetChildrenString(children) + const textVariant = useMemo(() => { if (size === 'xsmall') { return 'captionStrong' @@ -121,12 +125,13 @@ export const Link = forwardRef( return 'bodyStrong' }, [size]) + useEffect(() => { - if (oneLine && usedRef?.current) { - const { offsetWidth, scrollWidth } = usedRef.current + if (oneLine && elementRef?.current) { + const { offsetWidth, scrollWidth } = elementRef.current setIsTruncated(offsetWidth < scrollWidth) } - }, [oneLine, ref, usedRef]) + }, [oneLine, ref, elementRef]) const computedClassName = cn( className, @@ -134,7 +139,6 @@ export const Link = forwardRef( oneLine, prominence, sentiment, - type: variant, variant: textVariant, }), linkStyle.defaultLink, @@ -147,8 +151,7 @@ export const Link = forwardRef( children, className: computedClassName, 'data-testid': dataTestId, - 'data-variant': variant, - ref: usedRef, + ref: elementRef, style, })} @@ -163,13 +166,12 @@ export const Link = forwardRef( aria-label={ariaLabel} className={computedClassName} data-testid={dataTestId} - data-variant={variant} download={download} href={href} onClick={onClick} onKeyDown={onKeyDown} - ref={usedRef} - rel={computedRel} + ref={elementRef} + rel={rel ?? (isBlank ? 'noopener noreferrer' : undefined)} style={style} target={target} > @@ -179,9 +181,10 @@ export const Link = forwardRef( {children} {isBlank ? ( - - - + ) : null} {!isBlank && iconPosition === 'right' ? ( diff --git a/packages/ui/src/components/Link/styles.css.ts b/packages/ui/src/components/Link/styles.css.ts index f690858d26..88b198e556 100644 --- a/packages/ui/src/components/Link/styles.css.ts +++ b/packages/ui/src/components/Link/styles.css.ts @@ -1,13 +1,12 @@ import { theme } from '@ultraviolet/themes' import { capitalize } from '@ultraviolet/utils' -import { globalStyle, style } from '@vanilla-extract/css' +import { globalStyle, style, styleVariants } from '@vanilla-extract/css' import { recipe } from '@vanilla-extract/recipes' -import { PROMINENCES } from './constants' +import type { ProminenceType } from './constants' +import { PROMINENCE_VALUES, PROMINENCES } from './constants' const TRANSITION_DURATION = 250 -type ProminenceType = keyof typeof PROMINENCES - function getLinkStyle(sentiment: 'primary' | 'info', prominence: ProminenceType) { const definedProminence = capitalize(PROMINENCES[prominence]) const text = `text${definedProminence}` as keyof typeof theme.colors.primary @@ -16,21 +15,9 @@ function getLinkStyle(sentiment: 'primary' | 'info', prominence: ProminenceType) return { color: theme.colors[sentiment][text] ?? theme.colors.neutral.text, selectors: { - '&:hover': { - textDecorationColor: theme.colors[sentiment][textHover], - color: theme.colors[sentiment][textHover], - }, - '&:focus': { - textDecorationColor: theme.colors[sentiment][textHover], + '&:hover, &:focus': { color: theme.colors[sentiment][textHover], }, - '&:active': { - textDecorationThickness: '2px', - }, - '&:visited': { - color: theme.colors.secondary.text, - textDecorationColor: theme.colors.secondary.text, - }, }, } } @@ -42,55 +29,41 @@ function makeVariant(variant: 'captionStrong' | 'bodySmallStrong' | 'bodyStrong' fontWeight: theme.typography[variant].weight, letterSpacing: theme.typography[variant].letterSpacing, lineHeight: theme.typography[variant].lineHeight, - paragraphSpacing: theme.typography[variant].paragraphSpacing, - textCase: theme.typography[variant].textCase, } } const link = recipe({ base: { - backgroundColor: 'none', border: 'none', padding: 0, - textDecoration: 'underline', - textDecorationThickness: 1, - textUnderlineOffset: 2, - textDecorationStyle: 'dotted', - gap: theme.space[1], + textDecoration: 'none', + textDecorationThickness: '1px', + textUnderlineOffset: '3px', position: 'relative', cursor: 'pointer', selectors: { - '&:hover': { - outline: 'none', + '&:hover, &:focus, &:active': { textDecoration: 'underline', - textDecorationThickness: 1, + }, + '&:active': { + textDecorationThickness: '2px', }, }, }, variants: { sentiment: { - primary: { - selectors: { - '&:hover::after': { - backgroundColor: theme.colors.primary.text, - }, - '&:focus::after': { - backgroundColor: theme.colors.primary.text, - }, - }, - }, + primary: {}, info: { + textDecorationLine: 'underline', + textDecorationStyle: 'dotted', selectors: { - '&:hover::after': { - backgroundColor: theme.colors.info.text, - }, - '&:focus::after': { - backgroundColor: theme.colors.info.text, + '&:visited, &:visited:hover, &:visited:focus, &:visited:active': { + color: theme.colors.secondary.text, }, }, }, }, - prominence: Object.fromEntries(Object.keys(PROMINENCES).map(prominence => [prominence, {}])), + prominence: Object.fromEntries(PROMINENCE_VALUES.map(prominence => [prominence, {}])), oneLine: { true: { whiteSpace: 'nowrap', @@ -107,42 +80,40 @@ const link = recipe({ bodySmallStrong: makeVariant('bodySmallStrong'), bodyStrong: makeVariant('bodyStrong'), }, - type: { - inline: { - textDecoration: 'underline', - textDecorationThickness: 1, - }, - standalone: {}, - }, }, - compoundVariants: [ - ...Object.keys(PROMINENCES).map(prominence => ({ + compoundVariants: PROMINENCE_VALUES.flatMap(prominence => [ + { variants: { sentiment: 'primary' as const, - prominence: prominence as ProminenceType, + prominence, }, - style: getLinkStyle('primary', prominence as ProminenceType), - })), - ...Object.keys(PROMINENCES).map(prominence => ({ + style: getLinkStyle('primary', prominence), + }, + { variants: { sentiment: 'info' as const, - prominence: prominence as ProminenceType, + prominence, }, - style: getLinkStyle('info', prominence as ProminenceType), - })), - ], + style: getLinkStyle('info', prominence), + }, + ]), defaultVariants: { prominence: 'default', - sentiment: 'info', oneLine: false, variant: 'bodyStrong', - type: 'standalone', }, }) -const containerIcon = style({ - display: 'inline-flex', - paddingBottom: theme.space['0.5'], +const externalIcon = styleVariants({ + large: { + marginBottom: theme.space['0.5'], + }, + small: { + marginBottom: theme.space['0.25'], + }, + xsmall: { + marginBottom: 0, + }, }) /* Make this to have a global syle which does not depend on props @@ -154,11 +125,8 @@ const iconLeft = style({ marginRight: theme.space['0.5'], transition: `transform ${TRANSITION_DURATION}ms ease-out`, selectors: { - [`${defaultLink}:hover &`]: { - transform: 'translate(0.25rem, 0)', - }, - [`${defaultLink}:focus &`]: { - transform: 'translate(0.25rem, 0)', + [`${defaultLink}:hover &, ${defaultLink}:focus &`]: { + transform: `translate(${theme.space['0.25']}, 0)`, }, }, }) @@ -168,10 +136,7 @@ const iconRight = style({ marginLeft: theme.space['0.5'], transition: `transform ${TRANSITION_DURATION}ms ease-out`, selectors: { - [`${defaultLink}:hover &`]: { - transform: `translate(calc(${theme.space['0.25']}*-1), 0)`, - }, - [`${defaultLink}:focus &`]: { + [`${defaultLink}:hover &, ${defaultLink}:focus &`]: { transform: `translate(calc(${theme.space['0.25']}*-1), 0)`, }, }, @@ -184,7 +149,7 @@ globalStyle(`${defaultLink} > * `, { export const linkStyle = { link, - containerIcon, + externalIcon, defaultLink, iconLeft, iconRight, diff --git a/packages/ui/src/components/Tabs/__tests__/__snapshots__/index.test.tsx.snap b/packages/ui/src/components/Tabs/__tests__/__snapshots__/index.test.tsx.snap index 77e5be23ac..6f272850b1 100644 --- a/packages/ui/src/components/Tabs/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/ui/src/components/Tabs/__tests__/__snapshots__/index.test.tsx.snap @@ -1311,8 +1311,7 @@ exports[`tabs > renders correctly with custom Tabs component 1`] = `
components > renders correctly Toast.Link 1`] = ` data-testid="testing" > Test