Skip to content

Commit

Permalink
compose: Add types to createHigherOrderComponent and ifCondition
Browse files Browse the repository at this point in the history
  • Loading branch information
sarayourfriend committed Apr 15, 2021
1 parent 0fa010e commit 4b81b46
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 66 deletions.
18 changes: 14 additions & 4 deletions packages/compose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,35 @@ name, returns the enhanced component augmented with a generated displayName.

_Parameters_

- _mapComponentToEnhancedComponent_ `Function`: Function mapping component to enhanced component.
- _mapComponentToEnhancedComponent_ `( OriginalComponent: ComponentType< TProps > ) => ComponentType< Subtract< TProps, TObviatedProps > >`: Function mapping component to enhanced component.
- _modifierName_ `string`: Seed name from which to generated display name.

_Returns_

- `WPComponent`: Component class with generated display name assigned.
- `HigherOrderComponent< TObviatedProps >`: Component class with generated display name assigned.

<a name="ifCondition" href="#ifCondition">#</a> **ifCondition**

Higher-order component creator, creating a new component which renders if
the given condition is satisfied or with the given optional prop name.

_Usage_

```ts
type Props = { foo: string };
const Component = ( props: Props ) => <div>{ props.foo }</div>;
const ConditionalComponent = ifCondition( ( props: Props ) => props.foo.length !== 0 )( Component );
<ConditionalComponent foo="" />; // => null
<ConditionalComponent foo="bar" />; // => <div>bar</div>;
```

_Parameters_

- _predicate_ `Function`: Function to test condition.
- _predicate_ `( props: TProps ) => boolean`: Function to test condition.

_Returns_

- `Function`: Higher-order component.
- `HigherOrderComponent`: Higher-order component.

<a name="pure" href="#pure">#</a> **pure**

Expand Down
26 changes: 0 additions & 26 deletions packages/compose/src/higher-order/if-condition/index.js

This file was deleted.

39 changes: 39 additions & 0 deletions packages/compose/src/higher-order/if-condition/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Internal dependencies
*/
import createHigherOrderComponent from '../../utils/create-higher-order-component';
// eslint-disable-next-line no-duplicate-imports
import type { HigherOrderComponent } from '../../utils/create-higher-order-component';

/**
* Higher-order component creator, creating a new component which renders if
* the given condition is satisfied or with the given optional prop name.
*
* @example
* ```ts
* type Props = { foo: string };
* const Component = ( props: Props ) => <div>{ props.foo }</div>;
* const ConditionalComponent = ifCondition( ( props: Props ) => props.foo.length !== 0 )( Component );
* <ConditionalComponent foo="" />; // => null
* <ConditionalComponent foo="bar" />; // => <div>bar</div>;
* ```
*
* @param predicate Function to test condition.
*
* @return Higher-order component.
*/
const ifCondition = < TProps, >(
predicate: ( props: TProps ) => boolean
): HigherOrderComponent =>
createHigherOrderComponent< {}, TProps >(
( WrappedComponent ) => ( props: TProps ) => {
if ( ! predicate( props ) ) {
return null;
}

return <WrappedComponent { ...props } />;
},
'ifCondition'
);

export default ifCondition;
36 changes: 0 additions & 36 deletions packages/compose/src/utils/create-higher-order-component/index.js

This file was deleted.

58 changes: 58 additions & 0 deletions packages/compose/src/utils/create-higher-order-component/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* External dependencies
*/
import { camelCase, upperFirst } from 'lodash';
// eslint-disable-next-line no-restricted-imports
import type { ComponentType } from 'react';
import type { Subtract } from 'utility-types';

/**
* Higher order components can cause props to be obviated. For example a HOC that
* injects i18n props will obviate the need for the i18n props to be passed to the component.
*
* If a HOC does not obviate the need for any specific props then we default to `{}` which
* essentially subtracts 0 from the original props of the passed in component. An example
* of this is the `pure` HOC which does not change the API surface of the component but
* simply modifies the internals.
*/
export interface HigherOrderComponent< TObviatedProps extends object = {} > {
< TP extends TObviatedProps >(
OriginalComponent: ComponentType< TP >
): ComponentType< Subtract< TP, TObviatedProps > >;
}

/**
* Given a function mapping a component to an enhanced component and modifier
* name, returns the enhanced component augmented with a generated displayName.
*
* @param mapComponentToEnhancedComponent Function mapping component to enhanced component.
* @param modifierName Seed name from which to generated display name.
*
* @return Component class with generated display name assigned.
*/
function createHigherOrderComponent<
TObviatedProps extends object,
TProps extends TObviatedProps
>(
mapComponentToEnhancedComponent: (
OriginalComponent: ComponentType< TProps >
) => ComponentType< Subtract< TProps, TObviatedProps > >,
modifierName: string
): HigherOrderComponent< TObviatedProps > {
return ( ( OriginalComponent: ComponentType< TProps > ) => {
const EnhancedComponent = mapComponentToEnhancedComponent(
OriginalComponent
);

const {
displayName = OriginalComponent.name || 'Component',
} = OriginalComponent;

EnhancedComponent.displayName = `${ upperFirst(
camelCase( modifierName )
) }(${ displayName })`;

return EnhancedComponent;
} ) as HigherOrderComponent< TObviatedProps >;
}
export default createHigherOrderComponent;
11 changes: 11 additions & 0 deletions packages/compose/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"declarationDir": "build-types"
},
"include": [
"src/higher-order/if-condition/**/*",
"src/utils/**/*"
]
}

0 comments on commit 4b81b46

Please sign in to comment.