Commit 7539bb17 authored by Illya Klymov's avatar Illya Klymov

Merge branch '227262-migrate-project-new-popover' into 'master'

Migrate project new popover

See merge request gitlab-org/gitlab!54700
parents d17e2ab4 2f5e1192
<script>
import { GlPopover, GlFormInputGroup } from '@gitlab/ui';
import { __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
export default {
components: {
GlPopover,
GlFormInputGroup,
ClipboardButton,
},
inject: ['pushToCreateProjectCommand', 'workingWithProjectsHelpPath'],
props: {
target: {
type: [Function, HTMLElement],
required: true,
},
},
i18n: {
clipboardButtonTitle: __('Copy command'),
commandInputAriaLabel: __('Push project from command line'),
helpLinkText: __('What does this command do?'),
labelText: __('Private projects can be created in your personal namespace with:'),
popoverTitle: __('Push to create a project'),
},
};
</script>
<template>
<gl-popover
:target="target"
:title="$options.i18n.popoverTitle"
triggers="click blur"
placement="top"
>
<p>
<label for="push-to-create-tip" class="gl-font-weight-normal">
{{ $options.i18n.labelText }}
</label>
</p>
<p>
<gl-form-input-group
id="push-to-create-tip"
:value="pushToCreateProjectCommand"
readonly
select-on-click
:aria-label="$options.i18n.commandInputAriaLabel"
>
<template #append>
<clipboard-button
:text="pushToCreateProjectCommand"
:title="$options.i18n.clipboardButtonTitle"
tooltip-placement="right"
/>
</template>
</gl-form-input-group>
</p>
<p>
<a
:href="`${workingWithProjectsHelpPath}#push-to-create-a-new-project`"
class="gl-font-sm"
target="_blank"
>{{ $options.i18n.helpLinkText }}</a
>
</p>
</gl-popover>
</template>
<script> <script>
/* eslint-disable vue/no-v-html */ /* eslint-disable vue/no-v-html */
import { GlPopover } from '@gitlab/ui';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import LegacyContainer from './legacy_container.vue'; import NewProjectPushTipPopover from './new_project_push_tip_popover.vue';
const trackingMixin = Tracking.mixin(gon.tracking_data); const trackingMixin = Tracking.mixin(gon.tracking_data);
export default { export default {
components: { components: {
GlPopover, NewProjectPushTipPopover,
LegacyContainer,
}, },
mixins: [trackingMixin], mixins: [trackingMixin],
props: { props: {
...@@ -52,19 +50,15 @@ export default { ...@@ -52,19 +50,15 @@ export default {
<p> <p>
{{ __('You can also create a project from the command line.') }} {{ __('You can also create a project from the command line.') }}
<a <a
id="cli-tip" ref="clipTip"
href="#" href="#"
click.prevent click.prevent
class="push-new-project-tip" class="push-new-project-tip"
data-title="Push to create a project"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
{{ __('Show command') }} {{ __('Show command') }}
</a> </a>
<new-project-push-tip-popover :target="() => $refs.clipTip" />
<gl-popover target="cli-tip" triggers="click blur" placement="top">
<legacy-container selector=".push-new-project-tip-template" />
</gl-popover>
</p> </p>
</div> </div>
</div> </div>
......
...@@ -2,11 +2,17 @@ import Vue from 'vue'; ...@@ -2,11 +2,17 @@ import Vue from 'vue';
import NewProjectCreationApp from './components/app.vue'; import NewProjectCreationApp from './components/app.vue';
export default function initNewProjectCreation(el, props) { export default function initNewProjectCreation(el, props) {
const { pushToCreateProjectCommand, workingWithProjectsHelpPath } = el.dataset;
return new Vue({ return new Vue({
el, el,
components: { components: {
NewProjectCreationApp, NewProjectCreationApp,
}, },
provide: {
workingWithProjectsHelpPath,
pushToCreateProjectCommand,
},
render(h) { render(h) {
return h(NewProjectCreationApp, { props }); return h(NewProjectCreationApp, { props });
}, },
......
import $ from 'jquery'; import $ from 'jquery';
import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates'; import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates';
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
import { import {
convertToTitleCase, convertToTitleCase,
humanize, humanize,
...@@ -81,7 +80,6 @@ const bindEvents = () => { ...@@ -81,7 +80,6 @@ const bindEvents = () => {
const $selectedTemplateText = $('.selected-template'); const $selectedTemplateText = $('.selected-template');
const $changeTemplateBtn = $('.change-template'); const $changeTemplateBtn = $('.change-template');
const $selectedIcon = $('.selected-icon'); const $selectedIcon = $('.selected-icon');
const $pushNewProjectTipTrigger = $('.push-new-project-tip');
const $projectTemplateButtons = $('.project-templates-buttons'); const $projectTemplateButtons = $('.project-templates-buttons');
const $projectName = $('.tab-pane.active #project_name'); const $projectName = $('.tab-pane.active #project_name');
...@@ -108,39 +106,6 @@ const bindEvents = () => { ...@@ -108,39 +106,6 @@ const bindEvents = () => {
); );
}); });
if ($pushNewProjectTipTrigger) {
$pushNewProjectTipTrigger
.removeAttr('rel')
.removeAttr('target')
.on('click', (e) => {
e.preventDefault();
})
.popover({
title: $pushNewProjectTipTrigger.data('title'),
placement: 'bottom',
html: true,
content: $('.push-new-project-tip-template').html(),
})
.on('shown.bs.popover', () => {
$(document).on('click.popover touchstart.popover', (event) => {
if ($(event.target).closest('.popover').length === 0) {
$pushNewProjectTipTrigger.trigger('click');
}
});
const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find(
'.js-select-on-focus',
);
addSelectOnFocusBehaviour(target);
target.focus();
})
.on('hide.bs.popover', () => {
// eslint-disable-next-line @gitlab/no-global-event-off
$(document).off('click.popover touchstart.popover');
});
}
function chooseTemplate() { function chooseTemplate() {
$projectTemplateButtons.addClass('hidden'); $projectTemplateButtons.addClass('hidden');
$projectFieldsForm.addClass('selected'); $projectFieldsForm.addClass('selected');
......
.push-to-create-popover
%p
= label_tag(:push_to_create_tip, _("Private projects can be created in your personal namespace with:"), class: "weight-normal")
%p.input-group.project-tip-command
%span
= text_field_tag :push_to_create_tip, push_to_create_project_command, class: "js-select-on-focus form-control monospace", readonly: true, aria: { label: _("Push project from command line") }
%span.input-group-append
= clipboard_button(text: push_to_create_project_command, title: _("Copy command"), class: 'input-group-text', placement: "right")
%p
= link_to("What does this command do?", help_page_path("user/project/working_with_projects", anchor: "push-to-create-a-new-project"), target: "_blank")
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.project-edit-errors .project-edit-errors
= render 'projects/errors' = render 'projects/errors'
.js-experiment-new-project-creation{ data: { is_ci_cd_available: (ci_cd_projects_available? if Gitlab.ee?), has_errors: @project.errors.any?, new_project_guidelines: brand_new_project_guidelines } } .js-experiment-new-project-creation{ data: { is_ci_cd_available: (ci_cd_projects_available? if Gitlab.ee?), has_errors: @project.errors.any?, new_project_guidelines: brand_new_project_guidelines, push_to_create_project_command: push_to_create_project_command, working_with_projects_help_path: help_page_path("user/project/working_with_projects") } }
.row{ 'v-cloak': true } .row{ 'v-cloak': true }
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
...@@ -28,9 +28,6 @@ ...@@ -28,9 +28,6 @@
%p %p
%strong= _("Tip:") %strong= _("Tip:")
= _("You can also create a project from the command line.") = _("You can also create a project from the command line.")
%a.push-new-project-tip{ data: { title: _("Push to create a project") }, href: help_page_path('user/project/working_with_projects', anchor: 'push-to-create-a-new-project'), target: "_blank", rel: "noopener noreferrer" }
= _("Show command")
%template.push-new-project-tip-template= render partial: "new_project_push_tip"
.col-lg-9.js-toggle-container .col-lg-9.js-toggle-container
%ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' } %ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' }
......
...@@ -105,7 +105,6 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders ...@@ -105,7 +105,6 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders
<div <div
class="gl-popover" class="gl-popover"
title="nora.schaden"
> >
<p <p
class="gl-m-0" class="gl-m-0"
...@@ -151,7 +150,6 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders ...@@ -151,7 +150,6 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders
<div <div
class="gl-popover" class="gl-popover"
title="racheal.loving"
> >
<p <p
class="gl-m-0" class="gl-m-0"
......
...@@ -33547,6 +33547,9 @@ msgstr "" ...@@ -33547,6 +33547,9 @@ msgstr ""
msgid "What describes you best?" msgid "What describes you best?"
msgstr "" msgstr ""
msgid "What does this command do?"
msgstr ""
msgid "What is squashing?" msgid "What is squashing?"
msgstr "" msgstr ""
......
...@@ -49,7 +49,7 @@ RSpec.describe 'Project' do ...@@ -49,7 +49,7 @@ RSpec.describe 'Project' do
it 'shows the command in a popover', :js do it 'shows the command in a popover', :js do
click_link 'Show command' click_link 'Show command'
expect(page).to have_css('.popover .push-to-create-popover #push_to_create_tip') expect(page).to have_css('.popover #push-to-create-tip')
expect(page).to have_content 'Private projects can be created in your personal namespace with:' expect(page).to have_content 'Private projects can be created in your personal namespace with:'
end end
end end
......
...@@ -39,7 +39,10 @@ jest.mock('@gitlab/ui/dist/components/base/popover/popover.js', () => ({ ...@@ -39,7 +39,10 @@ jest.mock('@gitlab/ui/dist/components/base/popover/popover.js', () => ({
default: () => [], default: () => [],
}, },
...Object.fromEntries( ...Object.fromEntries(
['target', 'triggers', 'placement', 'boundary', 'container'].map((prop) => [prop, {}]), ['title', 'target', 'triggers', 'placement', 'boundary', 'container'].map((prop) => [
prop,
{},
]),
), ),
}, },
render(h) { render(h) {
......
import { GlPopover, GlFormInputGroup } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import NewProjectPushTipPopover from '~/projects/experiment_new_project_creation/components/new_project_push_tip_popover.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
describe('New project push tip popover', () => {
let wrapper;
const targetId = 'target';
const pushToCreateProjectCommand = 'command';
const workingWithProjectsHelpPath = 'path';
const findPopover = () => wrapper.findComponent(GlPopover);
const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
const findFormInput = () => wrapper.findComponent(GlFormInputGroup);
const findHelpLink = () => wrapper.find('a');
const findTarget = () => document.getElementById(targetId);
const buildWrapper = () => {
wrapper = shallowMount(NewProjectPushTipPopover, {
propsData: {
target: findTarget(),
},
stubs: {
GlFormInputGroup,
},
provide: {
pushToCreateProjectCommand,
workingWithProjectsHelpPath,
},
});
};
beforeEach(() => {
setFixtures(`<a id="${targetId}"></a>`);
buildWrapper();
});
afterEach(() => {
wrapper.destroy();
});
it('renders popover that targets the specified target', () => {
expect(findPopover().props()).toMatchObject({
target: findTarget(),
triggers: 'click blur',
placement: 'top',
title: 'Push to create a project',
});
});
it('renders a readonly form input with the push to create command', () => {
expect(findFormInput().props()).toMatchObject({
value: pushToCreateProjectCommand,
selectOnClick: true,
});
expect(findFormInput().attributes()).toMatchObject({
'aria-label': 'Push project from command line',
readonly: 'readonly',
});
});
it('allows copying the push command using the clipboard button', () => {
expect(findClipboardButton().props()).toMatchObject({
text: pushToCreateProjectCommand,
tooltipPlacement: 'right',
title: 'Copy command',
});
});
it('displays a link to open the push command help page reference', () => {
expect(findHelpLink().attributes().href).toBe(
`${workingWithProjectsHelpPath}#push-to-create-a-new-project`,
);
});
});
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { mockTracking } from 'helpers/tracking_helper'; import { mockTracking } from 'helpers/tracking_helper';
import NewProjectPushTipPopover from '~/projects/experiment_new_project_creation/components/new_project_push_tip_popover.vue';
import WelcomePage from '~/projects/experiment_new_project_creation/components/welcome.vue'; import WelcomePage from '~/projects/experiment_new_project_creation/components/welcome.vue';
describe('Welcome page', () => { describe('Welcome page', () => {
...@@ -28,4 +29,13 @@ describe('Welcome page', () => { ...@@ -28,4 +29,13 @@ describe('Welcome page', () => {
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', { label: 'test' }); expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', { label: 'test' });
}); });
}); });
it('renders new project push tip popover', () => {
createComponent({ panels: [{ name: 'test', href: '#' }] });
const popover = wrapper.findComponent(NewProjectPushTipPopover);
expect(popover.exists()).toBe(true);
expect(popover.props().target()).toBe(wrapper.find({ ref: 'clipTip' }).element);
});
}); });
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