Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ui: Support Route optional parameters/segments #10212

Merged
merged 20 commits into from
May 26, 2021
Merged
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