diff --git a/ee/app/assets/javascripts/analytics/productivity_analytics/index.js b/ee/app/assets/javascripts/analytics/productivity_analytics/index.js index 9edb6fc806889329680662ae2af58c561b819b12..47ad86bf0dbb97bdd86471e60a05347bd688b9b4 100644 --- a/ee/app/assets/javascripts/analytics/productivity_analytics/index.js +++ b/ee/app/assets/javascripts/analytics/productivity_analytics/index.js @@ -1,7 +1,10 @@ import Vue from 'vue'; +import { mapState, mapActions } from 'vuex'; +import { getDateInPast } from '~/lib/utils/datetime_utility'; +import { defaultDaysInPast } from './constants'; import store from './store'; import FilterDropdowns from './components/filter_dropdowns.vue'; -import DateRange from './components/daterange.vue'; +import DateRange from '../shared/components/daterange.vue'; import ProductivityAnalyticsApp from './components/app.vue'; import FilteredSearchProductivityAnalytics from './filtered_search_productivity_analytics'; import { getLabelsEndpoint, getMilestonesEndpoint } from './utils'; @@ -18,6 +21,10 @@ export default () => { const { endpoint, emptyStateSvgPath, noAccessSvgPath } = appContainer.dataset; + const now = new Date(Date.now()); + const defaultStartDate = new Date(getDateInPast(now, defaultDaysInPast)); + const defaultEndDate = now; + let filterManager; // eslint-disable-next-line no-new @@ -70,8 +77,31 @@ export default () => { new Vue({ el: timeframeContainer, store, + computed: { + ...mapState('filters', ['groupNamespace', 'startDate', 'endDate']), + }, + mounted() { + // let's not fetch data since we might not have a groupNamespace selected yet + // this just populates the store with the initial data and waits for a groupNamespace to be set + this.setDateRange({ startDate: defaultStartDate, endDate: defaultEndDate, skipFetch: true }); + }, + methods: { + ...mapActions('filters', ['setDateRange']), + onDateRangeChange({ startDate, endDate }) { + this.setDateRange({ startDate, endDate }); + }, + }, render(h) { - return h(DateRange, {}); + return h(DateRange, { + props: { + show: this.groupNamespace !== null, + startDate: defaultStartDate, + endDate: defaultEndDate, + }, + on: { + change: this.onDateRangeChange, + }, + }); }, }); diff --git a/ee/app/assets/javascripts/analytics/productivity_analytics/components/daterange.vue b/ee/app/assets/javascripts/analytics/shared/components/daterange.vue similarity index 51% rename from ee/app/assets/javascripts/analytics/productivity_analytics/components/daterange.vue rename to ee/app/assets/javascripts/analytics/shared/components/daterange.vue index bed4118e510e2477374398de286254b1424f3992..81bfe151a8e7a72f666682a009ee5022c4411670 100644 --- a/ee/app/assets/javascripts/analytics/productivity_analytics/components/daterange.vue +++ b/ee/app/assets/javascripts/analytics/shared/components/daterange.vue @@ -1,43 +1,42 @@ <script> -import { mapState, mapActions } from 'vuex'; import { GlDaterangePicker } from '@gitlab/ui'; -import { getDateInPast } from '~/lib/utils/datetime_utility'; -import { defaultDaysInPast } from '../constants'; export default { components: { GlDaterangePicker, }, + props: { + show: { + type: Boolean, + required: false, + default: true, + }, + startDate: { + type: Date, + required: false, + default: null, + }, + endDate: { + type: Date, + required: false, + default: null, + }, + }, computed: { - ...mapState('filters', ['groupNamespace', 'startDate', 'endDate']), dateRange: { get() { return { startDate: this.startDate, endDate: this.endDate }; }, set({ startDate, endDate }) { - this.setDateRange({ startDate, endDate }); + this.$emit('change', { startDate, endDate }); }, }, }, - mounted() { - this.initDateRange(); - }, - methods: { - ...mapActions('filters', ['setDateRange']), - initDateRange() { - const endDate = new Date(Date.now()); - const startDate = new Date(getDateInPast(endDate, defaultDaysInPast)); - - // let's not fetch data since we might not have a groupNamespace selected yet - // this just populates the store with the initial data and waits for a groupNamespace to be set - this.setDateRange({ skipFetch: true, startDate, endDate }); - }, - }, }; </script> <template> <div - v-if="groupNamespace" + v-if="show" class="daterange-container d-flex flex-column flex-lg-row align-items-lg-center justify-content-lg-end" > <gl-daterange-picker diff --git a/ee/spec/frontend/analytics/productivity_analytics/components/daterange_spec.js b/ee/spec/frontend/analytics/productivity_analytics/components/daterange_spec.js deleted file mode 100644 index 28251ddcad7a75c6534658ad9146932dccc4a840..0000000000000000000000000000000000000000 --- a/ee/spec/frontend/analytics/productivity_analytics/components/daterange_spec.js +++ /dev/null @@ -1,108 +0,0 @@ -import { createLocalVue, shallowMount } from '@vue/test-utils'; -import Vuex from 'vuex'; -import AxiosMockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import Daterange from 'ee/analytics/productivity_analytics/components/daterange.vue'; -import store from 'ee/analytics/productivity_analytics/store'; -import { GlDaterangePicker } from '@gitlab/ui'; -import resetStore from '../helpers'; - -const localVue = createLocalVue(); -localVue.use(Vuex); - -const startDate = new Date(2019, 8, 1); -const endDate = new Date(2019, 8, 11); -const groupNamespace = 'gitlab-org'; - -describe('Daterange component', () => { - let wrapper; - let axiosMock; - - const actionSpies = { - setDateRange: jest.fn(), - }; - - const factory = (props = {}) => { - wrapper = shallowMount(localVue.extend(Daterange), { - localVue, - store, - sync: false, - propsData: { ...props }, - methods: { - ...actionSpies, - }, - }); - }; - - beforeEach(() => { - axiosMock = new AxiosMockAdapter(axios); - axiosMock.onGet(store.state.endpoint).reply(200); - jest.spyOn(global.Date, 'now').mockImplementation(() => new Date('2019-09-25T00:00:00Z')); - factory(); - }); - - afterEach(() => { - wrapper.destroy(); - resetStore(store); - axiosMock.restore(); - }); - - const findDaterangePicker = () => wrapper.find(GlDaterangePicker); - - describe('template', () => { - describe('when there is no groupNamespace set', () => { - it('does not render the daterange picker', () => { - expect(findDaterangePicker().exists()).toBe(false); - }); - }); - - describe('when a groupNamespace is set', () => { - beforeEach(() => { - store.state.filters.groupNamespace = groupNamespace; - }); - - it('renders the daterange picker', () => { - expect(findDaterangePicker().exists()).toBe(true); - }); - }); - }); - - describe('mounted', () => { - describe('initDateRange', () => { - it('dispatches setDateRange with skipFetch=true', () => { - expect(actionSpies.setDateRange).toHaveBeenCalledWith({ - skipFetch: true, - startDate: new Date('2019-08-26T00:00:00.000Z'), - endDate: new Date('2019-09-25T00:00:00.000Z'), - }); - }); - }); - }); - - describe('computed', () => { - beforeEach(() => { - store.state.filters.groupNamespace = groupNamespace; - }); - - describe('dateRange', () => { - describe('set', () => { - it('calls `setDateRange` with an object containing startDate and endDate', () => { - wrapper.vm.dateRange = { startDate, endDate }; - - expect(actionSpies.setDateRange).toHaveBeenCalledWith({ startDate, endDate }); - }); - }); - - describe('get', () => { - beforeEach(() => { - store.state.filters.startDate = startDate; - store.state.filters.endDate = endDate; - }); - - it("returns value of dateRange from state's startDate and endDate", () => { - expect(wrapper.vm.dateRange).toEqual({ startDate, endDate }); - }); - }); - }); - }); -}); diff --git a/ee/spec/frontend/analytics/shared/components/daterange_spec.js b/ee/spec/frontend/analytics/shared/components/daterange_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..0922feea04873c93fafcfcd75354366cb7d62a84 --- /dev/null +++ b/ee/spec/frontend/analytics/shared/components/daterange_spec.js @@ -0,0 +1,79 @@ +import { shallowMount } from '@vue/test-utils'; +import Daterange from 'ee/analytics/shared/components/daterange.vue'; +import { GlDaterangePicker } from '@gitlab/ui'; + +const defaultProps = { + startDate: new Date(2019, 8, 1), + endDate: new Date(2019, 8, 11), +}; + +describe('Daterange component', () => { + let wrapper; + + const factory = (props = defaultProps) => { + wrapper = shallowMount(Daterange, { + propsData: { + ...defaultProps, + ...props, + }, + }); + }; + + beforeEach(() => { + jest.spyOn(global.Date, 'now').mockImplementation(() => new Date('2019-09-25T00:00:00Z')); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + const findDaterangePicker = () => wrapper.find(GlDaterangePicker); + + describe('template', () => { + describe('when show is false', () => { + it('does not render the daterange picker', () => { + factory({ show: false }); + expect(findDaterangePicker().exists()).toBe(false); + }); + }); + + describe('when show is true', () => { + it('renders the daterange picker', () => { + factory({ show: true }); + expect(findDaterangePicker().exists()).toBe(true); + }); + }); + }); + + describe('computed', () => { + describe('dateRange', () => { + beforeEach(() => { + factory({ show: true }); + }); + + describe('set', () => { + it('emits the change event with an object containing startDate and endDate', () => { + const startDate = new Date('2019-10-01'); + const endDate = new Date('2019-10-05'); + wrapper.vm.dateRange = { startDate, endDate }; + + expect(wrapper.emittedByOrder()).toEqual([ + { + name: 'change', + args: [{ startDate, endDate }], + }, + ]); + }); + }); + + describe('get', () => { + it("returns value of dateRange from state's startDate and endDate", () => { + expect(wrapper.vm.dateRange).toEqual({ + startDate: defaultProps.startDate, + endDate: defaultProps.endDate, + }); + }); + }); + }); + }); +});