diff options
Diffstat (limited to 'app/assets/javascripts/invite_members/components/invite_modal_base.vue')
-rw-r--r-- | app/assets/javascripts/invite_members/components/invite_modal_base.vue | 235 |
1 files changed, 130 insertions, 105 deletions
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 fc00f5b9343..bafbe94b8bd 100644 --- a/app/assets/javascripts/invite_members/components/invite_modal_base.vue +++ b/app/assets/javascripts/invite_members/components/invite_modal_base.vue @@ -10,19 +10,27 @@ import { GlButton, GlFormInput, } from '@gitlab/ui'; -import { unescape } from 'lodash'; -import { sanitize } from '~/lib/dompurify'; import { sprintf } from '~/locale'; +import ContentTransition from '~/vue_shared/components/content_transition.vue'; import { ACCESS_LEVEL, ACCESS_EXPIRE_DATE, - INVALID_FEEDBACK_MESSAGE_DEFAULT, READ_MORE_TEXT, INVITE_BUTTON_TEXT, CANCEL_BUTTON_TEXT, HEADER_CLOSE_LABEL, } from '../constants'; -import { responseMessageFromError } from '../utils/response_message_parser'; + +const DEFAULT_SLOT = 'default'; +const DEFAULT_SLOTS = [ + { + key: DEFAULT_SLOT, + attributes: { + class: 'invite-modal-content', + 'data-testid': 'invite-modal-initial-content', + }, + }, +]; export default { components: { @@ -35,6 +43,7 @@ export default { GlSprintf, GlButton, GlFormInput, + ContentTransition, }, inheritAttrs: false, props: { @@ -80,14 +89,37 @@ export default { required: false, default: false, }, + isLoading: { + type: Boolean, + required: false, + default: false, + }, + invalidFeedbackMessage: { + type: String, + required: false, + default: '', + }, + submitButtonText: { + type: String, + required: false, + default: INVITE_BUTTON_TEXT, + }, + currentSlot: { + type: String, + required: false, + default: DEFAULT_SLOT, + }, + extraSlots: { + type: Array, + required: false, + default: () => [], + }, }, data() { // Be sure to check out reset! return { - invalidFeedbackMessage: '', selectedAccessLevel: this.defaultAccessLevel, selectedDate: undefined, - isLoading: false, minDate: new Date(), }; }, @@ -106,6 +138,9 @@ export default { (key) => this.accessLevels[key] === Number(this.selectedAccessLevel), ); }, + contentSlots() { + return [...DEFAULT_SLOTS, ...(this.extraSlots || [])]; + }, }, watch: { selectedAccessLevel: { @@ -116,16 +151,9 @@ export default { }, }, methods: { - showInvalidFeedbackMessage(response) { - const message = this.unescapeMsg(responseMessageFromError(response)); - - this.invalidFeedbackMessage = message || INVALID_FEEDBACK_MESSAGE_DEFAULT; - }, reset() { // This component isn't necessarily disposed, // so we might need to reset it's state. - this.isLoading = false; - this.invalidFeedbackMessage = ''; this.selectedAccessLevel = this.defaultAccessLevel; this.selectedDate = undefined; @@ -135,33 +163,15 @@ export default { this.reset(); this.$refs.modal.hide(); }, - clearValidation() { - this.invalidFeedbackMessage = ''; - }, changeSelectedItem(item) { this.selectedAccessLevel = item; }, submit() { - this.isLoading = true; - this.invalidFeedbackMessage = ''; - this.$emit('submit', { - onSuccess: () => { - this.isLoading = false; - }, - onError: (...args) => { - this.isLoading = false; - this.showInvalidFeedbackMessage(...args); - }, - data: { - accessLevel: this.selectedAccessLevel, - expiresAt: this.selectedDate, - }, + accessLevel: this.selectedAccessLevel, + expiresAt: this.selectedDate, }); }, - unescapeMsg(message) { - return unescape(sanitize(message, { ALLOWED_TAGS: [] })); - }, }, HEADER_CLOSE_LABEL, ACCESS_EXPIRE_DATE, @@ -169,6 +179,7 @@ export default { READ_MORE_TEXT, INVITE_BUTTON_TEXT, CANCEL_BUTTON_TEXT, + DEFAULT_SLOT, }; </script> @@ -185,91 +196,105 @@ export default { @close="reset" @hide="reset" > - <div class="gl-display-flex" data-testid="modal-base-intro-text"> - <slot name="intro-text-before"></slot> - <p> - <gl-sprintf :message="introText"> - <template #strong="{ content }"> - <strong>{{ content }}</strong> - </template> - </gl-sprintf> - </p> - <slot name="intro-text-after"></slot> - </div> - - <gl-form-group - :invalid-feedback="invalidFeedbackMessage" - :state="validationState" - :description="formGroupDescription" - data-testid="members-form-group" + <content-transition + class="gl-display-grid" + transition-name="invite-modal-transition" + :slots="contentSlots" + :current-slot="currentSlot" > - <label :id="selectLabelId" class="col-form-label">{{ labelSearchField }}</label> - <slot - name="select" - v-bind="{ clearValidation, validationState, labelId: selectLabelId }" - ></slot> - </gl-form-group> + <template #[$options.DEFAULT_SLOT]> + <div class="gl-display-flex" data-testid="modal-base-intro-text"> + <slot name="intro-text-before"></slot> + <p> + <gl-sprintf :message="introText"> + <template #strong="{ content }"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </p> + <slot name="intro-text-after"></slot> + </div> - <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" - 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> + <gl-form-group + :invalid-feedback="invalidFeedbackMessage" + :state="validationState" + :description="formGroupDescription" + data-testid="members-form-group" + > + <label :id="selectLabelId" class="col-form-label">{{ labelSearchField }}</label> + <slot name="select" v-bind="{ validationState, labelId: selectLabelId }"></slot> + </gl-form-group> - <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> + <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" + 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> - <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-datepicker - v-model="selectedDate" - class="gl-display-inline!" - :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> - <slot name="form-after"></slot> + <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> + <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-datepicker + v-model="selectedDate" + class="gl-display-inline!" + :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> + <slot name="form-after"></slot> + </template> + <template v-for="{ key } in extraSlots" #[key]> + <slot :name="key"></slot> + </template> + </content-transition> <template #modal-footer> - <gl-button data-testid="cancel-button" @click="closeModal"> - {{ $options.CANCEL_BUTTON_TEXT }} - </gl-button> + <slot name="cancel-button"> + <gl-button data-testid="cancel-button" @click="closeModal"> + {{ $options.CANCEL_BUTTON_TEXT }} + </gl-button> + </slot> <gl-button :disabled="submitDisabled" :loading="isLoading" - variant="success" + variant="confirm" data-qa-selector="invite_button" data-testid="invite-button" @click="submit" > - {{ $options.INVITE_BUTTON_TEXT }} + {{ submitButtonText }} </gl-button> </template> </gl-modal> |