Skip to content

Commit 3cb8104

Browse files
authored
frint-router-component-handlers (#327)
* #290 Create `frint-router-component-handlers` * #290 Add README.md for `frint-router-component-handlers` * #290 Add tests for `frint-router-component-handlers` * #290 Update `frint-router-component-handlers` dependencies to 3.0.1 * Update frint-router-react dependencies * Add links to frint-router-component-handlers in docs
1 parent bb1bd61 commit 3cb8104

23 files changed

+838
-162
lines changed

README.md

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,25 @@ Find more examples [here](https://github.com/Travix-International/frint/tree/mas
4242

4343
The framework is a collection of these packages, which can be composed together on demand:
4444

45-
| Package | Status | Description |
46-
|-----------------------------|------------------------------------------------------------------------|-------------|
47-
| [frint] | [![frint-status]][frint-package] | Base for creating Apps |
48-
| [frint-store] | [![frint-store-status]][frint-store-package] | State management with reactive stores |
49-
| [frint-data] | [![frint-data-status]][frint-data-package] | Reactive data modelling |
50-
| [frint-react] | [![frint-react-status]][frint-react-package] | React.js integration |
51-
| [frint-react-server] | [![frint-react-server-status]][frint-react-server-package] | Server-side rendering of Apps |
52-
| [frint-router] | [![frint-router-status]][frint-router-package] | Router services for building Single Page Applications |
53-
| [frint-router-react] | [![frint-router-react-status]][frint-router-react-package] | React components for building SPAs |
54-
| [frint-cli] | [![frint-cli-status]][frint-cli-package] | CLI runner |
55-
| [frint-compat] | [![frint-compat-status]][frint-compat-package] | Backwards compatibility for older versions |
56-
| [frint-model] | [![frint-model-status]][frint-model-package] | Use `frint-data` instead |
57-
| **For library developers:** | | |
58-
| [frint-component-utils] | [![frint-component-utils-status]][frint-component-utils-package] | Utils for reactive Components |
59-
| [frint-component-handlers] | [![frint-component-handlers-status]][frint-component-handlers-package] | Handlers for integrating with other rendering libraries |
60-
| **Internally used:** | | |
61-
| [frint-test-utils] | [![frint-test-utils-status]][frint-test-utils-package] | Internally used test utilities |
62-
| [frint-config] | [![frint-config-status]][frint-config-package] | Common config for your Apps |
45+
| Package | Status | Description |
46+
|------------------------------------|--------------------------------------------------------------------------------------|-------------|
47+
| [frint] | [![frint-status]][frint-package] | Base for creating Apps |
48+
| [frint-store] | [![frint-store-status]][frint-store-package] | State management with reactive stores |
49+
| [frint-data] | [![frint-data-status]][frint-data-package] | Reactive data modelling |
50+
| [frint-react] | [![frint-react-status]][frint-react-package] | React.js integration |
51+
| [frint-react-server] | [![frint-react-server-status]][frint-react-server-package] | Server-side rendering of Apps |
52+
| [frint-router] | [![frint-router-status]][frint-router-package] | Router services for building Single Page Applications |
53+
| [frint-router-react] | [![frint-router-react-status]][frint-router-react-package] | React components for building SPAs |
54+
| [frint-cli] | [![frint-cli-status]][frint-cli-package] | CLI runner |
55+
| [frint-compat] | [![frint-compat-status]][frint-compat-package] | Backwards compatibility for older versions |
56+
| [frint-model] | [![frint-model-status]][frint-model-package] | Use `frint-data` instead |
57+
| **For library developers:** | | |
58+
| [frint-component-utils] | [![frint-component-utils-status]][frint-component-utils-package] | Utils for reactive Components |
59+
| [frint-component-handlers] | [![frint-component-handlers-status]][frint-component-handlers-package] | Handlers for integrating with other rendering libraries |
60+
| [frint-router-component-handlers] | [![frint-router-component-handlers-status]][frint-router-component-handlers-package] | Handlers for integrating `frint-router` with other rendering libraries |
61+
| **Internally used:** | | |
62+
| [frint-test-utils] | [![frint-test-utils-status]][frint-test-utils-package] | Internally used test utilities |
63+
| [frint-config] | [![frint-config-status]][frint-config-package] | Common config for your Apps |
6364

6465
[frint]: https://frint.js.org/docs/packages/frint
6566
[frint-store]: https://frint.js.org/docs/packages/frint-store
@@ -73,6 +74,7 @@ The framework is a collection of these packages, which can be composed together
7374
[frint-compat]: https://frint.js.org/docs/packages/frint-compat
7475
[frint-component-utils]: https://frint.js.org/docs/packages/frint-component-utils
7576
[frint-component-handlers]: https://frint.js.org/docs/packages/frint-component-handlers
77+
[frint-router-component-handlers]: https://frint.js.org/docs/packages/frint-router-component-handlers
7678
[frint-test-utils]: https://frint.js.org/docs/packages/frint-test-utils
7779
[frint-config]: https://frint.js.org/docs/packages/frint-config
7880

@@ -88,6 +90,7 @@ The framework is a collection of these packages, which can be composed together
8890
[frint-compat-status]: https://img.shields.io/npm/v/frint-compat.svg
8991
[frint-component-utils-status]: https://img.shields.io/npm/v/frint-component-utils.svg
9092
[frint-component-handlers-status]: https://img.shields.io/npm/v/frint-component-handlers.svg
93+
[frint-router-component-handlers-status]: https://img.shields.io/npm/v/frint-router-component-handlers.svg
9194
[frint-test-utils-status]: https://img.shields.io/npm/v/frint-test-utils.svg
9295
[frint-config-status]: https://img.shields.io/npm/v/frint-config.svg
9396

@@ -103,6 +106,7 @@ The framework is a collection of these packages, which can be composed together
103106
[frint-compat-package]: https://npmjs.com/package/frint-compat
104107
[frint-component-utils-package]: https://npmjs.com/package/frint-component-utils
105108
[frint-component-handlers-package]: https://npmjs.com/package/frint-component-handlers
109+
[frint-router-component-handlers-package]: https://npmjs.com/package/frint-router-component-handlers
106110
[frint-test-utils-package]: https://npmjs.com/package/frint-test-utils
107111
[frint-config-package]: https://npmjs.com/package/frint-config
108112

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/*.js
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
src
2+
test
3+
coverage
4+
node_modules
5+
.nyc_output
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# frint-router-component-handlers
2+
3+
[![npm](https://img.shields.io/npm/v/frint-router-component-handlers.svg)](https://www.npmjs.com/package/frint-router-component-handlers)
4+
5+
> Router component handlers package of Frint
6+
7+
<!-- MarkdownTOC autolink=true bracket=round -->
8+
9+
- [Guide](#guide)
10+
- [Installation](#installation)
11+
- [API](#api)
12+
- [createLinkHandler](#createlinkhandler)
13+
- [createRouteHandler](#createroutehandler)
14+
- [createSwitchHandler](#createswitchhandler)
15+
16+
<!-- /MarkdownTOC -->
17+
18+
---
19+
20+
# Guide
21+
22+
This package is aimed at enabling other reactive rendering/templating libraries integrate with FrintJS router, and not to be used directly by developers in their applications.
23+
24+
For example, take a look at `frint-router-react` for its implementation using this package internally.
25+
26+
## Installation
27+
28+
With [npm](https://www.npmjs.com/):
29+
30+
```
31+
$ npm install --save frint-router-component-handlers
32+
```
33+
34+
# API
35+
36+
37+
## createLinkHandler
38+
39+
> createLinkHandler(ComponentHandler, app, component)
40+
41+
Method for creating handler for `Link` component.
42+
43+
#### Arguments
44+
45+
1. `ComponentHandler` (`Object`): framework specific component handler implementing common API to work with `component`
46+
2. `app` (`Object`): app instance
47+
3. `component` (`Object`): `Link` component instance
48+
49+
50+
## createRouteHandler
51+
52+
> createRouteHandler(ComponentHandler, app, component)
53+
54+
Method for creating handler for `Route` component.
55+
56+
#### Arguments
57+
58+
1. `ComponentHandler` (`Object`): framework specific component handler implementing common API to work with `component`
59+
2. `app` (`Object`): app instance
60+
3. `component` (`Object`): `Route` component instance
61+
62+
63+
## createSwitchHandler
64+
65+
> createSwitchHandler(ComponentHandler, app, component)
66+
67+
Method for creating handler for `Switch` component.
68+
69+
#### Arguments
70+
71+
1. `ComponentHandler` (`Object`): framework specific component handler implementing common API to work with `component`
72+
2. `app` (`Object`): app instance
73+
3. `component` (`Object`): `Switch` component instance
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "frint-router-component-handlers",
3+
"version": "3.0.1",
4+
"description": "Router component handlers package for Frint",
5+
"main": "lib/index.js",
6+
"homepage": "https://github.com/Travix-International/frint/tree/master/packages/frint-router-component-handlers",
7+
"scripts": {
8+
"lint": "cross-env ../../node_modules/.bin/eslint --color '{src,test}/**/*.js'",
9+
"transpile": "cross-env ../../node_modules/.bin/babel src --out-dir lib",
10+
"test": "cross-env ../../node_modules/.bin/mocha --colors --compilers js:babel-register --recursive ./src/**/*.spec.js",
11+
"cover:run": "cross-env ../../node_modules/.bin/nyc --reporter=json --require babel-register ../../node_modules/.bin/mocha --colors --compilers js:babel-register --recursive ./src/**/*.spec.js",
12+
"cover:report": "cross-env ../../node_modules/.bin/nyc report",
13+
"cover": "npm run cover:run && npm run cover:report",
14+
"dist:lib": "cross-env ../../node_modules/.bin/webpack --config ./webpack.config.js",
15+
"dist:min": "cross-env DIST_MIN=1 ../../node_modules/.bin/webpack --config ./webpack.config.js",
16+
"dist": "npm run dist:lib && npm run dist:min",
17+
"prepublish": "npm run transpile"
18+
},
19+
"repository": {
20+
"type": "git",
21+
"url": "git+https://github.com/Travix-International/frint.git"
22+
},
23+
"author": {
24+
"name": "Travix International B.V.",
25+
"url": "https://travix.com"
26+
},
27+
"keywords": [
28+
"frint"
29+
],
30+
"dependencies": {
31+
"frint-component-utils": "^3.0.1"
32+
},
33+
"devDependencies": {
34+
"cross-env": "^5.0.5",
35+
"frint-router": "^3.0.1",
36+
"frint": "^3.0.1"
37+
},
38+
"bugs": {
39+
"url": "https://github.com/Travix-International/frint/issues"
40+
},
41+
"license": "MIT"
42+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { composeHandlers } from 'frint-component-utils';
2+
3+
const LinkHandler = {
4+
_routerSubscription: null,
5+
6+
getInitialData() {
7+
return {
8+
active: false,
9+
};
10+
},
11+
12+
beforeMount() {
13+
this._resubscribeToRouter(this.getProps());
14+
},
15+
16+
propsChange(nextProps, toChanged, exactChanged) {
17+
this._resubscribeToRouter(nextProps, toChanged, exactChanged);
18+
},
19+
20+
beforeDestroy() {
21+
this._unsubscribeFromRouter();
22+
},
23+
24+
handleClick() {
25+
const router = this._getRouter();
26+
const to = this.getProp('to');
27+
28+
if (router.getMatch(to, router.getHistory(), { exact: true }) === null) {
29+
router.push(to);
30+
}
31+
},
32+
33+
_getRouter() {
34+
return this.app.get('router');
35+
},
36+
37+
_resubscribeToRouter(nextProps, toChanged = false, exactChanged = false) {
38+
const { activeClassName, to, exact } = nextProps;
39+
40+
this._unsubscribeFromRouter();
41+
42+
if (typeof activeClassName === 'string') {
43+
if (!this._routerSubscription || toChanged || exactChanged) {
44+
this._routerSubscription = this._getRouter()
45+
.getMatch$(to, { exact })
46+
.subscribe((matched) => {
47+
if (!matched) {
48+
return this.setData('active', false);
49+
}
50+
51+
return this.setData('active', true);
52+
});
53+
}
54+
}
55+
},
56+
57+
_unsubscribeFromRouter() {
58+
if (this._routerSubscription) {
59+
this._routerSubscription.unsubscribe();
60+
this._routerSubscription = null;
61+
}
62+
},
63+
};
64+
65+
export default function createLinkHandler(ComponentHandler, app, component) {
66+
return composeHandlers(
67+
LinkHandler,
68+
ComponentHandler,
69+
{
70+
app,
71+
component
72+
},
73+
);
74+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* eslint-disable import/no-extraneous-dependencies, func-names, no-unused-expressions */
2+
/* global describe, it */
3+
import { expect } from 'chai';
4+
5+
import { MemoryRouterService } from 'frint-router';
6+
import createLinkHandler from './createLinkHandler';
7+
import { ComponentHandler, createTestAppInstance, createComponent } from './testHelpers';
8+
9+
describe('frint-router-component-handlers › createLinkHandler', function () {
10+
it('creates a handler with getInitialData, beforeMount, propsChange, handleClick, beforeDestroy methods', function () {
11+
const handler = createLinkHandler(
12+
ComponentHandler,
13+
createTestAppInstance(new MemoryRouterService()),
14+
createComponent()
15+
);
16+
17+
expect(handler).to.include.all.keys('getInitialData', 'beforeMount', 'propsChange', 'handleClick', 'beforeDestroy');
18+
});
19+
20+
describe('LinkHandler functions properly throughout lifecycle', function () {
21+
const router = new MemoryRouterService();
22+
23+
const component = createComponent();
24+
component.props.to = '/about';
25+
component.props.exact = false;
26+
component.props.activeClassName = 'active-link';
27+
28+
const handler = createLinkHandler(
29+
ComponentHandler,
30+
createTestAppInstance(router),
31+
component
32+
);
33+
34+
it('when getInitialData() called, it returns object with active key', function () {
35+
component.data = handler.getInitialData();
36+
expect(component.data).to.have.all.keys({ active: false });
37+
});
38+
39+
it('when beforeMount() called, subscribes to router and changes active data accordingly', function () {
40+
handler.beforeMount();
41+
expect(component.data.active).to.be.false;
42+
43+
router.push('/about');
44+
45+
expect(component.data.active).to.be.true;
46+
});
47+
48+
it('when props change and propsChange() called, it resubscribes to router and changes active data accordingly', function () {
49+
component.props.to = '/contact';
50+
handler.propsChange(component.props, true, false);
51+
52+
expect(component.data.active).to.be.false;
53+
54+
router.push('/contact/us');
55+
expect(component.data.active).to.be.true;
56+
});
57+
58+
it('when handleClick() is called, it changes router url', function () {
59+
expect(router.getLocation().pathname).to.equal('/contact/us');
60+
61+
handler.handleClick();
62+
63+
expect(router.getLocation().pathname).to.equal('/contact');
64+
});
65+
66+
it('when beforeDestroy() is called, unsubscribes from router and doesn\'t change active data anymore', function () {
67+
expect(component.data.active).to.be.true;
68+
69+
handler.beforeDestroy();
70+
router.push('/random');
71+
expect(component.data.active).to.be.true;
72+
});
73+
});
74+
});

0 commit comments

Comments
 (0)