refactor(ui): add surface prop types and validation to defineComponent
Adds shared prop types for surface-aware components and type-level validation to ensure proper surface prop declarations. - Add `LeafSurfaceProps` and `ContainerSurfaceProps` shared types - Add `SurfacePropsConstraint` type that errors if `surface: 'leaf'` is set but `onSurface` is missing from props (and vice versa for container components) - Update Button to extend `LeafSurfaceProps` instead of declaring `onSurface` manually Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
@@ -16,21 +16,15 @@
|
||||
|
||||
import type { ReactElement, ReactNode, CSSProperties } from 'react';
|
||||
import type { ButtonProps as RAButtonProps } from 'react-aria-components';
|
||||
import type { Responsive, Surface } from '../../types';
|
||||
import type { LeafSurfaceProps, Responsive } from '../../types';
|
||||
|
||||
/**
|
||||
* Button's own properties (excluding inherited React Aria props)
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ButtonOwnProps = {
|
||||
/** @public */
|
||||
export type ButtonOwnProps = LeafSurfaceProps & {
|
||||
size?: Responsive<'small' | 'medium' | 'large'>;
|
||||
variant?: Responsive<'primary' | 'secondary' | 'tertiary'>;
|
||||
iconStart?: ReactElement;
|
||||
iconEnd?: ReactElement;
|
||||
loading?: boolean;
|
||||
/** Surface the button is placed on. Defaults to context surface if available */
|
||||
onSurface?: Responsive<Surface>;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ComponentConfig } from './types';
|
||||
import type { ComponentConfig, SurfacePropsConstraint } from './types';
|
||||
|
||||
export function defineComponent<P extends Record<string, any>>() {
|
||||
return <
|
||||
const S extends Record<string, string>,
|
||||
const C extends ComponentConfig<P, S>,
|
||||
>(
|
||||
config: C,
|
||||
config: C & SurfacePropsConstraint<P, C['surface']>,
|
||||
): C => config;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,25 @@ export interface ComponentConfig<
|
||||
surface?: 'container' | 'leaf';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type constraint that validates surface props are present in the props type.
|
||||
* - If surface is 'leaf', P must include 'onSurface'
|
||||
* - If surface is 'container', P must include 'surface'
|
||||
*/
|
||||
export type SurfacePropsConstraint<P, Surface> = Surface extends 'leaf'
|
||||
? 'onSurface' extends keyof P
|
||||
? {}
|
||||
: {
|
||||
__error: 'Leaf components must include onSurface in props type. Extend LeafProps.';
|
||||
}
|
||||
: Surface extends 'container'
|
||||
? 'surface' extends keyof P
|
||||
? {}
|
||||
: {
|
||||
__error: 'Container components must include surface in props type. Extend ContainerProps.';
|
||||
}
|
||||
: {};
|
||||
|
||||
export interface UseDefinitionOptions<D extends ComponentConfig<any, any>> {
|
||||
utilityTarget?: keyof D['classNames'] | null;
|
||||
classNameTarget?: keyof D['classNames'] | null;
|
||||
|
||||
@@ -191,3 +191,13 @@ export type Surface =
|
||||
| 'warning'
|
||||
| 'success'
|
||||
| 'auto';
|
||||
|
||||
/** @public */
|
||||
export interface LeafSurfaceProps {
|
||||
onSurface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ContainerSurfaceProps {
|
||||
surface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user