mirror of
https://github.com/hashicorp/vault.git
synced 2026-02-18 18:38:08 -05:00
UI: Update date-picker UX in Client Counts (#27796)
* Create date-range component with tests * update selectors and callback behavior * update cc tests & selectors * cleanup * RIP calendar-widget and date-dropdown -- you were good components * reset on close * Add changelog * use parseApiTimestamp * fix test * cleanup * make date-range typescript, update behavior * add words * minor styling * fix test
This commit is contained in:
parent
d444a32f8c
commit
050a90b2ab
19 changed files with 454 additions and 838 deletions
3
changelog/27796.txt
Normal file
3
changelog/27796.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
ui: simplify the date range editing experience in the client counts dashboard.
|
||||
```
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { ARRAY_OF_MONTHS, parseAPITimestamp } from 'core/utils/date-formatters';
|
||||
import { addYears, isSameYear, subYears } from 'date-fns';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
/**
|
||||
* @module CalendarWidget
|
||||
* CalendarWidget component is used in the client counts dashboard to select a month/year to query the /activity endpoint.
|
||||
* The component returns an object with selected date info, example: { dateType: 'endDate', monthIdx: 0, monthName: 'January', year: 2022 }
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <CalendarWidget @startTimestamp={{this.startTime}} @endTimestamp={{this.endTime}} @selectMonth={{this.handleSelection}} />
|
||||
*
|
||||
* @param {string} startTimestamp - ISO timestamp string of the calendar widget's start time, displays in dropdown trigger
|
||||
* @param {string} endTimestamp - ISO timestamp string for the calendar widget's end time, displays in dropdown trigger
|
||||
* @param {function} selectMonth - callback function from parent - fires when selecting a month or clicking "Current billing period"
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export default class CalendarWidget extends Component {
|
||||
currentDate = timestamp.now();
|
||||
@tracked calendarDisplayDate = this.currentDate; // init to current date, updates when user clicks on calendar chevrons
|
||||
@tracked showCalendar = false;
|
||||
|
||||
// both date getters return a date object
|
||||
get startDate() {
|
||||
return parseAPITimestamp(this.args.startTimestamp);
|
||||
}
|
||||
get endDate() {
|
||||
return parseAPITimestamp(this.args.endTimestamp);
|
||||
}
|
||||
get displayYear() {
|
||||
return this.calendarDisplayDate.getFullYear();
|
||||
}
|
||||
get disableFutureYear() {
|
||||
return isSameYear(this.calendarDisplayDate, this.currentDate);
|
||||
}
|
||||
get disablePastYear() {
|
||||
// calendar widget should only go as far back as the passed in start time
|
||||
return isSameYear(this.calendarDisplayDate, this.startDate);
|
||||
}
|
||||
get widgetMonths() {
|
||||
const startYear = this.startDate.getFullYear();
|
||||
const startMonthIdx = this.startDate.getMonth();
|
||||
return ARRAY_OF_MONTHS.map((month, index) => {
|
||||
let readonly = false;
|
||||
|
||||
// if widget is showing same year as @startTimestamp year, disable if month is before start month
|
||||
if (startYear === this.displayYear && index < startMonthIdx) {
|
||||
readonly = true;
|
||||
}
|
||||
|
||||
// if widget showing current year, disable if month is later than current month
|
||||
if (this.displayYear === this.currentDate.getFullYear() && index > this.currentDate.getMonth()) {
|
||||
readonly = true;
|
||||
}
|
||||
return {
|
||||
index,
|
||||
year: this.displayYear,
|
||||
name: month,
|
||||
readonly,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
addYear() {
|
||||
this.calendarDisplayDate = addYears(this.calendarDisplayDate, 1);
|
||||
}
|
||||
|
||||
@action
|
||||
subYear() {
|
||||
this.calendarDisplayDate = subYears(this.calendarDisplayDate, 1);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleShowCalendar() {
|
||||
this.showCalendar = !this.showCalendar;
|
||||
this.calendarDisplayDate = this.endDate;
|
||||
}
|
||||
|
||||
@action
|
||||
handleDateShortcut(dropdown, { target }) {
|
||||
this.args.selectMonth({ dateType: target.name }); // send clicked shortcut to parent callback
|
||||
this.showCalendar = false;
|
||||
dropdown.close();
|
||||
}
|
||||
|
||||
@action
|
||||
selectMonth(month, dropdown) {
|
||||
const { index, year, name } = month;
|
||||
this.toggleShowCalendar();
|
||||
this.args.selectMonth({ monthIdx: index, monthName: name, year, dateType: 'endDate' });
|
||||
dropdown.close();
|
||||
}
|
||||
}
|
||||
102
ui/app/components/clients/date-range.hbs
Normal file
102
ui/app/components/clients/date-range.hbs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<div ...attributes>
|
||||
<Hds::Text::Display @tag="p" class="has-bottom-margin-xs">
|
||||
Date range
|
||||
</Hds::Text::Display>
|
||||
|
||||
<div class="is-flex-align-baseline">
|
||||
{{#if (and @startTime @endTime)}}
|
||||
<p class="is-size-6" data-test-date-range="start">{{this.formattedDate @startTime}}</p>
|
||||
<p class="has-left-margin-xs"> — </p>
|
||||
<p class="is-size-6 has-left-margin-xs" data-test-date-range="end">{{this.formattedDate @endTime}}</p>
|
||||
<Hds::Button
|
||||
class="has-left-margin-xs"
|
||||
@text="Edit"
|
||||
@color="tertiary"
|
||||
@icon="edit"
|
||||
@iconPosition="trailing"
|
||||
data-test-date-range-edit
|
||||
{{on "click" (fn (mut this.showEditModal) true)}}
|
||||
/>
|
||||
{{else}}
|
||||
<Hds::Button
|
||||
@text="Set date range"
|
||||
@icon="edit"
|
||||
{{on "click" (fn (mut this.showEditModal) true)}}
|
||||
data-test-set-date-range
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.showEditModal}}
|
||||
<Hds::Modal data-test-date-range-edit-modal @onClose={{this.onClose}} as |M|>
|
||||
<M.Header>
|
||||
Edit date range
|
||||
</M.Header>
|
||||
<M.Body>
|
||||
<p class="has-bottom-margin-s">
|
||||
The start date will be used as the client counting start time and all clients in that month will be considered new.
|
||||
{{#if this.version.isEnterprise}}
|
||||
We recommend setting this date as your license or billing start date to get the most accurate new and total
|
||||
client count estimations. These dates are only for querying data in storage. Editing the date range does not
|
||||
change any license or billing configurations.
|
||||
{{/if}}
|
||||
</p>
|
||||
<div class="clients-date-range-display">
|
||||
<div>
|
||||
<Hds::Form::Label for="start-month">Start</Hds::Form::Label>
|
||||
<input
|
||||
class="hds-form-text-input"
|
||||
type="month"
|
||||
value={{this.startDate}}
|
||||
max={{this.currentMonth}}
|
||||
id="start-month"
|
||||
name="start"
|
||||
{{on "change" this.updateDate}}
|
||||
data-test-date-edit="start"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Hds::Form::Label for="end-month">End</Hds::Form::Label>
|
||||
<input
|
||||
class="hds-form-text-input"
|
||||
type="month"
|
||||
value={{this.endDate}}
|
||||
max={{this.currentMonth}}
|
||||
id="end-month"
|
||||
name="end"
|
||||
{{on "change" this.updateDate}}
|
||||
data-test-date-edit="end"
|
||||
/>
|
||||
</div>
|
||||
<Hds::Button
|
||||
@text="Reset"
|
||||
@color="tertiary"
|
||||
@icon="reload"
|
||||
{{on "click" this.resetDates}}
|
||||
data-test-date-edit="reset"
|
||||
/>
|
||||
</div>
|
||||
{{#if this.validationError}}
|
||||
<Hds::Form::Error
|
||||
class="has-top-margin-xs"
|
||||
data-test-date-range-validation
|
||||
>{{this.validationError}}</Hds::Form::Error>
|
||||
{{/if}}
|
||||
{{#if this.useDefaultDates}}
|
||||
<Hds::Alert @type="compact" @color="highlight" class="has-top-margin-xs" data-test-range-default-alert as |A|>
|
||||
<A.Description>Dashboard will use the default date range from the API.</A.Description>
|
||||
</Hds::Alert>
|
||||
{{/if}}
|
||||
</M.Body>
|
||||
<M.Footer as |F|>
|
||||
<Hds::Button data-test-save @text="Save" {{on "click" this.handleSave}} />
|
||||
<Hds::Button data-test-cancel @text="Cancel" @color="secondary" {{on "click" F.close}} />
|
||||
</M.Footer>
|
||||
</Hds::Modal>
|
||||
{{/if}}
|
||||
</div>
|
||||
125
ui/app/components/clients/date-range.ts
Normal file
125
ui/app/components/clients/date-range.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { action } from '@ember/object';
|
||||
import { service } from '@ember/service';
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { formatDateObject } from 'core/utils/client-count-utils';
|
||||
import { parseAPITimestamp } from 'core/utils/date-formatters';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
import { format } from 'date-fns';
|
||||
import type VersionService from 'vault/services/version';
|
||||
import type { HTMLElementEvent } from 'forms';
|
||||
|
||||
interface OnChangeParams {
|
||||
start_time: number | undefined;
|
||||
end_time: number | undefined;
|
||||
}
|
||||
interface Args {
|
||||
onChange: (callback: OnChangeParams) => void;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
}
|
||||
/**
|
||||
* @module ClientsDateRange
|
||||
* ClientsDateRange components are used to display the current date range and provide a modal interface for editing the date range.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* <Clients::DateRange @startTime="2018-01-01T14:15:30Z" @endTime="2019-01-31T14:15:30Z" @onChange={{this.handleDateChange}} />
|
||||
*
|
||||
* @param {function} onChange - callback when a new range is saved.
|
||||
* @param {string} [startTime] - ISO string timestamp of the current start date
|
||||
* @param {string} [endTime] - ISO string timestamp of the current end date
|
||||
*/
|
||||
|
||||
export default class ClientsDateRangeComponent extends Component<Args> {
|
||||
@service declare readonly version: VersionService;
|
||||
|
||||
@tracked showEditModal = false;
|
||||
@tracked startDate = ''; // format yyyy-MM
|
||||
@tracked endDate = ''; // format yyyy-MM
|
||||
currentMonth = format(timestamp.now(), 'yyyy-MM');
|
||||
|
||||
constructor(owner: unknown, args: Args) {
|
||||
super(owner, args);
|
||||
this.setTrackedFromArgs();
|
||||
}
|
||||
|
||||
setTrackedFromArgs() {
|
||||
if (this.args.startTime) {
|
||||
this.startDate = parseAPITimestamp(this.args.startTime, 'yyyy-MM') as string;
|
||||
}
|
||||
if (this.args.endTime) {
|
||||
this.endDate = parseAPITimestamp(this.args.endTime, 'yyyy-MM') as string;
|
||||
}
|
||||
}
|
||||
|
||||
formattedDate = (isoTimestamp: string) => {
|
||||
return parseAPITimestamp(isoTimestamp, 'MMMM yyyy');
|
||||
};
|
||||
|
||||
get useDefaultDates() {
|
||||
return !this.startDate && !this.endDate;
|
||||
}
|
||||
|
||||
get validationError() {
|
||||
if (this.useDefaultDates) {
|
||||
// this means we want to reset, which is fine
|
||||
return null;
|
||||
}
|
||||
if (!this.startDate || !this.endDate) {
|
||||
return 'You must supply both start and end dates.';
|
||||
}
|
||||
if (this.startDate > this.endDate) {
|
||||
return 'Start date must be before end date.';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@action onClose() {
|
||||
// since the component never gets torn down, we have to manually re-set this on close
|
||||
this.setTrackedFromArgs();
|
||||
this.showEditModal = false;
|
||||
}
|
||||
|
||||
@action resetDates() {
|
||||
this.startDate = '';
|
||||
this.endDate = '';
|
||||
}
|
||||
|
||||
@action updateDate(evt: HTMLElementEvent<HTMLInputElement>) {
|
||||
const { name, value } = evt.target;
|
||||
if (name === 'end') {
|
||||
this.endDate = value;
|
||||
} else {
|
||||
this.startDate = value;
|
||||
}
|
||||
}
|
||||
|
||||
@action handleSave() {
|
||||
if (this.validationError) return;
|
||||
const params: OnChangeParams = {
|
||||
start_time: undefined,
|
||||
end_time: undefined,
|
||||
};
|
||||
if (this.startDate) {
|
||||
const [year, month] = this.startDate.split('-');
|
||||
if (year && month) {
|
||||
params.start_time = formatDateObject({ monthIdx: parseInt(month) - 1, year: parseInt(year) }, false);
|
||||
}
|
||||
}
|
||||
if (this.endDate) {
|
||||
const [year, month] = this.endDate.split('-');
|
||||
if (year && month) {
|
||||
params.end_time = formatDateObject({ monthIdx: parseInt(month) - 1, year: parseInt(year) }, true);
|
||||
}
|
||||
}
|
||||
|
||||
this.args.onChange(params);
|
||||
this.onClose();
|
||||
}
|
||||
}
|
||||
|
|
@ -18,35 +18,12 @@
|
|||
Date queries are sent in UTC.
|
||||
</p>
|
||||
|
||||
<Hds::Text::Display @tag="p" class="has-bottom-margin-xs" data-test-counts-start-label>
|
||||
{{this.versionText.label}}
|
||||
</Hds::Text::Display>
|
||||
|
||||
<div class="is-flex-align-baseline">
|
||||
{{#if this.formattedStartDate}}
|
||||
<p class="is-size-6" data-test-counts-start-month>{{this.formattedStartDate}}</p>
|
||||
<Hds::Button
|
||||
class="has-left-margin-xs"
|
||||
@text="Edit"
|
||||
@color="tertiary"
|
||||
@icon="edit"
|
||||
@iconPosition="trailing"
|
||||
data-test-counts-start-edit
|
||||
{{on "click" (fn (mut this.showBillingStartModal) true)}}
|
||||
/>
|
||||
{{else}}
|
||||
<DateDropdown
|
||||
@handleSubmit={{this.onDateChange}}
|
||||
@dateType="startDate"
|
||||
@submitText="Save"
|
||||
data-test-counts-start-dropdown
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<Hds::Text::Body @tag="p" @color="faint" @size="300" class="has-bottom-margin-l" data-test-counts-description>
|
||||
{{this.versionText.description}}
|
||||
</Hds::Text::Body>
|
||||
<Clients::DateRange
|
||||
@startTime={{this.startTimestampISO}}
|
||||
@endTime={{this.endTimestampISO}}
|
||||
@onChange={{this.onDateChange}}
|
||||
class="has-bottom-margin-l"
|
||||
/>
|
||||
|
||||
{{#if (eq @activity.id "no-data")}}
|
||||
<Clients::NoData @config={{@config}} @dateRangeMessage={{this.dateRangeMessage}} />
|
||||
|
|
@ -76,11 +53,6 @@
|
|||
</Hds::Text::Body>
|
||||
<Toolbar aria-label="toolbar for filtering client count data" class="has-bottom-margin-m" data-test-clients-filter-bar>
|
||||
<ToolbarFilters>
|
||||
<CalendarWidget
|
||||
@startTimestamp={{this.startTimestampISO}}
|
||||
@endTimestamp={{this.endTimestampISO}}
|
||||
@selectMonth={{this.onDateChange}}
|
||||
/>
|
||||
{{#if (or @namespace this.namespaces)}}
|
||||
<SearchSelect
|
||||
@id="namespace-search-select"
|
||||
|
|
@ -172,22 +144,4 @@
|
|||
</EmptyState>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.showBillingStartModal}}
|
||||
<Hds::Modal id="clients-edit-date-modal" @onClose={{fn (mut this.showBillingStartModal) false}} as |M|>
|
||||
<M.Header>
|
||||
Edit start month
|
||||
</M.Header>
|
||||
<M.Body>
|
||||
<p class="has-bottom-margin-s">
|
||||
{{this.versionText.description}}
|
||||
</p>
|
||||
<p><strong>{{this.versionText.label}}</strong></p>
|
||||
<DateDropdown class="has-top-padding-s" @handleSubmit={{this.onDateChange}} @dateType="startDate" @submitText="Save" />
|
||||
</M.Body>
|
||||
<M.Footer as |F|>
|
||||
<Hds::Button data-test-date-dropdown-cancel @text="Cancel" @color="secondary" {{on "click" F.close}} />
|
||||
</M.Footer>
|
||||
</Hds::Modal>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
@ -6,10 +6,9 @@
|
|||
import Component from '@glimmer/component';
|
||||
import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { fromUnixTime, getUnixTime, isSameMonth, isAfter } from 'date-fns';
|
||||
import { fromUnixTime, isSameMonth, isAfter } from 'date-fns';
|
||||
import { parseAPITimestamp } from 'core/utils/date-formatters';
|
||||
import { filterVersionHistory, formatDateObject } from 'core/utils/client-count-utils';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
import { filterVersionHistory } from 'core/utils/client-count-utils';
|
||||
|
||||
import type AdapterError from '@ember-data/adapter';
|
||||
import type FlagsService from 'vault/services/flags';
|
||||
|
|
@ -94,17 +93,11 @@ export default class ClientsCountsPageComponent extends Component<Args> {
|
|||
get versionText() {
|
||||
return this.version.isEnterprise
|
||||
? {
|
||||
label: 'Billing start month',
|
||||
description:
|
||||
'This date comes from your license, and defines when client counting starts. Without this starting point, the data shown is not reliable.',
|
||||
title: 'No billing start date found',
|
||||
message:
|
||||
'In order to get the most from this data, please enter your billing period start month. This will ensure that the resulting data is accurate.',
|
||||
}
|
||||
: {
|
||||
label: 'Client counting start date',
|
||||
description:
|
||||
'This date is when client counting starts. Without this starting point, the data shown is not reliable.',
|
||||
title: 'No start date found',
|
||||
message:
|
||||
'In order to get the most from this data, please enter a start month above. Vault will calculate new clients starting from that month.',
|
||||
|
|
@ -174,25 +167,8 @@ export default class ClientsCountsPageComponent extends Component<Args> {
|
|||
}
|
||||
|
||||
@action
|
||||
onDateChange(dateObject: { dateType: string; monthIdx: number; year: number }) {
|
||||
const { dateType, monthIdx, year } = dateObject;
|
||||
const { config } = this.args;
|
||||
const currentTimestamp = getUnixTime(timestamp.now());
|
||||
|
||||
// converts the selectedDate to unix timestamp for activity query
|
||||
const selectedDate = formatDateObject({ monthIdx, year }, dateType === 'endDate');
|
||||
|
||||
if (dateType !== 'cancel') {
|
||||
const start_time = {
|
||||
reset: getUnixTime(config?.billingStartTimestamp) || null, // clicked 'Current billing period' in calendar widget -> resets to billing start date
|
||||
currentMonth: currentTimestamp, // clicked 'Current month' from calendar widget -> defaults to currentTimestamp
|
||||
startDate: selectedDate, // from "Edit billing start" modal
|
||||
}[dateType];
|
||||
// endDate type is selection from calendar widget
|
||||
const end_time = dateType === 'endDate' ? selectedDate : currentTimestamp; // defaults to currentTimestamp
|
||||
const params = start_time !== undefined ? { start_time, end_time } : { end_time };
|
||||
this.args.onFilterChange(params);
|
||||
}
|
||||
onDateChange(params: { start_time: number | undefined; end_time: number | undefined }) {
|
||||
this.args.onFilterChange(params);
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
/**
|
||||
* @module DateDropdown
|
||||
* DateDropdown components are used to display a dropdown of months and years to handle date selection. Future dates are disabled (current month and year are selectable).
|
||||
* The component returns an object with selected date info, example: { dateType: 'start', monthIdx: 0, monthName: 'January', year: 2022 }
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <DateDropdown @handleSubmit={{this.actionFromParent}} @name="startTime" @submitText="Save" />
|
||||
* ```
|
||||
* @param {function} handleSubmit - callback function from parent that the date picker triggers on submit
|
||||
* @param {string} [dateType] - optional argument to give the selected month/year a type
|
||||
* @param {string} [submitText] - optional argument to change submit button text
|
||||
* @param {function} [validateDate] - parent function to validate date selection, receives date object and returns an error message that's passed to the inline alert
|
||||
*/
|
||||
export default class DateDropdown extends Component {
|
||||
currentDate = timestamp.now();
|
||||
currentYear = this.currentDate.getFullYear(); // integer of year
|
||||
currentMonthIdx = this.currentDate.getMonth(); // integer of month, 0 indexed
|
||||
dropdownMonths = ARRAY_OF_MONTHS.map((m, i) => ({ name: m, index: i }));
|
||||
dropdownYears = Array.from({ length: 5 }, (item, i) => this.currentYear - i);
|
||||
|
||||
@tracked maxMonthIdx = 11; // disables months with index greater than this number, initially all months are selectable
|
||||
@tracked disabledYear = null; // year as integer if current year should be disabled
|
||||
@tracked selectedMonth = null;
|
||||
@tracked selectedYear = null;
|
||||
@tracked invalidDate = null;
|
||||
|
||||
@action
|
||||
selectMonth(month, dropdown) {
|
||||
this.selectedMonth = month;
|
||||
// disable current year if selected month is later than current month
|
||||
this.disabledYear = month.index > this.currentMonthIdx ? this.currentYear : null;
|
||||
dropdown.close();
|
||||
}
|
||||
|
||||
@action
|
||||
selectYear(year, dropdown) {
|
||||
this.selectedYear = year;
|
||||
// disable months after current month if selected year is current year
|
||||
this.maxMonthIdx = year === this.currentYear ? this.currentMonthIdx : 11;
|
||||
dropdown.close();
|
||||
}
|
||||
|
||||
@action
|
||||
handleSubmit() {
|
||||
if (this.args.validateDate) {
|
||||
this.invalidDate = null;
|
||||
this.invalidDate = this.args.validateDate(new Date(this.selectedYear, this.selectedMonth.index));
|
||||
if (this.invalidDate) return;
|
||||
}
|
||||
const { index, name } = this.selectedMonth;
|
||||
this.args.handleSubmit({
|
||||
monthIdx: index,
|
||||
monthName: name,
|
||||
year: this.selectedYear,
|
||||
dateType: this.args.dateType,
|
||||
});
|
||||
this.resetDropdown();
|
||||
}
|
||||
|
||||
resetDropdown() {
|
||||
this.maxMonthIdx = 11;
|
||||
this.disabledYear = null;
|
||||
this.selectedMonth = null;
|
||||
this.selectedYear = null;
|
||||
this.invalidDate = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,8 +13,6 @@ import type StoreService from 'vault/services/store';
|
|||
import type VersionService from 'vault/services/version';
|
||||
import type { ModelFrom } from 'vault/vault/route';
|
||||
import type ClientsRoute from '../clients';
|
||||
import type ClientsActivityModel from 'vault/models/clients/activity';
|
||||
import type ClientsConfigModel from 'vault/models/clients/config';
|
||||
import type ClientsCountsController from 'vault/controllers/vault/cluster/clients/counts';
|
||||
import { setStartTimeQuery } from 'core/utils/client-count-utils';
|
||||
|
||||
|
|
|
|||
14
ui/app/styles/components/clients-date-range.scss
Normal file
14
ui/app/styles/components/clients-date-range.scss
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
.clients-date-range-display {
|
||||
display: flex;
|
||||
align-items: bottom;
|
||||
> * {
|
||||
margin-right: $spacing-8;
|
||||
vertical-align: bottom;
|
||||
align-self: end;
|
||||
}
|
||||
}
|
||||
|
|
@ -59,6 +59,7 @@
|
|||
@import './components/box-label';
|
||||
@import './components/calendar-widget';
|
||||
@import './components/chart-container';
|
||||
@import './components/clients-date-range';
|
||||
@import './components/cluster-banners';
|
||||
@import './components/codemirror';
|
||||
@import './components/console-ui-panel';
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<Hds::SegmentedGroup ...attributes as |S|>
|
||||
<S.Dropdown @listPosition="bottom-left" @height="200px" as |dd|>
|
||||
<dd.ToggleButton data-test-toggle-month @text={{or this.selectedMonth.name "Month"}} @color="secondary" />
|
||||
{{#each this.dropdownMonths as |month|}}
|
||||
<dd.Interactive
|
||||
data-test-dropdown-month={{month.name}}
|
||||
disabled={{if (gt month.index this.maxMonthIdx) true false}}
|
||||
{{on "click" (fn this.selectMonth month dd)}}
|
||||
@text={{month.name}}
|
||||
/>
|
||||
{{/each}}
|
||||
</S.Dropdown>
|
||||
<S.Dropdown data-test-year-list @listPosition="bottom-left" @height="200px" as |dd|>
|
||||
<dd.ToggleButton data-test-toggle-year @text={{or this.selectedYear "Year"}} @color="secondary" />
|
||||
{{#each this.dropdownYears as |year|}}
|
||||
<dd.Interactive
|
||||
data-test-dropdown-year={{year}}
|
||||
disabled={{if (eq year this.disabledYear) true false}}
|
||||
{{on "click" (fn this.selectYear year dd)}}
|
||||
@text={{year}}
|
||||
/>
|
||||
{{/each}}
|
||||
</S.Dropdown>
|
||||
<S.Button
|
||||
data-test-date-dropdown-submit
|
||||
disabled={{if (and this.selectedMonth this.selectedYear) false true}}
|
||||
{{on "click" this.handleSubmit}}
|
||||
@text={{or @submitText "Submit"}}
|
||||
/>
|
||||
</Hds::SegmentedGroup>
|
||||
{{#if this.invalidDate}}
|
||||
<AlertInline @type="danger" @message={{this.invalidDate}} class="has-top-padding-s" />
|
||||
{{/if}}
|
||||
|
|
@ -8,7 +8,7 @@ import { setupApplicationTest } from 'ember-qunit';
|
|||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import clientsHandler, { STATIC_NOW } from 'vault/mirage/handlers/clients';
|
||||
import sinon from 'sinon';
|
||||
import { visit, click, currentURL } from '@ember/test-helpers';
|
||||
import { visit, click, currentURL, fillIn } from '@ember/test-helpers';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
import { CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors';
|
||||
|
|
@ -42,14 +42,20 @@ module('Acceptance | clients | counts', function (hooks) {
|
|||
|
||||
test('it should persist filter query params between child routes', async function (assert) {
|
||||
await visit('/vault/clients/counts/overview');
|
||||
await click(CLIENT_COUNT.rangeDropdown);
|
||||
await click(CLIENT_COUNT.currentBillingPeriod);
|
||||
const timeQueryRegex = /end_time=\d+&start_time=\d+/g;
|
||||
assert.ok(currentURL().match(timeQueryRegex).length, 'Start and end times added as query params');
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2020-03');
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2022-02');
|
||||
await click(GENERAL.saveButton);
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
'/vault/clients/counts/overview?end_time=1706659200&start_time=1643673600',
|
||||
'Start and end times added as query params'
|
||||
);
|
||||
|
||||
await click(GENERAL.tab('token'));
|
||||
assert.ok(
|
||||
currentURL().match(timeQueryRegex).length,
|
||||
assert.strictEqual(
|
||||
currentURL(),
|
||||
'/vault/clients/counts/token?end_time=1706659200&start_time=1643673600',
|
||||
'Start and end times persist through child route change'
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ import { setupMirage } from 'ember-cli-mirage/test-support';
|
|||
import clientsHandler, { STATIC_NOW, LICENSE_START, UPGRADE_DATE } from 'vault/mirage/handlers/clients';
|
||||
import syncHandler from 'vault/mirage/handlers/sync';
|
||||
import sinon from 'sinon';
|
||||
import { visit, click, findAll, settled } from '@ember/test-helpers';
|
||||
import { visit, click, findAll, settled, fillIn } from '@ember/test-helpers';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
import { CHARTS, CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors';
|
||||
import { create } from 'ember-cli-page-object';
|
||||
|
|
@ -19,6 +18,7 @@ import { clickTrigger } from 'ember-power-select/test-support/helpers';
|
|||
import { formatNumber } from 'core/helpers/format-number';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
import ss from 'vault/tests/pages/components/search-select';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
const searchSelect = create(ss);
|
||||
|
||||
|
|
@ -36,11 +36,11 @@ module('Acceptance | clients | overview', function (hooks) {
|
|||
|
||||
test('it should render charts', async function (assert) {
|
||||
assert
|
||||
.dom(CLIENT_COUNT.counts.startMonth)
|
||||
.dom(CLIENT_COUNT.dateRange.dateDisplay('start'))
|
||||
.hasText('July 2023', 'billing start month is correctly parsed from license');
|
||||
assert
|
||||
.dom(CLIENT_COUNT.rangeDropdown)
|
||||
.hasText('Jul 2023 - Jan 2024', 'Date range shows dates correctly parsed activity response');
|
||||
.dom(CLIENT_COUNT.dateRange.dateDisplay('end'))
|
||||
.hasText('January 2024', 'billing start month is correctly parsed from license');
|
||||
assert.dom(CLIENT_COUNT.attributionBlock).exists('Shows attribution area');
|
||||
assert
|
||||
.dom(CHARTS.container('Vault client counts'))
|
||||
|
|
@ -53,12 +53,13 @@ module('Acceptance | clients | overview', function (hooks) {
|
|||
|
||||
test('it should update charts when querying date ranges', async function (assert) {
|
||||
// query for single, historical month with no new counts (July 2023)
|
||||
await click(CLIENT_COUNT.rangeDropdown);
|
||||
await click(CLIENT_COUNT.calendarWidget.customEndMonth);
|
||||
await click(CLIENT_COUNT.calendarWidget.previousYear);
|
||||
const licenseStartMonth = format(LICENSE_START, 'yyyy-MM');
|
||||
const upgradeMonth = format(UPGRADE_DATE, 'yyyy-MM');
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('start'), licenseStartMonth);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('end'), licenseStartMonth);
|
||||
|
||||
const month = ARRAY_OF_MONTHS[LICENSE_START.getMonth()];
|
||||
await click(CLIENT_COUNT.calendarWidget.calendarMonth(month));
|
||||
await click(GENERAL.saveButton);
|
||||
assert
|
||||
.dom(CLIENT_COUNT.usageStats('Vault client counts'))
|
||||
.doesNotExist('running total single month stat boxes do not show');
|
||||
|
|
@ -75,16 +76,15 @@ module('Acceptance | clients | overview', function (hooks) {
|
|||
assert.dom(CHARTS.container('total-clients')).exists('total client attribution chart shows');
|
||||
|
||||
// reset to billing period
|
||||
await click(CLIENT_COUNT.rangeDropdown);
|
||||
await click(CLIENT_COUNT.currentBillingPeriod);
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await click(CLIENT_COUNT.dateRange.reset);
|
||||
await click(GENERAL.saveButton);
|
||||
|
||||
// change to start on month/year of upgrade to 1.10
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('start'), upgradeMonth);
|
||||
await click(GENERAL.saveButton);
|
||||
|
||||
// change billing start to month/year of upgrade to 1.10
|
||||
await click(CLIENT_COUNT.counts.startEdit);
|
||||
await click(CLIENT_COUNT.monthDropdown);
|
||||
await click(CLIENT_COUNT.dateDropdown.selectMonth(ARRAY_OF_MONTHS[UPGRADE_DATE.getMonth()]));
|
||||
await click(CLIENT_COUNT.yearDropdown);
|
||||
await click(CLIENT_COUNT.dateDropdown.selectYear(UPGRADE_DATE.getFullYear()));
|
||||
await click(CLIENT_COUNT.dateDropdown.submit);
|
||||
assert.dom(CLIENT_COUNT.attributionBlock).exists('Shows attribution area');
|
||||
assert
|
||||
.dom(CHARTS.container('Vault client counts'))
|
||||
|
|
@ -95,11 +95,11 @@ module('Acceptance | clients | overview', function (hooks) {
|
|||
assert.strictEqual(findAll(CHARTS.plotPoint).length, 5, 'line chart plots 5 points to match query');
|
||||
|
||||
// query for single, historical month (upgrade month)
|
||||
await click(CLIENT_COUNT.rangeDropdown);
|
||||
await click(CLIENT_COUNT.calendarWidget.customEndMonth);
|
||||
assert.dom(CLIENT_COUNT.calendarWidget.displayYear).hasText('2024');
|
||||
await click(CLIENT_COUNT.calendarWidget.previousYear);
|
||||
await click(CLIENT_COUNT.calendarWidget.calendarMonth('September'));
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('start'), upgradeMonth);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('end'), upgradeMonth);
|
||||
await click(GENERAL.saveButton);
|
||||
|
||||
assert
|
||||
.dom(CLIENT_COUNT.usageStats('Vault client counts'))
|
||||
.exists('running total single month usage stats show');
|
||||
|
|
@ -111,9 +111,10 @@ module('Acceptance | clients | overview', function (hooks) {
|
|||
assert.dom(CHARTS.container('total-clients')).exists('total client attribution chart shows');
|
||||
|
||||
// query historical date range (from September 2023 to December 2023)
|
||||
await click(CLIENT_COUNT.rangeDropdown);
|
||||
await click(CLIENT_COUNT.calendarWidget.customEndMonth);
|
||||
await click(CLIENT_COUNT.calendarWidget.calendarMonth('December'));
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2023-09');
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('end'), '2023-12');
|
||||
await click(GENERAL.saveButton);
|
||||
|
||||
assert.dom(CLIENT_COUNT.attributionBlock).exists('Shows attribution area');
|
||||
assert
|
||||
|
|
@ -126,15 +127,15 @@ module('Acceptance | clients | overview', function (hooks) {
|
|||
.hasText('12/23', 'x-axis labels end with queried end month');
|
||||
|
||||
// reset to billing period
|
||||
await click(CLIENT_COUNT.rangeDropdown);
|
||||
await click(CLIENT_COUNT.currentBillingPeriod);
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await click(CLIENT_COUNT.dateRange.reset);
|
||||
await click(GENERAL.saveButton);
|
||||
|
||||
// query month older than count start date
|
||||
await click(CLIENT_COUNT.counts.startEdit);
|
||||
await click(CLIENT_COUNT.monthDropdown);
|
||||
await click(CLIENT_COUNT.dateDropdown.selectMonth(ARRAY_OF_MONTHS[LICENSE_START.getMonth()]));
|
||||
await click(CLIENT_COUNT.yearDropdown);
|
||||
await click(CLIENT_COUNT.dateDropdown.selectYear(LICENSE_START.getFullYear() - 3));
|
||||
await click(CLIENT_COUNT.dateDropdown.submit);
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2020-07');
|
||||
await click(GENERAL.saveButton);
|
||||
|
||||
assert
|
||||
.dom(CLIENT_COUNT.counts.startDiscrepancy)
|
||||
.hasTextContaining(
|
||||
|
|
|
|||
|
|
@ -3,18 +3,8 @@
|
|||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { click, findAll } from '@ember/test-helpers';
|
||||
import { CLIENT_COUNT, CHARTS } from './client-count-selectors';
|
||||
|
||||
export async function dateDropdownSelect(month, year) {
|
||||
const { dateDropdown, counts } = CLIENT_COUNT;
|
||||
await click(counts.startEdit);
|
||||
await click(dateDropdown.toggleMonth);
|
||||
await click(dateDropdown.selectMonth(month));
|
||||
await click(dateDropdown.toggleYear);
|
||||
await click(dateDropdown.selectYear(year));
|
||||
await click(dateDropdown.submit);
|
||||
}
|
||||
import { findAll } from '@ember/test-helpers';
|
||||
import { CHARTS } from './client-count-selectors';
|
||||
|
||||
export function assertBarChart(assert, chartName, byMonthData, isStacked = false) {
|
||||
// assertion count is byMonthData.length, plus 2
|
||||
|
|
|
|||
|
|
@ -6,44 +6,28 @@
|
|||
// TODO: separate nested into distinct exported consts
|
||||
export const CLIENT_COUNT = {
|
||||
counts: {
|
||||
startLabel: '[data-test-counts-start-label]',
|
||||
description: '[data-test-counts-description]',
|
||||
startMonth: '[data-test-counts-start-month]',
|
||||
startEdit: '[data-test-counts-start-edit]',
|
||||
startDropdown: '[data-test-counts-start-dropdown]',
|
||||
configDisabled: '[data-test-counts-disabled]',
|
||||
namespaces: '[data-test-counts-namespaces]',
|
||||
mountPaths: '[data-test-counts-auth-mounts]',
|
||||
startDiscrepancy: '[data-test-counts-start-discrepancy]',
|
||||
},
|
||||
dateRange: {
|
||||
dateDisplay: (name: string) => (name ? `[data-test-date-range="${name}"]` : '[data-test-date-range]'),
|
||||
set: '[data-test-set-date-range]',
|
||||
edit: '[data-test-date-range-edit]',
|
||||
editModal: '[data-test-date-range-edit-modal]',
|
||||
editDate: (name: string) => `[data-test-date-edit="${name}"]`,
|
||||
reset: '[data-test-date-edit="reset"]',
|
||||
defaultRangeAlert: '[data-test-range-default-alert]',
|
||||
validation: '[data-test-date-range-validation]',
|
||||
},
|
||||
statText: (label: string) => `[data-test-stat-text="${label}"]`,
|
||||
statTextValue: (label: string) =>
|
||||
label ? `[data-test-stat-text="${label}"] .stat-value` : '[data-test-stat-text]',
|
||||
usageStats: (title: string) => `[data-test-usage-stats="${title}"]`,
|
||||
dateDisplay: '[data-test-date-display]',
|
||||
attributionBlock: '[data-test-clients-attribution]',
|
||||
filterBar: '[data-test-clients-filter-bar]',
|
||||
rangeDropdown: '[data-test-calendar-widget-trigger]',
|
||||
monthDropdown: '[data-test-toggle-month]',
|
||||
yearDropdown: '[data-test-toggle-year]',
|
||||
currentBillingPeriod: '[data-test-current-billing-period]',
|
||||
dateDropdown: {
|
||||
toggleMonth: '[data-test-toggle-month]',
|
||||
toggleYear: '[data-test-toggle-year]',
|
||||
selectMonth: (month: string) => `[data-test-dropdown-month="${month}"]`,
|
||||
selectYear: (year: string) => `[data-test-dropdown-year="${year}"]`,
|
||||
submit: '[data-test-date-dropdown-submit]',
|
||||
},
|
||||
calendarWidget: {
|
||||
trigger: '[data-test-calendar-widget-trigger]',
|
||||
currentMonth: '[data-test-current-month]',
|
||||
currentBillingPeriod: '[data-test-current-billing-period]',
|
||||
customEndMonth: '[data-test-show-calendar]',
|
||||
previousYear: '[data-test-previous-year]',
|
||||
nextYear: '[data-test-next-year]',
|
||||
displayYear: '[data-test-display-year]',
|
||||
calendarMonth: (month: string) => `[data-test-calendar-month="${month}"]`,
|
||||
},
|
||||
selectedAuthMount: 'div#mounts-search-select [data-test-selected-option] div',
|
||||
selectedNs: 'div#namespace-search-select [data-test-selected-option] div',
|
||||
upgradeWarning: '[data-test-clients-upgrade-warning]',
|
||||
|
|
|
|||
|
|
@ -1,240 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, click } from '@ember/test-helpers';
|
||||
import sinon from 'sinon';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import calendarDropdown from 'vault/tests/pages/components/calendar-widget';
|
||||
import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters';
|
||||
import { subMonths, subYears } from 'date-fns';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
|
||||
module('Integration | Component | calendar-widget', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
sinon.replace(timestamp, 'now', sinon.fake.returns(new Date('2018-04-03T14:15:30')));
|
||||
const CURRENT_DATE = timestamp.now();
|
||||
this.set('currentDate', CURRENT_DATE);
|
||||
this.set('calendarStartDate', subMonths(CURRENT_DATE, 12));
|
||||
this.set('calendarEndDate', CURRENT_DATE);
|
||||
this.set('startTimestamp', subMonths(CURRENT_DATE, 12).toISOString());
|
||||
this.set('endTimestamp', CURRENT_DATE.toISOString());
|
||||
this.set('handleClientActivityQuery', sinon.spy());
|
||||
});
|
||||
|
||||
test('it renders and disables correct months when start date is 12 months ago', async function (assert) {
|
||||
assert.expect(14);
|
||||
await render(hbs`
|
||||
<CalendarWidget
|
||||
@startTimestamp={{this.startTimestamp}}
|
||||
@endTimestamp={{this.endTimestamp}}
|
||||
@selectMonth={{this.handleClientActivityQuery}}
|
||||
/>
|
||||
`);
|
||||
assert
|
||||
.dom('[data-test-calendar-widget-trigger]')
|
||||
.hasText(`Apr 2017 - Apr 2018`, 'renders and formats start and end dates');
|
||||
await calendarDropdown.openCalendar();
|
||||
assert.ok(calendarDropdown.showsCalendar, 'renders the calendar component');
|
||||
// assert months in current year are disabled/enabled correctly
|
||||
const enabledMonths = ['January', 'February', 'March', 'April'];
|
||||
ARRAY_OF_MONTHS.forEach(function (month) {
|
||||
if (enabledMonths.includes(month)) {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`);
|
||||
} else {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders and disables months before start timestamp', async function (assert) {
|
||||
await render(hbs`
|
||||
<CalendarWidget
|
||||
@startTimestamp={{this.startTimestamp}}
|
||||
@endTimestamp={{this.endTimestamp}}
|
||||
@selectMonth={{this.handleClientActivityQuery}}
|
||||
/>
|
||||
`);
|
||||
|
||||
await calendarDropdown.openCalendar();
|
||||
assert.dom('[data-test-next-year]').isDisabled('Future year is disabled');
|
||||
await calendarDropdown.clickPreviousYear();
|
||||
assert
|
||||
.dom('[data-test-display-year]')
|
||||
.hasText(`${subYears(this.currentDate, 1).getFullYear()}`, 'shows the previous year');
|
||||
assert.dom('[data-test-previous-year]').isDisabled('disables previous year');
|
||||
|
||||
// assert months in previous year are disabled/enabled correctly
|
||||
const disabledMonths = ['January', 'February', 'March'];
|
||||
ARRAY_OF_MONTHS.forEach(function (month) {
|
||||
if (disabledMonths.includes(month)) {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`);
|
||||
} else {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('it calls parent callback with correct arg when clicking "Current billing period"', async function (assert) {
|
||||
await render(hbs`
|
||||
<CalendarWidget
|
||||
@startTimestamp={{this.startTimestamp}}
|
||||
@endTimestamp={{this.endTimestamp}}
|
||||
@selectMonth={{this.handleClientActivityQuery}}
|
||||
/>
|
||||
`);
|
||||
await calendarDropdown.menuToggle();
|
||||
await calendarDropdown.clickCurrentBillingPeriod();
|
||||
assert.propEqual(
|
||||
this.handleClientActivityQuery.args[0][0],
|
||||
{ dateType: 'reset' },
|
||||
'it calls parent function with reset dateType'
|
||||
);
|
||||
});
|
||||
|
||||
test('it calls parent callback with correct arg when clicking "Current month"', async function (assert) {
|
||||
await render(hbs`
|
||||
<CalendarWidget
|
||||
@startTimestamp={{this.startTimestamp}}
|
||||
@endTimestamp={{this.endTimestamp}}
|
||||
@selectMonth={{this.handleClientActivityQuery}}
|
||||
/>
|
||||
`);
|
||||
await calendarDropdown.menuToggle();
|
||||
await calendarDropdown.clickCurrentMonth();
|
||||
assert.propEqual(
|
||||
this.handleClientActivityQuery.args[0][0],
|
||||
{ dateType: 'currentMonth' },
|
||||
'it calls parent function with currentMoth dateType'
|
||||
);
|
||||
});
|
||||
|
||||
test('it calls parent callback with correct arg when selecting a month', async function (assert) {
|
||||
await render(hbs`
|
||||
<CalendarWidget
|
||||
@startTimestamp={{this.startTimestamp}}
|
||||
@endTimestamp={{this.endTimestamp}}
|
||||
@selectMonth={{this.handleClientActivityQuery}}
|
||||
/>
|
||||
`);
|
||||
await calendarDropdown.openCalendar();
|
||||
await click(`[data-test-calendar-month="April"`);
|
||||
assert.propEqual(
|
||||
this.handleClientActivityQuery.lastCall.lastArg,
|
||||
{
|
||||
dateType: 'endDate',
|
||||
monthIdx: 3,
|
||||
monthName: 'April',
|
||||
year: 2018,
|
||||
},
|
||||
'it calls parent function with end date (current) month/year'
|
||||
);
|
||||
|
||||
await calendarDropdown.openCalendar();
|
||||
await calendarDropdown.clickPreviousYear();
|
||||
await click(`[data-test-calendar-month="May"]`);
|
||||
assert.propEqual(
|
||||
this.handleClientActivityQuery.lastCall.lastArg,
|
||||
{
|
||||
dateType: 'endDate',
|
||||
monthIdx: 4,
|
||||
monthName: 'May',
|
||||
year: 2017,
|
||||
},
|
||||
'it calls parent function with selected start date month/year'
|
||||
);
|
||||
});
|
||||
|
||||
test('it disables correct months when start date 6 months ago', async function (assert) {
|
||||
this.set('calendarStartDate', subMonths(this.currentDate, 6)); // Nov 3, 2017
|
||||
this.set('startTimestamp', subMonths(this.currentDate, 6).toISOString());
|
||||
await render(hbs`
|
||||
<CalendarWidget
|
||||
@startTimestamp={{this.startTimestamp}}
|
||||
@endTimestamp={{this.endTimestamp}}
|
||||
@selectMonth={{this.handleClientActivityQuery}}
|
||||
/>
|
||||
`);
|
||||
|
||||
await calendarDropdown.openCalendar();
|
||||
assert.dom('[data-test-next-year]').isDisabled('Future year is disabled');
|
||||
|
||||
// Check start year disables correct months
|
||||
await calendarDropdown.clickPreviousYear();
|
||||
assert.dom('[data-test-previous-year]').isDisabled('previous year is disabled');
|
||||
const prevYearEnabled = ['October', 'November', 'December'];
|
||||
ARRAY_OF_MONTHS.forEach(function (month) {
|
||||
if (prevYearEnabled.includes(month)) {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`);
|
||||
} else {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is read only`);
|
||||
}
|
||||
});
|
||||
|
||||
// Check end year disables correct months
|
||||
await click('[data-test-next-year]');
|
||||
const currYearEnabled = ['January', 'February', 'March', 'April'];
|
||||
ARRAY_OF_MONTHS.forEach(function (month) {
|
||||
if (currYearEnabled.includes(month)) {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`);
|
||||
} else {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('it disables correct months when start date 36 months ago', async function (assert) {
|
||||
this.set('calendarStartDate', subMonths(this.currentDate, 36)); // April 3 2015
|
||||
this.set('startTimestamp', subMonths(this.currentDate, 36).toISOString());
|
||||
await render(hbs`
|
||||
<CalendarWidget
|
||||
@startTimestamp={{this.startTimestamp}}
|
||||
@endTimestamp={{this.endTimestamp}}
|
||||
@selectMonth={{this.handleClientActivityQuery}}
|
||||
/>
|
||||
`);
|
||||
|
||||
await calendarDropdown.openCalendar();
|
||||
assert.dom('[data-test-next-year]').isDisabled('Future year is disabled');
|
||||
for (const year of [2017, 2016, 2015]) {
|
||||
await calendarDropdown.clickPreviousYear();
|
||||
assert.dom('[data-test-display-year]').hasText(year.toString());
|
||||
}
|
||||
|
||||
assert.dom('[data-test-previous-year]').isDisabled('previous year is disabled');
|
||||
assert.dom('[data-test-next-year]').isEnabled('next year is enabled');
|
||||
|
||||
const disabledMonths = ['January', 'February', 'March'];
|
||||
ARRAY_OF_MONTHS.forEach(function (month) {
|
||||
if (disabledMonths.includes(month)) {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`);
|
||||
} else {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`);
|
||||
}
|
||||
});
|
||||
|
||||
await click('[data-test-next-year]');
|
||||
ARRAY_OF_MONTHS.forEach(function (month) {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled for 2016`);
|
||||
});
|
||||
await click('[data-test-next-year]');
|
||||
ARRAY_OF_MONTHS.forEach(function (month) {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled for 2017`);
|
||||
});
|
||||
await click('[data-test-next-year]');
|
||||
|
||||
const enabledMonths = ['January', 'February', 'March', 'April'];
|
||||
ARRAY_OF_MONTHS.forEach(function (month) {
|
||||
if (enabledMonths.includes(month)) {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`);
|
||||
} else {
|
||||
assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
106
ui/tests/integration/components/clients/date-range-test.js
Normal file
106
ui/tests/integration/components/clients/date-range-test.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'vault/tests/helpers';
|
||||
import { click, fillIn, render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import Sinon from 'sinon';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
import { CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors';
|
||||
|
||||
const DATE_RANGE = CLIENT_COUNT.dateRange;
|
||||
module('Integration | Component | clients/date-range', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
Sinon.replace(timestamp, 'now', Sinon.fake.returns(new Date('2018-04-03T14:15:30')));
|
||||
this.now = timestamp.now();
|
||||
this.startTime = '2018-01-01T14:15:30';
|
||||
this.endTime = '2019-01-31T14:15:30';
|
||||
this.onChange = Sinon.spy();
|
||||
this.renderComponent = async () => {
|
||||
await render(
|
||||
hbs`<Clients::DateRange @startTime={{this.startTime}} @endTime={{this.endTime}} @onChange={{this.onChange}} />`
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
test('it renders prompt to set dates if no start time', async function (assert) {
|
||||
this.startTime = undefined;
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(DATE_RANGE.set).exists();
|
||||
|
||||
await click(DATE_RANGE.set);
|
||||
assert.dom(DATE_RANGE.editModal).exists();
|
||||
assert.dom(DATE_RANGE.editDate('start')).hasValue('');
|
||||
await fillIn(DATE_RANGE.editDate('start'), '2018-01');
|
||||
await fillIn(DATE_RANGE.editDate('end'), '2019-01');
|
||||
await click(GENERAL.saveButton);
|
||||
assert.deepEqual(this.onChange.args[0], [
|
||||
{
|
||||
end_time: 1548892800,
|
||||
start_time: 1514764800,
|
||||
},
|
||||
]);
|
||||
assert.dom(DATE_RANGE.editModal).doesNotExist('closes modal');
|
||||
});
|
||||
|
||||
test('it renders the date range passed and can reset it', async function (assert) {
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(DATE_RANGE.dateDisplay('start')).hasText('January 2018');
|
||||
assert.dom(DATE_RANGE.dateDisplay('end')).hasText('January 2019');
|
||||
|
||||
await click(DATE_RANGE.edit);
|
||||
assert.dom(DATE_RANGE.editModal).exists();
|
||||
assert.dom(DATE_RANGE.editDate('start')).hasValue('2018-01');
|
||||
assert.dom(DATE_RANGE.editDate('end')).hasValue('2019-01');
|
||||
assert.dom(DATE_RANGE.defaultRangeAlert).doesNotExist();
|
||||
|
||||
await click(DATE_RANGE.editDate('reset'));
|
||||
assert.dom(DATE_RANGE.editDate('start')).hasValue('');
|
||||
assert.dom(DATE_RANGE.editDate('end')).hasValue('');
|
||||
assert.dom(DATE_RANGE.defaultRangeAlert).exists();
|
||||
await click(GENERAL.saveButton);
|
||||
assert.deepEqual(this.onChange.args[0], [{ start_time: undefined, end_time: undefined }]);
|
||||
});
|
||||
|
||||
test('it does not trigger onChange if date range invalid', async function (assert) {
|
||||
await this.renderComponent();
|
||||
|
||||
await click(DATE_RANGE.edit);
|
||||
await click(DATE_RANGE.editDate('reset'));
|
||||
await fillIn(DATE_RANGE.editDate('end'), '2017-05');
|
||||
assert.dom(DATE_RANGE.validation).hasText('You must supply both start and end dates.');
|
||||
await click(GENERAL.saveButton);
|
||||
assert.false(this.onChange.called);
|
||||
|
||||
await fillIn(DATE_RANGE.editDate('start'), '2018-01');
|
||||
assert.dom(DATE_RANGE.validation).hasText('Start date must be before end date.');
|
||||
await click(GENERAL.saveButton);
|
||||
assert.false(this.onChange.called);
|
||||
|
||||
await click(GENERAL.cancelButton);
|
||||
assert.false(this.onChange.called);
|
||||
assert.dom(DATE_RANGE.editModal).doesNotExist();
|
||||
});
|
||||
|
||||
test('it resets the tracked values on close', async function (assert) {
|
||||
await this.renderComponent();
|
||||
|
||||
await click(DATE_RANGE.edit);
|
||||
await click(DATE_RANGE.editDate('reset'));
|
||||
assert.dom(DATE_RANGE.editDate('start')).hasValue('');
|
||||
assert.dom(DATE_RANGE.editDate('end')).hasValue('');
|
||||
await click(GENERAL.cancelButton);
|
||||
|
||||
await click(DATE_RANGE.edit);
|
||||
assert.dom(DATE_RANGE.editDate('start')).hasValue('2018-01');
|
||||
assert.dom(DATE_RANGE.editDate('end')).hasValue('2019-01');
|
||||
});
|
||||
});
|
||||
|
|
@ -6,13 +6,12 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import { render, click, settled, findAll } from '@ember/test-helpers';
|
||||
import { render, click, findAll, fillIn } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import clientsHandler, { LICENSE_START, STATIC_NOW } from 'vault/mirage/handlers/clients';
|
||||
import { getUnixTime } from 'date-fns';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
import { CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors';
|
||||
import { dateDropdownSelect } from 'vault/tests/helpers/clients/client-count-helpers';
|
||||
import { selectChoose } from 'ember-power-select/test-support';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
import sinon from 'sinon';
|
||||
|
|
@ -57,38 +56,11 @@ module('Integration | Component | clients | Page::Counts', function (hooks) {
|
|||
`);
|
||||
});
|
||||
|
||||
test('it should render start date label and description based on version', async function (assert) {
|
||||
const versionService = this.owner.lookup('service:version');
|
||||
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(CLIENT_COUNT.counts.startLabel).hasText('Client counting start date', 'Label renders for OSS');
|
||||
assert
|
||||
.dom(CLIENT_COUNT.counts.description)
|
||||
.hasText(
|
||||
'This date is when client counting starts. Without this starting point, the data shown is not reliable.',
|
||||
'Description renders for OSS'
|
||||
);
|
||||
|
||||
versionService.set('type', 'enterprise');
|
||||
await settled();
|
||||
|
||||
assert.dom(CLIENT_COUNT.counts.startLabel).hasText('Billing start month', 'Label renders for Enterprise');
|
||||
assert
|
||||
.dom(CLIENT_COUNT.counts.description)
|
||||
.hasText(
|
||||
'This date comes from your license, and defines when client counting starts. Without this starting point, the data shown is not reliable.',
|
||||
'Description renders for Enterprise'
|
||||
);
|
||||
});
|
||||
|
||||
test('it should populate start and end month displays', async function (assert) {
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(CLIENT_COUNT.counts.startMonth).hasText('July 2023', 'Start month renders');
|
||||
assert
|
||||
.dom(CLIENT_COUNT.calendarWidget.trigger)
|
||||
.hasText('Jul 2023 - Jan 2024', 'Start and end months render in filter bar');
|
||||
assert.dom(CLIENT_COUNT.dateRange.dateDisplay('start')).hasText('July 2023', 'Start month renders');
|
||||
assert.dom(CLIENT_COUNT.dateRange.dateDisplay('end')).hasText('January 2024', 'End month renders');
|
||||
});
|
||||
|
||||
test('it should render no data empty state', async function (assert) {
|
||||
|
|
@ -123,31 +95,41 @@ module('Integration | Component | clients | Page::Counts', function (hooks) {
|
|||
});
|
||||
|
||||
test('it should send correct values on start and end date change', async function (assert) {
|
||||
assert.expect(4);
|
||||
assert.expect(3);
|
||||
const jan23start = getUnixTime(new Date('2023-01-01T00:00:00Z'));
|
||||
const dec23end = getUnixTime(new Date('2023-12-31T00:00:00Z'));
|
||||
const jan24end = getUnixTime(new Date('2024-01-31T00:00:00Z'));
|
||||
|
||||
let expected = { start_time: getUnixTime(new Date('2023-01-01T00:00:00Z')), end_time: END_TIME };
|
||||
const expected = { start_time: START_TIME, end_time: END_TIME };
|
||||
this.onFilterChange = (params) => {
|
||||
assert.deepEqual(params, expected, 'Correct values sent on filter change');
|
||||
this.startTimestamp = params.start_time || START_TIME;
|
||||
this.endTimestamp = params.end_time || END_TIME;
|
||||
this.set('startTimestamp', params.start_time || START_TIME);
|
||||
this.set('endTimestamp', params.end_time || END_TIME);
|
||||
};
|
||||
|
||||
// page starts with default billing dates, which are july 23 - jan 24
|
||||
await this.renderComponent();
|
||||
await dateDropdownSelect('January', '2023');
|
||||
|
||||
expected.start_time = END_TIME;
|
||||
await click(CLIENT_COUNT.calendarWidget.trigger);
|
||||
await click(CLIENT_COUNT.calendarWidget.currentMonth);
|
||||
// First, change only the start date
|
||||
expected.start_time = jan23start;
|
||||
// the end date which is first set to STATIC_NOW gets recalculated
|
||||
// to the end of given month/year on date range change
|
||||
expected.end_time = jan24end;
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2023-01');
|
||||
await click(GENERAL.saveButton);
|
||||
|
||||
expected.start_time = getUnixTime(this.config.billingStartTimestamp);
|
||||
await click(CLIENT_COUNT.calendarWidget.trigger);
|
||||
await click(CLIENT_COUNT.calendarWidget.currentBillingPeriod);
|
||||
// Then change only the end date
|
||||
expected.end_time = dec23end;
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await fillIn(CLIENT_COUNT.dateRange.editDate('end'), '2023-12');
|
||||
await click(GENERAL.saveButton);
|
||||
|
||||
expected = { end_time: getUnixTime(new Date('2023-12-31T00:00:00Z')) };
|
||||
await click(CLIENT_COUNT.calendarWidget.trigger);
|
||||
await click(CLIENT_COUNT.calendarWidget.customEndMonth);
|
||||
await click(CLIENT_COUNT.calendarWidget.previousYear);
|
||||
await click(CLIENT_COUNT.calendarWidget.calendarMonth('December'));
|
||||
// Then reset to billing which should reset the params
|
||||
expected.start_time = undefined;
|
||||
expected.end_time = undefined;
|
||||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await click(CLIENT_COUNT.dateRange.reset);
|
||||
await click(GENERAL.saveButton);
|
||||
});
|
||||
|
||||
test('it should render namespace and auth mount filters', async function (assert) {
|
||||
|
|
@ -254,9 +236,7 @@ module('Integration | Component | clients | Page::Counts', function (hooks) {
|
|||
await this.renderComponent();
|
||||
|
||||
assert.dom(GENERAL.emptyStateTitle).hasText('No start date found', 'Empty state renders');
|
||||
assert
|
||||
.dom(CLIENT_COUNT.counts.startDropdown)
|
||||
.exists('Date dropdown renders when start time is not provided');
|
||||
assert.dom(CLIENT_COUNT.dateRange.set).exists();
|
||||
});
|
||||
|
||||
test('it should render catch all empty state', async function (assert) {
|
||||
|
|
|
|||
|
|
@ -1,165 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import sinon from 'sinon';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { click, render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters';
|
||||
import timestamp from 'core/utils/timestamp';
|
||||
|
||||
const SELECTORS = {
|
||||
monthDropdown: '[data-test-toggle-month]',
|
||||
specificMonth: (m) => `[data-test-dropdown-month="${m}"]`,
|
||||
yearDropdown: '[data-test-toggle-year]',
|
||||
specificYear: (y) => `[data-test-dropdown-year="${y}"]`,
|
||||
submitButton: '[data-test-date-dropdown-submit]',
|
||||
monthOptions: '[data-test-dropdown-month]',
|
||||
};
|
||||
|
||||
module('Integration | Component | date-dropdown', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
sinon.replace(timestamp, 'now', sinon.fake.returns(new Date('2018-04-03T14:15:30')));
|
||||
});
|
||||
|
||||
test('it renders dropdown', async function (assert) {
|
||||
await render(hbs`
|
||||
<div class="has-padding-l">
|
||||
<DateDropdown/>
|
||||
</div>
|
||||
`);
|
||||
assert.dom(SELECTORS.submitButton).hasText('Submit', 'button renders default text');
|
||||
});
|
||||
|
||||
test('it renders dropdown and selects month and year', async function (assert) {
|
||||
assert.expect(26);
|
||||
const parentAction = (args) => {
|
||||
assert.propEqual(
|
||||
args,
|
||||
{
|
||||
dateType: 'start',
|
||||
monthIdx: 1,
|
||||
monthName: 'February',
|
||||
year: 2016,
|
||||
},
|
||||
'sends correct args to parent'
|
||||
);
|
||||
};
|
||||
this.set('parentAction', parentAction);
|
||||
|
||||
await render(hbs`
|
||||
<div class="has-padding-l">
|
||||
<DateDropdown
|
||||
@handleSubmit={{this.parentAction}}
|
||||
@dateType="start"
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
assert.dom(SELECTORS.submitButton).isDisabled('button is disabled when no month or year selected');
|
||||
|
||||
await click(SELECTORS.monthDropdown);
|
||||
|
||||
assert.dom(SELECTORS.monthOptions).exists({ count: 12 }, 'dropdown has 12 months');
|
||||
ARRAY_OF_MONTHS.forEach((month) => {
|
||||
assert.dom(SELECTORS.specificMonth(month)).hasText(`${month}`, `dropdown includes ${month}`);
|
||||
});
|
||||
|
||||
await click(SELECTORS.specificMonth('February'));
|
||||
assert.dom(SELECTORS.monthDropdown).hasText('February', 'dropdown shows selected month');
|
||||
assert.dom('.ember-basic-dropdown-content').doesNotExist('dropdown closes after selecting month');
|
||||
|
||||
await click(SELECTORS.yearDropdown);
|
||||
|
||||
assert.dom('[data-test-dropdown-year]').exists({ count: 5 }, 'dropdown has 5 years');
|
||||
for (const year of [2018, 2017, 2016, 2015, 2014]) {
|
||||
assert.dom(SELECTORS.specificYear(year)).exists();
|
||||
}
|
||||
|
||||
await click('[data-test-dropdown-year="2016"]');
|
||||
assert.dom(SELECTORS.yearDropdown).hasText(`2016`, `dropdown shows selected year`);
|
||||
assert.dom('.ember-basic-dropdown-content').doesNotExist('dropdown closes after selecting year');
|
||||
assert.dom(SELECTORS.submitButton).isNotDisabled('button enabled when month and year selected');
|
||||
|
||||
await click(SELECTORS.submitButton);
|
||||
});
|
||||
|
||||
test('selecting month first: current year enabled when current month selected', async function (assert) {
|
||||
assert.expect(5);
|
||||
await render(hbs`
|
||||
<div class="has-padding-l">
|
||||
<DateDropdown/>
|
||||
</div>
|
||||
`);
|
||||
// select current month
|
||||
await click(SELECTORS.monthDropdown);
|
||||
await click(SELECTORS.specificMonth('January'));
|
||||
await click(SELECTORS.yearDropdown);
|
||||
// all years should be selectable
|
||||
for (const year of [2018, 2017, 2016, 2015, 2014]) {
|
||||
assert.dom(SELECTORS.specificYear(year)).isNotDisabled(`year ${year} is selectable`);
|
||||
}
|
||||
});
|
||||
|
||||
test('selecting month first: it disables current year when future months selected', async function (assert) {
|
||||
assert.expect(5);
|
||||
await render(hbs`
|
||||
<div class="has-padding-l">
|
||||
<DateDropdown/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
// select future month
|
||||
await click(SELECTORS.monthDropdown);
|
||||
await click(SELECTORS.specificMonth('June'));
|
||||
await click(SELECTORS.yearDropdown);
|
||||
|
||||
assert.dom(SELECTORS.specificYear(2018)).isDisabled(`current year is disabled`);
|
||||
// previous years should be selectable
|
||||
for (const year of [2017, 2016, 2015, 2014]) {
|
||||
assert.dom(SELECTORS.specificYear(year)).isNotDisabled(`year ${year} is selectable`);
|
||||
}
|
||||
});
|
||||
|
||||
test('selecting year first: it disables future months when current year selected', async function (assert) {
|
||||
assert.expect(12);
|
||||
await render(hbs`
|
||||
<div class="has-padding-l">
|
||||
<DateDropdown/>
|
||||
</div>
|
||||
`);
|
||||
await click(SELECTORS.yearDropdown);
|
||||
await click(SELECTORS.specificYear(2018));
|
||||
await click(SELECTORS.monthDropdown);
|
||||
|
||||
const expectedSelectable = ['January', 'February', 'March', 'April'];
|
||||
ARRAY_OF_MONTHS.forEach((month) => {
|
||||
if (expectedSelectable.includes(month)) {
|
||||
assert.dom(SELECTORS.specificMonth(month)).isNotDisabled(`${month} is selectable for current year`);
|
||||
} else {
|
||||
assert.dom(SELECTORS.specificMonth(month)).isDisabled(`${month} is disabled for current year`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('selecting year first: it enables all months when past year is selected', async function (assert) {
|
||||
assert.expect(12);
|
||||
await render(hbs`
|
||||
<div class="has-padding-l">
|
||||
<DateDropdown/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
await click(SELECTORS.yearDropdown);
|
||||
await click(SELECTORS.specificYear(2017));
|
||||
await click(SELECTORS.monthDropdown);
|
||||
|
||||
ARRAY_OF_MONTHS.forEach((month) => {
|
||||
assert.dom(SELECTORS.specificMonth(month)).isNotDisabled(`${month} is selectable for previous year`);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue