Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/common/components/Link/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ export interface LinkProps extends RouterLinkProps, PropsWithTestId {}
/**
* The `Link` React component formats and renders an `<a />` anchor HTML element using
* the `Link` component from React Router.
* @param {LinkProps} props - Component properties, `LinkProps`.
* @returns {JSX.Element} JSX
* @see {@link LinkProps}
* @see {@link https://reactrouter.com/en/main/components/link | Link}
*/
const Link = ({ children, className, testId = 'link', ...props }: LinkProps): JSX.Element => {
return (
<RouterLink
className={cn('text-blue-600 hover:underline hover:opacity-75 dark:text-blue-400', className)}
className={cn(
'font-medium underline decoration-sky-500 decoration-1 underline-offset-3 hover:decoration-2 dark:decoration-sky-400',
className,
)}
data-testid={testId}
{...props}
>
Expand Down
2 changes: 1 addition & 1 deletion src/common/components/Link/__stories__/Link.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const Icon: Story = {
export const Styled: Story = {
args: {
children: 'Styled link',
className: 'hover:no-underline text-sm text-red-500 font-bold',
className: 'decoration-rose-400 text-rose-500 text-2xl',
to: '/',
},
};
5 changes: 5 additions & 0 deletions src/common/components/Router/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const HelpTextComponents = lazy(() => import('pages/Components/components/HelpTe
const IconComponents = lazy(() => import('pages/Components/components/IconComponents'));
const InputComponents = lazy(() => import('pages/Components/components/InputComponents'));
const LabelComponents = lazy(() => import('pages/Components/components/LabelComponents'));
const LinkComponents = lazy(() => import('pages/Components/components/LinkComponents'));
const PageComponents = lazy(() => import('pages/Components/components/PageComponents'));
const PopoverComponents = lazy(() => import('pages/Components/components/PopoverComponents'));
const SearchInputComponents = lazy(
Expand Down Expand Up @@ -168,6 +169,10 @@ export const routes: RouteObject[] = [
path: 'label',
element: withSuspense(<LabelComponents />),
},
{
path: 'link',
element: withSuspense(<LinkComponents />),
},
{
path: 'page',
element: withSuspense(<PageComponents />),
Expand Down
3 changes: 3 additions & 0 deletions src/pages/Components/ComponentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ const ComponentsPage = (): JSX.Element => {
<MenuNavLink to="label" styleActive>
Label
</MenuNavLink>
<MenuNavLink to="link" styleActive>
Link
</MenuNavLink>
<MenuNavLink to="page" styleActive>
Page
</MenuNavLink>
Expand Down
135 changes: 135 additions & 0 deletions src/pages/Components/components/LinkComponents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { ColumnDef, createColumnHelper } from '@tanstack/react-table';

import { BaseComponentProps } from 'common/utils/types';
import { ComponentProperty } from '../model/components';
import Table from 'common/components/Table/Table';
import CodeSnippet from 'common/components/Text/CodeSnippet';
import Heading from 'common/components/Text/Heading';
import Link from 'common/components/Link/Link';

/**
* The `LinkComponents` component renders a set of examples illustrating
* the use of the `Link` component.
*/
const LinkComponents = ({
className,
testId = 'components-link',
}: BaseComponentProps): JSX.Element => {
const data: ComponentProperty[] = [
{
name: 'className',
description: 'Optional. Additional CSS class names.',
},
{
name: 'testId',
description: 'Optional. Identifier for testing.',
},
{
name: 'to',
description: 'The URL to link to. This can be a string or an object.',
},
];
const columnHelper = createColumnHelper<ComponentProperty>();
const columns = [
columnHelper.accessor('name', {
cell: (info) => (
<span className="font-mono text-sky-700 dark:text-sky-500">{info.getValue()}</span>
),
header: () => 'Name',
}),
columnHelper.accessor('description', {
cell: (info) => info.renderValue(),
header: () => 'Description',
}),
] as ColumnDef<ComponentProperty>[];

return (
<section className={className} data-testid={testId}>
<Heading level={2} className="mb-4">
Link Component
</Heading>

<div className="my-8">
<div className="mb-4">
The <span className="font-mono font-bold">Link</span> component is a wrapper around the
React Router Link component. It provides a way to navigate between different routes in
your application without causing a full page reload. The Link component accepts all the
same props as the React Router Link component, as well as some additional props for
styling and testing.
</div>

<div className="my-8">
<Heading level={3} className="mb-2">
Properties
</Heading>
<Table<ComponentProperty> data={data} columns={columns} />
</div>

<Heading level={3} className="mb-2">
Examples
</Heading>

<Heading level={4} className="my-2">
Basic
</Heading>
<div className="mb-4 opacity-85">
This is a basic example of the <span className="font-mono font-bold">Link</span>{' '}
component. It renders a HTML anchor element that links to the specified URL. The Link is
styled with accessibility in mind.
</div>
<div className="my-8">
<div className="mb-2 flex flex-col place-content-center rounded-sm border border-neutral-500/10 p-4 dark:bg-neutral-700/25">
{/* Example */}
<div>
To learn more about using the Link component view the{' '}
<Link to="/pub/components/link">Link</Link> examples page.
</div>
</div>
<CodeSnippet
className="my-2"
code={`<div>
To learn more about using the Link component view the{' '}
<Link to="/pub/components/link">Link</Link> examples page.
</div>`}
/>
</div>

<Heading level={4} className="my-2">
Router Link Properties
</Heading>
<div className="mb-4 opacity-85">
The Link component accepts all the same props as the React Router Link component. This
includes the <span className="font-mono font-bold">to</span> prop, which specifies the URL
to link to. Or the <span className="font-mono font-bold">target</span> prop, which
specifies where to open the linked document.
</div>
<div className="my-8">
<div className="mb-2 flex flex-col place-content-center rounded-sm border border-neutral-500/10 p-4 dark:bg-neutral-700/25">
{/* Example */}
<div>
Open an external link in a new tab. To view the official React Router documentation,
refer to the{' '}
<Link to="https://reactrouter.com/api/components/Link" target="_blank">
Link component guide
</Link>
.
</div>
</div>
<CodeSnippet
className="my-2"
code={`<div>
Open an external link in a new tab. To view the official React Router documentation,
refer to the{' '}
<Link to="https://reactrouter.com/api/components/Link" target="_blank">
Link component guide
</Link>
.
</div>`}
/>
</div>
</div>
</section>
);
};

export default LinkComponents;
16 changes: 16 additions & 0 deletions src/pages/Components/components/__tests__/LinkComponents.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, expect, it } from 'vitest';

import { render, screen } from 'test/test-utils';

import LinkComponents from '../LinkComponents';

describe('LinkComponents', () => {
it('should render successfully', async () => {
// ARRANGE
render(<LinkComponents />);
await screen.findByTestId('components-link');

// ASSERT
expect(screen.getByTestId('components-link')).toBeInTheDocument();
});
});