Skip to content

Commit

Permalink
fix(react): add jsdoc for all non-internal apis
Browse files Browse the repository at this point in the history
  • Loading branch information
Igmat committed Oct 4, 2018
1 parent 0bc4147 commit f6f8969
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 121 deletions.
44 changes: 28 additions & 16 deletions packages/react/src/ApplicationRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,49 @@ import { Component } from 'react';
import { HOC, IRequirementsOverride, resolver } from './Resolver';

/**
* // TODO: comment IApplicationRootProps
* @description application root props
* @description ApplicationRoot props
*/
export interface IApplicationRootProps {

/**
* // TODO: comment dependencies
* @description
* Entities of this array meant to be built with `dependency` helper
* @description array of overrides for `dependencies`
*/
dependencies: IOverrideResult[];

/**
* // TODO: comment requirements
* @description
* Entities of this array meant to be built with `requirement` helper
* @description array of overrides for `requirements`
*/
requirements: IRequirementsOverride[];
}

interface IApplicationRootState {
hocs: HOC[];
}
/**
* @internal
*/
export const config = {
shouldThrowForSeveralRoots: true,
};
let applicationRootWasAdded = false;

/**
* // TODO: comment ApplicationRoot
* @description Application root
* Component that is used for declaring overrides for `dependencies` and `requirements`.
* Also `requirements` HOCs applied to `render` method of this component in order to
* wrap you application tree for providing proper environment for your `ResolvableComponent`s
* @description ApplicationRoot Component
* > **NOTE:** `<ApplicationRoot>` is meant to appear only once in your app
* and in majority of cases it should be the first and only wrapper around
* your `App` component. Adding other `ApplicationRoot`s somewhere in your app
* won't affect dependency resolution mechanism but instead will throw an error
*/
export class ApplicationRoot extends Component<IApplicationRootProps, IApplicationRootState> {

/**
* // TODO: comment defaultProps
* @description Default props of application root
* by default there are no predefined `dependencies` or `requirements`
* @description default props of `ApplicationRoot` component
*/
static defaultProps = {
dependencies: [],
Expand All @@ -46,20 +58,20 @@ export class ApplicationRoot extends Component<IApplicationRootProps, IApplicati

/**
* Creates an instance of application root.
* @param props
* @param props contains overrides for `dependencies` and `requirements`
*/
constructor(props: Readonly<IApplicationRootProps>) {
super(props);
if (config.shouldThrowForSeveralRoots && applicationRootWasAdded) {
throw new Error("It's restricted to add more than one <ApplicationRoot>");
} else {
applicationRootWasAdded = true;
}
this.resolver.setOverrides(props.dependencies);
this.resolver.setRequirementsOverrides(props.requirements);
this.resolver.subscribeForRequirements(this.requirementsUpdate);
}

/**
* // TODO: comment render
* @description Renders application root
* @returns
*/
render() {
return this.wrapInHocs();
}
Expand Down
20 changes: 11 additions & 9 deletions packages/react/src/MockRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as MetafResolvable from 'metaf-resolvable';
import React, { ReactNode } from 'react';
import { ApplicationRoot, IApplicationRootProps } from './ApplicationRoot';
import { ApplicationRoot, config, IApplicationRootProps } from './ApplicationRoot';
import { ReactResolver, resolver } from './Resolver';

interface IRenderWrapper {
Expand Down Expand Up @@ -72,16 +72,23 @@ function resolveFor<I extends MetafResolvable.IInjections = {}>(
let isResolverFnChangedToMock = false;

/**
* // TODO: comment MockRoot
* @description Mock root
* Exactly same as `ApplicationRoot` with only difference:
* you are able to apply as many `MockRoot`s as your testing flow requires
* @description MockRoot component
* > **NOTE:** this component _MUST NOT_ be used as replacement for
* `ApplicationRoot` in your production code, since it significantly affects
* render flow in order to properly resolve dependencies for each subtree,
* which may cause performance issues in some circumstances.
* **BE AWARE THAT IT WAS DESIGNED FOR TESTING PUPOSES _ONLY_**
*/
export class MockRoot extends ApplicationRoot {

/**
* Creates an instance of mock root.
* @param props
* @param props exactly same `props` as `ApplicationRoot` uses
*/
constructor(props: Readonly<IApplicationRootProps>) {
config.shouldThrowForSeveralRoots = false;
super(props);
if (!isResolverFnChangedToMock) {
resolver.resolveFor = resolveFor;
Expand All @@ -90,11 +97,6 @@ export class MockRoot extends ApplicationRoot {
this.resolver = new ReactResolver(props.dependencies);
}

/**
* // TODO: comment render
* @description Renders mock root
* @returns
*/
render() {
return addPropsToAll(this.wrapInHocs(), this.resolver.resolveFor);
}
Expand Down
33 changes: 18 additions & 15 deletions packages/react/src/ResolvableComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,32 @@ import { AbstractResolvable, IInjections, Resolvable } from 'metaf-resolvable';
import { Component } from 'react';
import { IRequirements } from './Resolver';

export type ResolvableComponent<I extends IInjections> =
export type ResolvableComponent<I extends IInjections = {}> =
new <P = {}, S = {}, SS = any>(props: Readonly<P>) => AbstractResolvable<I> & Component<P, S, SS>;

export interface IResolvableComponent {
/**
* // TODO: comment ResolvableComponent
* @description Resolvable component
* @template I
* @template R
* @param [injections] dependencies that will be used by class directly
* @param requirements dependencies that should exist in code that use this class
* @returns component class to extend
* Resolvable component factory
* @description creates new class with resolved `injections` as `dependencies` and
* inform `ApplicationRoot` that it should fit specific `requirements`
* @template I type of injections' dictionary
* @template R type of requirements' tuple
* @param injections dependencies that will be used by component directly
* @param requirements dependencies that should exist in code that use this component
* @returns react `Component` class to extend
*/
<I extends IInjections = {}, R extends IRequirements = []>(injections?: I, ...requirements: R): ResolvableComponent<I>;
<I extends IInjections = {}, R extends IRequirements = []>(injections: I, ...requirements: R): ResolvableComponent<I>;
/**
* // TODO: comment ResolvableComponent
* @description Resolvable component
* @template I
* @template R
* Resolvable component factory
* @description creates new class without `dependencies` but with
* informing `ApplicationRoot` that it should fit specific `requirements`
* @template R type of requirements' tuple
* @param requirements dependencies that should exist in code that use this class
* @returns component class to extend
*/
<I extends IInjections = {}, R extends IRequirements = []>(...requirements: R): ResolvableComponent<I>;
<R extends IRequirements = []>(...requirements: R): ResolvableComponent;
}
// tslint:disable-next-line:variable-name
/**
* @description Resolvable component factory
*/
export const ResolvableComponent = Resolvable(Component) as IResolvableComponent;
54 changes: 0 additions & 54 deletions packages/react/src/ResolvableRoot.tsx

This file was deleted.

42 changes: 28 additions & 14 deletions packages/react/src/Resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,34 @@ import { ReactNode } from 'react';
export type HOC = (app: ReactNode) => JSX.Element;

/**
* // TODO: comment IRequirements
* @description requirements
* @description requirements for host application
*/
export type IRequirements = HOC[];
const requirementsOverride = Symbol('Requirements Override');

/**
* // TODO: comment IRequirementsOverride
* Interface that forces users to make type safe overrides
* @description requirements override
* @template T
* @template T type of dependency that have to be overwritten
* @template R
*/
export interface IRequirementsOverride<T extends HOC = any, R extends T = any> {
export interface IRequirementsOverride<T extends HOC = any, R extends T = T> {
[requirementsOverride]: [T, R];
}

/**
* // TODO: comment overrideRequirement
* @description Overrides requirement
* @template T
* @template R
* @param key
* @param value
* @returns requirement
* Function that meant to be used as only way for specifying requirements overrides
* it's designed to provide best possible type safety, but until something like this:
* https://github.com/Microsoft/TypeScript/issues/13890
* will land to TSX type inference, it only forces users to replace requirement HOC
* by another HOC, which, obviously, could ignore initial invariants,
* but unfortunately there are not better options for now
* @description creates a `[key, value]` pair describing requirement override
* @template T type of requirement key that has to be overwritten
* @template R type of value that will be used instead of original requirement
* @param key actual key that has to be overwritten
* @param value value that will be passed as requirement for this `key`
* @returns object with symbol property which point to `[key, value]` pair
*/
export function overrideRequirement<T extends HOC, R extends T>(key: T, value: R): IRequirementsOverride<T, R> {
return { [requirementsOverride]: [key, value] };
Expand Down Expand Up @@ -81,10 +85,20 @@ export class ReactResolver extends Resolver {
*/
export const resolver = new ReactResolver();

/**
* Entry point for resolving requirements for particular `ResolvableComponent`,
* but also could be used as a HOC for any other component including `SFC`s
* @description resolves `requirements` for `componentImpl` and applies them to `ApplicationRoot`
* @template R type of `requirements` tuple
* @template C type of `componentImpl`
* @param requirements tuple of specific requirements that should be met by hosting application
* @param componentImpl particular resolvable class implementation
* @returns exactly same `componentImpl`, but with resolved requirements
*/
export function resolveRequirements<
R extends IRequirements = [],
C extends object = any>(requirements: R, classImpl: C) {
return resolver.resolveRequirements(requirements, classImpl);
C extends object = any>(requirements: R, componentImpl: C) {
return resolver.resolveRequirements(requirements, componentImpl);
}
function resolveFor<I extends IInjections = {}>(
instance: object,
Expand Down
15 changes: 12 additions & 3 deletions packages/react/src/defineRequirement.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import React, { ReactNode } from 'react';

export const defineRequirement = <T extends {}>(Requirement: React.ComponentType<T>, props?: Pick<T, Exclude<keyof T, 'children'>>) =>
(app: ReactNode) =>
<Requirement { ...props }>{ app }</Requirement>;
/**
* Helper function for defining requirements
* @description Creates requirement from particular component and props that will be passed to it
* @template T type of props for this `Requirement` component
* @param Requirement component type that will be used as HOC for wrapping `ApplicationRoot`
* @param [props] actual props that have to be passed to `Requirement`
* @returns requirement that could be easily used by `ResolvableComponent` or `withRequirements` HOC
*/
export function defineRequirement<T extends {}>(Requirement: React.ComponentType<T>, props?: Pick<T, Exclude<keyof T, 'children'>>) {
return (app: ReactNode) =>
<Requirement {...props}>{app}</Requirement>;
}
23 changes: 13 additions & 10 deletions packages/resolvable/src/AbstractResolvable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,40 @@ export type Injected<T extends Injectable> = T extends Constructable
: never;

/**
* // TODO: comment IInjections
* @description injections
* Injections interface
* @description it's a dictionary that contains constructors/getters
* for dependencies initialization
*/
export interface IInjections {
[index: string]: Injectable;
}

/**
* // TODO: comment IDependencies
* @description dependencies
* Dependencies interface
* @description it's a dictionary of resolved and initialized instances for each dependency
*/
export type IDependencies<T extends IInjections> = {
[K in keyof T]: Injected<T[K]>
};

/**
* // TODO: comment AbstractResolvable
* @description Abstract resolvable
* @template I
* AbstractResolvable class
* @description this class doesn't meant to be used directly
* its main purpose is correct type inferring for dependencies
* and providing strict contract for further usage
* @template I type of dependencies' dictionary
*/
export abstract class AbstractResolvable<I extends IInjections> {

/**
* // TODO: comment dependencies
* @description Dependencies of abstract resolvable
* Dictionary of dependencies for this class
* @description contains resolved and initialized instances for each dependency
*/
protected readonly dependencies: Readonly<IDependencies<I>>;

/**
* Creates an instance of abstract resolvable.
* @param dependencies
* @param dependencies dependencies dictionary that should be injected to resolvable class
*/
constructor(dependencies: Readonly<IDependencies<I>>) {
this.dependencies = dependencies;
Expand Down

0 comments on commit f6f8969

Please sign in to comment.