add stories and fix some edge cases with HandledLink

This commit is contained in:
ChaosExAnima 2025-10-02 17:13:24 +02:00
parent e53f58835f
commit 11fe92a5c6
No known key found for this signature in database
GPG Key ID: 8F2B333100FB6117
2 changed files with 83 additions and 14 deletions

View File

@ -0,0 +1,65 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { HashtagMenuController } from '@/mastodon/features/ui/components/hashtag_menu_controller';
import { accountFactoryState } from '@/testing/factories';
import { HoverCardController } from '../hover_card_controller';
import type { HandledLinkProps } from './handled_link';
import { HandledLink } from './handled_link';
const meta = {
title: 'Components/Status/HandledLink',
render(args) {
return (
<>
<HandledLink {...args} mentionAccountId='1' hashtagAccountId='1' />
<HashtagMenuController />
<HoverCardController />
</>
);
},
args: {
href: 'https://example.com/path/subpath?query=1#hash',
text: 'https://example.com',
},
parameters: {
state: {
accounts: {
'1': accountFactoryState(),
},
},
},
} satisfies Meta<Pick<HandledLinkProps, 'href' | 'text'>>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
export const Hashtag: Story = {
args: {
text: '#example',
},
};
export const Mention: Story = {
args: {
text: '@user',
},
};
export const InternalLink: Story = {
args: {
href: '/about',
text: 'About',
},
};
export const InvalidURL: Story = {
args: {
href: 'ht!tp://invalid-url',
text: 'ht!tp://invalid-url -- invalid!',
},
};

View File

@ -1,9 +1,8 @@
import { useId } from 'react';
import type { ComponentProps, FC } from 'react';
import { Link } from 'react-router-dom';
interface HandledLinkProps {
export interface HandledLinkProps {
href: string;
text: string;
hashtagAccountId?: string;
@ -15,9 +14,9 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
text,
hashtagAccountId,
mentionAccountId,
key,
...props
}) => {
const id = useId();
// Handle hashtags
if (text.startsWith('#')) {
const hashtag = text.slice(1).trim();
@ -28,7 +27,7 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
to={`/tags/${hashtag}`}
rel='tag'
data-menu-hashtag={hashtagAccountId}
key={id}
key={key}
>
#<span>{hashtag}</span>
</Link>
@ -39,20 +38,29 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
return (
<Link
{...props}
className='mention'
to={`/@${mention}`}
title={`@${mention}`}
data-hover-card-account={mentionAccountId}
key={id}
key={key}
>
@<span>{mention}</span>
</Link>
);
}
// Non-absolute paths treated as internal links.
if (href.startsWith('/')) {
return text;
return (
<Link {...props} className='unhandled-link' to={href} key={key}>
{text}
</Link>
);
}
try {
const url = new URL(href);
const [first, ...rest] = url.pathname.split('/').slice(1); // Start at 1 to skip the leading slash.
return (
<a
{...props}
@ -62,15 +70,11 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
target='_blank'
rel='noreferrer noopener'
translate='no'
key={id}
key={key}
>
<span className='invisible'>{url.protocol}</span>
<span className='ellipsis'>
{url.hostname + url.pathname.split('/').slice(0, 1).join('/')}
</span>
<span className='invisible'>
{url.pathname.split('/').slice(1).join('/') + url.search + url.hash}
</span>
<span className='invisible'>{url.protocol + '//'}</span>
<span className='ellipsis'>{`${url.hostname}/${first ?? ''}`}</span>
<span className='invisible'>{'/' + rest.join('/')}</span>
</a>
);
} catch {