Skip to content

Commit

Permalink
ui: Support Route optional parameters/segments (#10212)
Browse files Browse the repository at this point in the history
Moves our URLs with 'optional namespace segment' into a separately abstracted 'optional URL segment' feature
  • Loading branch information
johncowen authored and hc-github-team-consul-core committed May 26, 2021
1 parent 56a165b commit dd280ee
Show file tree
Hide file tree
Showing 63 changed files with 848 additions and 450 deletions.
3 changes: 3 additions & 0 deletions .changelog/10212.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: Add 'optional route segments' and move namespaces to use them
```
4 changes: 0 additions & 4 deletions ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ npm-debug.log*
testem.log
yarn-error.log

# storybook
storybook-static
**/.storybook/*.html

# ember-try
.node_modules.ember-try
bower.json.ember-try
Expand Down
6 changes: 5 additions & 1 deletion ui/packages/consul-ui/app/abilities/nspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export default class NspaceAbility extends BaseAbility {
}

get canChoose() {
return this.env.var('CONSUL_NSPACES_ENABLED') && this.nspaces.length > 0;
return this.canUse && this.nspaces.length > 0;
}

get canUse() {
return this.env.var('CONSUL_NSPACES_ENABLED');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,22 @@ as |item index|>
</Tooltip>
</dd>
</dl>
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (not-eq item.Namespace @nspace))}}
<a data-test-service-name href={{href-to 'nspace.dc.services.show' (concat '~' item.Namespace) @dc item.Name }}>
{{#if (and (can 'use nspaces') (not-eq item.Namespace @nspace))}}
<a
data-test-service-name
href={{href-to 'dc.services.show' @dc item.Name
params=(hash
nspace=item.Namespace
)
}}
>
{{item.Name}}
</a>
{{else}}
{{else}}
<a data-test-service-name href={{href-to 'dc.services.show' item.Name}}>
{{item.Name}}
</a>
{{/if}}
{{/if}}
{{else}}
<p data-test-service-name>
{{item.Name}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<MenuItem
data-test-datacenter-picker
class={{concat (if (eq @dc.Name item.Name) 'is-active') (if item.Local ' is-local') }}
@href={{href-mut (hash dc=item.Name)}}
@href={{href-to '.' params=(hash dc=item.Name)}}
>
<BlockSlot @name="label">
{{item.Name}}
Expand Down Expand Up @@ -81,7 +81,7 @@
{{#each (reject-by 'DeletedAt' nspaces) as |item|}}
<MenuItem
class={{if (eq @nspace.Name item.Name) 'is-active'}}
@href={{href-mut (hash nspace=(concat '~' item.Name))}}
@href={{href-to '.' params=(hash nspace=item.Name)}}
>
<BlockSlot @name="label">
{{item.Name}}
Expand Down
1 change: 1 addition & 0 deletions ui/packages/consul-ui/app/components/route/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ routes.
| export | Type | Default | Description |
| --- | --- | --- | --- |
| `model` | `Object` | `undefined` | Arbitrary hash of data passed down from the parent route/outlet |
| `params` | `Object` | `undefined` | An object/merge of **all** optional route params and normal route params |

```hbs
<Route
Expand Down
3 changes: 2 additions & 1 deletion ui/packages/consul-ui/app/components/route/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
{{/if}}

{{yield (hash
model=model
model=this.model
params=this.params
)}}
4 changes: 4 additions & 0 deletions ui/packages/consul-ui/app/components/route/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export default class RouteComponent extends Component {
return this.args.title;
}

get params() {
return this.routlet.paramsFor(this.args.name);
}

@action
connect() {
this.routlet.addRoute(this.args.name, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<a class="topology-metrics-card"
href={{if
(and (env 'CONSUL_NSPACES_ENABLED') (not-eq @item.Namespace @service.Namespace))
(href-to "nspace.dc.services.show.index" (concat '~' @item.Namespace) @item.Datacenter @item.Name)
(href-to "dc.services.show.index" @item.Datacenter @item.Name params=(hash nspace=@item.Namespace))
(href-to "dc.services.show.index" @item.Name)
}}
data-permission={{service/intention-permissions @item}}
Expand Down
2 changes: 1 addition & 1 deletion ui/packages/consul-ui/app/controllers/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default class ApplicationController extends Controller {
// you potentially have a new namespace
// if you do redirect to it
if (nspace !== this.nspace.Name) {
params.nspace = `~${nspace}`;
params.nspace = `${nspace}`;
}
}
}
Expand Down
18 changes: 0 additions & 18 deletions ui/packages/consul-ui/app/helpers/href-mut.js

This file was deleted.

86 changes: 50 additions & 36 deletions ui/packages/consul-ui/app/helpers/href-to.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,73 @@
/*eslint ember/no-observers: "warn"*/
// TODO: Remove ^
// This helper requires `ember-href-to` for the moment at least
// It's similar code but allows us to check on the type of route
// (dynamic or wildcard) and encode or not depending on the type
import { inject as service } from '@ember/service';
import Helper from '@ember/component/helper';
import { observes } from '@ember-decorators/object';
import { hrefTo as _hrefTo } from 'ember-href-to/helpers/href-to';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { getOwner } from '@ember/application';

import transitionable from 'consul-ui/utils/routing/transitionable';
import wildcard from 'consul-ui/utils/routing/wildcard';

import { routes } from 'consul-ui/router';

const isWildcard = wildcard(routes);
export const hrefTo = function(owned, router, [targetRouteName, ...rest], namedArgs) {
if (isWildcard(targetRouteName)) {
rest = rest.map(function(item, i) {
return item
.split('/')
.map(encodeURIComponent)
.join('/');
});

export const hrefTo = function(container, params, hash = {}) {
// TODO: consider getting this from @service('router')._router which is
// private but we don't need getOwner, and it ensures setupRouter is called
// How private is 'router:main'? If its less private maybe stick with it?
const location = container.lookup('router:main').location;
const router = container.lookup('service:router');

let _params = params.slice(0);
let routeName = _params.shift();
let _hash = hash.params || {};
// a period means use the same routeName we are currently at and therefore
// use transitionable to figure out all the missing params
if (routeName === '.') {
_params = transitionable(router.currentRoute, _hash, container);
// _hash = {};
routeName = _params.shift();
}
if (namedArgs.params) {
return _hrefTo(owned, namedArgs.params);
} else {
// we don't check to see if nspaces are enabled here as routes
// with beginning with 'nspace' only exist if nspaces are enabled

// this globally converts non-nspaced href-to's to nspace aware
// href-to's only if you are within a namespace
const currentRouteName = router.currentRouteName || '';
if (currentRouteName.startsWith('nspace.') && targetRouteName.startsWith('dc.')) {
targetRouteName = `nspace.${targetRouteName}`;

try {
// if the routeName is a wildcard (*) route url encode all of the params
if (isWildcard(routeName)) {
_params = _params.map((item, i) => {
return item
.split('/')
.map(encodeURIComponent)
.join('/');
});
}
return location.hrefTo(routeName, _params, _hash);
} catch (e) {
if (e.constructor === Error) {
e.message = `${e.message} For "${params[0]}:${JSON.stringify(params.slice(1))}"`;
}
return _hrefTo(owned, [targetRouteName, ...rest]);
throw e;
}
};

export default class HrefToHelper extends Helper {
@service('router') router;

init() {
super.init(...arguments);
this.router.on('routeWillChange', this.routeWillChange);
}

compute(params, hash) {
let href;
try {
href = hrefTo(this, this.router, params, hash);
} catch (e) {
e.message = `${e.message} For "${params[0]}:${JSON.stringify(params.slice(1))}"`;
throw e;
}
return href;
return hrefTo(getOwner(this), params, hash);
}

@observes('router.currentURL')
onURLChange() {
@action
routeWillChange(transition) {
this.recompute();
}

willDestroy() {
this.router.off('routeWillChange', this.routeWillChange);
super.willDestroy();
}
}
1 change: 1 addition & 0 deletions ui/packages/consul-ui/app/helpers/is-href.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ export default class IsHrefHelper extends Helper {

willDestroy() {
this.router.off('routeWillChange', this.routeWillChange);
super.willDestroy();
}
}
Loading

0 comments on commit dd280ee

Please sign in to comment.