Commit 699354cc authored by Peter Hegman's avatar Peter Hegman Committed by Ezekiel Kigbo

Migrate enable Gitpod confirmation modal to `GlModal`

Previously it was a Bootstrap modal. Migrate to `GlModal` so it is
compliant with Pajamas design system.

Changelog: changed
parent 50df24e4
......@@ -119,6 +119,8 @@ export default {
:show-gitpod-button="mr.showGitpodButton"
:gitpod-url="mr.gitpodUrl"
:gitpod-enabled="mr.gitpodEnabled"
:user-preferences-gitpod-path="mr.userPreferencesGitpodPath"
:user-profile-enable-gitpod-path="mr.userProfileEnableGitpodPath"
:gitpod-text="$options.i18n.gitpodText"
class="gl-display-none gl-md-display-inline-block gl-mr-3"
data-placement="bottom"
......
......@@ -222,6 +222,8 @@ export default class MergeRequestStore {
this.showGitpodButton = data.show_gitpod_button;
this.gitpodUrl = data.gitpod_url;
this.gitpodEnabled = data.gitpod_enabled;
this.userPreferencesGitpodPath = data.user_preferences_gitpod_path;
this.userProfileEnableGitpodPath = data.user_profile_enable_gitpod_path;
}
setState() {
......
<script>
import $ from 'jquery';
import { __ } from '~/locale';
import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import ActionsButton from '~/vue_shared/components/actions_button.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
......@@ -12,6 +13,19 @@ export default {
components: {
ActionsButton,
LocalStorageSync,
GlModal,
GlSprintf,
GlLink,
},
i18n: {
modal: {
title: __('Enable Gitpod?'),
content: s__(
'Gitpod|To use Gitpod you must first enable the feature in the integrations section of your %{linkStart}user preferences%{linkEnd}.',
),
actionCancelText: __('Cancel'),
actionPrimaryText: __('Enable Gitpod'),
},
},
props: {
isFork: {
......@@ -49,6 +63,16 @@ export default {
required: false,
default: false,
},
userPreferencesGitpodPath: {
type: String,
required: false,
default: '',
},
userProfileEnableGitpodPath: {
type: String,
required: false,
default: '',
},
editUrl: {
type: String,
required: false,
......@@ -78,6 +102,7 @@ export default {
data() {
return {
selection: KEY_WEB_IDE,
showEnableGitpodModal: false,
};
},
computed: {
......@@ -94,7 +119,7 @@ export default {
href: '#modal-confirm-fork-edit',
handle: () => {
this.$emit('edit', 'simple');
this.showModal('#modal-confirm-fork-edit');
this.showJQueryModal('#modal-confirm-fork-edit');
},
}
: { href: this.editUrl };
......@@ -133,7 +158,7 @@ export default {
href: '#modal-confirm-fork-webide',
handle: () => {
this.$emit('edit', 'ide');
this.showModal('#modal-confirm-fork-webide');
this.showJQueryModal('#modal-confirm-fork-webide');
},
}
: { href: this.webIdeUrl };
......@@ -154,14 +179,23 @@ export default {
gitpodActionText() {
return this.gitpodText || __('Gitpod');
},
computedShowGitpodButton() {
return (
this.showGitpodButton && this.userPreferencesGitpodPath && this.userProfileEnableGitpodPath
);
},
gitpodAction() {
if (!this.showGitpodButton) {
if (!this.computedShowGitpodButton) {
return null;
}
const handleOptions = this.gitpodEnabled
? { href: this.gitpodUrl }
: { href: '#modal-enable-gitpod', handle: () => this.showModal('#modal-enable-gitpod') };
: {
handle: () => {
this.showModal('showEnableGitpodModal');
},
};
const secondaryText = __('Launch a ready-to-code development environment for your project.');
......@@ -176,14 +210,36 @@ export default {
...handleOptions,
};
},
enableGitpodModalProps() {
return {
'modal-id': 'enable-gitpod-modal',
size: 'sm',
title: this.$options.i18n.modal.title,
'action-cancel': {
text: this.$options.i18n.modal.actionCancelText,
},
'action-primary': {
text: this.$options.i18n.modal.actionPrimaryText,
attributes: {
variant: 'confirm',
category: 'primary',
href: this.userProfileEnableGitpodPath,
'data-method': 'put',
},
},
};
},
},
methods: {
select(key) {
this.selection = key;
},
showModal(id) {
showJQueryModal(id) {
$(id).modal('show');
},
showModal(dataKey) {
this[dataKey] = true;
},
},
};
</script>
......@@ -202,5 +258,16 @@ export default {
:value="selection"
@input="select"
/>
<gl-modal
v-if="computedShowGitpodButton && !gitpodEnabled"
v-model="showEnableGitpodModal"
v-bind="enableGitpodModalProps"
>
<gl-sprintf :message="$options.i18n.modal.content">
<template #link="{ content }">
<gl-link :href="userPreferencesGitpodPath">{{ content }}</gl-link>
</template>
</gl-sprintf>
</gl-modal>
</div>
</template>
......@@ -191,7 +191,10 @@ module TreeHelper
web_ide_url: web_ide_url,
edit_url: edit_url(options),
gitpod_url: gitpod_url
gitpod_url: gitpod_url,
user_preferences_gitpod_path: profile_preferences_path(anchor: 'user_gitpod_enabled'),
user_profile_enable_gitpod_path: profile_path(user: { gitpod_enabled: true })
}
end
......
......@@ -20,5 +20,7 @@
window.gl.mrWidgetData.codequality_help_path = '#{help_page_path("user/project/merge_requests/code_quality", anchor: "code-quality-reports")}';
window.gl.mrWidgetData.false_positive_doc_url = '#{help_page_path('user/application_security/vulnerabilities/index')}';
window.gl.mrWidgetData.can_view_false_positive = '#{@merge_request.project.licensed_feature_available?(:sast_fp_reduction).to_s}';
window.gl.mrWidgetData.user_preferences_gitpod_path = '#{profile_preferences_path(anchor: 'user_gitpod_enabled')}';
window.gl.mrWidgetData.user_profile_enable_gitpod_path = '#{profile_path(user: { gitpod_enabled: true })}';
#js-vue-mr-widget.mr-widget
......@@ -97,6 +97,4 @@
#js-review-bar
= render 'projects/invite_members_modal', project: @project
- if Gitlab::CurrentSettings.gitpod_enabled && !current_user&.gitpod_enabled
= render 'shared/gitpod/enable_gitpod_modal'
= render 'shared/web_ide_path'
......@@ -6,5 +6,3 @@
= render 'shared/confirm_fork_modal', fork_path: fork_and_edit_path(@project, @ref, @path), type: 'edit'
- if show_web_ide_button?
= render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path), type: 'webide'
- if show_gitpod_button?
= render 'shared/gitpod/enable_gitpod_modal'
#modal-enable-gitpod.modal.qa-enable-gitpod-modal
.modal-dialog
.modal-content
.modal-header
%h3.page-title= _('Enable Gitpod?')
%button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
%span{ "aria-hidden": "true" } &times;
.modal-body.p-3
%p= (_("To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}.") % { user_prefs: link_to(_('user preferences'), profile_preferences_path(anchor: 'gitpod')) }).html_safe
.modal-footer
= link_to _('Cancel'), '#', class: "gl-button btn btn-default btn-cancel", "data-dismiss" => "modal"
= link_to _('Enable Gitpod'), profile_path(user: { gitpod_enabled: true}), class: 'gl-button btn btn-confirm', method: :put
......@@ -16373,6 +16373,9 @@ msgstr ""
msgid "Gitpod|The URL to your Gitpod instance configured to read your GitLab projects, such as https://gitpod.example.com."
msgstr ""
msgid "Gitpod|To use Gitpod you must first enable the feature in the integrations section of your %{linkStart}user preferences%{linkEnd}."
msgstr ""
msgid "Gitpod|To use the integration, each user must also enable Gitpod on their GitLab account. %{link_start}How do I enable it?%{link_end} "
msgstr ""
......@@ -37200,9 +37203,6 @@ msgstr ""
msgid "To update Snippets with multiple files, you must use the `files` parameter"
msgstr ""
msgid "To use Gitpod you must first enable the feature in the integrations section of your %{user_prefs}."
msgstr ""
msgid "To use the additional formats, you must start the required %{container_link_start}companion containers%{container_link_end}."
msgstr ""
......@@ -43160,9 +43160,6 @@ msgstr ""
msgid "user namespace cannot be the parent of another namespace"
msgstr ""
msgid "user preferences"
msgstr ""
msgid "username"
msgstr ""
......
......@@ -153,6 +153,8 @@ describe('MRWidgetHeader', () => {
gitpodEnabled: true,
showGitpodButton: true,
gitpodUrl: 'http://gitpod.localhost',
userPreferencesGitpodPath: '/-/profile/preferences#user_gitpod_enabled',
userProfileEnableGitpodPath: '/-/profile?user%5Bgitpod_enabled%5D=true',
};
it('renders checkout branch button with modal trigger', () => {
......@@ -208,6 +210,8 @@ describe('MRWidgetHeader', () => {
gitpodEnabled: true,
showGitpodButton: true,
gitpodUrl: 'http://gitpod.localhost',
userPreferencesGitpodPath: mrDefaultOptions.userPreferencesGitpodPath,
userProfileEnableGitpodPath: mrDefaultOptions.userProfileEnableGitpodPath,
webIdeUrl,
});
});
......
......@@ -282,6 +282,8 @@ export default {
gitpod_enabled: true,
show_gitpod_button: true,
gitpod_url: 'http://gitpod.localhost',
user_preferences_gitpod_path: '/-/profile/preferences#user_gitpod_enabled',
user_profile_enable_gitpod_path: '/-/profile?user%5Bgitpod_enabled%5D=true',
};
export const mockStore = {
......
......@@ -15,6 +15,8 @@ describe('MergeRequestStore', () => {
gitpodEnabled: mockData.gitpod_enabled,
showGitpodButton: mockData.show_gitpod_button,
gitpodUrl: mockData.gitpod_url,
userPreferencesGitpodPath: mockData.user_preferences_gitpod_path,
userProfileEnableGitpodPath: mockData.user_profile_enable_gitpod_path,
});
});
......
import { shallowMount } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui';
import { nextTick } from 'vue';
import ActionsButton from '~/vue_shared/components/actions_button.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
import { stubComponent } from 'helpers/stub_component';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
const TEST_EDIT_URL = '/gitlab-test/test/-/edit/main/';
const TEST_WEB_IDE_URL = '/-/ide/project/gitlab-test/test/edit/main/-/';
const TEST_GITPOD_URL = 'https://gitpod.test/';
const TEST_USER_PREFERENCES_GITPOD_PATH = '/-/profile/preferences#user_gitpod_enabled';
const TEST_USER_PROFILE_ENABLE_GITPOD_PATH = '/-/profile?user%5Bgitpod_enabled%5D=true';
const ACTION_EDIT = {
href: TEST_EDIT_URL,
......@@ -54,21 +61,31 @@ const ACTION_GITPOD = {
};
const ACTION_GITPOD_ENABLE = {
...ACTION_GITPOD,
href: '#modal-enable-gitpod',
href: undefined,
handle: expect.any(Function),
};
describe('Web IDE link component', () => {
let wrapper;
function createComponent(props) {
wrapper = shallowMount(WebIdeLink, {
function createComponent(props, mountFn = shallowMountExtended) {
wrapper = mountFn(WebIdeLink, {
propsData: {
editUrl: TEST_EDIT_URL,
webIdeUrl: TEST_WEB_IDE_URL,
gitpodUrl: TEST_GITPOD_URL,
...props,
},
stubs: {
GlModal: stubComponent(GlModal, {
template: `
<div>
<slot name="modal-title"></slot>
<slot></slot>
<slot name="modal-footer"></slot>
</div>`,
}),
},
});
}
......@@ -78,6 +95,7 @@ describe('Web IDE link component', () => {
const findActionsButton = () => wrapper.find(ActionsButton);
const findLocalStorageSync = () => wrapper.find(LocalStorageSync);
const findModal = () => wrapper.findComponent(GlModal);
it.each([
{
......@@ -97,19 +115,68 @@ describe('Web IDE link component', () => {
expectedActions: [ACTION_WEB_IDE_CONFIRM_FORK, ACTION_EDIT_CONFIRM_FORK],
},
{
props: { showWebIdeButton: false, showGitpodButton: true, gitpodEnabled: true },
props: {
showWebIdeButton: false,
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: true,
},
expectedActions: [ACTION_EDIT, ACTION_GITPOD],
},
{
props: { showWebIdeButton: false, showGitpodButton: true, gitpodEnabled: false },
props: {
showWebIdeButton: false,
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
gitpodEnabled: true,
},
expectedActions: [ACTION_EDIT],
},
{
props: {
showWebIdeButton: false,
showGitpodButton: true,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: true,
},
expectedActions: [ACTION_EDIT],
},
{
props: {
showWebIdeButton: false,
showGitpodButton: true,
gitpodEnabled: true,
},
expectedActions: [ACTION_EDIT],
},
{
props: {
showWebIdeButton: false,
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: false,
},
expectedActions: [ACTION_EDIT, ACTION_GITPOD_ENABLE],
},
{
props: { showGitpodButton: true, gitpodEnabled: false },
props: {
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: false,
},
expectedActions: [ACTION_WEB_IDE, ACTION_EDIT, ACTION_GITPOD_ENABLE],
},
{
props: { showEditButton: false, showGitpodButton: true, gitpodText: 'Test Gitpod' },
props: {
showEditButton: false,
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodText: 'Test Gitpod',
},
expectedActions: [ACTION_WEB_IDE, { ...ACTION_GITPOD_ENABLE, text: 'Test Gitpod' }],
},
{
......@@ -128,6 +195,8 @@ describe('Web IDE link component', () => {
showEditButton: false,
showWebIdeButton: true,
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: true,
});
});
......@@ -182,4 +251,72 @@ describe('Web IDE link component', () => {
},
);
});
describe('when Gitpod is not enabled', () => {
it('renders closed modal to enable Gitpod', () => {
createComponent({
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: false,
});
const modal = findModal();
expect(modal.exists()).toBe(true);
expect(modal.props()).toMatchObject({
visible: false,
modalId: 'enable-gitpod-modal',
size: 'sm',
title: WebIdeLink.i18n.modal.title,
actionCancel: {
text: WebIdeLink.i18n.modal.actionCancelText,
},
actionPrimary: {
text: WebIdeLink.i18n.modal.actionPrimaryText,
attributes: {
variant: 'confirm',
category: 'primary',
href: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
'data-method': 'put',
},
},
});
});
it('opens modal when `Gitpod` action is clicked', async () => {
const gitpodText = 'Open in Gitpod';
createComponent(
{
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: false,
gitpodText,
},
mountExtended,
);
findLocalStorageSync().vm.$emit('input', ACTION_GITPOD.key);
await nextTick();
await wrapper.findByRole('button', { name: gitpodText }).trigger('click');
expect(findModal().props('visible')).toBe(true);
});
});
describe('when Gitpod is enabled', () => {
it('does not render modal', () => {
createComponent({
showGitpodButton: true,
userPreferencesGitpodPath: TEST_USER_PREFERENCES_GITPOD_PATH,
userProfileEnableGitpodPath: TEST_USER_PROFILE_ENABLE_GITPOD_PATH,
gitpodEnabled: true,
});
expect(findModal().exists()).toBe(false);
});
});
});
......@@ -84,14 +84,21 @@ RSpec.describe TreeHelper do
describe '#web_ide_button_data' do
let(:blob) { project.repository.blob_at('refs/heads/master', @path) }
let_it_be(:user_preferences_gitpod_path) { '/-/profile/preferences#user_gitpod_enabled' }
let_it_be(:user_profile_enable_gitpod_path) { '/-/profile?user%5Bgitpod_enabled%5D=true' }
before do
@path = ''
@project = project
@ref = sha
allow(helper).to receive(:current_user).and_return(nil)
allow(helper).to receive(:can_collaborate_with_project?).and_return(true)
allow(helper).to receive(:can?).and_return(true)
allow(helper).to receive_messages(
current_user: nil,
can_collaborate_with_project?: true,
can?: true,
user_preferences_gitpod_path: user_preferences_gitpod_path,
user_profile_enable_gitpod_path: user_profile_enable_gitpod_path
)
end
subject { helper.web_ide_button_data(blob: blob) }
......@@ -112,7 +119,10 @@ RSpec.describe TreeHelper do
edit_url: '',
web_ide_url: "/-/ide/project/#{project.full_path}/edit/#{sha}",
gitpod_url: ''
gitpod_url: '',
user_preferences_gitpod_path: user_preferences_gitpod_path,
user_profile_enable_gitpod_path: user_profile_enable_gitpod_path
)
end
......
......@@ -45,32 +45,4 @@ RSpec.describe 'projects/merge_requests/show.html.haml', :aggregate_failures do
end
end
end
describe 'gitpod modal' do
let(:gitpod_modal_selector) { '#modal-enable-gitpod' }
let(:user) { create(:user) }
let(:user_gitpod_enabled) { create(:user).tap { |x| x.update!(gitpod_enabled: true) } }
where(:site_enabled, :current_user, :should_show) do
false | ref(:user) | false
true | ref(:user) | true
true | nil | true
true | ref(:user_gitpod_enabled) | false
end
with_them do
it 'handles rendering gitpod user enable modal' do
allow(Gitlab::CurrentSettings).to receive(:gitpod_enabled).and_return(site_enabled)
allow(view).to receive(:current_user).and_return(current_user)
render
if should_show
expect(rendered).to have_css(gitpod_modal_selector)
else
expect(rendered).to have_no_css(gitpod_modal_selector)
end
end
end
end
end
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