Skip to content

Commit

Permalink
Fix incorrect TypeScript type for custom components passed to EuiIcon (
Browse files Browse the repository at this point in the history
…#3252)

* Fix incorrect TypeScript type for custom components passed to EuiIcon

* TS->proptype generation for ComponentType

* changelog
  • Loading branch information
chandlerprall authored Apr 4, 2020
1 parent 24bfdb7 commit 0fc7625
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

- Fixed the `img` element in `EuiIcon` using custom SVGs to have an `alt` attribute with an empty string, rather than no `alt` attribute at all ([#3245](https://github.com/elastic/eui/pull/3245))
- Added overflows to EuiDataGrid toolbar dropdowns when there are many columns ([#3238](https://github.com/elastic/eui/pull/3238))
- Fixed `EuiIcon`'s icon `type` definition to allow custom React components ([#3252](https://github.com/elastic/eui/pull/3252))

## [`22.3.0`](https://github.com/elastic/eui/tree/v22.3.0)

Expand Down
6 changes: 6 additions & 0 deletions scripts/babel/proptypes-from-ts-props/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ function resolveIdentifierToPropTypes(node, state) {
types.identifier('node')
);

case 'ComponentType':
return types.memberExpression(
types.identifier('PropTypes'),
types.identifier('elementType')
);

case 'JSXElementConstructor':
return types.memberExpression(
types.identifier('PropTypes'),
Expand Down
52 changes: 52 additions & 0 deletions scripts/babel/proptypes-from-ts-props/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,58 @@ FooComponent.propTypes = {

});

describe('elementType propType', () => {

it('understands React.ComponentType', () => {
const result = transform(
`
import React from 'react';
interface IFooProps {foo: React.ComponentType, bar: React.ComponentType<{ prop: any }>}
const FooComponent: React.SFC<IFooProps> = () => {
return (<div>Hello World</div>);
}`,
babelOptions
);

expect(result.code).toBe(`import React from 'react';
import PropTypes from "prop-types";
const FooComponent = () => {
return <div>Hello World</div>;
};
FooComponent.propTypes = {
foo: PropTypes.elementType.isRequired,
bar: PropTypes.elementType.isRequired
};`);
});

it('understands ComponentType', () => {
const result = transform(
`
import React from 'react';
interface IFooProps {foo: ComponentType, bar: ComponentType<{ prop: any }>}
const FooComponent: React.SFC<IFooProps> = () => {
return (<div>Hello World</div>);
}`,
babelOptions
);

expect(result.code).toBe(`import React from 'react';
import PropTypes from "prop-types";
const FooComponent = () => {
return <div>Hello World</div>;
};
FooComponent.propTypes = {
foo: PropTypes.elementType.isRequired,
bar: PropTypes.elementType.isRequired
};`);
});

});

});

describe('intersection types', () => {
Expand Down
12 changes: 12 additions & 0 deletions src/components/icon/__snapshots__/icon.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9053,3 +9053,15 @@ exports[`EuiIcon props type wrench is rendered 1`] = `
/>
</svg>
`;

exports[`EuiIcon renders custom components 1`] = `
<span
aria-hidden="true"
aria-label="heart"
class="euiIcon euiIcon--medium euiIcon-isLoaded"
focusable="false"
role="img"
>
❤️
</span>
`;
12 changes: 12 additions & 0 deletions src/components/icon/icon.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,16 @@ describe('EuiIcon', () => {
);
});
});

it('renders custom components', () => {
const CustomIcon = ({ ...props }) => {
return (
<span role="img" aria-label="heart" {...props}>
❤️
</span>
);
};
const component = mount(<EuiIcon type={CustomIcon} />);
expect(prettyHtml(component.html())).toMatchSnapshot();
});
});
10 changes: 5 additions & 5 deletions src/components/icon/icon.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {
PureComponent,
HTMLAttributes,
ReactElement,
ComponentType,
SVGAttributes,
} from 'react';
import classNames from 'classnames';
Expand Down Expand Up @@ -415,7 +415,7 @@ export const TYPES = keysOf(typeToPathMap);

export type EuiIconType = keyof typeof typeToPathMap;

export type IconType = EuiIconType | string | ReactElement;
export type IconType = EuiIconType | string | ComponentType;

const colorToClassMap = {
default: null,
Expand Down Expand Up @@ -457,7 +457,7 @@ export type IconSize = keyof typeof sizeToClassNameMap;
export type EuiIconProps = CommonProps &
Omit<SVGAttributes<SVGElement>, 'type' | 'color' | 'size'> & {
/**
* `Enum` is any of the named icons listed in the docs, `Element` is any React SVG element, and `string` is usually a URL to an SVG file
* `Enum` is any of the named icons listed in the docs, `string` is usually a URL to an SVG file, and `elementType` is any React SVG component
*/
type: IconType;
/**
Expand Down Expand Up @@ -488,7 +488,7 @@ export type EuiIconProps = CommonProps &
};

interface State {
icon: undefined | ReactElement | string;
icon: undefined | ComponentType | string;
iconTitle: undefined | string;
isLoading: boolean;
}
Expand Down Expand Up @@ -625,7 +625,7 @@ export class EuiIcon extends PureComponent<EuiIconProps, State> {
className
);

const icon = this.state.icon || empty;
const icon = this.state.icon || (empty as ComponentType);

// This is a fix for IE and Edge, which ignores tabindex="-1" on an SVG, but respects
// focusable="false".
Expand Down

0 comments on commit 0fc7625

Please sign in to comment.