Commit 6b310d57 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch '225818-add-an-upgrade-banner-for-non-premium-plans' into 'master'

Add an upgrade banner for non-premium plans

See merge request gitlab-org/gitlab!36725
parents 42649c60 e97dff4f
...@@ -106,10 +106,11 @@ export default { ...@@ -106,10 +106,11 @@ export default {
return { return {
id: this.fieldId, id: this.fieldId,
name: this.fieldName, name: this.fieldName,
state: this.valid,
}; };
}, },
valid() { valid() {
return !this.required || !isEmpty(this.model) || !this.validated; return !this.required || !isEmpty(this.model) || this.isNonEmptyPassword || !this.validated;
}, },
}, },
created() { created() {
......
<script> <script>
import { GlFormGroup, GlFormCheckbox, GlFormInput, GlSprintf, GlLink } from '@gitlab/ui'; import eventHub from '../event_hub';
import {
GlFormGroup,
GlFormCheckbox,
GlFormInput,
GlSprintf,
GlLink,
GlButton,
GlCard,
} from '@gitlab/ui';
export default { export default {
name: 'JiraIssuesFields', name: 'JiraIssuesFields',
...@@ -9,17 +18,30 @@ export default { ...@@ -9,17 +18,30 @@ export default {
GlFormInput, GlFormInput,
GlSprintf, GlSprintf,
GlLink, GlLink,
GlButton,
GlCard,
}, },
props: { props: {
showJiraIssuesIntegration: {
type: Boolean,
required: false,
default: false,
},
initialEnableJiraIssues: { initialEnableJiraIssues: {
type: Boolean, type: Boolean,
required: false, required: false,
default: null,
}, },
initialProjectKey: { initialProjectKey: {
type: String, type: String,
required: false, required: false,
default: null, default: null,
}, },
upgradePlanPath: {
type: String,
required: false,
default: null,
},
editProjectPath: { editProjectPath: {
type: String, type: String,
required: false, required: false,
...@@ -30,8 +52,25 @@ export default { ...@@ -30,8 +52,25 @@ export default {
return { return {
enableJiraIssues: this.initialEnableJiraIssues, enableJiraIssues: this.initialEnableJiraIssues,
projectKey: this.initialProjectKey, projectKey: this.initialProjectKey,
validated: false,
}; };
}, },
computed: {
validProjectKey() {
return !this.enableJiraIssues || Boolean(this.projectKey) || !this.validated;
},
},
created() {
eventHub.$on('validateForm', this.validateForm);
},
beforeDestroy() {
eventHub.$off('validateForm', this.validateForm);
},
methods: {
validateForm() {
this.validated = true;
},
},
}; };
</script> </script>
...@@ -45,10 +84,11 @@ export default { ...@@ -45,10 +84,11 @@ export default {
<p> <p>
{{ {{
s__( s__(
'JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of issues and view any issue as read-only.', 'JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only.',
) )
}} }}
</p> </p>
<template v-if="showJiraIssuesIntegration">
<input name="service[issues_enabled]" type="hidden" value="false" /> <input name="service[issues_enabled]" type="hidden" value="false" />
<gl-form-checkbox v-model="enableJiraIssues" name="service[issues_enabled]"> <gl-form-checkbox v-model="enableJiraIssues" name="service[issues_enabled]">
{{ s__('JiraService|Enable Jira issues') }} {{ s__('JiraService|Enable Jira issues') }}
...@@ -60,14 +100,34 @@ export default { ...@@ -60,14 +100,34 @@ export default {
}} }}
</template> </template>
</gl-form-checkbox> </gl-form-checkbox>
</template>
<gl-card v-else class="gl-mt-7">
<strong>{{ __('This is a Premium feature') }}</strong>
<p>{{ __('Upgrade your plan to enable this feature of the Jira Integration.') }}</p>
<gl-button
v-if="upgradePlanPath"
category="primary"
variant="info"
:href="upgradePlanPath"
target="_blank"
>
{{ __('Upgrade your plan') }}
</gl-button>
</gl-card>
</div> </div>
</gl-form-group> </gl-form-group>
<gl-form-group :label="s__('JiraService|Jira project key')"> <template v-if="showJiraIssuesIntegration">
<gl-form-group
:label="s__('JiraService|Jira project key')"
:invalid-feedback="__('This field is required.')"
:state="validProjectKey"
>
<gl-form-input <gl-form-input
v-model="projectKey" v-model="projectKey"
type="text"
name="service[project_key]" name="service[project_key]"
:placeholder="s__('JiraService|e.g. AB')" :placeholder="s__('JiraService|e.g. AB')"
:required="enableJiraIssues"
:state="validProjectKey"
:disabled="!enableJiraIssues" :disabled="!enableJiraIssues"
/> />
</gl-form-group> </gl-form-group>
...@@ -84,5 +144,6 @@ export default { ...@@ -84,5 +144,6 @@ export default {
</template> </template>
</gl-sprintf> </gl-sprintf>
</p> </p>
</template>
</div> </div>
</template> </template>
...@@ -19,6 +19,7 @@ export default el => { ...@@ -19,6 +19,7 @@ export default el => {
type, type,
commentDetail, commentDetail,
projectKey, projectKey,
upgradePlanPath,
editProjectPath, editProjectPath,
triggerEvents, triggerEvents,
fields, fields,
...@@ -30,6 +31,7 @@ export default el => { ...@@ -30,6 +31,7 @@ export default el => {
commitEvents, commitEvents,
mergeRequestEvents, mergeRequestEvents,
enableComments, enableComments,
showJiraIssuesIntegration,
enableJiraIssues, enableJiraIssues,
} = parseBooleanInData(booleanAttributes); } = parseBooleanInData(booleanAttributes);
...@@ -50,8 +52,10 @@ export default el => { ...@@ -50,8 +52,10 @@ export default el => {
initialCommentDetail: commentDetail, initialCommentDetail: commentDetail,
}, },
jiraIssuesProps: { jiraIssuesProps: {
showJiraIssuesIntegration,
initialEnableJiraIssues: enableJiraIssues, initialEnableJiraIssues: enableJiraIssues,
initialProjectKey: projectKey, initialProjectKey: projectKey,
upgradePlanPath,
editProjectPath, editProjectPath,
}, },
triggerEvents: JSON.parse(triggerEvents), triggerEvents: JSON.parse(triggerEvents),
......
...@@ -15,8 +15,10 @@ module EE ...@@ -15,8 +15,10 @@ module EE
if integration.is_a?(JiraService) if integration.is_a?(JiraService)
form_data.merge!( form_data.merge!(
show_jira_issues_integration: @project&.feature_available?(:jira_issues_integration).to_s,
enable_jira_issues: integration.issues_enabled.to_s, enable_jira_issues: integration.issues_enabled.to_s,
project_key: integration.project_key, project_key: integration.project_key,
upgrade_plan_path: @project && ::Gitlab::CurrentSettings.should_check_namespace_plan? ? upgrade_plan_path(@project.group) : nil,
edit_project_path: @project ? edit_project_path(@project, anchor: 'js-shared-permissions') : nil edit_project_path: @project ? edit_project_path(@project, anchor: 'js-shared-permissions') : nil
) )
end end
......
...@@ -24,7 +24,7 @@ RSpec.describe EE::ServicesHelper do ...@@ -24,7 +24,7 @@ RSpec.describe EE::ServicesHelper do
let(:integration) { build(:slack_service) } let(:integration) { build(:slack_service) }
it 'does not include Jira specific fields' do it 'does not include Jira specific fields' do
is_expected.not_to include(:enable_jira_issues, :project_key, :edit_project_path) is_expected.not_to include(:show_jira_issues_integration, :enable_jira_issues, :project_key, :edit_project_path)
end end
end end
...@@ -32,7 +32,7 @@ RSpec.describe EE::ServicesHelper do ...@@ -32,7 +32,7 @@ RSpec.describe EE::ServicesHelper do
let(:integration) { build(:jira_service) } let(:integration) { build(:jira_service) }
it 'includes Jira specific fields' do it 'includes Jira specific fields' do
is_expected.to include(:enable_jira_issues, :project_key, :edit_project_path) is_expected.to include(:show_jira_issues_integration, :enable_jira_issues, :project_key, :edit_project_path)
end end
end end
end end
......
...@@ -13122,7 +13122,7 @@ msgstr "" ...@@ -13122,7 +13122,7 @@ msgstr ""
msgid "JiraService|Web URL" msgid "JiraService|Web URL"
msgstr "" msgstr ""
msgid "JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of issues and view any issue as read-only." msgid "JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only."
msgstr "" msgstr ""
msgid "JiraService|e.g. AB" msgid "JiraService|e.g. AB"
...@@ -23857,6 +23857,9 @@ msgstr "" ...@@ -23857,6 +23857,9 @@ msgstr ""
msgid "This is a \"Ghost User\", created to hold all issues authored by users that have since been deleted. This user cannot be removed." msgid "This is a \"Ghost User\", created to hold all issues authored by users that have since been deleted. This user cannot be removed."
msgstr "" msgstr ""
msgid "This is a Premium feature"
msgstr ""
msgid "This is a Work in Progress" msgid "This is a Work in Progress"
msgstr "" msgstr ""
...@@ -25265,6 +25268,9 @@ msgstr "" ...@@ -25265,6 +25268,9 @@ msgstr ""
msgid "Upgrade plan to unlock Canary Deployments feature" msgid "Upgrade plan to unlock Canary Deployments feature"
msgstr "" msgstr ""
msgid "Upgrade your plan"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search." msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "" msgstr ""
...@@ -25274,6 +25280,9 @@ msgstr "" ...@@ -25274,6 +25280,9 @@ msgstr ""
msgid "Upgrade your plan to activate Group Webhooks." msgid "Upgrade your plan to activate Group Webhooks."
msgstr "" msgstr ""
msgid "Upgrade your plan to enable this feature of the Jira Integration."
msgstr ""
msgid "Upgrade your plan to improve Issue boards." msgid "Upgrade your plan to improve Issue boards."
msgstr "" msgstr ""
......
...@@ -189,5 +189,39 @@ describe('DynamicField', () => { ...@@ -189,5 +189,39 @@ describe('DynamicField', () => {
}); });
}); });
}); });
describe('validations', () => {
describe('password field', () => {
beforeEach(() => {
createComponent({
type: 'password',
required: true,
value: null,
});
wrapper.vm.validated = true;
});
describe('without value', () => {
it('requires validation', () => {
expect(wrapper.vm.valid).toBe(false);
expect(findGlFormGroup().classes('is-invalid')).toBe(true);
expect(findGlFormInput().classes('is-invalid')).toBe(true);
});
});
describe('with value', () => {
beforeEach(() => {
wrapper.setProps({ value: 'true' });
});
it('does not require validation', () => {
expect(wrapper.vm.valid).toBe(true);
expect(findGlFormGroup().classes('is-valid')).toBe(true);
expect(findGlFormInput().classes('is-valid')).toBe(true);
});
});
});
});
}); });
}); });
...@@ -6,6 +6,7 @@ describe('JiraIssuesFields', () => { ...@@ -6,6 +6,7 @@ describe('JiraIssuesFields', () => {
let wrapper; let wrapper;
const defaultProps = { const defaultProps = {
showJiraIssuesIntegration: true,
editProjectPath: '/edit', editProjectPath: '/edit',
}; };
...@@ -24,13 +25,33 @@ describe('JiraIssuesFields', () => { ...@@ -24,13 +25,33 @@ describe('JiraIssuesFields', () => {
const findEnableCheckbox = () => wrapper.find(GlFormCheckbox); const findEnableCheckbox = () => wrapper.find(GlFormCheckbox);
const findProjectKey = () => wrapper.find(GlFormInput); const findProjectKey = () => wrapper.find(GlFormInput);
const expectedBannerText = 'This is a Premium feature';
describe('template', () => { describe('template', () => {
describe('upgrade banner for non-Premium user', () => {
beforeEach(() => {
createComponent({ initialProjectKey: '', showJiraIssuesIntegration: false });
});
it('shows upgrade banner', () => {
expect(wrapper.text()).toContain(expectedBannerText);
});
it('does not show checkbox and input field', () => {
expect(findEnableCheckbox().exists()).toBe(false);
expect(findProjectKey().exists()).toBe(false);
});
});
describe('Enable Jira issues checkbox', () => { describe('Enable Jira issues checkbox', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ initialProjectKey: '' }); createComponent({ initialProjectKey: '' });
}); });
it('does not show upgrade banner', () => {
expect(wrapper.text()).not.toContain(expectedBannerText);
});
// As per https://vuejs.org/v2/guide/forms.html#Checkbox-1, // As per https://vuejs.org/v2/guide/forms.html#Checkbox-1,
// browsers don't include unchecked boxes in form submissions. // browsers don't include unchecked boxes in form submissions.
it('includes issues_enabled as false even if unchecked', () => { it('includes issues_enabled as false even if unchecked', () => {
...@@ -41,6 +62,10 @@ describe('JiraIssuesFields', () => { ...@@ -41,6 +62,10 @@ describe('JiraIssuesFields', () => {
expect(findProjectKey().attributes('disabled')).toBe('disabled'); expect(findProjectKey().attributes('disabled')).toBe('disabled');
}); });
it('does not require project_key', () => {
expect(findProjectKey().attributes('required')).toBeUndefined();
});
describe('on enable issues', () => { describe('on enable issues', () => {
it('enables project_key input', () => { it('enables project_key input', () => {
findEnableCheckbox().vm.$emit('input', true); findEnableCheckbox().vm.$emit('input', true);
...@@ -49,6 +74,14 @@ describe('JiraIssuesFields', () => { ...@@ -49,6 +74,14 @@ describe('JiraIssuesFields', () => {
expect(findProjectKey().attributes('disabled')).toBeUndefined(); expect(findProjectKey().attributes('disabled')).toBeUndefined();
}); });
}); });
it('requires project_key input', () => {
findEnableCheckbox().vm.$emit('input', true);
return wrapper.vm.$nextTick().then(() => {
expect(findProjectKey().attributes('required')).toBe('required');
});
});
}); });
}); });
......
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