Skip to content

Commit

Permalink
fix: Simplify EuiFlex generic types (#7792)
Browse files Browse the repository at this point in the history
  • Loading branch information
tkajtoch authored May 28, 2024
1 parent f2af6f1 commit 1bbf8f3
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 28 deletions.
3 changes: 3 additions & 0 deletions packages/eui/changelogs/upcoming/7792.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Bug fixes**

- Fixed `EuiFlexGroup` and `EuiFlexItem` types to correctly accept global attribute props and simplify type resolution when used with `styled()`-like wrappers
5 changes: 5 additions & 0 deletions packages/eui/scripts/dtsgenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const generator = dtsGenerator({
// 2. replace any import("src/...") declarations to import("@elastic/eui/src/...")
// 3. replace any import("./...") declarations to import("@elastic/eui/src/...)
// 4. generate & add EuiTokenObject
// 5. Fix React.ElementType being incorrectly expanded to React.ElementType<any, keyof React.JSX.IntrinsicElements>
generator.then(() => {
const defsFilePath = path.resolve(baseDir, 'eui.d.ts');

Expand Down Expand Up @@ -155,6 +156,10 @@ generator.then(() => {
}
) // end 3.
.replace(/$/, `\n\n${buildEuiTokensObject()}`) // 4.
.replaceAll(
'React.ElementType<any, keyof React.JSX.IntrinsicElements>',
'React.ElementType'
) // 5.
);
});

Expand Down
26 changes: 13 additions & 13 deletions packages/eui/src/components/flex/flex_group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@

import React, {
ComponentPropsWithoutRef,
ComponentType,
ElementType,
ForwardedRef,
forwardRef,
FunctionComponent,
PropsWithChildren,
ReactElement,
Ref,
} from 'react';
import classNames from 'classnames';
Expand Down Expand Up @@ -51,9 +52,7 @@ export const DIRECTIONS = [
] as const;
type FlexGroupDirection = (typeof DIRECTIONS)[number];

type ComponentPropType = ElementType<CommonProps>;

export type EuiFlexGroupProps<TComponent extends ComponentPropType = 'div'> =
export type EuiFlexGroupProps<TComponent extends ElementType = 'div'> =
PropsWithChildren &
CommonProps &
ComponentPropsWithoutRef<TComponent> & {
Expand Down Expand Up @@ -83,7 +82,7 @@ export type EuiFlexGroupProps<TComponent extends ComponentPropType = 'div'> =
wrap?: boolean;
};

const EuiFlexGroupInternal = <TComponent extends ComponentPropType>(
const EuiFlexGroupInternal = <TComponent extends ElementType>(
{
className,
component = 'div' as TComponent,
Expand All @@ -96,7 +95,7 @@ const EuiFlexGroupInternal = <TComponent extends ComponentPropType>(
...rest
}: EuiFlexGroupProps<TComponent>,
ref: ForwardedRef<TComponent>
) => {
): ReactElement<EuiFlexGroupProps<TComponent>, TComponent> => {
const styles = useEuiMemoizedStyles(euiFlexGroupStyles);
const cssStyles = [
styles.euiFlexGroup,
Expand All @@ -110,23 +109,24 @@ const EuiFlexGroupInternal = <TComponent extends ComponentPropType>(

const classes = classNames('euiFlexGroup', className);

// Cast the resolved component prop type to ComponentType to help TS
// process multiple infers and the overall type complexity.
// This might not be needed in TypeScript 5
const Component = component as ComponentType<CommonProps & typeof rest>;
// Cast `component` to FunctionComponent to simplify its type.
// Note that FunctionComponent type is used here for purely typing
// convenience since we specify the return type above, and function
// components don't support `ref`s, but that doesn't matter in this case.
const Component = component as FunctionComponent<CommonProps & typeof rest>;

return <Component {...rest} ref={ref} className={classes} css={cssStyles} />;
};

// Cast forwardRef return type to work with the generic TComponent type
// and not fallback to implicit any typing
export const EuiFlexGroup = forwardRef(EuiFlexGroupInternal) as (<
TComponent extends ComponentPropType = 'div',
TComponentRef = ReturnType<typeof EuiFlexGroupInternal>
TComponent extends ElementType = 'div',
TComponentRef = ReactElement<any, TComponent>
>(
props: EuiFlexGroupProps<TComponent> & {
ref?: Ref<TComponentRef>;
}
) => ReturnType<typeof EuiFlexGroupInternal>) & { displayName?: string };
) => ReactElement) & { displayName?: string };

EuiFlexGroup.displayName = 'EuiFlexGroup';
30 changes: 15 additions & 15 deletions packages/eui/src/components/flex/flex_item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import React, {
useEffect,
ComponentPropsWithoutRef,
PropsWithChildren,
ComponentType,
ForwardedRef,
forwardRef,
Ref,
ReactElement,
FunctionComponent,
} from 'react';
import classNames from 'classnames';
import { CommonProps } from '../common';
Expand All @@ -39,9 +40,7 @@ const VALID_GROW_VALUES = [
10,
] as const;

type ComponentPropType = ElementType<CommonProps>;

export type EuiFlexItemProps<TComponent extends ComponentPropType = 'div'> =
export type EuiFlexItemProps<TComponent extends ElementType = 'div'> =
PropsWithChildren &
CommonProps &
ComponentPropsWithoutRef<TComponent> & {
Expand All @@ -53,20 +52,20 @@ export type EuiFlexItemProps<TComponent extends ComponentPropType = 'div'> =
* such as `'div'` or `'span'`, a React component (a function, a class,
* or an exotic component like `memo()`).
*
* `<EuiFlexGroup>` accepts and forwards all extra props to the custom
* `<EuiFlexItem>` accepts and forwards all extra props to the custom
* component.
*
* @example
* // Renders a <button> element
* <EuiFlexItem component="button">
* Submit form
* </EuiFlexGroup>
* </EuiFlexItem>
* @default "div"
*/
component?: TComponent;
};

const EuiFlexItemInternal = <TComponent extends ComponentPropType>(
const EuiFlexItemInternal = <TComponent extends ElementType>(
{
children,
className,
Expand All @@ -75,7 +74,7 @@ const EuiFlexItemInternal = <TComponent extends ComponentPropType>(
...rest
}: EuiFlexItemProps<TComponent>,
ref: ForwardedRef<TComponent>
) => {
): ReactElement<EuiFlexItemProps<TComponent>, TComponent> => {
useEffect(() => {
if (VALID_GROW_VALUES.indexOf(grow) === -1) {
throw new Error(
Expand All @@ -95,10 +94,11 @@ const EuiFlexItemInternal = <TComponent extends ComponentPropType>(

const classes = classNames('euiFlexItem', className);

// Cast the resolved component prop type to ComponentType to help TS
// process multiple infers and the overall type complexity.
// This might not be needed in TypeScript 5
const Component = component as ComponentType<CommonProps & typeof rest>;
// Cast `component` to FunctionComponent to simplify its type.
// Note that FunctionComponent type is used here for purely typing
// convenience since we specify the return type above, and function
// components don't support `ref`s, but that doesn't matter in this case.
const Component = component as FunctionComponent<CommonProps & typeof rest>;

return (
<Component {...rest} ref={ref} css={cssStyles} className={classes}>
Expand All @@ -110,12 +110,12 @@ const EuiFlexItemInternal = <TComponent extends ComponentPropType>(
// Cast forwardRef return type to work with the generic TComponent type
// and not fallback to implicit any typing
export const EuiFlexItem = forwardRef(EuiFlexItemInternal) as (<
TComponent extends ComponentPropType,
TComponentRef = ReturnType<typeof EuiFlexItemInternal>
TComponent extends ElementType,
TComponentRef = ReactElement<any, TComponent>
>(
props: EuiFlexItemProps<TComponent> & {
ref?: Ref<TComponentRef>;
}
) => ReturnType<typeof EuiFlexItemInternal>) & { displayName?: string };
) => ReactElement) & { displayName?: string };

EuiFlexItem.displayName = 'EuiFlexItem';

0 comments on commit 1bbf8f3

Please sign in to comment.