Commit e61e29a0 authored by Daniel Tian's avatar Daniel Tian

Update header and table for security config page

Update the header and use GlTable for the the security configuration
page
parent 041f190f
<script> <script>
import { GlLink } from '@gitlab/ui'; import { GlLink, GlSprintf, GlTable } from '@gitlab/ui';
import { s__, __, sprintf } from '~/locale'; import { s__, __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import AutoFixSettings from './auto_fix_settings.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AutoFixSettings from './auto_fix_settings.vue';
export default { export default {
components: { components: {
GlLink, GlLink,
Icon, GlSprintf,
GlTable,
AutoFixSettings, AutoFixSettings,
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
...@@ -41,33 +41,40 @@ export default { ...@@ -41,33 +41,40 @@ export default {
}, },
}, },
computed: { computed: {
headerContent() { devopsMessage() {
const body = __('Configure Security %{wordBreakOpportunity}and Compliance'); return this.autoDevopsEnabled
const wordBreakOpportunity = '<wbr />'; ? __(
'All security scans are enabled because %{linkStart}Auto DevOps%{linkEnd} is enabled on this project',
return sprintf(body, { wordBreakOpportunity }, false); )
: __(
`The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan.`,
);
}, },
callOutLink() { devopsUrl() {
return this.autoDevopsEnabled ? this.autoDevopsHelpPagePath : this.latestPipelinePath; return this.autoDevopsEnabled ? this.autoDevopsHelpPagePath : this.latestPipelinePath;
}, },
calloutContent() { fields() {
const bodyDefault = __(`The status of the table below only applies to the default branch and return [
is based on the %{linkStart}latest pipeline%{linkEnd}. {
Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan.`); key: 'feature',
label: s__('SecurityConfiguration|Security Control'),
const bodyAutoDevopsEnabled = __( thClass: 'gl-text-gray-900 bg-transparent border-bottom',
'All security scans are enabled because %{linkStart}Auto DevOps%{linkEnd} is enabled on this project', },
); {
key: 'configured',
const body = this.autoDevopsEnabled ? bodyAutoDevopsEnabled : bodyDefault; label: s__('SecurityConfiguration|Status'),
thClass: 'gl-text-gray-900 bg-transparent border-bottom',
const linkStart = `<a href="${this.callOutLink}" target="_blank" rel="noopener">`; formatter: this.getStatusText,
const linkEnd = '</a>'; },
];
return sprintf(body, { linkStart, linkEnd }, false);
}, },
}, },
methods: { methods: {
getStatusText(value) {
return value
? s__('SecurityConfiguration|Enabled')
: s__('SecurityConfiguration|Not yet enabled');
},
getFeatureDocumentationLinkLabel(featureName) { getFeatureDocumentationLinkLabel(featureName) {
return sprintf(s__('SecurityConfiguration|Feature documentation for %{featureName}'), { return sprintf(s__('SecurityConfiguration|Feature documentation for %{featureName}'), {
featureName, featureName,
...@@ -80,73 +87,32 @@ export default { ...@@ -80,73 +87,32 @@ export default {
<template> <template>
<article> <article>
<header> <header>
<h2 class="h4 my-3"> <h4 class="my-3">{{ __('Security Configuration') }}</h4>
<span v-html="headerContent"></span> <h5 class="gl-font-lg mt-5">{{ s__('SecurityConfiguration|Testing & Compliance') }}</h5>
<gl-link <p>
target="_blank" <gl-sprintf :message="devopsMessage">
:href="helpPagePath" <template #link="{ content }">
:aria-label="__('Security configuration help link')" <gl-link ref="pipelinesLink" :href="devopsUrl" target="_blank">{{ content }}</gl-link>
> </template>
<icon name="question" /> </gl-sprintf>
</gl-link> </p>
</h2>
</header> </header>
<section
ref="callout" <gl-table ref="securityControlTable" :items="features" :fields="fields" stacked="md">
class="bs-callout bs-callout-info mb-3 m-md-1 text-secondary" <template #cell(feature)="{ item }">
v-html="calloutContent" <div class="gl-text-gray-900">{{ item.name }}</div>
></section> <div>
<section ref="featuresTable" class="mt-0"> {{ item.description }}
<div
class="gl-responsive-table-row table-row-header text-2 font-weight-bold px-2 gl-text-gray-900"
role="row"
>
<div class="table-section section-80">
{{ s__('SecurityConfiguration|Secure features') }}
</div>
<div class="table-section section-20">{{ s__('SecurityConfiguration|Status') }}</div>
</div>
<div
v-for="feature in features"
ref="featureRow"
:key="feature.name"
class="gl-responsive-table-row flex-md-column align-items-md-stretch px-2"
>
<div class="d-md-flex align-items-center">
<div class="table-section section-80 section-wrap pr-md-3">
<div role="rowheader" class="table-mobile-header">
{{ s__('SecurityConfiguration|Feature') }}
</div>
<div class="table-mobile-content">
<div class="d-flex align-items-center justify-content-end justify-content-md-start">
<div class="text-2 gl-text-gray-900">{{ feature.name }}</div>
</div>
<div class="text-secondary">
{{ feature.description }}
<gl-link <gl-link
target="_blank" target="_blank"
:href="feature.link" :href="item.link"
:aria-label="getFeatureDocumentationLinkLabel(feature.name)" :aria-label="getFeatureDocumentationLinkLabel(item.name)"
>{{ __('More information') }}</gl-link
> >
{{ __('More information') }}
</gl-link>
</div> </div>
</div> </template>
</div> </gl-table>
<div class="table-section section-20 section-wrap pr-md-3">
<div role="rowheader" class="table-mobile-header">
{{ s__('SecurityConfiguration|Status') }}
</div>
<div ref="featureConfigStatus" class="table-mobile-content">
{{
feature.configured
? s__('SecurityConfiguration|Enabled')
: s__('SecurityConfiguration|Not yet enabled')
}}
</div>
</div>
</div>
</div>
</section>
<auto-fix-settings v-if="glFeatures.securityAutoFix" v-bind="autoFixSettingsProps" /> <auto-fix-settings v-if="glFeatures.securityAutoFix" v-bind="autoFixSettingsProps" />
</article> </article>
</template> </template>
---
title: Update header for security config page and change to GlTable
merge_request: 31471
author:
type: changed
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Security Configuration App features table displays a given feature 1`] = `
<section
class="mt-0"
>
<div
class="gl-responsive-table-row table-row-header text-2 font-weight-bold px-2 gl-text-gray-900"
role="row"
>
<div
class="table-section section-80"
>
Secure features
</div>
<div
class="table-section section-20"
>
Status
</div>
</div>
<div
class="gl-responsive-table-row flex-md-column align-items-md-stretch px-2"
>
<div
class="d-md-flex align-items-center"
>
<div
class="table-section section-80 section-wrap pr-md-3"
>
<div
class="table-mobile-header"
role="rowheader"
>
Feature
</div>
<div
class="table-mobile-content"
>
<div
class="d-flex align-items-center justify-content-end justify-content-md-start"
>
<div
class="text-2 gl-text-gray-900"
>
name-feature-0
</div>
</div>
<div
class="text-secondary"
>
description-feature-0
<gl-link-stub
aria-label="Feature documentation for name-feature-0"
href="link-feature-0"
target="_blank"
>
More information
</gl-link-stub>
</div>
</div>
</div>
<div
class="table-section section-20 section-wrap pr-md-3"
>
<div
class="table-mobile-header"
role="rowheader"
>
Status
</div>
<div
class="table-mobile-content"
>
Not yet enabled
</div>
</div>
</div>
</div>
</section>
`;
import { shallowMount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import SecurityConfigurationApp from 'ee/security_configuration/components/app.vue'; import SecurityConfigurationApp from 'ee/security_configuration/components/app.vue';
import stubChildren from 'helpers/stub_children';
describe('Security Configuration App', () => { describe('Security Configuration App', () => {
let wrapper; let wrapper;
const createComponent = (props = {}) => { const createComponent = (props = {}) => {
wrapper = shallowMount(SecurityConfigurationApp, { wrapper = mount(SecurityConfigurationApp, {
stubs: {
...stubChildren(SecurityConfigurationApp),
GlTable: false,
GlSprintf: false,
},
propsData: { propsData: {
features: [], features: [],
autoDevopsEnabled: false, autoDevopsEnabled: false,
...@@ -23,28 +28,19 @@ describe('Security Configuration App', () => { ...@@ -23,28 +28,19 @@ describe('Security Configuration App', () => {
wrapper.destroy(); wrapper.destroy();
}); });
const generateFeatures = n => const generateFeatures = n => {
[...Array(n).keys()].map(i => ({ return [...Array(n).keys()].map(i => ({
name: `name-feature-${i}`, name: `name-feature-${i}`,
description: `description-feature-${i}`, description: `description-feature-${i}`,
link: `link-feature-${i}`, link: `link-feature-${i}`,
configured: i % 2 === 0,
})); }));
};
const getHelpLink = () => wrapper.find('header').find(GlLink); const getPipelinesLink = () => wrapper.find({ ref: 'pipelinesLink' });
const getNotification = () => wrapper.find({ ref: 'callout' }); const getFeaturesTable = () => wrapper.find({ ref: 'securityControlTable' });
const getPipelinesLink = () => getNotification().find('a');
const getFeaturesTable = () => wrapper.find({ ref: 'featuresTable' });
const getFeatureConfigStatus = () => wrapper.find({ ref: 'featureConfigStatus' });
describe('header', () => { describe('header', () => {
it('displays a link to the given help page', () => {
const helpPagePath = 'http://foo';
createComponent({ helpPagePath });
expect(getHelpLink().attributes('href')).toBe(helpPagePath);
});
it.each` it.each`
autoDevopsEnabled | expectedUrl autoDevopsEnabled | expectedUrl
${true} | ${'http://autoDevopsHelpPagePath'} ${true} | ${'http://autoDevopsHelpPagePath'}
...@@ -55,41 +51,28 @@ describe('Security Configuration App', () => { ...@@ -55,41 +51,28 @@ describe('Security Configuration App', () => {
createComponent({ autoDevopsEnabled }); createComponent({ autoDevopsEnabled });
expect(getPipelinesLink().attributes('href')).toBe(expectedUrl); expect(getPipelinesLink().attributes('href')).toBe(expectedUrl);
expect(getPipelinesLink().attributes('rel')).toBe('noopener'); expect(getPipelinesLink().attributes('target')).toBe('_blank');
}, },
); );
}); });
describe('features table', () => { describe('features table', () => {
it('displays a row for each given feature', () => { it('passes the expected data to the GlTable', () => {
const features = generateFeatures(5); const features = generateFeatures(5);
createComponent({ features }); createComponent({ features });
expect(wrapper.findAll({ ref: 'featureRow' })).toHaveLength(5); expect(getFeaturesTable().classes('b-table-stacked-md')).toBeTruthy();
}); const rows = getFeaturesTable().findAll('tbody tr');
expect(rows).toHaveLength(5);
it('displays a given feature', () => {
const features = generateFeatures(1); for (let i = 0; i < features.length; i += 1) {
const [feature, status] = rows.at(i).findAll('td').wrappers;
createComponent({ features }); expect(feature.text()).toMatch(features[i].name);
expect(feature.text()).toMatch(features[i].description);
expect(getFeaturesTable().element).toMatchSnapshot(); expect(feature.find(GlLink).attributes('href')).toBe(features[i].link);
expect(status.text()).toMatch(features[i].configured ? 'Enabled' : 'Not yet enabled');
}
}); });
it.each`
configured | statusText
${true} | ${'Enabled'}
${false} | ${'Not yet enabled'}
`(
`displays "$statusText" if the given feature's configuration status is: "$configured"`,
({ configured, statusText }) => {
const features = [{ configured }];
createComponent({ features });
expect(getFeatureConfigStatus().text()).toBe(statusText);
},
);
}); });
}); });
...@@ -5684,9 +5684,6 @@ msgstr "" ...@@ -5684,9 +5684,6 @@ msgstr ""
msgid "Configure Prometheus" msgid "Configure Prometheus"
msgstr "" msgstr ""
msgid "Configure Security %{wordBreakOpportunity}and Compliance"
msgstr ""
msgid "Configure Tracing" msgid "Configure Tracing"
msgstr "" msgstr ""
...@@ -18928,9 +18925,6 @@ msgstr "" ...@@ -18928,9 +18925,6 @@ msgstr ""
msgid "Security Dashboard" msgid "Security Dashboard"
msgstr "" msgstr ""
msgid "Security configuration help link"
msgstr ""
msgid "Security dashboard" msgid "Security dashboard"
msgstr "" msgstr ""
...@@ -18943,21 +18937,21 @@ msgstr "" ...@@ -18943,21 +18937,21 @@ msgstr ""
msgid "SecurityConfiguration|Enabled" msgid "SecurityConfiguration|Enabled"
msgstr "" msgstr ""
msgid "SecurityConfiguration|Feature"
msgstr ""
msgid "SecurityConfiguration|Feature documentation for %{featureName}" msgid "SecurityConfiguration|Feature documentation for %{featureName}"
msgstr "" msgstr ""
msgid "SecurityConfiguration|Not yet enabled" msgid "SecurityConfiguration|Not yet enabled"
msgstr "" msgstr ""
msgid "SecurityConfiguration|Secure features" msgid "SecurityConfiguration|Security Control"
msgstr "" msgstr ""
msgid "SecurityConfiguration|Status" msgid "SecurityConfiguration|Status"
msgstr "" msgstr ""
msgid "SecurityConfiguration|Testing & Compliance"
msgstr ""
msgid "SecurityReports|%{firstProject} and %{secondProject}" msgid "SecurityReports|%{firstProject} and %{secondProject}"
msgstr "" msgstr ""
......
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