diff --git a/.changeset/rude-groups-shout.md b/.changeset/rude-groups-shout.md new file mode 100644 index 0000000000..59e4a22aa9 --- /dev/null +++ b/.changeset/rude-groups-shout.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Improved type safety in `useDefinition` by centralizing prop resolution and strengthening the `BgPropsConstraint` to require that `bg` provider components declare `children` as a required prop in their OwnProps type. diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index 87cea3d387..5922155e93 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -118,7 +118,7 @@ export interface AccordionGroupProps // @public export type AccordionOwnProps = { bg?: ProviderBg; - children?: ReactNode; + children: ReactNode; className?: string; }; @@ -360,7 +360,7 @@ export const BoxDefinition: { export type BoxOwnProps = { as?: keyof JSX.IntrinsicElements; bg?: Responsive; - children?: ReactNode; + children: ReactNode; className?: string; style?: CSSProperties; }; @@ -370,7 +370,7 @@ export interface BoxProps extends SpaceProps, BoxOwnProps, BoxUtilityProps, - React.HTMLAttributes {} + Omit, 'children'> {} // @public (undocumented) export type BoxUtilityProps = { diff --git a/packages/ui/src/components/Accordion/types.ts b/packages/ui/src/components/Accordion/types.ts index 8895b79086..39ce1ab5be 100644 --- a/packages/ui/src/components/Accordion/types.ts +++ b/packages/ui/src/components/Accordion/types.ts @@ -29,7 +29,7 @@ import type { ProviderBg } from '../../types'; */ export type AccordionOwnProps = { bg?: ProviderBg; - children?: ReactNode; + children: ReactNode; className?: string; }; diff --git a/packages/ui/src/components/Box/Box.stories.tsx b/packages/ui/src/components/Box/Box.stories.tsx index f5bc8e5b23..5d60d99872 100644 --- a/packages/ui/src/components/Box/Box.stories.tsx +++ b/packages/ui/src/components/Box/Box.stories.tsx @@ -60,6 +60,7 @@ export const Default = meta.story({ fontWeight: 'bold', color: '#2563eb', }, + children: null, }, }); @@ -326,6 +327,7 @@ const CardDisplay = ({ children }: { children?: ReactNode }) => { }; export const Display = meta.story({ + args: { children: null }, render: args => ( @@ -347,7 +349,7 @@ export const Display = meta.story({ }); export const BackgroundColors = meta.story({ - args: { px: '6', py: '4' }, + args: { px: '6', py: '4', children: null }, render: args => ( Default @@ -377,7 +379,7 @@ export const BackgroundColors = meta.story({ }); export const NestedNeutralColors = meta.story({ - args: { px: '6', py: '4' }, + args: { px: '6', py: '4', children: null }, render: args => ( diff --git a/packages/ui/src/components/Box/types.ts b/packages/ui/src/components/Box/types.ts index f7641d2f27..1b4259099b 100644 --- a/packages/ui/src/components/Box/types.ts +++ b/packages/ui/src/components/Box/types.ts @@ -21,7 +21,7 @@ import type { Responsive, ProviderBg, SpaceProps } from '../../types'; export type BoxOwnProps = { as?: keyof JSX.IntrinsicElements; bg?: Responsive; - children?: ReactNode; + children: ReactNode; className?: string; style?: CSSProperties; }; @@ -45,4 +45,4 @@ export interface BoxProps extends SpaceProps, BoxOwnProps, BoxUtilityProps, - React.HTMLAttributes {} + Omit, 'children'> {} diff --git a/packages/ui/src/components/Container/Container.stories.tsx b/packages/ui/src/components/Container/Container.stories.tsx index 44ff6f2d17..e6fbee091b 100644 --- a/packages/ui/src/components/Container/Container.stories.tsx +++ b/packages/ui/src/components/Container/Container.stories.tsx @@ -43,6 +43,7 @@ const DecorativeBox = () => ( backgroundImage: 'url("data:image/svg+xml,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22%232563eb%22%20fill-opacity%3D%220.3%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M5%200h1L0%206V5zM6%205v1H5z%22/%3E%3C/g%3E%3C/svg%3E")', }} + children={null} /> ); diff --git a/packages/ui/src/components/Flex/Flex.stories.tsx b/packages/ui/src/components/Flex/Flex.stories.tsx index 8b3b7af220..ff2aa442ea 100644 --- a/packages/ui/src/components/Flex/Flex.stories.tsx +++ b/packages/ui/src/components/Flex/Flex.stories.tsx @@ -70,6 +70,7 @@ const DecorativeBox = ({ fontWeight: 'bold', color: '#2563eb', }} + children={null} /> ); }; diff --git a/packages/ui/src/components/Grid/Grid.stories.tsx b/packages/ui/src/components/Grid/Grid.stories.tsx index d5b83d257d..b3b6957359 100644 --- a/packages/ui/src/components/Grid/Grid.stories.tsx +++ b/packages/ui/src/components/Grid/Grid.stories.tsx @@ -36,6 +36,7 @@ const FakeBox = () => ( backgroundImage: 'url("data:image/svg+xml,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22%232563eb%22%20fill-opacity%3D%220.3%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M5%200h1L0%206V5zM6%205v1H5z%22/%3E%3C/g%3E%3C/svg%3E")', }} + children={null} /> ); @@ -94,6 +95,7 @@ export const RowAndColumns = meta.story({ backgroundImage: 'url("data:image/svg+xml,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22%232563eb%22%20fill-opacity%3D%220.3%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M5%200h1L0%206V5zM6%205v1H5z%22/%3E%3C/g%3E%3C/svg%3E")', }} + children={null} /> diff --git a/packages/ui/src/hooks/useDefinition/types.ts b/packages/ui/src/hooks/useDefinition/types.ts index 45ce5e3dc8..2ddc0c88c5 100644 --- a/packages/ui/src/hooks/useDefinition/types.ts +++ b/packages/ui/src/hooks/useDefinition/types.ts @@ -47,14 +47,22 @@ export interface ComponentConfig< /** * Type constraint that validates bg props are present in the props type. - * - Provider components must include 'bg' in their props + * - Provider components must include 'bg' in their props and 'children' in propDefs * - Consumer components don't need a bg prop */ export type BgPropsConstraint = Bg extends 'provider' ? 'bg' extends keyof P - ? {} + ? 'children' extends keyof P + ? {} extends Pick + ? { + __error: 'Bg provider components cannot have children as optional.'; + } + : {} + : { + __error: 'Bg provider components must include children in own props type.'; + } : { - __error: 'Bg provider components must include bg in props type.'; + __error: 'Bg provider components must include bg in own props type.'; } : {};