Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@codegouvfr/react-dsfr",
"version": "1.23.9",
"version": "1.23.10-rc.2",
"description": "French State Design System React integration library",
"repository": {
"type": "git",
Expand Down
89 changes: 65 additions & 24 deletions src/Tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ type DataAttribute = Record<`data-${string}`, string | boolean | null | undefine

export type TagProps = TagProps.Common &
(TagProps.WithIcon | TagProps.WithoutIcon) &
(TagProps.AsAnchor | TagProps.AsButton | TagProps.AsParagraph);
(TagProps.AsAnchor | TagProps.AsButton | TagProps.AsParagraph | TagProps.AsSpan);
export namespace TagProps {
export type HTMLElement =
| HTMLButtonElement
| HTMLAnchorElement
| HTMLParagraphElement
| HTMLSpanElement;

export type Common = {
id?: string;
className?: string;
Expand All @@ -32,6 +38,7 @@ export namespace TagProps {
style?: CSSProperties;
title?: string;
children: ReactNode;
as?: "p" | "span" | "button" | "a";
};

export type WithIcon = {
Expand All @@ -44,18 +51,20 @@ export namespace TagProps {
};

export type AsAnchor = {
as?: "a";
linkProps: RegisteredLinkProps;
onClick?: never;
nativeButtonProps?: never;
/** @deprecated Tag is now <p> by default. Use `nativeParagraphProps` instead. */
/** @deprecated Tag is now `<p>` by default. Use `nativeParagraphProps` instead. */
nativeSpanProps?: never;
nativeParagraphProps?: never;
dismissible?: never;
pressed?: never;
};
export type AsButton = {
as?: "button";
linkProps?: never;
/** @deprecated Tag is now <p> by default. Use `nativeParagraphProps` instead. */
/** @deprecated Tag is now `<p>` by default. Use `nativeParagraphProps` instead. */
nativeSpanProps?: never;
nativeParagraphProps?: never;
/** Default: false */
Expand All @@ -65,23 +74,32 @@ export namespace TagProps {
nativeButtonProps?: ComponentProps<"button"> & DataAttribute;
};
export type AsParagraph = {
as?: "p";
linkProps?: never;
onClick?: never;
dismissible?: never;
pressed?: never;
nativeButtonProps?: never;
/** @deprecated Tag is now <p> by default. Use `nativeParagraphProps` instead. */
/** @deprecated Tag is now `<p>` by default. Use `nativeParagraphProps` instead. */
nativeSpanProps?: ComponentProps<"span"> & DataAttribute;
nativeParagraphProps?: ComponentProps<"p"> & DataAttribute;
};

/** @deprecated Tag is now <p> by default. Use `AsParagraph` instead. */
export type AsSpan = AsParagraph;
export type AsSpan = {
as: "span";
linkProps?: never;
onClick?: never;
dismissible?: never;
pressed?: never;
nativeButtonProps?: never;
nativeSpanProps?: ComponentProps<"span"> & DataAttribute;
nativeParagraphProps?: never;
};
}

/** @see <https://components.react-dsfr.codegouv.studio/?path=/docs/components-tag> */
export const Tag = memo(
forwardRef<HTMLButtonElement | HTMLAnchorElement | HTMLParagraphElement, TagProps>(
forwardRef<TagProps.HTMLElement, TagProps>(
// -- (lint hack) to keep same indent as before
(props, ref) => {
const {
id: id_props,
Expand All @@ -98,6 +116,7 @@ export const Tag = memo(
nativeSpanProps,
style,
onClick,
as: AsTag = "p",
...rest
} = props;

Expand All @@ -122,6 +141,7 @@ export const Tag = memo(
prop_className
);

// to support old usage
const nativeParagraphOrSpanProps = nativeParagraphProps ?? nativeSpanProps;

return (
Expand Down Expand Up @@ -161,22 +181,42 @@ export const Tag = memo(
{children}
</button>
)}
{linkProps === undefined && nativeButtonProps === undefined && (
<p
{...nativeParagraphOrSpanProps}
id={id_props ?? nativeParagraphOrSpanProps?.id ?? id}
className={cx(nativeParagraphOrSpanProps?.className, className)}
style={{
...nativeParagraphOrSpanProps?.style,
...style
}}
title={title ?? nativeParagraphOrSpanProps?.title}
ref={ref as React.ForwardedRef<HTMLParagraphElement>}
{...rest}
>
{children}
</p>
)}
{linkProps === undefined &&
nativeButtonProps === undefined &&
AsTag === "p" && (
<p
{...nativeParagraphOrSpanProps}
id={id_props ?? nativeParagraphOrSpanProps?.id ?? id}
className={cx(nativeParagraphOrSpanProps?.className, className)}
style={{
...nativeParagraphOrSpanProps?.style,
...style
}}
title={title ?? nativeParagraphOrSpanProps?.title}
ref={ref as React.ForwardedRef<HTMLParagraphElement>}
{...rest}
>
{children}
</p>
)}
{linkProps === undefined &&
nativeButtonProps === undefined &&
AsTag === "span" && (
<span
{...nativeParagraphOrSpanProps}
id={id_props ?? nativeParagraphOrSpanProps?.id ?? id}
className={cx(nativeParagraphOrSpanProps?.className, className)}
style={{
...nativeParagraphOrSpanProps?.style,
...style
}}
title={title ?? nativeParagraphOrSpanProps?.title}
ref={ref as React.ForwardedRef<HTMLSpanElement>}
{...rest}
>
{children}
</span>
)}
</>
);
}
Expand All @@ -189,6 +229,7 @@ export const Tag = memo(
| (TagProps.AsAnchor & RefAttributes<HTMLAnchorElement>)
| (TagProps.AsButton & RefAttributes<HTMLButtonElement>)
| (TagProps.AsParagraph & RefAttributes<HTMLParagraphElement>)
| (TagProps.AsSpan & RefAttributes<HTMLParagraphElement>)
)
>
>;
Expand Down
18 changes: 18 additions & 0 deletions stories/Tag.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ const { meta, getStory } = getStoryFactory({
Example: \`{ "aria-controls": "fr-modal-1", onMouseEnter: event => {...} }\``,
"control": { "type": null }
},

"as": {
"options": (() => {
const options = ["p", "span", "button", "a", undefined] as const;

assert<Equals<typeof options[number], TagProps["as"]>>();

return options;
})(),
"control": { type: "select", labels: { null: "default p element" } },
"description":
"You can specify a 'span' element instead of default 'p' if the badge is inside a `<p>`. 'button' and 'a' are implicit."
},
"children": {
"description": "The label of the button",
"control": { "type": "string" }
Expand Down Expand Up @@ -107,3 +120,8 @@ export const TagPressed = getStory({
onClick: () => console.log("click")
}
});

export const AsSpan = getStory({
"children": "Label button",
as: "span"
});