Commit e7c254d0 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Kushal Pandya

Expose availability in issue sidebar assignees

Extract user status from hidden data

Extracts the user availability status for assignees
from the hidden input field
parent 931ad1c6
...@@ -51,7 +51,7 @@ export default { ...@@ -51,7 +51,7 @@ export default {
<div> <div>
<collapsed-assignee-list :users="sortedAssigness" :issuable-type="issuableType" /> <collapsed-assignee-list :users="sortedAssigness" :issuable-type="issuableType" />
<div class="value hide-collapsed"> <div data-testid="expanded-assignee" class="value hide-collapsed">
<template v-if="hasNoUsers"> <template v-if="hasNoUsers">
<span class="assign-yourself no-value qa-assign-yourself"> <span class="assign-yourself no-value qa-assign-yourself">
{{ __('None') }} {{ __('None') }}
......
...@@ -44,6 +44,11 @@ export default { ...@@ -44,6 +44,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
assigneeAvailabilityStatus: {
type: Object,
required: false,
default: () => ({}),
},
}, },
data() { data() {
return { return {
...@@ -101,6 +106,13 @@ export default { ...@@ -101,6 +106,13 @@ export default {
return new Flash(__('Error occurred when saving assignees')); return new Flash(__('Error occurred when saving assignees'));
}); });
}, },
exposeAvailabilityStatus(users) {
return users.map(({ username, ...rest }) => ({
...rest,
username,
availability: this.assigneeAvailabilityStatus[username] || '',
}));
},
}, },
}; };
</script> </script>
...@@ -123,7 +135,7 @@ export default { ...@@ -123,7 +135,7 @@ export default {
<assignees <assignees
v-if="!store.isFetching.assignees" v-if="!store.isFetching.assignees"
:root-path="relativeUrlRoot" :root-path="relativeUrlRoot"
:users="store.assignees" :users="exposeAvailabilityStatus(store.assignees)"
:editable="store.editable" :editable="store.editable"
:issuable-type="issuableType" :issuable-type="issuableType"
class="value" class="value"
......
...@@ -30,6 +30,28 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op ...@@ -30,6 +30,28 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op
return JSON.parse(sidebarOptEl.innerHTML); return JSON.parse(sidebarOptEl.innerHTML);
} }
/**
* Extracts the list of assignees with availability information from a hidden input
* field and converts to a key:value pair for use in the sidebar assignees component.
* The assignee username is used as the key and their busy status is the value
*
* e.g { root: 'busy', admin: '' }
*
* @returns {Object}
*/
function getSidebarAssigneeAvailabilityData() {
const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input');
return Array.from(sidebarAssigneeEl)
.map((el) => el.dataset)
.reduce(
(acc, { username, availability = '' }) => ({
...acc,
[username]: availability,
}),
{},
);
}
function mountAssigneesComponent(mediator) { function mountAssigneesComponent(mediator) {
const el = document.getElementById('js-vue-sidebar-assignees'); const el = document.getElementById('js-vue-sidebar-assignees');
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
...@@ -39,6 +61,7 @@ function mountAssigneesComponent(mediator) { ...@@ -39,6 +61,7 @@ function mountAssigneesComponent(mediator) {
if (!el) return; if (!el) return;
const { iid, fullPath } = getSidebarOptions(); const { iid, fullPath } = getSidebarOptions();
const assigneeAvailabilityStatus = getSidebarAssigneeAvailabilityData();
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Vue({ new Vue({
el, el,
...@@ -56,6 +79,7 @@ function mountAssigneesComponent(mediator) { ...@@ -56,6 +79,7 @@ function mountAssigneesComponent(mediator) {
signedIn: el.hasAttribute('data-signed-in'), signedIn: el.hasAttribute('data-signed-in'),
issuableType: issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage() ? 'issue' : 'merge_request', isInIssuePage() || isInIncidentPage() || isInDesignPage() ? 'issue' : 'merge_request',
assigneeAvailabilityStatus,
}, },
}), }),
}); });
......
...@@ -9,6 +9,7 @@ import { ...@@ -9,6 +9,7 @@ import {
AJAX_USERS_SELECT_PARAMS_MAP, AJAX_USERS_SELECT_PARAMS_MAP,
} from 'ee_else_ce/users_select/constants'; } from 'ee_else_ce/users_select/constants';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { isUserBusy } from '~/set_status_modal/utils';
import { fixTitle, dispose } from '~/tooltips'; import { fixTitle, dispose } from '~/tooltips';
import ModalStore from '../boards/stores/modal_store'; import ModalStore from '../boards/stores/modal_store';
import axios from '../lib/utils/axios_utils'; import axios from '../lib/utils/axios_utils';
...@@ -795,13 +796,17 @@ UsersSelect.prototype.renderRow = function ( ...@@ -795,13 +796,17 @@ UsersSelect.prototype.renderRow = function (
? `data-container="body" data-placement="left" data-title="${tooltip}"` ? `data-container="body" data-placement="left" data-title="${tooltip}"`
: ''; : '';
const name =
user?.availability && isUserBusy(user.availability)
? sprintf(__('%{name} (Busy)'), { name: user.name })
: user.name;
return ` return `
<li data-user-id=${user.id}> <li data-user-id=${user.id}>
<a href="#" class="dropdown-menu-user-link d-flex align-items-center ${linkClasses}" ${tooltipAttributes}> <a href="#" class="dropdown-menu-user-link d-flex align-items-center ${linkClasses}" ${tooltipAttributes}>
${this.renderRowAvatar(issuableType, user, img)} ${this.renderRowAvatar(issuableType, user, img)}
<span class="d-flex flex-column overflow-hidden"> <span class="d-flex flex-column overflow-hidden">
<strong class="dropdown-menu-user-full-name gl-font-weight-bold"> <strong class="dropdown-menu-user-full-name gl-font-weight-bold">
${escape(user.name)} ${escape(name)}
</strong> </strong>
${ ${
username username
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
= _('Assignee') = _('Assignee')
= loading_icon(css_class: 'gl-vertical-align-text-bottom') = loading_icon(css_class: 'gl-vertical-align-text-bottom')
.selectbox.hide-collapsed .js-sidebar-assignee-data.selectbox.hide-collapsed
- if assignees.none? - if assignees.none?
= hidden_field_tag "#{issuable_type}[assignee_ids][]", 0, id: nil = hidden_field_tag "#{issuable_type}[assignee_ids][]", 0, id: nil
- else - else
......
---
title: Display user busy status in issue sidebar
merge_request: 54165
author:
type: added
...@@ -199,6 +199,38 @@ RSpec.describe 'User edit profile' do ...@@ -199,6 +199,38 @@ RSpec.describe 'User edit profile' do
expect(busy_status.checked?).to eq(true) expect(busy_status.checked?).to eq(true)
end end
context 'with user status set to busy' do
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, author: user) }
before do
toggle_busy_status
submit_settings
project.add_developer(user)
visit project_issue_path(project, issue)
end
it 'shows author as busy in the assignee dropdown' do
find('.block.assignee .edit-link').click
wait_for_requests
page.within '.dropdown-menu-user' do
expect(page).to have_content("#{user.name} (Busy)")
end
end
it 'displays the assignee busy status' do
click_button 'assign yourself'
wait_for_requests
visit project_issue_path(project, issue)
wait_for_requests
expect(page.find('[data-testid="expanded-assignee"]')).to have_text("#{user.name} (Busy)")
end
end
context 'with set_user_availability_status feature flag disabled' do context 'with set_user_availability_status feature flag disabled' do
before do before do
stub_feature_flags(set_user_availability_status: false) stub_feature_flags(set_user_availability_status: false)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment