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
7c42b1e0
Commit
7c42b1e0
authored
Jul 06, 2021
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab master
parents
e2596c1f
384861b8
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
239 additions
and
102 deletions
+239
-102
GITALY_SERVER_VERSION
GITALY_SERVER_VERSION
+1
-1
app/helpers/issuables_helper.rb
app/helpers/issuables_helper.rb
+9
-0
app/views/admin/application_settings/_localization.html.haml
app/views/admin/application_settings/_localization.html.haml
+5
-0
app/views/admin/application_settings/preferences.html.haml
app/views/admin/application_settings/preferences.html.haml
+1
-1
app/views/shared/issuable/_sidebar.html.haml
app/views/shared/issuable/_sidebar.html.haml
+1
-1
doc/user/profile/preferences.md
doc/user/profile/preferences.md
+1
-1
ee/app/assets/javascripts/sidebar/components/status/sidebar_status.vue
.../javascripts/sidebar/components/status/sidebar_status.vue
+124
-16
ee/app/assets/javascripts/sidebar/constants.js
ee/app/assets/javascripts/sidebar/constants.js
+13
-0
ee/app/assets/javascripts/sidebar/mount_sidebar.js
ee/app/assets/javascripts/sidebar/mount_sidebar.js
+8
-2
ee/app/assets/javascripts/sidebar/queries/issue_health_status.query.graphql
...scripts/sidebar/queries/issue_health_status.query.graphql
+11
-0
ee/app/assets/javascripts/sidebar/sidebar_mediator.js
ee/app/assets/javascripts/sidebar/sidebar_mediator.js
+0
-17
ee/spec/frontend/sidebar/components/status/sidebar_status_spec.js
...frontend/sidebar/components/status/sidebar_status_spec.js
+50
-35
ee/spec/frontend/sidebar/ee_sidebar_mediator_spec.js
ee/spec/frontend/sidebar/ee_sidebar_mediator_spec.js
+0
-25
locale/gitlab.pot
locale/gitlab.pot
+15
-3
No files found.
GITALY_SERVER_VERSION
View file @
7c42b1e0
1ce9d77e4ed41e124d840b23689392fda6945b3c
6ee267a02055a7f48871d66fae594417c4cdec9b
app/helpers/issuables_helper.rb
View file @
7c42b1e0
...
...
@@ -425,6 +425,15 @@ module IssuablesHelper
}
end
def
sidebar_status_data
(
issuable_sidebar
,
project
)
{
iid:
issuable_sidebar
[
:iid
],
issuable_type:
issuable_sidebar
[
:type
],
full_path:
project
.
full_path
,
can_edit:
issuable_sidebar
.
dig
(
:current_user
,
:can_edit
).
to_s
}
end
def
parent
@project
||
@group
end
...
...
app/views/admin/application_settings/_localization.html.haml
View file @
7c42b1e0
...
...
@@ -7,6 +7,7 @@
=
f
.
select
:first_day_of_week
,
first_day_of_week_choices
,
{},
class:
'form-control'
.form-text.text-muted
=
_
(
'Default first day of the week in calendars and date pickers.'
)
=
link_to
_
(
'Learn more.'
),
help_page_path
(
'user/admin_area/settings/index.md'
,
anchor:
'default-first-day-of-the-week'
),
target:
'_blank'
.form-group
=
f
.
label
:time_tracking
,
_
(
'Time tracking'
),
class:
'label-bold'
...
...
@@ -14,5 +15,9 @@
=
f
.
check_box
:time_tracking_limit_to_hours
,
class:
'form-check-input'
=
f
.
label
:time_tracking_limit_to_hours
,
class:
'form-check-label'
do
=
_
(
'Limit display of time tracking units to hours.'
)
.form-text.text-muted
=
_
(
'Display time tracking in issues in total hours only.'
)
=
link_to
_
(
'What is time tracking?'
),
help_page_path
(
'user/project/time_tracking.md'
),
target:
'_blank'
=
f
.
submit
_
(
'Save changes'
),
class:
"gl-button btn btn-confirm"
app/views/admin/application_settings/preferences.html.haml
View file @
7c42b1e0
...
...
@@ -77,6 +77,6 @@
%button
.btn.gl-button.btn-default.js-settings-toggle
{
type:
'button'
}
=
expanded_by_default?
?
_
(
'Collapse'
)
:
_
(
'Expand'
)
%p
=
_
(
'
Various localization setting
s.'
)
=
_
(
'
Configure the default first day of the week and time tracking unit
s.'
)
.settings-content
=
render
'localization'
app/views/shared/issuable/_sidebar.html.haml
View file @
7c42b1e0
...
...
@@ -81,7 +81,7 @@
#js-severity
-
if
issuable_sidebar
.
dig
(
:features_available
,
:health_status
)
.js-sidebar-status-entry-point
.js-sidebar-status-entry-point
{
data:
sidebar_status_data
(
issuable_sidebar
,
@project
)
}
-
if
issuable_sidebar
.
has_key?
(
:confidential
)
%script
#js-confidential-issue-data
{
type:
"application/json"
}=
{
is_confidential:
issuable_sidebar
[
:confidential
],
is_editable:
can_edit_issuable
}.
to_json
.
html_safe
...
...
doc/user/profile/preferences.md
View file @
7c42b1e0
...
...
@@ -163,7 +163,7 @@ You can choose one of the following options as the first day of the week:
-
Sunday
-
Monday
If you select
**System Default**
, the
system-wide default
setting is used.
If you select
**System Default**
, the
[
instance default
](
../admin_area/settings/index.md#default-first-day-of-the-week
)
setting is used.
## Integrations
...
...
ee/app/assets/javascripts/sidebar/components/status/sidebar_status.vue
View file @
7c42b1e0
<
script
>
import
{
mapGetters
}
from
'
vuex
'
;
import
produce
from
'
immer
'
;
import
createFlash
from
'
~/flash
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
,
sprintf
}
from
'
~/locale
'
;
import
{
OPENED
,
REOPENED
}
from
'
~/notes/constants
'
;
import
{
healthStatusQueries
}
from
'
../../constants
'
;
import
Status
from
'
./status.vue
'
;
export
default
{
...
...
@@ -10,27 +11,134 @@ export default {
Status
,
},
props
:
{
mediator
:
{
issuableType
:
{
type
:
String
,
required
:
true
,
type
:
Object
,
validator
(
mediatorObject
)
{
return
Boolean
(
mediatorObject
.
store
);
},
},
iid
:
{
type
:
String
,
required
:
true
,
},
fullPath
:
{
type
:
String
,
required
:
true
,
},
canUpdate
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
computed
:
{
...
mapGetters
([
'
getNoteableData
'
]),
isOpen
()
{
return
this
.
getNoteableData
.
state
===
OPENED
||
this
.
getNoteableData
.
state
===
REOPENED
;
return
this
.
issuableData
?.
state
===
OPENED
||
this
.
issuableData
?.
state
===
REOPENED
;
},
isLoading
()
{
return
this
.
$apollo
.
queries
.
issuableData
.
loading
;
},
healthStatus
()
{
return
this
.
issuableData
?.
healthStatus
;
},
},
apollo
:
{
issuableData
:
{
query
()
{
return
healthStatusQueries
[
this
.
issuableType
].
query
;
},
variables
()
{
return
{
fullPath
:
this
.
fullPath
,
iid
:
this
.
iid
,
};
},
update
(
data
)
{
return
{
healthStatus
:
data
.
workspace
?.
issuable
?.
healthStatus
,
state
:
data
.
workspace
?.
issuable
?.
state
,
};
},
result
({
data
})
{
this
.
$emit
(
'
issuableData
'
,
{
healthStatus
:
data
.
workspace
?.
issuable
?.
healthStatus
,
state
:
data
.
workspace
?.
issuable
?.
state
,
});
},
error
()
{
createFlash
({
message
:
sprintf
(
__
(
'
Something went wrong while setting %{issuableType} health status.
'
),
{
issuableType
:
this
.
issuableType
,
},
),
});
},
},
},
methods
:
{
handleDropdownClick
(
status
)
{
this
.
mediator
.
updateStatus
(
status
).
catch
(()
=>
{
createFlash
({
message
:
__
(
'
Error occurred while updating the issue status
'
),
return
this
.
$apollo
.
mutate
({
mutation
:
healthStatusQueries
[
this
.
issuableType
].
mutation
,
variables
:
{
projectPath
:
this
.
fullPath
,
iid
:
this
.
iid
,
healthStatus
:
status
,
},
update
:
(
_
,
{
data
:
{
updateIssue
:
{
issue
:
{
healthStatus
},
},
},
},
)
=>
{
const
{
defaultClient
:
client
}
=
this
.
$apollo
.
provider
.
clients
;
const
queryVariables
=
{
fullPath
:
this
.
fullPath
,
iid
:
this
.
iid
};
const
sourceData
=
client
.
readQuery
({
query
:
healthStatusQueries
[
this
.
issuableType
].
query
,
variables
:
queryVariables
,
});
const
data
=
produce
(
sourceData
,
(
draftData
)
=>
{
draftData
.
workspace
.
issuable
.
healthStatus
=
healthStatus
;
});
client
.
writeQuery
({
query
:
healthStatusQueries
[
this
.
issuableType
].
query
,
variables
:
queryVariables
,
data
,
});
},
optimisticResponse
:
{
__typename
:
'
Mutation
'
,
// eslint-disable-line @gitlab/require-i18n-strings
updateIssue
:
{
issue
:
{
healthStatus
:
status
,
__typename
:
'
Issue
'
,
// eslint-disable-line @gitlab/require-i18n-strings
},
errors
:
[],
__typename
:
'
UpdateIssuePayload
'
,
},
},
})
.
then
(({
data
:
{
updateIssue
}
=
{}
}
=
{})
=>
{
const
error
=
updateIssue
?.
errors
[
0
];
if
(
error
)
{
createFlash
({
message
:
error
});
}
})
.
catch
(()
=>
{
createFlash
({
message
:
sprintf
(
__
(
'
Error occurred while updating the %{issuableType} status
'
),
{
issuableType
:
this
.
issuableType
,
}),
});
});
});
},
},
};
...
...
@@ -39,9 +147,9 @@ export default {
<
template
>
<status
:is-open=
"isOpen"
:is-editable=
"
mediator.store.editabl
e"
:is-fetching=
"
mediator.store.isFetching.status
"
:status=
"
mediator.store.s
tatus"
:is-editable=
"
canUpdat
e"
:is-fetching=
"
isLoading
"
:status=
"
healthS
tatus"
@
onDropdownClick=
"handleDropdownClick"
/>
</
template
>
ee/app/assets/javascripts/sidebar/constants.js
View file @
7c42b1e0
...
...
@@ -5,9 +5,11 @@ import {
IssuableAttributeState
as
IssuableAttributeStateFoss
,
issuableAttributesQueries
as
issuableAttributesQueriesFoss
,
}
from
'
~/sidebar/constants
'
;
import
updateStatusMutation
from
'
~/sidebar/queries/updateStatus.mutation.graphql
'
;
import
epicAncestorsQuery
from
'
./queries/epic_ancestors.query.graphql
'
;
import
groupEpicsQuery
from
'
./queries/group_epics.query.graphql
'
;
import
groupIterationsQuery
from
'
./queries/group_iterations.query.graphql
'
;
import
issueHealthStatusQuery
from
'
./queries/issue_health_status.query.graphql
'
;
import
projectIssueEpicMutation
from
'
./queries/project_issue_epic.mutation.graphql
'
;
import
projectIssueEpicQuery
from
'
./queries/project_issue_epic.query.graphql
'
;
import
projectIssueIterationMutation
from
'
./queries/project_issue_iteration.mutation.graphql
'
;
...
...
@@ -128,3 +130,14 @@ export const ancestorsQueries = {
query
:
epicAncestorsQuery
,
},
};
export
const
healthStatusQueries
=
{
[
IssuableType
.
Issue
]:
{
mutation
:
updateStatusMutation
,
query
:
issueHealthStatusQuery
,
},
[
IssuableType
.
Epic
]:
{
mutation
:
updateStatusMutation
,
query
:
issueHealthStatusQuery
,
},
};
ee/app/assets/javascripts/sidebar/mount_sidebar.js
View file @
7c42b1e0
...
...
@@ -28,15 +28,18 @@ const mountWeightComponent = () => {
});
};
const
mountStatusComponent
=
(
mediator
)
=>
{
const
mountStatusComponent
=
()
=>
{
const
el
=
document
.
querySelector
(
'
.js-sidebar-status-entry-point
'
);
if
(
!
el
)
{
return
false
;
}
const
{
iid
,
fullPath
,
issuableType
,
canEdit
}
=
el
.
dataset
;
return
new
Vue
({
el
,
apolloProvider
,
store
,
components
:
{
SidebarStatus
,
...
...
@@ -44,7 +47,10 @@ const mountStatusComponent = (mediator) => {
render
:
(
createElement
)
=>
createElement
(
'
sidebar-status
'
,
{
props
:
{
mediator
,
issuableType
,
iid
,
fullPath
,
canUpdate
:
parseBoolean
(
canEdit
),
},
}),
});
...
...
ee/app/assets/javascripts/sidebar/queries/issue_health_status.query.graphql
0 → 100644
View file @
7c42b1e0
query
issueHealthStatus
(
$fullPath
:
ID
!,
$iid
:
String
)
{
workspace
:
project
(
fullPath
:
$fullPath
)
{
__typename
issuable
:
issue
(
iid
:
$iid
)
{
__typename
id
healthStatus
state
}
}
}
ee/app/assets/javascripts/sidebar/sidebar_mediator.js
View file @
7c42b1e0
import
Store
from
'
ee/sidebar/stores/sidebar_store
'
;
import
updateStatusMutation
from
'
~/sidebar/queries/updateStatus.mutation.graphql
'
;
import
CESidebarMediator
from
'
~/sidebar/sidebar_mediator
'
;
export
default
class
SidebarMediator
extends
CESidebarMediator
{
...
...
@@ -28,20 +27,4 @@ export default class SidebarMediator extends CESidebarMediator {
throw
err
;
});
}
updateStatus
(
healthStatus
)
{
this
.
store
.
setFetchingState
(
'
status
'
,
true
);
return
this
.
service
.
updateWithGraphQl
(
updateStatusMutation
,
{
healthStatus
})
.
then
(({
data
})
=>
{
if
(
data
?.
updateIssue
?.
errors
?.
length
>
0
)
{
throw
data
.
updateIssue
.
errors
[
0
];
}
this
.
store
.
setStatus
(
data
?.
updateIssue
?.
issue
?.
healthStatus
);
})
.
catch
((
error
)
=>
{
throw
error
;
})
.
finally
(()
=>
this
.
store
.
setFetchingState
(
'
status
'
,
false
));
}
}
ee/spec/frontend/sidebar/components/status/sidebar_status_spec.js
View file @
7c42b1e0
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
{
ApolloMutation
,
ApolloQuery
}
from
'
vue-apollo
'
;
import
SidebarStatus
from
'
ee/sidebar/components/status/sidebar_status.vue
'
;
import
Status
from
'
ee/sidebar/components/status/status.vue
'
;
import
{
healthStatusQueries
}
from
'
ee/sidebar/constants
'
;
Vue
.
use
(
Vuex
);
const
mutate
=
jest
.
fn
().
mockResolvedValue
(
);
describe
(
'
SidebarStatus
'
,
()
=>
{
let
mediator
;
let
wrapper
;
const
createMediator
=
(
states
)
=>
{
mediator
=
{
updateStatus
:
jest
.
fn
().
mockResolvedValue
(),
store
:
{
isFetching
:
{
status
:
true
,
const
createWrapper
=
({
issuableType
=
'
issue
'
,
state
=
'
opened
'
,
healthStatus
=
'
onTrack
'
,
loading
=
false
,
}
=
{})
=>
{
const
$apollo
=
{
queries
:
{
issuableData
:
{
loading
,
},
status
:
''
,
...
states
,
},
mutate
,
};
};
const
createWrapper
=
({
noteableState
}
=
{})
=>
{
const
store
=
new
Vuex
.
Store
({
getters
:
{
getNoteableData
:
()
=>
({
state
:
noteableState
}),
},
});
wrapper
=
shallowMount
(
SidebarStatus
,
{
propsData
:
{
mediator
,
issuableType
,
iid
:
'
1
'
,
fullPath
:
'
foo/bar
'
,
canUpdate
:
true
,
},
data
()
{
return
{
issuableData
:
{
state
,
healthStatus
,
},
};
},
sync
:
false
,
mocks
:
{
$apollo
},
stubs
:
{
ApolloMutation
,
ApolloQuery
,
},
store
,
});
};
beforeEach
(()
=>
{
createMediator
();
createWrapper
({
getters
:
{
getNoteableData
:
{},
},
});
createWrapper
();
});
afterEach
(()
=>
{
...
...
@@ -53,17 +58,16 @@ describe('SidebarStatus', () => {
describe
(
'
computed
'
,
()
=>
{
describe
.
each
`
noteableState
| isOpen
state
| isOpen
${
'
opened
'
}
|
${
true
}
${
'
reopened
'
}
|
${
true
}
${
'
closed
'
}
|
${
false
}
`
(
'
isOpen
'
,
({
noteableS
tate
,
isOpen
})
=>
{
`
(
'
isOpen
'
,
({
s
tate
,
isOpen
})
=>
{
beforeEach
(()
=>
{
createMediator
({
editable
:
true
});
createWrapper
({
noteableState
});
createWrapper
({
state
});
});
it
(
`returns
${
isOpen
}
when issue is
${
noteableS
tate
}
`
,
()
=>
{
it
(
`returns
${
isOpen
}
when issue is
${
s
tate
}
`
,
()
=>
{
expect
(
wrapper
.
vm
.
isOpen
).
toBe
(
isOpen
);
});
});
...
...
@@ -76,10 +80,21 @@ describe('SidebarStatus', () => {
expect
(
wrapper
.
find
(
Status
).
exists
()).
toBe
(
true
);
});
it
(
'
calls
mediator status upd
ate when receiving an onDropdownClick event from Status component
'
,
()
=>
{
it
(
'
calls
apollo mut
ate when receiving an onDropdownClick event from Status component
'
,
()
=>
{
wrapper
.
find
(
Status
).
vm
.
$emit
(
'
onDropdownClick
'
,
'
onTrack
'
);
expect
(
mediator
.
updateStatus
).
toHaveBeenCalledWith
(
'
onTrack
'
);
const
mutationVariables
=
{
mutation
:
healthStatusQueries
.
issue
.
mutation
,
update
:
expect
.
anything
(),
optimisticResponse
:
expect
.
anything
(),
variables
:
{
projectPath
:
'
foo/bar
'
,
iid
:
'
1
'
,
healthStatus
:
'
onTrack
'
,
},
};
expect
(
mutate
).
toHaveBeenCalledWith
(
mutationVariables
);
});
});
});
ee/spec/frontend/sidebar/ee_sidebar_mediator_spec.js
View file @
7c42b1e0
import
SidebarMediator
from
'
ee/sidebar/sidebar_mediator
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
SidebarService
from
'
~/sidebar/services/sidebar_service
'
;
import
CESidebarMediator
from
'
~/sidebar/sidebar_mediator
'
;
import
CESidebarStore
from
'
~/sidebar/stores/sidebar_store
'
;
...
...
@@ -27,28 +26,4 @@ describe('EE Sidebar mediator', () => {
expect
(
mediator
.
store
.
weight
).
toBe
(
mockData
.
weight
);
expect
(
mediator
.
store
.
status
).
toBe
(
mockGraphQlData
.
project
.
issue
.
healthStatus
);
});
it
(
'
updates status when updateStatus is called
'
,
()
=>
{
const
healthStatus
=
'
onTrack
'
;
jest
.
spyOn
(
mediator
.
service
,
'
updateWithGraphQl
'
).
mockReturnValue
(
Promise
.
resolve
({
data
:
{
updateIssue
:
{
issue
:
{
healthStatus
,
},
},
},
}),
);
expect
(
mediator
.
store
.
status
).
toBe
(
''
);
mediator
.
updateStatus
(
healthStatus
);
return
waitForPromises
().
then
(()
=>
{
expect
(
mediator
.
store
.
status
).
toBe
(
healthStatus
);
});
});
});
locale/gitlab.pot
View file @
7c42b1e0
...
...
@@ -8277,6 +8277,9 @@ msgstr ""
msgid "Configure the %{link} integration."
msgstr ""
msgid "Configure the default first day of the week and time tracking units."
msgstr ""
msgid "Configure the way a user creates a new account."
msgstr ""
...
...
@@ -11524,6 +11527,9 @@ msgstr ""
msgid "Display source"
msgstr ""
msgid "Display time tracking in issues in total hours only."
msgstr ""
msgid "Do not display offers from third parties"
msgstr ""
...
...
@@ -12817,6 +12823,9 @@ msgstr ""
msgid "Error occurred when saving reviewers"
msgstr ""
msgid "Error occurred while updating the %{issuableType} status"
msgstr ""
msgid "Error occurred while updating the issue status"
msgstr ""
...
...
@@ -30456,6 +30465,9 @@ msgstr ""
msgid "Something went wrong while setting %{issuableType} confidentiality."
msgstr ""
msgid "Something went wrong while setting %{issuableType} health status."
msgstr ""
msgid "Something went wrong while setting %{issuableType} notifications."
msgstr ""
...
...
@@ -35895,9 +35907,6 @@ msgstr ""
msgid "Various email settings."
msgstr ""
msgid "Various localization settings."
msgstr ""
msgid "Various settings that affect GitLab performance."
msgstr ""
...
...
@@ -36721,6 +36730,9 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
msgid "What is time tracking?"
msgstr ""
msgid "What is your job title? (optional)"
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