diff --git a/ui/lib/core/addon/components/ttl-form.js b/ui/lib/core/addon/components/ttl-form.js
new file mode 100644
index 0000000000..99d7a61ef1
--- /dev/null
+++ b/ui/lib/core/addon/components/ttl-form.js
@@ -0,0 +1,104 @@
+/**
+ * @module TtlForm
+ * TtlForm components are used to enter a Time To Live (TTL) input.
+ * This component does not include a label and is designed to take
+ * a time and unit, and pass an object including seconds and
+ * timestring when those two values are changed.
+ *
+ * @example
+ * ```js
+ *
+ * ```
+ * @param {function} onChange - This function will be called when the user changes the value. An object will be passed in as a parameter with values seconds{number}, timeString{string}
+ * @param {number} [time] - Time is the value that will be passed into the value input. Can be null/undefined to start if input is required.
+ * @param {unit} [unit="s"] - This is the unit key which will show by default on the form. Can be one of `s` (seconds), `m` (minutes), `h` (hours), `d` (days)
+ * @param {number} [recalculationTimeout=5000] - This is the time, in milliseconds, that `recalculateSeconds` will be be true after time is updated
+ */
+
+import Ember from 'ember';
+import Component from '@ember/component';
+import { computed } from '@ember/object';
+import { task, timeout } from 'ember-concurrency';
+import layout from '../templates/components/ttl-form';
+
+const secondsMap = {
+ s: 1,
+ m: 60,
+ h: 3600,
+ d: 86400,
+};
+const convertToSeconds = (time, unit) => {
+ return time * secondsMap[unit];
+};
+const convertFromSeconds = (seconds, unit) => {
+ return seconds / secondsMap[unit];
+};
+
+export default Component.extend({
+ layout,
+ time: '',
+ unit: 's',
+
+ /* Used internally */
+ recalculationTimeout: 5000,
+ recalculateSeconds: false,
+ errorMessage: null,
+ unitOptions: computed(function() {
+ return [
+ { label: 'seconds', value: 's' },
+ { label: 'minutes', value: 'm' },
+ { label: 'hours', value: 'h' },
+ { label: 'days', value: 'd' },
+ ];
+ }),
+ handleChange() {
+ let { time, unit, seconds } = this.getProperties('time', 'unit', 'seconds');
+ const ttl = {
+ seconds,
+ timeString: time + unit,
+ };
+ this.onChange(ttl);
+ },
+ keepSecondsRecalculate(newUnit) {
+ const newTime = convertFromSeconds(this.seconds, newUnit);
+ this.setProperties({
+ time: newTime,
+ unit: newUnit,
+ });
+ },
+ updateTime: task(function*(newTime) {
+ this.set('errorMessage', '');
+ let parsedTime;
+ parsedTime = parseInt(newTime, 10);
+ if (!newTime) {
+ this.set('errorMessage', 'This field is required');
+ return;
+ } else if (Number.isNaN(parsedTime)) {
+ this.set('errorMessage', 'Value must be a number');
+ return;
+ }
+ this.set('time', parsedTime);
+ this.handleChange();
+ if (Ember.testing) {
+ return;
+ }
+ this.set('recalculateSeconds', true);
+ yield timeout(this.recalculationTimeout);
+ this.set('recalculateSeconds', false);
+ }).restartable(),
+
+ seconds: computed('time', 'unit', function() {
+ return convertToSeconds(this.time, this.unit);
+ }),
+
+ actions: {
+ updateUnit(newUnit) {
+ if (this.recalculateSeconds) {
+ this.set('unit', newUnit);
+ } else {
+ this.keepSecondsRecalculate(newUnit);
+ }
+ this.handleChange();
+ },
+ },
+});
diff --git a/ui/lib/core/addon/components/ttl-picker2.js b/ui/lib/core/addon/components/ttl-picker2.js
index 622b255d81..898f7da346 100644
--- a/ui/lib/core/addon/components/ttl-picker2.js
+++ b/ui/lib/core/addon/components/ttl-picker2.js
@@ -21,12 +21,10 @@
* @param changeOnInit=false {Boolean} - set this value if you'd like the passed onChange function to be called on component initialization
*/
-import Ember from 'ember';
-import Component from '@ember/component';
import { computed } from '@ember/object';
-import { task, timeout } from 'ember-concurrency';
import { typeOf } from '@ember/utils';
import Duration from 'Duration.js';
+import TtlForm from './ttl-form';
import layout from '../templates/components/ttl-picker2';
const secondsMap = {
@@ -36,14 +34,11 @@ const secondsMap = {
d: 86400,
};
const validUnits = ['s', 'm', 'h', 'd'];
-const convertToSeconds = (time, unit) => {
- return time * secondsMap[unit];
-};
const convertFromSeconds = (seconds, unit) => {
return seconds / secondsMap[unit];
};
-export default Component.extend({
+export default TtlForm.extend({
layout,
enableTTL: false,
label: 'Time to live (TTL)',
@@ -52,7 +47,6 @@ export default Component.extend({
description: '',
time: 30,
unit: 's',
- recalculationTimeout: 5000,
initialValue: null,
changeOnInit: false,
@@ -88,7 +82,6 @@ export default Component.extend({
time = seconds;
}
} catch (e) {
- console.error(e);
// if parsing fails leave as default 30s
}
}
@@ -121,52 +114,13 @@ export default Component.extend({
};
this.onChange(ttl);
},
- updateTime: task(function*(newTime) {
- this.set('errorMessage', '');
- let parsedTime;
- parsedTime = parseInt(newTime, 10);
- if (!newTime) {
- this.set('errorMessage', 'This field is required');
- return;
- } else if (Number.isNaN(parsedTime)) {
- this.set('errorMessage', 'Value must be a number');
- return;
- }
- this.set('time', parsedTime);
- this.handleChange();
- if (Ember.testing) {
- return;
- }
- this.set('recalculateSeconds', true);
- yield timeout(this.recalculationTimeout);
- this.set('recalculateSeconds', false);
- }).restartable(),
- recalculateTime(newUnit) {
- const newTime = convertFromSeconds(this.seconds, newUnit);
- this.setProperties({
- time: newTime,
- unit: newUnit,
- });
- },
-
- seconds: computed('time', 'unit', function() {
- return convertToSeconds(this.time, this.unit);
- }),
helperText: computed('enableTTL', 'helperTextUnset', 'helperTextSet', function() {
return this.enableTTL ? this.helperTextEnabled : this.helperTextDisabled;
}),
- errorMessage: null,
+
recalculateSeconds: false,
actions: {
- updateUnit(newUnit) {
- if (this.recalculateSeconds) {
- this.set('unit', newUnit);
- } else {
- this.recalculateTime(newUnit);
- }
- this.handleChange();
- },
toggleEnabled() {
this.toggleProperty('enableTTL');
this.handleChange();
diff --git a/ui/lib/core/addon/templates/components/ttl-form.hbs b/ui/lib/core/addon/templates/components/ttl-form.hbs
new file mode 100644
index 0000000000..391023d473
--- /dev/null
+++ b/ui/lib/core/addon/templates/components/ttl-form.hbs
@@ -0,0 +1,40 @@
+{{yeild}}
+
+{{#if errorMessage}}
+
+
+
+
+
+ {{errorMessage}}
+
+
+{{/if}}
diff --git a/ui/lib/core/app/components/ttl-form.js b/ui/lib/core/app/components/ttl-form.js
new file mode 100644
index 0000000000..21893eb3c6
--- /dev/null
+++ b/ui/lib/core/app/components/ttl-form.js
@@ -0,0 +1 @@
+export { default } from 'core/components/ttl-form';
diff --git a/ui/lib/core/stories/ttl-form.md b/ui/lib/core/stories/ttl-form.md
new file mode 100644
index 0000000000..365b144ea0
--- /dev/null
+++ b/ui/lib/core/stories/ttl-form.md
@@ -0,0 +1,29 @@
+
+
+## TtlForm
+TtlForm components are used to enter a Time To Live (TTL) input.
+This component does not include a label and is designed to take
+a time and unit, and pass an object including seconds and
+timestring when those two values are changed.
+
+**Params**
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| onChange | function | | This function will be called when the user changes the value. An object will be passed in as a parameter with values seconds{number}, timeString{string} |
+| [time] | number | | Time is the value that will be passed into the value input. Can be null/undefined to start if input is required. |
+| [unit] | unit | "s" | This is the unit key which will show by default on the form. Can be one of `s` (seconds), `m` (minutes), `h` (hours), `d` (days) |
+| [recalculationTimeout] | number | 5000 | This is the time, in milliseconds, that `recalculateSeconds` will be be true after time is updated |
+
+**Example**
+
+```js
+
+```
+
+**See**
+
+- [Uses of TtlForm](https://github.com/hashicorp/vault/search?l=Handlebars&q=TtlForm+OR+ttl-form)
+- [TtlForm Source Code](https://github.com/hashicorp/vault/blob/master/ui/lib/core/addon/components/ttl-form.js)
+
+---
diff --git a/ui/lib/core/stories/ttl-form.stories.js b/ui/lib/core/stories/ttl-form.stories.js
new file mode 100644
index 0000000000..9b0bc4d46c
--- /dev/null
+++ b/ui/lib/core/stories/ttl-form.stories.js
@@ -0,0 +1,17 @@
+import hbs from 'htmlbars-inline-precompile';
+import { storiesOf } from '@storybook/ember';
+import notes from './ttl-form.md';
+
+storiesOf('TtlForm', module)
+ .addParameters({ options: { showPanel: true } })
+ .add(
+ `TtlForm`,
+ () => ({
+ template: hbs`
+ Ttl Form
+
+ `,
+ context: {},
+ }),
+ { notes }
+ );
diff --git a/ui/tests/integration/components/ttl-form-test.js b/ui/tests/integration/components/ttl-form-test.js
new file mode 100644
index 0000000000..61a9d4428f
--- /dev/null
+++ b/ui/tests/integration/components/ttl-form-test.js
@@ -0,0 +1,49 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, fillIn } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+import sinon from 'sinon';
+
+module('Integration | Component | ttl-form', function(hooks) {
+ setupRenderingTest(hooks);
+
+ hooks.beforeEach(function() {
+ this.changeSpy = sinon.spy();
+ this.set('onChange', this.changeSpy);
+ });
+
+ test('it shows no initial time and initial unit of s when not time or unit passed in', async function(assert) {
+ await render(hbs``);
+ assert.dom('[data-test-ttlform-value]').hasValue('');
+ assert.dom('[data-test-select="ttl-unit"]').hasValue('s');
+ });
+
+ test('it calls the change fn with the correct values', async function(assert) {
+ await render(hbs``);
+
+ assert.dom('[data-test-select="ttl-unit"]').hasValue('m', 'unit value initially shows m (minutes)');
+ await fillIn('[data-test-ttlform-value]', '10');
+ await assert.ok(this.changeSpy.calledOnce, 'it calls the passed onChange');
+ assert.ok(
+ this.changeSpy.calledWith({
+ seconds: 600,
+ timeString: '10m',
+ }),
+ 'Passes the default values back to onChange'
+ );
+ });
+
+ test('it correctly shows initial unit', async function(assert) {
+ let changeSpy = sinon.spy();
+ this.set('onChange', changeSpy);
+ await render(hbs`
+
+ `);
+
+ assert.dom('[data-test-select="ttl-unit"]').hasValue('h', 'unit value initially shows as h (hours)');
+ });
+});