Открыть боковую панель
GitLab.org
Gitlab
Коммиты
f99409d2
Коммит
f99409d2
создал
Сен 12, 2022
по автору
Peter Hegman
Зафиксировано автором
David Pisek
Сен 12, 2022
Просмотр файлов
Add clear status after dropdown to profile set status form
Also refactors to Vue Changelog: added
владелец
13d56e85
Изменения
25
Скрыть пробелы
Построчно
Рядом
app/assets/javascripts/pages/profiles/show/emoji_menu.js
удалено
100644 → 0
Просмотр файла @
13d56e85
import
'
~/commons/bootstrap
'
;
import
{
AwardsHandler
}
from
'
~/awards_handler
'
;
class
EmojiMenu
extends
AwardsHandler
{
constructor
(
emoji
,
toggleButtonSelector
,
menuClass
,
selectEmojiCallback
)
{
super
(
emoji
);
this
.
selectEmojiCallback
=
selectEmojiCallback
;
this
.
toggleButtonSelector
=
toggleButtonSelector
;
this
.
menuClass
=
menuClass
;
}
postEmoji
(
$emojiButton
,
awardUrl
,
selectedEmoji
,
callback
)
{
this
.
selectEmojiCallback
(
selectedEmoji
,
this
.
emoji
.
glEmojiTag
(
selectedEmoji
));
callback
();
}
}
export
default
EmojiMenu
;
app/assets/javascripts/pages/profiles/show/index.js
Просмотр файла @
f99409d2
import
emojiRegex
from
'
emoji-regex
'
;
import
$
from
'
jquery
'
;
import
GfmAutoComplete
from
'
ee_else_ce/gfm_auto_complete
'
;
import
*
as
Emoji
from
'
~/emoji
'
;
import
createFlash
from
'
~/flash
'
;
import
{
__
}
from
'
~/locale
'
;
import
EmojiMenu
from
'
./emoji_menu
'
;
import
{
initSetStatusForm
}
from
'
~/profile/profile
'
;
const
defaultStatusEmoji
=
'
speech_balloon
'
;
const
toggleEmojiMenuButtonSelector
=
'
.js-toggle-emoji-menu
'
;
const
toggleEmojiMenuButton
=
document
.
querySelector
(
toggleEmojiMenuButtonSelector
);
const
statusEmojiField
=
document
.
getElementById
(
'
js-status-emoji-field
'
);
const
statusMessageField
=
document
.
getElementById
(
'
js-status-message-field
'
);
const
toggleNoEmojiPlaceholder
=
(
isVisible
)
=>
{
const
placeholderElement
=
document
.
getElementById
(
'
js-no-emoji-placeholder
'
);
placeholderElement
.
classList
.
toggle
(
'
hidden
'
,
!
isVisible
);
};
const
findStatusEmoji
=
()
=>
toggleEmojiMenuButton
.
querySelector
(
'
gl-emoji
'
);
const
removeStatusEmoji
=
()
=>
{
const
statusEmoji
=
findStatusEmoji
();
if
(
statusEmoji
)
{
statusEmoji
.
remove
();
}
};
const
selectEmojiCallback
=
(
emoji
,
emojiTag
)
=>
{
statusEmojiField
.
value
=
emoji
;
toggleNoEmojiPlaceholder
(
false
);
removeStatusEmoji
();
// eslint-disable-next-line no-unsanitized/property
toggleEmojiMenuButton
.
innerHTML
+=
emojiTag
;
};
const
clearEmojiButton
=
document
.
getElementById
(
'
js-clear-user-status-button
'
);
clearEmojiButton
.
addEventListener
(
'
click
'
,
()
=>
{
statusEmojiField
.
value
=
''
;
statusMessageField
.
value
=
''
;
removeStatusEmoji
();
toggleNoEmojiPlaceholder
(
true
);
});
const
emojiAutocomplete
=
new
GfmAutoComplete
();
emojiAutocomplete
.
setup
(
$
(
statusMessageField
),
{
emojis
:
true
});
initSetStatusForm
();
const
userNameInput
=
document
.
getElementById
(
'
user_name
'
);
userNameInput
.
addEventListener
(
'
input
'
,
()
=>
{
const
EMOJI_REGEX
=
emojiRegex
();
if
(
EMOJI_REGEX
.
test
(
userNameInput
.
value
))
{
// set field to invalid so it gets detected by GlFieldErrors
userNameInput
.
setCustomValidity
(
__
(
'
Invalid field
'
));
}
else
{
userNameInput
.
setCustomValidity
(
''
);
}
});
Emoji
.
initEmojiMap
()
.
then
(()
=>
{
const
emojiMenu
=
new
EmojiMenu
(
Emoji
,
toggleEmojiMenuButtonSelector
,
'
js-status-emoji-menu
'
,
selectEmojiCallback
,
);
emojiMenu
.
bindEvents
();
const
defaultEmojiTag
=
Emoji
.
glEmojiTag
(
defaultStatusEmoji
);
statusMessageField
.
addEventListener
(
'
input
'
,
()
=>
{
const
hasStatusMessage
=
statusMessageField
.
value
.
trim
()
!==
''
;
const
statusEmoji
=
findStatusEmoji
();
if
(
hasStatusMessage
&&
statusEmoji
)
{
return
;
}
if
(
hasStatusMessage
)
{
toggleNoEmojiPlaceholder
(
false
);
// eslint-disable-next-line no-unsanitized/property
toggleEmojiMenuButton
.
innerHTML
+=
defaultEmojiTag
;
}
else
if
(
statusEmoji
.
dataset
.
name
===
defaultStatusEmoji
)
{
toggleNoEmojiPlaceholder
(
true
);
removeStatusEmoji
();
}
});
})
.
catch
(()
=>
createFlash
({
message
:
__
(
'
Failed to load emoji list.
'
),
}),
);
if
(
userNameInput
)
{
userNameInput
.
addEventListener
(
'
input
'
,
()
=>
{
const
EMOJI_REGEX
=
emojiRegex
();
if
(
EMOJI_REGEX
.
test
(
userNameInput
.
value
))
{
// set field to invalid so it gets detected by GlFieldErrors
userNameInput
.
setCustomValidity
(
__
(
'
Invalid field
'
));
}
else
{
userNameInput
.
setCustomValidity
(
''
);
}
});
}
app/assets/javascripts/profile/profile.js
Просмотр файла @
f99409d2
import
$
from
'
jquery
'
;
import
Vue
from
'
vue
'
;
import
{
VARIANT_DANGER
,
VARIANT_INFO
,
createAlert
}
from
'
~/flash
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
{
parseRailsFormFields
}
from
'
~/lib/utils/forms
'
;
import
{
Rails
}
from
'
~/lib/utils/rails_ujs
'
;
import
TimezoneDropdown
,
{
formatTimezone
,
}
from
'
~/pages/projects/pipeline_schedules/shared/components/timezone_dropdown
'
;
import
UserProfileSetStatusWrapper
from
'
~/set_status_modal/user_profile_set_status_wrapper.vue
'
;
export
default
class
Profile
{
constructor
({
form
}
=
{})
{
...
...
@@ -116,3 +119,24 @@ export default class Profile {
}
}
}
export
const
initSetStatusForm
=
()
=>
{
const
el
=
document
.
getElementById
(
'
js-user-profile-set-status-form
'
);
if
(
!
el
)
{
return
null
;
}
const
fields
=
parseRailsFormFields
(
el
);
return
new
Vue
({
el
,
name
:
'
UserProfileStatusForm
'
,
provide
:
{
fields
,
},
render
(
h
)
{
return
h
(
UserProfileSetStatusWrapper
);
},
});
};
app/assets/javascripts/set_status_modal/constants.js
0 → 100644
Просмотр файла @
f99409d2
import
{
timeRanges
}
from
'
~/vue_shared/constants
'
;
import
{
__
}
from
'
~/locale
'
;
export
const
NEVER_TIME_RANGE
=
{
label
:
__
(
'
Never
'
),
name
:
'
never
'
,
};
export
const
TIME_RANGES_WITH_NEVER
=
[
NEVER_TIME_RANGE
,
...
timeRanges
];
export
const
AVAILABILITY_STATUS
=
{
BUSY
:
'
busy
'
,
NOT_SET
:
'
not_set
'
,
};
app/assets/javascripts/set_status_modal/set_status_form.vue
Просмотр файла @
f99409d2
...
...
@@ -9,26 +9,14 @@ import {
GlDropdown
,
GlDropdownItem
,
GlSprintf
,
GlFormGroup
,
GlSafeHtmlDirective
,
}
from
'
@gitlab/ui
'
;
import
$
from
'
jquery
'
;
import
GfmAutoComplete
from
'
ee_else_ce/gfm_auto_complete
'
;
import
*
as
Emoji
from
'
~/emoji
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
{
timeRanges
}
from
'
~/vue_shared/constants
'
;
export
const
AVAILABILITY_STATUS
=
{
BUSY
:
'
busy
'
,
NOT_SET
:
'
not_set
'
,
};
const
statusTimeRanges
=
[
{
label
:
__
(
'
Never
'
),
name
:
'
never
'
,
},
...
timeRanges
,
];
import
{
s__
}
from
'
~/locale
'
;
import
{
TIME_RANGES_WITH_NEVER
,
AVAILABILITY_STATUS
}
from
'
./constants
'
;
export
default
{
components
:
{
...
...
@@ -40,6 +28,7 @@ export default {
GlDropdown
,
GlDropdownItem
,
GlSprintf
,
GlFormGroup
,
EmojiPicker
:
()
=>
import
(
'
~/emoji/components/picker.vue
'
),
},
directives
:
{
...
...
@@ -136,7 +125,8 @@ export default {
this
.
clearEmoji
();
},
},
statusTimeRanges
,
TIME_RANGES_WITH_NEVER
,
AVAILABILITY_STATUS
,
safeHtmlConfig
:
{
ADD_TAGS
:
[
'
gl-emoji
'
]
},
i18n
:
{
statusMessagePlaceholder
:
s__
(
`SetStatusModal|What's your status?`
),
...
...
@@ -153,14 +143,11 @@ export default {
<
template
>
<div>
<input
:value=
"emoji"
class=
"js-status-emoji-field"
type=
"hidden"
name=
"user[status][emoji]"
/>
<gl-form-input-group
class=
"gl-mb-5"
>
<gl-form-input
ref=
"statusMessageField"
:value=
"message"
:placeholder=
"$options.i18n.statusMessagePlaceholder"
class=
"js-status-message-field"
name=
"user[status][message]"
@
keyup=
"setDefaultEmoji"
@
input=
"$emit('message-input', $event)"
@
keyup.enter.prevent
...
...
@@ -216,28 +203,29 @@ export default {
</
template
>
</gl-form-checkbox>
<div
class=
"form-group"
>
<div
class=
"gl-display-flex gl-align-items-baseline"
>
<span
class=
"gl-mr-3"
>
{{ $options.i18n.clearStatusAfterDropdownLabel }}
</span>
<gl-dropdown
:text=
"clearStatusAfter.label"
data-testid=
"clear-status-at-dropdown"
>
<gl-dropdown-item
v-for=
"after in $options.statusTimeRanges"
:key=
"after.name"
:data-testid=
"after.name"
@
click=
"$emit('clear-status-after-click', after)"
>
{{ after.label }}
</gl-dropdown-item
>
</gl-dropdown>
</div>
<p
v-if=
"currentClearStatusAfter.length"
class=
"gl-mt-3 gl-text-gray-400 gl-font-sm"
data-testid=
"clear-status-at-message"
<gl-form-group
:label=
"$options.i18n.clearStatusAfterDropdownLabel"
class=
"gl-mb-0"
>
<gl-dropdown
block
:text=
"clearStatusAfter.label"
data-testid=
"clear-status-at-dropdown"
toggle-class=
"gl-mb-0 gl-form-input-md"
>
<gl-sprintf
:message=
"$options.i18n.clearStatusAfterMessage"
>
<
template
#date
>
{{
currentClearStatusAfter
}}
</
template
>
</gl-sprintf>
</p>
</div>
<gl-dropdown-item
v-for=
"after in $options.TIME_RANGES_WITH_NEVER"
:key=
"after.name"
:data-testid=
"after.name"
@
click=
"$emit('clear-status-after-click', after)"
>
{{ after.label }}
</gl-dropdown-item
>
</gl-dropdown>
<
template
v-if=
"currentClearStatusAfter.length"
#description
>
<span
data-testid=
"clear-status-at-message"
>
<gl-sprintf
:message=
"$options.i18n.clearStatusAfterMessage"
>
<template
#date
>
{{
currentClearStatusAfter
}}
</
template
>
</gl-sprintf>
</span>
</template>
</gl-form-group>
</div>
</template>
app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
Просмотр файла @
f99409d2
...
...
@@ -3,28 +3,15 @@ import { GlToast, GlTooltipDirective, GlSafeHtmlDirective, GlModal } from '@gitl
import
Vue
from
'
vue
'
;
import
createFlash
from
'
~/flash
'
;
import
{
BV_SHOW_MODAL
,
BV_HIDE_MODAL
}
from
'
~/lib/utils/constants
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
updateUserStatus
}
from
'
~/rest_api
'
;
import
{
timeRanges
}
from
'
~/vue_shared/constants
'
;
import
glFeatureFlagsMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
{
isUserBusy
}
from
'
./utils
'
;
import
{
NEVER_TIME_RANGE
,
AVAILABILITY_STATUS
}
from
'
./constants
'
;
import
SetStatusForm
from
'
./set_status_form.vue
'
;
export
const
AVAILABILITY_STATUS
=
{
BUSY
:
'
busy
'
,
NOT_SET
:
'
not_set
'
,
};
Vue
.
use
(
GlToast
);
const
statusTimeRanges
=
[
{
label
:
__
(
'
Never
'
),
name
:
'
never
'
,
},
...
timeRanges
,
];
export
default
{
components
:
{
GlModal
,
...
...
@@ -67,7 +54,7 @@ export default {
message
:
this
.
currentMessage
,
modalId
:
'
set-user-status-modal
'
,
availability
:
isUserBusy
(
this
.
currentAvailability
),
clearStatusAfter
:
statusTimeRanges
[
0
]
,
clearStatusAfter
:
NEVER_TIME_RANGE
,
};
},
mounted
()
{
...
...
@@ -91,7 +78,7 @@ export default {
message
,
availability
:
availability
?
AVAILABILITY_STATUS
.
BUSY
:
AVAILABILITY_STATUS
.
NOT_SET
,
clearStatusAfter
:
clearStatusAfter
.
label
===
statusTimeRanges
[
0
]
.
label
?
null
:
clearStatusAfter
.
shortcut
,
clearStatusAfter
.
label
===
NEVER_TIME_RANGE
.
label
?
null
:
clearStatusAfter
.
shortcut
,
})
.
then
(
this
.
onUpdateSuccess
)
.
catch
(
this
.
onUpdateFail
);
...
...
@@ -123,7 +110,6 @@ export default {
this
.
availability
=
value
;
},
},
statusTimeRanges
,
safeHtmlConfig
:
{
ADD_TAGS
:
[
'
gl-emoji
'
]
},
actionPrimary
:
{
text
:
s__
(
'
SetStatusModal|Set status
'
)
},
actionSecondary
:
{
text
:
s__
(
'
SetStatusModal|Remove status
'
)
},
...
...
app/assets/javascripts/set_status_modal/user_profile_set_status_wrapper.vue
0 → 100644
Просмотр файла @
f99409d2
<
script
>
import
{
secondsToMilliseconds
}
from
'
~/lib/utils/datetime_utility
'
;
import
dateFormat
from
'
~/lib/dateformat
'
;
import
SetStatusForm
from
'
./set_status_form.vue
'
;
import
{
isUserBusy
}
from
'
./utils
'
;
import
{
NEVER_TIME_RANGE
,
AVAILABILITY_STATUS
}
from
'
./constants
'
;
export
default
{
components
:
{
SetStatusForm
},
inject
:
[
'
fields
'
],
data
()
{
return
{
emoji
:
this
.
fields
.
emoji
.
value
,
message
:
this
.
fields
.
message
.
value
,
availability
:
isUserBusy
(
this
.
fields
.
availability
.
value
),
clearStatusAfter
:
NEVER_TIME_RANGE
,
currentClearStatusAfter
:
this
.
fields
.
clearStatusAfter
.
value
,
};
},
computed
:
{
clearStatusAfterInputValue
()
{
return
this
.
clearStatusAfter
.
label
===
NEVER_TIME_RANGE
.
label
?
null
:
this
.
clearStatusAfter
.
shortcut
;
},
availabilityInputValue
()
{
return
this
.
availability
?
this
.
$options
.
AVAILABILITY_STATUS
.
BUSY
:
this
.
$options
.
AVAILABILITY_STATUS
.
NOT_SET
;
},
},
mounted
()
{
this
.
$options
.
formEl
=
document
.
querySelector
(
'
form.js-edit-user
'
);
if
(
!
this
.
$options
.
formEl
)
return
;
this
.
$options
.
formEl
.
addEventListener
(
'
ajax:success
'
,
this
.
handleFormSuccess
);
},
beforeDestroy
()
{
if
(
!
this
.
$options
.
formEl
)
return
;
this
.
$options
.
formEl
.
removeEventListener
(
'
ajax:success
'
,
this
.
handleFormSuccess
);
},
methods
:
{
handleMessageInput
(
value
)
{
this
.
message
=
value
;
},
handleEmojiClick
(
emoji
)
{
this
.
emoji
=
emoji
;
},
handleClearStatusAfterClick
(
after
)
{
this
.
clearStatusAfter
=
after
;
},
handleAvailabilityInput
(
value
)
{
this
.
availability
=
value
;
},
handleFormSuccess
()
{
if
(
!
this
.
clearStatusAfter
?.
duration
?.
seconds
)
{
this
.
currentClearStatusAfter
=
''
;
return
;
}
const
now
=
new
Date
();
const
currentClearStatusAfterDate
=
new
Date
(
now
.
getTime
()
+
secondsToMilliseconds
(
this
.
clearStatusAfter
.
duration
.
seconds
),
);
this
.
currentClearStatusAfter
=
dateFormat
(
currentClearStatusAfterDate
,
"
UTC:yyyy-mm-dd HH:MM:ss 'UTC'
"
,
);
this
.
clearStatusAfter
=
NEVER_TIME_RANGE
;
},
},
AVAILABILITY_STATUS
,
formEl
:
null
,
};
</
script
>
<
template
>
<div>
<input
:value=
"emoji"
type=
"hidden"
:name=
"fields.emoji.name"
/>
<input
:value=
"message"
type=
"hidden"
:name=
"fields.message.name"
/>
<input
:value=
"availabilityInputValue"
type=
"hidden"
:name=
"fields.availability.name"
/>
<input
:value=
"clearStatusAfterInputValue"
type=
"hidden"
:name=
"fields.clearStatusAfter.name"
/>
<set-status-form
default-emoji=
"speech_balloon"
:emoji=
"emoji"
:message=
"message"
:availability=
"availability"
:clear-status-after=
"clearStatusAfter"
:current-clear-status-after=
"currentClearStatusAfter"
@
message-input=
"handleMessageInput"
@
emoji-click=
"handleEmojiClick"
@
clear-status-after-click=
"handleClearStatusAfterClick"
@
availability-input=
"handleAvailabilityInput"
/>
</div>
</
template
>
app/assets/javascripts/set_status_modal/utils.js
Просмотр файла @
f99409d2
export
const
AVAILABILITY_STATUS
=
{
BUSY
:
'
busy
'
,
NOT_SET
:
'
not_set
'
,
};
import
{
AVAILABILITY_STATUS
}
from
'
./constants
'
;
export
const
isUserBusy
=
(
status
=
''
)
=>
Boolean
(
status
.
length
&&
status
.
toLowerCase
().
trim
()
===
AVAILABILITY_STATUS
.
BUSY
);
app/controllers/profiles_controller.rb
Просмотр файла @
f99409d2
...
...
@@ -137,7 +137,7 @@ def user_params_attributes
:pronouns
,
:pronunciation
,
:validation_password
,
status:
[
:emoji
,
:message
,
:availability
]
status:
[
:emoji
,
:message
,
:availability
,
:clear_status_after
]
]
end
...
...
app/models/user_status.rb
Просмотр файла @
f99409d2
...
...
@@ -29,6 +29,10 @@ class UserStatus < ApplicationRecord
cache_markdown_field
:message
,
pipeline: :emoji
def
clear_status_after
clear_status_at
end
def
clear_status_after
=
(
value
)
self
.
clear_status_at
=
CLEAR_STATUS_QUICK_OPTIONS
[
value
]
&
.
from_now
end
...
...
app/views/profiles/show.html.haml
Просмотр файла @
f99409d2
...
...
@@ -2,8 +2,6 @@
-
page_title
s_
(
"Profiles|Edit Profile"
)
-
@content_class
=
"limit-container-width"
unless
fluid_layout
-
gravatar_link
=
link_to
Gitlab
.
config
.
gravatar
.
host
,
'https://'
+
Gitlab
.
config
.
gravatar
.
host
-
availability
=
availability_values
-
custom_emoji
=
@user
.
status
&
.
customized?
=
gitlab_ui_form_for
@user
,
url:
profile_path
,
method: :put
,
html:
{
multipart:
true
,
class:
'edit-user js-edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form'
,
remote:
true
},
authenticity_token:
true
do
|
f
|
.row.js-search-settings-section
...
...
@@ -43,39 +41,12 @@
%h4
.gl-mt-0
=
s_
(
"Profiles|Current status"
)
%p
=
s_
(
"Profiles|This emoji and message will appear on your profile and throughout the interface."
)
.col-lg-8
=
f
.
fields_for
:status
,
@user
.
status
do
|
status_form
|
-
emoji_button
=
render
Pajamas
::
ButtonComponent
.
new
(
button_options:
{
title:
s_
(
"Profiles|Add status emoji"
),
class:
'js-toggle-emoji-menu emoji-menu-toggle-button has-tooltip'
}
)
do
-
if
custom_emoji
=
emoji_icon
(
@user
.
status
.
emoji
,
class:
'gl-mr-0!'
)
%span
#js-no-emoji-placeholder
.no-emoji-placeholder
{
class:
(
'hidden'
if
custom_emoji
)
}
=
sprite_icon
(
'slight-smile'
,
css_class:
'award-control-icon-neutral'
)
=
sprite_icon
(
'smiley'
,
css_class:
'award-control-icon-positive'
)
=
sprite_icon
(
'smile'
,
css_class:
'award-control-icon-super-positive'
)
-
reset_message_button
=
render
Pajamas
::
ButtonComponent
.
new
(
icon:
'close'
,
button_options:
{
id:
'js-clear-user-status-button'
,
class:
'has-tooltip'
,
title:
s_
(
"Profiles|Clear status"
)
}
)
=
status_form
.
hidden_field
:emoji
,
id:
'js-status-emoji-field'
.form-group.gl-form-group
=
status_form
.
label
:message
,
s_
(
"Profiles|Your status"
)
.input-group
{
role:
'group'
}
.input-group-prepend
=
emoji_button
=
status_form
.
text_field
:message
,
id:
'js-status-message-field'
,
class:
'form-control gl-form-input input-lg'
,
placeholder:
s_
(
"Profiles|What's your status?"
)
.input-group-append
=
reset_message_button
.form-group.gl-form-group
=
status_form
.
gitlab_ui_checkbox_component
:availability
,
s_
(
"Profiles|Busy"
),
help_text:
s_
(
'Profiles|An indicator appears next to your name and avatar.'
),
checkbox_options:
{
data:
{
testid:
"user-availability-checkbox"
}
},
checked_value:
availability
[
"busy"
],
unchecked_value:
availability
[
"not_set"
]
#js-user-profile-set-status-form
=
f
.
fields_for
:status
,
@user
.
status
do
|
status_form
|
=
status_form
.
hidden_field
:emoji
,
data:
{
js_name:
'emoji'
}
=
status_form
.
hidden_field
:message
,
data:
{
js_name:
'message'
}
=
status_form
.
hidden_field
:availability
,
data:
{
js_name:
'availability'
}
=
status_form
.
hidden_field
:clear_status_after
,
data:
{
js_name:
'clearStatusAfter'
}
.col-lg-12
%hr
.row.user-time-preferences.js-search-settings-section
...
...
doc/user/profile/index.md
Просмотр файла @
f99409d2
...
...
@@ -205,7 +205,7 @@ To set your current status:
1.
Select a value from the
**Clear status after**
dropdown list.
1.
Select
**Set status**
. Alternatively, you can select
**Remove status**
to remove your user status entirely.
You can also set your current status by
[
using the API
](
../../api/users.md#user-status
)
.
You can also set your current status
from
[
your user settings
](
#access-your-user-settings
)
or
by
[
using the API
](
../../api/users.md#user-status
)
.
If you select the
**Busy**
checkbox, remember to clear it when you become available again.
...
...
ee/spec/frontend/notes/components/note_header_spec.js
Просмотр файла @
f99409d2
...
...
@@ -4,7 +4,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
GitlabTeamMemberBadge
from
'
ee/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue
'
;
import
NoteHeader
from
'
~/notes/components/note_header.vue
'
;
import
{
AVAILABILITY_STATUS
}
from
'
~/set_status_modal/
util
s
'
;
import
{
AVAILABILITY_STATUS
}
from
'
~/set_status_modal/
constant
s
'
;
Vue
.
use
(
Vuex
);
...
...
locale/gitlab.pot
Просмотр файла @
f99409d2
...
...
@@ -16080,9 +16080,6 @@ msgstr ""
msgid "Failed to load deploy keys."
msgstr ""
msgid "Failed to load emoji list."
msgstr ""
msgid "Failed to load error details from Sentry."
msgstr ""
...
...
@@ -30166,15 +30163,9 @@ msgstr ""
msgid "Profiles|Add key"
msgstr ""
msgid "Profiles|Add status emoji"
msgstr ""
msgid "Profiles|An error occurred while updating your username, please try again."
msgstr ""
msgid "Profiles|An indicator appears next to your name and avatar."
msgstr ""
msgid "Profiles|Avatar cropper"
msgstr ""
...
...
@@ -30187,9 +30178,6 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
msgid "Profiles|Busy"
msgstr ""
msgid "Profiles|Change username"
msgstr ""
...
...
@@ -30205,9 +30193,6 @@ msgstr ""
msgid "Profiles|City, country"
msgstr ""
msgid "Profiles|Clear status"
msgstr ""
msgid "Profiles|Commit email"
msgstr ""
...
...
@@ -30457,9 +30442,6 @@ msgstr ""
msgid "Profiles|Website url"
msgstr ""
msgid "Profiles|What's your status?"
msgstr ""
msgid "Profiles|Who you represent or work for."
msgstr ""
...
...
@@ -30505,9 +30487,6 @@ msgstr ""
msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
msgstr ""
msgid "Profiles|Your status"
msgstr ""
msgid "Profiles|https://website.com"
msgstr ""
spec/controllers/profiles_controller_spec.rb
Просмотр файла @
f99409d2
...
...
@@ -82,13 +82,17 @@
expect
(
ldap_user
.
location
).
to
eq
(
'City, Country'
)
end
it
'allows setting a user status'
do
it
'allows setting a user status'
,
:freeze_time
do
sign_in
(
user
)
put
:update
,
params:
{
user:
{
status:
{
message:
'Working hard!'
,
availability:
'busy'
}
}
}
put
(
:update
,
params:
{
user:
{
status:
{
message:
'Working hard!'
,
availability:
'busy'
,
clear_status_after:
'8_hours'
}
}
}
)
expect
(
user
.
reload
.
status
.
message
).
to
eq
(
'Working hard!'
)
expect
(
user
.
reload
.
status
.
availability
).
to
eq
(
'busy'
)
expect
(
user
.
reload
.
status
.
clear_status_after
).
to
eq
(
8
.
hours
.
from_now
)
expect
(
response
).
to
have_gitlab_http_status
(
:found
)
end
...
...
spec/features/profiles/user_edit_profile_spec.rb
Просмотр файла @
f99409d2
...
...
@@ -180,7 +180,7 @@ def select_emoji(emoji_name, is_modal = false)
end
it
'adds emoji to user status'
do
emoji
=
'b
iohazard
'
emoji
=
'b
asketball
'
select_emoji
(
emoji
)
submit_settings
...
...
@@ -193,7 +193,7 @@ def select_emoji(emoji_name, is_modal = false)
it
'adds message to user status'
do
message
=
'I have something to say'
fill_in
'js-status-message-field'
,
with:
message
fill_in
s_
(
"SetStatusModal|What's your status?"
)
,
with:
message
submit_settings
visit_user
...
...
@@ -208,7 +208,7 @@ def select_emoji(emoji_name, is_modal = false)
emoji
=
'8ball'
message
=
'Playing outside'
select_emoji
(
emoji
)
fill_in
'js-status-message-field'
,
with:
message
fill_in
s_
(
"SetStatusModal|What's your status?"
)
,
with:
message
submit_settings
visit_user
...
...
@@ -230,7 +230,7 @@ def select_emoji(emoji_name, is_modal = false)
end
visit
(
profile_path
)
click_button
'js-clear-user-status-button'
click_button
s_
(
'SetStatusModal|Clear status'
)
submit_settings
visit_user
...
...
@@ -240,9 +240,9 @@ def select_emoji(emoji_name, is_modal = false)
it
'displays a default emoji if only message is entered'
do
message
=
'a status without emoji'
fill_in
'js-status-message-field'
,
with:
message
fill_in
s_
(
"SetStatusModal|What's your status?"
)
,
with:
message
within
(
'.
js-toggle-
emoji-menu'
)
do
within
(
'.emoji-menu
-toggle-button
'
)
do
expect
(
page
).
to
have_emoji
(
'speech_balloon'
)
end
end
...
...
@@ -406,7 +406,7 @@ def set_user_status_in_modal
it
'adds message to user status'
do
message
=
'I have something to say'
open_user_status_modal
find
(
'.js-status-message-field'
).
native
.
send_keys
(
message
)
find
_field
(
s_
(
"SetStatusModal|What's your status?"
)
).
native
.
send_keys
(
message
)
set_user_status_in_modal
visit_user
...
...
@@ -422,7 +422,7 @@ def set_user_status_in_modal
message
=
'Playing outside'
open_user_status_modal
select_emoji
(
emoji
,
true
)
find
(
'.js-status-message-field'
).
native
.
send_keys
(
message
)
find
_field
(
s_
(
"SetStatusModal|What's your status?"
)
).
native
.
send_keys
(
message
)
set_user_status_in_modal
visit_user
...
...
@@ -446,7 +446,7 @@ def set_user_status_in_modal
open_edit_status_modal
find
(
'.js-clear-user-status-button'
).
click
click_button
s_
(
'SetStatusModal|Clear status'
)
set_user_status_in_modal
visit_user
...
...
@@ -491,7 +491,7 @@ def set_user_status_in_modal
it
'displays a default emoji if only message is entered'
do
message
=
'a status without emoji'
open_user_status_modal
find
(
'.js-status-message-field'
).
native
.
send_keys
(
message
)
find
_field
(
s_
(
"SetStatusModal|What's your status?"
)
).
native
.
send_keys
(
message
)
expect
(
page
).
to
have_emoji
(
'speech_balloon'
)
end
...
...
spec/frontend/members/components/avatars/user_avatar_spec.js
Просмотр файла @
f99409d2
import
{
GlAvatarLink
,
GlBadge
}
from
'
@gitlab/ui
'
;
import
{
mountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
UserAvatar
from
'
~/members/components/avatars/user_avatar.vue
'
;
import
{
AVAILABILITY_STATUS
}
from
'
~/set_status_modal/
util
s
'
;
import
{
AVAILABILITY_STATUS
}
from
'
~/set_status_modal/
constant
s
'
;
import
{
member
as
memberMock
,
member2faEnabled
,
orphanedMember
}
from
'
../../mock_data
'
;
...
...
spec/frontend/notes/components/note_header_spec.js
Просмотр файла @
f99409d2
...
...
@@ -3,7 +3,7 @@ import Vue, { nextTick } from 'vue';
import
Vuex
from
'
vuex
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
NoteHeader
from
'
~/notes/components/note_header.vue
'
;
import
{
AVAILABILITY_STATUS
}
from
'
~/set_status_modal/
util
s
'
;
import
{
AVAILABILITY_STATUS
}
from
'
~/set_status_modal/
constant
s
'
;
import
UserNameWithStatus
from
'
~/sidebar/components/assignees/user_name_with_status.vue
'
;
Vue
.
use
(
Vuex
);
...
...
spec/frontend/pages/profiles/show/emoji_menu_spec.js
удалено
100644 → 0
Просмотр файла @
13d56e85
import
$
from
'
jquery
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
EmojiMenu
from
'
~/pages/profiles/show/emoji_menu
'
;
describe
(
'
EmojiMenu
'
,
()
=>
{
const
dummyEmojiTag
=
'
<dummy></tag>
'
;
const
dummyToggleButtonSelector
=
'
.toggle-button-selector
'
;
const
dummyMenuClass
=
'
dummy-menu-class
'
;
let
emojiMenu
;
let
dummySelectEmojiCallback
;
let
dummyEmojiList
;
beforeEach
(()
=>
{
dummySelectEmojiCallback
=
jest
.
fn
().
mockName
(
'
dummySelectEmojiCallback
'
);
dummyEmojiList
=
{
glEmojiTag
()
{
return
dummyEmojiTag
;
},
normalizeEmojiName
(
emoji
)
{
return
emoji
;
},
isEmojiNameValid
()
{
return
true
;
},
getEmojiCategoryMap
()
{
return
{
dummyCategory
:
[]
};
},
};
emojiMenu
=
new
EmojiMenu
(
dummyEmojiList
,
dummyToggleButtonSelector
,
dummyMenuClass
,
dummySelectEmojiCallback
,
);
});
afterEach
(()
=>
{
emojiMenu
.
destroy
();
});
describe
(
'
addAward
'
,
()
=>
{
const
dummyAwardUrl
=
`
${
TEST_HOST
}
/award/url`
;
const
dummyEmoji
=
'
tropical_fish
'
;
const
dummyVotesBlock
=
()
=>
$
(
'
<div />
'
);
it
(
'
calls selectEmojiCallback
'
,
async
()
=>
{
expect
(
dummySelectEmojiCallback
).
not
.
toHaveBeenCalled
();
await
emojiMenu
.
addAward
(
dummyVotesBlock
(),
dummyAwardUrl
,
dummyEmoji
,
false
);
expect
(
dummySelectEmojiCallback
).
toHaveBeenCalledWith
(
dummyEmoji
,
dummyEmojiTag
);
});
it
(
'
does not make an axios request
'
,
async
()
=>
{
jest
.
spyOn
(
axios
,
'
request
'
).
mockReturnValue
();
await
emojiMenu
.
addAward
(
dummyVotesBlock
(),
dummyAwardUrl
,
dummyEmoji
,
false
);
expect
(
axios
.
request
).
not
.
toHaveBeenCalled
();
});
});
describe
(
'
bindEvents
'
,
()
=>
{
beforeEach
(()
=>
{
jest
.
spyOn
(
emojiMenu
,
'
registerEventListener
'
).
mockReturnValue
();
});
it
(
'
binds event listeners to custom toggle button
'
,
()
=>
{
emojiMenu
.
bindEvents
();
expect
(
emojiMenu
.
registerEventListener
).
toHaveBeenCalledWith
(
'
one
'
,
expect
.
anything
(),
'
mouseenter focus
'
,
dummyToggleButtonSelector
,
'
mouseenter focus
'
,
expect
.
anything
(),
);
expect
(
emojiMenu
.
registerEventListener
).
toHaveBeenCalledWith
(
'
on
'
,
expect
.
anything
(),
'
click
'
,
dummyToggleButtonSelector
,
expect
.
anything
(),
);
});
it
(
'
binds event listeners to custom menu class
'
,
()
=>
{
emojiMenu
.
bindEvents
();
expect
(
emojiMenu
.
registerEventListener
).
toHaveBeenCalledWith
(
'
on
'
,
expect
.
anything
(),
'
click
'
,
`.js-awards-block .js-emoji-btn, .
${
dummyMenuClass
}
.js-emoji-btn`
,
expect
.
anything
(),
);
});
});
describe
(
'
createEmojiMenu
'
,
()
=>
{
it
(
'
renders the menu with custom menu class
'
,
()
=>
{
const
menuElement
=
()
=>
document
.
body
.
querySelector
(
`.emoji-menu.
${
dummyMenuClass
}
.emoji-menu-content`
);
expect
(
menuElement
()).
toBe
(
null
);
emojiMenu
.
createEmojiMenu
();
expect
(
menuElement
()).
not
.
toBe
(
null
);
});
});
});
spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
Просмотр файла @
f99409d2
import
{
GlModal
,
GlFormCheckbox
}
from
'
@gitlab/ui
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
nextTick
}
from
'
vue
'
;
import
{
mountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
initEmojiMock
,
clearEmojiMock
}
from
'
helpers/emoji
'
;
import
*
as
UserApi
from
'
~/api/user_api
'
;
import
EmojiPicker
from
'
~/emoji/components/picker.vue
'
;
import
createFlash
from
'
~/flash
'
;
import
stubChildren
from
'
helpers/stub_children
'
;
import
SetStatusModalWrapper
,
{
AVAILABILITY_STATUS
,
}
from
'
~/set_status_modal/set_status_modal_wrapper.vue
'
;
import
SetStatusModalWrapper
from
'
~/set_status_modal/set_status_modal_wrapper.vue
'
;
import
{
AVAILABILITY_STATUS
}
from
'
~/set_status_modal/constants
'
;
import
SetStatusForm
from
'
~/set_status_modal/set_status_form.vue
'
;
jest
.
mock
(
'
~/flash
'
);
...
...
@@ -34,7 +33,7 @@ describe('SetStatusModalWrapper', () => {
};
const
createComponent
=
(
props
=
{})
=>
{
return
mount
(
SetStatusModalWrapper
,
{
return
mount
Extended
(
SetStatusModalWrapper
,
{
propsData
:
{
...
defaultProps
,
...
props
,
...
...
@@ -53,7 +52,8 @@ describe('SetStatusModalWrapper', () => {
};
const
findModal
=
()
=>
wrapper
.
find
(
GlModal
);
const
findFormField
=
(
field
)
=>
wrapper
.
find
(
`[name="user[status][
${
field
}
]"]`
);
const
findMessageField
=
()
=>
wrapper
.
findByPlaceholderText
(
SetStatusForm
.
i18n
.
statusMessagePlaceholder
);
const
findClearStatusButton
=
()
=>
wrapper
.
find
(
'
.js-clear-user-status-button
'
);
const
findAvailabilityCheckbox
=
()
=>
wrapper
.
find
(
GlFormCheckbox
);
const
findClearStatusAtMessage
=
()
=>
wrapper
.
find
(
'
[data-testid="clear-status-at-message"]
'
);
...
...
@@ -83,14 +83,8 @@ describe('SetStatusModalWrapper', () => {
return
initModal
();
});
it
(
'
sets the hidden status emoji field
'
,
()
=>
{
const
field
=
findFormField
(
'
emoji
'
);
expect
(
field
.
exists
()).
toBe
(
true
);
expect
(
field
.
element
.
value
).
toBe
(
defaultEmoji
);
});
it
(
'
sets the message field
'
,
()
=>
{
const
field
=
find
FormField
(
'
message
'
);
const
field
=
find
MessageField
(
);
expect
(
field
.
exists
()).
toBe
(
true
);
expect
(
field
.
element
.
value
).
toBe
(
defaultMessage
);
});
...
...
@@ -135,7 +129,7 @@ describe('SetStatusModalWrapper', () => {
});
it
(
'
does not set the message field
'
,
()
=>
{
expect
(
find
FormField
(
'
message
'
).
element
.
value
).
toBe
(
''
);
expect
(
find
MessageField
(
).
element
.
value
).
toBe
(
''
);
});
it
(
'
hides the clear status button
'
,
()
=>
{
...
...
@@ -143,18 +137,6 @@ describe('SetStatusModalWrapper', () => {
});
});
describe
(
'
with no currentEmoji set
'
,
()
=>
{
beforeEach
(
async
()
=>
{
await
initEmojiMock
();
wrapper
=
createComponent
({
currentEmoji
:
''
});
return
initModal
();
});
it
(
'
does not set the hidden status emoji field
'
,
()
=>
{
expect
(
findFormField
(
'
emoji
'
).
element
.
value
).
toBe
(
''
);
});
});
describe
(
'
with currentClearStatusAfter set
'
,
()
=>
{
beforeEach
(
async
()
=>
{
await
initEmojiMock
();
...
...
@@ -184,8 +166,7 @@ describe('SetStatusModalWrapper', () => {
findModal
().
vm
.
$emit
(
'
secondary
'
);
await
nextTick
();
expect
(
findFormField
(
'
message
'
).
element
.
value
).
toBe
(
''
);
expect
(
findFormField
(
'
emoji
'
).
element
.
value
).
toBe
(
''
);
expect
(
findMessageField
().
element
.
value
).
toBe
(
''
);
});
it
(
'
clicking "setStatus" submits the user status
'
,
async
()
=>
{
...
...
Пред
1
2
След
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать