From 16d424276a4cfde6e1929c0ab9e040acca2f1239 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Wed, 4 Mar 2026 20:24:12 +0000 Subject: [PATCH] Improve types Signed-off-by: Charles de Dreuille --- docs-ui/src/app/components/card/page.mdx | 2 +- .../app/components/card/props-definition.ts | 2 +- packages/ui/report.api.md | 31 ++++++++----- .../ui/src/components/Card/Card.module.css | 1 + packages/ui/src/components/Card/Card.tsx | 4 +- packages/ui/src/components/Card/types.ts | 45 ++++++++++++------- 6 files changed, 53 insertions(+), 32 deletions(-) diff --git a/docs-ui/src/app/components/card/page.mdx b/docs-ui/src/app/components/card/page.mdx index df4d531604..8df6eccb40 100644 --- a/docs-ui/src/app/components/card/page.mdx +++ b/docs-ui/src/app/components/card/page.mdx @@ -110,7 +110,7 @@ Pass `onPress` and a `label` (used as the accessible name for screen readers) to ### Link -Pass `href` to make the card surface navigate to a URL. The `label` prop is optional but recommended for accessibility when the card content alone may not sufficiently describe the destination. +Pass `href` to make the card surface navigate to a URL. `label` is required — it provides the accessible name for the invisible overlay link read by screen readers. = { type: 'string', responsive: false, description: - 'Accessible label announced by screen readers for the interactive overlay. Required when onPress is provided.', + 'Accessible label announced by screen readers for the interactive overlay. Required when onPress or href is provided.', }, ...classNamePropDefs, ...stylePropDefs, diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index 31617a36bb..7e8c8833ee 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -603,9 +603,12 @@ export interface CardBodyProps // @public (undocumented) export type CardButtonVariant = { - onPress: () => void; + onPress: NonNullable; href?: never; label: string; + target?: never; + rel?: never; + download?: never; }; // @public @@ -695,20 +698,23 @@ export type CardLinkVariant = { onPress?: never; label: string; target?: string; -}; - -// @public -export type CardOwnProps = { - children?: ReactNode; - className?: string; - onPress?: () => void; - href?: string; - label?: string; - target?: string; rel?: string; download?: boolean | string; }; +// @public +export type CardOwnProps = Pick< + CardBaseProps & (CardButtonVariant | CardLinkVariant | CardStaticVariant), + | 'children' + | 'className' + | 'onPress' + | 'href' + | 'label' + | 'target' + | 'rel' + | 'download' +>; + // @public export type CardProps = CardBaseProps & Omit, 'onPress'> & @@ -719,6 +725,9 @@ export type CardStaticVariant = { onPress?: never; href?: never; label?: never; + target?: never; + rel?: never; + download?: never; }; // @public (undocumented) diff --git a/packages/ui/src/components/Card/Card.module.css b/packages/ui/src/components/Card/Card.module.css index 978731344d..f0aaa6831f 100644 --- a/packages/ui/src/components/Card/Card.module.css +++ b/packages/ui/src/components/Card/Card.module.css @@ -51,6 +51,7 @@ */ .bui-Card[data-interactive] { cursor: pointer; + overflow: hidden; &::after { content: ''; diff --git a/packages/ui/src/components/Card/Card.tsx b/packages/ui/src/components/Card/Card.tsx index b948e19df9..2527a3a46b 100644 --- a/packages/ui/src/components/Card/Card.tsx +++ b/packages/ui/src/components/Card/Card.tsx @@ -24,7 +24,6 @@ import { CardFooterDefinition, } from './definition'; import type { - CardOwnProps, CardProps, CardHeaderProps, CardBodyProps, @@ -40,8 +39,7 @@ import { Box } from '../Box/Box'; export const Card = forwardRef((props, ref) => { const { ownProps, restProps, dataAttributes } = useDefinition( CardDefinition, - props as CardOwnProps & - Omit, 'onPress'>, + props, ); const { classes, children, onPress, href, label, target, rel, download } = ownProps; diff --git a/packages/ui/src/components/Card/types.ts b/packages/ui/src/components/Card/types.ts index f70f4b7c3b..f1e91055e9 100644 --- a/packages/ui/src/components/Card/types.ts +++ b/packages/ui/src/components/Card/types.ts @@ -15,21 +15,7 @@ */ import type { ReactNode } from 'react'; - -/** - * Flat own-props shape used by the component definition system. - * @public - */ -export type CardOwnProps = { - children?: ReactNode; - className?: string; - onPress?: () => void; - href?: string; - label?: string; - target?: string; - rel?: string; - download?: boolean | string; -}; +import type { ButtonProps as RAButtonProps } from 'react-aria-components'; /** @public */ export type CardBaseProps = { children?: ReactNode; className?: string }; @@ -37,10 +23,13 @@ export type CardBaseProps = { children?: ReactNode; className?: string }; /** @public */ export type CardButtonVariant = { /** Handler called when the card is pressed. Makes the card interactive as a button. */ - onPress: () => void; + onPress: NonNullable; href?: never; /** Accessible label announced by screen readers for the interactive card. */ label: string; + target?: never; + rel?: never; + download?: never; }; /** @public */ @@ -52,6 +41,10 @@ export type CardLinkVariant = { label: string; /** Specifies where to open the linked URL (e.g. `_blank` for a new tab). */ target?: string; + /** Relationship between the current document and the linked URL (e.g. `noopener`). */ + rel?: string; + /** Prompts the user to save the linked URL. Pass `true` for default filename or a string for a custom filename. */ + download?: boolean | string; }; /** @public */ @@ -59,6 +52,9 @@ export type CardStaticVariant = { onPress?: never; href?: never; label?: never; + target?: never; + rel?: never; + download?: never; }; /** @@ -70,6 +66,23 @@ export type CardProps = CardBaseProps & Omit, 'onPress'> & (CardButtonVariant | CardLinkVariant | CardStaticVariant); +/** + * Flat own-props shape used by the component definition system. + * Derived from the Card variant types so it automatically stays in sync with CardProps. + * @public + */ +export type CardOwnProps = Pick< + CardBaseProps & (CardButtonVariant | CardLinkVariant | CardStaticVariant), + | 'children' + | 'className' + | 'onPress' + | 'href' + | 'label' + | 'target' + | 'rel' + | 'download' +>; + /** @public */ export type CardHeaderOwnProps = { children?: ReactNode;