ui: Create and use collapsible notices component (#10270)

* Create and use collapsible notices

* Refactor collapsible-notices

* Split up the topology acceptance tests

* Add acceptance tests for tproxy notices

* Add component file

* Adds additional TProxy notices tests

* Adds conditional to only show collapsable if more than 2 notices are present

* Adds changelog

* Refactorting the conditonal for collapsing the notices

* Renaming undefinedIntention to be notDefinedIntention

* Refactor tests
This commit is contained in:
Kenia 2021-05-25 11:02:38 -04:00 committed by GitHub
parent 7e4ea16149
commit e78e9de396
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 419 additions and 68 deletions

3
.changelog/10270.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ui: Create a collapsible notices component for the Topology tab
```

View File

@ -0,0 +1,66 @@
# CollapsibleNotices
Used as a wrapper to collapse the details of `<Notices/>`.
```hbs preview-template
<CollapsibleNotices>
<Notice
@type="error"
role="alert"
as |notice|>
<notice.Header>
<h3>Header</h3>
</notice.Header>
<notice.Body>
<p>
Body
</p>
</notice.Body>
</Notice>
<Notice
@type="info"
as |notice|>
<notice.Header>
<h3>Header</h3>
</notice.Header>
<notice.Body>
<p>
Body
</p>
</notice.Body>
<notice.Footer>
<p>
Footer
</p>
</notice.Footer>
</Notice>
<Notice
@type="warning"
as |notice|>
<notice.Header>
<h3>Header</h3>
</notice.Header>
<notice.Body>
<p>
Body
</p>
</notice.Body>
<notice.Footer>
<p>
Footer
</p>
</notice.Footer>
</Notice>
</CollapsibleNotices>
```
## Arguments
No arguments required. Wrap this component around the needed notices.
## See
- [Template Source Code](./index.hbs)
---

View File

@ -0,0 +1,10 @@
<div class="collapsible-notices {{if this.collapsed 'collapsed'}}">
<div class="notices">
{{yield}}
</div>
{{#if this.collapsed}}
<button type="button" class="expand" {{on 'click' (set this 'collapsed' false)}}>{{t "components.app.collapsible-notices.expand"}}</button>
{{else}}
<button type="button" class="collapse" {{on 'click' (set this 'collapsed' true)}}>{{t "components.app.collapsible-notices.collapse"}}</button>
{{/if}}
</div>

View File

@ -0,0 +1,3 @@
import Component from '@glimmer/component';
export default class CollapsibleNotices extends Component {}

View File

@ -0,0 +1,31 @@
.collapsible-notices {
display: grid;
grid-template-columns: auto 168px;;
grid-template-rows: auto 55px;
grid-template-areas:
"notices notices"
". toggle-button";
&.collapsed p {
display: none;
}
.notices {
grid-area: notices;
:last-child {
margin-bottom: 0;
}
}
button {
@extend %button;
color: $color-action;
float: right;
grid-area: toggle-button;
margin-top: 1em;
margin-bottom: 2em;
}
button.expand::before {
@extend %with-chevron-down-mask, %as-pseudo;
}
button.collapse::before {
@extend %with-chevron-up-mask, %as-pseudo;
}
}

View File

@ -19,23 +19,27 @@ export default class Topology extends Model {
@attr() Downstreams; // Service[], @attr() Downstreams; // Service[],
@attr() meta; // {} @attr() meta; // {}
@computed('Upstreams', 'Downstreams') @computed('Downstreams')
get undefinedIntention() { get notDefinedIntention() {
let undefinedUpstream = false;
let undefinedDownstream = false; let undefinedDownstream = false;
undefinedUpstream =
this.Upstreams.filter(
item =>
item.Source === 'specific-intention' && !item.TransparentProxy && item.Intention.Allowed
).length !== 0;
undefinedDownstream = undefinedDownstream =
this.Downstreams.filter( this.Downstreams.filter(
item => item =>
item.Source === 'specific-intention' && !item.TransparentProxy && item.Intention.Allowed item.Source === 'specific-intention' && !item.TransparentProxy && item.Intention.Allowed
).length !== 0; ).length !== 0;
return undefinedUpstream || undefinedDownstream; return undefinedDownstream;
}
@computed('FilteredByACL', 'DefaultAllow', 'WildcardIntention', 'notDefinedIntention')
get collapsible() {
if (this.DefaultAllow && this.FilteredByACLs && this.notDefinedIntention) {
return true;
} else if (this.WildcardIntention && this.FilteredByACLs && this.notDefinedIntention) {
return true;
}
return false;
} }
} }

View File

@ -73,6 +73,7 @@
@import 'consul-ui/components/consul/auth-method'; @import 'consul-ui/components/consul/auth-method';
@import 'consul-ui/components/role-selector'; @import 'consul-ui/components/role-selector';
@import 'consul-ui/components/collapsible-notices';
@import 'consul-ui/components/topology-metrics'; @import 'consul-ui/components/topology-metrics';
@import 'consul-ui/components/topology-metrics/card'; @import 'consul-ui/components/topology-metrics/card';
@import 'consul-ui/components/topology-metrics/source-type'; @import 'consul-ui/components/topology-metrics/source-type';

View File

@ -22,8 +22,11 @@ as |route|>
</BlockSlot> </BlockSlot>
</EmptyState> </EmptyState>
{{else}} {{else}}
{{#if topology.collapsible}}
<CollapsibleNotices>
{{#if topology.FilteredByACLs}} {{#if topology.FilteredByACLs}}
<TopologyMetrics::Notice <TopologyMetrics::Notice
data-test-notice='filtered-by-acls'
@type="info" @type="info"
@for="limited-access" @for="limited-access"
@action={{false}} @action={{false}}
@ -31,6 +34,7 @@ as |route|>
{{/if}} {{/if}}
{{#if topology.DefaultAllow}} {{#if topology.DefaultAllow}}
<TopologyMetrics::Notice <TopologyMetrics::Notice
data-test-notice='default-allow'
@type="warning" @type="warning"
@for="default-allow" @for="default-allow"
@internal={{true}} @internal={{true}}
@ -39,14 +43,16 @@ as |route|>
{{/if}} {{/if}}
{{#if topology.WildcardIntention}} {{#if topology.WildcardIntention}}
<TopologyMetrics::Notice <TopologyMetrics::Notice
data-test-notice='wildcard-intention'
@type="warning" @type="warning"
@for="wildcard-intention" @for="wildcard-intention"
@internal={{true}} @internal={{true}}
@action={{true}} @action={{true}}
/> />
{{/if}} {{/if}}
{{#if topology.undefinedIntention}} {{#if topology.notDefinedIntention}}
<TopologyMetrics::Notice <TopologyMetrics::Notice
data-test-notice='not-defined-intention'
@type="warning" @type="warning"
@for="not-defined-intention" @for="not-defined-intention"
@link="{{env 'CONSUL_DOCS_URL'}}/connect/registration/service-registration#upstreams" @link="{{env 'CONSUL_DOCS_URL'}}/connect/registration/service-registration#upstreams"
@ -54,11 +60,53 @@ as |route|>
@action={{true}} @action={{true}}
/> />
{{/if}} {{/if}}
</CollapsibleNotices>
{{else}}
{{#if topology.FilteredByACLs}}
<TopologyMetrics::Notice
data-test-notice='filtered-by-acls'
@type="info"
@for="limited-access"
@action={{false}}
/>
{{/if}}
{{#if topology.DefaultAllow}}
<TopologyMetrics::Notice
data-test-notice='default-allow'
@type="warning"
@for="default-allow"
@internal={{true}}
@action={{true}}
/>
{{/if}}
{{#if topology.WildcardIntention}}
<TopologyMetrics::Notice
data-test-notice='wildcard-intention'
@type="warning"
@for="wildcard-intention"
@internal={{true}}
@action={{true}}
/>
{{/if}}
{{#if topology.notDefinedIntention}}
<TopologyMetrics::Notice
data-test-notice='not-defined-intention'
@type="warning"
@for="not-defined-intention"
@link="{{env 'CONSUL_DOCS_URL'}}/connect/registration/service-registration#upstreams"
@internal={{false}}
@action={{true}}
/>
{{/if}}
{{/if}}
<TopologyMetrics <TopologyMetrics
@nspace={{nspace}} @nspace={{nspace}}
@dc={{dc.Name}} @dc={{dc.Name}}
@service={{items.firstObject}} @service={{items.firstObject}}
@topology={{topology}} @topology={{topology}}
@metricsHref={{render-template urls.service (hash @metricsHref={{render-template urls.service (hash
Datacenter=dc.Name Datacenter=dc.Name
Service=items.firstObject Service=items.firstObject

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / services / show / topology: Intention Create Feature: dc / services / show / topology / intentions
Background: Background:
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
And the local datacenter is "datacenter" And the local datacenter is "datacenter"
@ -27,27 +27,7 @@ Feature: dc / services / show / topology: Intention Create
Datacenter: datacenter Datacenter: datacenter
Intention: {} Intention: {}
--- ---
Scenario: Metrics is not enabled with prometheus provider
When I visit the service page for yaml
---
dc: datacenter
service: web
---
And I don't see the "[data-test-sparkline]" element
Scenario: Metrics is enabled with prometheus provider
Given 1 datacenter model with the value "datacenter"
And the local datacenter is "datacenter"
And ui_config from yaml
---
metrics_proxy_enabled: true
metrics_provider: 'prometheus'
---
When I visit the service page for yaml
---
dc: datacenter
service: web
---
And I see the "[data-test-sparkline]" element
Scenario: Allow a connection between service and upstream by saving an intention Scenario: Allow a connection between service and upstream by saving an intention
When I visit the service page for yaml When I visit the service page for yaml
--- ---
@ -67,4 +47,3 @@ Feature: dc / services / show / topology: Intention Create
When I click ".consul-topology-metrics [data-test-action]" When I click ".consul-topology-metrics [data-test-action]"
And I click ".consul-topology-metrics [data-test-confirm]" And I click ".consul-topology-metrics [data-test-confirm]"
And "[data-notification]" has the "error" class And "[data-notification]" has the "error" class

View File

@ -0,0 +1,50 @@
@setupApplicationTest
Feature: dc / services / show / topology / metrics
Background:
Given 1 datacenter model with the value "datacenter"
And the local datacenter is "datacenter"
And 1 intention model from yaml
---
SourceNS: default
SourceName: web
DestinationNS: default
DestinationName: db
ID: intention-id
---
And 1 node model
And 1 service model from yaml
---
- Service:
Name: web
Kind: ~
---
And 1 topology model from yaml
---
Downstreams: []
Upstreams:
- Name: db
Namespace: default
Datacenter: datacenter
Intention: {}
---
Scenario: Metrics is not enabled with prometheus provider
When I visit the service page for yaml
---
dc: datacenter
service: web
---
And I don't see the "[data-test-sparkline]" element
Scenario: Metrics is enabled with prometheus provider
Given 1 datacenter model with the value "datacenter"
And the local datacenter is "datacenter"
And ui_config from yaml
---
metrics_proxy_enabled: true
metrics_provider: 'prometheus'
---
When I visit the service page for yaml
---
dc: datacenter
service: web
---
And I see the "[data-test-sparkline]" element

View File

@ -0,0 +1,101 @@
@setupApplicationTest
Feature: dc / services / show / topology / tproxy
Background:
Given 1 datacenter model with the value "datacenter"
And the local datacenter is "datacenter"
And 1 intention model from yaml
---
SourceNS: default
SourceName: web
DestinationNS: default
DestinationName: db
ID: intention-id
---
And 1 node model
And 1 service model from yaml
---
- Service:
Name: web
Kind: ~
---
Scenario: Deafult allow is set to true
Given 1 topology model from yaml
---
FilteredByACLs: false
TransparentProxy: false
DefaultAllow: true
WildcardIntention: false
---
When I visit the service page for yaml
---
dc: datacenter
service: web
---
Then the url should be /datacenter/services/web/topology
And I see the tabs.topologyTab.defaultAllowNotice object
Scenario: WildcardIntetions and FilteredByACLs are set to true
Given 1 topology model from yaml
---
FilteredByACLs: true
TransparentProxy: false
DefaultAllow: false
WildcardIntention: true
---
When I visit the service page for yaml
---
dc: datacenter
service: web
---
Then the url should be /datacenter/services/web/topology
And I see the tabs.topologyTab.filteredByACLs object
And I see the tabs.topologyTab.wildcardIntention object
Scenario: TProxy for a downstream is set to false
Given 1 topology model from yaml
---
FilteredByACLs: false
TransparentProxy: false
DefaultAllow: false
WildcardIntention: false
Downstreams:
- Name: db
Namespace: default
Datacenter: datacenter
Intention:
Allowed: true
Source: specific-intention
TransparentProxy: false
---
When I visit the service page for yaml
---
dc: datacenter
service: web
---
Then the url should be /datacenter/services/web/topology
And I see the tabs.topologyTab.notDefinedIntention object
Scenario: TProxy for a downstream is set to true
Given 1 topology model from yaml
---
FilteredByACLs: false
TransparentProxy: false
DefaultAllow: false
WildcardIntention: false
Downstreams:
- Name: db
Namespace: default
Datacenter: datacenter
Intention:
Allowed: true
Source: specific-intention
TransparentProxy: true
---
When I visit the service page for yaml
---
dc: datacenter
service: web
---
Then the url should be /datacenter/services/web/topology
And I don't see the tabs.topologyTab.notDefinedIntention object

View File

@ -1,4 +1,4 @@
import steps from '../../../steps'; import steps from '../../../../steps';
// step definitions that are shared between features should be moved to the // step definitions that are shared between features should be moved to the
// tests/acceptance/steps/steps.js file // tests/acceptance/steps/steps.js file

View File

@ -0,0 +1,10 @@
import steps from '../../../../steps';
// step definitions that are shared between features should be moved to the
// tests/acceptance/steps/steps.js file
export default function(assert) {
return steps(assert).then('I should find a file', function() {
assert.ok(true, this.step);
});
}

View File

@ -0,0 +1,10 @@
import steps from '../../../../steps';
// step definitions that are shared between features should be moved to the
// tests/acceptance/steps/steps.js file
export default function(assert) {
return steps(assert).then('I should find a file', function() {
assert.ok(true, this.step);
});
}

View File

@ -157,7 +157,16 @@ export default {
) )
), ),
service: create( service: create(
service(visitable, clickable, attribute, collection, text, consulIntentionList, tabgroup) service(
visitable,
clickable,
attribute,
isPresent,
collection,
text,
consulIntentionList,
tabgroup
)
), ),
instance: create( instance: create(
instance( instance(

View File

@ -1,4 +1,13 @@
export default function(visitable, clickable, attribute, collection, text, intentions, tabs) { export default function(
visitable,
clickable,
attribute,
isPresent,
collection,
text,
intentions,
tabs
) {
const page = { const page = {
visit: visitable('/:dc/services/:service'), visit: visitable('/:dc/services/:service'),
externalSource: attribute('data-test-external-source', '[data-test-external-source]', { externalSource: attribute('data-test-external-source', '[data-test-external-source]', {
@ -26,6 +35,20 @@ export default function(visitable, clickable, attribute, collection, text, inten
}), }),
intentionList: intentions(), intentionList: intentions(),
}; };
page.tabs.topologyTab = {
defaultAllowNotice: {
see: isPresent('[data-test-notice="default-allow"]'),
},
filteredByACLs: {
see: isPresent('[data-test-notice="filtered-by-acls"]'),
},
wildcardIntention: {
see: isPresent('[data-test-notice="wildcard-intention"]'),
},
notDefinedIntention: {
see: isPresent('[data-test-notice="not-defined-intention"]'),
},
};
page.tabs.upstreamsTab = { page.tabs.upstreamsTab = {
services: collection('.consul-upstream-list > ul > li:not(:first-child)', { services: collection('.consul-upstream-list > ul > li:not(:first-child)', {
name: text('[data-test-service-name]'), name: text('[data-test-service-name]'),

View File

@ -5,3 +5,6 @@ toggle_menu: Toggle Menu
# therefore potentially do not need the word 'navigation' adding to them # therefore potentially do not need the word 'navigation' adding to them
main: Main main: Main
complementary: Complementary complementary: Complementary
collapsible-notices:
expand: Expand Banners
collapse: Collapse Banners