diff --git a/src/components/Link/Link.less b/src/components/Link/Link.less deleted file mode 100644 index 41e0a28c..00000000 --- a/src/components/Link/Link.less +++ /dev/null @@ -1,9 +0,0 @@ -@import (reference) '/src/assets/styles/_shared.less'; - -.a-link__icon-after-text svg { - margin-left: 0.25em; -} - -.a-link__icon-before-text svg { - margin-right: 0.25em; -} diff --git a/src/components/Link/Link.stories.tsx b/src/components/Link/Link.stories.tsx index 03a68b7e..90f718a5 100644 --- a/src/components/Link/Link.stories.tsx +++ b/src/components/Link/Link.stories.tsx @@ -5,106 +5,135 @@ import { Link, LinkText, List, - ListLink as ListLinkComponent + ListLink } from '~/src/index'; const meta: Meta = { title: 'Components (Draft)/Links', - component: Link, - parameters: { - docs: { - description: { - component: ` -### CFPB DS Link component - -https://cfpb.github.io/design-system/components/links -` - } - } - } + component: Link }; export default meta; type Story = StoryObj; -export const Default: Story = { +const DefaultArguments = { args: { href: '#', - children: Link Text + children: 'Link Text' } }; -export const ListLink: Story = { +export const Inline: Story = { + name: 'Inline links', + render: () => ( +

+ Here's the default link style. For reference, + here's the{' '} + + hover link style + + . Train your eyes on the{' '} + + focused link style + + . Jump to the{' '} + + active link style + + . We've all been to the{' '} + + visited link style + + . +

+ ) +}; + +export const CallToAction: Story = { + name: 'Call-to-action links', args: { - ...Default.args + ...DefaultArguments.args }, render: arguments_ => ( - + Sample call-to-action link + Another sample call-to-action link ) }; export const Destructive: Story = { + name: 'Destructive links', args: { - ...Default.args + ...DefaultArguments.args, + children: 'Sample destructive link' }, render: arguments_ => }; -export const WithIcon: Story = { +export const StandardLinkWithIcon: Story = { + name: 'Standard link with icon', args: { - ...Default.args, - hasIcon: true, - type: 'default' + ...DefaultArguments.args, + hasIcon: true }, render: arguments_ => ( - - Download file - + <> + The document icon should emphasize a link that contains a{' '} + + file or document + + . Use the external link icon to emphasize{' '} + + a non-CFPB webpage + {' '} + for further details. + ) }; -export const WithIconNoWrapping: Story = { +export const StandardLinkWithIconNoWrapping: Story = { + name: 'Non-wrapping icon links', args: { - ...WithIcon.args, + ...StandardLinkWithIcon.args, noWrap: true }, render: arguments_ => ( - - - Lorem ipsum dolor, sit amet consectetur adipisicing elit. Obcaecati - incidunt explicabo, odio delectus quia magnam non . teeeeext - - - - + <> + The document icon should emphasize a link that contains a{' '} + + file or document + + . + ) }; export const JumpLink: Story = { + name: 'Jump link', args: { - ...Default.args, + ...DefaultArguments.args, isJump: true }, render: arguments_ => ( - Jump link + Default jump link  ) }; export const JumpLinkIconLeft: Story = { + name: 'Jump link with icon on left', args: { - ...Default.args, + ...DefaultArguments.args, isJumpLeft: true }, render: arguments_ => ( - Jump link +  Jump link with icon on left ) }; diff --git a/src/components/Link/Link.test.tsx b/src/components/Link/Link.test.tsx new file mode 100644 index 00000000..a372d316 --- /dev/null +++ b/src/components/Link/Link.test.tsx @@ -0,0 +1,101 @@ +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/react'; +import { DestructiveLink, Link, LinkText, ListLink } from '~/src/index'; + +describe('', () => { + const linkBaseProperties = { + href: '/#', + 'data-testid': 'link-test-id' + }; + + const testId = linkBaseProperties['data-testid']; + + it('Type: "default"', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('a-link'); + expect(link).toHaveAttribute('href', '/#'); + }); + + it('Type: "destructive"', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('a-link a-btn a-btn__link a-btn__warning'); + }); + + it('Type: "list"', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('m-list_link'); + expect(link).not.toHaveClass('a-link'); + }); + + it('Option: noWrap - it adds classnames', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('a-link__no-wrap'); + }); + + it('Option: isJump - it adds classnames', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('a-link__jump a-link__icon-after-text'); + }); + + it('Option: isJumpLeft - it adds classnames', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('a-link__jump a-link__icon-before-text'); + }); + + it('Option: hasIcon - it adds classnames', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveClass('a-link a-link__icon'); + }); + + it('Other: propagates other attributes', () => { + render(); + const link = screen.getByTestId(testId); + expect(link).toHaveAttribute('target', '_blank'); + }); +}); + +describe('', () => { + it('includes appropriate classnames', () => { + render(Test text); + const linkText = screen.getByTestId('link-text'); + expect(linkText).toHaveClass('a-link_text'); + expect(linkText).toHaveTextContent('Test text'); + }); +}); + +describe('', () => { + const testId = 'list-link'; + + it('includes all expected elements', () => { + render(Test text); + // ListItem + const listItem = screen.getByRole('listitem'); + expect(listItem).toBeInTheDocument(); + expect(listItem).toHaveTextContent('Test text'); + + // Link + const linkText = screen.getByTestId(testId); + expect(linkText).toHaveClass('m-list_link'); + expect(linkText).toHaveTextContent('Test text'); + }); +}); + +describe('', () => { + const testId = 'destructive-link'; + + it('includes all expected elements', () => { + render(Test text); + const linkDestructive = screen.getByTestId(testId); + expect(linkDestructive).toHaveClass( + 'a-link a-btn a-btn__link a-btn__warning' + ); + expect(linkDestructive).toHaveTextContent('Test text'); + }); +}); diff --git a/src/components/Link/Link.tsx b/src/components/Link/Link.tsx index 690aea5f..84e58872 100644 --- a/src/components/Link/Link.tsx +++ b/src/components/Link/Link.tsx @@ -1,7 +1,6 @@ import classnames from 'classnames'; import type { JSXElement } from '../../types/jsxElement'; import ListItem from '../List/ListItem'; -import './Link.less'; interface LinkProperties extends React.HTMLProps { type?: 'default' | 'destructive' | 'list'; @@ -11,6 +10,11 @@ interface LinkProperties extends React.HTMLProps { isJumpLeft?: boolean; } +/** + * Links lead users to a different page or further information. In contrast, buttons are used to signal actions. Users should be able to identify links without relying on color or styling alone. + * + * Source: https://cfpb.github.io/design-system/components/links + */ export default function Link({ children, type = 'default',