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
4a8b8929
Commit
4a8b8929
authored
Apr 10, 2020
by
Kushal Pandya
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'health-status-dropdown' into 'master'
Use dropdown to change health status See merge request gitlab-org/gitlab!28547
parents
3f474171
24198491
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
225 additions
and
219 deletions
+225
-219
app/assets/stylesheets/framework/sidebar.scss
app/assets/stylesheets/framework/sidebar.scss
+12
-0
doc/user/project/issues/img/issue_health_status_dropdown_v12_10.png
...roject/issues/img/issue_health_status_dropdown_v12_10.png
+0
-0
doc/user/project/issues/index.md
doc/user/project/issues/index.md
+1
-1
ee/app/assets/javascripts/sidebar/components/status/sidebar_status.vue
.../javascripts/sidebar/components/status/sidebar_status.vue
+2
-2
ee/app/assets/javascripts/sidebar/components/status/status.vue
...p/assets/javascripts/sidebar/components/status/status.vue
+93
-62
ee/app/assets/javascripts/sidebar/constants.js
ee/app/assets/javascripts/sidebar/constants.js
+0
-6
ee/changelogs/unreleased/health-status-dropdown.yml
ee/changelogs/unreleased/health-status-dropdown.yml
+5
-0
ee/spec/frontend/sidebar/components/status/sidebar_status_spec.js
...frontend/sidebar/components/status/sidebar_status_spec.js
+31
-62
ee/spec/frontend/sidebar/components/status/status_spec.js
ee/spec/frontend/sidebar/components/status/status_spec.js
+69
-77
locale/gitlab.pot
locale/gitlab.pot
+12
-9
No files found.
app/assets/stylesheets/framework/sidebar.scss
View file @
4a8b8929
...
@@ -210,3 +210,15 @@
...
@@ -210,3 +210,15 @@
}
}
}
}
}
}
.health-status
{
.dropdown-body
{
.health-divider
{
border-top-color
:
$gray-200
;
}
.dropdown-item
:not
(
.health-dropdown-item
)
{
padding
:
0
;
}
}
}
doc/user/project/issues/img/issue_health_status_dropdown_v12_10.png
0 → 100644
View file @
4a8b8929
43.7 KB
doc/user/project/issues/index.md
View file @
4a8b8929
...
@@ -177,7 +177,7 @@ that's progressing as planned or needs attention to keep on schedule:
...
@@ -177,7 +177,7 @@ that's progressing as planned or needs attention to keep on schedule:
-
**Needs attention**
(amber)
-
**Needs attention**
(amber)
-
**At risk**
(red)
-
**At risk**
(red)
![
"On track" health status on an issue
](
img/issue_health_status_v12_10.png
)
![
"On track" health status on an issue
](
img/issue_health_status_
dropdown_
v12_10.png
)
You can then see issue statuses on the
You can then see issue statuses on the
[
Epic tree
](
../../group/epics/index.md#issue-health-status-in-epic-tree-ultimate
)
.
[
Epic tree
](
../../group/epics/index.md#issue-health-status-in-epic-tree-ultimate
)
.
...
...
ee/app/assets/javascripts/sidebar/components/status/sidebar_status.vue
View file @
4a8b8929
...
@@ -17,7 +17,7 @@ export default {
...
@@ -17,7 +17,7 @@ export default {
},
},
},
},
methods
:
{
methods
:
{
handle
FormSubmission
(
status
)
{
handle
DropdownClick
(
status
)
{
this
.
mediator
.
updateStatus
(
status
).
catch
(()
=>
{
this
.
mediator
.
updateStatus
(
status
).
catch
(()
=>
{
Flash
(
__
(
'
Error occurred while updating the issue status
'
));
Flash
(
__
(
'
Error occurred while updating the issue status
'
));
});
});
...
@@ -31,6 +31,6 @@ export default {
...
@@ -31,6 +31,6 @@ export default {
:is-editable=
"mediator.store.editable"
:is-editable=
"mediator.store.editable"
:is-fetching=
"mediator.store.isFetching.status"
:is-fetching=
"mediator.store.isFetching.status"
:status=
"mediator.store.status"
:status=
"mediator.store.status"
@
on
StatusChange=
"handleFormSubmission
"
@
on
DropdownClick=
"handleDropdownClick
"
/>
/>
</
template
>
</
template
>
ee/app/assets/javascripts/sidebar/components/status/status.vue
View file @
4a8b8929
<
script
>
<
script
>
import
Tracking
from
'
~/tracking
'
;
import
{
import
{
GlDeprecatedButton
,
GlFormGroup
,
GlFormRadioGroup
,
GlIcon
,
GlIcon
,
GlNewButton
,
GlLoadingIcon
,
GlLoadingIcon
,
GlTooltip
,
GlTooltip
,
GlDropdownItem
,
GlDropdown
,
GlDropdownDivider
,
}
from
'
@gitlab/ui
'
;
}
from
'
@gitlab/ui
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
healthStatus
ColorMap
,
healthStatus
TextMap
}
from
'
../../constants
'
;
import
{
healthStatusTextMap
}
from
'
../../constants
'
;
export
default
{
export
default
{
components
:
{
components
:
{
GlDeprecatedButton
,
GlIcon
,
GlIcon
,
GlNewButton
,
GlLoadingIcon
,
GlLoadingIcon
,
GlFormGroup
,
GlFormRadioGroup
,
GlTooltip
,
GlTooltip
,
GlDropdown
,
GlDropdownItem
,
GlDropdownDivider
,
},
},
mixins
:
[
Tracking
.
mixin
()],
props
:
{
props
:
{
isEditable
:
{
isEditable
:
{
type
:
Boolean
,
type
:
Boolean
,
...
@@ -38,11 +42,11 @@ export default {
...
@@ -38,11 +42,11 @@ export default {
},
},
data
()
{
data
()
{
return
{
return
{
is
Form
Showing
:
false
,
is
Dropdown
Showing
:
false
,
selectedStatus
:
this
.
status
,
selectedStatus
:
this
.
status
,
statusOptions
:
Object
.
keys
(
healthStatusTextMap
).
map
(
key
=>
({
statusOptions
:
Object
.
keys
(
healthStatusTextMap
).
map
(
key
=>
({
value
:
key
,
key
,
text
:
healthStatusTextMap
[
key
],
value
:
healthStatusTextMap
[
key
],
})),
})),
};
};
},
},
...
@@ -53,11 +57,11 @@ export default {
...
@@ -53,11 +57,11 @@ export default {
statusText
()
{
statusText
()
{
return
this
.
status
?
healthStatusTextMap
[
this
.
status
]
:
s__
(
'
Sidebar|None
'
);
return
this
.
status
?
healthStatusTextMap
[
this
.
status
]
:
s__
(
'
Sidebar|None
'
);
},
},
statusColor
()
{
dropdownText
()
{
return
healthStatusColorMap
[
this
.
status
]
;
return
this
.
status
?
healthStatusTextMap
[
this
.
status
]
:
s__
(
'
Select health status
'
)
;
},
},
tooltipText
()
{
tooltipText
()
{
let
tooltipText
=
s__
(
'
Sidebar|
S
tatus
'
);
let
tooltipText
=
s__
(
'
Sidebar|
Health s
tatus
'
);
if
(
this
.
status
)
{
if
(
this
.
status
)
{
tooltipText
+=
`:
${
this
.
statusText
}
`
;
tooltipText
+=
`:
${
this
.
statusText
}
`
;
...
@@ -72,19 +76,33 @@ export default {
...
@@ -72,19 +76,33 @@ export default {
},
},
},
},
methods
:
{
methods
:
{
handleFormSubmission
()
{
handleDropdownClick
(
status
)
{
this
.
$emit
(
'
onStatusChange
'
,
this
.
selectedStatus
);
this
.
selectedStatus
=
status
;
this
.
hideForm
();
this
.
$emit
(
'
onDropdownClick
'
,
status
);
this
.
track
(
'
change_health_status
'
,
{
property
:
status
});
this
.
hideDropdown
();
},
},
hideForm
()
{
hideDropdown
()
{
this
.
isFormShowing
=
false
;
this
.
isDropdownShowing
=
false
;
this
.
$refs
.
editButton
.
focus
();
},
},
toggleFormDropdown
()
{
toggleFormDropdown
()
{
this
.
isFormShowing
=
!
this
.
isFormShowing
;
this
.
isDropdownShowing
=
!
this
.
isDropdownShowing
;
/**
* We need to programmatically open the dropdown to make the
* outside click on document close the dropdown.
*/
const
{
dropdown
}
=
this
.
$refs
.
dropdown
.
$refs
;
if
(
dropdown
&&
this
.
isDropdownShowing
)
{
dropdown
.
show
();
}
},
},
removeStatus
()
{
removeStatus
()
{
this
.
$emit
(
'
onStatusChange
'
,
null
);
this
.
handleDropdownClick
(
null
);
},
isSelected
(
status
)
{
return
this
.
status
===
status
;
},
},
},
},
};
};
...
@@ -104,62 +122,75 @@ export default {
...
@@ -104,62 +122,75 @@ export default {
<div
class=
"hide-collapsed"
>
<div
class=
"hide-collapsed"
>
<p
class=
"title d-flex justify-content-between"
>
<p
class=
"title d-flex justify-content-between"
>
{{
s__
(
'
Sidebar|
S
tatus
'
)
}}
{{
s__
(
'
Sidebar|
Health s
tatus
'
)
}}
<a
<a
v-if=
"isEditable"
v-if=
"isEditable"
ref=
"editButton"
ref=
"editButton"
class=
"btn-link"
class=
"btn-link"
href=
"#"
href=
"#"
@
click=
"toggleFormDropdown"
@
click=
"toggleFormDropdown"
@
keydown.esc=
"hide
Form
"
@
keydown.esc=
"hide
Dropdown
"
>
>
{{
__
(
'
Edit
'
)
}}
{{
__
(
'
Edit
'
)
}}
</a>
</a>
</p>
</p>
<div
v-if=
"isFormShowing"
class=
"dropdown show"
>
<div
<form
class=
"dropdown-menu p-3"
@
submit.prevent=
"handleFormSubmission"
>
class=
"dropdown dropdown-menu-selectable"
<p>
:class=
"
{ show: isDropdownShowing, 'd-none': !isDropdownShowing }"
{{
>
__
(
'
Choose which status most accurately reflects the current state of this issue:
'
)
<gl-dropdown
}}
ref=
"dropdown"
</p>
class=
"w-100"
<gl-form-group>
:text=
"dropdownText"
<gl-form-radio-group
@
keydown.esc.native=
"hideDropdown"
v-model=
"selectedStatus"
@
hide=
"hideDropdown"
:checked=
"selectedStatus"
>
:options=
"statusOptions"
<div
class=
"dropdown-title"
>
stacked
<span
class=
"health-title"
>
{{
s__
(
'
Sidebar|Assign health status
'
)
}}
</span>
@
keydown.esc.native=
"hideForm"
<gl-new-button
:aria-label=
"__('Close')"
variant=
"link"
class=
"dropdown-title-button dropdown-menu-close"
icon=
"close"
@
click=
"hideDropdown"
/>
/>
</gl-form-group>
</div>
<gl-form-group
class=
"mb-0"
>
<gl-deprecated-button
type=
"button"
class=
"append-right-10"
@
click=
"hideForm"
>
<div
class=
"dropdown-content dropdown-body"
>
{{
__
(
'
Cancel
'
)
}}
<gl-dropdown-item
@
click=
"handleDropdownClick(null)"
>
</gl-deprecated-button>
<gl-new-button
<gl-deprecated-button
type=
"submit"
variant=
"success"
>
variant=
"link"
{{
__
(
'
Save
'
)
}}
class=
"dropdown-item health-dropdown-item"
</gl-deprecated-button>
:class=
"
{ 'is-active': isSelected(null) }"
</gl-form-group>
>
</form>
{{
s__
(
'
Sidebar|No status
'
)
}}
</gl-new-button>
</gl-dropdown-item>
<gl-dropdown-divider
class=
"divider health-divider"
/>
<gl-dropdown-item
v-for=
"option in statusOptions"
:key=
"option.key"
@
click=
"handleDropdownClick(option.key)"
>
<gl-new-button
variant=
"link"
class=
"dropdown-item health-dropdown-item"
:class=
"
{ 'is-active': isSelected(option.key) }"
>
{{
option
.
value
}}
</gl-new-button>
</gl-dropdown-item>
</div>
</gl-dropdown>
</div>
</div>
<gl-loading-icon
v-if=
"isFetching"
:inline=
"true"
/>
<gl-loading-icon
v-if=
"isFetching"
:inline=
"true"
/>
<p
v-else
class=
"value d-flex align-items-center m-0"
:class=
"
{ 'no-value': !status }">
<p
v-else-if=
"!isDropdownShowing"
class=
"value m-0"
:class=
"
{ 'no-value': !status }">
<gl-icon
<span
v-if=
"status"
class=
"text-plain bold"
>
{{
statusText
}}
</span>
v-if=
"status"
<span
v-else
>
{{
__
(
'
None
'
)
}}
</span>
name=
"severity-low"
:size=
"14"
class=
"align-bottom append-right-10"
:class=
"statusColor"
/>
{{
statusText
}}
<template
v-if=
"canRemoveStatus"
>
<span
class=
"text-secondary mx-1"
aria-hidden=
"true"
>
-
</span>
<gl-deprecated-button
variant=
"link"
class=
"text-secondary"
@
click=
"removeStatus"
>
{{
__
(
'
remove status
'
)
}}
</gl-deprecated-button>
</
template
>
</p>
</p>
</div>
</div>
</div>
</div>
...
...
ee/app/assets/javascripts/sidebar/constants.js
View file @
4a8b8929
...
@@ -6,12 +6,6 @@ export const healthStatus = {
...
@@ -6,12 +6,6 @@ export const healthStatus = {
AT_RISK
:
'
atRisk
'
,
AT_RISK
:
'
atRisk
'
,
};
};
export
const
healthStatusColorMap
=
{
[
healthStatus
.
ON_TRACK
]:
'
text-success
'
,
[
healthStatus
.
NEEDS_ATTENTION
]:
'
text-warning
'
,
[
healthStatus
.
AT_RISK
]:
'
text-danger
'
,
};
export
const
healthStatusTextMap
=
{
export
const
healthStatusTextMap
=
{
[
healthStatus
.
ON_TRACK
]:
__
(
'
On track
'
),
[
healthStatus
.
ON_TRACK
]:
__
(
'
On track
'
),
[
healthStatus
.
NEEDS_ATTENTION
]:
__
(
'
Needs attention
'
),
[
healthStatus
.
NEEDS_ATTENTION
]:
__
(
'
Needs attention
'
),
...
...
ee/changelogs/unreleased/health-status-dropdown.yml
0 → 100644
View file @
4a8b8929
---
title
:
Use dropdown to change health status
merge_request
:
28547
author
:
type
:
changed
ee/spec/frontend/sidebar/components/status/sidebar_status_spec.js
View file @
4a8b8929
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
SidebarStatus
from
'
ee/sidebar/components/status/sidebar_status.vue
'
;
import
SidebarStatus
from
'
ee/sidebar/components/status/sidebar_status.vue
'
;
import
Status
from
'
ee/sidebar/components/status/status.vue
'
;
import
Status
from
'
ee/sidebar/components/status/status.vue
'
;
const
getStatusText
=
wrapper
=>
wrapper
.
find
(
'
.value
'
).
text
();
describe
(
'
SidebarStatus
'
,
()
=>
{
describe
(
'
SidebarStatus
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
let
handleDropdownClickMock
;
beforeEach
(()
=>
{
const
mediator
=
{
store
:
{
isFetching
:
{
status
:
true
,
},
status
:
''
,
},
};
handleDropdownClickMock
=
jest
.
fn
();
wrapper
=
shallowMount
(
SidebarStatus
,
{
propsData
:
{
mediator
,
},
methods
:
{
handleDropdownClick
:
handleDropdownClickMock
,
},
});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
.
destroy
();
...
@@ -14,73 +34,22 @@ describe('SidebarStatus', () => {
...
@@ -14,73 +34,22 @@ describe('SidebarStatus', () => {
});
});
describe
(
'
Status child component
'
,
()
=>
{
describe
(
'
Status child component
'
,
()
=>
{
let
handleFormSubmissionMock
;
beforeEach
(()
=>
{});
beforeEach
(()
=>
{
const
mediator
=
{
store
:
{
isFetching
:
{
status
:
true
,
},
status
:
''
,
},
};
handleFormSubmissionMock
=
jest
.
fn
();
wrapper
=
shallowMount
(
SidebarStatus
,
{
propsData
:
{
mediator
,
},
methods
:
{
handleFormSubmission
:
handleFormSubmissionMock
,
},
});
});
it
(
'
renders Status component
'
,
()
=>
{
it
(
'
renders Status component
'
,
()
=>
{
expect
(
wrapper
.
contains
(
Status
)).
toBe
(
true
);
expect
(
wrapper
.
contains
(
Status
)).
toBe
(
true
);
});
});
it
(
'
calls handleFormSubmission when receiving an on
StatusChange
event from Status component
'
,
()
=>
{
it
(
'
calls handleFormSubmission when receiving an on
DropdownClick
event from Status component
'
,
()
=>
{
wrapper
.
find
(
Status
).
vm
.
$emit
(
'
on
StatusChange
'
,
'
onTrack
'
);
wrapper
.
find
(
Status
).
vm
.
$emit
(
'
on
DropdownClick
'
,
'
onTrack
'
);
expect
(
handle
FormSubmission
Mock
).
toHaveBeenCalledWith
(
'
onTrack
'
);
expect
(
handle
DropdownClick
Mock
).
toHaveBeenCalledWith
(
'
onTrack
'
);
});
});
});
});
it
(
'
removes status when user clicks on "remove status"
'
,
()
=>
{
it
(
'
calls handleFormSubmission when receiving an onFormSubmit event from Status component
'
,
()
=>
{
const
mediator
=
{
wrapper
.
find
(
Status
).
vm
.
$emit
(
'
onDropdownClick
'
,
'
onTrack
'
);
store
:
{
editable
:
true
,
isFetching
:
{
status
:
false
,
},
status
:
'
onTrack
'
,
},
updateStatus
(
status
)
{
this
.
store
.
status
=
status
;
wrapper
.
setProps
({
mediator
:
{
...
this
,
},
});
return
Promise
.
resolve
();
},
};
wrapper
=
mount
(
SidebarStatus
,
{
expect
(
handleDropdownClickMock
).
toHaveBeenCalledWith
(
'
onTrack
'
);
propsData
:
{
mediator
,
},
});
expect
(
getStatusText
(
wrapper
)).
toContain
(
'
On track
'
);
wrapper
.
find
(
'
button.btn-link
'
).
trigger
(
'
click
'
);
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
getStatusText
(
wrapper
)).
toBe
(
'
None
'
);
});
});
});
});
});
ee/spec/frontend/sidebar/components/status/status_spec.js
View file @
4a8b8929
import
{
GlD
eprecatedButton
,
GlFormRadioGroup
,
GlLoadingIcon
,
GlTooltip
}
from
'
@gitlab/ui
'
;
import
{
GlD
ropdown
,
GlDropdownItem
,
GlLoadingIcon
,
GlTooltip
}
from
'
@gitlab/ui
'
;
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
Status
from
'
ee/sidebar/components/status/status.vue
'
;
import
Status
from
'
ee/sidebar/components/status/status.vue
'
;
import
{
healthStatus
,
healthStatus
ColorMap
,
healthStatus
TextMap
}
from
'
ee/sidebar/constants
'
;
import
{
healthStatus
,
healthStatusTextMap
}
from
'
ee/sidebar/constants
'
;
const
getStatusText
=
wrapper
=>
wrapper
.
find
(
'
.value
'
).
text
();
const
getStatusText
=
wrapper
=>
wrapper
.
find
(
'
.value
.text-plain
'
).
text
();
const
getTooltipText
=
wrapper
=>
wrapper
.
find
(
GlTooltip
).
text
();
const
getTooltipText
=
wrapper
=>
wrapper
.
find
(
GlTooltip
).
text
();
const
getStatusIconCssClasses
=
wrapper
=>
wrapper
.
find
(
'
[name="severity-low"]
'
).
classes
();
const
getEditButton
=
wrapper
=>
wrapper
.
find
({
ref
:
'
editButton
'
});
const
getEditButton
=
wrapper
=>
wrapper
.
find
({
ref
:
'
editButton
'
});
const
getRemoveStatusButton
=
wrapper
=>
wrapper
.
find
(
GlDeprecatedButton
);
const
getDropdownElement
=
wrapper
=>
wrapper
.
find
(
GlDropdown
);
const
getEditForm
=
wrapper
=>
wrapper
.
find
(
'
form
'
);
const
getRadioInputs
=
wrapper
=>
wrapper
.
findAll
(
'
input[type="radio"]
'
);
const
getR
adioComponent
=
wrapper
=>
wrapper
.
find
(
GlFormRadioGroup
);
const
getR
emoveStatusItem
=
wrapper
=>
wrapper
.
find
(
GlDropdownItem
);
describe
(
'
Status
'
,
()
=>
{
describe
(
'
Status
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
...
@@ -41,8 +35,7 @@ describe('Status', () => {
...
@@ -41,8 +35,7 @@ describe('Status', () => {
it
(
'
shows the text "Status"
'
,
()
=>
{
it
(
'
shows the text "Status"
'
,
()
=>
{
shallowMountStatus
();
shallowMountStatus
();
expect
(
wrapper
.
find
(
'
.title
'
).
text
()).
toBe
(
'
Health status
'
);
expect
(
wrapper
.
find
(
'
.title
'
).
text
()).
toBe
(
'
Status
'
);
});
});
describe
(
'
loading icon
'
,
()
=>
{
describe
(
'
loading icon
'
,
()
=>
{
...
@@ -89,18 +82,7 @@ describe('Status', () => {
...
@@ -89,18 +82,7 @@ describe('Status', () => {
});
});
});
});
describe
(
'
remove status button
'
,
()
=>
{
describe
(
'
remove status dropdown item
'
,
()
=>
{
it
(
'
is hidden when there is no status
'
,
()
=>
{
const
props
=
{
isEditable
:
true
,
status
:
''
,
};
shallowMountStatus
(
props
);
expect
(
getRemoveStatusButton
(
wrapper
).
exists
()).
toBe
(
false
);
});
it
(
'
is displayed when there is a status
'
,
()
=>
{
it
(
'
is displayed when there is a status
'
,
()
=>
{
const
props
=
{
const
props
=
{
isEditable
:
true
,
isEditable
:
true
,
...
@@ -109,10 +91,14 @@ describe('Status', () => {
...
@@ -109,10 +91,14 @@ describe('Status', () => {
shallowMountStatus
(
props
);
shallowMountStatus
(
props
);
expect
(
getRemoveStatusButton
(
wrapper
).
exists
()).
toBe
(
true
);
wrapper
.
vm
.
isDropdownShowing
=
true
;
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
getRemoveStatusItem
(
wrapper
).
exists
()).
toBe
(
true
);
});
});
});
it
(
'
emits an on
StatusChange
event with argument null when clicked
'
,
()
=>
{
it
(
'
emits an on
DropdownClick
event with argument null when clicked
'
,
()
=>
{
const
props
=
{
const
props
=
{
isEditable
:
true
,
isEditable
:
true
,
status
:
healthStatus
.
AT_RISK
,
status
:
healthStatus
.
AT_RISK
,
...
@@ -120,9 +106,13 @@ describe('Status', () => {
...
@@ -120,9 +106,13 @@ describe('Status', () => {
shallowMountStatus
(
props
);
shallowMountStatus
(
props
);
getRemoveStatusButton
(
wrapper
).
vm
.
$emit
(
'
click
'
);
wrapper
.
vm
.
isDropdownShowing
=
true
;
wrapper
.
vm
.
$nextTick
(()
=>
{
getRemoveStatusItem
(
wrapper
).
vm
.
$emit
(
'
click
'
,
{
preventDefault
:
()
=>
null
});
expect
(
wrapper
.
emitted
().
onStatusChange
[
0
]).
toEqual
([
null
]);
expect
(
wrapper
.
emitted
().
onDropdownClick
[
0
]).
toEqual
([
null
]);
});
});
});
});
});
...
@@ -137,11 +127,11 @@ describe('Status', () => {
...
@@ -137,11 +127,11 @@ describe('Status', () => {
});
});
it
(
'
shows "None"
'
,
()
=>
{
it
(
'
shows "None"
'
,
()
=>
{
expect
(
getStatusText
(
wrapper
)).
toBe
(
'
None
'
);
expect
(
wrapper
.
find
(
'
.no-value
'
).
text
(
)).
toBe
(
'
None
'
);
});
});
it
(
'
shows "Status" in the tooltip
'
,
()
=>
{
it
(
'
shows "Status" in the tooltip
'
,
()
=>
{
expect
(
getTooltipText
(
wrapper
)).
toBe
(
'
S
tatus
'
);
expect
(
getTooltipText
(
wrapper
)).
toBe
(
'
Health s
tatus
'
);
});
});
});
});
...
@@ -159,24 +149,22 @@ describe('Status', () => {
...
@@ -159,24 +149,22 @@ describe('Status', () => {
});
});
it
(
`shows "Status:
${
healthStatusTextMap
[
statusValue
]}
" in the tooltip`
,
()
=>
{
it
(
`shows "Status:
${
healthStatusTextMap
[
statusValue
]}
" in the tooltip`
,
()
=>
{
expect
(
getTooltipText
(
wrapper
)).
toBe
(
`Status:
${
healthStatusTextMap
[
statusValue
]}
`
);
expect
(
getTooltipText
(
wrapper
)).
toBe
(
`Health status:
${
healthStatusTextMap
[
statusValue
]}
`
);
});
it
(
`uses
${
healthStatusColorMap
[
statusValue
]}
color for the status icon`
,
()
=>
{
expect
(
getStatusIconCssClasses
(
wrapper
)).
toContain
(
healthStatusColorMap
[
statusValue
]);
});
});
});
});
});
});
describe
(
'
status
edit form
'
,
()
=>
{
describe
(
'
status
dropdown
'
,
()
=>
{
it
(
'
is hidden by default
'
,
()
=>
{
it
(
'
is hidden by default
'
,
()
=>
{
const
props
=
{
const
props
=
{
isEditable
:
true
,
isEditable
:
true
,
};
};
shallowMountStatus
(
props
);
mountStatus
(
props
);
const
dropdown
=
wrapper
.
find
(
'
.dropdown
'
);
expect
(
getEditForm
(
wrapper
).
exists
()).
toBe
(
false
);
expect
(
dropdown
.
classes
()).
toContain
(
'
d-none
'
);
});
});
describe
(
'
when hidden
'
,
()
=>
{
describe
(
'
when hidden
'
,
()
=>
{
...
@@ -185,14 +173,14 @@ describe('Status', () => {
...
@@ -185,14 +173,14 @@ describe('Status', () => {
isEditable
:
true
,
isEditable
:
true
,
};
};
shallowM
ountStatus
(
props
);
m
ountStatus
(
props
);
});
});
it
(
'
shows the
form
when the Edit button is clicked
'
,
()
=>
{
it
(
'
shows the
dropdown
when the Edit button is clicked
'
,
()
=>
{
getEditButton
(
wrapper
).
trigger
(
'
click
'
);
getEditButton
(
wrapper
).
trigger
(
'
click
'
);
return
Vue
.
nextTick
().
then
(()
=>
{
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
getEditForm
(
wrapper
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
find
(
'
.dropdown
'
).
classes
()).
toContain
(
'
show
'
);
});
});
});
});
});
});
...
@@ -205,47 +193,43 @@ describe('Status', () => {
...
@@ -205,47 +193,43 @@ describe('Status', () => {
shallowMountStatus
(
props
);
shallowMountStatus
(
props
);
wrapper
.
setData
({
is
Form
Showing
:
true
});
wrapper
.
setData
({
is
Dropdown
Showing
:
true
});
});
});
it
(
'
shows text to ask the user to pick an option
'
,
()
=>
{
it
(
'
shows text to ask the user to pick an option
'
,
()
=>
{
const
message
=
const
message
=
'
Assign health status
'
;
'
Choose which status most accurately reflects the current state of this issue:
'
;
expect
(
expect
(
get
EditForm
(
wrapper
)
get
DropdownElement
(
wrapper
)
.
find
(
'
p
'
)
.
find
(
'
.health-title
'
)
.
text
(),
.
text
(),
).
toContain
(
message
);
).
toContain
(
message
);
});
});
it
(
'
hides form when the
Edit
button is clicked
'
,
()
=>
{
it
(
'
hides form when the
`edit`
button is clicked
'
,
()
=>
{
getEditButton
(
wrapper
).
trigger
(
'
click
'
);
getEditButton
(
wrapper
).
trigger
(
'
click
'
);
return
Vue
.
nextTick
().
then
(()
=>
{
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
getEditForm
(
wrapper
).
exists
()).
toBe
(
false
);
expect
(
wrapper
.
find
(
'
.dropdown
'
).
classes
()).
toContain
(
'
d-none
'
);
});
});
});
});
it
(
'
hides form when
the Cancel button
is clicked
'
,
()
=>
{
it
(
'
hides form when
a dropdown item
is clicked
'
,
()
=>
{
const
button
=
getEditForm
(
wrapper
).
find
(
'
[type="button"]
'
);
const
dropdownItem
=
wrapper
.
findAll
(
GlDropdownItem
).
at
(
1
);
button
.
vm
.
$emit
(
'
click
'
);
dropdownItem
.
vm
.
$emit
(
'
click
'
);
return
Vue
.
nextTick
().
then
(()
=>
{
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
getEditForm
(
wrapper
).
exists
()).
toBe
(
false
);
expect
(
wrapper
.
find
(
'
.dropdown
'
).
classes
()).
toContain
(
'
d-none
'
);
});
});
it
(
'
hides form when the form is submitted
'
,
()
=>
{
getEditForm
(
wrapper
).
trigger
(
'
submit
'
);
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
getEditForm
(
wrapper
).
exists
()).
toBe
(
false
);
});
});
});
});
});
});
describe
(
'
radio buttons
'
,
()
=>
{
describe
(
'
dropdown
'
,
()
=>
{
const
getIterableArray
=
arr
=>
{
return
arr
.
map
((
value
,
index
)
=>
[
value
,
index
]);
};
beforeEach
(()
=>
{
beforeEach
(()
=>
{
const
props
=
{
const
props
=
{
isEditable
:
true
,
isEditable
:
true
,
...
@@ -253,29 +237,37 @@ describe('Status', () => {
...
@@ -253,29 +237,37 @@ describe('Status', () => {
mountStatus
(
props
);
mountStatus
(
props
);
wrapper
.
setData
({
is
Form
Showing
:
true
});
wrapper
.
setData
({
is
Dropdown
Showing
:
true
});
});
});
it
(
'
shows
3 radio button
s
'
,
()
=>
{
it
(
'
shows
4 dropdown item
s
'
,
()
=>
{
expect
(
getRadioInputs
(
wrapper
).
length
).
toBe
(
3
);
expect
(
wrapper
.
findAll
(
GlDropdownItem
).
length
).
toBe
(
4
);
});
});
// Test that "On track", "Needs attention", and "At risk" are displayed
// Test that "On track", "Needs attention", and "At risk" are displayed
it
.
each
(
Object
.
values
(
healthStatusTextMap
))(
'
shows "%s" text
'
,
statusText
=>
{
it
.
each
(
getIterableArray
(
Object
.
values
(
healthStatusTextMap
)))(
expect
(
getRadioComponent
(
wrapper
).
text
()).
toContain
(
statusText
);
'
shows "%s" text
'
,
});
(
statusText
,
index
)
=>
{
expect
(
wrapper
.
findAll
(
GlDropdownItem
)
.
at
(
index
+
1
)
// +1 in index to account for 1st item as `No status`
.
text
(),
).
toContain
(
statusText
);
},
);
// Test that "onTrack", "needsAttention", and "atRisk" values are emitted when form is submitted
// Test that "onTrack", "needsAttention", and "atRisk" values are emitted when form is submitted
it
.
each
(
Object
.
values
(
healthStatus
))(
it
.
each
(
getIterableArray
(
Object
.
values
(
healthStatus
)))(
'
emits onStatusChange event with argument "%s" when user selects the option and submits form
'
,
'
emits onFormSubmit event with argument "%s" when user selects the option and submits form
'
,
status
=>
{
(
status
,
index
)
=>
{
getEditForm
(
wrapper
)
wrapper
.
find
(
`input[value="
${
status
}
"]`
)
.
findAll
(
GlDropdownItem
)
.
trigger
(
'
click
'
);
.
at
(
index
+
1
)
.
vm
.
$emit
(
'
click
'
,
{
preventDefault
:
()
=>
null
});
return
Vue
.
nextTick
().
then
(()
=>
{
return
Vue
.
nextTick
().
then
(()
=>
{
getEditForm
(
wrapper
).
trigger
(
'
submit
'
);
expect
(
wrapper
.
emitted
().
onDropdownClick
[
0
]).
toEqual
([
status
]);
expect
(
wrapper
.
emitted
().
onStatusChange
[
0
]).
toEqual
([
status
]);
});
});
},
},
);
);
...
...
locale/gitlab.pot
View file @
4a8b8929
...
@@ -3850,9 +3850,6 @@ msgstr ""
...
@@ -3850,9 +3850,6 @@ msgstr ""
msgid "Choose which shards you wish to synchronize to this secondary node"
msgid "Choose which shards you wish to synchronize to this secondary node"
msgstr ""
msgstr ""
msgid "Choose which status most accurately reflects the current state of this issue:"
msgstr ""
msgid "Choose your framework"
msgid "Choose your framework"
msgstr ""
msgstr ""
...
@@ -18040,6 +18037,9 @@ msgstr ""
...
@@ -18040,6 +18037,9 @@ msgstr ""
msgid "Select groups to replicate"
msgid "Select groups to replicate"
msgstr ""
msgstr ""
msgid "Select health status"
msgstr ""
msgid "Select labels"
msgid "Select labels"
msgstr ""
msgstr ""
...
@@ -18576,16 +18576,22 @@ msgstr ""
...
@@ -18576,16 +18576,22 @@ msgstr ""
msgid "Side-by-side"
msgid "Side-by-side"
msgstr ""
msgstr ""
msgid "Sidebar|Assign health status"
msgstr ""
msgid "Sidebar|Change weight"
msgid "Sidebar|Change weight"
msgstr ""
msgstr ""
msgid "Sidebar|
None
"
msgid "Sidebar|
Health status
"
msgstr ""
msgstr ""
msgid "Sidebar|
Only numeral characters allowed
"
msgid "Sidebar|
No status
"
msgstr ""
msgstr ""
msgid "Sidebar|Status"
msgid "Sidebar|None"
msgstr ""
msgid "Sidebar|Only numeral characters allowed"
msgstr ""
msgstr ""
msgid "Sidebar|Weight"
msgid "Sidebar|Weight"
...
@@ -24957,9 +24963,6 @@ msgstr ""
...
@@ -24957,9 +24963,6 @@ msgstr ""
msgid "remove due date"
msgid "remove due date"
msgstr ""
msgstr ""
msgid "remove status"
msgstr ""
msgid "remove weight"
msgid "remove weight"
msgstr ""
msgstr ""
...
...
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