✨(react) render Button as link
For a variety of reasons, such as accessibility or integration with external react-router deps style we needed to be able to provide the ability to render the Button component as link in order to be able to provide link-specific attribute for rendering such as href.
This commit is contained in:
5
.changeset/breezy-llamas-fail.md
Normal file
5
.changeset/breezy-llamas-fail.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@openfun/cunningham-react": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
render Button as link
|
||||||
@@ -4,7 +4,9 @@
|
|||||||
border: thin solid transparent;
|
border: thin solid transparent;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
|
// When button is rendered as link.
|
||||||
|
text-decoration: none;
|
||||||
font-weight: var(--c--components--button--font-weight);
|
font-weight: var(--c--components--button--font-weight);
|
||||||
font-family: var(--c--components--button--font-family);
|
font-family: var(--c--components--button--font-family);
|
||||||
transition: all var(--c--theme--transitions--duration) var(--c--theme--transitions--ease-out);
|
transition: all var(--c--theme--transitions--duration) var(--c--theme--transitions--ease-out);
|
||||||
|
|||||||
@@ -65,6 +65,21 @@ describe("<Button/>", () => {
|
|||||||
expect(handleClick).not.toHaveBeenCalled();
|
expect(handleClick).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders as link when href is used", () => {
|
||||||
|
render(
|
||||||
|
<Button
|
||||||
|
href="https://www.fun-mooc.fr/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Open link
|
||||||
|
</Button>,
|
||||||
|
);
|
||||||
|
const button = screen.getByRole("link", { name: "Open link" });
|
||||||
|
expect(button).toHaveAttribute("target", "_blank");
|
||||||
|
expect(button).toHaveAttribute("rel", "noopener noreferrer");
|
||||||
|
});
|
||||||
|
|
||||||
it("uses custom token", async () => {
|
it("uses custom token", async () => {
|
||||||
await buildTheme();
|
await buildTheme();
|
||||||
const tokens = await loadTokens();
|
const tokens = await loadTokens();
|
||||||
|
|||||||
@@ -103,3 +103,14 @@ export const IconOnly: Story = {
|
|||||||
color: "primary",
|
color: "primary",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AsLink: Story = {
|
||||||
|
args: {
|
||||||
|
children: "Go to fun-mooc.fr",
|
||||||
|
icon: <span className="material-icons">link</span>,
|
||||||
|
color: "primary",
|
||||||
|
href: "https://www.fun-mooc.fr/",
|
||||||
|
target: "_blank",
|
||||||
|
rel: "noopener noreferrer",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import React, { ButtonHTMLAttributes, forwardRef, ReactNode } from "react";
|
import React, {
|
||||||
|
AnchorHTMLAttributes,
|
||||||
|
ButtonHTMLAttributes,
|
||||||
|
createElement,
|
||||||
|
forwardRef,
|
||||||
|
ReactNode,
|
||||||
|
} from "react";
|
||||||
|
|
||||||
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> &
|
||||||
color?: "primary" | "secondary" | "tertiary" | "danger";
|
AnchorHTMLAttributes<HTMLAnchorElement> & {
|
||||||
size?: "medium" | "small" | "nano";
|
color?: "primary" | "secondary" | "tertiary" | "danger";
|
||||||
icon?: ReactNode;
|
size?: "medium" | "small" | "nano";
|
||||||
iconPosition?: "left" | "right";
|
icon?: ReactNode;
|
||||||
active?: boolean;
|
iconPosition?: "left" | "right";
|
||||||
fullWidth?: boolean;
|
active?: boolean;
|
||||||
}
|
fullWidth?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
export type ButtonElement = HTMLButtonElement & HTMLAnchorElement;
|
||||||
|
|
||||||
|
export const Button = forwardRef<ButtonElement, ButtonProps>(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
children,
|
children,
|
||||||
@@ -43,13 +52,19 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|||||||
classes.push("c__button--full-width");
|
classes.push("c__button--full-width");
|
||||||
}
|
}
|
||||||
const iconElement = <span className="c__button__icon">{icon}</span>;
|
const iconElement = <span className="c__button__icon">{icon}</span>;
|
||||||
// const iconElement = icon;
|
const tagName = props.href ? "a" : "button";
|
||||||
return (
|
return createElement(
|
||||||
<button className={classes.join(" ")} {...props} ref={ref}>
|
tagName,
|
||||||
|
{
|
||||||
|
className: classes.join(" "),
|
||||||
|
...props,
|
||||||
|
ref,
|
||||||
|
},
|
||||||
|
<>
|
||||||
{!!icon && iconPosition === "left" && iconElement}
|
{!!icon && iconPosition === "left" && iconElement}
|
||||||
{children}
|
{children}
|
||||||
{!!icon && iconPosition === "right" && iconElement}
|
{!!icon && iconPosition === "right" && iconElement}
|
||||||
</button>
|
</>,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
isToday,
|
isToday,
|
||||||
} from "@internationalized/date";
|
} from "@internationalized/date";
|
||||||
import { CalendarState, RangeCalendarState } from "@react-stately/calendar";
|
import { CalendarState, RangeCalendarState } from "@react-stately/calendar";
|
||||||
import { Button } from ":/components/Button";
|
import { Button, ButtonElement } from ":/components/Button";
|
||||||
|
|
||||||
interface CalendarCellProps {
|
interface CalendarCellProps {
|
||||||
state: CalendarState | RangeCalendarState;
|
state: CalendarState | RangeCalendarState;
|
||||||
@@ -20,7 +20,7 @@ const isRangeCalendar = (object: any): object is RangeCalendarState => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CalendarCell = ({ state, date }: CalendarCellProps) => {
|
export const CalendarCell = ({ state, date }: CalendarCellProps) => {
|
||||||
const ref = useRef<HTMLButtonElement>(null);
|
const ref = useRef<ButtonElement>(null);
|
||||||
const {
|
const {
|
||||||
cellProps,
|
cellProps,
|
||||||
buttonProps,
|
buttonProps,
|
||||||
|
|||||||
Reference in New Issue
Block a user