Skip to content
Snippets Groups Projects
Commit a1535c4f authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖
Browse files

Automatic merge of gitlab-org/gitlab master

parents 3ff3a4ff 7056a043
No related branches found
No related tags found
No related merge requests found
Showing
with 229 additions and 65 deletions
......@@ -62,7 +62,7 @@ _Consider adding links to check for Sentry errors, Production logs for 5xx, 302s
- Ensure that the feature MRs have been deployed to non-production environments.
- [ ] `/chatops run auto_deploy status <merge-commit-of-your-feature>`
- [ ] Enable the feature globally on non-production environments.
- [ ] `/chatops run feature set <feature-flag-name> true --dev --staging`
- [ ] `/chatops run feature set <feature-flag-name> true --dev --staging --staging-ref`
- [ ] Verify that the feature works as expected. Posting the QA result in this issue is preferable.
The best environment to validate the feature in is [staging-canary](https://about.gitlab.com/handbook/engineering/infrastructure/environments/#staging-canary)
as this is the first environment deployed to. Note you will need to make sure you are configured to use canary as outlined [here](https://about.gitlab.com/handbook/engineering/infrastructure/environments/canary-stage/)
......@@ -120,7 +120,7 @@ To do so, follow these steps:
the feature can be officially announced in a release blog post.
- [ ] `/chatops run release check <merge-request-url> <milestone>`
- [ ] Consider cleaning up the feature flag from all environments by running these chatops command in `#production` channel. Otherwise these settings may override the default enabled.
- [ ] `/chatops run feature delete <feature-flag-name> --dev --staging --production`
- [ ] `/chatops run feature delete <feature-flag-name> --dev --staging --staging-ref --production`
- [ ] Close [the feature issue](ISSUE LINK) to indicate the feature will be released in the current milestone.
- [ ] Set the next milestone to this rollout issue for scheduling [the flag removal](#release-the-feature).
- [ ] (Optional) You can [create a separate issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Feature%20Flag%20Cleanup) for scheduling the steps below to [Release the feature](#release-the-feature).
......@@ -156,7 +156,7 @@ You can either [create a follow-up issue for Feature Flag Cleanup](https://gitla
- [ ] `/chatops run release check <merge-request-url> <milestone>`
- [ ] Close [the feature issue](ISSUE LINK) to indicate the feature will be released in the current milestone.
- [ ] If not already done, clean up the feature flag from all environments by running these chatops command in `#production` channel:
- [ ] `/chatops run feature delete <feature-flag-name> --dev --staging --production`
- [ ] `/chatops run feature delete <feature-flag-name> --dev --staging --staging-ref --production`
- [ ] Close this rollout issue.
## Rollback Steps
......
......@@ -14,12 +14,12 @@ export default {
</script>
<template>
<div>
<div class="color-item">
<span
class="dropdown-label-box gl-flex-shrink-0 gl-top-1 gl-mr-0"
class="dropdown-label-box color-item-color"
data-testid="color-item"
:style="{ backgroundColor: color }"
></span>
<span class="hide-collapsed">{{ title }}</span>
<span class="color-item-text">{{ title }}</span>
</div>
</template>
<script>
import { isString } from 'lodash';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
......@@ -52,13 +53,23 @@ export default {
required: false,
default: s__('ColorWidget|Assign epic color'),
},
defaultColor: {
type: Object,
required: false,
validator(value) {
return isString(value?.color) && isString(value?.title);
},
default() {
return {
color: '',
title: '',
};
},
},
},
data() {
return {
issuableColor: {
color: '',
title: '',
},
issuableColor: this.defaultColor,
colorUpdateInProgress: false,
oldIid: null,
sidebarExpandedOnClick: false,
......
import { __, s__ } from '~/locale';
import { s__ } from '~/locale';
export const COLOR_WIDGET_COLOR = s__('ColorWidget|Color');
......@@ -7,7 +7,7 @@ export const DROPDOWN_VARIANT = {
Embedded: 'embedded',
};
export const DEFAULT_COLOR = { title: __('SuggestedColors|Blue'), color: '#1068bf' };
export const DEFAULT_COLOR = { title: s__('SuggestedColors|Blue'), color: '#1068bf' };
export const ISSUABLE_COLORS = [
DEFAULT_COLOR,
......
<script>
import { GlDropdown } from '@gitlab/ui';
import ColorItem from './color_item.vue';
import DropdownContentsColorView from './dropdown_contents_color_view.vue';
import DropdownHeader from './dropdown_header.vue';
import { isDropdownVariantSidebar } from './utils';
export default {
components: {
ColorItem,
DropdownContentsColorView,
DropdownHeader,
GlDropdown,
......@@ -42,12 +44,15 @@ export default {
},
computed: {
buttonText() {
if (!this.localSelectedColor?.title) {
if (!this.hasSelectedColor()) {
return this.dropdownButtonText;
}
return this.localSelectedColor.title;
},
hasSelectedColor() {
return this.localSelectedColor?.title;
},
},
watch: {
localSelectedColor: {
......@@ -91,7 +96,15 @@ export default {
</script>
<template>
<gl-dropdown ref="dropdown" :text="buttonText" class="gl-w-full" @hide="handleDropdownHide">
<gl-dropdown ref="dropdown" class="gl-w-full" @hide="handleDropdownHide">
<template #button-text>
<color-item
v-if="hasSelectedColor"
:color="localSelectedColor.color"
:title="localSelectedColor.title"
/>
<span v-else>{{ buttonText }}</span>
</template>
<template #header>
<dropdown-header
ref="header"
......
......@@ -36,8 +36,8 @@ export default {
</script>
<template>
<gl-dropdown-form>
<div>
<gl-dropdown-form class="js-colors-list">
<div data-testid="dropdown-content">
<gl-dropdown-item
v-for="color in colors"
:key="color.color"
......
......@@ -20,6 +20,11 @@ export default {
required: true,
},
},
computed: {
hasColor() {
return this.selectedColor.color !== '';
},
},
};
</script>
......@@ -31,13 +36,18 @@ export default {
class="sidebar-collapsed-icon"
>
<gl-icon name="appearance" />
<color-item :color="selectedColor.color" :title="selectedColor.title" />
</div>
<span v-if="!hasColor" class="no-value hide-collapsed">
<slot></slot>
</span>
<template v-else>
<color-item
class="hide-collapsed"
:color="selectedColor.color"
:title="selectedColor.title"
class="gl-font-base gl-line-height-24"
/>
</div>
<color-item class="hide-collapsed" :color="selectedColor.color" :title="selectedColor.title" />
</template>
</div>
</template>
@import './pages/branches';
@import './pages/clusters';
@import './pages/colors';
@import './pages/commits';
@import './pages/deploy_keys';
@import './pages/detail_page';
......@@ -8,6 +9,7 @@
@import './pages/events';
@import './pages/groups';
@import './pages/help';
@import './pages/hierarchy';
@import './pages/issuable';
@import './pages/issues';
@import './pages/labels';
......@@ -25,9 +27,8 @@
@import './pages/registry';
@import './pages/search';
@import './pages/service_desk';
@import './pages/settings';
@import './pages/settings_ci_cd';
@import './pages/settings';
@import './pages/storage_quota';
@import './pages/tree';
@import './pages/users';
@import './pages/hierarchy';
......@@ -188,7 +188,11 @@ $dark-il: #de935f;
.diff-line-num.new,
.line-coverage.new,
.line-codequality.new,
.line_content.new {
.line_content.new,
.diff-line-num.new-nomappinginraw,
.line-coverage.new-nomappinginraw,
.line-codequality.new-nomappinginraw,
.line_content.new-nomappinginraw {
@include diff-background($dark-new-bg, $dark-new-idiff, $dark-border);
&::before,
......@@ -200,7 +204,11 @@ $dark-il: #de935f;
.diff-line-num.old,
.line-coverage.old,
.line-codequality.old,
.line_content.old {
.line_content.old,
.diff-line-num.old-nomappinginraw,
.line-coverage.old-nomappinginraw,
.line-codequality.old-nomappinginraw,
.line_content.old-nomappinginraw {
@include diff-background($dark-old-bg, $dark-old-idiff, $dark-border);
&::before,
......
......@@ -178,7 +178,11 @@ $monokai-gh: #75715e;
.diff-line-num.new,
.line-coverage.new,
.line-codequality.new,
.line_content.new {
.line_content.new,
.diff-line-num.new-nomappinginraw,
.line-coverage.new-nomappinginraw,
.line-codequality.new-nomappinginraw,
.line_content.new-nomappinginraw {
@include diff-background($monokai-new-bg, $monokai-new-idiff, $monokai-diff-border);
&::before,
......@@ -190,7 +194,11 @@ $monokai-gh: #75715e;
.diff-line-num.old,
.line-coverage.old,
.line-codequality.old,
.line_content.old {
.line_content.old,
.diff-line-num.old-nomappinginraw,
.line-coverage.old-nomappinginraw,
.line-codequality.old-nomappinginraw,
.line_content.old-nomappinginraw {
@include diff-background($monokai-old-bg, $monokai-old-idiff, $monokai-diff-border);
&::before,
......
......@@ -75,7 +75,9 @@
.line-coverage,
.line-codequality {
&.old,
&.new {
&.new,
&.new-nomappinginraw,
&.old-nomappinginraw {
background-color: $white-normal;
}
}
......@@ -131,7 +133,7 @@
}
.line_content {
&.old {
&.old, &.old-nomappinginraw {
background-color: $white-normal;
&::before {
......@@ -144,7 +146,7 @@
}
}
&.new {
&.new, &.new-nomappinginraw {
background-color: $white-normal;
&::before {
......
......@@ -117,7 +117,7 @@ $solarized-dark-il: #2aa198;
@include hljs-override('bullet', $solarized-dark-n);
@include hljs-override('subst', $solarized-dark-p);
@include hljs-override('symbol', $solarized-dark-ni);
// Line numbers
.file-line-num {
@include line-number-link($solarized-dark-line-color);
......@@ -189,7 +189,11 @@ $solarized-dark-il: #2aa198;
.diff-line-num.new,
.line-coverage.new,
.line-codequality.new,
.line_content.new {
.line_content.new,
.diff-line-num.new-nomappinginraw,
.line-coverage.new-nomappinginraw,
.line-codequality.new-nomappinginraw,
.line_content.new-nomappinginraw {
@include diff-background($solarized-dark-new-bg, $solarized-dark-new-idiff, $solarized-dark-border);
&::before,
......@@ -201,7 +205,11 @@ $solarized-dark-il: #2aa198;
.diff-line-num.old,
.line-coverage.old,
.line-codequality.old,
.line_content.old {
.line_content.old,
.diff-line-num.old-nomappinginraw,
.line-coverage.old-nomappinginraw,
.line-codequality.old-nomappinginraw,
.line_content.old-nomappinginraw {
@include diff-background($solarized-dark-old-bg, $solarized-dark-old-idiff, $solarized-dark-border);
&::before,
......
......@@ -169,7 +169,11 @@ $solarized-light-il: #2aa198;
.diff-line-num.new,
.line-coverage.new,
.line-codequality.new,
.line_content.new {
.line_content.new,
.diff-line-num.new-nomappinginraw,
.line-coverage.new-nomappinginraw,
.line-codequality.new-nomappinginraw,
.line_content.new-nomappinginraw {
@include diff-background($solarized-light-new-bg,
$solarized-light-new-idiff, $solarized-light-border);
......@@ -190,7 +194,11 @@ $solarized-light-il: #2aa198;
.diff-line-num.old,
.line-coverage.old,
.line-codequality.old,
.line_content.old {
.line_content.old,
.diff-line-num.old-nomappinginraw,
.line-coverage.old-nomappinginraw,
.line-codequality.old-nomappinginraw,
.line_content.old-nomappinginraw {
@include diff-background($solarized-light-old-bg, $solarized-light-old-idiff, $solarized-light-border);
&::before,
......
......@@ -158,7 +158,8 @@ pre.code,
}
.diff-line-num {
&.old {
&.old,
&.old-nomappinginraw {
background-color: $line-number-old;
a {
......@@ -166,7 +167,8 @@ pre.code,
}
}
&.new {
&.new,
&.new-nomappinginraw {
background-color: $line-number-new;
a {
......@@ -204,7 +206,8 @@ pre.code,
}
.line_content {
&.old {
&.old,
&.old-nomappinginraw {
background-color: $line-removed;
&::before {
......@@ -216,7 +219,8 @@ pre.code,
}
}
&.new {
&.new,
&.new-nomappinginraw {
background-color: $line-added;
&::before {
......@@ -243,11 +247,13 @@ pre.code,
.line-coverage,
.line-codequality {
&.old {
&.old,
&.old-nomappinginraw {
background-color: $line-removed;
}
&.new {
&.new,
&.new-nomappinginraw {
background-color: $line-added;
}
......
.color-item {
@include gl-align-items-center;
@include gl-display-flex;
}
.color-item-color {
@include gl-flex-shrink-0;
@include gl-mr-3;
@include gl-top-0;
}
.right-sidebar-collapsed {
.color-item {
@include gl-pt-3;
}
.color-item-color {
margin: 0;
}
.color-item-text {
display: none;
}
}
......@@ -1060,6 +1060,9 @@ License.current.expires_at
# Is this a trial license?
License.current.trial?
# License ID for lookup on CustomersDot
License.current.license_id
```
### Check if a project feature is available on the instance
......
......@@ -1147,6 +1147,9 @@ Payload example:
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": null,
"finished_at": null,
"duration": null,
"queued_duration": null,
"failure_reason": null,
"when": "manual",
"manual": true,
"allow_failure": false,
......@@ -1175,7 +1178,10 @@ Payload example:
"status": "success",
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": "2016-08-12 15:26:12 UTC",
"finished_at": null,
"finished_at": "2016-08-12 15:26:29 UTC",
"duration": 17.0,
"queued_duration": 196.0,
"failure_reason": null,
"when": "on_success",
"manual": false,
"allow_failure": false,
......@@ -1208,10 +1214,13 @@ Payload example:
"id": 378,
"stage": "test",
"name": "test-build",
"status": "success",
"status": "failed",
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": "2016-08-12 15:26:12 UTC",
"finished_at": "2016-08-12 15:26:29 UTC",
"duration": 17.0,
"queued_duration": 196.0,
"failure_reason": "script_failure",
"when": "on_success",
"manual": false,
"allow_failure": false,
......@@ -1247,6 +1256,9 @@ Payload example:
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": "2016-08-12 15:24:56 UTC",
"finished_at": "2016-08-12 15:25:26 UTC",
"duration": 17.0,
"queued_duration": 196.0,
"failure_reason": null,
"when": "on_success",
"manual": false,
"allow_failure": false,
......@@ -1282,6 +1294,9 @@ Payload example:
"created_at": "2016-08-12 15:23:28 UTC",
"started_at": null,
"finished_at": null,
"duration": null,
"queued_duration": null,
"failure_reason": null,
"when": "on_success",
"manual": false,
"allow_failure": false,
......
......@@ -16,25 +16,31 @@ import { visitUrl } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import LabelsSelectWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
import ColorSelectDropdown from '~/vue_shared/components/color_select_dropdown/color_select_root.vue';
import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
import { DEFAULT_COLOR } from '~/vue_shared/components/color_select_dropdown/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import createEpic from '../queries/create_epic.mutation.graphql';
export default {
components: {
ColorSelectDropdown,
GlButton,
GlDatepicker,
GlForm,
GlFormCheckbox,
GlFormInput,
GlFormGroup,
MarkdownField,
GlFormInput,
LabelsSelectWidget,
MarkdownField,
},
mixins: [glFeatureFlagsMixin()],
inject: ['groupPath', 'groupEpicsPath', 'markdownPreviewPath', 'markdownDocsPath'],
data() {
return {
title: '',
description: '',
color: DEFAULT_COLOR,
confidential: false,
labels: [],
startDateFixed: null,
......@@ -47,6 +53,9 @@ export default {
labelIds() {
return this.labels.map((label) => label.id);
},
isEpicColorEnabled() {
return this.glFeatures.epicColorHighlight;
},
},
i18n: {
confidentialityLabel: s__(`
......@@ -83,24 +92,26 @@ export default {
save() {
this.loading = true;
const input = {
addLabelIds: this.labelIds,
groupPath: this.groupPath,
title: this.title,
description: this.description,
confidential: this.confidential,
startDateFixed: this.startDateFixed ? formatDate(this.startDateFixed, 'yyyy-mm-dd') : null,
startDateIsFixed: Boolean(this.startDateFixed),
dueDateFixed: this.dueDateFixed ? formatDate(this.dueDateFixed, 'yyyy-mm-dd') : null,
dueDateIsFixed: Boolean(this.dueDateFixed),
};
if (this.isEpicColorEnabled && this.color?.color !== '') {
input.color = this.color.color;
}
return this.$apollo
.mutate({
mutation: createEpic,
variables: {
input: {
addLabelIds: this.labelIds,
groupPath: this.groupPath,
title: this.title,
description: this.description,
confidential: this.confidential,
startDateFixed: this.startDateFixed
? formatDate(this.startDateFixed, 'yyyy-mm-dd')
: null,
startDateIsFixed: Boolean(this.startDateFixed),
dueDateFixed: this.dueDateFixed ? formatDate(this.dueDateFixed, 'yyyy-mm-dd') : null,
dueDateIsFixed: Boolean(this.dueDateFixed),
},
},
variables: { input },
})
.then(({ data }) => {
const { errors, epic } = data.createEpic;
......@@ -131,6 +142,9 @@ export default {
handleUpdateSelectedLabels(labels) {
this.labels = labels.map((label) => ({ ...label, id: getIdFromGraphQLId(label.id) }));
},
handleUpdateSelectedColor(color) {
this.color = color;
},
},
};
</script>
......@@ -189,6 +203,7 @@ export default {
>{{ $options.i18n.confidentialityLabel }}</gl-form-checkbox
>
</gl-form-group>
<gl-form-group :label="__('Labels')">
<labels-select-widget
class="block labels js-labels-block"
......@@ -220,11 +235,7 @@ export default {
>{{ __('Clear start date') }}</gl-button
>
</gl-form-group>
<gl-form-group
class="gl-pb-4"
:label="__('Due date')"
:description="$options.i18n.epicDatesHint"
>
<gl-form-group :label="__('Due date')" :description="$options.i18n.epicDatesHint">
<div class="gl-display-inline-block gl-mr-2">
<gl-datepicker v-model="dueDateFixed" data-testid="epic-due-date" />
</div>
......@@ -238,7 +249,23 @@ export default {
>
</gl-form-group>
<div class="footer-block">
<gl-form-group v-if="isEpicColorEnabled" :label="__('Color')" label-for="epic-color">
<color-select-dropdown
class="block colors js-colors-block"
:full-path="groupPath"
:attr-workspace-path="groupPath"
workspace-type="group"
:label-create-type="LabelType.group"
:default-color="color"
issuable-type="epic"
variant="embedded"
@updateSelectedColor="handleUpdateSelectedColor($event.color)"
>
{{ __('Select a color') }}
</color-select-dropdown>
</gl-form-group>
<div class="footer-block gl-pt-4">
<gl-button
type="submit"
variant="confirm"
......
......@@ -15,6 +15,7 @@ import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sideb
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import sidebarEventHub from '~/sidebar/event_hub';
import LabelsSelectWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
import ColorSelectDropdown from '~/vue_shared/components/color_select_dropdown/color_select_root.vue';
import { LabelType } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
......@@ -34,6 +35,7 @@ export default {
SidebarReferenceWidget,
SidebarTodoWidget,
LabelsSelectWidget,
ColorSelectDropdown,
},
mixins: [glFeatureFlagMixin()],
inject: ['iid'],
......@@ -87,6 +89,9 @@ export default {
this.toggleSidebar({ sidebarCollapsed: false });
}
},
isEpicColorEnabled() {
return this.glFeatures.epicColorHighlight;
},
},
};
</script>
......@@ -145,6 +150,20 @@ export default {
>
{{ __('None') }}
</labels-select-widget>
<color-select-dropdown
v-if="isEpicColorEnabled()"
class="block colors js-colors-block"
:allow-edit="canUpdate"
:iid="String(iid)"
:full-path="fullPath"
workspace-type="group"
issuable-type="epic"
variant="sidebar"
data-testid="colors-select"
>
{{ __('None') }}
</color-select-dropdown>
<sidebar-confidentiality-widget
:iid="String(iid)"
:full-path="fullPath"
......
......@@ -19,6 +19,7 @@ class Groups::EpicsController < Groups::ApplicationController
before_action do
push_frontend_feature_flag(:confidential_notes, @group, type: :development)
push_frontend_feature_flag(:realtime_labels, group)
push_frontend_feature_flag(:epic_color_highlight)
end
feature_category :portfolio_management
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment