Commit 34fa04d0 authored by Tom Quirk's avatar Tom Quirk Committed by David O'Regan

Break Jira connect app into multiple page components

Creates 2 "page" components for jira connect. Shows them
depending on whether the user is signed in.
parent 9fcc3738
<script>
import { GlAlert, GlLink, GlSprintf, GlEmptyState } from '@gitlab/ui';
import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { mapState, mapMutations } from 'vuex';
import { retrieveAlert } from '~/jira_connect/subscriptions/utils';
import { SET_ALERT } from '../store/mutation_types';
import SubscriptionsList from './subscriptions_list.vue';
import AddNamespaceButton from './add_namespace_button.vue';
import SignInButton from './sign_in_button.vue';
import SignInPage from '../pages/sign_in.vue';
import SubscriptionsPage from '../pages/subscriptions.vue';
import UserLink from './user_link.vue';
import CompatibilityAlert from './compatibility_alert.vue';
......@@ -16,12 +15,10 @@ export default {
GlAlert,
GlLink,
GlSprintf,
GlEmptyState,
SubscriptionsList,
AddNamespaceButton,
SignInButton,
UserLink,
CompatibilityAlert,
SignInPage,
SubscriptionsPage,
},
inject: {
usersPath: {
......@@ -84,43 +81,9 @@ export default {
<user-link :user-signed-in="userSignedIn" :has-subscriptions="hasSubscriptions" />
<h2 class="gl-text-center gl-mb-7">{{ s__('JiraService|GitLab for Jira Configuration') }}</h2>
<div class="jira-connect-app-body gl-mx-auto gl-px-5 gl-mb-7">
<template v-if="hasSubscriptions">
<div class="gl-display-flex gl-justify-content-end">
<sign-in-button v-if="!userSignedIn" :users-path="usersPath" />
<add-namespace-button v-else />
</div>
<subscriptions-list />
</template>
<template v-else>
<div v-if="!userSignedIn" class="gl-text-center">
<p class="gl-mb-7">{{ s__('JiraService|Sign in to GitLab.com to get started.') }}</p>
<sign-in-button class="gl-mb-7" :users-path="usersPath">
{{ __('Sign in to GitLab') }}
</sign-in-button>
<p>
{{
s__(
'Integrations|Note: this integration only works with accounts on GitLab.com (SaaS).',
)
}}
</p>
</div>
<gl-empty-state
v-else
:title="s__('Integrations|No linked namespaces')"
:description="
s__(
'Integrations|Namespaces are the GitLab groups and subgroups you link to this Jira instance.',
)
"
>
<template #actions>
<add-namespace-button />
</template>
</gl-empty-state>
</template>
<div class="gl-layout-w-limited gl-mx-auto gl-px-5 gl-mb-7">
<sign-in-page v-if="!userSignedIn" :has-subscriptions="hasSubscriptions" />
<subscriptions-page v-else :has-subscriptions="hasSubscriptions" />
</div>
</div>
</template>
<script>
import { GlButton } from '@gitlab/ui';
import { getGitlabSignInURL } from '~/jira_connect/subscriptions/utils';
import { s__ } from '~/locale';
export default {
components: {
......@@ -25,12 +26,15 @@ export default {
this.signInURL = await getGitlabSignInURL(this.usersPath);
},
},
i18n: {
defaultButtonText: s__('Integrations|Sign in to GitLab'),
},
};
</script>
<template>
<gl-button category="primary" variant="info" :href="signInURL" target="_blank">
<slot>
{{ s__('Integrations|Sign in to add namespaces') }}
{{ $options.i18n.defaultButtonText }}
</slot>
</gl-button>
</template>
<script>
import { s__ } from '~/locale';
import SubscriptionsList from '../components/subscriptions_list.vue';
import SignInButton from '../components/sign_in_button.vue';
export default {
name: 'SignInPage',
components: {
SubscriptionsList,
SignInButton,
},
inject: ['usersPath'],
props: {
hasSubscriptions: {
type: Boolean,
required: true,
},
},
i18n: {
signinButtonTextWithSubscriptions: s__('Integrations|Sign in to add namespaces'),
signInText: s__('JiraService|Sign in to GitLab.com to get started.'),
},
};
</script>
<template>
<div v-if="hasSubscriptions">
<div class="gl-display-flex gl-justify-content-end">
<sign-in-button :users-path="usersPath">
{{ $options.i18n.signinButtonTextWithSubscriptions }}
</sign-in-button>
</div>
<subscriptions-list />
</div>
<div v-else class="gl-text-center">
<p class="gl-mb-7">{{ $options.i18n.signInText }}</p>
<sign-in-button class="gl-mb-7" :users-path="usersPath" />
</div>
</template>
<script>
import { GlEmptyState } from '@gitlab/ui';
import SubscriptionsList from '../components/subscriptions_list.vue';
import AddNamespaceButton from '../components/add_namespace_button.vue';
export default {
name: 'SubscriptionsPage',
components: {
GlEmptyState,
SubscriptionsList,
AddNamespaceButton,
},
props: {
hasSubscriptions: {
type: Boolean,
required: true,
},
},
};
</script>
<template>
<div v-if="hasSubscriptions">
<div class="gl-display-flex gl-justify-content-end">
<add-namespace-button />
</div>
<subscriptions-list />
</div>
<gl-empty-state
v-else
:title="s__('Integrations|No linked namespaces')"
:description="
s__(
'Integrations|Namespaces are the GitLab groups and subgroups you link to this Jira instance.',
)
"
>
<template #actions>
<add-namespace-button />
</template>
</gl-empty-state>
</template>
......@@ -40,10 +40,6 @@ $header-height: 40px;
max-width: 1000px;
}
.jira-connect-app-body {
max-width: 768px;
}
// needed for external_link
svg.s16 {
width: 16px;
......
......@@ -19340,9 +19340,6 @@ msgstr ""
msgid "Integrations|No linked namespaces"
msgstr ""
msgid "Integrations|Note: this integration only works with accounts on GitLab.com (SaaS)."
msgstr ""
msgid "Integrations|Projects using custom settings"
msgstr ""
......@@ -19379,6 +19376,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
msgid "Integrations|Sign in to GitLab"
msgstr ""
msgid "Integrations|Sign in to add namespaces"
msgstr ""
......
import { GlLink, GlEmptyState } from '@gitlab/ui';
import { GlLink } from '@gitlab/ui';
import { nextTick } from 'vue';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import JiraConnectApp from '~/jira_connect/subscriptions/components/app.vue';
import AddNamespaceButton from '~/jira_connect/subscriptions/components/add_namespace_button.vue';
import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
import SignInPage from '~/jira_connect/subscriptions/pages/sign_in.vue';
import SubscriptionsPage from '~/jira_connect/subscriptions/pages/subscriptions.vue';
import UserLink from '~/jira_connect/subscriptions/components/user_link.vue';
import createStore from '~/jira_connect/subscriptions/store';
import { SET_ALERT } from '~/jira_connect/subscriptions/store/mutation_types';
......@@ -23,10 +22,8 @@ describe('JiraConnectApp', () => {
const findAlert = () => wrapper.findByTestId('jira-connect-persisted-alert');
const findAlertLink = () => findAlert().findComponent(GlLink);
const findSignInButton = () => wrapper.findComponent(SignInButton);
const findAddNamespaceButton = () => wrapper.findComponent(AddNamespaceButton);
const findSubscriptionsList = () => wrapper.findComponent(SubscriptionsList);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findSignInPage = () => wrapper.findComponent(SignInPage);
const findSubscriptionsPage = () => wrapper.findComponent(SubscriptionsPage);
const createComponent = ({ provide, mountFn = shallowMountExtended } = {}) => {
store = createStore();
......@@ -43,49 +40,35 @@ describe('JiraConnectApp', () => {
describe('template', () => {
describe.each`
scenario | usersPath | subscriptions | expectSignInButton | expectEmptyState | expectNamespaceButton | expectSubscriptionsList
${'user is not signed in with subscriptions'} | ${'/users'} | ${[mockSubscription]} | ${true} | ${false} | ${false} | ${true}
${'user is not signed in without subscriptions'} | ${'/users'} | ${undefined} | ${true} | ${false} | ${false} | ${false}
${'user is signed in with subscriptions'} | ${undefined} | ${[mockSubscription]} | ${false} | ${false} | ${true} | ${true}
${'user is signed in without subscriptions'} | ${undefined} | ${undefined} | ${false} | ${true} | ${false} | ${false}
`(
'when $scenario',
({
usersPath,
expectSignInButton,
subscriptions,
expectEmptyState,
expectNamespaceButton,
expectSubscriptionsList,
}) => {
beforeEach(() => {
createComponent({
provide: {
usersPath,
subscriptions,
},
});
});
it(`${expectSignInButton ? 'renders' : 'does not render'} sign in button`, () => {
expect(findSignInButton().exists()).toBe(expectSignInButton);
});
it(`${expectEmptyState ? 'renders' : 'does not render'} empty state`, () => {
expect(findEmptyState().exists()).toBe(expectEmptyState);
scenario | usersPath | shouldRenderSignInPage | shouldRenderSubscriptionsPage
${'user is not signed in'} | ${'/users'} | ${true} | ${false}
${'user is signed in'} | ${undefined} | ${false} | ${true}
`('when $scenario', ({ usersPath, shouldRenderSignInPage, shouldRenderSubscriptionsPage }) => {
beforeEach(() => {
createComponent({
provide: {
usersPath,
subscriptions: [mockSubscription],
},
});
});
it(`${
expectNamespaceButton ? 'renders' : 'does not render'
} button to add namespace`, () => {
expect(findAddNamespaceButton().exists()).toBe(expectNamespaceButton);
});
it(`${shouldRenderSignInPage ? 'renders' : 'does not render'} sign in page`, () => {
expect(findSignInPage().exists()).toBe(shouldRenderSignInPage);
if (shouldRenderSignInPage) {
expect(findSignInPage().props('hasSubscriptions')).toBe(true);
}
});
it(`${expectSubscriptionsList ? 'renders' : 'does not render'} subscriptions list`, () => {
expect(findSubscriptionsList().exists()).toBe(expectSubscriptionsList);
});
},
);
it(`${
shouldRenderSubscriptionsPage ? 'renders' : 'does not render'
} subscriptions page`, () => {
expect(findSubscriptionsPage().exists()).toBe(shouldRenderSubscriptionsPage);
if (shouldRenderSubscriptionsPage) {
expect(findSubscriptionsPage().props('hasSubscriptions')).toBe(true);
}
});
});
it('renders UserLink component', () => {
createComponent({
......
......@@ -11,11 +11,12 @@ jest.mock('~/jira_connect/subscriptions/utils');
describe('SignInButton', () => {
let wrapper;
const createComponent = () => {
const createComponent = ({ slots } = {}) => {
wrapper = shallowMount(SignInButton, {
propsData: {
usersPath: MOCK_USERS_PATH,
},
slots,
});
};
......@@ -29,6 +30,7 @@ describe('SignInButton', () => {
createComponent();
expect(findButton().exists()).toBe(true);
expect(findButton().text()).toBe(SignInButton.i18n.defaultButtonText);
});
describe.each`
......@@ -45,4 +47,12 @@ describe('SignInButton', () => {
expect(findButton().attributes('href')).toBe(expectedHref);
});
});
describe('with slot', () => {
const mockSlotContent = 'custom button content!';
it('renders slot content in button', () => {
createComponent({ slots: { default: mockSlotContent } });
expect(wrapper.text()).toMatchInterpolatedText(mockSlotContent);
});
});
});
import { mount } from '@vue/test-utils';
import SignInPage from '~/jira_connect/subscriptions/pages/sign_in.vue';
import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
import createStore from '~/jira_connect/subscriptions/store';
jest.mock('~/jira_connect/subscriptions/utils');
describe('SignInPage', () => {
let wrapper;
let store;
const findSignInButton = () => wrapper.findComponent(SignInButton);
const findSubscriptionsList = () => wrapper.findComponent(SubscriptionsList);
const createComponent = ({ provide, props } = {}) => {
store = createStore();
wrapper = mount(SignInPage, {
store,
provide,
propsData: props,
});
};
afterEach(() => {
wrapper.destroy();
});
describe('template', () => {
const mockUsersPath = '/test';
describe.each`
scenario | expectSubscriptionsList | signInButtonText
${'with subscriptions'} | ${true} | ${SignInPage.i18n.signinButtonTextWithSubscriptions}
${'without subscriptions'} | ${false} | ${SignInButton.i18n.defaultButtonText}
`('$scenario', ({ expectSubscriptionsList, signInButtonText }) => {
beforeEach(() => {
createComponent({
provide: {
usersPath: mockUsersPath,
},
props: {
hasSubscriptions: expectSubscriptionsList,
},
});
});
it(`renders sign in button with text ${signInButtonText}`, () => {
expect(findSignInButton().text()).toMatchInterpolatedText(signInButtonText);
});
it('renders sign in button with `usersPath` prop', () => {
expect(findSignInButton().props('usersPath')).toBe(mockUsersPath);
});
it(`${expectSubscriptionsList ? 'renders' : 'does not render'} subscriptions list`, () => {
expect(findSubscriptionsList().exists()).toBe(expectSubscriptionsList);
});
});
});
});
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SubscriptionsPage from '~/jira_connect/subscriptions/pages/subscriptions.vue';
import AddNamespaceButton from '~/jira_connect/subscriptions/components/add_namespace_button.vue';
import SubscriptionsList from '~/jira_connect/subscriptions/components/subscriptions_list.vue';
import createStore from '~/jira_connect/subscriptions/store';
describe('SubscriptionsPage', () => {
let wrapper;
let store;
const findAddNamespaceButton = () => wrapper.findComponent(AddNamespaceButton);
const findSubscriptionsList = () => wrapper.findComponent(SubscriptionsList);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const createComponent = ({ props } = {}) => {
store = createStore();
wrapper = shallowMount(SubscriptionsPage, {
store,
propsData: props,
});
};
afterEach(() => {
wrapper.destroy();
});
describe('template', () => {
describe.each`
scenario | expectSubscriptionsList | expectEmptyState
${'with subscriptions'} | ${true} | ${false}
${'without subscriptions'} | ${false} | ${true}
`('$scenario', ({ expectEmptyState, expectSubscriptionsList }) => {
beforeEach(() => {
createComponent({
props: {
hasSubscriptions: expectSubscriptionsList,
},
});
});
it('renders button to add namespace', () => {
expect(findAddNamespaceButton().exists()).toBe(true);
});
it(`${expectEmptyState ? 'renders' : 'does not render'} empty state`, () => {
expect(findEmptyState().exists()).toBe(expectEmptyState);
});
it(`${expectSubscriptionsList ? 'renders' : 'does not render'} subscriptions list`, () => {
expect(findSubscriptionsList().exists()).toBe(expectSubscriptionsList);
});
});
});
});
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