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
4e53ae6c
Commit
4e53ae6c
authored
Dec 01, 2021
by
Florie Guibert
Committed by
Natalia Tepluhina
Dec 01, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Clean up labels_widget feature flag
parent
a9a8dd14
Changes
32
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
140 additions
and
658 deletions
+140
-658
app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
...ets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
+6
-0
app/assets/javascripts/boards/components/board_content_sidebar.vue
...s/javascripts/boards/components/board_content_sidebar.vue
+0
-4
app/assets/javascripts/boards/stores/actions.js
app/assets/javascripts/boards/stores/actions.js
+0
-28
app/assets/javascripts/right_sidebar.js
app/assets/javascripts/right_sidebar.js
+0
-9
app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue
.../javascripts/sidebar/components/labels/sidebar_labels.vue
+1
-153
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
...nents/sidebar/labels_select_widget/labels_select_root.vue
+2
-1
app/controllers/groups/boards_controller.rb
app/controllers/groups/boards_controller.rb
+0
-1
app/controllers/projects/boards_controller.rb
app/controllers/projects/boards_controller.rb
+0
-1
app/controllers/projects/issues_controller.rb
app/controllers/projects/issues_controller.rb
+0
-1
app/controllers/projects/merge_requests_controller.rb
app/controllers/projects/merge_requests_controller.rb
+0
-1
config/feature_flags/development/labels_widget.yml
config/feature_flags/development/labels_widget.yml
+0
-8
ee/app/assets/javascripts/behaviors/shortcuts/shortcuts_epic.js
.../assets/javascripts/behaviors/shortcuts/shortcuts_epic.js
+2
-12
ee/app/assets/javascripts/boards/components/epic_board_content_sidebar.vue
...ascripts/boards/components/epic_board_content_sidebar.vue
+0
-4
ee/app/assets/javascripts/boards/graphql/update_epic_labels.mutation.graphql
...cripts/boards/graphql/update_epic_labels.mutation.graphql
+0
-16
ee/app/assets/javascripts/boards/stores/actions.js
ee/app/assets/javascripts/boards/stores/actions.js
+1
-28
ee/app/assets/javascripts/epic/components/epic_form.vue
ee/app/assets/javascripts/epic/components/epic_form.vue
+2
-45
ee/app/assets/javascripts/epic/components/epic_sidebar.vue
ee/app/assets/javascripts/epic/components/epic_sidebar.vue
+0
-9
ee/app/controllers/groups/epic_boards_controller.rb
ee/app/controllers/groups/epic_boards_controller.rb
+0
-3
ee/app/controllers/groups/epics_controller.rb
ee/app/controllers/groups/epics_controller.rb
+0
-1
ee/spec/frontend/boards/components/__snapshots__/board_content_sidebar_spec.js.snap
...mponents/__snapshots__/board_content_sidebar_spec.js.snap
+22
-2
ee/spec/frontend/boards/components/board_content_sidebar_spec.js
.../frontend/boards/components/board_content_sidebar_spec.js
+5
-1
ee/spec/frontend/boards/components/epic_board_content_sidebar_spec.js
...tend/boards/components/epic_board_content_sidebar_spec.js
+6
-3
ee/spec/frontend/boards/stores/actions_spec.js
ee/spec/frontend/boards/stores/actions_spec.js
+21
-71
ee/spec/frontend/epic/components/epic_form_spec.js
ee/spec/frontend/epic/components/epic_form_spec.js
+5
-3
qa/qa/page/component/issuable/sidebar.rb
qa/qa/page/component/issuable/sidebar.rb
+0
-9
qa/qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb
...ser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb
+0
-5
spec/features/issues/user_edits_issue_spec.rb
spec/features/issues/user_edits_issue_spec.rb
+1
-2
spec/features/labels_hierarchy_spec.rb
spec/features/labels_hierarchy_spec.rb
+4
-6
spec/frontend/boards/components/board_content_sidebar_spec.js
.../frontend/boards/components/board_content_sidebar_spec.js
+6
-3
spec/frontend/boards/stores/actions_spec.js
spec/frontend/boards/stores/actions_spec.js
+20
-70
spec/frontend/sidebar/sidebar_labels_spec.js
spec/frontend/sidebar/sidebar_labels_spec.js
+28
-153
spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js
...s/sidebar/labels_select_widget/labels_select_root_spec.js
+8
-5
No files found.
app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
View file @
4e53ae6c
...
...
@@ -3,6 +3,7 @@ import Mousetrap from 'mousetrap';
import
{
clickCopyToClipboardButton
}
from
'
~/behaviors/copy_to_clipboard
'
;
import
{
getSelectedFragment
}
from
'
~/lib/utils/common_utils
'
;
import
{
isElementVisible
}
from
'
~/lib/utils/dom_utils
'
;
import
{
DEBOUNCE_DROPDOWN_DELAY
}
from
'
~/vue_shared/components/sidebar/labels_select_widget/constants
'
;
import
Sidebar
from
'
../../right_sidebar
'
;
import
{
CopyAsGFM
}
from
'
../markdown/copy_as_gfm
'
;
import
{
...
...
@@ -114,6 +115,11 @@ export default class ShortcutsIssuable extends Shortcuts {
static
openSidebarDropdown
(
name
)
{
Sidebar
.
instance
.
openDropdown
(
name
);
// Wait for the sidebar to trigger('click') open
// so it doesn't cause our dropdown to close preemptively
setTimeout
(()
=>
{
document
.
querySelector
(
`.block.
${
name
}
.shortcut-sidebar-dropdown-toggle`
).
click
();
},
DEBOUNCE_DROPDOWN_DELAY
);
return
false
;
}
...
...
app/assets/javascripts/boards/components/board_content_sidebar.vue
View file @
4e53ae6c
...
...
@@ -4,7 +4,6 @@ import { MountingPortal } from 'portal-vue';
import
{
mapState
,
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
SidebarDropdownWidget
from
'
ee_else_ce/sidebar/components/sidebar_dropdown_widget.vue
'
;
import
{
__
,
sprintf
}
from
'
~/locale
'
;
import
BoardSidebarLabelsSelect
from
'
~/boards/components/sidebar/board_sidebar_labels_select.vue
'
;
import
BoardSidebarTimeTracker
from
'
~/boards/components/sidebar/board_sidebar_time_tracker.vue
'
;
import
BoardSidebarTitle
from
'
~/boards/components/sidebar/board_sidebar_title.vue
'
;
import
{
ISSUABLE
}
from
'
~/boards/constants
'
;
...
...
@@ -26,7 +25,6 @@ export default {
SidebarDateWidget
,
SidebarConfidentialityWidget
,
BoardSidebarTimeTracker
,
BoardSidebarLabelsSelect
,
SidebarLabelsWidget
,
SidebarSubscriptionsWidget
,
SidebarDropdownWidget
,
...
...
@@ -210,7 +208,6 @@ export default {
data-testid=
"sidebar-due-date"
/>
<sidebar-labels-widget
v-if=
"glFeatures.labelsWidget"
class=
"block labels"
data-testid=
"sidebar-labels"
:iid=
"activeBoardItem.iid"
...
...
@@ -230,7 +227,6 @@ export default {
>
{{ __('None') }}
</sidebar-labels-widget>
<board-sidebar-labels-select
v-else
class=
"block labels"
/>
<sidebar-weight-widget
v-if=
"weightFeatureAvailable"
:iid=
"activeBoardItem.iid"
...
...
app/assets/javascripts/boards/stores/actions.js
View file @
4e53ae6c
...
...
@@ -39,7 +39,6 @@ import boardLabelsQuery from '../graphql/board_labels.query.graphql';
import
groupBoardMilestonesQuery
from
'
../graphql/group_board_milestones.query.graphql
'
;
import
groupProjectsQuery
from
'
../graphql/group_projects.query.graphql
'
;
import
issueCreateMutation
from
'
../graphql/issue_create.mutation.graphql
'
;
import
issueSetLabelsMutation
from
'
../graphql/issue_set_labels.mutation.graphql
'
;
import
listsIssuesQuery
from
'
../graphql/lists_issues.query.graphql
'
;
import
projectBoardMilestonesQuery
from
'
../graphql/project_board_milestones.query.graphql
'
;
...
...
@@ -609,33 +608,6 @@ export default {
setActiveIssueLabels
:
async
({
commit
,
getters
},
input
)
=>
{
const
{
activeBoardItem
}
=
getters
;
if
(
!
gon
.
features
?.
labelsWidget
)
{
const
{
data
}
=
await
gqlClient
.
mutate
({
mutation
:
issueSetLabelsMutation
,
variables
:
{
input
:
{
iid
:
input
.
iid
||
String
(
activeBoardItem
.
iid
),
labelIds
:
input
.
labelsId
??
undefined
,
addLabelIds
:
input
.
addLabelIds
??
[],
removeLabelIds
:
input
.
removeLabelIds
??
[],
projectPath
:
input
.
projectPath
,
},
},
});
if
(
data
.
updateIssue
?.
errors
?.
length
>
0
)
{
throw
new
Error
(
data
.
updateIssue
.
errors
);
}
commit
(
types
.
UPDATE_BOARD_ITEM_BY_ID
,
{
itemId
:
data
.
updateIssue
?.
issue
?.
id
||
activeBoardItem
.
id
,
prop
:
'
labels
'
,
value
:
data
.
updateIssue
?.
issue
?.
labels
.
nodes
,
});
return
;
}
let
labels
=
input
?.
labels
||
[];
if
(
input
.
removeLabelIds
)
{
labels
=
activeBoardItem
.
labels
.
filter
(
...
...
app/assets/javascripts/right_sidebar.js
View file @
4e53ae6c
...
...
@@ -3,7 +3,6 @@
import
$
from
'
jquery
'
;
import
Cookies
from
'
js-cookie
'
;
import
{
hide
,
fixTitle
}
from
'
~/tooltips
'
;
import
{
DEBOUNCE_DROPDOWN_DELAY
}
from
'
~/vue_shared/components/sidebar/labels_select_widget/constants
'
;
import
createFlash
from
'
./flash
'
;
import
axios
from
'
./lib/utils/axios_utils
'
;
import
{
sprintf
,
s__
,
__
}
from
'
./locale
'
;
...
...
@@ -127,14 +126,6 @@ Sidebar.prototype.openDropdown = function (blockOrName) {
this
.
setCollapseAfterUpdate
(
$block
);
this
.
toggleSidebar
(
'
open
'
);
}
// Wait for the sidebar to trigger('click') open
// so it doesn't cause our dropdown to close preemptively
setTimeout
(()
=>
{
if
(
!
gon
.
features
?.
labelsWidget
&&
!
$block
.
hasClass
(
'
labels-select-wrapper
'
))
{
$block
.
find
(
'
.js-sidebar-dropdown-toggle
'
).
trigger
(
'
click
'
);
}
},
DEBOUNCE_DROPDOWN_DELAY
);
};
Sidebar
.
prototype
.
setCollapseAfterUpdate
=
function
(
$block
)
{
...
...
app/assets/javascripts/sidebar/components/labels/sidebar_labels.vue
View file @
4e53ae6c
<
script
>
import
$
from
'
jquery
'
;
import
{
camelCase
,
difference
,
union
}
from
'
lodash
'
;
import
updateIssueLabelsMutation
from
'
~/boards/graphql/issue_set_labels.mutation.graphql
'
;
import
createFlash
from
'
~/flash
'
;
import
{
getIdFromGraphQLId
,
MutationOperationMode
}
from
'
~/graphql_shared/utils
'
;
import
{
IssuableType
}
from
'
~/issue_show/constants
'
;
import
{
__
}
from
'
~/locale
'
;
import
updateMergeRequestLabelsMutation
from
'
~/sidebar/queries/update_merge_request_labels.mutation.graphql
'
;
import
{
toLabelGid
}
from
'
~/sidebar/utils
'
;
import
{
DropdownVariant
}
from
'
~/vue_shared/components/sidebar/labels_select_vue/constants
'
;
import
LabelsSelect
from
'
~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
'
;
import
LabelsSelectWidget
from
'
~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
'
;
import
{
LabelType
}
from
'
~/vue_shared/components/sidebar/labels_select_widget/constants
'
;
import
glFeatureFlagMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
const
mutationMap
=
{
[
IssuableType
.
Issue
]:
{
mutation
:
updateIssueLabelsMutation
,
mutationName
:
'
updateIssue
'
,
},
[
IssuableType
.
MergeRequest
]:
{
mutation
:
updateMergeRequestLabelsMutation
,
mutationName
:
'
mergeRequestSetLabels
'
,
},
};
export
default
{
components
:
{
LabelsSelect
,
LabelsSelectWidget
,
},
variant
:
DropdownVariant
.
Sidebar
,
mixins
:
[
glFeatureFlagMixin
()],
inject
:
[
'
allowLabelCreate
'
,
'
allowLabelEdit
'
,
'
allowScopedLabels
'
,
'
iid
'
,
'
fullPath
'
,
'
initiallySelectedLabels
'
,
'
issuableType
'
,
'
labelsFetchPath
'
,
'
labelsManagePath
'
,
'
projectIssuesPath
'
,
'
projectPath
'
,
],
inject
:
[
'
allowLabelEdit
'
,
'
iid
'
,
'
fullPath
'
,
'
issuableType
'
,
'
projectIssuesPath
'
],
data
()
{
return
{
isLabelsSelectInProgress
:
false
,
selectedLabels
:
this
.
initiallySelectedLabels
,
LabelType
,
};
},
methods
:
{
handleDropdownClose
()
{
$
(
this
.
$el
).
trigger
(
'
hidden.gl.dropdown
'
);
},
getUpdateVariables
(
labels
)
{
let
labelIds
=
[];
if
(
this
.
glFeatures
.
labelsWidget
)
{
labelIds
=
labels
.
map
(({
id
})
=>
toLabelGid
(
id
));
}
else
{
const
currentLabelIds
=
this
.
selectedLabels
.
map
((
label
)
=>
label
.
id
);
const
userAddedLabelIds
=
labels
.
filter
((
label
)
=>
label
.
set
).
map
((
label
)
=>
label
.
id
);
const
userRemovedLabelIds
=
labels
.
filter
((
label
)
=>
!
label
.
set
).
map
((
label
)
=>
label
.
id
);
labelIds
=
difference
(
union
(
currentLabelIds
,
userAddedLabelIds
),
userRemovedLabelIds
).
map
(
toLabelGid
,
);
}
switch
(
this
.
issuableType
)
{
case
IssuableType
.
Issue
:
return
{
iid
:
this
.
iid
,
projectPath
:
this
.
projectPath
,
labelIds
,
};
case
IssuableType
.
MergeRequest
:
return
{
iid
:
this
.
iid
,
labelIds
,
operationMode
:
MutationOperationMode
.
Replace
,
projectPath
:
this
.
projectPath
,
};
default
:
return
{};
}
},
handleUpdateSelectedLabels
(
dropdownLabels
)
{
this
.
updateSelectedLabels
(
this
.
getUpdateVariables
(
dropdownLabels
));
},
getRemoveVariables
(
labelId
)
{
switch
(
this
.
issuableType
)
{
case
IssuableType
.
Issue
:
return
{
iid
:
this
.
iid
,
projectPath
:
this
.
projectPath
,
removeLabelIds
:
[
labelId
],
};
case
IssuableType
.
MergeRequest
:
return
{
iid
:
this
.
iid
,
labelIds
:
[
toLabelGid
(
labelId
)],
operationMode
:
MutationOperationMode
.
Remove
,
projectPath
:
this
.
projectPath
,
};
default
:
return
{};
}
},
handleLabelRemove
(
labelId
)
{
this
.
updateSelectedLabels
(
this
.
getRemoveVariables
(
labelId
));
},
updateSelectedLabels
(
inputVariables
)
{
this
.
isLabelsSelectInProgress
=
true
;
this
.
$apollo
.
mutate
({
mutation
:
mutationMap
[
this
.
issuableType
].
mutation
,
variables
:
{
input
:
inputVariables
},
})
.
then
(({
data
})
=>
{
const
{
mutationName
}
=
mutationMap
[
this
.
issuableType
];
if
(
data
[
mutationName
]?.
errors
?.
length
)
{
throw
new
Error
();
}
const
issuableType
=
camelCase
(
this
.
issuableType
);
this
.
selectedLabels
=
data
[
mutationName
]?.[
issuableType
]?.
labels
?.
nodes
?.
map
((
label
)
=>
({
...
label
,
id
:
getIdFromGraphQLId
(
label
.
id
),
}));
})
.
catch
(()
=>
createFlash
({
message
:
__
(
'
An error occurred while updating labels.
'
)
}))
.
finally
(()
=>
{
this
.
isLabelsSelectInProgress
=
false
;
});
},
},
};
</
script
>
<
template
>
<labels-select-widget
v-if=
"glFeatures.labelsWidget"
class=
"block labels js-labels-block"
:iid=
"iid"
:full-path=
"fullPath"
...
...
@@ -165,28 +37,4 @@ export default {
>
{{
__
(
'
None
'
)
}}
</labels-select-widget>
<labels-select
v-else
class=
"block labels js-labels-block"
:allow-label-remove=
"allowLabelEdit"
:allow-label-create=
"allowLabelCreate"
:allow-label-edit=
"allowLabelEdit"
:allow-multiselect=
"true"
:allow-scoped-labels=
"allowScopedLabels"
:footer-create-label-title=
"__('Create project label')"
:footer-manage-label-title=
"__('Manage project labels')"
:labels-create-title=
"__('Create project label')"
:labels-fetch-path=
"labelsFetchPath"
:labels-filter-base-path=
"projectIssuesPath"
:labels-manage-path=
"labelsManagePath"
:labels-select-in-progress=
"isLabelsSelectInProgress"
:selected-labels=
"selectedLabels"
:variant=
"$options.sidebar"
data-qa-selector=
"labels_block"
@
onDropdownClose=
"handleDropdownClose"
@
onLabelRemove=
"handleLabelRemove"
@
updateSelectedLabels=
"handleUpdateSelectedLabels"
>
{{
__
(
'
None
'
)
}}
</labels-select>
</
template
>
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
View file @
4e53ae6c
...
...
@@ -231,9 +231,10 @@ export default {
throw
new
Error
();
}
this
.
issuableLabels
=
data
[
mutationName
]?.[
this
.
issuableType
]?.
labels
?.
nodes
;
this
.
$emit
(
'
updateSelectedLabels
'
,
{
id
:
data
[
mutationName
]?.[
this
.
issuableType
]?.
id
,
labels
:
data
[
mutationName
]?.[
this
.
issuableType
]?.
labels
?.
node
s
,
labels
:
this
.
issuableLabel
s
,
});
})
.
catch
((
error
)
=>
...
...
app/controllers/groups/boards_controller.rb
View file @
4e53ae6c
...
...
@@ -11,7 +11,6 @@ class Groups::BoardsController < Groups::ApplicationController
push_frontend_feature_flag
(
:board_multi_select
,
group
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:swimlanes_buffered_rendering
,
group
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:iteration_cadences
,
group
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:labels_widget
,
group
,
default_enabled: :yaml
)
experiment
(
:prominent_create_board_btn
,
subject:
current_user
)
do
|
e
|
e
.
use
{
}
e
.
try
{
}
...
...
app/controllers/projects/boards_controller.rb
View file @
4e53ae6c
...
...
@@ -11,7 +11,6 @@ class Projects::BoardsController < Projects::ApplicationController
push_frontend_feature_flag
(
:issue_boards_filtered_search
,
project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:board_multi_select
,
project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:iteration_cadences
,
project
&
.
group
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:labels_widget
,
project
,
default_enabled: :yaml
)
experiment
(
:prominent_create_board_btn
,
subject:
current_user
)
do
|
e
|
e
.
use
{
}
e
.
try
{
}
...
...
app/controllers/projects/issues_controller.rb
View file @
4e53ae6c
...
...
@@ -51,7 +51,6 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag
(
:real_time_issue_sidebar
,
@project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:confidential_notes
,
@project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:issue_assignees_widget
,
@project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:labels_widget
,
@project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:paginated_issue_discussions
,
@project
,
default_enabled: :yaml
)
experiment
(
:invite_members_in_comment
,
namespace:
@project
.
root_ancestor
)
do
|
experiment_instance
|
...
...
app/controllers/projects/merge_requests_controller.rb
View file @
4e53ae6c
...
...
@@ -42,7 +42,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag
(
:restructured_mr_widget
,
project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:mr_changes_fluid_layout
,
project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:mr_attention_requests
,
project
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:labels_widget
,
project
,
default_enabled: :yaml
)
# Usage data feature flags
push_frontend_feature_flag
(
:users_expanding_widgets_usage_data
,
@project
,
default_enabled: :yaml
)
...
...
config/feature_flags/development/labels_widget.yml
deleted
100644 → 0
View file @
a9a8dd14
---
name
:
labels_widget
introduced_by_url
:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62898
rollout_issue_url
:
https://gitlab.com/gitlab-org/gitlab/-/issues/332327
milestone
:
'
14.0'
type
:
development
group
:
group::project management
default_enabled
:
false
ee/app/assets/javascripts/behaviors/shortcuts/shortcuts_epic.js
View file @
4e53ae6c
import
$
from
'
jquery
'
;
import
Cookies
from
'
js-cookie
'
;
import
Mousetrap
from
'
mousetrap
'
;
import
{
keysFor
,
...
...
@@ -8,7 +7,6 @@ import {
ISSUABLE_EDIT_DESCRIPTION
,
}
from
'
~/behaviors/shortcuts/keybindings
'
;
import
ShortcutsIssuable
from
'
~/behaviors/shortcuts/shortcuts_issuable
'
;
import
{
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
export
default
class
ShortcutsEpic
extends
ShortcutsIssuable
{
constructor
()
{
...
...
@@ -23,15 +21,7 @@ export default class ShortcutsEpic extends ShortcutsIssuable {
Mousetrap
.
bind
(
keysFor
(
ISSUABLE_EDIT_DESCRIPTION
),
ShortcutsIssuable
.
editIssue
);
}
static
openSidebarDropdown
(
$block
)
{
if
(
gon
.
features
?.
labelsWidget
)
{
document
.
dispatchEvent
(
new
Event
(
'
toggleSidebarRevealLabelsDropdown
'
));
return
;
}
if
(
parseBoolean
(
Cookies
.
get
(
'
collapsed_gutter
'
)))
{
document
.
dispatchEvent
(
new
Event
(
'
toggleSidebarRevealLabelsDropdown
'
));
}
else
{
$block
.
find
(
'
.js-sidebar-dropdown-toggle
'
).
get
(
0
).
dispatchEvent
(
new
Event
(
'
click
'
));
}
static
openSidebarDropdown
()
{
document
.
dispatchEvent
(
new
Event
(
'
toggleSidebarRevealLabelsDropdown
'
));
}
}
ee/app/assets/javascripts/boards/components/epic_board_content_sidebar.vue
View file @
4e53ae6c
...
...
@@ -3,7 +3,6 @@ import { GlDrawer } from '@gitlab/ui';
import
{
MountingPortal
}
from
'
portal-vue
'
;
import
{
mapState
,
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
SidebarAncestorsWidget
from
'
ee_component/sidebar/components/ancestors_tree/sidebar_ancestors_widget.vue
'
;
import
BoardSidebarLabelsSelect
from
'
~/boards/components/sidebar/board_sidebar_labels_select.vue
'
;
import
BoardSidebarTitle
from
'
~/boards/components/sidebar/board_sidebar_title.vue
'
;
import
{
ISSUABLE
}
from
'
~/boards/constants
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
...
...
@@ -19,7 +18,6 @@ export default {
components
:
{
GlDrawer
,
SidebarTodoWidget
,
BoardSidebarLabelsSelect
,
BoardSidebarTitle
,
SidebarLabelsWidget
,
SidebarConfidentialityWidget
,
...
...
@@ -113,7 +111,6 @@ export default {
:can-inherit=
"true"
/>
<sidebar-labels-widget
v-if=
"glFeatures.labelsWidget"
class=
"block labels"
data-testid=
"sidebar-labels"
:iid=
"activeBoardItem.iid"
...
...
@@ -130,7 +127,6 @@ export default {
>
{{
__
(
'
None
'
)
}}
</sidebar-labels-widget>
<board-sidebar-labels-select
v-else
class=
"labels"
/>
<sidebar-confidentiality-widget
:iid=
"activeBoardItem.iid"
:full-path=
"fullPath"
...
...
ee/app/assets/javascripts/boards/graphql/update_epic_labels.mutation.graphql
deleted
100644 → 0
View file @
a9a8dd14
mutation
updateBoardEpicLabels
(
$input
:
UpdateEpicInput
!)
{
updateEpic
(
input
:
$input
)
{
epic
{
id
labels
{
nodes
{
id
title
color
description
}
}
}
errors
}
}
ee/app/assets/javascripts/boards/stores/actions.js
View file @
4e53ae6c
...
...
@@ -36,7 +36,6 @@ import listUpdateLimitMetricsMutation from '../graphql/list_update_limit_metrics
import
listsEpicsQuery
from
'
../graphql/lists_epics.query.graphql
'
;
import
subGroupsQuery
from
'
../graphql/sub_groups.query.graphql
'
;
import
updateBoardEpicUserPreferencesMutation
from
'
../graphql/update_board_epic_user_preferences.mutation.graphql
'
;
import
updateEpicLabelsMutation
from
'
../graphql/update_epic_labels.mutation.graphql
'
;
import
*
as
types
from
'
./mutation_types
'
;
...
...
@@ -631,35 +630,9 @@ export default {
}
},
setActiveEpicLabels
:
async
({
commit
,
getters
,
state
},
input
)
=>
{
setActiveEpicLabels
:
async
({
commit
,
getters
},
input
)
=>
{
const
{
activeBoardItem
}
=
getters
;
if
(
!
gon
.
features
?.
labelsWidget
)
{
const
{
data
}
=
await
gqlClient
.
mutate
({
mutation
:
updateEpicLabelsMutation
,
variables
:
{
input
:
{
iid
:
String
(
activeBoardItem
.
iid
),
addLabelIds
:
input
.
addLabelIds
??
[],
removeLabelIds
:
input
.
removeLabelIds
??
[],
groupPath
:
state
.
fullPath
,
},
},
});
if
(
data
.
updateEpic
?.
errors
?.
length
>
0
)
{
throw
new
Error
(
data
.
updateEpic
.
errors
);
}
commit
(
typesCE
.
UPDATE_BOARD_ITEM_BY_ID
,
{
itemId
:
activeBoardItem
.
id
,
prop
:
'
labels
'
,
value
:
data
.
updateEpic
.
epic
.
labels
.
nodes
,
});
return
;
}
let
labels
=
input
?.
labels
||
[];
if
(
input
.
removeLabelIds
)
{
labels
=
activeBoardItem
.
labels
.
filter
(
...
...
ee/app/assets/javascripts/epic/components/epic_form.vue
View file @
4e53ae6c
...
...
@@ -13,10 +13,8 @@ import { formatDate } from '~/lib/utils/datetime_utility';
import
{
visitUrl
}
from
'
~/lib/utils/url_utility
'
;
import
{
s__
}
from
'
~/locale
'
;
import
MarkdownField
from
'
~/vue_shared/components/markdown/field.vue
'
;
import
LabelsSelectVue
from
'
~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
'
;
import
LabelsSelectWidget
from
'
~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
'
;
import
{
LabelType
}
from
'
~/vue_shared/components/sidebar/labels_select_widget/constants
'
;
import
glFeatureFlagMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
createEpic
from
'
../queries/createEpic.mutation.graphql
'
;
export
default
{
...
...
@@ -28,18 +26,9 @@ export default {
GlFormInput
,
GlFormGroup
,
MarkdownField
,
LabelsSelectVue
,
LabelsSelectWidget
,
},
mixins
:
[
glFeatureFlagMixin
()],
inject
:
[
'
groupPath
'
,
'
groupEpicsPath
'
,
'
labelsFetchPath
'
,
'
labelsManagePath
'
,
'
markdownPreviewPath
'
,
'
markdownDocsPath
'
,
],
inject
:
[
'
groupPath
'
,
'
groupEpicsPath
'
,
'
markdownPreviewPath
'
,
'
markdownDocsPath
'
],
data
()
{
return
{
title
:
''
,
...
...
@@ -113,20 +102,7 @@ export default {
this
.
startDateFixed
=
val
;
},
handleUpdateSelectedLabels
(
labels
)
{
if
(
this
.
glFeatures
.
labelsWidget
)
{
this
.
labels
=
labels
.
map
((
label
)
=>
({
...
label
,
id
:
getIdFromGraphQLId
(
label
.
id
)
}));
return
;
}
const
ids
=
[];
const
allLabels
=
[...
labels
,
...
this
.
labels
];
this
.
labels
=
allLabels
.
filter
((
label
)
=>
{
const
exists
=
ids
.
includes
(
label
.
id
);
ids
.
push
(
label
.
id
);
return
!
exists
&&
label
.
set
;
});
this
.
labels
=
labels
.
map
((
label
)
=>
({
...
label
,
id
:
getIdFromGraphQLId
(
label
.
id
)
}));
},
},
};
...
...
@@ -190,7 +166,6 @@ export default {
<hr
/>
<gl-form-group
:label=
"__('Labels')"
>
<labels-select-widget
v-if=
"glFeatures.labelsWidget"
class=
"block labels js-labels-block"
:full-path=
"groupPath"
:allow-label-create=
"true"
...
...
@@ -207,24 +182,6 @@ export default {
>
{{ __('None') }}
</labels-select-widget>
<labels-select-vue
v-else
:allow-label-edit=
"false"
:allow-label-create=
"true"
:allow-multiselect=
"true"
:allow-scoped-labels=
"false"
:selected-labels=
"labels"
:labels-fetch-path=
"labelsFetchPath"
:labels-manage-path=
"labelsManagePath"
:labels-filter-base-path=
"groupEpicsPath"
:labels-list-title=
"__('Select label')"
:dropdown-button-text=
"__('Choose labels')"
variant=
"embedded"
class=
"block labels js-labels-block"
@
updateSelectedLabels=
"handleUpdateSelectedLabels"
>
{{ __('None') }}
</labels-select-vue>
</gl-form-group>
<gl-form-group
:label=
"__('Start date')"
:description=
"$options.i18n.epicDatesHint"
>
<div
class=
"gl-display-inline-block gl-mr-2"
>
...
...
ee/app/assets/javascripts/epic/components/epic_sidebar.vue
View file @
4e53ae6c
...
...
@@ -22,7 +22,6 @@ import { dateTypes } from '../constants';
import
epicUtils
from
'
../utils/epic_utils
'
;
import
SidebarDatePicker
from
'
./sidebar_items/sidebar_date_picker.vue
'
;
import
SidebarHeader
from
'
./sidebar_items/sidebar_header.vue
'
;
import
SidebarLabels
from
'
./sidebar_items/sidebar_labels.vue
'
;
export
default
{
dateTypes
,
...
...
@@ -30,7 +29,6 @@ export default {
SidebarHeader
,
SidebarDatePicker
,
SidebarDatePickerCollapsed
,
SidebarLabels
,
SidebarAncestorsWidget
,
SidebarParticipantsWidget
,
SidebarConfidentialityWidget
,
...
...
@@ -239,7 +237,6 @@ export default {
@
toggleCollapse=
"toggleSidebar(
{ sidebarCollapsed })"
/>
<labels-select-widget
v-if=
"glFeatures.labelsWidget"
class=
"block labels js-labels-block"
:iid=
"String(iid)"
:full-path=
"fullPath"
...
...
@@ -256,12 +253,6 @@ export default {
>
{{
__
(
'
None
'
)
}}
</labels-select-widget>
<sidebar-labels
v-else
:can-update=
"canUpdate"
:sidebar-collapsed=
"sidebarCollapsed"
data-testid=
"labels-select"
/>
<sidebar-confidentiality-widget
:iid=
"String(iid)"
:full-path=
"fullPath"
...
...
ee/app/controllers/groups/epic_boards_controller.rb
View file @
4e53ae6c
...
...
@@ -8,9 +8,6 @@ class Groups::EpicBoardsController < Groups::ApplicationController
before_action
:redirect_to_recent_board
,
only:
[
:index
]
before_action
:assign_endpoint_vars
before_action
do
push_frontend_feature_flag
(
:labels_widget
,
group
,
default_enabled: :yaml
)
end
track_redis_hll_event
:index
,
:show
,
name:
'g_project_management_users_viewing_epic_boards'
...
...
ee/app/controllers/groups/epics_controller.rb
View file @
4e53ae6c
...
...
@@ -23,7 +23,6 @@ class Groups::EpicsController < Groups::ApplicationController
before_action
do
push_frontend_feature_flag
(
:vue_epics_list
,
@group
,
type: :development
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:improved_emoji_picker
,
@group
,
type: :development
,
default_enabled: :yaml
)
push_frontend_feature_flag
(
:labels_widget
,
@group
,
default_enabled: :yaml
)
end
feature_category
:portfolio_management
...
...
ee/spec/frontend/boards/components/__snapshots__/board_content_sidebar_spec.js.snap
View file @
4e53ae6c
...
...
@@ -69,9 +69,29 @@ exports[`ee/BoardContentSidebar matches the snapshot 1`] = `
issuabletype="issue"
/>
<boardsidebarlabelsselect-stub
<sidebarlabelswidget-stub
allowmultiselect="true"
attrworkspacepath="gitlab-org/gitlab-test"
class="block labels"
/>
data-testid="sidebar-labels"
dropdownbuttontext="Label"
footercreatelabeltitle="Create project label"
footermanagelabeltitle="Manage project labels"
fullpath="gitlab-org/gitlab-test"
iid="27"
issuabletype="issue"
labelcreatetype="project"
labelscreatetitle="Create project label"
labelsfilterbasepath=""
labelsfilterparam="label_name"
labelslisttitle="Assign labels"
variant="sidebar"
workspacetype="project"
>
None
</sidebarlabelswidget-stub>
<sidebarweightwidget-stub
full-path="gitlab-org/gitlab-test"
...
...
ee/spec/frontend/boards/components/board_content_sidebar_spec.js
View file @
4e53ae6c
import
{
GlDrawer
}
from
'
@gitlab/ui
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
{
stubComponent
}
from
'
helpers/stub_component
'
;
import
BoardContentSidebar
from
'
~/boards/components/board_content_sidebar.vue
'
;
import
{
ISSUABLE
,
issuableTypes
}
from
'
~/boards/constants
'
;
import
{
mockIssue
,
mockIssueGroupPath
,
mockIssueProjectPath
}
from
'
../mock_data
'
;
Vue
.
use
(
Vuex
);
describe
(
'
ee/BoardContentSidebar
'
,
()
=>
{
let
wrapper
;
let
store
;
...
...
@@ -25,6 +28,7 @@ describe('ee/BoardContentSidebar', () => {
projectPathForActiveIssue
:
()
=>
mockIssueProjectPath
,
groupPathForActiveIssue
:
()
=>
mockIssueGroupPath
,
isSidebarOpen
:
()
=>
true
,
isGroupBoard
:
()
=>
false
,
...
mockGetters
,
},
actions
:
mockActions
,
...
...
@@ -69,7 +73,7 @@ describe('ee/BoardContentSidebar', () => {
BoardEditableItem
:
true
,
BoardSidebarTitle
:
true
,
BoardSidebarTimeTracker
:
true
,
BoardSidebarLabelsSelec
t
:
true
,
SidebarLabelsWidge
t
:
true
,
SidebarAssigneesWidget
:
true
,
SidebarConfidentialityWidget
:
true
,
SidebarDateWidget
:
true
,
...
...
ee/spec/frontend/boards/components/epic_board_content_sidebar_spec.js
View file @
4e53ae6c
import
{
GlDrawer
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
MountingPortal
}
from
'
portal-vue
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
EpicBoardContentSidebar
from
'
ee_component/boards/components/epic_board_content_sidebar.vue
'
;
import
SidebarAncestorsWidget
from
'
ee_component/sidebar/components/ancestors_tree/sidebar_ancestors_widget.vue
'
;
import
{
stubComponent
}
from
'
helpers/stub_component
'
;
import
BoardSidebarLabelsSelect
from
'
~/boards/components/sidebar/board_sidebar_labels_select.vue
'
;
import
BoardSidebarTitle
from
'
~/boards/components/sidebar/board_sidebar_title.vue
'
;
import
{
ISSUABLE
}
from
'
~/boards/constants
'
;
import
SidebarConfidentialityWidget
from
'
~/sidebar/components/confidential/sidebar_confidentiality_widget.vue
'
;
...
...
@@ -13,8 +13,11 @@ import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue
import
SidebarParticipantsWidget
from
'
~/sidebar/components/participants/sidebar_participants_widget.vue
'
;
import
SidebarSubscriptionsWidget
from
'
~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
'
;
import
SidebarTodoWidget
from
'
~/sidebar/components/todo_toggle/sidebar_todo_widget.vue
'
;
import
LabelsSelectWidget
from
'
~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
'
;
import
{
mockFormattedBoardEpic
}
from
'
../mock_data
'
;
Vue
.
use
(
Vuex
);
describe
(
'
EpicBoardContentSidebar
'
,
()
=>
{
let
wrapper
;
let
store
;
...
...
@@ -95,8 +98,8 @@ describe('EpicBoardContentSidebar', () => {
);
});
it
(
'
renders
BoardSidebarLabelsSelec
t
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
BoardSidebarLabelsSelec
t
).
exists
()).
toBe
(
true
);
it
(
'
renders
LabelsSelectWidge
t
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
LabelsSelectWidge
t
).
exists
()).
toBe
(
true
);
});
it
(
'
renders BoardSidebarTitle
'
,
()
=>
{
...
...
ee/spec/frontend/boards/stores/actions_spec.js
View file @
4e53ae6c
...
...
@@ -1340,7 +1340,7 @@ describe('setSelectedGroup', () => {
describe
(
'
setActiveEpicLabels
'
,
()
=>
{
const
state
=
{
boardItems
:
{
[
mockEpic
.
id
]:
mockEpic
}
};
const
getters
=
{
activeBoardItem
:
mockEpic
};
const
getters
=
{
activeBoardItem
:
{
...
mockEpic
,
labels
}
};
const
testLabelIds
=
labels
.
map
((
label
)
=>
label
.
id
);
const
input
=
{
addLabelIds
:
testLabelIds
,
...
...
@@ -1348,11 +1348,7 @@ describe('setActiveEpicLabels', () => {
groupPath
:
'
h/b
'
,
};
it
(
'
should assign labels on success
'
,
(
done
)
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
)
.
mockResolvedValue
({
data
:
{
updateEpic
:
{
epic
:
{
labels
:
{
nodes
:
labels
}
}
}
}
});
it
(
'
should assign labels
'
,
()
=>
{
const
payload
=
{
itemId
:
getters
.
activeBoardItem
.
id
,
prop
:
'
labels
'
,
...
...
@@ -1365,78 +1361,32 @@ describe('setActiveEpicLabels', () => {
{
...
state
,
...
getters
},
[
{
type
:
types
CE
.
UPDATE_BOARD_ITEM_BY_ID
,
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
,
},
],
[],
done
,
);
});
it
(
'
throws error if fails
'
,
async
()
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
)
.
mockResolvedValue
({
data
:
{
updateEpic
:
{
errors
:
[
'
failed mutation
'
]
}
}
});
await
expect
(
actions
.
setActiveEpicLabels
({
getters
},
input
)).
rejects
.
toThrow
(
Error
);
});
describe
(
'
labels_widget FF on
'
,
()
=>
{
beforeEach
(()
=>
{
window
.
gon
=
{
features
:
{
labelsWidget
:
true
},
};
getters
.
activeBoardItem
=
{
...
mockIssue
,
labels
};
});
afterEach
(()
=>
{
window
.
gon
=
{
features
:
{},
};
});
it
(
'
should assign labels
'
,
()
=>
{
const
payload
=
{
itemId
:
getters
.
activeBoardItem
.
id
,
prop
:
'
labels
'
,
value
:
labels
,
};
testAction
(
actions
.
setActiveEpicLabels
,
input
,
{
...
state
,
...
getters
},
[
{
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
,
},
],
[],
);
});
it
(
'
should remove label
'
,
()
=>
{
const
payload
=
{
itemId
:
getters
.
activeBoardItem
.
id
,
prop
:
'
labels
'
,
value
:
[
labels
[
1
]],
};
it
(
'
should remove label
'
,
()
=>
{
const
payload
=
{
itemId
:
getters
.
activeBoardItem
.
id
,
prop
:
'
labels
'
,
value
:
[
labels
[
1
]],
};
testAction
(
actions
.
setActiveEpicLabels
,
{
...
input
,
removeLabelIds
:
[
getIdFromGraphQLId
(
labels
[
0
].
id
)]
},
{
...
state
,
...
getters
},
[
{
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
,
},
],
[],
);
});
testAction
(
actions
.
setActiveEpicLabels
,
{
...
input
,
removeLabelIds
:
[
getIdFromGraphQLId
(
labels
[
0
].
id
)]
},
{
...
state
,
...
getters
},
[
{
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
,
},
],
[],
);
});
});
ee/spec/frontend/epic/components/epic_form_spec.js
View file @
4e53ae6c
...
...
@@ -6,7 +6,7 @@ import EpicForm from 'ee/epic/components/epic_form.vue';
import
createEpic
from
'
ee/epic/queries/createEpic.mutation.graphql
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
{
visitUrl
}
from
'
~/lib/utils/url_utility
'
;
import
LabelsSelect
Vue
from
'
~/vue_shared/components/sidebar/labels_select_vue
/labels_select_root.vue
'
;
import
LabelsSelect
Widget
from
'
~/vue_shared/components/sidebar/labels_select_widget
/labels_select_root.vue
'
;
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
visitUrl
:
jest
.
fn
(),
...
...
@@ -48,7 +48,7 @@ describe('ee/epic/components/epic_form.vue', () => {
});
const
findForm
=
()
=>
wrapper
.
find
(
GlForm
);
const
findLabels
=
()
=>
wrapper
.
find
(
LabelsSelect
Vue
);
const
findLabels
=
()
=>
wrapper
.
find
(
LabelsSelect
Widget
);
const
findTitle
=
()
=>
wrapper
.
find
(
'
[data-testid="epic-title"]
'
);
const
findDescription
=
()
=>
wrapper
.
find
(
'
[data-testid="epic-description"]
'
);
const
findConfidentialityCheck
=
()
=>
wrapper
.
find
(
'
[data-testid="epic-confidentiality"]
'
);
...
...
@@ -113,7 +113,9 @@ describe('ee/epic/components/epic_form.vue', () => {
findTitle
().
vm
.
$emit
(
'
input
'
,
title
);
findDescription
().
setValue
(
description
);
findConfidentialityCheck
().
vm
.
$emit
(
'
input
'
,
confidential
);
findLabels
().
vm
.
$emit
(
'
updateSelectedLabels
'
,
[{
id
:
1
,
set
:
1
}]);
findLabels
().
vm
.
$emit
(
'
updateSelectedLabels
'
,
{
labels
:
[{
id
:
'
gid://gitlab/GroupLabel/1
'
}],
});
// Make sure the submitted values for start and due dates are date strings without timezone info.
// (Datepicker emits a Date object but the submitted value must be a date string).
...
...
qa/qa/page/component/issuable/sidebar.rb
View file @
4e53ae6c
...
...
@@ -124,15 +124,6 @@ module QA
click_element
(
:more_assignees_link
)
end
# When the labels_widget feature flag is enabled, wait until the labels widget appears
def
wait_for_labels_widget_feature_flag
Support
::
Retrier
.
retry_until
(
max_duration:
60
,
reload_page:
page
,
retry_on_exception:
true
,
sleep_interval:
5
)
do
within_element
(
:labels_block
)
do
find_element
(
:edit_link
)
end
end
end
private
def
wait_assignees_block_finish_loading
...
...
qa/qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb
View file @
4e53ae6c
...
...
@@ -19,8 +19,6 @@ module QA
end
before
do
Runtime
::
Feature
.
enable
(
:labels_widget
,
project:
issue
.
project
)
Flow
::
Login
.
sign_in
[
...
...
@@ -43,9 +41,6 @@ module QA
testcase:
'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1181'
)
do
Page
::
Project
::
Issue
::
Show
.
perform
do
|
show
|
# TODO: Remove this method when the `Runtime::Feature.enable` method call is removed
show
.
wait_for_labels_widget_feature_flag
show
.
select_labels
(
[
new_label_same_scope
,
...
...
spec/features/issues/user_edits_issue_spec.rb
View file @
4e53ae6c
...
...
@@ -15,7 +15,6 @@ RSpec.describe "Issues > User edits issue", :js do
context
'with authorized user'
do
before
do
stub_feature_flags
(
labels_widget:
false
)
project
.
add_developer
(
user
)
project_with_milestones
.
add_developer
(
user
)
sign_in
(
user
)
...
...
@@ -151,7 +150,7 @@ RSpec.describe "Issues > User edits issue", :js do
page
.
within
'.block.labels'
do
# Remove `verisimilitude` label
within
'.gl-label'
do
click_button
click_button
'Remove label'
end
expect
(
page
).
to
have_text
(
'syzygy'
)
...
...
spec/features/labels_hierarchy_spec.rb
View file @
4e53ae6c
...
...
@@ -17,7 +17,6 @@ RSpec.describe 'Labels Hierarchy', :js do
let!
(
:project_label_1
)
{
create
(
:label
,
project:
project_1
,
title:
'Label_4'
)
}
before
do
stub_feature_flags
(
labels_widget:
false
)
grandparent
.
add_owner
(
user
)
sign_in
(
user
)
...
...
@@ -28,13 +27,12 @@ RSpec.describe 'Labels Hierarchy', :js do
[
grandparent_group_label
,
parent_group_label
,
project_label_1
].
each
do
|
label
|
page
.
within
(
'.block.labels'
)
do
click_on
'Edit'
end
wait_for_requests
wait_for_requests
find
(
'a.label-item'
,
text:
label
.
title
).
click
wait_for_requests
click_on
'Close'
click_on
label
.
title
click_on
'Close'
end
wait_for_requests
...
...
spec/frontend/boards/components/board_content_sidebar_spec.js
View file @
4e53ae6c
import
{
GlDrawer
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
MountingPortal
}
from
'
portal-vue
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
SidebarDropdownWidget
from
'
ee_else_ce/sidebar/components/sidebar_dropdown_widget.vue
'
;
import
{
stubComponent
}
from
'
helpers/stub_component
'
;
import
BoardContentSidebar
from
'
~/boards/components/board_content_sidebar.vue
'
;
import
BoardSidebarLabelsSelect
from
'
~/boards/components/sidebar/board_sidebar_labels_select.vue
'
;
import
BoardSidebarTitle
from
'
~/boards/components/sidebar/board_sidebar_title.vue
'
;
import
{
ISSUABLE
}
from
'
~/boards/constants
'
;
import
SidebarDateWidget
from
'
~/sidebar/components/date/sidebar_date_widget.vue
'
;
import
SidebarSubscriptionsWidget
from
'
~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
'
;
import
SidebarTodoWidget
from
'
~/sidebar/components/todo_toggle/sidebar_todo_widget.vue
'
;
import
SidebarLabelsWidget
from
'
~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
'
;
import
{
mockActiveIssue
,
mockIssue
,
mockIssueGroupPath
,
mockIssueProjectPath
}
from
'
../mock_data
'
;
Vue
.
use
(
Vuex
);
describe
(
'
BoardContentSidebar
'
,
()
=>
{
let
wrapper
;
let
store
;
...
...
@@ -32,6 +34,7 @@ describe('BoardContentSidebar', () => {
groupPathForActiveIssue
:
()
=>
mockIssueGroupPath
,
projectPathForActiveIssue
:
()
=>
mockIssueProjectPath
,
isSidebarOpen
:
()
=>
true
,
isGroupBoard
:
()
=>
false
,
...
mockGetters
,
},
actions
:
mockActions
,
...
...
@@ -115,8 +118,8 @@ describe('BoardContentSidebar', () => {
expect
(
wrapper
.
findComponent
(
SidebarTodoWidget
).
exists
()).
toBe
(
true
);
});
it
(
'
renders
BoardSidebarLabelsSelec
t
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
BoardSidebarLabelsSelec
t
).
exists
()).
toBe
(
true
);
it
(
'
renders
SidebarLabelsWidge
t
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
SidebarLabelsWidge
t
).
exists
()).
toBe
(
true
);
});
it
(
'
renders BoardSidebarTitle
'
,
()
=>
{
...
...
spec/frontend/boards/stores/actions_spec.js
View file @
4e53ae6c
...
...
@@ -1570,7 +1570,7 @@ describe('addListNewIssue', () => {
describe
(
'
setActiveIssueLabels
'
,
()
=>
{
const
state
=
{
boardItems
:
{
[
mockIssue
.
id
]:
mockIssue
}
};
const
getters
=
{
activeBoardItem
:
mockIssue
};
const
getters
=
{
activeBoardItem
:
{
...
mockIssue
,
labels
}
};
const
testLabelIds
=
labels
.
map
((
label
)
=>
label
.
id
);
const
input
=
{
labelIds
:
testLabelIds
,
...
...
@@ -1579,11 +1579,7 @@ describe('setActiveIssueLabels', () => {
labels
,
};
it
(
'
should assign labels on success
'
,
(
done
)
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
)
.
mockResolvedValue
({
data
:
{
updateIssue
:
{
issue
:
{
labels
:
{
nodes
:
labels
}
}
}
}
});
it
(
'
should assign labels
'
,
()
=>
{
const
payload
=
{
itemId
:
getters
.
activeBoardItem
.
id
,
prop
:
'
labels
'
,
...
...
@@ -1601,74 +1597,28 @@ describe('setActiveIssueLabels', () => {
},
],
[],
done
,
);
});
it
(
'
throws error if fails
'
,
async
()
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
)
.
mockResolvedValue
({
data
:
{
updateIssue
:
{
errors
:
[
'
failed mutation
'
]
}
}
});
await
expect
(
actions
.
setActiveIssueLabels
({
getters
},
input
)).
rejects
.
toThrow
(
Error
);
});
describe
(
'
labels_widget FF on
'
,
()
=>
{
beforeEach
(()
=>
{
window
.
gon
=
{
features
:
{
labelsWidget
:
true
},
};
getters
.
activeBoardItem
=
{
...
mockIssue
,
labels
};
});
afterEach
(()
=>
{
window
.
gon
=
{
features
:
{},
};
});
it
(
'
should assign labels
'
,
()
=>
{
const
payload
=
{
itemId
:
getters
.
activeBoardItem
.
id
,
prop
:
'
labels
'
,
value
:
labels
,
};
testAction
(
actions
.
setActiveIssueLabels
,
input
,
{
...
state
,
...
getters
},
[
{
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
,
},
],
[],
);
});
it
(
'
should remove label
'
,
()
=>
{
const
payload
=
{
itemId
:
getters
.
activeBoardItem
.
id
,
prop
:
'
labels
'
,
value
:
[
labels
[
1
]],
};
it
(
'
should remove label
'
,
()
=>
{
const
payload
=
{
itemId
:
getters
.
activeBoardItem
.
id
,
prop
:
'
labels
'
,
value
:
[
labels
[
1
]],
};
testAction
(
actions
.
setActiveIssueLabels
,
{
...
input
,
removeLabelIds
:
[
getIdFromGraphQLId
(
labels
[
0
].
id
)]
},
{
...
state
,
...
getters
},
[
{
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
,
},
],
[],
);
});
testAction
(
actions
.
setActiveIssueLabels
,
{
...
input
,
removeLabelIds
:
[
getIdFromGraphQLId
(
labels
[
0
].
id
)]
},
{
...
state
,
...
getters
},
[
{
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
,
},
],
[],
);
});
});
...
...
spec/frontend/sidebar/sidebar_labels_spec.js
View file @
4e53ae6c
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
mockLabels
,
mockRegularLabel
,
}
from
'
jest/vue_shared/components/sidebar/labels_select_vue/mock_data
'
;
import
updateIssueLabelsMutation
from
'
~/boards/graphql/issue_set_labels.mutation.graphql
'
;
import
{
MutationOperationMode
}
from
'
~/graphql_shared/utils
'
;
import
{
IssuableType
}
from
'
~/issue_show/constants
'
;
import
SidebarLabels
from
'
~/sidebar/components/labels/sidebar_labels.vue
'
;
import
updateMergeRequestLabelsMutation
from
'
~/sidebar/queries/update_merge_request_labels.mutation.graphql
'
;
import
{
toLabelGid
}
from
'
~/sidebar/utils
'
;
import
{
DropdownVariant
}
from
'
~/vue_shared/components/sidebar/labels_select_vue/constants
'
;
import
LabelsSelect
from
'
~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
'
;
import
{
DropdownVariant
,
LabelType
,
}
from
'
~/vue_shared/components/sidebar/labels_select_widget/constants
'
;
import
LabelsSelect
from
'
~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
'
;
describe
(
'
sidebar labels
'
,
()
=>
{
let
wrapper
;
const
defaultProps
=
{
allowLabelCreate
:
true
,
allowLabelEdit
:
true
,
allowScopedLabels
:
true
,
canEdit
:
true
,
iid
:
'
1
'
,
initiallySelectedLabels
:
mockLabels
,
issuableType
:
'
issue
'
,
labelsFetchPath
:
'
/gitlab-org/gitlab-test/-/labels.json?include_ancestor_groups=true
'
,
labelsManagePath
:
'
/gitlab-org/gitlab-test/-/labels
'
,
projectIssuesPath
:
'
/gitlab-org/gitlab-test/-/issues
'
,
projectPath
:
'
gitlab-org/gitlab-test
'
,
fullPath
:
'
gitlab-org/gitlab-test
'
,
};
const
$apollo
=
{
mutate
:
jest
.
fn
().
mockResolvedValue
(),
};
const
userUpdatedLabels
=
[
{
...
mockRegularLabel
,
set
:
false
,
},
{
id
:
40
,
title
:
'
Security
'
,
color
:
'
#ddd
'
,
text_color
:
'
#fff
'
,
set
:
true
,
},
{
id
:
55
,
title
:
'
Tooling
'
,
color
:
'
#ddd
'
,
text_color
:
'
#fff
'
,
set
:
false
,
},
];
const
findLabelsSelect
=
()
=>
wrapper
.
find
(
LabelsSelect
);
const
mountComponent
=
(
props
=
{})
=>
{
...
...
@@ -63,9 +25,6 @@ describe('sidebar labels', () => {
...
defaultProps
,
...
props
,
},
mocks
:
{
$apollo
,
},
});
};
...
...
@@ -75,115 +34,31 @@ describe('sidebar labels', () => {
});
describe
(
'
LabelsSelect props
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
are as expected
'
,
()
=>
{
expect
(
findLabelsSelect
().
props
()).
toMatchObject
({
allowLabelCreate
:
defaultProps
.
allowLabelCreate
,
allowLabelEdit
:
defaultProps
.
allowLabelEdit
,
allowMultiselect
:
true
,
allowScopedLabels
:
defaultProps
.
allowScopedLabels
,
footerCreateLabelTitle
:
'
Create project label
'
,
footerManageLabelTitle
:
'
Manage project labels
'
,
labelsCreateTitle
:
'
Create project label
'
,
labelsFetchPath
:
defaultProps
.
labelsFetchPath
,
labelsFilterBasePath
:
defaultProps
.
projectIssuesPath
,
labelsManagePath
:
defaultProps
.
labelsManagePath
,
labelsSelectInProgress
:
false
,
selectedLabels
:
defaultProps
.
initiallySelectedLabels
,
variant
:
DropdownVariant
.
Sidebar
,
});
});
});
describe
(
'
when type is issue
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
issuableType
:
IssuableType
.
Issue
});
});
describe
(
'
when labels are updated
'
,
()
=>
{
it
(
'
invokes a mutation
'
,
()
=>
{
findLabelsSelect
().
vm
.
$emit
(
'
updateSelectedLabels
'
,
userUpdatedLabels
);
const
expected
=
{
mutation
:
updateIssueLabelsMutation
,
variables
:
{
input
:
{
iid
:
defaultProps
.
iid
,
projectPath
:
defaultProps
.
projectPath
,
labelIds
:
[
toLabelGid
(
29
),
toLabelGid
(
28
),
toLabelGid
(
27
),
toLabelGid
(
40
)],
},
},
};
expect
(
$apollo
.
mutate
).
toHaveBeenCalledWith
(
expected
);
describe
.
each
`
issuableType
${
'
issue
'
}
${
'
merge_request
'
}
`
(
'
issuableType $issuableType
'
,
({
issuableType
})
=>
{
beforeEach
(()
=>
{
mountComponent
({
issuableType
});
});
});
describe
(
'
when label `x` is clicked
'
,
()
=>
{
it
(
'
invokes a mutation
'
,
()
=>
{
findLabelsSelect
().
vm
.
$emit
(
'
onLabelRemove
'
,
27
);
const
expected
=
{
mutation
:
updateIssueLabelsMutation
,
variables
:
{
input
:
{
iid
:
defaultProps
.
iid
,
projectPath
:
defaultProps
.
projectPath
,
removeLabelIds
:
[
27
],
},
},
};
expect
(
$apollo
.
mutate
).
toHaveBeenCalledWith
(
expected
);
});
});
});
describe
(
'
when type is merge_request
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
issuableType
:
IssuableType
.
MergeRequest
});
});
describe
(
'
when labels are updated
'
,
()
=>
{
it
(
'
invokes a mutation
'
,
()
=>
{
findLabelsSelect
().
vm
.
$emit
(
'
updateSelectedLabels
'
,
userUpdatedLabels
);
const
expected
=
{
mutation
:
updateMergeRequestLabelsMutation
,
variables
:
{
input
:
{
iid
:
defaultProps
.
iid
,
labelIds
:
[
toLabelGid
(
29
),
toLabelGid
(
28
),
toLabelGid
(
27
),
toLabelGid
(
40
)],
operationMode
:
MutationOperationMode
.
Replace
,
projectPath
:
defaultProps
.
projectPath
,
},
},
};
expect
(
$apollo
.
mutate
).
toHaveBeenCalledWith
(
expected
);
});
});
describe
(
'
when label `x` is clicked
'
,
()
=>
{
it
(
'
invokes a mutation
'
,
()
=>
{
findLabelsSelect
().
vm
.
$emit
(
'
onLabelRemove
'
,
27
);
const
expected
=
{
mutation
:
updateMergeRequestLabelsMutation
,
variables
:
{
input
:
{
iid
:
defaultProps
.
iid
,
labelIds
:
[
toLabelGid
(
27
)],
operationMode
:
MutationOperationMode
.
Remove
,
projectPath
:
defaultProps
.
projectPath
,
},
},
};
expect
(
$apollo
.
mutate
).
toHaveBeenCalledWith
(
expected
);
it
(
'
has expected props
'
,
()
=>
{
expect
(
findLabelsSelect
().
props
()).
toMatchObject
({
iid
:
defaultProps
.
iid
,
fullPath
:
defaultProps
.
fullPath
,
allowLabelRemove
:
defaultProps
.
allowLabelEdit
,
allowMultiselect
:
true
,
footerCreateLabelTitle
:
'
Create project label
'
,
footerManageLabelTitle
:
'
Manage project labels
'
,
labelsCreateTitle
:
'
Create project label
'
,
labelsFilterBasePath
:
defaultProps
.
projectIssuesPath
,
variant
:
DropdownVariant
.
Sidebar
,
issuableType
,
workspaceType
:
'
project
'
,
attrWorkspacePath
:
defaultProps
.
fullPath
,
labelCreateType
:
LabelType
.
project
,
});
});
});
});
...
...
spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js
View file @
4e53ae6c
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
nextTick
}
from
'
vue
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
...
...
@@ -8,14 +8,14 @@ import { IssuableType } from '~/issue_show/constants';
import
SidebarEditableItem
from
'
~/sidebar/components/sidebar_editable_item.vue
'
;
import
DropdownContents
from
'
~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue
'
;
import
DropdownValue
from
'
~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue
'
;
import
DropdownValueCollapsed
from
'
~/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue
'
;
import
issueLabelsQuery
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql
'
;
import
LabelsSelectRoot
from
'
~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
'
;
import
{
mockConfig
,
issuableLabelsQueryResponse
}
from
'
./mock_data
'
;
jest
.
mock
(
'
~/flash
'
);
const
localVue
=
createLocalVue
();
localVue
.
use
(
VueApollo
);
Vue
.
use
(
VueApollo
);
const
successfulQueryHandler
=
jest
.
fn
().
mockResolvedValue
(
issuableLabelsQueryResponse
);
const
errorQueryHandler
=
jest
.
fn
().
mockRejectedValue
(
'
Houston, we have a problem
'
);
...
...
@@ -25,6 +25,7 @@ describe('LabelsSelectRoot', () => {
const
findSidebarEditableItem
=
()
=>
wrapper
.
findComponent
(
SidebarEditableItem
);
const
findDropdownValue
=
()
=>
wrapper
.
findComponent
(
DropdownValue
);
const
findDropdownValueCollapsed
=
()
=>
wrapper
.
findComponent
(
DropdownValueCollapsed
);
const
findDropdownContents
=
()
=>
wrapper
.
findComponent
(
DropdownContents
);
const
createComponent
=
({
...
...
@@ -37,7 +38,6 @@ describe('LabelsSelectRoot', () => {
wrapper
=
shallowMount
(
LabelsSelectRoot
,
{
slots
,
apolloProvider
:
mockApollo
,
localVue
,
propsData
:
{
...
config
,
issuableType
:
IssuableType
.
Issue
,
...
...
@@ -107,6 +107,9 @@ describe('LabelsSelectRoot', () => {
expect
(
findDropdownValue
().
props
(
'
selectedLabels
'
)).
toEqual
(
issuableLabelsQueryResponse
.
data
.
workspace
.
issuable
.
labels
.
nodes
,
);
expect
(
findDropdownValueCollapsed
().
props
(
'
labels
'
)).
toEqual
(
issuableLabelsQueryResponse
.
data
.
workspace
.
issuable
.
labels
.
nodes
,
);
});
it
(
'
emits `onLabelRemove` event on dropdown value label remove event
'
,
()
=>
{
...
...
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