Commit ca47ae08 authored by Alex Kalderimis's avatar Alex Kalderimis

Merge branch '2647-create-gitlab-branch-from-jira-issue-3' into 'master'

Create GitLab branch from Jira issue: UI Polish

See merge request gitlab-org/gitlab!66803
parents 10ac75d0 2ade7129
...@@ -3,8 +3,6 @@ import { GlFormGroup, GlButton, GlFormInput, GlForm, GlAlert } from '@gitlab/ui' ...@@ -3,8 +3,6 @@ import { GlFormGroup, GlButton, GlFormInput, GlForm, GlAlert } from '@gitlab/ui'
import { import {
CREATE_BRANCH_ERROR_GENERIC, CREATE_BRANCH_ERROR_GENERIC,
CREATE_BRANCH_ERROR_WITH_CONTEXT, CREATE_BRANCH_ERROR_WITH_CONTEXT,
CREATE_BRANCH_SUCCESS_ALERT,
I18N_NEW_BRANCH_PAGE_TITLE,
I18N_NEW_BRANCH_LABEL_DROPDOWN, I18N_NEW_BRANCH_LABEL_DROPDOWN,
I18N_NEW_BRANCH_LABEL_BRANCH, I18N_NEW_BRANCH_LABEL_BRANCH,
I18N_NEW_BRANCH_LABEL_SOURCE, I18N_NEW_BRANCH_LABEL_SOURCE,
...@@ -19,8 +17,6 @@ const DEFAULT_ALERT_PARAMS = { ...@@ -19,8 +17,6 @@ const DEFAULT_ALERT_PARAMS = {
title: '', title: '',
message: '', message: '',
variant: DEFAULT_ALERT_VARIANT, variant: DEFAULT_ALERT_VARIANT,
primaryButtonLink: '',
primaryButtonText: '',
}; };
export default { export default {
...@@ -34,13 +30,7 @@ export default { ...@@ -34,13 +30,7 @@ export default {
ProjectDropdown, ProjectDropdown,
SourceBranchDropdown, SourceBranchDropdown,
}, },
props: { inject: ['initialBranchName'],
initialBranchName: {
type: String,
required: false,
default: '',
},
},
data() { data() {
return { return {
selectedProject: null, selectedProject: null,
...@@ -111,10 +101,7 @@ export default { ...@@ -111,10 +101,7 @@ export default {
message: errors[0], message: errors[0],
}); });
} else { } else {
this.displayAlert({ this.$emit('success');
...CREATE_BRANCH_SUCCESS_ALERT,
variant: 'success',
});
} }
} catch (e) { } catch (e) {
this.onError({ this.onError({
...@@ -126,7 +113,6 @@ export default { ...@@ -126,7 +113,6 @@ export default {
}, },
}, },
i18n: { i18n: {
I18N_NEW_BRANCH_PAGE_TITLE,
I18N_NEW_BRANCH_LABEL_DROPDOWN, I18N_NEW_BRANCH_LABEL_DROPDOWN,
I18N_NEW_BRANCH_LABEL_BRANCH, I18N_NEW_BRANCH_LABEL_BRANCH,
I18N_NEW_BRANCH_LABEL_SOURCE, I18N_NEW_BRANCH_LABEL_SOURCE,
...@@ -134,15 +120,8 @@ export default { ...@@ -134,15 +120,8 @@ export default {
}, },
}; };
</script> </script>
<template> <template>
<div> <gl-form @submit.prevent="onSubmit">
<div class="gl-border-1 gl-border-b-solid gl-border-gray-100 gl-mb-5 gl-mt-7">
<h1 class="page-title">
{{ $options.i18n.I18N_NEW_BRANCH_PAGE_TITLE }}
</h1>
</div>
<gl-alert <gl-alert
v-if="showAlert" v-if="showAlert"
class="gl-mb-5" class="gl-mb-5"
...@@ -152,50 +131,44 @@ export default { ...@@ -152,50 +131,44 @@ export default {
> >
{{ alertParams.message }} {{ alertParams.message }}
</gl-alert> </gl-alert>
<gl-form-group :label="$options.i18n.I18N_NEW_BRANCH_LABEL_DROPDOWN" label-for="project-select">
<project-dropdown
id="project-select"
:selected-project="selectedProject"
@change="onProjectSelect"
@error="onError"
/>
</gl-form-group>
<gl-form @submit.prevent="onSubmit"> <gl-form-group
<gl-form-group :label="$options.i18n.I18N_NEW_BRANCH_LABEL_BRANCH"
:label="$options.i18n.I18N_NEW_BRANCH_LABEL_DROPDOWN" label-for="branch-name-input"
label-for="project-select" >
> <gl-form-input id="branch-name-input" v-model="branchName" type="text" required />
<project-dropdown </gl-form-group>
id="project-select"
:selected-project="selectedProject"
@change="onProjectSelect"
@error="onError"
/>
</gl-form-group>
<gl-form-group <gl-form-group
:label="$options.i18n.I18N_NEW_BRANCH_LABEL_BRANCH" :label="$options.i18n.I18N_NEW_BRANCH_LABEL_SOURCE"
label-for="branch-name-input" label-for="source-branch-select"
> >
<gl-form-input id="branch-name-input" v-model="branchName" type="text" required /> <source-branch-dropdown
</gl-form-group> id="source-branch-select"
:selected-project="selectedProject"
:selected-branch-name="selectedSourceBranchName"
@change="onSourceBranchSelect"
@error="onError"
/>
</gl-form-group>
<gl-form-group <div class="form-actions">
:label="$options.i18n.I18N_NEW_BRANCH_LABEL_SOURCE" <gl-button
label-for="source-branch-select" :loading="createBranchLoading"
type="submit"
variant="confirm"
:disabled="disableSubmitButton"
> >
<source-branch-dropdown {{ $options.i18n.I18N_NEW_BRANCH_SUBMIT_BUTTON_TEXT }}
id="source-branch-select" </gl-button>
:selected-project="selectedProject" </div>
:selected-branch-name="selectedSourceBranchName" </gl-form>
@change="onSourceBranchSelect"
@error="onError"
/>
</gl-form-group>
<div class="form-actions">
<gl-button
:loading="createBranchLoading"
type="submit"
variant="confirm"
:disabled="disableSubmitButton"
>
{{ $options.i18n.I18N_NEW_BRANCH_SUBMIT_BUTTON_TEXT }}
</gl-button>
</div>
</gl-form>
</div>
</template> </template>
...@@ -3,7 +3,6 @@ import { __, s__ } from '~/locale'; ...@@ -3,7 +3,6 @@ import { __, s__ } from '~/locale';
export const BRANCHES_PER_PAGE = 20; export const BRANCHES_PER_PAGE = 20;
export const PROJECTS_PER_PAGE = 20; export const PROJECTS_PER_PAGE = 20;
export const I18N_NEW_BRANCH_PAGE_TITLE = __('New branch');
export const I18N_NEW_BRANCH_LABEL_DROPDOWN = __('Project'); export const I18N_NEW_BRANCH_LABEL_DROPDOWN = __('Project');
export const I18N_NEW_BRANCH_LABEL_BRANCH = __('Branch name'); export const I18N_NEW_BRANCH_LABEL_BRANCH = __('Branch name');
export const I18N_NEW_BRANCH_LABEL_SOURCE = __('Source branch'); export const I18N_NEW_BRANCH_LABEL_SOURCE = __('Source branch');
...@@ -14,7 +13,13 @@ export const CREATE_BRANCH_ERROR_GENERIC = s__( ...@@ -14,7 +13,13 @@ export const CREATE_BRANCH_ERROR_GENERIC = s__(
); );
export const CREATE_BRANCH_ERROR_WITH_CONTEXT = s__('JiraConnect|Failed to create branch.'); export const CREATE_BRANCH_ERROR_WITH_CONTEXT = s__('JiraConnect|Failed to create branch.');
export const CREATE_BRANCH_SUCCESS_ALERT = { export const I18N_PAGE_TITLE_WITH_BRANCH_NAME = s__(
title: s__('JiraConnect|New branch was successfully created.'), 'JiraConnect|Create branch for Jira issue %{jiraIssue}',
message: s__('JiraConnect|You can now close this window and return to Jira.'), );
}; export const I18N_PAGE_TITLE_DEFAULT = __('New branch');
export const I18N_NEW_BRANCH_SUCCESS_TITLE = s__(
'JiraConnect|New branch was successfully created.',
);
export const I18N_NEW_BRANCH_SUCCESS_MESSAGE = s__(
'JiraConnect|You can now close this window and return to Jira.',
);
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import JiraConnectNewBranchForm from '~/jira_connect/branches/components/new_branch_form.vue'; import JiraConnectNewBranchPage from '~/jira_connect/branches/pages/index.vue';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -11,7 +11,7 @@ export default async function initJiraConnectBranches() { ...@@ -11,7 +11,7 @@ export default async function initJiraConnectBranches() {
return null; return null;
} }
const { initialBranchName } = el.dataset; const { initialBranchName, successStateSvgPath } = el.dataset;
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient( defaultClient: createDefaultClient(
...@@ -25,12 +25,12 @@ export default async function initJiraConnectBranches() { ...@@ -25,12 +25,12 @@ export default async function initJiraConnectBranches() {
return new Vue({ return new Vue({
el, el,
apolloProvider, apolloProvider,
provide: {
initialBranchName,
successStateSvgPath,
},
render(createElement) { render(createElement) {
return createElement(JiraConnectNewBranchForm, { return createElement(JiraConnectNewBranchPage);
props: {
initialBranchName,
},
});
}, },
}); });
} }
<script>
import { GlEmptyState } from '@gitlab/ui';
import { sprintf } from '~/locale';
import NewBranchForm from '../components/new_branch_form.vue';
import {
I18N_PAGE_TITLE_WITH_BRANCH_NAME,
I18N_PAGE_TITLE_DEFAULT,
I18N_NEW_BRANCH_SUCCESS_TITLE,
I18N_NEW_BRANCH_SUCCESS_MESSAGE,
} from '../constants';
export default {
components: {
GlEmptyState,
NewBranchForm,
},
inject: ['initialBranchName', 'successStateSvgPath'],
data() {
return {
showForm: true,
};
},
computed: {
pageTitle() {
return this.initialBranchName
? sprintf(this.$options.i18n.I18N_PAGE_TITLE_WITH_BRANCH_NAME, {
jiraIssue: this.initialBranchName,
})
: this.$options.i18n.I18N_PAGE_TITLE_DEFAULT;
},
},
methods: {
onNewBranchFormSuccess() {
// light-weight toggle to hide the form and show the success state
this.showForm = false;
},
},
i18n: {
I18N_PAGE_TITLE_WITH_BRANCH_NAME,
I18N_PAGE_TITLE_DEFAULT,
I18N_NEW_BRANCH_SUCCESS_TITLE,
I18N_NEW_BRANCH_SUCCESS_MESSAGE,
},
};
</script>
<template>
<div>
<div class="gl-border-1 gl-border-b-solid gl-border-gray-100 gl-mb-5 gl-mt-7">
<h1 data-testid="page-title" class="page-title">{{ pageTitle }}</h1>
</div>
<new-branch-form v-if="showForm" @success="onNewBranchFormSuccess" />
<gl-empty-state
v-else
:title="$options.i18n.I18N_NEW_BRANCH_SUCCESS_TITLE"
:description="$options.i18n.I18N_NEW_BRANCH_SUCCESS_MESSAGE"
:svg-path="successStateSvgPath"
/>
</div>
</template>
...@@ -8,20 +8,31 @@ class JiraConnect::BranchesController < ApplicationController ...@@ -8,20 +8,31 @@ class JiraConnect::BranchesController < ApplicationController
feature_category :integrations feature_category :integrations
def new def new
@new_branch_data = new_branch_data
end
def self.feature_enabled?(user)
Feature.enabled?(:jira_connect_create_branch, user, default_enabled: :yaml)
end
private
def initial_branch_name
return unless params[:issue_key].present? return unless params[:issue_key].present?
@branch_name = Issue.to_branch_name( Issue.to_branch_name(
params[:issue_key], params[:issue_key],
params[:issue_summary] params[:issue_summary]
) )
end end
def self.feature_enabled?(user) def new_branch_data
Feature.enabled?(:jira_connect_create_branch, user, default_enabled: :yaml) {
initial_branch_name: initial_branch_name,
success_state_svg_path: ActionController::Base.helpers.image_path('illustrations/merge_requests.svg')
}
end end
private
def feature_enabled! def feature_enabled!
render_404 unless self.class.feature_enabled?(current_user) render_404 unless self.class.feature_enabled?(current_user)
end end
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
- @hide_top_links = true - @hide_top_links = true
- page_title _('New branch') - page_title _('New branch')
.js-jira-connect-create-branch{ data: { initial_branch_name: @branch_name } } .js-jira-connect-create-branch{ data: @new_branch_data }
...@@ -18710,6 +18710,9 @@ msgstr "" ...@@ -18710,6 +18710,9 @@ msgstr ""
msgid "Jira-GitLab user mapping template" msgid "Jira-GitLab user mapping template"
msgstr "" msgstr ""
msgid "JiraConnect|Create branch for Jira issue %{jiraIssue}"
msgstr ""
msgid "JiraConnect|Failed to create branch." msgid "JiraConnect|Failed to create branch."
msgstr "" msgstr ""
......
...@@ -15,21 +15,24 @@ RSpec.describe JiraConnect::BranchesController do ...@@ -15,21 +15,24 @@ RSpec.describe JiraConnect::BranchesController do
get :new, params: { issue_key: 'ACME-123', issue_summary: 'My Issue !@#$%' } get :new, params: { issue_key: 'ACME-123', issue_summary: 'My Issue !@#$%' }
expect(response).to be_successful expect(response).to be_successful
expect(assigns(:branch_name)).to eq('ACME-123-my-issue') expect(assigns(:new_branch_data)).to include(
initial_branch_name: 'ACME-123-my-issue',
success_state_svg_path: start_with('/assets/illustrations/merge_requests-')
)
end end
it 'ignores missing summary' do it 'ignores missing summary' do
get :new, params: { issue_key: 'ACME-123' } get :new, params: { issue_key: 'ACME-123' }
expect(response).to be_successful expect(response).to be_successful
expect(assigns(:branch_name)).to eq('ACME-123') expect(assigns(:new_branch_data)).to include(initial_branch_name: 'ACME-123')
end end
it 'does not set a branch name if key is not passed' do it 'does not set a branch name if key is not passed' do
get :new, params: { issue_summary: 'My issue' } get :new, params: { issue_summary: 'My issue' }
expect(response).to be_successful expect(response).to be_successful
expect(assigns(:branch_name)).to be_nil expect(assigns(:new_branch_data)).to include('initial_branch_name': nil)
end end
context 'when feature flag is disabled' do context 'when feature flag is disabled' do
......
...@@ -9,7 +9,6 @@ import SourceBranchDropdown from '~/jira_connect/branches/components/source_bran ...@@ -9,7 +9,6 @@ import SourceBranchDropdown from '~/jira_connect/branches/components/source_bran
import { import {
CREATE_BRANCH_ERROR_GENERIC, CREATE_BRANCH_ERROR_GENERIC,
CREATE_BRANCH_ERROR_WITH_CONTEXT, CREATE_BRANCH_ERROR_WITH_CONTEXT,
CREATE_BRANCH_SUCCESS_ALERT,
} from '~/jira_connect/branches/constants'; } from '~/jira_connect/branches/constants';
import createBranchMutation from '~/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql'; import createBranchMutation from '~/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql';
...@@ -74,10 +73,14 @@ describe('NewBranchForm', () => { ...@@ -74,10 +73,14 @@ describe('NewBranchForm', () => {
return mockApollo; return mockApollo;
} }
function createComponent({ mockApollo } = {}) { function createComponent({ mockApollo, provide } = {}) {
wrapper = shallowMount(NewBranchForm, { wrapper = shallowMount(NewBranchForm, {
localVue, localVue,
apolloProvider: mockApollo || createMockApolloProvider(), apolloProvider: mockApollo || createMockApolloProvider(),
provide: {
initialBranchName: '',
...provide,
},
}); });
} }
...@@ -139,14 +142,8 @@ describe('NewBranchForm', () => { ...@@ -139,14 +142,8 @@ describe('NewBranchForm', () => {
await waitForPromises(); await waitForPromises();
}); });
it('displays a success message', () => { it('emits `success` event', () => {
const alert = findAlert(); expect(wrapper.emitted('success')).toBeTruthy();
expect(alert.exists()).toBe(true);
expect(alert.text()).toBe(CREATE_BRANCH_SUCCESS_ALERT.message);
expect(alert.props()).toMatchObject({
title: CREATE_BRANCH_SUCCESS_ALERT.title,
variant: 'success',
});
}); });
it('called `createBranch` mutation correctly', () => { it('called `createBranch` mutation correctly', () => {
...@@ -195,6 +192,15 @@ describe('NewBranchForm', () => { ...@@ -195,6 +192,15 @@ describe('NewBranchForm', () => {
}); });
}); });
describe('when `initialBranchName` is specified', () => {
it('sets value of branch name input to `initialBranchName` by default', () => {
const mockInitialBranchName = 'ap1-test-branch-name';
createComponent({ provide: { initialBranchName: mockInitialBranchName } });
expect(findInput().attributes('value')).toBe(mockInitialBranchName);
});
});
describe('error handling', () => { describe('error handling', () => {
describe.each` describe.each`
component | componentName component | componentName
......
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import NewBranchForm from '~/jira_connect/branches/components/new_branch_form.vue';
import {
I18N_PAGE_TITLE_WITH_BRANCH_NAME,
I18N_PAGE_TITLE_DEFAULT,
} from '~/jira_connect/branches/constants';
import JiraConnectNewBranchPage from '~/jira_connect/branches/pages/index.vue';
import { sprintf } from '~/locale';
describe('NewBranchForm', () => {
let wrapper;
const findPageTitle = () => wrapper.find('h1');
const findNewBranchForm = () => wrapper.findComponent(NewBranchForm);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
function createComponent({ provide } = {}) {
wrapper = shallowMount(JiraConnectNewBranchPage, {
provide: {
initialBranchName: '',
successStateSvgPath: '',
...provide,
},
});
}
afterEach(() => {
wrapper.destroy();
});
describe('page title', () => {
it.each`
initialBranchName | pageTitle
${undefined} | ${I18N_PAGE_TITLE_DEFAULT}
${'ap1-test-button'} | ${sprintf(I18N_PAGE_TITLE_WITH_BRANCH_NAME, { jiraIssue: 'ap1-test-button' })}
`(
'sets page title to "$pageTitle" when initial branch name is "$initialBranchName"',
({ initialBranchName, pageTitle }) => {
createComponent({ provide: { initialBranchName } });
expect(findPageTitle().text()).toBe(pageTitle);
},
);
});
it('renders NewBranchForm by default', () => {
createComponent();
expect(findNewBranchForm().exists()).toBe(true);
expect(findEmptyState().exists()).toBe(false);
});
describe('when `sucesss` event emitted from NewBranchForm', () => {
it('renders the success state', async () => {
createComponent();
const newBranchForm = findNewBranchForm();
await newBranchForm.vm.$emit('success');
expect(findNewBranchForm().exists()).toBe(false);
expect(findEmptyState().exists()).toBe(true);
});
});
});
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