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: updates info table row jsdoc #20697

Merged
merged 18 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/20697.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: update detail views that render ttl durations to display full unit instead of letter (i.e. 'days' instead of 'd')
```
2 changes: 1 addition & 1 deletion ui/lib/core/addon/components/info-table-row.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
{{else if @formatDate}}
{{date-format @value @formatDate}}
{{else if @formatTtl}}
{{this.formattedTtl}}
{{format-duration @value}}
{{else}}
{{#if (eq @type "array")}}
<InfoTableItemArray
Expand Down
38 changes: 16 additions & 22 deletions ui/lib/core/addon/components/info-table-row.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { typeOf } from '@ember/utils';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { convertFromSeconds, largestUnitFromSeconds } from 'core/utils/duration-utils';

/**
* @module InfoTableRow
Expand All @@ -19,19 +18,22 @@ import { convertFromSeconds, largestUnitFromSeconds } from 'core/utils/duration-
* <InfoTableRow @value={{5}} @label="TTL" @helperText="Some description"/>
* ```
*
* @param label=null {string} - The display name for the value.
* @param helperText=null {string} - Text to describe the value displayed beneath the label.
* @param value=null {any} - The the data to be displayed - by default the content of the component will only show if there is a value. Also note that special handling is given to boolean values - they will render `Yes` for true and `No` for false. Overridden by block if exists
* @param [alwaysRender=false] {Boolean} - Indicates if the component content should be always be rendered. When false, the value of `value` will be used to determine if the component should render.
* @param [defaultShown] {String} - Text that renders as value if alwaysRender=true. Eg. "Vault default"
* @param [tooltipText] {String} - Text if a tooltip should display over the value.
* @param [isTooltipCopyable] {Boolean} - Allows tooltip click to copy
* @param [type=array] {string} - The type of value being passed in. This is used for when you want to trim an array. For example, if you have an array value that can equal length 15+ this will trim to show 5 and count how many more are there
* @param [isLink=true] {Boolean} - Passed through to InfoTableItemArray. Indicates if the item should contain a link-to component. Only setup for arrays, but this could be changed if needed.
* @param [modelType=null] {string} - Passed through to InfoTableItemArray. Tells what model you want data for the allOptions to be returned from. Used in conjunction with the the isLink.
* @param [queryParam] {String} - Passed through to InfoTableItemArray. If you want to specific a tab for the View All XX to display to. Ex= role
* @param [backend] {String} - Passed through to InfoTableItemArray. To specify secrets backend to point link to Ex= transformation
* @param [viewAll] {String} - Passed through to InfoTableItemArray. Specify the word at the end of the link View all.
* @param {string} label=null - The display name for the value.
* @param {string} helperText=null - Text to describe the value displayed beneath the label.
* @param {any} value=null - The the data to be displayed - by default the content of the component will only show if there is a value. Also note that special handling is given to boolean values - they will render `Yes` for true and `No` for false. Overridden by block if exists
* @param {boolean} [alwaysRender=false] - Indicates if the component content should be always be rendered. When false, the value of `value` will be used to determine if the component should render.
* @param {string} [defaultShown] - Text that renders as value if alwaysRender=true. Eg. "Vault default"
* @param {string} [tooltipText] - Text if a tooltip should display over the value.
* @param {boolean} [isTooltipCopyable] - Allows tooltip click to copy
* @param {string} [formatDate] - A string of the desired date format that's passed to the date-format helper to render timestamps (ex. "MMM d yyyy, h:mm:ss aaa", see: https://date-fns.org/v2.30.0/docs/format)
* @param {boolean} [formatTtl=false] - When true, value is passed to the format-duration helper, useful for TTL values
* @param {string} [type=array] - The type of value being passed in. This is used for when you want to trim an array. For example, if you have an array value that can equal length 15+ this will trim to show 5 and count how many more are there
* * InfoTableItemArray *
* @param {boolean} [isLink=true] - Passed through to InfoTableItemArray. Indicates if the item should contain a link-to component. Only setup for arrays, but this could be changed if needed.
* @param {string} [modelType=null] - Passed through to InfoTableItemArray. Tells what model you want data for the allOptions to be returned from. Used in conjunction with the the isLink.
* @param {string} [queryParam] - Passed through to InfoTableItemArray. If you want to specific a tab for the View All XX to display to. Ex= role
* @param {string} [backend] - Passed through to InfoTableItemArray. To specify secrets backend to point link to Ex= transformation
* @param {string} [viewAll] - Passed through to InfoTableItemArray. Specify the word at the end of the link View all.
*/

export default class InfoTableRowComponent extends Component {
Expand Down Expand Up @@ -62,14 +64,6 @@ export default class InfoTableRowComponent extends Component {
return false;
}
}
get formattedTtl() {
const { value } = this.args;
if (Number.isInteger(value)) {
const unit = largestUnitFromSeconds(value);
return `${convertFromSeconds(value, unit)}${unit}`;
}
return value;
}

@action
calculateLabelOverflow(el) {
Expand Down
24 changes: 9 additions & 15 deletions ui/lib/core/addon/helpers/format-duration.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,17 @@
import { helper } from '@ember/component/helper';
import { formatDuration, intervalToDuration } from 'date-fns';

export function duration([time], { nullable = false }) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullable = false was only used by transit, which is removed now

// intervalToDuration creates a durationObject that turns the seconds (ex 3600) to respective:
// { years: 0, months: 0, days: 0, hours: 1, minutes: 0, seconds: 0 }
// then formatDuration returns the filled in keys of the durationObject
// nullable if you don't want a value to be returned instead of 0s

if (nullable && (time === '0' || time === 0)) {
return null;
}

export function duration([time]) {
// time must be in seconds
const duration = Number.parseInt(time, 10);
if (isNaN(duration)) {
return time;
// 0 does not always mean 0 seconds, i.e. it can representing using system defaults
if (Number.isInteger(time) && time !== 0) {
const milliseconds = time * 1000;
// pass milliseconds to intervalToDuration returns a durationObject: { years: 0, months: 0, days: 0, hours: 1, minutes: 0, seconds: 6 }
// formatDuration converts to human-readable format: '1 hour 6 seconds'
return formatDuration(intervalToDuration({ start: 0, end: milliseconds }));
}

return formatDuration(intervalToDuration({ start: 0, end: duration * 1000 }));
// to avoid making any assumptions return strings and 0 as-is
return time;
}

export default helper(duration);
2 changes: 1 addition & 1 deletion ui/tests/acceptance/secrets/backend/kv/secret-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ module('Acceptance | secrets/secret/create, read, delete', function (hooks) {
assert.strictEqual(cas.trim(), 'Yes', 'displays the cas set when configuring the secret-engine');
assert.strictEqual(
deleteVersionAfter.trim(),
'1s',
'1 second',
'displays the delete version after set when configuring the secret-engine'
);
await deleteEngine(enginePath, assert);
Expand Down
19 changes: 13 additions & 6 deletions ui/tests/acceptance/settings/mount-secret-backend-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ module('Acceptance | settings/mount-secret-backend', function (hooks) {

hooks.beforeEach(function () {
this.uid = uuidv4();
this.calcDays = (hours) => {
const days = Math.floor(hours / 24);
const remainder = hours % 24;
return `${days} days ${remainder} hours`;
};
return authPage.login();
});

Expand All @@ -32,7 +37,6 @@ module('Acceptance | settings/mount-secret-backend', function (hooks) {
const path = `mount-kv-${this.uid}`;
const defaultTTLHours = 100;
const maxTTLHours = 300;

await page.visit();

assert.strictEqual(currentRouteName(), 'vault.cluster.settings.mount-secret-backend');
Expand All @@ -49,8 +53,8 @@ module('Acceptance | settings/mount-secret-backend', function (hooks) {
.maxTTLVal(maxTTLHours)
.submit();
await configPage.visit({ backend: path });
assert.strictEqual(configPage.defaultTTL, `${defaultTTLHours}h`, 'shows the proper TTL');
assert.strictEqual(configPage.maxTTL, `${maxTTLHours}h`, 'shows the proper max TTL');
assert.strictEqual(configPage.defaultTTL, `${this.calcDays(defaultTTLHours)}`, 'shows the proper TTL');
assert.strictEqual(configPage.maxTTL, `${this.calcDays(maxTTLHours)}`, 'shows the proper max TTL');
});

test('it sets the ttl when enabled then disabled', async function (assert) {
Expand All @@ -67,14 +71,17 @@ module('Acceptance | settings/mount-secret-backend', function (hooks) {
.path(path)
.toggleOptions()
.enableDefaultTtl()
.enableDefaultTtl()
.enableMaxTtl()
.maxTTLUnit('h')
.maxTTLVal(maxTTLHours)
.submit();
await configPage.visit({ backend: path });
assert.strictEqual(configPage.defaultTTL, '0s', 'shows the proper TTL');
assert.strictEqual(configPage.maxTTL, `${maxTTLHours}h`, 'shows the proper max TTL');
assert.strictEqual(
configPage.defaultTTL,
'0',
'shows 0 (with no seconds) which means using the system default TTL'
); // https://developer.hashicorp.com/vault/api-docs/system/mounts#default_lease_ttl-1
assert.strictEqual(configPage.maxTTL, `${this.calcDays(maxTTLHours)}`, 'shows the proper max TTL');
});

test('it sets the max ttl after pki chosen, resets after', async function (assert) {
Expand Down
16 changes: 13 additions & 3 deletions ui/tests/integration/components/info-table-row-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { module, test } from 'qunit';
import { resolve } from 'rsvp';
import Service from '@ember/service';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled, triggerEvent } from '@ember/test-helpers';
import { render, triggerEvent } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

const VALUE = 'test value';
Expand Down Expand Up @@ -277,9 +277,19 @@ module('Integration | Component | InfoTableRow', function (hooks) {
@formatTtl={{true}}
/>`);

assert.dom('[data-test-value-div]').hasText('100m', 'Translates number value to largest unit');
assert
.dom('[data-test-value-div]')
.hasText('1 hour 40 minutes', 'Translates number value to largest unit with carryover of minutes');
});

test('Formats string value as-is when formatTtl present', async function (assert) {
this.set('value', '45m');
await settled();
await render(hbs`<InfoTableRow
@label={{this.label}}
@value={{this.value}}
@formatTtl={{true}}
/>`);

assert.dom('[data-test-value-div]').hasText('45m', 'Renders non-number values as-is');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ module('Integration | Component | pki role details page', function (hooks) {
.dom(SELECTORS.extKeyUsageValue)
.hasText('bar,baz', 'Key usage shows comma-joined values when array has items');
assert.dom(SELECTORS.noStoreValue).containsText('Yes', 'noStore shows opposite of what the value is');
assert.dom(SELECTORS.customTtlValue).containsText('10m', 'TTL shown as duration');
assert.dom(SELECTORS.customTtlValue).containsText('10 minutes', 'TTL shown as duration');
});

test('it should render the notAfter date if present', async function (assert) {
Expand Down
44 changes: 15 additions & 29 deletions ui/tests/integration/helpers/format-duration-test.js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,37 @@

import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { duration } from 'core/helpers/format-duration';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { hbs } from 'ember-cli-htmlbars';

module('Integration | Helper | format-duration', function (hooks) {
setupRenderingTest(hooks);

test('it supports strings and formats seconds', async function (assert) {
await render(hbs`<p data-test-format-duration>Date: {{format-duration '3606'}}</p>`);
test('it formats-duration in template view', async function (assert) {
await render(hbs`<p data-test-format-duration>Date: {{format-duration 3606 }}</p>`);

assert
.dom('[data-test-format-duration]')
.includesText('1 hour 6 seconds', 'it renders the duration in hours and seconds');
});

test('it is able to format seconds and days', async function (assert) {
await render(hbs`<p data-test-format-duration>Date: {{format-duration '93606000'}}</p>`);

assert
.dom('[data-test-format-duration]')
.includesText(
'2 years 11 months 18 days 9 hours 40 minutes',
'it renders with years months and days and hours and minutes'
);
test('it formats seconds', async function (assert) {
assert.strictEqual(duration([3606]), '1 hour 6 seconds');
});

test('it is able to format numbers', async function (assert) {
this.set('number', 60);
await render(hbs`<p data-test-format-duration>Date: {{format-duration this.number}}</p>`);

assert
.dom('[data-test-format-duration]')
.includesText('1 minute', 'it renders duration when a number is passed in.');
test('it format seconds and days', async function (assert) {
assert.strictEqual(duration([93606000]), '2 years 11 months 18 days 9 hours 40 minutes');
});

test('it renders the input if time not found', async function (assert) {
this.set('number', 'arg');

await render(hbs`<p data-test-format-duration>Date: {{format-duration this.number}}</p>`);
assert.dom('[data-test-format-duration]').hasText('Date: arg');
test('it returns the integer 0', async function (assert) {
assert.strictEqual(duration([0]), 0);
});

test('it renders no value if nullable true', async function (assert) {
this.set('number', 0);

await render(hbs`<p data-test-format-duration>Date: {{format-duration this.number nullable=true}}</p>`);
assert.dom('[data-test-format-duration]').hasText('Date:');
test('it returns string inputs', async function (assert) {
this.set('number', 'arg');
assert.strictEqual(duration(['0']), '0');
assert.strictEqual(duration(['arg']), 'arg');
assert.strictEqual(duration(['1245']), '1245');
});
});