Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
89356357
Commit
89356357
authored
Feb 22, 2022
by
Diana Zubova
Committed by
Miguel Rincon
Feb 22, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add transition between invite members modal
Smooth UX transition EE: true
parent
d94c5986
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
152 additions
and
95 deletions
+152
-95
ee/app/assets/javascripts/invite_members/components/invite_modal_base.vue
...vascripts/invite_members/components/invite_modal_base.vue
+96
-79
ee/app/assets/stylesheets/pages/groups.scss
ee/app/assets/stylesheets/pages/groups.scss
+31
-0
ee/spec/frontend/invite_members/components/invite_modal_base_spec.js
...ntend/invite_members/components/invite_modal_base_spec.js
+25
-16
No files found.
ee/app/assets/javascripts/invite_members/components/invite_modal_base.vue
View file @
89356357
...
...
@@ -131,10 +131,13 @@ export default {
return
this
.
glFeatures
.
overageMembersModal
;
},
modalInfo
()
{
const
infoText
=
this
.
$options
.
i18n
.
infoText
(
this
.
subscriptionSeats
);
const
infoWarning
=
this
.
$options
.
i18n
.
infoWarning
(
this
.
totalUserCount
,
this
.
name
);
if
(
this
.
totalUserCount
)
{
const
infoText
=
this
.
$options
.
i18n
.
infoText
(
this
.
subscriptionSeats
);
const
infoWarning
=
this
.
$options
.
i18n
.
infoWarning
(
this
.
totalUserCount
,
this
.
name
);
return
`
${
infoText
}
${
infoWarning
}
`
;
return
`
${
infoText
}
${
infoWarning
}
`
;
}
return
''
;
},
modalTitleLabel
()
{
return
this
.
showOverageModal
?
this
.
$options
.
i18n
.
OVERAGE_MODAL_TITLE
:
this
.
modalTitle
;
...
...
@@ -243,88 +246,102 @@ export default {
@
close=
"reset"
@
hide=
"reset"
>
<div
v-show=
"!showOverageModal"
>
<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>
<div
class=
"gl-display-grid"
>
<transition
name=
"invite-modal-transition"
>
<div
v-show=
"!showOverageModal"
class=
"invite-modal-content"
data-testid=
"invite-modal-initial-content"
>
<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"
>
<label
:id=
"selectLabelId"
class=
"col-form-label"
>
{{ labelSearchField }}
</label>
<slot
name=
"select"
v-bind=
"{ clearValidation, validationState, labelId: selectLabelId }"
></slot>
</gl-form-group>
<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=
"{ clearValidation, validationState, labelId: selectLabelId }"
></slot>
</gl-form-group>
<label
class=
"gl-font-weight-bold"
>
{{ $options.i18n.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)"
<label
class=
"gl-font-weight-bold"
>
{{ $options.i18n.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"
>
<div>
{{
item
}}
</div>
</gl-dropdown-item>
</
template
>
</gl-dropdown>
</div>
<
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.i18n.READ_MORE_TEXT"
>
<
template
#link=
"{ content }"
>
<gl-link
:href=
"helpLink"
target=
"_blank"
>
{{
content
}}
</gl-link>
</
template
>
</gl-sprintf>
</div>
<div
class=
"gl-mt-2 gl-w-half gl-xs-w-full"
>
<gl-sprintf
:message=
"$options.i18n.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.i18n.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"
<label
class=
"gl-mt-5 gl-display-block"
for=
"expires_at"
>
{{
$options.i18n.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>
</transition>
<transition
name=
"invite-modal-transition"
>
<div
v-show=
"showOverageModal"
class=
"invite-modal-content"
data-testid=
"invite-modal-overage-content"
>
<
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>
<div
v-if=
"showOverageModal"
>
{{ modalInfo }}
<gl-link
:href=
"$options.i18n.OVERAGE_MODAL_LINK"
target=
"_blank"
>
{{
$options.i18n.OVERAGE_MODAL_LINK_TEXT
}}
</gl-link>
{{ modalInfo }}
<gl-link
:href=
"$options.i18n.OVERAGE_MODAL_LINK"
target=
"_blank"
>
{{
$options.i18n.OVERAGE_MODAL_LINK_TEXT
}}
</gl-link>
</div>
</transition>
</div>
<
template
#modal-footer
>
<template
v-if=
"!showOverageModal"
>
...
...
ee/app/assets/stylesheets/pages/groups.scss
View file @
89356357
...
...
@@ -42,3 +42,34 @@
svg
g
{
fill
:
$gray-600
;
}
}
}
.invite-modal-content
{
grid-row
:
1
;
grid-column
:
1
;
}
$max-invite-modal-height
:
600px
;
// Custom styles for invite-modal-transition
// Used by Vue Transition API
.invite-modal-transition-enter-active
,
.invite-modal-transition-leave-active
{
transition-property
:
max-height
,
opacity
;
transition-timing-function
:
ease-in-out
;
@include
gl-transition-slow
;
overflow
:
hidden
;
}
.invite-modal-transition-enter
,
.invite-modal-transition-leave-to
{
max-height
:
0
;
opacity
:
0
;
}
.invite-modal-transition-enter-to
,
.invite-modal-transition-leave
{
max-height
:
px-to-rem
(
$max-invite-modal-height
);
}
ee/spec/frontend/invite_members/components/invite_modal_base_spec.js
View file @
89356357
...
...
@@ -16,7 +16,6 @@ import {
OVERAGE_MODAL_CONTINUE_BUTTON
,
OVERAGE_MODAL_BACK_BUTTON
,
}
from
'
ee/invite_members/constants
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
propsData
}
from
'
jest/invite_members/mock_data/modal_base
'
;
describe
(
'
InviteModalBase
'
,
()
=>
{
...
...
@@ -63,6 +62,8 @@ describe('InviteModalBase', () => {
const
findInviteButton
=
()
=>
wrapper
.
findByTestId
(
'
invite-button
'
);
const
findBackButton
=
()
=>
wrapper
.
findByTestId
(
'
overage-back-button
'
);
const
findOverageInviteButton
=
()
=>
wrapper
.
findByTestId
(
'
invite-with-overage-button
'
);
const
findInitialModalContent
=
()
=>
wrapper
.
findByTestId
(
'
invite-modal-initial-content
'
);
const
findOverageModalContent
=
()
=>
wrapper
.
findByTestId
(
'
invite-modal-overage-content
'
);
const
clickInviteButton
=
()
=>
findInviteButton
().
vm
.
$emit
(
'
click
'
);
const
clickBackButton
=
()
=>
findBackButton
().
vm
.
$emit
(
'
click
'
);
...
...
@@ -102,25 +103,23 @@ describe('InviteModalBase', () => {
});
});
describe
(
'
rendering the help link
'
,
()
=>
{
it
(
'
renders the correct link
'
,
()
=>
{
expect
(
findLink
().
attributes
(
'
href
'
)).
toBe
(
propsData
.
helpLink
);
});
it
(
'
renders the correct link
'
,
()
=>
{
expect
(
findLink
().
attributes
(
'
href
'
)).
toBe
(
propsData
.
helpLink
);
});
describe
(
'
rendering the access expiration date field
'
,
()
=>
{
it
(
'
renders the datepicker
'
,
()
=>
{
expect
(
findDatepicker
().
exists
()).
toBe
(
true
);
});
it
(
'
renders the datepicker
'
,
()
=>
{
expect
(
findDatepicker
().
exists
()).
toBe
(
true
);
});
it
(
"
doesn't show the overage content
"
,
()
=>
{
expect
(
findOverageModalContent
().
isVisible
()).
toBe
(
false
);
});
});
describe
(
'
displays overage modal
'
,
()
=>
{
beforeEach
(
async
()
=>
{
beforeEach
(()
=>
{
createComponent
({},
{},
{
glFeatures
:
{
overageMembersModal
:
true
}
});
clickInviteButton
();
await
waitForPromises
();
});
it
(
'
renders the modal with the correct title
'
,
()
=>
{
...
...
@@ -141,11 +140,21 @@ describe('InviteModalBase', () => {
);
});
it
(
'
switches back to the intial modal
'
,
async
()
=>
{
clickBackButton
();
await
waitForPromises
();
it
(
'
doesn
\t
show the initial modal content
'
,
()
=>
{
expect
(
findInitialModalContent
().
isVisible
()).
toBe
(
false
);
});
describe
(
'
when switches back to the initial modal
'
,
()
=>
{
beforeEach
(()
=>
clickBackButton
());
expect
(
wrapper
.
findComponent
(
GlModal
).
props
(
'
title
'
)).
toBe
(
'
_modal_title_
'
);
it
(
'
shows the initial modal
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
GlModal
).
props
(
'
title
'
)).
toBe
(
'
_modal_title_
'
);
expect
(
findInitialModalContent
().
isVisible
()).
toBe
(
true
);
});
it
(
"
doesn't show the overage content
"
,
()
=>
{
expect
(
findOverageModalContent
().
isVisible
()).
toBe
(
false
);
});
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment