Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-12-03 03:11:20 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-03 03:11:20 +0300
commit498ba9dc41fcf2b4be30a8f3721543953efb3c3b (patch)
treeed33fbf37c0c2ae3a71042455f9b51800907a984 /app/assets/javascripts
parent515f39456fce82eb2ab811fa366167ad084a3b12 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/header_search/components/app.vue80
-rw-r--r--app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue5
-rw-r--r--app/assets/javascripts/header_search/components/header_search_default_items.vue5
-rw-r--r--app/assets/javascripts/header_search/components/header_search_scoped_items.vue19
-rw-r--r--app/assets/javascripts/header_search/constants.js22
-rw-r--r--app/assets/javascripts/invite_members/components/invite_members_modal.vue37
-rw-r--r--app/assets/javascripts/invite_members/constants.js9
-rw-r--r--app/assets/javascripts/invite_members/init_invite_members_modal.js2
-rw-r--r--app/assets/javascripts/security_configuration/components/app.vue28
-rw-r--r--app/assets/javascripts/security_configuration/components/training_provider_list.vue36
10 files changed, 174 insertions, 69 deletions
diff --git a/app/assets/javascripts/header_search/components/app.vue b/app/assets/javascripts/header_search/components/app.vue
index a575b80facc..67e3998bc97 100644
--- a/app/assets/javascripts/header_search/components/app.vue
+++ b/app/assets/javascripts/header_search/components/app.vue
@@ -2,9 +2,14 @@
import { GlSearchBoxByType, GlOutsideDirective as Outside } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex';
import { visitUrl } from '~/lib/utils/url_utility';
-import { __ } from '~/locale';
+import { s__, sprintf } from '~/locale';
import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue';
-import { FIRST_DROPDOWN_INDEX, SEARCH_BOX_INDEX } from '../constants';
+import {
+ FIRST_DROPDOWN_INDEX,
+ SEARCH_BOX_INDEX,
+ SEARCH_INPUT_DESCRIPTION,
+ SEARCH_RESULTS_DESCRIPTION,
+} from '../constants';
import HeaderSearchAutocompleteItems from './header_search_autocomplete_items.vue';
import HeaderSearchDefaultItems from './header_search_default_items.vue';
import HeaderSearchScopedItems from './header_search_scoped_items.vue';
@@ -12,7 +17,21 @@ import HeaderSearchScopedItems from './header_search_scoped_items.vue';
export default {
name: 'HeaderSearchApp',
i18n: {
- searchPlaceholder: __('Search or jump to...'),
+ searchPlaceholder: s__('GlobalSearch|Search or jump to...'),
+ searchAria: s__('GlobalSearch|Search GitLab'),
+ searchInputDescribeByNoDropdown: s__(
+ 'GlobalSearch|Type and press the enter key to submit search.',
+ ),
+ searchInputDescribeByWithDropdown: s__(
+ 'GlobalSearch|Type for new suggestions to appear below.',
+ ),
+ searchDescribedByDefault: s__(
+ 'GlobalSearch|%{count} default results provided. Use the up and down arrow keys to navigate search results list.',
+ ),
+ searchDescribedByUpdated: s__(
+ 'GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit.',
+ ),
+ searchResultsLoading: s__('GlobalSearch|Search results are loading'),
},
directives: { Outside },
components: {
@@ -29,7 +48,7 @@ export default {
};
},
computed: {
- ...mapState(['search']),
+ ...mapState(['search', 'loading']),
...mapGetters(['searchQuery', 'searchOptions']),
searchText: {
get() {
@@ -42,6 +61,9 @@ export default {
currentFocusedOption() {
return this.searchOptions[this.currentFocusIndex];
},
+ currentFocusedId() {
+ return this.currentFocusedOption?.html_id;
+ },
isLoggedIn() {
return gon?.current_username;
},
@@ -58,6 +80,30 @@ export default {
return FIRST_DROPDOWN_INDEX;
},
+ searchInputDescribeBy() {
+ if (this.isLoggedIn) {
+ return this.$options.i18n.searchInputDescribeByWithDropdown;
+ }
+
+ return this.$options.i18n.searchInputDescribeByNoDropdown;
+ },
+ dropdownResultsDescription() {
+ if (!this.showSearchDropdown) {
+ return ''; // This allows aria-live to see register an update when the dropdown is shown
+ }
+
+ if (this.showDefaultItems) {
+ return sprintf(this.$options.i18n.searchDescribedByDefault, {
+ count: this.searchOptions.length,
+ });
+ }
+
+ return this.loading
+ ? this.$options.i18n.searchResultsLoading
+ : sprintf(this.$options.i18n.searchDescribedByUpdated, {
+ count: this.searchOptions.length,
+ });
+ },
},
methods: {
...mapActions(['setSearch', 'fetchAutocompleteOptions']),
@@ -79,22 +125,44 @@ export default {
},
},
SEARCH_BOX_INDEX,
+ SEARCH_INPUT_DESCRIPTION,
+ SEARCH_RESULTS_DESCRIPTION,
};
</script>
<template>
- <section v-outside="closeDropdown" class="header-search gl-relative">
+ <form
+ v-outside="closeDropdown"
+ role="search"
+ :aria-label="$options.i18n.searchAria"
+ class="header-search gl-relative"
+ >
<gl-search-box-by-type
v-model="searchText"
+ role="searchbox"
class="gl-z-index-1"
:debounce="500"
autocomplete="off"
:placeholder="$options.i18n.searchPlaceholder"
+ :aria-activedescendant="currentFocusedId"
+ :aria-describedby="$options.SEARCH_INPUT_DESCRIPTION"
@focus="openDropdown"
@click="openDropdown"
@input="getAutocompleteOptions"
@keydown.enter.stop.prevent="submitSearch"
/>
+ <span :id="$options.SEARCH_INPUT_DESCRIPTION" role="region" class="gl-sr-only">{{
+ searchInputDescribeBy
+ }}</span>
+ <span
+ role="region"
+ :data-testid="$options.SEARCH_RESULTS_DESCRIPTION"
+ class="gl-sr-only"
+ aria-live="polite"
+ aria-atomic="true"
+ >
+ {{ dropdownResultsDescription }}
+ </span>
<div
v-if="showSearchDropdown"
data-testid="header-search-dropdown-menu"
@@ -118,5 +186,5 @@ export default {
</template>
</div>
</div>
- </section>
+ </form>
</template>
diff --git a/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue b/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue
index cf1f7c030e2..9f4f4768247 100644
--- a/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue
+++ b/app/assets/javascripts/header_search/components/header_search_autocomplete_items.vue
@@ -69,13 +69,16 @@ export default {
<gl-dropdown-section-header>{{ option.category }}</gl-dropdown-section-header>
<gl-dropdown-item
v-for="data in option.data"
+ :id="data.html_id"
:ref="data.html_id"
:key="data.html_id"
:class="{ 'gl-bg-gray-50': isOptionFocused(data) }"
+ :aria-selected="isOptionFocused(data)"
+ :aria-label="data.label"
tabindex="-1"
:href="data.url"
>
- <div class="gl-display-flex gl-align-items-center">
+ <div class="gl-display-flex gl-align-items-center" aria-hidden="true">
<gl-avatar
v-if="data.avatar_url !== undefined"
:src="data.avatar_url"
diff --git a/app/assets/javascripts/header_search/components/header_search_default_items.vue b/app/assets/javascripts/header_search/components/header_search_default_items.vue
index 2228bc0c2a2..53e63bc6cca 100644
--- a/app/assets/javascripts/header_search/components/header_search_default_items.vue
+++ b/app/assets/javascripts/header_search/components/header_search_default_items.vue
@@ -43,13 +43,16 @@ export default {
<gl-dropdown-section-header>{{ sectionHeader }}</gl-dropdown-section-header>
<gl-dropdown-item
v-for="option in defaultSearchOptions"
+ :id="option.html_id"
:ref="option.html_id"
:key="option.html_id"
:class="{ 'gl-bg-gray-50': isOptionFocused(option) }"
+ :aria-selected="isOptionFocused(option)"
+ :aria-label="option.title"
tabindex="-1"
:href="option.url"
>
- {{ option.title }}
+ <span aria-hidden="true">{{ option.title }}</span>
</gl-dropdown-item>
</div>
</template>
diff --git a/app/assets/javascripts/header_search/components/header_search_scoped_items.vue b/app/assets/javascripts/header_search/components/header_search_scoped_items.vue
index d3def929752..3aebee71509 100644
--- a/app/assets/javascripts/header_search/components/header_search_scoped_items.vue
+++ b/app/assets/javascripts/header_search/components/header_search_scoped_items.vue
@@ -1,6 +1,7 @@
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { mapState, mapGetters } from 'vuex';
+import { __, sprintf } from '~/locale';
export default {
name: 'HeaderSearchScopedItems',
@@ -22,6 +23,13 @@ export default {
isOptionFocused(option) {
return this.currentFocusedOption?.html_id === option.html_id;
},
+ ariaLabel(option) {
+ return sprintf(__('%{search} %{description} %{scope}'), {
+ search: this.search,
+ description: option.description,
+ scope: option.scope || '',
+ });
+ },
},
};
</script>
@@ -30,15 +38,20 @@ export default {
<div>
<gl-dropdown-item
v-for="option in scopedSearchOptions"
+ :id="option.html_id"
:ref="option.html_id"
:key="option.html_id"
:class="{ 'gl-bg-gray-50': isOptionFocused(option) }"
+ :aria-selected="isOptionFocused(option)"
+ :aria-label="ariaLabel(option)"
tabindex="-1"
:href="option.url"
>
- "<span class="gl-font-weight-bold">{{ search }}</span
- >" {{ option.description }}
- <span v-if="option.scope" class="gl-font-style-italic">{{ option.scope }}</span>
+ <span aria-hidden="true">
+ "<span class="gl-font-weight-bold">{{ search }}</span
+ >" {{ option.description }}
+ <span v-if="option.scope" class="gl-font-style-italic">{{ option.scope }}</span>
+ </span>
</gl-dropdown-item>
</div>
</template>
diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js
index 34777697863..b2e45fcd648 100644
--- a/app/assets/javascripts/header_search/constants.js
+++ b/app/assets/javascripts/header_search/constants.js
@@ -1,20 +1,20 @@
-import { __ } from '~/locale';
+import { s__ } from '~/locale';
-export const MSG_ISSUES_ASSIGNED_TO_ME = __('Issues assigned to me');
+export const MSG_ISSUES_ASSIGNED_TO_ME = s__('GlobalSearch|Issues assigned to me');
-export const MSG_ISSUES_IVE_CREATED = __("Issues I've created");
+export const MSG_ISSUES_IVE_CREATED = s__("GlobalSearch|Issues I've created");
-export const MSG_MR_ASSIGNED_TO_ME = __('Merge requests assigned to me');
+export const MSG_MR_ASSIGNED_TO_ME = s__('GlobalSearch|Merge requests assigned to me');
-export const MSG_MR_IM_REVIEWER = __("Merge requests that I'm a reviewer");
+export const MSG_MR_IM_REVIEWER = s__("GlobalSearch|Merge requests that I'm a reviewer");
-export const MSG_MR_IVE_CREATED = __("Merge requests I've created");
+export const MSG_MR_IVE_CREATED = s__("GlobalSearch|Merge requests I've created");
-export const MSG_IN_ALL_GITLAB = __('in all GitLab');
+export const MSG_IN_ALL_GITLAB = s__('GlobalSearch|in all GitLab');
-export const MSG_IN_GROUP = __('in group');
+export const MSG_IN_GROUP = s__('GlobalSearch|in group');
-export const MSG_IN_PROJECT = __('in project');
+export const MSG_IN_PROJECT = s__('GlobalSearch|in project');
export const GROUPS_CATEGORY = 'Groups';
@@ -27,3 +27,7 @@ export const SMALL_AVATAR_PX = 16;
export const FIRST_DROPDOWN_INDEX = 0;
export const SEARCH_BOX_INDEX = -1;
+
+export const SEARCH_INPUT_DESCRIPTION = 'search-input-description';
+
+export const SEARCH_RESULTS_DESCRIPTION = 'search-results-description';
diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
index a6e747c6b48..7163d1be773 100644
--- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
@@ -23,7 +23,6 @@ import {
INVITE_MEMBERS_IN_COMMENT,
GROUP_FILTERS,
USERS_FILTER_ALL,
- MEMBER_AREAS_OF_FOCUS,
INVITE_MEMBERS_FOR_TASK,
MODAL_LABELS,
LEARN_GITLAB,
@@ -101,14 +100,6 @@ export default {
type: String,
required: true,
},
- areasOfFocusOptions: {
- type: Array,
- required: true,
- },
- noSelectionAreasOfFocus: {
- type: Array,
- required: true,
- },
tasksToBeDoneOptions: {
type: Array,
required: true,
@@ -126,7 +117,6 @@ export default {
inviteeType: 'members',
newUsersToInvite: [],
selectedDate: undefined,
- selectedAreasOfFocus: [],
selectedTasksToBeDone: [],
selectedTaskProject: this.projects[0],
groupToBeSharedWith: {},
@@ -182,16 +172,6 @@ export default {
this.newUsersToInvite.length === 0 && Object.keys(this.groupToBeSharedWith).length === 0
);
},
- areasOfFocusEnabled() {
- return !this.tasksToBeDoneEnabled && this.areasOfFocusOptions.length !== 0;
- },
- areasOfFocusForPost() {
- if (this.selectedAreasOfFocus.length === 0 && this.areasOfFocusEnabled) {
- return this.noSelectionAreasOfFocus;
- }
-
- return this.selectedAreasOfFocus;
- },
errorFieldDescription() {
if (this.inviteeType === 'group') {
return '';
@@ -232,8 +212,6 @@ export default {
this.openModal(options);
if (this.isOnLearnGitlab) {
this.trackEvent(INVITE_MEMBERS_FOR_TASK.name, this.source);
- } else {
- this.trackEvent(MEMBER_AREAS_OF_FOCUS.name, MEMBER_AREAS_OF_FOCUS.view);
}
});
@@ -280,8 +258,6 @@ export default {
if (this.source === INVITE_MEMBERS_IN_COMMENT) {
this.trackEvent(INVITE_MEMBERS_IN_COMMENT, 'comment_invite_success');
}
-
- this.trackEvent(MEMBER_AREAS_OF_FOCUS.name, MEMBER_AREAS_OF_FOCUS.submit);
},
trackinviteMembersForTask() {
const label = 'selected_tasks_to_be_done';
@@ -296,7 +272,6 @@ export default {
this.newUsersToInvite = [];
this.groupToBeSharedWith = {};
this.invalidFeedbackMessage = '';
- this.selectedAreasOfFocus = [];
this.selectedTasksToBeDone = [];
[this.selectedTaskProject] = this.projects;
},
@@ -350,7 +325,6 @@ export default {
email: usersToInviteByEmail,
access_level: this.selectedAccessLevel,
invite_source: this.source,
- areas_of_focus: this.areasOfFocusForPost,
tasks_to_be_done: this.tasksToBeDoneForPost,
tasks_project_id: this.tasksProjectForPost,
};
@@ -361,7 +335,6 @@ export default {
user_id: usersToAddById,
access_level: this.selectedAccessLevel,
invite_source: this.source,
- areas_of_focus: this.areasOfFocusForPost,
tasks_to_be_done: this.tasksToBeDoneForPost,
tasks_project_id: this.tasksProjectForPost,
};
@@ -517,16 +490,6 @@ export default {
</template>
</gl-datepicker>
</div>
- <div v-if="areasOfFocusEnabled">
- <label class="gl-mt-5">
- {{ $options.labels.areasOfFocusLabel }}
- </label>
- <gl-form-checkbox-group
- v-model="selectedAreasOfFocus"
- :options="areasOfFocusOptions"
- data-testid="area-of-focus-checks"
- />
- </div>
<div v-if="showTasksToBeDone" data-testid="invite-members-modal-tasks-to-be-done">
<label class="gl-mt-5">
{{ $options.labels.members.tasksToBeDone.title }}
diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js
index 2a4e7041ed1..87d2fbc6aac 100644
--- a/app/assets/javascripts/invite_members/constants.js
+++ b/app/assets/javascripts/invite_members/constants.js
@@ -3,11 +3,6 @@ import { __, s__ } from '~/locale';
export const SEARCH_DELAY = 200;
export const INVITE_MEMBERS_IN_COMMENT = 'invite_members_in_comment';
-export const MEMBER_AREAS_OF_FOCUS = {
- name: 'member_areas_of_focus',
- view: 'view',
- submit: 'submit',
-};
export const INVITE_MEMBERS_FOR_TASK = {
minimum_access_level: 30,
name: 'invite_members_for_task',
@@ -77,9 +72,6 @@ export const READ_MORE_TEXT = s__(
export const INVITE_BUTTON_TEXT = s__('InviteMembersModal|Invite');
export const CANCEL_BUTTON_TEXT = s__('InviteMembersModal|Cancel');
export const HEADER_CLOSE_LABEL = s__('InviteMembersModal|Close invite team members');
-export const AREAS_OF_FOCUS_LABEL = s__(
- 'InviteMembersModal|What would you like new member(s) to focus on? (optional)',
-);
export const MODAL_LABELS = {
members: {
@@ -142,7 +134,6 @@ export const MODAL_LABELS = {
inviteButtonText: INVITE_BUTTON_TEXT,
cancelButtonText: CANCEL_BUTTON_TEXT,
headerCloseLabel: HEADER_CLOSE_LABEL,
- areasOfFocusLabel: AREAS_OF_FOCUS_LABEL,
};
export const LEARN_GITLAB = 'learn_gitlab';
diff --git a/app/assets/javascripts/invite_members/init_invite_members_modal.js b/app/assets/javascripts/invite_members/init_invite_members_modal.js
index fc657a064dd..2cc056f2ddb 100644
--- a/app/assets/javascripts/invite_members/init_invite_members_modal.js
+++ b/app/assets/javascripts/invite_members/init_invite_members_modal.js
@@ -40,10 +40,8 @@ export default function initInviteMembersModal() {
defaultAccessLevel: parseInt(el.dataset.defaultAccessLevel, 10),
groupSelectFilter: el.dataset.groupsFilter,
groupSelectParentId: parseInt(el.dataset.parentId, 10),
- areasOfFocusOptions: JSON.parse(el.dataset.areasOfFocusOptions),
tasksToBeDoneOptions: JSON.parse(el.dataset.tasksToBeDoneOptions || '[]'),
projects: JSON.parse(el.dataset.projects || '[]'),
- noSelectionAreasOfFocus: JSON.parse(el.dataset.noSelectionAreasOfFocus),
usersFilter: el.dataset.usersFilter,
filterId: parseInt(el.dataset.filterId, 10),
},
diff --git a/app/assets/javascripts/security_configuration/components/app.vue b/app/assets/javascripts/security_configuration/components/app.vue
index cd2add6407f..62c30c941eb 100644
--- a/app/assets/javascripts/security_configuration/components/app.vue
+++ b/app/assets/javascripts/security_configuration/components/app.vue
@@ -8,6 +8,7 @@ import AutoDevOpsAlert from './auto_dev_ops_alert.vue';
import AutoDevOpsEnabledAlert from './auto_dev_ops_enabled_alert.vue';
import { AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY } from './constants';
import FeatureCard from './feature_card.vue';
+import TrainingProviderList from './training_provider_list.vue';
import SectionLayout from './section_layout.vue';
import UpgradeBanner from './upgrade_banner.vue';
@@ -28,8 +29,28 @@ export const i18n = {
securityTraining: s__('SecurityConfiguration|Security training'),
};
+// This will be removed and replaced with GraphQL query:
+// https://gitlab.com/gitlab-org/gitlab/-/issues/346480
+export const TRAINING_PROVIDERS = [
+ {
+ id: 101,
+ name: __('Kontra'),
+ description: __('Interactive developer security education.'),
+ url: 'https://application.security/',
+ isEnabled: false,
+ },
+ {
+ id: 102,
+ name: __('SecureCodeWarrior'),
+ description: __('Security training with guide and learning pathways.'),
+ url: 'https://www.securecodewarrior.com/',
+ isEnabled: true,
+ },
+];
+
export default {
i18n,
+ TRAINING_PROVIDERS,
components: {
AutoDevOpsAlert,
AutoDevOpsEnabledAlert,
@@ -43,6 +64,7 @@ export default {
SectionLayout,
UpgradeBanner,
UserCalloutDismisser,
+ TrainingProviderList,
},
mixins: [glFeatureFlagsMixin()],
inject: ['projectPath'],
@@ -240,7 +262,11 @@ export default {
data-testid="vulnerability-management-tab"
:title="$options.i18n.vulnerabilityManagement"
>
- <section-layout :heading="$options.i18n.securityTraining" />
+ <section-layout :heading="$options.i18n.securityTraining">
+ <template #features>
+ <training-provider-list :providers="$options.TRAINING_PROVIDERS" />
+ </template>
+ </section-layout>
</gl-tab>
</gl-tabs>
</article>
diff --git a/app/assets/javascripts/security_configuration/components/training_provider_list.vue b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
new file mode 100644
index 00000000000..160540f6989
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
@@ -0,0 +1,36 @@
+<script>
+import { GlCard, GlToggle, GlLink } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlCard,
+ GlToggle,
+ GlLink,
+ },
+ props: {
+ providers: {
+ type: Array,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <ul class="gl-list-style-none gl-m-0 gl-p-0">
+ <li v-for="{ id, isEnabled, name, description, url } in providers" :key="id" class="gl-mb-6">
+ <gl-card>
+ <div class="gl-display-flex">
+ <gl-toggle :value="isEnabled" :label="__('Training mode')" label-position="hidden" />
+ <div class="gl-ml-5">
+ <h3 class="gl-font-lg gl-m-0 gl-mb-2">{{ name }}</h3>
+ <p>
+ {{ description }}
+ <gl-link :href="url" target="_blank">{{ __('Learn more.') }}</gl-link>
+ </p>
+ </div>
+ </div>
+ </gl-card>
+ </li>
+ </ul>
+</template>