Commit 6f6bff4d authored by Robert Hunt's avatar Robert Hunt Committed by Mark Florian

Added new date range field component and app

Updated the HAML file to reference the new app rather than the generic
event_filters HAML template.

A future MR will generalise this work into the shared templates
parent 7bd06449
<script>
import { GlDaterangePicker } from '@gitlab/ui';
import { parsePikadayDate, pikadayToString } from '~/lib/utils/datetime_utility';
import { queryToObject } from '~/lib/utils/url_utility';
export default {
name: 'DateRangeField',
components: {
GlDaterangePicker,
},
props: {
formElement: {
type: HTMLFormElement,
required: true,
},
},
data() {
const data = {
startDate: null,
endDate: null,
};
const { created_after: initialStartDate, created_before: initialEndDate } = queryToObject(
window.location.search,
);
if (initialStartDate) {
data.startDate = parsePikadayDate(initialStartDate);
}
if (initialEndDate) {
data.endDate = parsePikadayDate(initialEndDate);
}
return data;
},
computed: {
createdAfter() {
return this.startDate ? pikadayToString(this.startDate) : '';
},
createdBefore() {
return this.endDate ? pikadayToString(this.endDate) : '';
},
},
methods: {
handleInput(dates) {
this.startDate = dates.startDate;
this.endDate = dates.endDate;
this.$nextTick(() => this.formElement.submit());
},
},
};
</script>
<template>
<div>
<gl-daterange-picker
class="d-flex flex-wrap flex-sm-nowrap"
:default-start-date="startDate"
:default-end-date="endDate"
start-picker-class="form-group align-items-lg-center mr-0 mr-sm-1 d-flex flex-column flex-lg-row"
end-picker-class="form-group align-items-lg-center mr-0 mr-sm-2 d-flex flex-column flex-lg-row"
@input="handleInput"
/>
<input type="hidden" name="created_after" :value="createdAfter" />
<input type="hidden" name="created_before" :value="createdBefore" />
</div>
</template>
import Vue from 'vue';
import DateRangeField from './components/date_range_field.vue';
import AuditLogs from './audit_logs'; import AuditLogs from './audit_logs';
document.addEventListener('DOMContentLoaded', () => new AuditLogs()); document.addEventListener('DOMContentLoaded', () => new AuditLogs());
document.addEventListener('DOMContentLoaded', () => {
const el = document.querySelector('#js-audit-logs-date-range-app');
const formElement = el.closest('form');
// eslint-disable-next-line no-new
new Vue({
el,
name: 'AuditLogsDateRangeApp',
render: createElement =>
createElement(DateRangeField, {
props: {
...el.dataset,
formElement,
},
}),
});
});
.audit-controls { .audit-controls {
// TODO: Remove this once https://gitlab.com/gitlab-org/gitlab-ui/-/issues/681 is resolved
.gl-datepicker-theme.pika-single {
position: absolute !important;
}
.dropdown-menu-toggle {
// New GitLab UI inputs are 32px high, while the older inputs are 34px
// This can be removed once the audit log fields are converted to use GitLab UI's dropdown instead
padding-bottom: 5px;
padding-top: 5px;
.fa-chevron-down,
.fa-spinner {
top: 10px;
}
}
@include media-breakpoint-down(md) { @include media-breakpoint-down(md) {
.dropdown-menu-toggle, .dropdown-menu-toggle,
.filter-item { .filter-item {
......
...@@ -43,8 +43,11 @@ ...@@ -43,8 +43,11 @@
placeholder: @entity&.full_path || _('Search groups'), idAttribute: 'id', placeholder: @entity&.full_path || _('Search groups'), idAttribute: 'id',
data: { order_by: 'last_activity_at', idattribute: 'id', all_available: true } }) data: { order_by: 'last_activity_at', idattribute: 'id', all_available: true } })
.d-flex.col-lg-auto .d-flex.col-lg-auto.flex-wrap
= render 'shared/audit_events/event_filter' %form.row-content-block.second-block.d-flex.justify-content-lg-end.pb-0
.audit-controls.d-flex.align-items-lg-center.flex-column.flex-lg-row.col-lg-auto.px-0
#js-audit-logs-date-range-app
= render 'shared/audit_events/event_sort'
- if @events.present? - if @events.present?
%table#events-table.table %table#events-table.table
......
...@@ -112,11 +112,27 @@ describe 'Admin::AuditLogs', :js do ...@@ -112,11 +112,27 @@ describe 'Admin::AuditLogs', :js do
let_it_be(:audit_event_2) { create(:user_audit_event, created_at: 3.days.ago) } let_it_be(:audit_event_2) { create(:user_audit_event, created_at: 3.days.ago) }
let_it_be(:audit_event_3) { create(:user_audit_event, created_at: 1.day.ago) } let_it_be(:audit_event_3) { create(:user_audit_event, created_at: 1.day.ago) }
before do it 'shows only 2 days old events' do
visit admin_audit_logs_path visit admin_audit_logs_path(created_after: 4.days.ago.to_date, created_before: 2.days.ago.to_date)
expect(page).to have_content(audit_event_2.present.date)
expect(page).not_to have_content(audit_event_1.present.date)
expect(page).not_to have_content(audit_event_3.present.date)
end
it 'shows only yesterday events' do
visit admin_audit_logs_path(created_after: 2.days.ago.to_date)
expect(page).to have_content(audit_event_3.present.date)
expect(page).not_to have_content(audit_event_1.present.date)
expect(page).not_to have_content(audit_event_2.present.date)
end end
it_behaves_like 'audit events filter' it 'shows a message if provided date is invalid' do
visit admin_audit_logs_path(created_after: '12-345-6789')
expect(page).to have_content('Invalid date format. Please use UTC format as YYYY-MM-DD')
end
end end
end end
......
import { shallowMount } from '@vue/test-utils';
import { GlDaterangePicker } from '@gitlab/ui';
import DateRangeField from 'ee/pages/admin/audit_logs/components/date_range_field.vue';
import { parsePikadayDate } from '~/lib/utils/datetime_utility';
describe('DateRangeField component', () => {
const DATE = '1970-01-01';
let wrapper;
const createComponent = (props = {}) => {
const formElement = document.createElement('form');
document.body.appendChild(formElement);
return shallowMount(DateRangeField, {
propsData: { formElement, ...props },
});
};
beforeEach(() => {
delete window.location;
window.location = { search: '' };
});
afterEach(() => {
document.querySelector('form').remove();
wrapper.destroy();
});
it('should populate the initial start date if passed in the query string', () => {
window.location.search = `?created_after=${DATE}`;
wrapper = createComponent();
expect(wrapper.find(GlDaterangePicker).props()).toMatchObject({
defaultStartDate: parsePikadayDate(DATE),
defaultEndDate: null,
});
});
it('should populate the initial end date if passed in the query string', () => {
window.location.search = `?created_before=${DATE}`;
wrapper = createComponent();
expect(wrapper.find(GlDaterangePicker).props()).toMatchObject({
defaultStartDate: null,
defaultEndDate: parsePikadayDate(DATE),
});
});
it('should populate both the initial start and end dates if passed in the query string', () => {
window.location.search = `?created_after=${DATE}&created_before=${DATE}`;
wrapper = createComponent();
expect(wrapper.find(GlDaterangePicker).props()).toMatchObject({
defaultStartDate: parsePikadayDate(DATE),
defaultEndDate: parsePikadayDate(DATE),
});
});
it('should populate the date hidden fields on input', () => {
wrapper = createComponent();
wrapper
.find(GlDaterangePicker)
.vm.$emit('input', { startDate: parsePikadayDate(DATE), endDate: parsePikadayDate(DATE) });
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find('input[name="created_after"]').attributes().value).toEqual(DATE);
expect(wrapper.find('input[name="created_before"]').attributes().value).toEqual(DATE);
});
});
it('should submit the form on input change', () => {
wrapper = createComponent();
const spy = jest.spyOn(wrapper.props().formElement, 'submit');
wrapper
.find(GlDaterangePicker)
.vm.$emit('input', { startDate: parsePikadayDate(DATE), endDate: parsePikadayDate(DATE) });
return wrapper.vm.$nextTick().then(() => {
expect(spy).toHaveBeenCalledTimes(1);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment