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
276e64be
Commit
276e64be
authored
Aug 11, 2021
by
Olena Horal-Koretska
Committed by
Jose Ivan Vargas
Aug 11, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add colors for rotation participants
parent
523cb9df
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
209 additions
and
31 deletions
+209
-31
ee/app/assets/javascripts/oncall_schedules/components/rotations/components/add_edit_rotation_modal.vue
...mponents/rotations/components/add_edit_rotation_modal.vue
+10
-4
ee/app/assets/javascripts/oncall_schedules/constants.js
ee/app/assets/javascripts/oncall_schedules/constants.js
+3
-9
ee/app/assets/javascripts/oncall_schedules/utils/common_utils.js
...assets/javascripts/oncall_schedules/utils/common_utils.js
+90
-9
ee/spec/frontend/oncall_schedule/common_utils_spec.js
ee/spec/frontend/oncall_schedule/common_utils_spec.js
+101
-9
ee/spec/frontend/oncall_schedule/mocks/apollo_mock.js
ee/spec/frontend/oncall_schedule/mocks/apollo_mock.js
+4
-0
ee/spec/frontend/oncall_schedule/rotations/components/add_edit_rotation_modal_spec.js
...dule/rotations/components/add_edit_rotation_modal_spec.js
+1
-0
No files found.
ee/app/assets/javascripts/oncall_schedules/components/rotations/components/add_edit_rotation_modal.vue
View file @
276e64be
...
...
@@ -8,9 +8,11 @@ import getOncallSchedulesWithRotationsQuery from 'ee/oncall_schedules/graphql/qu
import
{
updateStoreAfterRotationEdit
}
from
'
ee/oncall_schedules/utils/cache_updates
'
;
import
{
isNameFieldValid
,
getParticipantsForSave
,
parseHour
,
parseRotationDate
,
setParticipantsColors
,
getUserTokenStyles
,
getParticipantsForSave
,
}
from
'
ee/oncall_schedules/utils/common_utils
'
;
import
searchProjectMembersQuery
from
'
~/graphql_shared/queries/project_user_members_search.query.graphql
'
;
import
{
format24HourTimeStringFromInt
,
formatDate
}
from
'
~/lib/utils/datetime_utility
'
;
...
...
@@ -151,7 +153,6 @@ export default {
isRestrictedToTime
,
restrictedTo
:
{
startTime
:
activeStartTime
,
endTime
:
activeEndTime
},
}
=
this
.
form
;
const
variables
=
{
name
,
participants
:
getParticipantsForSave
(
participants
),
...
...
@@ -198,6 +199,9 @@ export default {
}
return
false
;
},
participantsWithTokenStylesData
()
{
return
setParticipantsColors
(
this
.
participants
,
this
.
rotation
?.
participants
?.
nodes
);
},
},
methods
:
{
createRotation
()
{
...
...
@@ -319,7 +323,9 @@ export default {
this
.
form
.
name
=
this
.
rotation
.
name
;
const
participants
=
this
.
rotation
?.
participants
?.
nodes
?.
map
(({
user
})
=>
({
...
user
}))
??
[];
this
.
rotation
?.
participants
?.
nodes
?.
map
(({
user
,
colorWeight
,
colorPalette
})
=>
getUserTokenStyles
({
...
user
,
colorWeight
,
colorPalette
}),
)
??
[];
this
.
form
.
participants
=
participants
;
this
.
form
.
rotationLength
=
{
...
...
@@ -366,7 +372,7 @@ export default {
:validation-state=
"validationState"
:form=
"form"
:schedule=
"schedule"
:participants=
"participants"
:participants=
"participants
WithTokenStylesData
"
:is-loading=
"isLoading"
@
update-rotation-form=
"updateRotationForm"
@
filter-participants=
"filterParticipants"
...
...
ee/app/assets/javascripts/oncall_schedules/constants.js
View file @
276e64be
...
...
@@ -17,16 +17,10 @@ export const LIGHT_TO_DARK_MODE_SHADE_MAPPING = {
950
:
50
,
};
/**
* an Array of Objects that represent the 30 possible
* color combinations for assignees
* @type {{colorWeight: string, colorPalette: string}[]}
*/
export
const
ASSIGNEE_COLORS_COMBO
=
CHEVRON_SKIPPING_SHADE_ENUM
.
map
((
shade
)
=>
export
const
ASSIGNEE_COLORS_COMBO
=
CHEVRON_SKIPPING_SHADE_ENUM
.
map
((
weight
)
=>
CHEVRON_SKIPPING_PALETTE_ENUM
.
map
((
color
)
=>
({
// eslint-disable-next-line @gitlab/require-i18n-strings
colorWeight
:
`WEIGHT_
${
shade
.
toUpperCase
()}
`
,
colorPalette
:
color
.
toUpperCase
(),
colorWeight
:
weight
,
colorPalette
:
color
,
})),
).
flat
();
...
...
ee/app/assets/javascripts/oncall_schedules/utils/common_utils.js
View file @
276e64be
import
*
as
cssVariables
from
'
@gitlab/ui/scss_to_js/scss_variables
'
;
import
{
startCase
}
from
'
lodash
'
;
import
{
darkModeEnabled
}
from
'
~/lib/utils/color_utils
'
;
import
{
newDateAsLocaleTime
}
from
'
~/lib/utils/datetime_utility
'
;
import
{
sprintf
,
__
}
from
'
~/locale
'
;
import
{
ASSIGNEE_COLORS_COMBO
}
from
'
../constants
'
;
import
{
ASSIGNEE_COLORS_COMBO
,
LIGHT_TO_DARK_MODE_SHADE_MAPPING
}
from
'
../constants
'
;
/**
* Returns formatted timezone string, e.g. (UTC-09:00) AKST Alaska
...
...
@@ -35,7 +38,6 @@ export const isNameFieldValid = (nameField) => {
* with his/her username and unique shift color values
*
* @param {Object[]} participants
* @param {string} participants[].username - The username of the participant.
*
* @returns {Object[]} A list of values to save each participant
* @property {string} username
...
...
@@ -43,16 +45,95 @@ export const isNameFieldValid = (nameField) => {
* @property {string} colorPalette
*/
export
const
getParticipantsForSave
=
(
participants
)
=>
participants
.
map
(({
username
},
index
)
=>
{
const
colorIndex
=
index
%
ASSIGNEE_COLORS_COMBO
.
length
;
const
{
colorWeight
,
colorPalette
}
=
ASSIGNEE_COLORS_COMBO
[
colorIndex
];
participants
.
map
(({
username
,
colorWeight
,
colorPalette
})
=>
({
username
,
// eslint-disable-next-line @gitlab/require-i18n-strings
colorWeight
:
`WEIGHT_
${
colorWeight
}
`
,
colorPalette
:
colorPalette
.
toUpperCase
(),
}));
return
{
username
,
/**
* Returns user data along with user token styles - color of the text
* as well as the token background color depending on light or dark mode
*
* @template User
* @param {User} user
*
* @returns {Object}
* @property {User}
* @property {string} class (CSS) for text color
* @property {string} styles for token background color
*/
export
const
getUserTokenStyles
=
(
user
)
=>
{
const
{
colorWeight
,
colorPalette
}
=
user
;
const
isDarkMode
=
darkModeEnabled
();
const
modeColorWeight
=
isDarkMode
?
LIGHT_TO_DARK_MODE_SHADE_MAPPING
[
colorWeight
]
:
colorWeight
;
const
bgColor
=
`dataViz
${
startCase
(
colorPalette
)}${
modeColorWeight
}
`
;
let
textClass
=
'
gl-text-white
'
;
if
(
isDarkMode
)
{
const
medianColorPaletteWeight
=
500
;
textClass
=
modeColorWeight
<
medianColorPaletteWeight
?
'
gl-text-white
'
:
'
gl-text-gray-900
'
;
}
return
{
...
user
,
class
:
textClass
,
style
:
{
backgroundColor
:
cssVariables
[
bgColor
]
},
};
};
/**
* Sets colorWeight and colorPalette for all participants options taking into account saved participants colors
* so that there will be no color overlap
*
* @param {Object[]} allParticipants
* @param {Object[]} selectedParticipants
*
* @returns {Object[]} A list of all participants with colorWeight and colorPalette properties set
*/
export
const
setParticipantsColors
=
(
allParticipants
,
selectedParticipants
=
[])
=>
{
// filter out the colors that saved participants have assigned
// so there are no duplicate colors
let
availableColors
=
ASSIGNEE_COLORS_COMBO
.
filter
(({
colorWeight
,
colorPalette
})
=>
selectedParticipants
.
every
(
({
colorWeight
:
weight
,
colorPalette
:
palette
})
=>
!
(
colorWeight
===
weight
&&
colorPalette
===
palette
),
),
);
// if all colors are exhausted, we allow to pick from the whole palette
if
(
!
availableColors
.
length
)
{
availableColors
=
ASSIGNEE_COLORS_COMBO
;
}
// filter out participants that were not saved previously and have no color info assigned
// and assign each one an available color set
const
participants
=
allParticipants
.
filter
((
participant
)
=>
selectedParticipants
.
every
(({
user
:
{
username
}
})
=>
username
!==
participant
.
username
),
)
.
map
((
participant
,
index
)
=>
{
const
colorIndex
=
index
%
availableColors
.
length
;
const
{
colorWeight
,
colorPalette
}
=
availableColors
[
colorIndex
];
return
{
...
participant
,
colorWeight
,
colorPalette
,
};
});
return
[
...
participants
,
...
selectedParticipants
.
map
(({
user
,
colorWeight
,
colorPalette
})
=>
({
...
user
,
colorWeight
,
colorPalette
,
};
});
})),
].
map
(
getUserTokenStyles
);
};
/**
* Parses a activePeriod string into an integer value
...
...
ee/spec/frontend/oncall_schedule/common_utils_spec.js
View file @
276e64be
import
{
ASSIGNEE_COLORS_COMBO
}
from
'
ee/oncall_schedules/constants
'
;
import
{
getFormattedTimezone
,
getParticipantsForSave
,
parseHour
,
parseRotationDate
,
getUserTokenStyles
,
setParticipantsColors
,
}
from
'
ee/oncall_schedules/utils/common_utils
'
;
import
*
as
ColorUtils
from
'
~/lib/utils/color_utils
'
;
import
mockTimezones
from
'
./mocks/mock_timezones.json
'
;
describe
(
'
getFormattedTimezone
'
,
()
=>
{
...
...
@@ -17,16 +19,106 @@ describe('getFormattedTimezone', () => {
describe
(
'
getParticipantsForSave
'
,
()
=>
{
it
(
'
returns participant shift color data along with the username
'
,
()
=>
{
const
participants
=
[{
username
:
'
user1
'
},
{
username
:
'
user2
'
},
{
username
:
'
user3
'
}];
const
result
=
getParticipantsForSave
(
participants
);
const
participants
=
[
{
username
:
'
user1
'
,
colorWeight
:
300
,
colorPalette
:
'
blue
'
,
extraProp
:
'
1
'
},
{
username
:
'
user2
'
,
colorWeight
:
400
,
colorPalette
:
'
red
'
,
extraProp
:
'
2
'
},
{
username
:
'
user3
'
,
colorWeight
:
500
,
colorPalette
:
'
green
'
,
extraProp
:
'
4
'
},
];
const
expectedParticipantsForSave
=
[
{
username
:
'
user1
'
,
colorWeight
:
'
WEIGHT_300
'
,
colorPalette
:
'
BLUE
'
},
{
username
:
'
user2
'
,
colorWeight
:
'
WEIGHT_400
'
,
colorPalette
:
'
RED
'
},
{
username
:
'
user3
'
,
colorWeight
:
'
WEIGHT_500
'
,
colorPalette
:
'
GREEN
'
},
];
expect
(
getParticipantsForSave
(
participants
)).
toEqual
(
expectedParticipantsForSave
);
});
});
describe
(
'
getUserTokenStyles
'
,
()
=>
{
it
.
each
`
isDarkMode | colorWeight | expectedTextClass | expectedBackgroundColor
${
true
}
|
${
900
}
|
${
'
gl-text-white
'
}
|
${
'
#d4dcfa
'
}
${
true
}
|
${
500
}
|
${
'
gl-text-gray-900
'
}
|
${
'
#5772ff
'
}
${
false
}
|
${
400
}
|
${
'
gl-text-white
'
}
|
${
'
#748eff
'
}
${
false
}
|
${
700
}
|
${
'
gl-text-white
'
}
|
${
'
#3547de
'
}
`
(
'
sets correct styles and class
'
,
({
isDarkMode
,
colorWeight
,
expectedTextClass
,
expectedBackgroundColor
})
=>
{
jest
.
spyOn
(
ColorUtils
,
'
darkModeEnabled
'
).
mockImplementation
(()
=>
isDarkMode
);
const
user
=
{
username
:
'
user1
'
,
colorWeight
,
colorPalette
:
'
blue
'
};
expect
(
getUserTokenStyles
(
user
)).
toMatchObject
({
class
:
expectedTextClass
,
style
:
{
backgroundColor
:
expectedBackgroundColor
},
});
},
);
});
expect
(
result
.
length
).
toBe
(
participants
.
length
);
describe
(
'
setParticipantsColors
'
,
()
=>
{
it
(
'
sets token color data to each of the eparticipant
'
,
()
=>
{
jest
.
spyOn
(
ColorUtils
,
'
darkModeEnabled
'
).
mockImplementation
(()
=>
false
);
result
.
forEach
((
participant
,
index
)
=>
{
const
{
colorWeight
,
colorPalette
}
=
ASSIGNEE_COLORS_COMBO
[
index
];
const
{
username
}
=
participants
[
index
];
expect
(
participant
).
toEqual
({
username
,
colorWeight
,
colorPalette
});
});
const
allParticpants
=
[
{
username
:
'
user1
'
},
{
username
:
'
user2
'
},
{
username
:
'
user3
'
},
{
username
:
'
user4
'
},
{
username
:
'
user5
'
},
{
username
:
'
user6
'
},
];
const
selectedParticpants
=
[
{
user
:
{
username
:
'
user2
'
},
colorPalette
:
'
blue
'
,
colorWeight
:
'
500
'
},
{
user
:
{
username
:
'
user4
'
},
colorPalette
:
'
magenta
'
,
colorWeight
:
'
500
'
},
{
user
:
{
username
:
'
user5
'
},
colorPalette
:
'
orange
'
,
colorWeight
:
'
500
'
},
];
const
expectedParticipants
=
[
{
username
:
'
user1
'
,
colorWeight
:
'
500
'
,
colorPalette
:
'
aqua
'
,
class
:
'
gl-text-white
'
,
style
:
{
backgroundColor
:
'
#0094b6
'
},
},
{
username
:
'
user3
'
,
colorWeight
:
'
500
'
,
colorPalette
:
'
green
'
,
class
:
'
gl-text-white
'
,
style
:
{
backgroundColor
:
'
#608b2f
'
},
},
{
username
:
'
user6
'
,
colorWeight
:
'
600
'
,
colorPalette
:
'
blue
'
,
class
:
'
gl-text-white
'
,
style
:
{
backgroundColor
:
'
#445cf2
'
},
},
{
username
:
'
user2
'
,
colorWeight
:
'
500
'
,
colorPalette
:
'
blue
'
,
class
:
'
gl-text-white
'
,
style
:
{
backgroundColor
:
'
#5772ff
'
},
},
{
username
:
'
user4
'
,
colorWeight
:
'
500
'
,
colorPalette
:
'
magenta
'
,
class
:
'
gl-text-white
'
,
style
:
{
backgroundColor
:
'
#d84280
'
},
},
{
username
:
'
user5
'
,
colorWeight
:
'
500
'
,
colorPalette
:
'
orange
'
,
class
:
'
gl-text-white
'
,
style
:
{
backgroundColor
:
'
#d14e00
'
},
},
];
expect
(
setParticipantsColors
(
allParticpants
,
selectedParticpants
)).
toEqual
(
expectedParticipants
,
);
});
});
...
...
ee/spec/frontend/oncall_schedule/mocks/apollo_mock.js
View file @
276e64be
...
...
@@ -11,6 +11,8 @@ export const participants = [
avatar
:
''
,
avatarUrl
:
''
,
webUrl
:
''
,
colorWeight
:
'
500
'
,
colorPalette
:
'
blue
'
,
},
{
id
:
'
2
'
,
...
...
@@ -19,6 +21,8 @@ export const participants = [
avatar
:
''
,
avatarUrl
:
''
,
webUrl
:
''
,
colorWeight
:
'
300
'
,
colorPalette
:
'
orange
'
,
},
];
...
...
ee/spec/frontend/oncall_schedule/rotations/components/add_edit_rotation_modal_spec.js
View file @
276e64be
...
...
@@ -20,6 +20,7 @@ import {
import
mockRotation
from
'
../../mocks/mock_rotation.json
'
;
jest
.
mock
(
'
~/flash
'
);
jest
.
mock
(
'
~/lib/utils/color_utils
'
);
const
schedule
=
getOncallSchedulesQueryResponse
.
data
.
project
.
incidentManagementOncallSchedules
.
nodes
[
0
];
...
...
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