Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dts-generator likes to expand all types and for some reason this causes longer type checking time

'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>;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ElementType<CommonProps> caused the global attributes to not be added to the final type


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 };
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loosens the return type but shouldn't have any impact on consumers of these two components. In case a stricter type is needed for ref usage, there's the TComponentRef generic argument they may override


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';