diff --git a/ui-v2/app/components/consul-lock-session-list/README.mdx b/ui-v2/app/components/consul-lock-session-list/README.mdx
new file mode 100644
index 0000000000..04afe202b9
--- /dev/null
+++ b/ui-v2/app/components/consul-lock-session-list/README.mdx
@@ -0,0 +1,24 @@
+## ConsulLockSessionList
+
+```
+
+```
+
+A presentational component for rendering Node Lock Sessions
+
+### Arguments
+
+| Argument/Attribute | Type | Default | Description |
+| --- | --- | --- | --- |
+| `items` | `array` | | An array of Node Lock Sessions |
+| `onInvalidate` | `function` | | An action to confirm when the `Invalidate` action is clicked and confirmed |
+
+### See
+
+- [Component Source Code](./index.js)
+- [Template Source Code](./index.hbs)
+
+---
diff --git a/ui-v2/app/components/consul-lock-session-list/index.hbs b/ui-v2/app/components/consul-lock-session-list/index.hbs
new file mode 100644
index 0000000000..0076c5c321
--- /dev/null
+++ b/ui-v2/app/components/consul-lock-session-list/index.hbs
@@ -0,0 +1,74 @@
+{{#if (gt items.length 0)}}
+
+
+ {{item.Name}}
+
+
+
+ -
+
+
+ - {{item.ID}}
+
+
+ -
+
+ Delay
+
+
+ - {{format-time item.LockDelay}}
+
+
+ -
+
+ TTL
+
+
+ - {{item.TTL}}
+
+
+ -
+
+ Behavior
+
+
+ - {{item.Behavior}}
+
+
+ -
+
+ Checks
+
+
+ -
+ {{#each item.Checks as |item|}}
+ {{item}}
+ {{/each}}
+
+
+
+
+
+
+
+
+
+
+ {{message}}
+
+
+
+
+
+
+
+{{/if}}
\ No newline at end of file
diff --git a/ui-v2/app/components/consul-lock-session-list/index.js b/ui-v2/app/components/consul-lock-session-list/index.js
new file mode 100644
index 0000000000..4798652642
--- /dev/null
+++ b/ui-v2/app/components/consul-lock-session-list/index.js
@@ -0,0 +1,5 @@
+import Component from '@ember/component';
+
+export default Component.extend({
+ tagName: '',
+});
diff --git a/ui-v2/app/helpers/format-time.js b/ui-v2/app/helpers/format-time.js
new file mode 100644
index 0000000000..5f0fa8ddb1
--- /dev/null
+++ b/ui-v2/app/helpers/format-time.js
@@ -0,0 +1,29 @@
+import { helper } from '@ember/component/helper';
+
+export default helper(function formatTime([params], hash) {
+ let day, hour, minute, seconds;
+ seconds = Math.floor(params / 1000);
+ minute = Math.floor(seconds / 60);
+ seconds = seconds % 60;
+ hour = Math.floor(minute / 60);
+ minute = minute % 60;
+ day = Math.floor(hour / 24);
+ hour = hour % 24;
+ const time = {
+ day: day,
+ hour: hour,
+ minute: minute,
+ seconds: seconds,
+ };
+
+ switch (true) {
+ case time.day !== 0:
+ return time.day + 'd';
+ case time.hour !== 0:
+ return time.hour + 'h';
+ case time.minute !== 0:
+ return time.minute + 'm';
+ default:
+ return time.seconds + 's';
+ }
+});
diff --git a/ui-v2/app/styles/base/icons/base-variables.scss b/ui-v2/app/styles/base/icons/base-variables.scss
index 8dd1eb6f64..d3e2e48d97 100644
--- a/ui-v2/app/styles/base/icons/base-variables.scss
+++ b/ui-v2/app/styles/base/icons/base-variables.scss
@@ -39,6 +39,7 @@ $consul-logo-color-svg: url('data:image/svg+xml;charset=UTF-8,');
$copy-success-svg: url('data:image/svg+xml;charset=UTF-8,');
$database-svg: url('data:image/svg+xml;charset=UTF-8,');
+$delay-svg: url('data:image/svg+xml;charset=UTF-8,');
$deny-alt-svg: url('data:image/svg+xml;charset=UTF-8,');
$deny-color-svg: url('data:image/svg+xml;charset=UTF-8,');
$deny-default-svg: url('data:image/svg+xml;charset=UTF-8,');
diff --git a/ui-v2/app/styles/base/icons/icon-placeholders.scss b/ui-v2/app/styles/base/icons/icon-placeholders.scss
index bde82cc049..907ab91d63 100644
--- a/ui-v2/app/styles/base/icons/icon-placeholders.scss
+++ b/ui-v2/app/styles/base/icons/icon-placeholders.scss
@@ -398,6 +398,16 @@
mask-image: $database-svg;
}
+%with-delay-icon {
+ @extend %with-icon;
+ background-image: $delay-svg;
+}
+%with-delay-mask {
+ @extend %with-mask;
+ -webkit-mask-image: $delay-svg;
+ mask-image: $delay-svg;
+}
+
%with-deny-alt-icon {
@extend %with-icon;
background-image: $deny-alt-svg;
diff --git a/ui-v2/app/styles/components/composite-row.scss b/ui-v2/app/styles/components/composite-row.scss
index 956141a8ec..7ffa997197 100644
--- a/ui-v2/app/styles/components/composite-row.scss
+++ b/ui-v2/app/styles/components/composite-row.scss
@@ -13,6 +13,9 @@
.consul-role-list > ul > li:not(:first-child) {
@extend %with-composite-row-intent;
}
+.consul-lock-session-list ul > li:not(:first-child) {
+ @extend %with-one-action-row;
+}
/*TODO: This hides the icons-less dt's in the below lists as */
/* they don't have tooltips */
.consul-nspace-list > ul > li:not(:first-child) dt,
diff --git a/ui-v2/app/styles/components/composite-row/layout.scss b/ui-v2/app/styles/components/composite-row/layout.scss
index ae4f1b8ee3..2f08ea6021 100644
--- a/ui-v2/app/styles/components/composite-row/layout.scss
+++ b/ui-v2/app/styles/components/composite-row/layout.scss
@@ -12,6 +12,20 @@
/* whilst this isn't in the designs this makes our temporary rollover look better */
padding-left: 12px;
}
+%with-one-action-row {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ grid-template-rows: 50% 50%;
+
+ // only one action applies to these rows
+ grid-template-areas:
+ 'header actions'
+ 'detail actions';
+
+ padding-top: 10px;
+ padding-bottom: 10px;
+ padding-right: 12px;
+}
%composite-row-header {
grid-area: header;
align-self: start;
diff --git a/ui-v2/app/styles/components/composite-row/skin.scss b/ui-v2/app/styles/components/composite-row/skin.scss
index 51045dd419..6bf4f212bc 100644
--- a/ui-v2/app/styles/components/composite-row/skin.scss
+++ b/ui-v2/app/styles/components/composite-row/skin.scss
@@ -106,6 +106,22 @@
@extend %with-protocol-mask, %as-pseudo;
background-color: $gray-500;
}
+%composite-row-detail dl.lock-delay dt::before {
+ @extend %with-delay-mask, %as-pseudo;
+ background-color: $gray-500;
+}
+%composite-row-detail dl.ttl dt::before {
+ @extend %with-history-mask, %as-pseudo;
+ background-color: $gray-500;
+}
+%composite-row-detail dl.behavior dt::before {
+ @extend %with-info-circle-outline-mask, %as-pseudo;
+ background-color: $gray-500;
+}
+%composite-row-detail dl.checks dt::before {
+ @extend %with-health-mask, %as-pseudo;
+ background-color: $gray-500;
+}
// In this case we do not need a background on the icon
%composite-row .combined-address .copy-button button:hover,
%composite-row-detail dt .copy-button button:hover {
diff --git a/ui-v2/app/templates/dc/nodes/show/sessions.hbs b/ui-v2/app/templates/dc/nodes/show/sessions.hbs
index 6a39b5e6e1..85ac9ae9c4 100644
--- a/ui-v2/app/templates/dc/nodes/show/sessions.hbs
+++ b/ui-v2/app/templates/dc/nodes/show/sessions.hbs
@@ -1,65 +1,15 @@
{{#if (gt sessions.length 0)}}
-
-
- Name |
- ID |
- Delay |
- TTL |
- Behavior |
- Checks |
- |
-
-
-
- {{item.Name}}
- |
-
- {{item.ID}}
- |
-
- {{item.LockDelay}}
- |
-
- {{item.TTL}}
- |
-
- {{item.Behavior}}
- |
-
-{{#if (gt item.Checks.length 0)}}
- {{ join ', ' item.Checks}}
-{{/if}}
- |
-
-
-
-
-
-
-
- {{message}}
-
-
-
-
-
- |
-
-
+
{{else}}
-
-
-
- There are no Lock Sessions for this Node. For more information, view our documentation
-
-
-
+
+
+
+ There are no Lock Sessions for this Node. For more information, view our documentation
+
+
+
{{/if}}
diff --git a/ui-v2/tests/acceptance/dc/nodes/sessions/list.feature b/ui-v2/tests/acceptance/dc/nodes/sessions/list.feature
index efcc9c1e4d..ba36a22056 100644
--- a/ui-v2/tests/acceptance/dc/nodes/sessions/list.feature
+++ b/ui-v2/tests/acceptance/dc/nodes/sessions/list.feature
@@ -26,3 +26,26 @@ Feature: dc / nodes / sessions / list: List Lock Sessions
- 30s
- 60m
---
+ Scenario: Given 2 session with LockDelay in milliseconds
+ Given 1 datacenter model with the value "dc1"
+ And 1 node model from yaml
+ ---
+ ID: node-0
+ ---
+ And 2 session models from yaml
+ ---
+ - LockDelay: 120000
+ - LockDelay: 18000000
+ ---
+ When I visit the node page for yaml
+ ---
+ dc: dc1
+ node: node-0
+ ---
+ And I click lockSessions on the tabs
+ Then I see lockSessionsIsSelected on the tabs
+ Then I see delay on the sessions like yaml
+ ---
+ - 2m
+ - 5h
+ ---
diff --git a/ui-v2/tests/integration/components/consul-lock-session-list-test.js b/ui-v2/tests/integration/components/consul-lock-session-list-test.js
new file mode 100644
index 0000000000..ddc5057ebd
--- /dev/null
+++ b/ui-v2/tests/integration/components/consul-lock-session-list-test.js
@@ -0,0 +1,26 @@
+import { module, skip } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | consul-lock-session-list', function(hooks) {
+ setupRenderingTest(hooks);
+
+ skip('it renders', async function(assert) {
+ // Set any properties with this.set('myProperty', 'value');
+ // Handle any actions with this.set('myAction', function(val) { ... });
+
+ await render(hbs``);
+
+ assert.equal(this.element.textContent.trim(), '');
+
+ // Template block usage:
+ await render(hbs`
+
+ template block text
+
+ `);
+
+ assert.equal(this.element.textContent.trim(), 'template block text');
+ });
+});
diff --git a/ui-v2/tests/integration/helpers/format-time-test.js b/ui-v2/tests/integration/helpers/format-time-test.js
new file mode 100644
index 0000000000..127d89f21a
--- /dev/null
+++ b/ui-v2/tests/integration/helpers/format-time-test.js
@@ -0,0 +1,17 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Helper | format-time', function(hooks) {
+ setupRenderingTest(hooks);
+
+ // Replace this with your real tests.
+ test('it renders', async function(assert) {
+ this.set('inputValue', '7200000');
+
+ await render(hbs`{{format-time inputValue}}`);
+
+ assert.equal(this.element.textContent.trim(), '2h');
+ });
+});
diff --git a/ui-v2/tests/pages/dc/nodes/show.js b/ui-v2/tests/pages/dc/nodes/show.js
index a5afb1c9f5..d3780143a5 100644
--- a/ui-v2/tests/pages/dc/nodes/show.js
+++ b/ui-v2/tests/pages/dc/nodes/show.js
@@ -16,12 +16,12 @@ export default function(visitable, deletable, clickable, attribute, collection,
port: attribute('data-test-service-port', '[data-test-service-port]'),
externalSource: attribute('data-test-external-source', '[data-test-external-source]'),
}),
- sessions: collection(
- '#lock-sessions [data-test-tabular-row]',
- deletable({
- TTL: attribute('data-test-session-ttl', '[data-test-session-ttl]'),
- })
- ),
+ sessions: collection('.consul-lock-session-list [data-test-list-row]', {
+ TTL: attribute('data-test-session-ttl', '[data-test-session-ttl]'),
+ delay: text('[data-test-session-delay]'),
+ actions: clickable('label'),
+ ...deletable(),
+ }),
metadata: collection('#metadata [data-test-tabular-row]', {}),
};
}