Skip to content

Commit f4a4823

Browse files
authored
feat(ui-components): add Signature and FunctionSignature components (#8667)
* feat(ui-components): add Signature and FunctionSignature components * fix: title overflow and mobile return icon alignment * chore: patch version * fix: static heading tag can cause semantic problems * chore: unnecessary fragment removed * fix: wrong naming on signature stories * chore: unused classnames removed * chore: try to make Vercel happy * Update index.stories.tsx
1 parent cac1af3 commit f4a4823

File tree

11 files changed

+568
-1
lines changed

11 files changed

+568
-1
lines changed

packages/ui-components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@node-core/ui-components",
3-
"version": "1.6.0",
3+
"version": "1.6.1",
44
"type": "module",
55
"exports": {
66
"./*": {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
@reference "../../../styles/index.css";
2+
3+
.header {
4+
@apply flex
5+
items-start
6+
gap-1;
7+
}
8+
9+
.attribute {
10+
@apply font-ibm-plex-mono
11+
inline-flex
12+
flex-wrap
13+
items-center
14+
gap-1
15+
text-sm
16+
font-semibold;
17+
18+
.longName {
19+
@apply break-all
20+
sm:break-keep;
21+
}
22+
23+
&.return {
24+
@apply font-open-sans
25+
shrink-0;
26+
27+
svg {
28+
@apply size-4;
29+
}
30+
}
31+
}
32+
33+
.type {
34+
@apply font-ibm-plex-mono
35+
inline-flex
36+
flex-wrap
37+
gap-0.5
38+
text-sm
39+
break-all;
40+
41+
a {
42+
@apply text-green-700
43+
dark:text-green-400;
44+
}
45+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { ArrowTurnDownLeftIcon } from '@heroicons/react/24/outline';
2+
import classNames from 'classnames';
3+
4+
import type Signature from '#ui/Common/Signature';
5+
import type { ComponentProps, FC } from 'react';
6+
7+
import styles from './index.module.css';
8+
9+
type SignatureHeaderProps = { isReturn?: boolean } & Omit<
10+
ComponentProps<typeof Signature>,
11+
'title' | 'description'
12+
>;
13+
14+
const SignatureHeader: FC<SignatureHeaderProps> = ({
15+
name,
16+
type,
17+
optional,
18+
isReturn = false,
19+
}) => (
20+
<div className={styles.header}>
21+
{name && (
22+
<span
23+
className={classNames(styles.attribute, {
24+
[styles.return]: isReturn,
25+
})}
26+
>
27+
{isReturn && <ArrowTurnDownLeftIcon />}
28+
<span
29+
className={classNames({
30+
[styles.longName]: name.length > 16,
31+
})}
32+
>
33+
{name}:
34+
{optional && (
35+
<span
36+
role="img"
37+
aria-label="Optional"
38+
data-tooltip="Optional"
39+
tabIndex={0}
40+
>
41+
?
42+
</span>
43+
)}
44+
</span>
45+
</span>
46+
)}
47+
{type && <span className={styles.type}>{type}</span>}
48+
</div>
49+
);
50+
51+
export default SignatureHeader;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
@reference "../../../styles/index.css";
2+
3+
.item {
4+
@apply flex
5+
flex-col
6+
gap-1;
7+
}
8+
9+
.return {
10+
@apply rounded-sm
11+
bg-green-100
12+
px-4
13+
py-3
14+
dark:bg-neutral-900/40;
15+
}
16+
17+
.children {
18+
@apply relative
19+
flex
20+
flex-col
21+
rounded-sm
22+
border
23+
border-neutral-200
24+
dark:border-neutral-900;
25+
26+
&:has(> .return:only-child) {
27+
@apply border-0;
28+
}
29+
30+
&:not(:has(.return:only-child)) .return {
31+
@apply mx-4
32+
mb-3;
33+
}
34+
35+
.item:not(.return) {
36+
@apply mx-4
37+
py-3;
38+
}
39+
40+
.item:not(:last-child, :has(+ .return)) {
41+
@apply border-b
42+
border-neutral-200
43+
dark:border-neutral-900;
44+
}
45+
}
46+
47+
.description {
48+
@apply text-sm
49+
break-all;
50+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import classNames from 'classnames';
2+
3+
import SignatureHeader from '#ui/Common/Signature/SignatureHeader';
4+
5+
import type Signature from '#ui/Common/Signature';
6+
import type { ComponentProps, FC, PropsWithChildren } from 'react';
7+
8+
import styles from './index.module.css';
9+
10+
type SignatureItemProps = Omit<ComponentProps<typeof Signature>, 'title'>;
11+
12+
const SignatureItem: FC<PropsWithChildren<SignatureItemProps>> = ({
13+
kind = 'default',
14+
name,
15+
type,
16+
description,
17+
optional,
18+
children,
19+
}) => (
20+
<div
21+
className={classNames(styles.item, {
22+
[styles.return]: kind === 'return',
23+
})}
24+
>
25+
<SignatureHeader
26+
name={name}
27+
type={type}
28+
optional={optional}
29+
isReturn={kind === 'return'}
30+
/>
31+
{description && <div className={styles.description}>{description}</div>}
32+
{children && <div className={styles.children}>{children}</div>}
33+
</div>
34+
);
35+
36+
export default SignatureItem;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@reference "../../../styles/index.css";
2+
3+
.container {
4+
@apply flex
5+
flex-col
6+
gap-3;
7+
}
8+
9+
.title {
10+
@apply text-base
11+
font-semibold;
12+
}
13+
14+
.root {
15+
@apply flex
16+
flex-col
17+
gap-4
18+
rounded-sm
19+
border
20+
border-neutral-200
21+
px-4
22+
py-3
23+
dark:border-neutral-900;
24+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useId } from 'react';
2+
3+
import type Signature from '#ui/Common/Signature';
4+
import type { ComponentProps, FC, PropsWithChildren } from 'react';
5+
6+
import styles from './index.module.css';
7+
8+
type SignatureRootProps = Pick<ComponentProps<typeof Signature>, 'title'>;
9+
10+
const SignatureRoot: FC<PropsWithChildren<SignatureRootProps>> = ({
11+
title,
12+
children,
13+
}) => {
14+
const titleId = useId();
15+
16+
return (
17+
<section className={styles.container} aria-labelledby={titleId}>
18+
<div className={styles.title} id={titleId}>
19+
{title}
20+
</div>
21+
<div className={styles.root}>{children}</div>
22+
</section>
23+
);
24+
};
25+
26+
export default SignatureRoot;
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import Signature from '#ui/Common/Signature';
2+
3+
import type { Meta as MetaObj, StoryObj } from '@storybook/react-webpack5';
4+
5+
type Story = StoryObj<typeof Signature>;
6+
type Meta = MetaObj<typeof Signature>;
7+
8+
export const Default: Story = {
9+
args: {
10+
title: 'Attributes',
11+
children: (
12+
<>
13+
<Signature
14+
name="attribute1"
15+
type={
16+
<>
17+
<a href="#">&lt;Type1&gt;</a>|<a href="#">&lt;Type2&gt;</a>
18+
</>
19+
}
20+
/>
21+
<Signature
22+
name="attribute2"
23+
optional
24+
type={<a href="#">&lt;Object&gt;</a>}
25+
description="An optional attribute."
26+
>
27+
<Signature name="option1" type={<a href="#">&lt;Type1&gt;</a>} />
28+
<Signature name="option2" type={<a href="#">&lt;Type2&gt;</a>} />
29+
<Signature
30+
name="option3"
31+
type={<a href="#">&lt;Type3&gt;</a>}
32+
description="One of the available options."
33+
/>
34+
</Signature>
35+
<Signature
36+
name="Returns"
37+
type={<a href="#">&lt;Type4&gt;</a>}
38+
description="Returns the result of the function."
39+
kind="return"
40+
/>
41+
</>
42+
),
43+
},
44+
};
45+
46+
export const WithLongAttributeNames: Story = {
47+
args: {
48+
title: 'Attributes',
49+
children: (
50+
<>
51+
<Signature
52+
name="thisIsAnAttributeWithAnExcessivelyLongNameToTestTextWrapping"
53+
type={
54+
<>
55+
<a href="#">&lt;Type1&gt;</a>|<a href="#">&lt;Type3&gt;</a>
56+
</>
57+
}
58+
/>
59+
</>
60+
),
61+
},
62+
};
63+
64+
export const WithLongTypeAndAttributeNames: Story = {
65+
args: {
66+
title: 'Attributes',
67+
children: (
68+
<>
69+
<Signature
70+
name="attribute1"
71+
type={
72+
<>
73+
<a href="#">
74+
&lt;ThisIsATypeWithAnExcessivelyLongNameToTestTextWrapping&gt;
75+
</a>
76+
</>
77+
}
78+
/>
79+
</>
80+
),
81+
},
82+
};
83+
84+
export const OptionalAttribute: Story = {
85+
args: {
86+
title: 'Attributes',
87+
children: (
88+
<Signature
89+
name="optionalAttribute"
90+
optional
91+
type={<a href="#">&lt;Object&gt;</a>}
92+
description="An optional attribute."
93+
/>
94+
),
95+
},
96+
};
97+
98+
export default {
99+
component: Signature,
100+
} as Meta;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import SignatureItem from '#ui/Common/Signature/SignatureItem';
2+
import SignatureRoot from '#ui/Common/Signature/SignatureRoot';
3+
4+
import type { FC, PropsWithChildren, ReactNode } from 'react';
5+
6+
export type SignatureProps = {
7+
title?: string;
8+
kind?: 'default' | 'return';
9+
name?: string;
10+
type?: ReactNode;
11+
description?: ReactNode;
12+
optional?: boolean;
13+
};
14+
15+
const Signature: FC<PropsWithChildren<SignatureProps>> = ({
16+
kind = 'default',
17+
name,
18+
type,
19+
description,
20+
optional,
21+
title,
22+
children,
23+
}) => {
24+
if (title) {
25+
return <SignatureRoot title={title}>{children}</SignatureRoot>;
26+
}
27+
28+
return (
29+
<SignatureItem
30+
kind={kind}
31+
name={name}
32+
type={type}
33+
description={description}
34+
optional={optional}
35+
>
36+
{children}
37+
</SignatureItem>
38+
);
39+
};
40+
41+
export default Signature;

0 commit comments

Comments
 (0)