diff --git a/.changeset/hip-queens-divide.md b/.changeset/hip-queens-divide.md new file mode 100644 index 00000000000..8852b3688fe --- /dev/null +++ b/.changeset/hip-queens-divide.md @@ -0,0 +1,7 @@ +--- +'@kaizen/components': minor +--- + +Add LinkButton component + +- Adds LinkButton component, stories and documentation to actions group diff --git a/packages/components/src/LinkButton/LinkButton.module.css b/packages/components/src/LinkButton/LinkButton.module.css new file mode 100644 index 00000000000..3fbad5c386a --- /dev/null +++ b/packages/components/src/LinkButton/LinkButton.module.css @@ -0,0 +1,4 @@ +.linkButton { + /* Reset */ + text-decoration: inherit; +} diff --git a/packages/components/src/LinkButton/LinkButton.tsx b/packages/components/src/LinkButton/LinkButton.tsx new file mode 100644 index 00000000000..25e2af42317 --- /dev/null +++ b/packages/components/src/LinkButton/LinkButton.tsx @@ -0,0 +1,71 @@ +import React, { forwardRef } from 'react' +import { Link as RACLink, type LinkProps as RACLinkProps } from 'react-aria-components' +import { type ButtonUIProps } from '~components/__rc__/Button' +import buttonStyles from '~components/__rc__/Button/Button.module.css' +import { ButtonContent } from '~components/__rc__/Button/subcomponents' +import { useReversedColors } from '~components/__utilities__/v3' +import { mergeClassNames } from '~components/utils/mergeClassNames' +import styles from './LinkButton.module.css' + +export type LinkButtonProps = ButtonUIProps & + Omit & { + /** Used as the label for the LinkButton. */ + children: RACLinkProps['children'] + } + +export const LinkButton = forwardRef( + ( + { + children, + variant = 'primary', + size = 'medium', + icon, + iconPosition = 'start', + hasHiddenLabel = false, + isFullWidth = false, + isDisabled, + className, + isReversed, + ...otherProps + }: LinkButtonProps, + ref: React.ForwardedRef, + ) => { + const shouldUseReverse = useReversedColors() + const isReversedVariant = isReversed ?? shouldUseReverse + + return ( + + {(racStateProps) => { + const childIsFunction = typeof children === 'function' + + return ( + + {childIsFunction ? children(racStateProps) : children} + + ) + }} + + ) + }, +) + +LinkButton.displayName = 'LinkButton' diff --git a/packages/components/src/LinkButton/_docs/LinkButton--api-specification.mdx b/packages/components/src/LinkButton/_docs/LinkButton--api-specification.mdx new file mode 100644 index 00000000000..caa54c5e586 --- /dev/null +++ b/packages/components/src/LinkButton/_docs/LinkButton--api-specification.mdx @@ -0,0 +1,281 @@ +import { Canvas, Meta, Controls, ArgTypes, DocsStory } from '@storybook/blocks' +import { ResourceLinks, KAIOInstallation, LinkTo } from '~storybook/components' +import * as exampleStories from './LinkButton.doc.stories' + + + +# LinkButton API Specification + +Updated Dec 18, 2024 + + + + + +## Overview + +`LinkButton` allows users to navigate to another page or resource. It shares the same visual styles and interaction states as the Button component, but is intended for navigational purposes and downloading documents. + +The following example and table showcases the essential props that enable the core functionality of `LinkButton`. For the remaining suite of API options refer to [this section](#additional-api-options). + + + + + +## API + +This is built on top of [React Aria's Link component](https://react-spectrum.adobe.com/react-aria/Link.html) and is the counterpart to the Kaizen Button, handling icons, variants and sizes in the same way. It provides a semantic wrapper for navigational buttons and allows for native `href` navigation and client side routing with [additional configuration](#client-side-routing). + +### Navigation and native anchor attributes + +Out of the box, the `LinkButton` offers majority of the native behavior and functionality on the `anchor` tag. `href` will trigger new page loads, `download` will download the referenced document, and `target` can be used to open links in new tabs or windows. + + + +While client side routing is possible, the `LinkButton` is agnostic to the routing technology chosen. Refer to our general set up guide to get started with [client side routing](#client-side-routing). + +#### Opening new tabs and accessibility considerations + +The general recommendation is to limit the number of links that open a new tab or window on a single page. While there are valid scenarios that can help avoid loss of data and or progress, as with links in forms, opening new tabs can be disorienting for users - especially for those who have difficulty perceiving visual content. + +In order to provide advance warning to all users, it is recommended that links using `target="_blank"` be accompanied by a visual indicator and audible warning. As shown in the following example, additional context can be provided via a visually hidden element within the `children` of the component. + + + +For more context on this recommendation, we recommend taking a look at the [W3C page on the G200 success criteria](https://www.w3.org/TR/WCAG20-TECHS/G200.html). + +### Variants + +`LinkButton` supports the following variants: `primary`, `secondary` and `tertiary`. If the `variant` prop is not specified, the default will be `primary`. + + + +Reversed variants are handled via the `ReversedColors` Provider. + + + +To enable the reversed theme, you will need to wrap the component or application in the `ReversedColors` provider, ie: + +```tsx +import { RouterProvider } from 'react-aria-components' +import { Button } from '@kaizen/components/v3/actions' +import { ReversedColors } from '@kaizen/components/v3/utilities' +// application code + +return ( + + + +) +``` + +### Sizes + +LinkButton supports the following sizes: `small`, `medium` and `large`. If the `size` prop is not specified, the default will be `medium`. + + + +### `onPress` + +As with Button, `LinkButton`'s API uses React Aria's `onPress` instead of `onClick`. Functionally this does not change the way we pass click events to a `LinkButton`. Consumers can safely replace `onClick` with `onPress` without any additional changes, ie: + +```tsx +