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
3a7e132d
Commit
3a7e132d
authored
Sep 10, 2020
by
Martin Wortschack
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '221205-delete-value-stream-mvc-fe' into 'master'
UI VSA delete value stream See merge request gitlab-org/gitlab!40927
parents
884a1e61
940b4003
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
240 additions
and
35 deletions
+240
-35
doc/user/analytics/img/delete_value_stream_v13.4.png
doc/user/analytics/img/delete_value_stream_v13.4.png
+0
-0
doc/user/analytics/value_stream_analytics.md
doc/user/analytics/value_stream_analytics.md
+13
-0
ee/app/assets/javascripts/analytics/cycle_analytics/components/value_stream_select.vue
...lytics/cycle_analytics/components/value_stream_select.vue
+54
-3
ee/changelogs/unreleased/221205-delete-value-stream-mvc-fe.yml
...angelogs/unreleased/221205-delete-value-stream-mvc-fe.yml
+5
-0
ee/spec/features/analytics/cycle_analytics/cycle_analytics_spec.rb
...eatures/analytics/cycle_analytics/cycle_analytics_spec.rb
+51
-13
ee/spec/frontend/analytics/cycle_analytics/components/value_stream_select_spec.js
...cs/cycle_analytics/components/value_stream_select_spec.js
+105
-19
locale/gitlab.pot
locale/gitlab.pot
+12
-0
No files found.
doc/user/analytics/img/delete_value_stream_v13.4.png
0 → 100644
View file @
3a7e132d
28.7 KB
doc/user/analytics/value_stream_analytics.md
View file @
3a7e132d
...
...
@@ -313,6 +313,19 @@ To create a value stream:
![
New value stream
](
img/new_value_stream_v13_3.png
"Creating a new value stream"
)
### Deleting a value stream
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/221205) in GitLab 13.4.
To delete a custom value stream:
1.
Navigate to your group's
**Analytics > Value Stream**
.
1.
Click the Value stream dropdown and select the value stream you would like to delete.
1.
Click the
**Delete (name of value stream)**
.
1.
Click the
**Delete**
button to confirm.
![
Delete value stream
](
img/delete_value_stream_v13.4.png
"Deleting a custom value stream"
)
### Disabling custom value streams
Custom value streams are enabled by default. If you have a self-managed instance, an
...
...
ee/app/assets/javascripts/analytics/cycle_analytics/components/value_stream_select.vue
View file @
3a7e132d
<
script
>
import
{
GlAlert
,
GlButton
,
GlNewDropdown
as
GlDropdown
,
GlNewDropdownItem
as
GlDropdownItem
,
...
...
@@ -33,6 +34,7 @@ const validate = ({ name }) => {
export
default
{
components
:
{
GlAlert
,
GlButton
,
GlDropdown
,
GlDropdownItem
,
...
...
@@ -53,11 +55,16 @@ export default {
},
computed
:
{
...
mapState
({
isLoading
:
'
isCreatingValueStream
'
,
isDeleting
:
'
isDeletingValueStream
'
,
isCreating
:
'
isCreatingValueStream
'
,
deleteValueStreamError
:
'
deleteValueStreamError
'
,
initialFormErrors
:
'
createValueStreamErrors
'
,
data
:
'
valueStreams
'
,
selectedValueStream
:
'
selectedValueStream
'
,
}),
isLoading
()
{
return
this
.
isDeleting
||
this
.
isCreating
;
},
isValid
()
{
return
!
this
.
errors
.
name
?.
length
;
},
...
...
@@ -73,10 +80,21 @@ export default {
selectedValueStreamId
()
{
return
this
.
selectedValueStream
?.
id
||
null
;
},
canDeleteSelectedStage
()
{
return
this
.
selectedValueStream
?.
isCustom
||
false
;
},
hasFormErrors
()
{
const
{
initialFormErrors
}
=
this
;
return
Boolean
(
Object
.
keys
(
initialFormErrors
).
length
);
},
deleteSelectedText
()
{
return
sprintf
(
__
(
'
Delete %{name}
'
),
{
name
:
this
.
selectedValueStreamName
});
},
deleteConfirmationText
()
{
return
sprintf
(
__
(
'
Are you sure you want to delete "%{name}" Value Stream?
'
),
{
name
:
this
.
selectedValueStreamName
,
});
},
},
watch
:
{
initialFormErrors
(
newErrors
=
{})
{
...
...
@@ -92,7 +110,7 @@ export default {
}
},
methods
:
{
...
mapActions
([
'
createValueStream
'
,
'
setSelectedValueStream
'
]),
...
mapActions
([
'
createValueStream
'
,
'
setSelectedValueStream
'
,
'
deleteValueStream
'
]),
onSubmit
()
{
const
{
name
}
=
this
;
return
this
.
createValueStream
({
name
}).
then
(()
=>
{
...
...
@@ -114,6 +132,16 @@ export default {
onSelect
(
id
)
{
this
.
setSelectedValueStream
(
id
);
},
onDelete
()
{
const
name
=
this
.
selectedValueStreamName
;
return
this
.
deleteValueStream
(
this
.
selectedValueStreamId
).
then
(()
=>
{
if
(
!
this
.
deleteValueStreamError
)
{
this
.
$toast
.
show
(
sprintf
(
__
(
"
'%{name}' Value Stream deleted
"
),
{
name
}),
{
position
:
'
top-center
'
,
});
}
});
},
},
};
</
script
>
...
...
@@ -137,12 +165,19 @@ export default {
<gl-dropdown-item
v-gl-modal-directive=
"'create-value-stream-modal'"
@
click=
"onHandleInput"
>
{{
__
(
'
Create new Value Stream
'
)
}}
</gl-dropdown-item>
<gl-dropdown-item
v-if=
"canDeleteSelectedStage"
v-gl-modal-directive=
"'delete-value-stream-modal'"
variant=
"danger"
data-testid=
"delete-value-stream"
>
{{
deleteSelectedText
}}
</gl-dropdown-item
>
</gl-dropdown>
<gl-button
v-else
v-gl-modal-directive=
"'create-value-stream-modal'"
@
click=
"onHandleInput"
>
{{
__
(
'
Create new Value Stream
'
)
}}
</gl-button>
<gl-modal
ref=
"
modal"
data-testid=
"create-value-stream-
modal"
modal-id=
"create-value-stream-modal"
:title=
"__('Value Stream Name')"
:action-primary=
"
{
...
...
@@ -175,5 +210,21 @@ export default {
/>
</gl-form-group>
</gl-modal>
<gl-modal
data-testid=
"delete-value-stream-modal"
modal-id=
"delete-value-stream-modal"
:title=
"__('Delete Value Stream')"
:action-primary=
"
{
text: __('Delete'),
attributes: [{ variant: 'danger' }, { loading: isLoading }],
}"
:action-cancel="{ text: __('Cancel') }"
@primary.prevent="onDelete"
>
<gl-alert
v-if=
"deleteValueStreamError"
variant=
"danger"
>
{{
deleteValueStreamError
}}
</gl-alert>
<p>
{{
deleteConfirmationText
}}
</p>
</gl-modal>
</gl-form>
</
template
>
ee/changelogs/unreleased/221205-delete-value-stream-mvc-fe.yml
0 → 100644
View file @
3a7e132d
---
title
:
Delete custom value streams
merge_request
:
40927
author
:
type
:
added
ee/spec/features/analytics/cycle_analytics/cycle_analytics_spec.rb
View file @
3a7e132d
...
...
@@ -562,27 +562,65 @@ RSpec.describe 'Group Value Stream Analytics', :js do
end
end
describe
'Create value stream'
,
:js
do
let
(
:custom_value_stream_name
)
{
"Test value stream"
}
def
toggle_value_stream_dropdown
value_stream_dropdown
.
click
end
def
select_value_stream
(
value_stream_name
)
toggle_value_stream_dropdown
page
.
find
(
'[data-testid="dropdown-value-streams"]'
).
all
(
'li button'
).
find
{
|
item
|
item
.
text
==
value_stream_name
.
to_s
}.
click
wait_for_requests
end
describe
'Multiple value streams'
,
:js
do
let
(
:value_stream_dropdown
)
{
page
.
find
(
value_stream_selector
)
}
let!
(
:default_value_stream
)
{
create
(
:cycle_analytics_group_value_stream
,
group:
group
,
name:
'default'
)
}
de
f
toggle_value_stream_dropdown
value_stream_dropdown
.
click
end
de
scribe
'Create value stream'
do
before
do
select_group
before
do
select_group
wait_for_requests
end
it
'can create a value stream'
do
custom_value_stream_name
=
"New created value stream"
toggle_value_stream_dropdown
page
.
find_button
(
_
(
'Create new Value Stream'
)).
click
fill_in
'create-value-stream-name'
,
with:
custom_value_stream_name
page
.
find_button
(
_
(
'Create Value Stream'
)).
click
wait_for_requests
expect
(
page
).
to
have_text
(
_
(
"'%{name}' Value Stream created"
)
%
{
name:
custom_value_stream_name
})
end
end
it
'can create a value stream'
do
toggle_value_stream_dropdown
describe
'Delete value stream'
do
let
(
:custom_value_stream_name
)
{
"Test value stream"
}
before
do
value_stream
=
create
(
:cycle_analytics_group_value_stream
,
name:
custom_value_stream_name
,
group:
group
)
create
(
:cycle_analytics_group_stage
,
value_stream:
value_stream
)
select_group
page
.
find_button
(
_
(
'Create new Value Stream'
)).
click
wait_for_requests
end
it
'can delete a value stream'
do
select_value_stream
(
custom_value_stream_name
)
fill_in
'create-value-stream-name'
,
with:
custom_value_stream_name
page
.
find_button
(
_
(
'Create Value Stream'
)).
click
toggle_value_stream_dropdown
expect
(
page
).
to
have_text
(
_
(
"'%{name}' Value Stream created"
)
%
{
name:
custom_value_stream_name
})
page
.
find_button
(
_
(
'Delete %{name}'
)
%
{
name:
custom_value_stream_name
}).
click
page
.
find_button
(
_
(
'Delete'
)).
click
wait_for_requests
expect
(
page
).
to
have_text
(
_
(
"'%{name}' Value Stream deleted"
)
%
{
name:
custom_value_stream_name
})
end
end
end
end
ee/spec/frontend/analytics/cycle_analytics/components/value_stream_select_spec.js
View file @
3a7e132d
import
Vuex
from
'
vuex
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
GlButton
,
Gl
Modal
,
Gl
NewDropdown
as
GlDropdown
,
GlFormGroup
}
from
'
@gitlab/ui
'
;
import
{
GlButton
,
GlNewDropdown
as
GlDropdown
,
GlFormGroup
}
from
'
@gitlab/ui
'
;
import
ValueStreamSelect
from
'
ee/analytics/cycle_analytics/components/value_stream_select.vue
'
;
import
{
valueStreams
}
from
'
../mock_data
'
;
import
{
findDropdownItemText
}
from
'
../helpers
'
;
...
...
@@ -12,21 +12,28 @@ describe('ValueStreamSelect', () => {
let
wrapper
=
null
;
const
createValueStreamMock
=
jest
.
fn
(()
=>
Promise
.
resolve
());
const
deleteValueStreamMock
=
jest
.
fn
(()
=>
Promise
.
resolve
());
const
mockEvent
=
{
preventDefault
:
jest
.
fn
()
};
const
mockToastShow
=
jest
.
fn
();
const
streamName
=
'
Cool stream
'
;
const
selectedValueStream
=
valueStreams
[
0
];
const
createValueStreamErrors
=
{
name
:
[
'
Name field required
'
]
};
const
deleteValueStreamError
=
'
Cannot delete default value stream
'
;
const
fakeStore
=
({
initialState
=
{}
})
=>
new
Vuex
.
Store
({
state
:
{
isLoading
:
false
,
isCreatingValueStream
:
false
,
isDeletingValueStream
:
false
,
createValueStreamErrors
:
{},
deleteValueStreamError
:
null
,
valueStreams
:
[],
selectedValueStream
:
{},
...
initialState
,
},
actions
:
{
createValueStream
:
createValueStreamMock
,
deleteValueStream
:
deleteValueStreamMock
,
},
});
...
...
@@ -46,12 +53,14 @@ describe('ValueStreamSelect', () => {
},
});
const
findModal
=
()
=>
wrapper
.
find
(
GlModal
);
const
submitButtonDisabledState
=
()
=>
findModal
().
props
(
'
actionPrimary
'
).
attributes
[
1
].
disabled
;
const
submitForm
=
()
=>
findModal
().
vm
.
$emit
(
'
primary
'
,
mockEvent
);
const
findModal
=
modal
=>
wrapper
.
find
(
`[data-testid="
${
modal
}
-value-stream-modal"]`
);
const
createSubmitButtonDisabledState
=
()
=>
findModal
(
'
create
'
).
props
(
'
actionPrimary
'
).
attributes
[
1
].
disabled
;
const
submitModal
=
modal
=>
findModal
(
modal
).
vm
.
$emit
(
'
primary
'
,
mockEvent
);
const
findSelectValueStreamDropdown
=
()
=>
wrapper
.
find
(
GlDropdown
);
const
findSelectValueStreamDropdownOptions
=
_wrapper
=>
findDropdownItemText
(
_wrapper
);
const
findCreateValueStreamButton
=
()
=>
wrapper
.
find
(
GlButton
);
const
findDeleteValueStreamButton
=
()
=>
wrapper
.
find
(
'
[data-testid="delete-value-stream"]
'
);
const
findFormGroup
=
()
=>
wrapper
.
find
(
GlFormGroup
);
beforeEach
(()
=>
{
...
...
@@ -81,6 +90,34 @@ describe('ValueStreamSelect', () => {
expect
(
opts
).
toContain
(
vs
);
});
});
describe
(
'
with a selected value stream
'
,
()
=>
{
it
(
'
renders a delete option for custom value streams
'
,
()
=>
{
wrapper
=
createComponent
({
initialState
:
{
valueStreams
,
selectedValueStream
:
{
...
selectedValueStream
,
isCustom
:
true
,
},
},
});
expect
(
findDeleteValueStreamButton
().
exists
()).
toBe
(
true
);
expect
(
findDeleteValueStreamButton
().
text
()).
toBe
(
`Delete
${
selectedValueStream
.
name
}
`
);
});
it
(
'
does not render a delete option for default value streams
'
,
()
=>
{
wrapper
=
createComponent
({
initialState
:
{
valueStreams
,
selectedValueStream
,
},
});
expect
(
findDeleteValueStreamButton
().
exists
()).
toBe
(
false
);
});
});
});
describe
(
'
Only the default value stream available
'
,
()
=>
{
...
...
@@ -121,29 +158,27 @@ describe('ValueStreamSelect', () => {
describe
(
'
Create value stream form
'
,
()
=>
{
it
(
'
submit button is disabled
'
,
()
=>
{
expect
(
s
ubmitButtonDisabledState
()).
toBe
(
true
);
expect
(
createS
ubmitButtonDisabledState
()).
toBe
(
true
);
});
describe
(
'
form errors
'
,
()
=>
{
const
fieldErrors
=
[
'
already exists
'
,
'
is required
'
];
beforeEach
(()
=>
{
wrapper
=
createComponent
({
data
:
{
name
:
streamName
},
initialState
:
{
createValueStreamErrors
:
{
name
:
fieldErrors
,
},
createValueStreamErrors
,
},
});
});
it
(
'
renders the error
'
,
()
=>
{
expect
(
findFormGroup
().
attributes
(
'
invalid-feedback
'
)).
toEqual
(
fieldErrors
.
join
(
'
\n
'
));
expect
(
findFormGroup
().
attributes
(
'
invalid-feedback
'
)).
toEqual
(
createValueStreamErrors
.
name
.
join
(
'
\n
'
),
);
});
it
(
'
submit button is disabled
'
,
()
=>
{
expect
(
s
ubmitButtonDisabledState
()).
toBe
(
true
);
expect
(
createS
ubmitButtonDisabledState
()).
toBe
(
true
);
});
});
...
...
@@ -153,12 +188,12 @@ describe('ValueStreamSelect', () => {
});
it
(
'
submit button is enabled
'
,
()
=>
{
expect
(
s
ubmitButtonDisabledState
()).
toBe
(
false
);
expect
(
createS
ubmitButtonDisabledState
()).
toBe
(
false
);
});
describe
(
'
form submitted successfully
'
,
()
=>
{
beforeEach
(()
=>
{
submit
Form
(
);
submit
Modal
(
'
create
'
);
});
it
(
'
calls the "createValueStream" event when submitted
'
,
()
=>
{
...
...
@@ -179,15 +214,19 @@ describe('ValueStreamSelect', () => {
});
describe
(
'
form submission fails
'
,
()
=>
{
const
createValueStreamMockFail
=
jest
.
fn
(()
=>
Promise
.
reject
());
beforeEach
(()
=>
{
wrapper
=
createComponent
({
data
:
{
name
:
streamName
},
actions
:
{
createValueStream
:
()
=>
createValueStreamMockFail
,
initialState
:
{
createValueStream
Errors
,
},
});
submitModal
(
'
create
'
);
});
it
(
'
calls the createValueStream action
'
,
()
=>
{
expect
(
createValueStreamMock
).
toHaveBeenCalled
();
});
it
(
'
does not clear the name field
'
,
()
=>
{
...
...
@@ -200,4 +239,51 @@ describe('ValueStreamSelect', () => {
});
});
});
describe
(
'
Delete value stream modal
'
,
()
=>
{
describe
(
'
succeeds
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
initialState
:
{
valueStreams
,
selectedValueStream
:
{
...
selectedValueStream
,
isCustom
:
true
,
},
},
});
submitModal
(
'
delete
'
);
});
it
(
'
calls the "deleteValueStream" event when submitted
'
,
()
=>
{
expect
(
deleteValueStreamMock
).
toHaveBeenCalledWith
(
expect
.
any
(
Object
),
selectedValueStream
.
id
,
);
});
it
(
'
displays a toast message
'
,
()
=>
{
expect
(
mockToastShow
).
toHaveBeenCalledWith
(
`'
${
selectedValueStream
.
name
}
' Value Stream deleted`
,
{
position
:
'
top-center
'
,
},
);
});
});
describe
(
'
fails
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
({
data
:
{
name
:
streamName
},
initialState
:
{
deleteValueStreamError
},
});
});
it
(
'
does not display a toast message
'
,
()
=>
{
expect
(
mockToastShow
).
not
.
toHaveBeenCalled
();
});
});
});
});
locale/gitlab.pot
View file @
3a7e132d
...
...
@@ -872,6 +872,9 @@ msgstr ""
msgid "'%{name}' Value Stream created"
msgstr ""
msgid "'%{name}' Value Stream deleted"
msgstr ""
msgid "'%{name}' stage already exists"
msgstr ""
...
...
@@ -3250,6 +3253,9 @@ msgstr ""
msgid "Are you sure you want to close this blocked issue?"
msgstr ""
msgid "Are you sure you want to delete \"%{name}\" Value Stream?"
msgstr ""
msgid "Are you sure you want to delete %{name}?"
msgstr ""
...
...
@@ -8089,12 +8095,18 @@ msgstr ""
msgid "Delete"
msgstr ""
msgid "Delete %{name}"
msgstr ""
msgid "Delete Comment"
msgstr ""
msgid "Delete Snippet"
msgstr ""
msgid "Delete Value Stream"
msgstr ""
msgid "Delete account"
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