Skip to content

Commit 642a7cd

Browse files
authored
<CallToAction as="span"> and type="submit" (#2057)
1 parent f909460 commit 642a7cd

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

.changeset/silent-crabs-protect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@theguild/components': minor
3+
---
4+
5+
<CallToAction as="span"> and type="submit"

packages/components/src/components/call-to-action.stories.ts renamed to packages/components/src/components/call-to-action.stories.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ export default {
1010
children: 'Click me',
1111
onClick: () => alert('Clicked!'),
1212
},
13+
argTypes: {
14+
as: {
15+
control: 'select',
16+
options: [undefined, 'span', 'div'],
17+
},
18+
variant: {
19+
control: 'select',
20+
options: ['primary', 'primary-inverted', 'secondary', 'secondary-inverted', 'tertiary'],
21+
},
22+
},
1323
parameters: {
1424
padding: true,
1525
},
@@ -44,3 +54,38 @@ export const Tertiary: StoryObj<CallToActionProps> = {
4454
variant: 'tertiary',
4555
},
4656
};
57+
58+
export const AsSpan: StoryObj<CallToActionProps> = {
59+
args: {
60+
as: 'span',
61+
children: 'Show More',
62+
onClick: () => {
63+
// no alert
64+
},
65+
},
66+
decorators: [
67+
(Story: React.FC) => (
68+
<details>
69+
<summary className="list-none pb-4">
70+
<Story />
71+
</summary>
72+
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</p>
73+
</details>
74+
),
75+
],
76+
};
77+
78+
export const Link: StoryObj<CallToActionProps> = {
79+
args: {
80+
href: 'https://the-guild.dev/graphql/hive/ecosystem',
81+
children: 'Explore the Ecosystem',
82+
variant: 'secondary-inverted',
83+
},
84+
};
85+
86+
export const Submit: StoryObj<CallToActionProps> = {
87+
args: {
88+
type: 'submit',
89+
children: 'Submit',
90+
},
91+
};

packages/components/src/components/call-to-action.tsx

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { HTMLAttributes } from 'react';
12
import { cn } from '../cn';
23
import { Anchor } from './anchor';
34

@@ -29,21 +30,38 @@ export declare namespace CallToActionProps {
2930

3031
export interface AnchorProps extends BaseProps, React.ComponentPropsWithoutRef<typeof Anchor> {
3132
href: string;
33+
as?: never;
3234
}
3335

3436
export interface ButtonProps
3537
extends BaseProps,
3638
React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
3739
href?: never;
38-
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
40+
as?: never;
41+
}
42+
43+
/**
44+
* Use inside `<summary>` or as visual part of bigger interactive element.
45+
* Prefer using `href` prop using CallToAction as `<button>` in other cases.
46+
*/
47+
export interface NonInteractiveProps
48+
extends BaseProps,
49+
React.DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> {
50+
href?: never;
51+
as: 'span' | 'div';
3952
}
4053
}
4154

42-
export type CallToActionProps = CallToActionProps.AnchorProps | CallToActionProps.ButtonProps;
55+
export type CallToActionProps =
56+
| CallToActionProps.AnchorProps
57+
| CallToActionProps.ButtonProps
58+
| CallToActionProps.NonInteractiveProps;
4359

4460
/**
4561
* This is called `Button` in Figma in the new Hive brand design system.
46-
* It's a styled variant of {@link Anchor}.
62+
* It's a styled variant of {@link Anchor}. Based on the presence of the
63+
* `href` prop or `as` prop, it renders a `button`, `a` or `props.as`
64+
* (for non-interactive elements) element.
4765
*
4866
* // TODO: Consider renaming it to `Button`.
4967
*/
@@ -54,22 +72,38 @@ export function CallToAction(props: CallToActionProps) {
5472
props.className,
5573
);
5674

75+
const growingBorderBox = (
76+
<span className="absolute inset-0 rounded-lg border border-green-800 dark:border-neutral-200" />
77+
);
78+
5779
if ('href' in props && typeof props.href === 'string') {
5880
const { className: _1, variant: _2, children, ...rest } = props;
5981

6082
return (
6183
<Anchor className={className} {...rest}>
62-
<div className="absolute inset-0 rounded-lg border border-green-800 dark:border-neutral-200" />
84+
{growingBorderBox}
6385
{children}
6486
</Anchor>
6587
);
6688
}
6789

90+
if (props.as) {
91+
const { className: _1, variant: _2, children, as, ...rest } = props;
92+
// for this use case HTMLElement is enough, we don't need to use the more specific HTMLDivElement
93+
const Root = as as 'span';
94+
return (
95+
<Root className={className} {...rest}>
96+
{growingBorderBox}
97+
{children}
98+
</Root>
99+
);
100+
}
101+
68102
const { className: _1, variant: _2, children, ...rest } = props;
69103

70104
return (
71105
<button className={className} {...rest}>
72-
<div className="absolute inset-0 rounded-lg border border-green-800 dark:border-neutral-200" />
106+
{growingBorderBox}
73107
{children}
74108
</button>
75109
);

0 commit comments

Comments
 (0)