Commit 2ade7129 authored by Tom Quirk's avatar Tom Quirk Committed by Alex Kalderimis

UI polish for Jira Connect Create branch

- Replace success alert with empty state
- Dynamic page title
parent a26fe071
......@@ -3,8 +3,6 @@ import { GlFormGroup, GlButton, GlFormInput, GlForm, GlAlert } from '@gitlab/ui'
import {
CREATE_BRANCH_ERROR_GENERIC,
CREATE_BRANCH_ERROR_WITH_CONTEXT,
CREATE_BRANCH_SUCCESS_ALERT,
I18N_NEW_BRANCH_PAGE_TITLE,
I18N_NEW_BRANCH_LABEL_DROPDOWN,
I18N_NEW_BRANCH_LABEL_BRANCH,
I18N_NEW_BRANCH_LABEL_SOURCE,
......@@ -19,8 +17,6 @@ const DEFAULT_ALERT_PARAMS = {
title: '',
message: '',
variant: DEFAULT_ALERT_VARIANT,
primaryButtonLink: '',
primaryButtonText: '',
};
export default {
......@@ -34,13 +30,7 @@ export default {
ProjectDropdown,
SourceBranchDropdown,
},
props: {
initialBranchName: {
type: String,
required: false,
default: '',
},
},
inject: ['initialBranchName'],
data() {
return {
selectedProject: null,
......@@ -111,10 +101,7 @@ export default {
message: errors[0],
});
} else {
this.displayAlert({
...CREATE_BRANCH_SUCCESS_ALERT,
variant: 'success',
});
this.$emit('success');
}
} catch (e) {
this.onError({
......@@ -126,7 +113,6 @@ export default {
},
},
i18n: {
I18N_NEW_BRANCH_PAGE_TITLE,
I18N_NEW_BRANCH_LABEL_DROPDOWN,
I18N_NEW_BRANCH_LABEL_BRANCH,
I18N_NEW_BRANCH_LABEL_SOURCE,
......@@ -134,15 +120,8 @@ export default {
},
};
</script>
<template>
<div>
<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-form @submit.prevent="onSubmit">
<gl-alert
v-if="showAlert"
class="gl-mb-5"
......@@ -152,50 +131,44 @@ export default {
>
{{ alertParams.message }}
</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
: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-group
:label="$options.i18n.I18N_NEW_BRANCH_LABEL_BRANCH"
label-for="branch-name-input"
>
<gl-form-input id="branch-name-input" v-model="branchName" type="text" required />
</gl-form-group>
<gl-form-group
:label="$options.i18n.I18N_NEW_BRANCH_LABEL_BRANCH"
label-for="branch-name-input"
>
<gl-form-input id="branch-name-input" v-model="branchName" type="text" required />
</gl-form-group>
<gl-form-group
:label="$options.i18n.I18N_NEW_BRANCH_LABEL_SOURCE"
label-for="source-branch-select"
>
<source-branch-dropdown
id="source-branch-select"
:selected-project="selectedProject"
:selected-branch-name="selectedSourceBranchName"
@change="onSourceBranchSelect"
@error="onError"
/>
</gl-form-group>
<gl-form-group
:label="$options.i18n.I18N_NEW_BRANCH_LABEL_SOURCE"
label-for="source-branch-select"
<div class="form-actions">
<gl-button
:loading="createBranchLoading"
type="submit"
variant="confirm"
:disabled="disableSubmitButton"
>
<source-branch-dropdown
id="source-branch-select"
:selected-project="selectedProject"
:selected-branch-name="selectedSourceBranchName"
@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>
{{ $options.i18n.I18N_NEW_BRANCH_SUBMIT_BUTTON_TEXT }}
</gl-button>
</div>
</gl-form>
</template>
......@@ -3,7 +3,6 @@ import { __, s__ } from '~/locale';
export const BRANCHES_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_BRANCH = __('Branch name');
export const I18N_NEW_BRANCH_LABEL_SOURCE = __('Source branch');
......@@ -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_SUCCESS_ALERT = {
title: s__('JiraConnect|New branch was successfully created.'),
message: s__('JiraConnect|You can now close this window and return to Jira.'),
};
export const I18N_PAGE_TITLE_WITH_BRANCH_NAME = s__(
'JiraConnect|Create branch for Jira issue %{jiraIssue}',
);
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 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';
Vue.use(VueApollo);
......@@ -11,7 +11,7 @@ export default async function initJiraConnectBranches() {
return null;
}
const { initialBranchName } = el.dataset;
const { initialBranchName, successStateSvgPath } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
......@@ -25,12 +25,12 @@ export default async function initJiraConnectBranches() {
return new Vue({
el,
apolloProvider,
provide: {
initialBranchName,
successStateSvgPath,
},
render(createElement) {
return createElement(JiraConnectNewBranchForm, {
props: {
initialBranchName,
},
});
return createElement(JiraConnectNewBranchPage);
},
});
}
<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
feature_category :integrations
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?
@branch_name = Issue.to_branch_name(
Issue.to_branch_name(
params[:issue_key],
params[:issue_summary]
)
end
def self.feature_enabled?(user)
Feature.enabled?(:jira_connect_create_branch, user, default_enabled: :yaml)
def new_branch_data
{
initial_branch_name: initial_branch_name,
success_state_svg_path: ActionController::Base.helpers.image_path('illustrations/merge_requests.svg')
}
end
private
def feature_enabled!
render_404 unless self.class.feature_enabled?(current_user)
end
......
......@@ -2,4 +2,4 @@
- @hide_top_links = true
- page_title _('New branch')
.js-jira-connect-create-branch{ data: { initial_branch_name: @branch_name } }
.js-jira-connect-create-branch{ data: @new_branch_data }
......@@ -18692,6 +18692,9 @@ msgstr ""
msgid "Jira-GitLab user mapping template"
msgstr ""
msgid "JiraConnect|Create branch for Jira issue %{jiraIssue}"
msgstr ""
msgid "JiraConnect|Failed to create branch."
msgstr ""
......
......@@ -15,21 +15,24 @@ RSpec.describe JiraConnect::BranchesController do
get :new, params: { issue_key: 'ACME-123', issue_summary: 'My Issue !@#$%' }
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
it 'ignores missing summary' do
get :new, params: { issue_key: 'ACME-123' }
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
it 'does not set a branch name if key is not passed' do
get :new, params: { issue_summary: 'My issue' }
expect(response).to be_successful
expect(assigns(:branch_name)).to be_nil
expect(assigns(:new_branch_data)).to include('initial_branch_name': nil)
end
context 'when feature flag is disabled' do
......
......@@ -9,7 +9,6 @@ import SourceBranchDropdown from '~/jira_connect/branches/components/source_bran
import {
CREATE_BRANCH_ERROR_GENERIC,
CREATE_BRANCH_ERROR_WITH_CONTEXT,
CREATE_BRANCH_SUCCESS_ALERT,
} from '~/jira_connect/branches/constants';
import createBranchMutation from '~/jira_connect/branches/graphql/mutations/create_branch.mutation.graphql';
......@@ -74,10 +73,14 @@ describe('NewBranchForm', () => {
return mockApollo;
}
function createComponent({ mockApollo } = {}) {
function createComponent({ mockApollo, provide } = {}) {
wrapper = shallowMount(NewBranchForm, {
localVue,
apolloProvider: mockApollo || createMockApolloProvider(),
provide: {
initialBranchName: '',
...provide,
},
});
}
......@@ -139,14 +142,8 @@ describe('NewBranchForm', () => {
await waitForPromises();
});
it('displays a success message', () => {
const alert = findAlert();
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('emits `success` event', () => {
expect(wrapper.emitted('success')).toBeTruthy();
});
it('called `createBranch` mutation correctly', () => {
......@@ -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.each`
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