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>2022-12-02 06:07:57 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-02 06:07:57 +0300
commit0409a31740fa29fa1131c0496da16677cb3debe4 (patch)
tree4cbcd702a01145e8acd723f21b6bd52f3c6639d0 /app/assets/javascripts/invite_members
parentb26eec8cbcf32085079eee0e196456eccefc993f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/invite_members')
-rw-r--r--app/assets/javascripts/invite_members/components/invite_members_modal.vue134
-rw-r--r--app/assets/javascripts/invite_members/components/invite_modal_base.vue107
-rw-r--r--app/assets/javascripts/invite_members/components/members_token_select.vue17
-rw-r--r--app/assets/javascripts/invite_members/constants.js6
4 files changed, 138 insertions, 126 deletions
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 887dad7465b..4c5e4fb1021 100644
--- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
@@ -123,7 +123,7 @@ export default {
selectedAccessLevel: undefined,
errorsLimit: 2,
isErrorsSectionExpanded: false,
- emptyInvitesError: false,
+ shouldShowEmptyInvitesAlert: false,
};
},
computed: {
@@ -208,12 +208,15 @@ export default {
count: this.errorsExpanded.length,
});
},
+ formGroupDescription() {
+ return this.invalidFeedbackMessage ? null : this.$options.labels.placeHolder;
+ },
},
watch: {
isEmptyInvites: {
handler(updatedValue) {
// nothing to do if the invites are **still** empty and the emptyInvites were never set from submit
- if (!updatedValue && !this.emptyInvitesError) {
+ if (!updatedValue && !this.shouldShowEmptyInvitesAlert) {
return;
}
@@ -262,16 +265,17 @@ export default {
const tracking = new ExperimentTracking(experimentName);
tracking.event(eventName);
},
- showEmptyInvitesError() {
- this.invalidFeedbackMessage = this.$options.labels.emptyInvitesErrorText;
- this.emptyInvitesError = true;
+ showEmptyInvitesAlert() {
+ this.invalidFeedbackMessage = this.$options.labels.placeHolder;
+ this.shouldShowEmptyInvitesAlert = true;
+ this.$refs.alerts.focus();
},
sendInvite({ accessLevel, expiresAt }) {
this.isLoading = true;
this.clearValidation();
if (!this.isEmptyInvites) {
- this.showEmptyInvitesError();
+ this.showEmptyInvitesAlert();
return;
}
@@ -312,6 +316,7 @@ export default {
},
showMemberErrors(message) {
this.invalidMembers = message;
+ this.$refs.alerts.focus();
},
tokenName(username) {
// initial token creation hits this and nothing is found... so safe navigation
@@ -326,6 +331,7 @@ export default {
resetFields() {
this.clearValidation();
this.isLoading = false;
+ this.shouldShowEmptyInvitesAlert = false;
this.newUsersToInvite = [];
this.selectedTasksToBeDone = [];
[this.selectedTaskProject] = this.projects;
@@ -351,7 +357,7 @@ export default {
},
clearEmptyInviteError() {
this.invalidFeedbackMessage = '';
- this.emptyInvitesError = false;
+ this.shouldShowEmptyInvitesAlert = false;
},
removeToken(token) {
delete this.invalidMembers[memberName(token)];
@@ -374,7 +380,7 @@ export default {
:help-link="helpLink"
:label-intro-text="labelIntroText"
:label-search-field="$options.labels.searchField"
- :form-group-description="$options.labels.placeHolder"
+ :form-group-description="formGroupDescription"
:invalid-feedback-message="invalidFeedbackMessage"
:is-loading="isLoading"
:new-users-to-invite="newUsersToInvite"
@@ -395,59 +401,77 @@ export default {
</template>
<template #alert>
- <gl-alert
- v-if="hasInvalidMembers"
- variant="danger"
- :dismissible="false"
- :title="memberErrorTitle"
- data-testid="alert-member-error"
- >
- {{ $options.labels.memberErrorListText }}
- <ul class="gl-pl-5 gl-mb-0">
- <li v-for="error in errorsLimited" :key="error.member" data-testid="errors-limited-item">
- <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
- </li>
- </ul>
- <template v-if="shouldErrorsSectionExpand">
- <gl-collapse v-model="isErrorsSectionExpanded">
- <ul class="gl-pl-5 gl-mb-0">
- <li
- v-for="error in errorsExpanded"
- :key="error.member"
- data-testid="errors-expanded-item"
- >
- <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
- </li>
- </ul>
- </gl-collapse>
- <gl-button
- class="gl-text-decoration-none! gl-shadow-none! gl-mt-3"
- data-testid="accordion-button"
- variant="link"
- @click="toggleErrorExpansion"
- >
- {{ errorCollapseText }}
- <gl-icon
- name="chevron-down"
- class="gl-transition-medium"
- :class="{ 'gl-rotate-180': isErrorsSectionExpanded }"
- />
- </gl-button>
- </template>
- </gl-alert>
- <user-limit-notification
- v-else-if="showUserLimitNotification"
- :limit-variant="limitVariant"
- :users-limit-dataset="usersLimitDataset"
- />
+ <div ref="alerts" tabindex="-1">
+ <gl-alert
+ v-if="shouldShowEmptyInvitesAlert"
+ id="empty-invites-alert"
+ class="gl-mb-4"
+ variant="danger"
+ :dismissible="false"
+ data-testid="empty-invites-alert"
+ >
+ {{ $options.labels.emptyInvitesAlertText }}
+ </gl-alert>
+ <gl-alert
+ v-if="hasInvalidMembers"
+ class="gl-mb-4"
+ variant="danger"
+ :dismissible="false"
+ :title="memberErrorTitle"
+ data-testid="alert-member-error"
+ >
+ {{ $options.labels.memberErrorListText }}
+ <ul class="gl-pl-5 gl-mb-0">
+ <li
+ v-for="error in errorsLimited"
+ :key="error.member"
+ data-testid="errors-limited-item"
+ >
+ <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
+ </li>
+ </ul>
+ <template v-if="shouldErrorsSectionExpand">
+ <gl-collapse v-model="isErrorsSectionExpanded">
+ <ul class="gl-pl-5 gl-mb-0">
+ <li
+ v-for="error in errorsExpanded"
+ :key="error.member"
+ data-testid="errors-expanded-item"
+ >
+ <strong>{{ error.displayedMemberName }}:</strong> {{ error.message }}
+ </li>
+ </ul>
+ </gl-collapse>
+ <gl-button
+ class="gl-text-decoration-none! gl-shadow-none! gl-mt-3"
+ data-testid="accordion-button"
+ variant="link"
+ @click="toggleErrorExpansion"
+ >
+ {{ errorCollapseText }}
+ <gl-icon
+ name="chevron-down"
+ class="gl-transition-medium"
+ :class="{ 'gl-rotate-180': isErrorsSectionExpanded }"
+ />
+ </gl-button>
+ </template>
+ </gl-alert>
+ <user-limit-notification
+ v-else-if="showUserLimitNotification"
+ :limit-variant="limitVariant"
+ :users-limit-dataset="usersLimitDataset"
+ />
+ </div>
</template>
- <template #select="{ exceptionState, labelId }">
+ <template #select="{ exceptionState, inputId }">
<members-token-select
v-model="newUsersToInvite"
class="gl-mb-2"
+ aria-labelledby="empty-invites-alert"
+ :input-id="inputId"
:exception-state="exceptionState"
- :aria-labelledby="labelId"
:users-filter="usersFilter"
:filter-id="filterId"
:invalid-members="invalidMembers"
diff --git a/app/assets/javascripts/invite_members/components/invite_modal_base.vue b/app/assets/javascripts/invite_members/components/invite_modal_base.vue
index e3511a49fc5..2cbd681c67d 100644
--- a/app/assets/javascripts/invite_members/components/invite_modal_base.vue
+++ b/app/assets/javascripts/invite_members/components/invite_modal_base.vue
@@ -1,14 +1,5 @@
<script>
-import {
- GlFormGroup,
- GlModal,
- GlDropdown,
- GlDropdownItem,
- GlDatepicker,
- GlLink,
- GlSprintf,
- GlFormInput,
-} from '@gitlab/ui';
+import { GlFormGroup, GlFormSelect, GlModal, GlDatepicker, GlLink, GlSprintf } from '@gitlab/ui';
import Tracking from '~/tracking';
import { sprintf } from '~/locale';
import ContentTransition from '~/vue_shared/components/content_transition.vue';
@@ -37,13 +28,11 @@ const DEFAULT_SLOTS = [
export default {
components: {
GlFormGroup,
+ GlFormSelect,
GlDatepicker,
GlLink,
GlModal,
- GlDropdown,
- GlDropdownItem,
GlSprintf,
- GlFormInput,
ContentTransition,
},
mixins: [Tracking.mixin()],
@@ -141,14 +130,23 @@ export default {
};
},
computed: {
+ accessLevelsOptions() {
+ return Object.entries(this.accessLevels).map(([text, value]) => ({ text, value }));
+ },
introText() {
return sprintf(this.labelIntroText, { name: this.name });
},
exceptionState() {
return this.invalidFeedbackMessage ? false : null;
},
- selectLabelId() {
- return `${this.modalId}_select`;
+ selectId() {
+ return `${this.modalId}_search`;
+ },
+ dropdownId() {
+ return `${this.modalId}_dropdown`;
+ },
+ datepickerId() {
+ return `${this.modalId}_expires_at`;
},
selectedRoleName() {
return Object.keys(this.accessLevels).find(
@@ -218,9 +216,6 @@ export default {
this.$emit('cancel');
},
- changeSelectedItem(item) {
- this.selectedAccessLevel = item;
- },
onSubmit(e) {
// We never want to hide when submitting
e.preventDefault();
@@ -279,64 +274,50 @@ export default {
<slot name="alert"></slot>
<gl-form-group
+ :label="labelSearchField"
+ :label-for="selectId"
:invalid-feedback="invalidFeedbackMessage"
:state="exceptionState"
:description="formGroupDescription"
data-testid="members-form-group"
>
- <label :id="selectLabelId" class="col-form-label">{{ labelSearchField }}</label>
- <slot name="select" v-bind="{ exceptionState, labelId: selectLabelId }"></slot>
+ <slot name="select" v-bind="{ exceptionState, inputId: selectId }"></slot>
</gl-form-group>
- <label class="gl-font-weight-bold">{{ $options.ACCESS_LEVEL }}</label>
- <div class="gl-mt-2 gl-w-half gl-xs-w-full">
- <gl-dropdown
- class="gl-shadow-none gl-w-full"
+ <gl-form-group
+ class="gl-w-half gl-xs-w-full"
+ :label="$options.ACCESS_LEVEL"
+ :label-for="dropdownId"
+ >
+ <template #description>
+ <gl-sprintf :message="$options.READ_MORE_TEXT">
+ <template #link="{ content }">
+ <gl-link :href="helpLink" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ <gl-form-select
+ :id="dropdownId"
+ v-model="selectedAccessLevel"
data-qa-selector="access_level_dropdown"
- v-bind="$attrs"
- :text="selectedRoleName"
- >
- <template v-for="(key, item) in accessLevels">
- <gl-dropdown-item
- :key="key"
- active-class="is-active"
- is-check-item
- :is-checked="key === selectedAccessLevel"
- @click="changeSelectedItem(key)"
- >
- <div>{{ item }}</div>
- </gl-dropdown-item>
- </template>
- </gl-dropdown>
- </div>
-
- <div class="gl-mt-2 gl-w-half gl-xs-w-full">
- <gl-sprintf :message="$options.READ_MORE_TEXT">
- <template #link="{ content }">
- <gl-link :href="helpLink" target="_blank">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </div>
+ :options="accessLevelsOptions"
+ />
+ </gl-form-group>
- <label class="gl-mt-5 gl-display-block" for="expires_at">{{
- $options.ACCESS_EXPIRE_DATE
- }}</label>
- <div class="gl-mt-2 gl-w-half gl-xs-w-full gl-display-inline-block">
+ <gl-form-group
+ class="gl-w-half gl-xs-w-full"
+ :label="$options.ACCESS_EXPIRE_DATE"
+ :label-for="datepickerId"
+ >
<gl-datepicker
v-model="selectedDate"
- class="gl-display-inline!"
+ :input-id="datepickerId"
+ class="gl-display-block!"
:min-date="minDate"
:target="null"
- >
- <template #default="{ formattedDate }">
- <gl-form-input
- class="gl-w-full"
- :value="formattedDate"
- :placeholder="__(`YYYY-MM-DD`)"
- />
- </template>
- </gl-datepicker>
- </div>
+ />
+ </gl-form-group>
+
<slot name="form-after"></slot>
</template>
diff --git a/app/assets/javascripts/invite_members/components/members_token_select.vue b/app/assets/javascripts/invite_members/components/members_token_select.vue
index 2ddb04e1eeb..68602068699 100644
--- a/app/assets/javascripts/invite_members/components/members_token_select.vue
+++ b/app/assets/javascripts/invite_members/components/members_token_select.vue
@@ -49,6 +49,11 @@ export default {
type: Object,
required: true,
},
+ inputId: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
return {
@@ -84,6 +89,13 @@ export default {
hasInvalidMembers() {
return !isEmpty(this.invalidMembers);
},
+ textInputAttrs() {
+ return {
+ 'data-testid': 'members-token-select-input',
+ 'data-qa-selector': 'members_token_select_input',
+ id: this.inputId,
+ };
+ },
},
watch: {
// We might not really want this to be *reactive* since we want the "class" state to be
@@ -183,10 +195,7 @@ export default {
:hide-dropdown-with-no-items="hideDropdownWithNoItems"
:placeholder="placeholderText"
:aria-labelledby="ariaLabelledby"
- :text-input-attrs="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
- 'data-testid': 'members-token-select-input',
- 'data-qa-selector': 'members_token_select_input',
- } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
+ :text-input-attrs="textInputAttrs"
@blur="handleBlur"
@text-input="handleTextInput"
@input="handleInput"
diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js
index 29889f47fe1..3b522f9a7d9 100644
--- a/app/assets/javascripts/invite_members/constants.js
+++ b/app/assets/javascripts/invite_members/constants.js
@@ -81,9 +81,7 @@ export const MEMBER_ERROR_LIST_TEXT = s__(
);
export const COLLAPSED_ERRORS = s__('InviteMembersModal|Show more (%{count})');
export const EXPANDED_ERRORS = s__('InviteMembersModal|Show less');
-export const EMPTY_INVITES_ERROR_TEXT = s__(
- 'InviteMembersModal|Please select members or type email addresses to invite',
-);
+export const EMPTY_INVITES_ALERT_TEXT = s__('InviteMembersModal|Please add members to invite');
export const MEMBER_MODAL_LABELS = {
modal: {
@@ -121,7 +119,7 @@ export const MEMBER_MODAL_LABELS = {
memberErrorListText: MEMBER_ERROR_LIST_TEXT,
collapsedErrors: COLLAPSED_ERRORS,
expandedErrors: EXPANDED_ERRORS,
- emptyInvitesErrorText: EMPTY_INVITES_ERROR_TEXT,
+ emptyInvitesAlertText: EMPTY_INVITES_ALERT_TEXT,
};
export const GROUP_MODAL_LABELS = {