Commit 6f4aa7aa authored by Tom Quirk's avatar Tom Quirk

Improvements to integrations_table.vue

- Change :active prop name to :show-updated-at
- improve column widths
- Make integrations list slightly more efficient
- Add tooltip to integration status icon
- add specs

Also, include JS integrations table in groups, admin
parent cc61a2a1
<script>
import { s__ } from '~/locale';
import IntegrationsTable from './integrations_table.vue';
export default {
......@@ -13,20 +14,44 @@ export default {
},
},
computed: {
activeIntegrations() {
return this.integrations.filter((integration) => integration.active);
integrationsGrouped() {
return this.integrations.reduce(
(integrations, integration) => {
if (integration.active) {
integrations.active.push(integration);
} else {
integrations.inactive.push(integration);
}
return integrations;
},
{ active: [], inactive: [] },
);
},
inactiveIntegrations() {
return this.integrations.filter((integration) => !integration.active);
},
i18n: {
activeTableEmptyText: s__("Integrations|You haven't activated any integrations yet."),
inactiveTableEmptyText: s__("Integrations|You've activated every integration 🎉"),
},
};
</script>
<template>
<div>
<integrations-table :active="true" :integrations="activeIntegrations" />
<h5>{{ s__('Integrations|Add an integration') }}</h5>
<integrations-table :active="false" :integrations="inactiveIntegrations" />
<h4>{{ s__('Integrations|Active integrations') }}</h4>
<integrations-table
class="gl-mb-7!"
:integrations="integrationsGrouped.active"
:empty-text="$options.i18n.activeTableEmptyText"
show-updated-at
data-testid="active-integrations-table"
/>
<h4>{{ s__('Integrations|Add an integration') }}</h4>
<integrations-table
:integrations="integrationsGrouped.inactive"
:empty-text="$options.i18n.inactiveTableEmptyText"
data-testid="inactive-integrations-table"
/>
</div>
</template>
<script>
import { GlIcon, GlLink, GlTable } from '@gitlab/ui';
import { GlIcon, GlLink, GlTable, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
......@@ -10,15 +10,23 @@ export default {
GlTable,
TimeAgoTooltip,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
active: {
integrations: {
type: Array,
required: true,
},
showUpdatedAt: {
type: Boolean,
required: false,
default: false,
},
integrations: {
type: Array,
required: true,
emptyText: {
type: String,
required: false,
default: undefined,
},
},
computed: {
......@@ -27,21 +35,24 @@ export default {
{
key: 'active',
label: '',
thClass: 'gl-w-10',
},
{
key: 'name',
label: __('Integration'),
thClass: 'gl-w-quarter',
},
{
key: 'description',
label: __('Description'),
thClass: 'gl-display-none d-sm-table-cell',
tdClass: 'gl-display-none d-sm-table-cell',
},
this.active
? {
{
key: 'updated_at',
label: __('Last updated'),
}
: {},
label: this.showUpdatedAt ? __('Last updated') : '',
thClass: 'gl-w-20p',
},
];
},
},
......@@ -49,9 +60,19 @@ export default {
</script>
<template>
<gl-table :items="integrations" :fields="fields">
<gl-table :items="integrations" :fields="fields" :empty-text="emptyText" show-empty fixed>
<template #cell(active)="{ item }">
<gl-icon v-if="item.active" name="check" class="gl-text-green-500" />
<gl-icon
v-if="item.active"
v-gl-tooltip
name="check"
class="gl-text-green-500"
:title="
sprintf(s__('Integrations|%{integrationName}: active'), {
integrationName: item.name,
})
"
/>
</template>
<template #cell(name)="{ item }">
......@@ -59,12 +80,13 @@ export default {
:href="item.edit_path"
class="gl-font-weight-bold"
:data-qa-selector="`${item.type}_link`"
>{{ item.name }}</gl-link
>
{{ item.name }}
</gl-link>
</template>
<template #cell(updated_at)="{ item }">
<time-ago-tooltip v-if="item.updated_at" :time="item.updated_at" />
<time-ago-tooltip v-if="showUpdatedAt && item.updated_at" :time="item.updated_at" />
</template>
</gl-table>
</template>
import initIntegrationsList from '~/integrations/index';
initIntegrationsList();
......@@ -13,8 +13,8 @@
.gl-alert-actions
= link_to s_('AdminSettings|Go to General Settings'), general_admin_application_settings_path, class: 'btn gl-alert-action btn-info gl-button'
%h4= s_('AdminSettings|Apply integration settings to all Projects')
%p
= s_('AdminSettings|Integrations configured here will automatically apply to all projects on this instance.')
= link_to _('Learn more'), integrations_help_page_path, target: '_blank', rel: 'noopener noreferrer'
%h3= s_('Integrations|Project integration management')
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
%p= s_('Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}.').html_safe % { integrations_link_start: integrations_link_start, link_end: '</a>'.html_safe }
= render 'shared/integrations/index', integrations: @integrations
......@@ -2,8 +2,8 @@
- page_title _('Integrations')
- @content_class = 'limit-container-width' unless fluid_layout
%h4= s_('GroupSettings|Apply integration settings to all Projects')
%p
= s_('GroupSettings|Integrations configured here will automatically apply to all projects in this group.')
= link_to _('Learn more'), integrations_help_page_path, target: '_blank', rel: 'noopener noreferrer'
%h3= s_('Integrations|Project integration management')
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
%p= s_('Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}.').html_safe % { integrations_link_start: integrations_link_start, link_end: '</a>'.html_safe }
= render 'shared/integrations/index', integrations: @integrations
......@@ -12,7 +12,7 @@
.gl-alert-actions
= link_to _('Go to Webhooks'), project_hooks_path(@project), class: 'gl-button btn gl-alert-action btn-info'
%h4= s_('Integrations|Active integrations')
%h3= s_('Integrations|Integrations')
- integrations_link_start = '<a href="%{url}">'.html_safe % { url: help_page_url('user/project/integrations/overview') }
- webhooks_link_start = '<a href="%{url}">'.html_safe % { url: project_hooks_path(@project) }
%p= _("%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, webhooks_link_start: webhooks_link_start, link_end: '</a>'.html_safe }
......
import $ from 'jquery';
import initIntegrationsList from '~/integrations/index';
import { loadCSSFile } from '~/lib/utils/css_utils';
import { select2AxiosTransport } from '~/lib/utils/select2_utils';
import { s__ } from '~/locale';
......@@ -41,6 +42,8 @@ const getDropdownConfig = (placeholder, url) => ({
const callout = document.querySelector('.js-admin-integrations-moved');
PersistentUserCallout.factory(callout);
initIntegrationsList();
// ElasticSearch
const $container = $('#js-elasticsearch-settings');
......
......@@ -2285,9 +2285,6 @@ msgstr ""
msgid "AdminProjects|Delete Project %{projectName}?"
msgstr ""
msgid "AdminSettings|Apply integration settings to all Projects"
msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
......@@ -2306,9 +2303,6 @@ msgstr ""
msgid "AdminSettings|Go to General Settings"
msgstr ""
msgid "AdminSettings|Integrations configured here will automatically apply to all projects on this instance."
msgstr ""
msgid "AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines"
msgstr ""
......@@ -15457,9 +15451,6 @@ msgstr ""
msgid "GroupSettings|Allow project access token creation"
msgstr ""
msgid "GroupSettings|Apply integration settings to all Projects"
msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
......@@ -15508,9 +15499,6 @@ msgstr ""
msgid "GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility."
msgstr ""
msgid "GroupSettings|Integrations configured here will automatically apply to all projects in this group."
msgstr ""
msgid "GroupSettings|Learn more about group-level project templates."
msgstr ""
......@@ -17068,6 +17056,9 @@ msgstr ""
msgid "Integrations"
msgstr ""
msgid "Integrations|%{integrationName}: active"
msgstr ""
msgid "Integrations|%{integration} settings saved and active."
msgstr ""
......@@ -17092,6 +17083,9 @@ msgstr ""
msgid "Integrations|All projects inheriting these settings will also be reset."
msgstr ""
msgid "Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}."
msgstr ""
msgid "Integrations|Browser limitations"
msgstr ""
......@@ -17131,6 +17125,9 @@ msgstr ""
msgid "Integrations|Includes commit title and branch"
msgstr ""
msgid "Integrations|Integrations"
msgstr ""
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
msgstr ""
......@@ -17155,6 +17152,9 @@ msgstr ""
msgid "Integrations|Note: this integration only works with accounts on GitLab.com (SaaS)."
msgstr ""
msgid "Integrations|Project integration management"
msgstr ""
msgid "Integrations|Projects using custom settings will not be affected."
msgstr ""
......@@ -17209,12 +17209,18 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
msgid "Integrations|You haven't activated any integrations yet."
msgstr ""
msgid "Integrations|You must have owner or maintainer permissions to link namespaces."
msgstr ""
msgid "Integrations|You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}"
msgstr ""
msgid "Integrations|You've activated every integration 🎉"
msgstr ""
msgid "Interactive mode"
msgstr ""
......@@ -24596,12 +24602,6 @@ msgstr ""
msgid "ProjectSelect|Search for project"
msgstr ""
msgid "ProjectService|%{service_title}: status off"
msgstr ""
msgid "ProjectService|%{service_title}: status on"
msgstr ""
msgid "ProjectService|Drone URL"
msgstr ""
......
......@@ -24,7 +24,7 @@ RSpec.describe 'User searches group settings', :js do
visit group_settings_integrations_path(group)
end
it_behaves_like 'can highlight results', 'integration settings'
it_behaves_like 'can highlight results', 'set default configuration'
end
context 'in Repository page' do
......
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import IntegrationsList from '~/integrations/index/components/integrations_list.vue';
import { mockActiveIntegrations, mockInactiveIntegrations } from '../mock_data';
describe('IntegrationsList', () => {
let wrapper;
const findActiveIntegrationsTable = () => wrapper.findByTestId('active-integrations-table');
const findInactiveIntegrationsTable = () => wrapper.findByTestId('inactive-integrations-table');
const createComponent = (propsData = {}) => {
wrapper = extendedWrapper(shallowMount(IntegrationsList, { propsData }));
};
afterEach(() => {
wrapper.destroy();
});
it('provides correct `integrations` prop to the IntegrationsTable instance', () => {
createComponent({ integrations: [...mockInactiveIntegrations, ...mockActiveIntegrations] });
expect(findActiveIntegrationsTable().props('integrations')).toEqual(mockActiveIntegrations);
expect(findInactiveIntegrationsTable().props('integrations')).toEqual(mockInactiveIntegrations);
});
});
import { GlTable, GlIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import IntegrationsTable from '~/integrations/index/components/integrations_table.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { mockActiveIntegrations, mockInactiveIntegrations } from '../mock_data';
describe('IntegrationsTable', () => {
let wrapper;
const findTable = () => wrapper.findComponent(GlTable);
const createComponent = (propsData = {}) => {
wrapper = mount(IntegrationsTable, {
propsData: {
integrations: mockActiveIntegrations,
...propsData,
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe.each([true, false])('when `showUpdatedAt` is %p', (showUpdatedAt) => {
beforeEach(() => {
createComponent({ showUpdatedAt });
});
it(`${showUpdatedAt ? 'renders' : 'does not render'} content in "Last updated" column`, () => {
const headers = findTable().findAll('th');
expect(headers.wrappers.some((header) => header.text() === 'Last updated')).toBe(
showUpdatedAt,
);
expect(wrapper.findComponent(TimeAgoTooltip).exists()).toBe(showUpdatedAt);
});
});
describe.each`
scenario | integrations | shouldRenderActiveIcon
${'when integration is active'} | ${[mockActiveIntegrations[0]]} | ${true}
${'when integration is inactive'} | ${[mockInactiveIntegrations[0]]} | ${false}
`('$scenario', ({ shouldRenderActiveIcon, integrations }) => {
beforeEach(() => {
createComponent({ integrations });
});
it(`${shouldRenderActiveIcon ? 'renders' : 'does not render'} icon in first column`, () => {
expect(findTable().findComponent(GlIcon).exists()).toBe(shouldRenderActiveIcon);
});
});
});
export const mockActiveIntegrations = [
{
active: true,
name: 'Asana',
description: 'Asana - Teamwork without email',
updated_at: '2021-03-18T00:27:09.634Z',
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/asana/edit',
type: 'asana',
},
{
active: true,
name: 'Jira',
description: 'Jira issue tracker',
updated_at: '2021-01-29T06:41:25.806Z',
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/jira/edit',
type: 'jira',
},
];
export const mockInactiveIntegrations = [
{
active: false,
name: 'Webex Teams',
description: 'Receive event notifications in Webex Teams',
updated_at: null,
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/webex_teams/edit',
type: 'webex_teams',
},
{
active: false,
name: 'YouTrack',
description: 'YouTrack issue tracker',
updated_at: null,
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/youtrack/edit',
type: 'youtrack',
},
{
active: false,
name: 'Atlassian Bamboo CI',
description: 'A continuous integration and build server',
updated_at: null,
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/bamboo/edit',
type: 'bamboo',
},
];
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