diff --git a/changelog/10848.txt b/changelog/10848.txt new file mode 100644 index 000000000000..9b3c659b43ec --- /dev/null +++ b/changelog/10848.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Upgrade date-fns from 1.3.0 to 2.16.1. +``` diff --git a/ui/app/components/pricing-metrics-dates.js b/ui/app/components/pricing-metrics-dates.js index 88af4dd665c7..a78a93de2be3 100644 --- a/ui/app/components/pricing-metrics-dates.js +++ b/ui/app/components/pricing-metrics-dates.js @@ -1,8 +1,8 @@ /** * @module PricingMetricsDates * PricingMetricsDates components are used on the Pricing Metrics page to handle queries related to pricing metrics. - * This component assumes that query parameters (as in, from route params) are being passed in with the format MM-YYYY, - * while the inputs expect a format of MM/YYYY. + * This component assumes that query parameters (as in, from route params) are being passed in with the format MM-yyyy, + * while the inputs expect a format of MM/yyyy. * * @example * ```js @@ -10,8 +10,8 @@ * ``` * @param {object} resultStart - resultStart is the start date of the metrics returned. Should be a valid date string that the built-in Date() fn can parse * @param {object} resultEnd - resultEnd is the end date of the metrics returned. Should be a valid date string that the built-in Date() fn can parse - * @param {string} [queryStart] - queryStart is the route param (formatted MM-YYYY) that the result will be measured against for showing discrepancy warning - * @param {string} [queryEnd] - queryEnd is the route param (formatted MM-YYYY) that the result will be measured against for showing discrepancy warning + * @param {string} [queryStart] - queryStart is the route param (formatted MM-yyyy) that the result will be measured against for showing discrepancy warning + * @param {string} [queryEnd] - queryEnd is the route param (formatted MM-yyyy) that the result will be measured against for showing discrepancy warning * @param {number} [defaultSpan=12] - setting for default time between start and end input dates * @param {number} [retentionMonths=24] - setting for the retention months, which informs valid dates to query by */ @@ -100,10 +100,10 @@ export default Component.extend({ error: computed('end', 'endDate', 'retentionMonths', 'start', 'startDate', function() { if (!this.startDate) { - return 'Start date is invalid. Please use format MM/YYYY'; + return 'Start date is invalid. Please use format MM/yyyy'; } if (!this.endDate) { - return 'End date is invalid. Please use format MM/YYYY'; + return 'End date is invalid. Please use format MM/yyyy'; } if (isBefore(this.endDate, this.startDate)) { return 'Start date is after end date'; @@ -114,7 +114,7 @@ export default Component.extend({ } const earliestRetained = startOfMonth(subMonths(lastMonthAvailable, this.retentionMonths)); if (isBefore(this.startDate, earliestRetained)) { - return `No data retained before ${format(earliestRetained, 'MM/YYYY')} due to your settings`; + return `No data retained before ${format(earliestRetained, 'MM/yyyy')} due to your settings`; } return null; @@ -130,24 +130,24 @@ export default Component.extend({ initialEnd = parseDateString(this.queryEnd, '-'); } else { // if query isn't passed in, set it so that showResultsWarning works - this.queryEnd = format(initialEnd, 'MM-YYYY'); + this.queryEnd = format(initialEnd, 'MM-yyyy'); } initialStart = subMonths(initialEnd, this.defaultSpan); if (this.queryStart) { initialStart = parseDateString(this.queryStart, '-'); } else { // if query isn't passed in, set it so that showResultsWarning works - this.queryStart = format(initialStart, 'MM-YYYY'); + this.queryStart = format(initialStart, 'MM-yyyy'); } - this.start = format(initialStart, 'MM/YYYY'); - this.end = format(initialEnd, 'MM/YYYY'); + this.start = format(initialStart, 'MM/yyyy'); + this.end = format(initialEnd, 'MM/yyyy'); }, actions: { handleQuery() { - const start = format(this.startDate, 'MM-YYYY'); - const end = format(this.endDate, 'MM-YYYY'); + const start = format(this.startDate, 'MM-yyyy'); + const end = format(this.endDate, 'MM-yyyy'); this.router.transitionTo('vault.cluster.metrics', { queryParams: { start, diff --git a/ui/app/components/tool-actions-form.js b/ui/app/components/tool-actions-form.js index 606fca66eca7..fd9f3df9a023 100644 --- a/ui/app/components/tool-actions-form.js +++ b/ui/app/components/tool-actions-form.js @@ -3,7 +3,7 @@ import { assign } from '@ember/polyfills'; import { inject as service } from '@ember/service'; import Component from '@ember/component'; import { setProperties, computed, set } from '@ember/object'; -import { addSeconds } from 'date-fns'; +import { addSeconds, parseISO } from 'date-fns'; const DEFAULTS = { token: null, @@ -67,8 +67,8 @@ export default Component.extend(DEFAULTS, { if (!(creation_time && creation_ttl)) { return null; } - - return addSeconds(creation_time, creation_ttl); + // returns new Date with seconds added. + return addSeconds(parseISO(creation_time), creation_ttl); }), handleError(e) { diff --git a/ui/app/helpers/date-from-now.js b/ui/app/helpers/date-from-now.js index 79cad4252dd3..2f446fb6455e 100644 --- a/ui/app/helpers/date-from-now.js +++ b/ui/app/helpers/date-from-now.js @@ -1,8 +1,11 @@ import { helper } from '@ember/component/helper'; -import { distanceInWordsToNow } from 'date-fns'; +import { formatDistanceToNow } from 'date-fns'; export function dateFromNow([date], options = {}) { - return distanceInWordsToNow(date, options); + // check first if string. If it is, it could be ISO format or UTC, either way create a new date object + // otherwise it's a number or object and just return + let newDate = typeof date === 'string' ? new Date(date) : date; + return formatDistanceToNow(newDate, { ...options }); } export default helper(dateFromNow); diff --git a/ui/app/helpers/parse-date-string.js b/ui/app/helpers/parse-date-string.js index 0e5c4552d78f..b4780c02c853 100644 --- a/ui/app/helpers/parse-date-string.js +++ b/ui/app/helpers/parse-date-string.js @@ -2,7 +2,7 @@ import { helper } from '@ember/component/helper'; import { isValid } from 'date-fns'; export function parseDateString(date, separator = '-') { - // Expects format MM-YYYY by default: no dates + // Expects format MM-yyyy by default: no dates let datePieces = date.split(separator); if (datePieces.length === 2) { if (datePieces[0] < 1 || datePieces[0] > 12) { @@ -14,7 +14,7 @@ export function parseDateString(date, separator = '-') { } } // what to return if not valid? - throw new Error(`Please use format MM${separator}YYYY`); + throw new Error(`Please use format MM${separator}yyyy`); } export default helper(parseDateString); diff --git a/ui/app/routes/vault/cluster/metrics/index.js b/ui/app/routes/vault/cluster/metrics/index.js index 39d19c24414e..59e7b5a3cd9c 100644 --- a/ui/app/routes/vault/cluster/metrics/index.js +++ b/ui/app/routes/vault/cluster/metrics/index.js @@ -1,25 +1,26 @@ import Route from '@ember/routing/route'; import ClusterRoute from 'vault/mixins/cluster-route'; import { hash } from 'rsvp'; -import { format, addDays } from 'date-fns'; +import { getTime } from 'date-fns'; import { parseDateString } from 'vault/helpers/parse-date-string'; const getActivityParams = ({ start, end }) => { - // Expects MM-YYYY format + // Expects MM-yyyy format // TODO: minStart, maxEnd let params = {}; if (start) { let startDate = parseDateString(start); if (startDate) { // TODO: Replace with formatRFC3339 when date-fns is updated - params.start_time = format(addDays(startDate, 1), 'X'); + // converts to milliseconds, divide by 1000 to get epoch + params.start_time = getTime(startDate) / 1000; } } if (end) { let endDate = parseDateString(end); if (endDate) { // TODO: Replace with formatRFC3339 when date-fns is updated - params.end_time = format(addDays(endDate, 10), 'X'); + params.end_time = getTime(endDate) / 1000; } } return params; diff --git a/ui/app/templates/components/auth-info.hbs b/ui/app/templates/components/auth-info.hbs index 032fdb2e0a9b..0c4d0f1fc099 100644 --- a/ui/app/templates/components/auth-info.hbs +++ b/ui/app/templates/components/auth-info.hbs @@ -10,7 +10,7 @@
  • + On {{date-format auth.tokenExpirationDate 'MMMM Do yyyy, h:mm:ss a'}}" />
  • {{/if}}
  • diff --git a/ui/app/templates/components/file-to-array-buffer.hbs b/ui/app/templates/components/file-to-array-buffer.hbs index c2d815471a85..46d74242378b 100644 --- a/ui/app/templates/components/file-to-array-buffer.hbs +++ b/ui/app/templates/components/file-to-array-buffer.hbs @@ -25,7 +25,7 @@ {{#if this.fileName}}

    - This file is {{this.fileSize}} and was created on {{date-format this.fileLastModified 'MMM DD, YYYY hh:mm:ss A'}}. + This file is {{this.fileSize}} and was created on {{date-format this.fileLastModified 'MMM dd, yyyy hh:mm:ss a'}}.

    {{/if}} {{#if @fileHelpText}} diff --git a/ui/app/templates/components/identity/item-alias/alias-details.hbs b/ui/app/templates/components/identity/item-alias/alias-details.hbs index 225f81d30eea..841ae229c7d9 100644 --- a/ui/app/templates/components/identity/item-alias/alias-details.hbs +++ b/ui/app/templates/components/identity/item-alias/alias-details.hbs @@ -17,11 +17,11 @@ diff --git a/ui/app/templates/components/identity/item-details.hbs b/ui/app/templates/components/identity/item-details.hbs index e82976a226f1..03d6c052c15c 100644 --- a/ui/app/templates/components/identity/item-details.hbs +++ b/ui/app/templates/components/identity/item-details.hbs @@ -26,12 +26,12 @@ \ No newline at end of file diff --git a/ui/app/templates/components/license-info.hbs b/ui/app/templates/components/license-info.hbs index f7568002492a..d52bacb7fb34 100644 --- a/ui/app/templates/components/license-info.hbs +++ b/ui/app/templates/components/license-info.hbs @@ -54,7 +54,7 @@
    - {{date-format model.startTime 'MMM DD, YYYY hh:mm:ss A'}} to {{date-format expirationTime 'MMM DD, YYYY hh:mm:ss A'}} + {{date-format startTime 'MMM dd, yyyy hh:mm:ss a'}} to {{date-format expirationTime 'MMM dd, yyyy hh:mm:ss a'}}
    diff --git a/ui/app/templates/components/pricing-metrics-config.hbs b/ui/app/templates/components/pricing-metrics-config.hbs index 401e3d4cbc24..a7288d0d4263 100644 --- a/ui/app/templates/components/pricing-metrics-config.hbs +++ b/ui/app/templates/components/pricing-metrics-config.hbs @@ -38,7 +38,7 @@ >
    diff --git a/ui/package.json b/ui/package.json index b9031476eb7a..1295c4330d0c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -64,7 +64,7 @@ "d3-time-format": "^2.1.1", "d3-tip": "^0.9.1", "d3-transition": "^1.2.0", - "date-fns": "^1.30.0", + "date-fns": "^2.16.1", "deepmerge": "^4.0.0", "doctoc": "^1.4.0", "ember-api-actions": "^0.2.8", diff --git a/ui/tests/integration/components/pricing-metrics-dates-test.js b/ui/tests/integration/components/pricing-metrics-dates-test.js index 2c4dbdcf02fa..3b8af51f80bd 100644 --- a/ui/tests/integration/components/pricing-metrics-dates-test.js +++ b/ui/tests/integration/components/pricing-metrics-dates-test.js @@ -13,10 +13,10 @@ module('Integration | Component | pricing-metrics-dates', function(hooks) { await render(hbs` `); - assert.dom('[data-test-end-input]').hasValue(format(expectedEnd, 'MM/YYYY'), 'End input is last month'); + assert.dom('[data-test-end-input]').hasValue(format(expectedEnd, 'MM/yyyy'), 'End input is last month'); assert .dom('[data-test-start-input]') - .hasValue(format(expectedStart, 'MM/YYYY'), 'Start input is 12 months before last month'); + .hasValue(format(expectedStart, 'MM/yyyy'), 'Start input is 12 months before last month'); }); test('On init if end date passed, start is calculated', async function(assert) { @@ -28,7 +28,7 @@ module('Integration | Component | pricing-metrics-dates', function(hooks) { assert.dom('[data-test-end-input]').hasValue('09/2020', 'End input matches query'); assert .dom('[data-test-start-input]') - .hasValue(format(expectedStart, 'MM/YYYY'), 'Start input is 12 months before end input'); + .hasValue(format(expectedStart, 'MM/yyyy'), 'Start input is 12 months before end input'); }); test('On init if query start date passed, end is default', async function(assert) { @@ -37,7 +37,7 @@ module('Integration | Component | pricing-metrics-dates', function(hooks) { await render(hbs` `); - assert.dom('[data-test-end-input]').hasValue(format(expectedEnd, 'MM/YYYY'), 'End input is last month'); + assert.dom('[data-test-end-input]').hasValue(format(expectedEnd, 'MM/yyyy'), 'End input is last month'); assert.dom('[data-test-start-input]').hasValue('01/2020', 'Start input matches query'); }); @@ -91,15 +91,15 @@ module('Integration | Component | pricing-metrics-dates', function(hooks) { `); assert.dom('[data-test-form-error]').doesNotExist('No form error shows by default'); - await fillIn('[data-test-start-input]', format(subMonths(firstAvailable, 1), 'MM/YYYY')); + await fillIn('[data-test-start-input]', format(subMonths(firstAvailable, 1), 'MM/yyyy')); assert .dom('[data-test-form-error]') .includesText( - `No data retained before ${format(firstAvailable, 'MM/YYYY')}`, + `No data retained before ${format(firstAvailable, 'MM/yyyy')}`, 'shows the correct error message for starting before the configured retainment period' ); - await fillIn('[data-test-end-input]', format(subMonths(lastAvailable, -1), 'MM/YYYY')); + await fillIn('[data-test-end-input]', format(subMonths(lastAvailable, -1), 'MM/yyyy')); assert .dom('[data-test-form-error]') .includesText( @@ -111,15 +111,15 @@ module('Integration | Component | pricing-metrics-dates', function(hooks) { assert .dom('[data-test-form-error]') .includesText( - 'End date is invalid. Please use format MM/YYYY', + 'End date is invalid. Please use format MM/yyyy', 'shows the correct error message for non-date input' ); - await fillIn('[data-test-start-input]', `13/${format(lastAvailable, 'YYYY')}`); + await fillIn('[data-test-start-input]', `13/${format(lastAvailable, 'yyyy')}`); assert .dom('[data-test-form-error]') .includesText( - 'Start date is invalid. Please use format MM/YYYY', + 'Start date is invalid. Please use format MM/yyyy', 'shows the correct error message for an invalid month' ); }); diff --git a/ui/tests/integration/helpers/date-format-test.js b/ui/tests/integration/helpers/date-format-test.js index bb2fd33cc208..a41db85f975d 100644 --- a/ui/tests/integration/helpers/date-format-test.js +++ b/ui/tests/integration/helpers/date-format-test.js @@ -10,7 +10,7 @@ module('Integration | Helper | date-format', function(hooks) { let today = new Date(); this.set('today', today); - await render(hbs`

    Date: {{date-format today "YYYY"}}

    `); + await render(hbs`

    Date: {{date-format today "yyyy"}}

    `); assert .dom('[data-test-date-format]') .includesText(today.getFullYear(), 'it renders the date in the year format'); @@ -29,7 +29,7 @@ module('Integration | Helper | date-format', function(hooks) { let todayString = new Date().getFullYear().toString(); this.set('todayString', todayString); - await render(hbs`

    Date: {{date-format todayString}}

    `); + await render(hbs`

    Date: {{date-format todayString "yyyy"}}

    `); assert .dom('[data-test-date-format]') .includesText(todayString, 'it renders the a date if passed in as a string'); diff --git a/ui/tests/integration/helpers/date-from-now-test.js b/ui/tests/integration/helpers/date-from-now-test.js index e8807f372e10..a89be51d6912 100644 --- a/ui/tests/integration/helpers/date-from-now-test.js +++ b/ui/tests/integration/helpers/date-from-now-test.js @@ -1,6 +1,9 @@ import { module, test } from 'qunit'; +import { subMinutes } from 'date-fns'; import { setupRenderingTest } from 'ember-qunit'; import { dateFromNow } from '../../../helpers/date-from-now'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; module('Integration | Helper | date-from-now', function(hooks) { setupRenderingTest(hooks); @@ -14,4 +17,31 @@ module('Integration | Helper | date-from-now', function(hooks) { let result = dateFromNow([1481022124443], { addSuffix: true }); assert.ok(result.includes(' ago')); }); + + test('you can pass in UTC timestamp', function(assert) { + let result = dateFromNow(['Fri, 11 Oct 2019 18:56:08 GMT'], { addSuffix: true }); + assert.ok(result.includes(' ago')); + }); + + test('you can pass in ISO timestamp', function(assert) { + let result = dateFromNow(['2019-10-11T18:56:08.984Z'], { addSuffix: true }); + assert.ok(result.includes(' ago')); + }); + + test('you can include a suffix using date class', function(assert) { + let now = Date.now(); + let pastDate = subMinutes(now, 30); + let result = dateFromNow([pastDate], { addSuffix: true }); + assert.ok(result.includes(' ago')); + }); + + test('you can include a suffix using ISO 8601 format', function(assert) { + let result = dateFromNow(['2021-02-05T20:43:09+00:00'], { addSuffix: true }); + assert.ok(result.includes(' ago')); + }); + + test('you can include a suffix in the helper', async function(assert) { + await render(hbs`

    Date: {{date-from-now 1481022124443 addSuffix=true}}

    `); + assert.dom('[data-test-date-from-now]').includesText(' years ago'); + }); }); diff --git a/ui/tests/unit/helpers/parse-date-string-test.js b/ui/tests/unit/helpers/parse-date-string-test.js index 5a094ff4bd97..8297a2ab901b 100644 --- a/ui/tests/unit/helpers/parse-date-string-test.js +++ b/ui/tests/unit/helpers/parse-date-string-test.js @@ -3,13 +3,13 @@ import { module, test } from 'qunit'; import { compareAsc } from 'date-fns'; module('Unit | Helpers | parse-date-string', function() { - test('it returns the first of the month when date like MM-YYYY passed in', function(assert) { + test('it returns the first of the month when date like MM-yyyy passed in', function(assert) { let expected = new Date(2020, 3, 1); let result = parseDateString('04-2020'); assert.equal(compareAsc(expected, result), 0); }); - test('it can handle a date format like MM/YYYY', function(assert) { + test('it can handle a date format like MM/yyyy', function(assert) { let expected = new Date(2020, 11, 1); let result = parseDateString('12/2020', '/'); assert.equal(compareAsc(expected, result), 0); @@ -22,7 +22,7 @@ module('Unit | Helpers | parse-date-string', function() { } catch (e) { result = e.message; } - assert.equal('Please use format MM-YYYY', result); + assert.equal('Please use format MM-yyyy', result); }); test('it throws an error with wrong separator', function(assert) { @@ -32,7 +32,7 @@ module('Unit | Helpers | parse-date-string', function() { } catch (e) { result = e.message; } - assert.equal('Please use format MM.YYYY', result); + assert.equal('Please use format MM.yyyy', result); }); test('it throws an error if month is invalid', function(assert) { diff --git a/ui/yarn.lock b/ui/yarn.lock index a171c5a2536a..c6b906f17f1e 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -7278,10 +7278,10 @@ data-urls@^1.0.1: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -date-fns@^1.30.0: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== +date-fns@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" + integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ== date-time@^2.1.0: version "2.1.0"