Skip to content

Commit 539d33a

Browse files
christopherthielenmergify[bot]
authored andcommitted
feat(urlRuleFactory): Add support for StateDeclarations in UrlRuleFactory.fromState()
1 parent e657cfe commit 539d33a

File tree

3 files changed

+95
-10
lines changed

3 files changed

+95
-10
lines changed

src/state/stateObject.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ export class StateObject {
125125
static isStateClass = (stateDecl: _StateDeclaration): stateDecl is { new (): StateDeclaration } =>
126126
isFunction(stateDecl) && stateDecl['__uiRouterState'] === true;
127127

128+
/** Predicate which returns true if the object is a [[StateDeclaration]] object */
129+
static isStateDeclaration = (obj: any): obj is StateDeclaration => isFunction(obj['$$state']);
130+
128131
/** Predicate which returns true if the object is an internal [[StateObject]] object */
129132
static isState = (obj: any): obj is StateObject => isObject(obj['__stateObjectCache']);
130133

@@ -181,7 +184,7 @@ export class StateObject {
181184
const inherited = (opts.inherit && this.parent && this.parent.parameters()) || [];
182185
return inherited
183186
.concat(values(this.params))
184-
.filter(param => !opts.matchingKeys || opts.matchingKeys.hasOwnProperty(param.id));
187+
.filter((param) => !opts.matchingKeys || opts.matchingKeys.hasOwnProperty(param.id));
185188
}
186189

187190
/**

src/url/urlRule.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
/** @packageDocumentation @publicapi @module url */
2+
import { StateDeclaration } from '../state';
23
import { UrlMatcher } from './urlMatcher';
34
import { isString, isDefined, isFunction } from '../common/predicates';
45
import { UIRouter } from '../router';
56
import { identity, extend } from '../common/common';
6-
import { is, pattern } from '../common/hof';
7+
import { is, or, pattern } from '../common/hof';
78
import { StateObject } from '../state/stateObject';
89
import { RawParams } from '../params/interface';
910
import {
@@ -29,7 +30,7 @@ import {
2930
* @internalapi
3031
*/
3132
export class UrlRuleFactory {
32-
static isUrlRule = obj => obj && ['type', 'match', 'handler'].every(key => isDefined(obj[key]));
33+
static isUrlRule = (obj) => obj && ['type', 'match', 'handler'].every((key) => isDefined(obj[key]));
3334

3435
constructor(public router: UIRouter) {}
3536

@@ -38,14 +39,14 @@ export class UrlRuleFactory {
3839
}
3940

4041
create(
41-
what: string | UrlMatcher | StateObject | RegExp | UrlRuleMatchFn,
42+
what: string | UrlMatcher | StateObject | StateDeclaration | RegExp | UrlRuleMatchFn,
4243
handler?: string | UrlRuleHandlerFn
4344
): UrlRule {
44-
const isState = StateObject.isState;
45+
const { isState, isStateDeclaration } = StateObject;
4546
const makeRule = pattern([
4647
[isString, (_what: string) => makeRule(this.compile(_what))],
4748
[is(UrlMatcher), (_what: UrlMatcher) => this.fromUrlMatcher(_what, handler)],
48-
[isState, (_what: StateObject) => this.fromState(_what, this.router)],
49+
[or(isState, isStateDeclaration), (_what: StateObject | StateDeclaration) => this.fromState(_what, this.router)],
4950
[is(RegExp), (_what: RegExp) => this.fromRegExp(_what, handler)],
5051
[isFunction, (_what: UrlRuleMatchFn) => new BaseUrlRule(_what, handler as UrlRuleHandlerFn)],
5152
]);
@@ -107,9 +108,9 @@ export class UrlRuleFactory {
107108
// - Some optional parameters, some matched
108109
// - Some optional parameters, all matched
109110
function matchPriority(params: RawParams): number {
110-
const optional = urlMatcher.parameters().filter(param => param.isOptional);
111+
const optional = urlMatcher.parameters().filter((param) => param.isOptional);
111112
if (!optional.length) return 0.000001;
112-
const matched = optional.filter(param => params[param.id]);
113+
const matched = optional.filter((param) => params[param.id]);
113114
return matched.length / optional.length;
114115
}
115116

@@ -128,7 +129,9 @@ export class UrlRuleFactory {
128129
* // Starts a transition to 'foo' with params: { fooId: '123', barId: '456' }
129130
* ```
130131
*/
131-
fromState(state: StateObject, router: UIRouter): StateRule {
132+
fromState(stateOrDecl: StateObject | StateDeclaration, router: UIRouter): StateRule {
133+
const state = StateObject.isStateDeclaration(stateOrDecl) ? stateOrDecl.$$state() : stateOrDecl;
134+
132135
/**
133136
* Handles match by transitioning to matched state
134137
*
@@ -213,7 +216,7 @@ export class BaseUrlRule implements UrlRule {
213216
_group: number;
214217
type: UrlRuleType = 'RAW';
215218
handler: UrlRuleHandlerFn;
216-
matchPriority = match => 0 - this.$id;
219+
matchPriority = (match) => 0 - this.$id;
217220

218221
constructor(public match: UrlRuleMatchFn, handler?: UrlRuleHandlerFn) {
219222
this.handler = handler || identity;

test/urlRuleSpec.ts

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { StateDeclaration, UIRouter, UrlMatcher, UrlRule } from '../src';
2+
import { UrlRuleFactory } from '../src/url';
3+
import { TestingPlugin } from './_testingPlugin';
4+
5+
const setup = () => {
6+
const router = new UIRouter();
7+
router.plugin(TestingPlugin);
8+
return new UrlRuleFactory(router);
9+
};
10+
11+
describe('UrlRuleFactory', () => {
12+
it('.compile() should create a UrlMatcher from a string', () => {
13+
const factory = setup();
14+
const rule: UrlMatcher = factory.compile('/foo/bar/baz');
15+
expect(rule instanceof UrlMatcher).toBeTruthy();
16+
expect(rule.exec('/foo/bar/baz')).toBeTruthy();
17+
});
18+
19+
describe('.create()', () => {
20+
it('should create a UrlRule from a string', () => {
21+
const factory = setup();
22+
const rule: UrlRule = factory.create('/foo/bar/baz');
23+
expect(rule.type).toBe('URLMATCHER');
24+
expect(rule.match({ path: '/foo/bar/baz' })).toBeTruthy();
25+
expect(rule.match({ path: '/nope/bar/baz' })).toBeFalsy();
26+
});
27+
28+
it('should create a UrlRule from a UrlMatcher', () => {
29+
const factory = setup();
30+
const matcher: UrlMatcher = factory.compile('/foo/bar/baz');
31+
const rule = factory.create(matcher);
32+
expect(rule.type).toBe('URLMATCHER');
33+
expect(rule.match({ path: '/foo/bar/baz' })).toBeTruthy();
34+
expect(rule.match({ path: '/nope/bar/baz' })).toBeFalsy();
35+
});
36+
37+
it('should create a UrlRule from a StateObject', () => {
38+
const factory = setup();
39+
const { stateRegistry } = factory.router;
40+
41+
const stateDecl: StateDeclaration = { name: 'state', url: '/foo/bar/baz' };
42+
stateRegistry.register(stateDecl);
43+
44+
const rule = factory.create(stateRegistry.get('state').$$state());
45+
expect(rule.type).toBe('STATE');
46+
expect(rule.match({ path: '/foo/bar/baz' })).toBeTruthy();
47+
expect(rule.match({ path: '/nope/bar/baz' })).toBeFalsy();
48+
});
49+
50+
it('should create a UrlRule from a StateDeclaration', () => {
51+
const factory = setup();
52+
const { stateRegistry } = factory.router;
53+
54+
const stateDecl: StateDeclaration = { name: 'state', url: '/foo/bar/baz' };
55+
stateRegistry.register(stateDecl);
56+
57+
const rule = factory.create(stateRegistry.get('state'));
58+
expect(rule.type).toBe('STATE');
59+
expect(rule.match({ path: '/foo/bar/baz' })).toBeTruthy();
60+
expect(rule.match({ path: '/nope/bar/baz' })).toBeFalsy();
61+
});
62+
63+
it('should create a UrlRule from a RegExp', () => {
64+
const factory = setup();
65+
const rule = factory.create(new RegExp('/foo/bar/baz'));
66+
expect(rule.type).toBe('REGEXP');
67+
expect(rule.match({ path: '/foo/bar/baz' })).toBeTruthy();
68+
expect(rule.match({ path: '/nope/bar/baz' })).toBeFalsy();
69+
});
70+
71+
it('should create a UrlRule from a UrlRuleMatchFn', () => {
72+
const factory = setup();
73+
const rule = factory.create((url) => url.path === '/foo/bar/baz');
74+
expect(rule.type).toBe('RAW');
75+
expect(rule.match({ path: '/foo/bar/baz' })).toBeTruthy();
76+
expect(rule.match({ path: '/nope/bar/baz' })).toBeFalsy();
77+
});
78+
});
79+
});

0 commit comments

Comments
 (0)