Skip to content

Instantly share code, notes, and snippets.

@honzabrecka
Created August 8, 2019 11:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save honzabrecka/a8ff53a55bcdf3750480db96f08764e8 to your computer and use it in GitHub Desktop.
Save honzabrecka/a8ff53a55bcdf3750480db96f08764e8 to your computer and use it in GitHub Desktop.
// @flow
import * as React from 'react';
import classNames from 'classnames';
// import type { ClassNames } from 'classnames';
type ClassNames = any;
export type ClassNamesOrFunction = ClassNames | ((props: any) => ClassNames);
type MinimalProps = {
+className?: ClassNames,
+children?: any
};
type MinimalAdditionalProps = {
+className?: ClassNamesOrFunction
};
/**
* Wraps a component and provides some useful behaviour:
* - it can have default props
* - it merges default classes with classes provided later (className prop)
* - it forwards ref
*/
export function wrapComponent<
Props: MinimalProps,
AdditionalProps: MinimalAdditionalProps,
Instance
>(
Component: React.AbstractComponent<Props, Instance>,
{ className: componentClasses, ...defaultProps }: AdditionalProps = {}
): React.AbstractComponent<$Diff<Props, AdditionalProps>, Instance> {
return React.forwardRef(({ className, children, ...props }, ref) => {
const classes = getClasses(componentClasses, className, props);
return (
<Component className={classes} {...defaultProps} {...props} ref={ref}>
{children}
</Component>
);
});
}
function getClasses(
componentClasses: ClassNamesOrFunction,
className: ClassNames,
props: any
): string {
const cls =
typeof componentClasses === 'function'
? componentClasses(props)
: componentClasses;
return classNames(cls, className);
}
type XProps = {|
+className: ClassNames,
+children?: any,
+a: string,
+b: string
|};
const X = ({ a, b }: XProps) => <div>{a + b}</div>;
type DProps = {|
+className: string,
+a: string
|};
const Y = wrapComponent<XProps, DProps, mixed>(X, {
className: 'foo',
a: 'foo'
});
const t = () => (
<>
<Y className="foo" />
<Y b="foo" />
<X className="foo" />
<X a="foo" b="foo" className="foo" />
</>
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment