Skip to content

Commit

Permalink
feat(demos): add initial history service demo (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
clebert committed Dec 13, 2018
1 parent 1decb95 commit cd9daf1
Show file tree
Hide file tree
Showing 9 changed files with 465 additions and 43 deletions.
4 changes: 4 additions & 0 deletions packages/demos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ Now run one of the following demos:
yarn watch:demo amd-module-loader
```

```sh
yarn watch:demo history-service
```

---

Copyright (c) 2018 SinnerSchrader Deutschland GmbH. Released under the terms of
Expand Down
7 changes: 6 additions & 1 deletion packages/demos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@
"private": true,
"license": "MIT",
"dependencies": {
"@blueprintjs/core": "^3.10.0",
"@feature-hub/core": "^0.5.0",
"@feature-hub/history-service": "^0.5.1",
"@feature-hub/module-loader": "^0.5.0",
"@feature-hub/react": "^0.5.1",
"@feature-hub/server-renderer": "^0.5.0",
"@types/express": "^4.16.0",
"@types/get-port": "^4.0.0",
"@types/webpack": "^4.4.20",
"@types/webpack-dev-middleware": "^2.0.2",
"css-loader": "^2.0.0",
"express": "^4.16.4",
"get-port": "^4.0.0",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"style-loader": "^0.23.1",
"ts-loader": "^5.3.1",
"ts-node": "^7.0.1",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "3.1.6",
"url-loader": "^1.1.2",
"webpack": "^4.27.0",
"webpack-dev-middleware": "^3.4.0"
}
Expand Down
30 changes: 3 additions & 27 deletions packages/demos/src/amd-module-loader/webpack-config.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,10 @@
import {join} from 'path';
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import {Configuration} from 'webpack';

const baseConfig: Configuration = {
devtool: false,
mode: 'production',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader'
}
]
},
resolve: {
extensions: ['.js', '.json', '.ts', '.tsx'],
plugins: [
new TsConfigPathsPlugin({
configFile: join(__dirname, '../../tsconfig.json')
})
]
},
resolveLoader: {
modules: [join(__dirname, '../../node_modules'), 'node_modules']
}
};
import {webpackBaseConfig} from '../webpack-base-config';

export default [
{
...baseConfig,
...webpackBaseConfig,
entry: join(__dirname, './feature-app.tsx'),
externals: {
react: 'react'
Expand All @@ -40,7 +16,7 @@ export default [
}
},
{
...baseConfig,
...webpackBaseConfig,
entry: join(__dirname, './integrator.tsx'),
output: {
filename: 'integrator.js',
Expand Down
71 changes: 71 additions & 0 deletions packages/demos/src/history-service/history-consumer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {Card, Classes, Label} from '@blueprintjs/core';
import {FeatureAppDefinition} from '@feature-hub/core';
import {History, HistoryServiceV1} from '@feature-hub/history-service';
import {ReactFeatureApp} from '@feature-hub/react';
import * as React from 'react';

interface HistoryConsumerProps {
readonly history: History;
}

interface HistoryConsumerState {
readonly pathname: string;
}

class HistoryConsumer extends React.Component<
HistoryConsumerProps,
HistoryConsumerState
> {
public readonly state = {pathname: this.props.history.location.pathname};

private unlisten?: () => void;

public componentDidMount(): void {
const {history} = this.props;

this.unlisten = history.listen(({pathname}) => this.setState({pathname}));
}

public componentWillUnmount(): void {
if (this.unlisten) {
this.unlisten();
}
}

public render(): React.ReactNode {
const {pathname} = this.state;

return (
<Card>
<Label className="bp3-inline">
Pathname
<input className={Classes.INPUT} value={pathname} disabled />
</Label>
</Card>
);
}
}

export const historyConsumerDefinition: FeatureAppDefinition<
ReactFeatureApp,
undefined,
{'s2:history': HistoryServiceV1}
> = {
id: 'test:history-consumer',

dependencies: {
's2:history': '^1.0'
},

create: env => {
const historyService = env.featureServices['s2:history'];

return {
render: () => (
<HistoryConsumer history={historyService.createBrowserHistory()} />
)
};
}
};

export default historyConsumerDefinition;
46 changes: 46 additions & 0 deletions packages/demos/src/history-service/integrator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {FeatureAppManager, FeatureServiceRegistry} from '@feature-hub/core';
import {
createRootLocationTransformer,
defineHistoryService
} from '@feature-hub/history-service';
import {FeatureAppContainer} from '@feature-hub/react';
import {defineServerRenderer} from '@feature-hub/server-renderer';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {historyConsumerDefinition} from './history-consumer';

// tslint:disable
import 'normalize.css/normalize.css';
import '@blueprintjs/icons/lib/css/blueprint-icons.css';
import '@blueprintjs/core/lib/css/blueprint.css';
// tslint:enable

const registry = new FeatureServiceRegistry();

registry.registerProviders(
[
defineServerRenderer(undefined),
defineHistoryService(
createRootLocationTransformer({consumerPathsQueryParamName: 'test'})
)
],
'integrator'
);

const manager = new FeatureAppManager(registry, async () => undefined);

ReactDOM.render(
<>
<FeatureAppContainer
manager={manager}
featureAppDefinition={historyConsumerDefinition}
idSpecifier="a"
/>
<FeatureAppContainer
manager={manager}
featureAppDefinition={historyConsumerDefinition}
idSpecifier="b"
/>
</>,
document.querySelector('main')
);
14 changes: 14 additions & 0 deletions packages/demos/src/history-service/webpack-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {join} from 'path';
import {Configuration} from 'webpack';
import {webpackBaseConfig} from '../webpack-base-config';

export default [
{
...webpackBaseConfig,
entry: join(__dirname, './integrator.tsx'),
output: {
filename: 'integrator.js',
publicPath: '/'
}
}
] as Configuration[];
35 changes: 35 additions & 0 deletions packages/demos/src/webpack-base-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {join} from 'path';
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import {Configuration} from 'webpack';

export const webpackBaseConfig: Configuration = {
devtool: false,
mode: 'production',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
use: 'url-loader'
}
]
},
resolve: {
extensions: ['.js', '.json', '.ts', '.tsx'],
plugins: [
new TsConfigPathsPlugin({
configFile: join(__dirname, '../tsconfig.json')
})
]
},
resolveLoader: {
modules: [join(__dirname, '../node_modules'), 'node_modules']
}
};
36 changes: 29 additions & 7 deletions packages/history-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ page.
- [Motivation](#motivation)
- [Functional Behaviour](#functional-behaviour)
- [Location Transformer](#location-transformer)
- [Demo](#demo)
- [Usage](#usage)
- [As a Consumer](#as-a-consumer)
- [In the browser:](#in-the-browser)
Expand Down Expand Up @@ -47,11 +48,11 @@ access to the history for multiple consumers.

## Functional Behaviour

The history service combines multiple consumer histories (and their locations)
The History Service combines multiple consumer histories (and their locations)
into a single one. It does this by merging all registered consumer locations
into one, and persisting this combined root location on the history stack. As
long as the consumer performs all history and location interactions through the
history it obtained from the history service, the existence of the facade and
history it obtained from the History Service, the existence of the facade and
other consumers isn't noticeable for the consumer. For example, the consumer
receives history change events only for location changes that affect its own
history.
Expand All @@ -71,6 +72,25 @@ inserted directly into the root location. All other consumer locations are
encoded into a json string which will be assigned to a single configurable query
parameter.

## Demo

There is a demo that simulates the capabilities of the History Service with two
Feature Apps.

### Running the Demo

Go to the monorepo top-level directory and install all dependencies:

```sh
yarn
```

Now run the demo:

```sh
yarn watch:demo history-service
```

## Usage

### As a Consumer
Expand All @@ -95,7 +115,7 @@ export default {
render: () => (
<Router history={browserHistory}>
<App />
<Router>
</Router>
)
};
}
Expand All @@ -122,7 +142,7 @@ export default {
render: () => (
<Router history={memoryHistory}>
<App />
<Router>
</Router>
)
};
}
Expand All @@ -135,7 +155,7 @@ the history package. For further information reference

### As the Integrator

The integrator defines the history service with a
The integrator defines the History Service with a
[location transformer](#location-transformer) and registers it at the Feature
Service registry:

Expand Down Expand Up @@ -163,7 +183,7 @@ registry.registerProviders(featureServiceDefinitions, 'integrator');
```

On the server, the integrator defines the server renderer with a request. The
history service depends on the server renderer to obtain its request and use it
History Service depends on the server renderer to obtain its request and use it
for the initial history location:

```js
Expand All @@ -181,7 +201,9 @@ const rootLocationTransformer = createRootLocationTransformer({
consumerPathsQueryParamName: '---'
});

const request = {}; // obtain the request from somewhere, e.g. a request handler
const request = {
// ... obtain the request from somewhere, e.g. a request handler
};

const featureServiceDefinitions = [
defineHistoryService(rootLocationTransformer),
Expand Down
Loading

0 comments on commit cd9daf1

Please sign in to comment.