Commit f23ed8d9 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '298930-ff-and-basic-toggle' into 'master'

Add dropdown and feature flag [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!56865
parents d50d836e 67fbe9e5
...@@ -10,3 +10,6 @@ export const ONE_COL_WIDTH = 180; ...@@ -10,3 +10,6 @@ export const ONE_COL_WIDTH = 180;
export const REST = 'rest'; export const REST = 'rest';
export const GRAPHQL = 'graphql'; export const GRAPHQL = 'graphql';
export const STAGE_VIEW = 'stage';
export const LAYER_VIEW = 'layer';
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
import { GlAlert, GlLoadingIcon } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql'; import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import { __ } from '~/locale'; import { __ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DEFAULT, DRAW_FAILURE, LOAD_FAILURE } from '../../constants'; import { DEFAULT, DRAW_FAILURE, LOAD_FAILURE } from '../../constants';
import { STAGE_VIEW } from './constants';
import PipelineGraph from './graph_component.vue'; import PipelineGraph from './graph_component.vue';
import GraphViewSelector from './graph_view_selector.vue';
import { import {
getQueryHeaders, getQueryHeaders,
reportToSentry, reportToSentry,
...@@ -17,8 +20,10 @@ export default { ...@@ -17,8 +20,10 @@ export default {
components: { components: {
GlAlert, GlAlert,
GlLoadingIcon, GlLoadingIcon,
GraphViewSelector,
PipelineGraph, PipelineGraph,
}, },
mixins: [glFeatureFlagMixin()],
inject: { inject: {
graphqlResourceEtag: { graphqlResourceEtag: {
default: '', default: '',
...@@ -35,8 +40,9 @@ export default { ...@@ -35,8 +40,9 @@ export default {
}, },
data() { data() {
return { return {
pipeline: null,
alertType: null, alertType: null,
currentViewType: STAGE_VIEW,
pipeline: null,
showAlert: false, showAlert: false,
}; };
}, },
...@@ -147,6 +153,9 @@ export default { ...@@ -147,6 +153,9 @@ export default {
} }
}, },
/* eslint-enable @gitlab/require-i18n-strings */ /* eslint-enable @gitlab/require-i18n-strings */
updateViewType(type) {
this.currentViewType = type;
},
}, },
}; };
</script> </script>
...@@ -155,6 +164,11 @@ export default { ...@@ -155,6 +164,11 @@ export default {
<gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert"> <gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert">
{{ alert.text }} {{ alert.text }}
</gl-alert> </gl-alert>
<graph-view-selector
v-if="glFeatures.pipelineGraphLayersView"
:type="currentViewType"
@updateViewType="updateViewType"
/>
<gl-loading-icon v-if="showLoadingIcon" class="gl-mx-auto gl-my-4" size="lg" /> <gl-loading-icon v-if="showLoadingIcon" class="gl-mx-auto gl-my-4" size="lg" />
<pipeline-graph <pipeline-graph
v-if="pipeline" v-if="pipeline"
......
<script>
import { GlDropdown, GlDropdownItem, GlIcon, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
import { STAGE_VIEW, LAYER_VIEW } from './constants';
export default {
name: 'GraphViewSelector',
components: {
GlDropdown,
GlDropdownItem,
GlIcon,
GlSprintf,
},
props: {
type: {
type: String,
required: true,
},
},
data() {
return {
currentViewType: STAGE_VIEW,
};
},
i18n: {
labelText: __('Order jobs by'),
},
views: {
[STAGE_VIEW]: {
type: STAGE_VIEW,
text: {
primary: __('Stage'),
secondary: __('View the jobs grouped into stages'),
},
},
[LAYER_VIEW]: {
type: LAYER_VIEW,
text: {
primary: __('%{codeStart}needs:%{codeEnd} relationships'),
secondary: __('View what jobs are needed for a job to run'),
},
},
},
computed: {
currentDropdownText() {
return this.$options.views[this.type].text.primary;
},
},
methods: {
itemClick(type) {
this.$emit('updateViewType', type);
},
},
};
</script>
<template>
<div class="gl-display-flex gl-justify-content-end gl-align-items-center gl-my-4">
<span>{{ $options.i18n.labelText }}</span>
<gl-dropdown class="gl-ml-4" :right="true">
<template #button-content>
<gl-sprintf :message="currentDropdownText">
<template #code="{ content }">
<code> {{ content }} </code>
</template>
</gl-sprintf>
<gl-icon class="gl-px-2" name="angle-down" :size="18" />
</template>
<gl-dropdown-item
v-for="view in $options.views"
:key="view.type"
:secondary-text="view.text.secondary"
@click="itemClick(view.type)"
>
<b>
<gl-sprintf :message="view.text.primary">
<template #code="{ content }">
<code> {{ content }} </code>
</template>
</gl-sprintf>
</b>
</gl-dropdown-item>
</gl-dropdown>
</div>
</template>
...@@ -14,6 +14,7 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -14,6 +14,7 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action do before_action do
push_frontend_feature_flag(:new_pipeline_form, project, default_enabled: :yaml) push_frontend_feature_flag(:new_pipeline_form, project, default_enabled: :yaml)
push_frontend_feature_flag(:pipeline_graph_layers_view, project, type: :development, default_enabled: :yaml)
push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml)
push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml)
push_frontend_feature_flag(:jira_for_vulnerabilities, project, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:jira_for_vulnerabilities, project, type: :development, default_enabled: :yaml)
......
---
name: pipeline_graph_layers_view
introduced_by_url:
rollout_issue_url:
milestone: '13.11'
type: development
group: group::pipeline authoring
default_enabled: false
...@@ -402,6 +402,9 @@ msgstr "" ...@@ -402,6 +402,9 @@ msgstr ""
msgid "%{board_target} not found" msgid "%{board_target} not found"
msgstr "" msgstr ""
msgid "%{codeStart}needs:%{codeEnd} relationships"
msgstr ""
msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements." msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements."
msgstr "" msgstr ""
...@@ -21684,6 +21687,9 @@ msgstr "" ...@@ -21684,6 +21687,9 @@ msgstr ""
msgid "Or you can choose one of the suggested colors below" msgid "Or you can choose one of the suggested colors below"
msgstr "" msgstr ""
msgid "Order jobs by"
msgstr ""
msgid "Orphaned member" msgid "Orphaned member"
msgstr "" msgstr ""
...@@ -33507,6 +33513,9 @@ msgstr "" ...@@ -33507,6 +33513,9 @@ msgstr ""
msgid "View the documentation" msgid "View the documentation"
msgstr "" msgstr ""
msgid "View the jobs grouped into stages"
msgstr ""
msgid "View the latest successful deployment to this environment" msgid "View the latest successful deployment to this environment"
msgstr "" msgstr ""
...@@ -33516,6 +33525,9 @@ msgstr "" ...@@ -33516,6 +33525,9 @@ msgstr ""
msgid "View users statistics" msgid "View users statistics"
msgstr "" msgstr ""
msgid "View what jobs are needed for a job to run"
msgstr ""
msgid "Viewed" msgid "Viewed"
msgstr "" msgstr ""
......
...@@ -6,6 +6,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; ...@@ -6,6 +6,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql'; import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue'; import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue'; import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
import { mockPipelineResponse } from './mock_data'; import { mockPipelineResponse } from './mock_data';
const defaultProvide = { const defaultProvide = {
...@@ -22,15 +23,19 @@ describe('Pipeline graph wrapper', () => { ...@@ -22,15 +23,19 @@ describe('Pipeline graph wrapper', () => {
const getAlert = () => wrapper.find(GlAlert); const getAlert = () => wrapper.find(GlAlert);
const getLoadingIcon = () => wrapper.find(GlLoadingIcon); const getLoadingIcon = () => wrapper.find(GlLoadingIcon);
const getGraph = () => wrapper.find(PipelineGraph); const getGraph = () => wrapper.find(PipelineGraph);
const getViewSelector = () => wrapper.find(GraphViewSelector);
const createComponent = ({ const createComponent = ({
apolloProvider, apolloProvider,
data = {}, data = {},
provide = defaultProvide, provide = {},
mountFn = shallowMount, mountFn = shallowMount,
} = {}) => { } = {}) => {
wrapper = mountFn(PipelineGraphWrapper, { wrapper = mountFn(PipelineGraphWrapper, {
provide, provide: {
...defaultProvide,
...provide,
},
apolloProvider, apolloProvider,
data() { data() {
return { return {
...@@ -40,13 +45,14 @@ describe('Pipeline graph wrapper', () => { ...@@ -40,13 +45,14 @@ describe('Pipeline graph wrapper', () => {
}); });
}; };
const createComponentWithApollo = ( const createComponentWithApollo = ({
getPipelineDetailsHandler = jest.fn().mockResolvedValue(mockPipelineResponse), getPipelineDetailsHandler = jest.fn().mockResolvedValue(mockPipelineResponse),
) => { provide = {},
} = {}) => {
const requestHandlers = [[getPipelineDetails, getPipelineDetailsHandler]]; const requestHandlers = [[getPipelineDetails, getPipelineDetailsHandler]];
const apolloProvider = createMockApollo(requestHandlers); const apolloProvider = createMockApollo(requestHandlers);
createComponent({ apolloProvider }); createComponent({ apolloProvider, provide });
}; };
afterEach(() => { afterEach(() => {
...@@ -100,7 +106,9 @@ describe('Pipeline graph wrapper', () => { ...@@ -100,7 +106,9 @@ describe('Pipeline graph wrapper', () => {
describe('when there is an error', () => { describe('when there is an error', () => {
beforeEach(async () => { beforeEach(async () => {
createComponentWithApollo(jest.fn().mockRejectedValue(new Error('GraphQL error'))); createComponentWithApollo({
getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')),
});
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
}); });
...@@ -154,7 +162,7 @@ describe('Pipeline graph wrapper', () => { ...@@ -154,7 +162,7 @@ describe('Pipeline graph wrapper', () => {
.mockResolvedValueOnce(mockPipelineResponse) .mockResolvedValueOnce(mockPipelineResponse)
.mockResolvedValueOnce(errorData); .mockResolvedValueOnce(errorData);
createComponentWithApollo(failSucceedFail); createComponentWithApollo({ getPipelineDetailsHandler: failSucceedFail });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
}); });
...@@ -174,4 +182,37 @@ describe('Pipeline graph wrapper', () => { ...@@ -174,4 +182,37 @@ describe('Pipeline graph wrapper', () => {
expect(getGraph().exists()).toBe(true); expect(getGraph().exists()).toBe(true);
}); });
}); });
describe('view dropdown', () => {
describe('when feature flag is off', () => {
beforeEach(async () => {
createComponentWithApollo();
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
});
it('does not appear', () => {
expect(getViewSelector().exists()).toBe(false);
});
});
describe('when feature flag is on', () => {
beforeEach(async () => {
createComponentWithApollo({
provide: {
glFeatures: {
pipelineGraphLayersView: true,
},
},
});
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
});
it('appears', () => {
expect(getViewSelector().exists()).toBe(true);
});
});
});
}); });
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