From afe82bf57cbdbe8127c52dd4e749d870bbc6f7c2 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 13 Mar 2025 10:38:44 +0000 Subject: [PATCH] Add Link component Signed-off-by: Charles de Dreuille --- .../src/components/Link/Link.stories.tsx | 92 +++++++++++++++++++ packages/canon/src/components/Link/Link.tsx | 55 +++++++++++ packages/canon/src/components/Link/index.ts | 18 ++++ packages/canon/src/components/Link/styles.css | 65 +++++++++++++ packages/canon/src/components/Link/types.ts | 33 +++++++ packages/canon/src/css/components.css | 2 +- 6 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 packages/canon/src/components/Link/Link.stories.tsx create mode 100644 packages/canon/src/components/Link/Link.tsx create mode 100644 packages/canon/src/components/Link/index.ts create mode 100644 packages/canon/src/components/Link/styles.css create mode 100644 packages/canon/src/components/Link/types.ts diff --git a/packages/canon/src/components/Link/Link.stories.tsx b/packages/canon/src/components/Link/Link.stories.tsx new file mode 100644 index 0000000000..38e71c310a --- /dev/null +++ b/packages/canon/src/components/Link/Link.stories.tsx @@ -0,0 +1,92 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Link } from './Link'; +import { Flex } from '../Flex'; +import { Text } from '../Text'; + +const meta = { + title: 'Components/Link', + component: Link, + args: { + children: 'Link', + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + href: 'https://canon.backstage.io', + children: 'Sign up for Backstage', + }, +}; + +export const AllVariants: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + + + ), +}; + +export const AllWeights: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + ), +}; + +export const Responsive: Story = { + args: { + variant: { + xs: 'label', + md: 'body', + }, + }, +}; + +export const Playground: Story = { + args: { + ...Default.args, + }, + render: args => ( + + Subtitle + + Body + + Caption + + Label + + + ), +}; diff --git a/packages/canon/src/components/Link/Link.tsx b/packages/canon/src/components/Link/Link.tsx new file mode 100644 index 0000000000..8763575bf9 --- /dev/null +++ b/packages/canon/src/components/Link/Link.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { forwardRef } from 'react'; +import { useResponsiveValue } from '../../hooks/useResponsiveValue'; +import clsx from 'clsx'; + +import type { LinkProps } from './types'; + +/** @public */ +export const Link = forwardRef((props, ref) => { + const { + children, + variant = 'body', + weight = 'regular', + style, + className, + ...restProps + } = props; + + // Get the responsive values for the variant and weight + const responsiveVariant = useResponsiveValue(variant); + const responsiveWeight = useResponsiveValue(weight); + + return ( + + {children} + + ); +}); + +Link.displayName = 'Link'; diff --git a/packages/canon/src/components/Link/index.ts b/packages/canon/src/components/Link/index.ts new file mode 100644 index 0000000000..1b38e89d62 --- /dev/null +++ b/packages/canon/src/components/Link/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { Link } from './Link'; +export type { LinkProps } from './types'; diff --git a/packages/canon/src/components/Link/styles.css b/packages/canon/src/components/Link/styles.css new file mode 100644 index 0000000000..40a4f340ca --- /dev/null +++ b/packages/canon/src/components/Link/styles.css @@ -0,0 +1,65 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.canon-Link { + font-family: var(--canon-font-regular); + color: var(--canon-fg-link); + padding: 0; + margin: 0; + cursor: pointer; + text-decoration-line: none; + + &:hover { + color: var(--canon-fg-link-hover); + text-decoration-line: underline; + text-decoration-style: solid; + text-decoration-thickness: min(2px, max(1px, 0.05em)); + text-underline-offset: calc(0.025em + 2px); + text-decoration-color: color-mix( + in srgb, + var(--canon-fg-link-hover) 30%, + transparent + ); + } +} + +.canon-Link--variant-body { + font-size: var(--canon-font-size-3); + line-height: 140%; +} + +.canon-Link--variant-subtitle { + font-size: var(--canon-font-size-4); + line-height: 140%; +} + +.canon-Link--variant-caption { + font-size: var(--canon-font-size-2); + line-height: 140%; +} + +.canon-Link--variant-label { + font-size: var(--canon-font-size-1); + line-height: 140%; +} + +.canon-Link--weight-regular { + font-weight: var(--canon-font-weight-regular); +} + +.canon-Link--weight-bold { + font-weight: var(--canon-font-weight-bold); +} diff --git a/packages/canon/src/components/Link/types.ts b/packages/canon/src/components/Link/types.ts new file mode 100644 index 0000000000..4c5b34e14e --- /dev/null +++ b/packages/canon/src/components/Link/types.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { CSSProperties, ReactNode } from 'react'; +import type { Breakpoint } from '../../types'; + +/** @public */ +export interface LinkProps + extends React.AnchorHTMLAttributes { + children: ReactNode; + variant?: + | 'subtitle' + | 'body' + | 'caption' + | 'label' + | Partial>; + weight?: 'regular' | 'bold' | Partial>; + className?: string; + style?: CSSProperties; +} diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 1abee6eb5d..d7d99bbe91 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -14,7 +14,6 @@ * limitations under the License. */ -/* Components */ @import '../components/Box/styles.css'; @import '../components/Button/styles.css'; @import '../components/Flex/styles.css'; @@ -27,3 +26,4 @@ @import '../components/Heading/styles.css'; @import '../components/Input/Input.styles.css'; @import '../components/Field/Field.styles.css'; +@import '../components/Link/styles.css';