Commit 54534c97 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '338644-project-overrides-add-counter' into 'master'

Add projects using custom overrides counter to tabs

See merge request gitlab-org/gitlab!76644
parents 16ac36f0 484f74ab
...@@ -23,3 +23,6 @@ export const I18N_FETCH_TEST_SETTINGS_DEFAULT_ERROR_MESSAGE = s__( ...@@ -23,3 +23,6 @@ export const I18N_FETCH_TEST_SETTINGS_DEFAULT_ERROR_MESSAGE = s__(
); );
export const I18N_DEFAULT_ERROR_MESSAGE = __('Something went wrong on our end.'); export const I18N_DEFAULT_ERROR_MESSAGE = __('Something went wrong on our end.');
export const I18N_SUCCESSFUL_CONNECTION_MESSAGE = s__('Integrations|Connection successful.'); export const I18N_SUCCESSFUL_CONNECTION_MESSAGE = s__('Integrations|Connection successful.');
export const settingsTabTitle = __('Settings');
export const overridesTabTitle = s__('Integrations|Projects using custom settings');
...@@ -11,6 +11,8 @@ import { __, s__ } from '~/locale'; ...@@ -11,6 +11,8 @@ import { __, s__ } from '~/locale';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue'; import UrlSync from '~/vue_shared/components/url_sync.vue';
import IntegrationTabs from './integration_tabs.vue';
const DEFAULT_PAGE = 1; const DEFAULT_PAGE = 1;
export default { export default {
...@@ -23,6 +25,7 @@ export default { ...@@ -23,6 +25,7 @@ export default {
GlAlert, GlAlert,
ProjectAvatar, ProjectAvatar,
UrlSync, UrlSync,
IntegrationTabs,
}, },
props: { props: {
overridesPath: { overridesPath: {
...@@ -46,6 +49,9 @@ export default { ...@@ -46,6 +49,9 @@ export default {
}; };
}, },
computed: { computed: {
overridesCount() {
return this.isLoading ? null : this.totalItems;
},
showPagination() { showPagination() {
return this.totalItems > this.$options.DEFAULT_PER_PAGE && this.overrides.length > 0; return this.totalItems > this.$options.DEFAULT_PER_PAGE && this.overrides.length > 0;
}, },
...@@ -100,6 +106,7 @@ export default { ...@@ -100,6 +106,7 @@ export default {
<template> <template>
<div> <div>
<integration-tabs :project-overrides-count="overridesCount" />
<gl-alert v-if="errorMessage" variant="danger" :dismissible="false"> <gl-alert v-if="errorMessage" variant="danger" :dismissible="false">
{{ errorMessage }} {{ errorMessage }}
</gl-alert> </gl-alert>
......
<script>
import { GlBadge, GlNavItem, GlTabs, GlTab } from '@gitlab/ui';
import { settingsTabTitle, overridesTabTitle } from '~/integrations/constants';
export default {
components: {
GlBadge,
GlNavItem,
GlTabs,
GlTab,
},
inject: {
editPath: {
default: '',
},
},
props: {
projectOverridesCount: {
type: [Number, String],
required: false,
default: null,
},
},
i18n: {
settingsTabTitle,
overridesTabTitle,
},
};
</script>
<template>
<gl-tabs>
<template #tabs-start>
<gl-nav-item role="presentation" link-classes="gl-tab-nav-item" :href="editPath">{{
$options.i18n.settingsTabTitle
}}</gl-nav-item>
</template>
<gl-tab active>
<template #title>
{{ $options.i18n.overridesTabTitle }}
<gl-badge
v-if="projectOverridesCount !== null"
variant="muted"
size="sm"
class="gl-tab-counter-badge"
>{{ projectOverridesCount }}</gl-badge
>
</template>
</gl-tab>
</gl-tabs>
</template>
...@@ -8,10 +8,13 @@ export default () => { ...@@ -8,10 +8,13 @@ export default () => {
return null; return null;
} }
const { overridesPath } = el.dataset; const { editPath, overridesPath } = el.dataset;
return new Vue({ return new Vue({
el, el,
provide: {
editPath,
},
render(createElement) { render(createElement) {
return createElement(IntegrationOverrides, { return createElement(IntegrationOverrides, {
props: { props: {
......
...@@ -101,8 +101,9 @@ module IntegrationsHelper ...@@ -101,8 +101,9 @@ module IntegrationsHelper
form_data form_data
end end
def integration_overrides_data(integration) def integration_overrides_data(integration, project: nil, group: nil)
{ {
edit_path: scoped_edit_integration_path(integration, project: project, group: group),
overrides_path: scoped_overrides_integration_path(integration, format: :json) overrides_path: scoped_overrides_integration_path(integration, format: :json)
} }
end end
......
...@@ -6,5 +6,4 @@ ...@@ -6,5 +6,4 @@
%h3.page-title %h3.page-title
= @integration.title = @integration.title
= render 'shared/integrations/tabs', integration: @integration, active_tab: 'overrides' do .js-vue-integration-overrides{ data: integration_overrides_data(@integration, project: @project, group: @group) }
.js-vue-integration-overrides{ data: integration_overrides_data(@integration) }
...@@ -5,6 +5,8 @@ import MockAdapter from 'axios-mock-adapter'; ...@@ -5,6 +5,8 @@ import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { DEFAULT_PER_PAGE } from '~/api'; import { DEFAULT_PER_PAGE } from '~/api';
import IntegrationOverrides from '~/integrations/overrides/components/integration_overrides.vue'; import IntegrationOverrides from '~/integrations/overrides/components/integration_overrides.vue';
import IntegrationTabs from '~/integrations/overrides/components/integration_tabs.vue';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
...@@ -49,6 +51,7 @@ describe('IntegrationOverrides', () => { ...@@ -49,6 +51,7 @@ describe('IntegrationOverrides', () => {
const findGlTable = () => wrapper.findComponent(GlTable); const findGlTable = () => wrapper.findComponent(GlTable);
const findPagination = () => wrapper.findComponent(GlPagination); const findPagination = () => wrapper.findComponent(GlPagination);
const findIntegrationTabs = () => wrapper.findComponent(IntegrationTabs);
const findRowsAsModel = () => const findRowsAsModel = () =>
findGlTable() findGlTable()
.findAllComponents(GlLink) .findAllComponents(GlLink)
...@@ -72,6 +75,12 @@ describe('IntegrationOverrides', () => { ...@@ -72,6 +75,12 @@ describe('IntegrationOverrides', () => {
expect(table.exists()).toBe(true); expect(table.exists()).toBe(true);
expect(table.attributes('busy')).toBe('true'); expect(table.attributes('busy')).toBe('true');
}); });
it('renders IntegrationTabs with count as `null`', () => {
createComponent();
expect(findIntegrationTabs().props('projectOverridesCount')).toBe(null);
});
}); });
describe('when initial request is successful', () => { describe('when initial request is successful', () => {
...@@ -84,6 +93,13 @@ describe('IntegrationOverrides', () => { ...@@ -84,6 +93,13 @@ describe('IntegrationOverrides', () => {
expect(table.attributes('busy')).toBeFalsy(); expect(table.attributes('busy')).toBeFalsy();
}); });
it('renders IntegrationTabs with count', async () => {
createComponent();
await waitForPromises();
expect(findIntegrationTabs().props('projectOverridesCount')).toBe(mockOverrides.length);
});
describe('table template', () => { describe('table template', () => {
beforeEach(async () => { beforeEach(async () => {
createComponent({ mountFn: mount }); createComponent({ mountFn: mount });
......
import { mount, shallowMount } from '@vue/test-utils';
import { GlBadge, GlTab } from '@gitlab/ui';
import IntegrationTabs from '~/integrations/overrides/components/integration_tabs.vue';
import { settingsTabTitle, overridesTabTitle } from '~/integrations/constants';
describe('IntegrationTabs', () => {
let wrapper;
const editPath = 'mock/edit';
const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => {
wrapper = mountFn(IntegrationTabs, {
propsData: props,
provide: {
editPath,
},
stubs: {
GlTab,
},
});
};
afterEach(() => {
wrapper.destroy();
});
const findGlBadge = () => wrapper.findComponent(GlBadge);
const findGlTab = () => wrapper.findComponent(GlTab);
const findSettingsLink = () => wrapper.find('a');
describe('template', () => {
it('renders "Settings" tab as a link', () => {
createComponent({ mountFn: mount });
expect(findSettingsLink().text()).toMatchInterpolatedText(settingsTabTitle);
expect(findSettingsLink().attributes('href')).toBe(editPath);
});
it('renders "Projects using custom settings" tab as active', () => {
const projectOverridesCount = '1';
createComponent({
props: { projectOverridesCount },
});
expect(findGlTab().exists()).toBe(true);
expect(findGlTab().text()).toMatchInterpolatedText(
`${overridesTabTitle} ${projectOverridesCount}`,
);
expect(findGlBadge().text()).toBe(projectOverridesCount);
});
describe('when count is `null', () => {
it('renders "Projects using custom settings" tab without count', () => {
createComponent();
expect(findGlTab().exists()).toBe(true);
expect(findGlTab().text()).toMatchInterpolatedText(overridesTabTitle);
expect(findGlBadge().exists()).toBe(false);
});
});
});
});
...@@ -70,6 +70,20 @@ RSpec.describe IntegrationsHelper do ...@@ -70,6 +70,20 @@ RSpec.describe IntegrationsHelper do
end end
end end
describe '#integration_overrides_data' do
let(:integration) { build_stubbed(:jira_integration) }
let(:fields) do
[
edit_path: edit_admin_application_settings_integration_path(integration),
overrides_path: overrides_admin_application_settings_integration_path(integration, format: :json)
]
end
subject { helper.integration_overrides_data(integration) }
it { is_expected.to include(*fields) }
end
describe '#scoped_reset_integration_path' do describe '#scoped_reset_integration_path' do
let(:integration) { build_stubbed(:jira_integration) } let(:integration) { build_stubbed(:jira_integration) }
let(:group) { nil } let(:group) { nil }
......
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