Skip to content

Commit

Permalink
Add definition for @reach/router (#2426)
Browse files Browse the repository at this point in the history
* Add @reach/router v1.1.x definition

* Provide tests, improve typings

* Add flow annotation
  • Loading branch information
zoontek authored and gantoine committed Jun 26, 2018
1 parent 943690a commit 433baa7
Show file tree
Hide file tree
Showing 2 changed files with 341 additions and 0 deletions.
114 changes: 114 additions & 0 deletions definitions/npm/@reach/router_v1.1.x/flow_v0.63.x-/router_v1.1.x.js
@@ -0,0 +1,114 @@
// @flow

declare module '@reach/router' {
declare type NavigateFn = (to: string, options?: NavigateOptions<{}>) => void;
declare export var navigate: NavigateFn;

declare export type HistoryListener = () => void;
declare export type HistoryUnlistener = () => void;

declare export interface History {
location: string;
transitioning: boolean;
listen: (listener: HistoryListener) => HistoryUnlistener;
navigate: NavigateFn;
}

declare export interface HistorySource {
location: typeof location;
addEventListener(name: string, listener: (event: Event) => void): void;
removeEventListener(name: string, listener: (event: Event) => void): void;

history: {
state: any,
pushState(state: any, title: string, uri: string): void,
replaceState(state: any, title: string, uri: string): void,
};
}

declare export interface NavigateOptions<State> {
state?: State;
replace?: boolean;
}

declare type CommonRouteProps = {|
children?: React$Node,
location?: typeof location,
navigate?: NavigateFn,
uri?: string,
|};

declare export type DefaultRouteProps = {
...CommonRouteProps,
default: boolean,
};

declare export type RouteProps = {
...CommonRouteProps,
path: string,
};

declare export type LocationProviderRenderFn = (context: {|
location: typeof location,
navigate: NavigateFn,
|}) => React$Node;

declare export class Router extends React$Component<{|
children?: React$Node,
basepath?: string,
primary?: boolean,
location?: typeof location,
|}> {}

declare export class Link<State> extends React$Component<{
...$Shape<HTMLAnchorElement>,
children: React$Node,
getProps?: (props: {
isCurrent: boolean,
isPartiallyCurrent: boolean,
href: string,
location: typeof location,
}) => {},
state?: State,
to?: string,
replace?: boolean,
href?: empty, // remove href, as it's ignored by the router
}> {}

declare export class Redirect extends React$Component<{|
from?: string,
to: string,
noThrow?: boolean,
|}> {}

declare export class Match<Params> extends React$Component<{|
path: string,
children: (props: {|
match: null | ({ uri: string, path: string } & Params),
location: typeof location,
navigate: NavigateFn,
|}) => React$Node,
|}> {}

declare export class Location extends React$Component<{|
children: LocationProviderRenderFn,
|}> {}

declare export class LocationProvider extends React$Component<{|
history: History,
children?: React$Node | LocationProviderRenderFn,
|}> {}

declare export class ServerLocation extends React$Component<{|
url: string,
children?: React$Node,
|}> {}

declare export function createHistory(source: HistorySource): History;
declare export function createMemorySource(
initialPath: string,
): HistorySource;

declare export function isRedirect(error: any): $Exact<{ uri: string }>;
declare export function redirectTo(uri: string): void;
}
@@ -0,0 +1,227 @@
// @flow

import * as React from 'react';

import {
Router,
Link,
Redirect,
Match,
navigate,
Location,
LocationProvider,
ServerLocation,
createHistory,
createMemorySource,
isRedirect,
redirectTo,
} from '@reach/router';

import type { DefaultRouteProps, RouteProps } from '@reach/router';

import { it, describe } from 'flow-typed-test';

describe('@reach/router', () => {
describe('Router', () => {
it('works', () => {
<Router basepath="/" primary={true}>
<div />
</Router>;
});

it('raises error', () => {
// $ExpectError - basepath must be a string
<Router basepath={{}} />;
// $ExpectError - primary must be a boolean
<Router primary={{}} />;
// $ExpectError - children must be a React$Node
<Router>{Array}</Router>;
});
});

describe('DefaultRoute', () => {
const DefaultRoute = (props: DefaultRouteProps) => <div {...props} />;

it('works', () => {
<DefaultRoute default />;
<DefaultRoute default>
<div />
</DefaultRoute>;
});

it('raises error', () => {
// $ExpectError - default must be a boolean
<DefaultRoute default={{}} />;
// $ExpectError - children must be a React$Node
<DefaultRoute default>{Array}</DefaultRoute>;
});
});

describe('Route', () => {
const Route = (props: RouteProps) => <div {...props} />;

it('works', () => {
<Route path="/" />;
<Route path="invoice/:invoiceId" />;
<Route path="admin/*" />;
<Route path="" custom />;
<Route path="/">
<div />
</Route>;
});

it('raises error', () => {
// $ExpectError - path must be a string
<Route path={{}} />;
// $ExpectError - children must be a React$Node
<Route path="/">{Array}</Route>;
});
});

describe('Link', () => {
it('works', () => {
<Link to="/" replace custom>
<div />
</Link>;
});

it('raises error', () => {
// $ExpectError - children must be a React$Node
<Link to="/">{Array}</Link>;
// $ExpectError - to must be a string
<Link to={{}} />;
// $ExpectError - href isn't available
<Link href="/" />;
});
});

describe('Redirect', () => {
it('works', () => {
<Redirect from="aboutus" to="about-us" />;
<Redirect from="users/:userId" to="profile/:userId" />;
<Redirect to="/" noThrow />;
});

it('raises error', () => {
// $ExpectError - from must be a string
<Redirect from={{}} to="/" />;
// $ExpectError - to must be a string
<Redirect to={{}} />;
// $ExpectError - noThrow must be a boolean
<Redirect to="/" noThrow="" />;
});
});

describe('Match', () => {
it('works', () => {
<Match path="/">
{props =>
props.match ? <div>Hot {props.match.item}</div> : <div>Uncool</div>
}
</Match>;
});

it('raises error', () => {
// $ExpectError - children must be a function
<Match>
<div />
</Match>;
});
});

describe('navigate', () => {
it('works', () => {
navigate('/');
navigate('/', { state: {}, redirect: true });
});

it('raises error', () => {
// $ExpectError - first param must be a string
navigate({});
});
});

describe('Location', () => {
it('works', () => {
<Location>{props => <div />}</Location>;
});

it('raises error', () => {
// $ExpectError - children must be a function
<Location>
<div />
</Location>;
});
});

describe('LocationProvider', () => {
it('works', () => {
const history = createHistory(window);

<LocationProvider history={history}>
<div>Alright, we've established some location context</div>
</LocationProvider>;
});

it('raises error', () => {
// $ExpectError - history must be an instance of History
<LocationProvider history={{}}>
<div>Alright, we've established some location context</div>
</LocationProvider>;
});
});

describe('ServerLocation', () => {
it('works', () => {
<ServerLocation url="/groups/123">
<div />
</ServerLocation>;
});

it('raises error', () => {
// $ExpectError - url must be a string
<ServerLocation url={{}}>
<div />
</ServerLocation>;
});
});

describe('createHistory', () => {
it('works', () => {
createHistory(window);
});

it('raises error', () => {
// $ExpectError - first param must implements HistorySource
createHistory({});
});
});

describe('createMemorySource', () => {
it('works', () => {
createMemorySource('/starting/url');
});

it('raises error', () => {
// $ExpectError - first param must be a string
createMemorySource({});
});
});

describe('isRedirect', () => {
it('works', () => {
isRedirect({});
});
});

describe('redirectTo', () => {
it('works', () => {
redirectTo('/');
});

it('raises error', () => {
// $ExpectError - first param must be a string
redirectTo({});
});
});
});

0 comments on commit 433baa7

Please sign in to comment.