refactor(ui): enforce required children on bg provider component types
Strengthens BgPropsConstraint to also check that children is present and non-optional in the OwnProps type of bg provider components. This ensures children can only flow through childrenWithBgProvider, preventing silent bypasses of the bg context system. Box and Accordion OwnProps updated to comply — children is now required. BoxProps updated to Omit children from React.HTMLAttributes to avoid an incompatible duplicate property declaration. Storybook stories updated accordingly. Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
@@ -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.
|
||||
@@ -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<ProviderBg>;
|
||||
children?: ReactNode;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
@@ -370,7 +370,7 @@ export interface BoxProps
|
||||
extends SpaceProps,
|
||||
BoxOwnProps,
|
||||
BoxUtilityProps,
|
||||
React.HTMLAttributes<HTMLDivElement> {}
|
||||
Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {}
|
||||
|
||||
// @public (undocumented)
|
||||
export type BoxUtilityProps = {
|
||||
|
||||
@@ -29,7 +29,7 @@ import type { ProviderBg } from '../../types';
|
||||
*/
|
||||
export type AccordionOwnProps = {
|
||||
bg?: ProviderBg;
|
||||
children?: ReactNode;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 => (
|
||||
<Flex direction="column" align="center">
|
||||
<Flex>
|
||||
@@ -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 => (
|
||||
<Flex align="center" style={{ flexWrap: 'wrap' }}>
|
||||
<Box {...args}>Default</Box>
|
||||
@@ -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 => (
|
||||
<Box {...args} bg="neutral-1">
|
||||
<Button variant="secondary">Button (on neutral-1)</Button>
|
||||
|
||||
@@ -21,7 +21,7 @@ import type { Responsive, ProviderBg, SpaceProps } from '../../types';
|
||||
export type BoxOwnProps = {
|
||||
as?: keyof JSX.IntrinsicElements;
|
||||
bg?: Responsive<ProviderBg>;
|
||||
children?: ReactNode;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
@@ -45,4 +45,4 @@ export interface BoxProps
|
||||
extends SpaceProps,
|
||||
BoxOwnProps,
|
||||
BoxUtilityProps,
|
||||
React.HTMLAttributes<HTMLDivElement> {}
|
||||
Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ const DecorativeBox = ({
|
||||
fontWeight: 'bold',
|
||||
color: '#2563eb',
|
||||
}}
|
||||
children={null}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan="2">
|
||||
|
||||
@@ -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<P, Bg> = Bg extends 'provider'
|
||||
? 'bg' extends keyof P
|
||||
? {}
|
||||
? 'children' extends keyof P
|
||||
? {} extends Pick<P, 'children'>
|
||||
? {
|
||||
__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.';
|
||||
}
|
||||
: {};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user