Commit 72baaa1a authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '227241-popover-sidebar-datepicker' into 'master'

Replace popover with the Glpopover

See merge request gitlab-org/gitlab!49035
parents 9c2b4578 fafdcf3e
<script> <script>
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import { GlLoadingIcon, GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { GlLoadingIcon, GlButton, GlIcon, GlTooltipDirective, GlPopover, GlLink } from '@gitlab/ui';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { dateInWords } from '~/lib/utils/datetime_utility'; import { dateInWords } from '~/lib/utils/datetime_utility';
import popover from '~/vue_shared/directives/popover';
import DatePicker from '~/vue_shared/components/pikaday.vue'; import DatePicker from '~/vue_shared/components/pikaday.vue';
import CollapsedCalendarIcon from '~/vue_shared/components/sidebar/collapsed_calendar_icon.vue'; import CollapsedCalendarIcon from '~/vue_shared/components/sidebar/collapsed_calendar_icon.vue';
import ToggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue'; import ToggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
...@@ -14,9 +13,13 @@ const label = __('Date picker'); ...@@ -14,9 +13,13 @@ const label = __('Date picker');
const pickerLabel = __('Fixed date'); const pickerLabel = __('Fixed date');
export default { export default {
dateHelpUrl: '/help/user/group/epics/index.md#start-date-and-due-date',
dateHelpValidMessage: s__(
'Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely.',
),
dateHelpInvalidUrlText: s__('Epics|How can I solve this?'),
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
popover,
}, },
components: { components: {
GlIcon, GlIcon,
...@@ -25,6 +28,8 @@ export default { ...@@ -25,6 +28,8 @@ export default {
ToggleSidebar, ToggleSidebar,
GlLoadingIcon, GlLoadingIcon,
GlButton, GlButton,
GlPopover,
GlLink,
}, },
props: { props: {
sidebarCollapsed: { sidebarCollapsed: {
...@@ -124,51 +129,8 @@ export default { ...@@ -124,51 +129,8 @@ export default {
collapsedText() { collapsedText() {
return this.selectedDateWords ? this.selectedDateWords : __('None'); return this.selectedDateWords ? this.selectedDateWords : __('None');
}, },
popoverOptions() {
return this.getPopoverConfig({
title: s__(
'Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely.',
),
content: `
<a
href="${gon.gitlab_url}/help/user/group/epics/index.md#start-date-and-due-date"
target="_blank"
rel="noopener noreferrer"
>${s__('Epics|More information')}</a>
`,
});
},
dateInvalidPopoverOptions() {
return this.getPopoverConfig({
title: this.dateInvalidTooltip,
content: `
<a
href="${gon.gitlab_url}/help/user/group/epics/index.md#start-date-and-due-date"
target="_blank"
rel="noopener noreferrer"
>${s__('Epics|How can I solve this?')}</a>
`,
});
},
}, },
methods: { methods: {
getPopoverConfig({ title, content }) {
return {
html: true,
trigger: 'focus',
container: 'body',
boundary: 'viewport',
template: `
<div class="popover" role="tooltip">
<div class="arrow"></div>
<div class="popover-header"></div>
<div class="popover-body"></div>
</div>
`,
title,
content,
};
},
stopEditing() { stopEditing() {
this.editing = false; this.editing = false;
this.$emit('toggleDateType', true, true); this.$emit('toggleDateType', true, true);
...@@ -199,11 +161,23 @@ export default { ...@@ -199,11 +161,23 @@ export default {
<gl-loading-icon v-if="dateSaveInProgress" :inline="true" /> <gl-loading-icon v-if="dateSaveInProgress" :inline="true" />
<div class="float-right d-flex"> <div class="float-right d-flex">
<gl-icon <gl-icon
v-popover="popoverOptions" ref="epicDatePopover"
name="question-o" name="question-o"
class="help-icon gl-mr-2" class="help-icon gl-mr-2"
tabindex="0" tabindex="0"
:aria-label="__('Help')"
/> />
<gl-popover
:target="() => $refs.epicDatePopover.$el"
triggers="focus"
placement="left"
boundary="viewport"
>
<p>{{ $options.dateHelpValidMessage }}</p>
<gl-link :href="$options.dateHelpUrl" target="_blank">{{
__('More information')
}}</gl-link>
</gl-popover>
<gl-button <gl-button
v-show="canUpdate && !editing" v-show="canUpdate && !editing"
ref="editButton" ref="editButton"
...@@ -244,13 +218,29 @@ export default { ...@@ -244,13 +218,29 @@ export default {
<span v-else class="d-flex value-content gl-ml-1"> <span v-else class="d-flex value-content gl-ml-1">
<template v-if="dateFixed"> <template v-if="dateFixed">
<span>{{ dateFixedWords }}</span> <span>{{ dateFixedWords }}</span>
<template v-if="isDateInvalid && selectedDateIsFixed">
<gl-icon <gl-icon
v-if="isDateInvalid && selectedDateIsFixed" ref="fixedDatePopoverWarning"
v-popover="dateInvalidPopoverOptions"
name="warning" name="warning"
class="date-warning-icon gl-mr-2 gl-ml-2" class="date-warning-icon gl-mr-2 gl-ml-2"
tabindex="0" tabindex="0"
:aria-label="__('Warning')"
/> />
<gl-popover
:target="() => $refs.fixedDatePopoverWarning.$el"
triggers="focus"
placement="left"
boundary="viewport"
>
<p>
{{ dateInvalidTooltip }}
</p>
<gl-link :href="$options.dateHelpUrl" target="_blank">{{
$options.dateHelpInvalidUrlText
}}</gl-link>
</gl-popover>
</template>
<span v-if="selectedAndEditable" class="no-value d-flex"> <span v-if="selectedAndEditable" class="no-value d-flex">
&nbsp;&ndash;&nbsp; &nbsp;&ndash;&nbsp;
<gl-button <gl-button
...@@ -281,13 +271,28 @@ export default { ...@@ -281,13 +271,28 @@ export default {
/> />
<span class="gl-ml-2">{{ __('Inherited:') }}</span> <span class="gl-ml-2">{{ __('Inherited:') }}</span>
<span class="value-content gl-ml-1">{{ dateFromMilestonesWords }}</span> <span class="value-content gl-ml-1">{{ dateFromMilestonesWords }}</span>
<template v-if="isDateInvalid && !selectedDateIsFixed">
<gl-icon <gl-icon
v-if="isDateInvalid && !selectedDateIsFixed" ref="datePopoverWarning"
v-popover="dateInvalidPopoverOptions"
name="warning" name="warning"
class="date-warning-icon gl-ml-2" class="date-warning-icon gl-ml-2"
tabindex="0" tabindex="0"
:aria-label="__('Warning')"
/> />
<gl-popover
:target="() => $refs.datePopoverWarning.$el"
triggers="focus"
placement="left"
boundary="viewport"
>
<p>
{{ dateInvalidTooltip }}
</p>
<gl-link :href="$options.dateHelpUrl" target="_blank">{{
$options.dateHelpInvalidUrlText
}}</gl-link>
</gl-popover>
</template>
</abbr> </abbr>
</div> </div>
</div> </div>
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SidebarDatePicker renders expected template 1`] = `
<div
class="block date epic-date"
>
<collapsed-calendar-icon-stub
class="sidebar-collapsed-icon"
containerclass=""
showicon="true"
text="None"
tooltiptext=""
/>
<div
class="title"
>
Date
<!---->
<div
class="float-right d-flex"
>
<gl-icon-stub
class="help-icon gl-mr-2"
name="question-o"
size="16"
tabindex="0"
/>
<gl-button-stub
buttontextclasses=""
category="primary"
class="btn-sidebar-action"
icon=""
size="medium"
variant="link"
>
Edit
</gl-button-stub>
<!---->
</div>
</div>
<div
class="value"
>
<div
class="value-type-fixed text-secondary is-option-selected d-flex"
>
<input
name="datetype_test"
type="radio"
/>
<span
class="gl-ml-2"
>
Fixed:
</span>
<span
class="d-flex value-content gl-ml-1"
>
<span
class="no-value"
>
None
</span>
</span>
</div>
<abbr
class="value-type-dynamic text-secondary d-flex gl-mt-3"
title="Select an issue with milestone to set date"
>
<input
name="datetype_test"
type="radio"
/>
<span
class="gl-ml-2"
>
Inherited:
</span>
<span
class="value-content gl-ml-1"
>
None
</span>
<!---->
</abbr>
</div>
</div>
`;
import { GlLoadingIcon, GlIcon } from '@gitlab/ui'; import { GlLoadingIcon, GlIcon, GlPopover, GlLink, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import SidebarDatepicker from 'ee/epic/components/sidebar_items/sidebar_date_picker.vue'; import SidebarDatepicker from 'ee/epic/components/sidebar_items/sidebar_date_picker.vue';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
...@@ -32,8 +32,6 @@ describe('SidebarDatePicker', () => { ...@@ -32,8 +32,6 @@ describe('SidebarDatePicker', () => {
.filter(w => w.props().name === name) .filter(w => w.props().name === name)
.at(0); .at(0);
const findEditButton = () => wrapper.find({ ref: 'editButton' }); const findEditButton = () => wrapper.find({ ref: 'editButton' });
const findDirectiveCallByTitle = title =>
mockPopoverBind.mock.calls.find(([, binding]) => binding.value.title === title);
const findRemoveButton = () => wrapper.find({ ref: 'removeButton' }); const findRemoveButton = () => wrapper.find({ ref: 'removeButton' });
const createFakeEvent = () => ({ stopPropagation: jest.fn() }); const createFakeEvent = () => ({ stopPropagation: jest.fn() });
...@@ -117,40 +115,33 @@ describe('SidebarDatePicker', () => { ...@@ -117,40 +115,33 @@ describe('SidebarDatePicker', () => {
expect(wrapper.text()).toContain('Inherited: None'); expect(wrapper.text()).toContain('Inherited: None');
}); });
it('passes correct popover options to directive', () => { it('popover has message and link', () => {
createComponent(); createComponent();
return wrapper.vm.$nextTick().then(() => {
const expectedTitle = const message =
'These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely.'; 'These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely.';
const [, binding] = findDirectiveCallByTitle(expectedTitle);
const { content, ...popoverConfig } = binding.value;
delete popoverConfig.template;
const expectedContent = '/help/user/group/epics/index.md#start-date-and-due-date';
const expectedPopoverConfig = {
html: true,
trigger: 'focus',
title: expectedTitle,
container: 'body',
boundary: 'viewport',
};
expect(mockPopoverBind).toHaveBeenCalled();
expect(popoverConfig).toStrictEqual(expectedPopoverConfig); const popover = wrapper.find(GlPopover);
expect(content).toContain(expectedContent); const popoverLink = popover.find(GlLink);
});
expect(popover.text()).toContain(message);
expect(popoverLink.text()).toBe('More information');
expect(popoverLink.attributes('href')).toContain(
'/help/user/group/epics/index.md#start-date-and-due-date',
);
}); });
it('returns popover config object containing title with appropriate string', () => { it('popover has different message and link when date is invalid', () => {
createComponent({ isDateInvalid: true, selectedDateIsFixed: false }); createComponent({ isDateInvalid: true, selectedDateIsFixed: false });
return wrapper.vm.$nextTick().then(() => {
const expectedTitle = 'Selected date is invalid';
const [, targetBinding] = findDirectiveCallByTitle(expectedTitle);
const { content } = targetBinding.value;
expect(content).toContain('/help/user/group/epics/index.md#start-date-and-due-date'); const popover = wrapper.findAll(GlPopover).at(1);
expect(content).toContain('How can I solve this?'); const popoverLink = popover.find(GlLink);
});
expect(popover.text()).toContain('Selected date is invalid');
expect(popoverLink.text()).toBe('How can I solve this?');
expect(popoverLink.attributes('href')).toContain(
'/help/user/group/epics/index.md#start-date-and-due-date',
);
}); });
it('stops editing and emits `toggleDateType` event on component on `hidePicker` from date picker', () => { it('stops editing and emits `toggleDateType` event on component on `hidePicker` from date picker', () => {
...@@ -212,12 +203,24 @@ describe('SidebarDatePicker', () => { ...@@ -212,12 +203,24 @@ describe('SidebarDatePicker', () => {
}); });
}); });
it('renders expected template', () => { it('renders help icon', () => {
createComponent({ createComponent();
fieldName: 'datetype_test',
expect(wrapper.find(GlIcon).attributes('arialabel')).toBe('Help');
});
it('renders edit button', () => {
createComponent();
expect(wrapper.find(GlButton).text()).toBe('Edit');
}); });
expect(wrapper.element).toMatchSnapshot(); it('renders an abbreviation', () => {
createComponent();
expect(wrapper.find('abbr').attributes('title')).toBe(
'Select an issue with milestone to set date',
);
}); });
it('renders collapse button when `showToggleSidebar` prop is `true`', () => { it('renders collapse button when `showToggleSidebar` prop is `true`', () => {
......
...@@ -10845,9 +10845,6 @@ msgstr "" ...@@ -10845,9 +10845,6 @@ msgstr ""
msgid "Epics|Leave empty to inherit from milestone dates" msgid "Epics|Leave empty to inherit from milestone dates"
msgstr "" msgstr ""
msgid "Epics|More information"
msgstr ""
msgid "Epics|Remove epic" msgid "Epics|Remove epic"
msgstr "" msgstr ""
...@@ -30617,6 +30614,9 @@ msgstr "" ...@@ -30617,6 +30614,9 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access." msgid "Want to see the data? Please ask an administrator for access."
msgstr "" msgstr ""
msgid "Warning"
msgstr ""
msgid "Warning:" msgid "Warning:"
msgstr "" msgstr ""
......
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