Commit 83f1d280 authored by Anna Vovchenko's avatar Anna Vovchenko Committed by Andrew Fontaine

Request deployment target info from users

As we want to learn more about the deployment target of the new project,
we are adding the select element to the project creation form.
The selected option will be stored as a Snowplow event.

Changelog: added
parent 0e114180
import { initNewProjectCreation, initNewProjectUrlSelect } from '~/projects/new'; import {
initNewProjectCreation,
initNewProjectUrlSelect,
initDeploymentTargetSelect,
} from '~/projects/new';
import initProjectVisibilitySelector from '~/projects/project_visibility'; import initProjectVisibilitySelector from '~/projects/project_visibility';
import initProjectNew from '~/projects/project_new'; import initProjectNew from '~/projects/project_new';
...@@ -6,3 +10,4 @@ initProjectVisibilitySelector(); ...@@ -6,3 +10,4 @@ initProjectVisibilitySelector();
initProjectNew.bindEvents(); initProjectNew.bindEvents();
initNewProjectCreation(); initNewProjectCreation();
initNewProjectUrlSelect(); initNewProjectUrlSelect();
initDeploymentTargetSelect();
<script>
import { GlFormGroup, GlFormSelect } from '@gitlab/ui';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import {
DEPLOYMENT_TARGET_SELECTIONS,
DEPLOYMENT_TARGET_LABEL,
DEPLOYMENT_TARGET_EVENT,
NEW_PROJECT_FORM,
} from '../constants';
const trackingMixin = Tracking.mixin({ label: DEPLOYMENT_TARGET_LABEL });
export default {
i18n: {
deploymentTargetLabel: s__('Deployment Target|Project deployment target (optional)'),
defaultOption: s__('Deployment Target|Select the deployment target'),
},
deploymentTargets: DEPLOYMENT_TARGET_SELECTIONS,
selectId: 'deployment-target-select',
components: {
GlFormGroup,
GlFormSelect,
},
mixins: [trackingMixin],
data() {
return {
selectedTarget: null,
formSubmitted: false,
};
},
mounted() {
const form = document.getElementById(NEW_PROJECT_FORM);
form.addEventListener('submit', () => {
this.formSubmitted = true;
this.trackSelection();
});
},
methods: {
trackSelection() {
if (this.formSubmitted && this.selectedTarget) {
this.track(DEPLOYMENT_TARGET_EVENT, { property: this.selectedTarget });
}
},
},
};
</script>
<template>
<gl-form-group :label="$options.i18n.deploymentTargetLabel" :label-for="$options.selectId">
<gl-form-select
:id="$options.selectId"
v-model="selectedTarget"
:options="$options.deploymentTargets"
>
<template #first>
<option :value="null" disabled>{{ $options.i18n.defaultOption }}</option>
</template>
</gl-form-select>
</gl-form-group>
</template>
import { s__ } from '~/locale';
export const DEPLOYMENT_TARGET_SELECTIONS = [
s__('DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)'),
s__('DeploymentTarget|Managed container runtime (Fargate, Cloud Run, DigitalOcean App)'),
s__('DeploymentTarget|Self-managed container runtime (Podman, Docker Swarm, Docker Compose)'),
s__('DeploymentTarget|Heroku'),
s__('DeploymentTarget|Virtual machine (for example, EC2)'),
s__('DeploymentTarget|Mobile app store'),
s__('DeploymentTarget|Registry (package or container)'),
s__('DeploymentTarget|Infrastructure provider (Terraform, Cloudformation, and so on)'),
s__('DeploymentTarget|Serverless backend (Lambda, Cloud functions)'),
s__('DeploymentTarget|GitLab Pages'),
s__('DeploymentTarget|Other hosting service'),
s__('DeploymentTarget|None'),
];
export const NEW_PROJECT_FORM = 'new_project';
export const DEPLOYMENT_TARGET_LABEL = 'new_project_deployment_target';
export const DEPLOYMENT_TARGET_EVENT = 'select_deployment_target';
...@@ -4,6 +4,7 @@ import createDefaultClient from '~/lib/graphql'; ...@@ -4,6 +4,7 @@ import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import NewProjectCreationApp from './components/app.vue'; import NewProjectCreationApp from './components/app.vue';
import NewProjectUrlSelect from './components/new_project_url_select.vue'; import NewProjectUrlSelect from './components/new_project_url_select.vue';
import DeploymentTargetSelect from './components/deployment_target_select.vue';
export function initNewProjectCreation() { export function initNewProjectCreation() {
const el = document.querySelector('.js-new-project-creation'); const el = document.querySelector('.js-new-project-creation');
...@@ -64,3 +65,16 @@ export function initNewProjectUrlSelect() { ...@@ -64,3 +65,16 @@ export function initNewProjectUrlSelect() {
}), }),
); );
} }
export function initDeploymentTargetSelect() {
const el = document.querySelector('.js-deployment-target-select');
if (!el) {
return null;
}
return new Vue({
el,
render: (createElement) => createElement(DeploymentTargetSelect),
});
}
...@@ -46,6 +46,8 @@ ...@@ -46,6 +46,8 @@
= s_('ProjectsNew|Project description %{tag_start}(optional)%{tag_end}').html_safe % { tag_start: '<span>'.html_safe, tag_end: '</span>'.html_safe } = s_('ProjectsNew|Project description %{tag_start}(optional)%{tag_end}').html_safe % { tag_start: '<span>'.html_safe, tag_end: '</span>'.html_safe }
= f.text_area :description, placeholder: s_('ProjectsNew|Description format'), class: "form-control gl-form-input", rows: 3, maxlength: 250, data: { track_label: "#{track_label}", track_action: "activate_form_input", track_property: "project_description", track_value: "" } = f.text_area :description, placeholder: s_('ProjectsNew|Description format'), class: "form-control gl-form-input", rows: 3, maxlength: 250, data: { track_label: "#{track_label}", track_action: "activate_form_input", track_property: "project_description", track_value: "" }
.js-deployment-target-select
= f.label :visibility_level, class: 'label-bold' do = f.label :visibility_level, class: 'label-bold' do
= s_('ProjectsNew|Visibility Level') = s_('ProjectsNew|Visibility Level')
= link_to sprite_icon('question-o'), help_page_path('public_access/public_access'), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer' = link_to sprite_icon('question-o'), help_page_path('public_access/public_access'), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer'
......
---
description: Deployment target option selected from new project creation form
category: projects:new
action: select_deployment_target
label_description: new_project_deployment_target
property_description: selected option (string)
product_section: ops
product_stage: configure
product_group: group::configure
product_category:
milestone: "14.8"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79873
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate
...@@ -99,6 +99,8 @@ To create a blank project: ...@@ -99,6 +99,8 @@ To create a blank project:
slug as the URL path to the project. To change the slug, first enter the project name, slug as the URL path to the project. To change the slug, first enter the project name,
then change the slug. then change the slug.
- In the **Project description (optional)** field, enter the description of your project's dashboard. - In the **Project description (optional)** field, enter the description of your project's dashboard.
- In the **Project target (optional)** field, select your project's deployment target.
This information helps GitLab better understand its users and their deployment requirements.
- To modify the project's [viewing and access rights](../../public_access/public_access.md) for - To modify the project's [viewing and access rights](../../public_access/public_access.md) for
users, change the **Visibility Level**. users, change the **Visibility Level**.
- To create README file so that the Git repository is initialized, has a default branch, and - To create README file so that the Git repository is initialized, has a default branch, and
......
...@@ -12093,9 +12093,51 @@ msgstr "" ...@@ -12093,9 +12093,51 @@ msgstr ""
msgid "Deployment Frequency" msgid "Deployment Frequency"
msgstr "" msgstr ""
msgid "Deployment Target|Project deployment target (optional)"
msgstr ""
msgid "Deployment Target|Select the deployment target"
msgstr ""
msgid "Deployment frequency" msgid "Deployment frequency"
msgstr "" msgstr ""
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
msgid "DeploymentTarget|Heroku"
msgstr ""
msgid "DeploymentTarget|Infrastructure provider (Terraform, Cloudformation, and so on)"
msgstr ""
msgid "DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)"
msgstr ""
msgid "DeploymentTarget|Managed container runtime (Fargate, Cloud Run, DigitalOcean App)"
msgstr ""
msgid "DeploymentTarget|Mobile app store"
msgstr ""
msgid "DeploymentTarget|None"
msgstr ""
msgid "DeploymentTarget|Other hosting service"
msgstr ""
msgid "DeploymentTarget|Registry (package or container)"
msgstr ""
msgid "DeploymentTarget|Self-managed container runtime (Podman, Docker Swarm, Docker Compose)"
msgstr ""
msgid "DeploymentTarget|Serverless backend (Lambda, Cloud functions)"
msgstr ""
msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
msgid "Deployments" msgid "Deployments"
msgstr "" msgstr ""
......
import { GlFormGroup, GlFormSelect } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { mockTracking } from 'helpers/tracking_helper';
import DeploymentTargetSelect from '~/projects/new/components/deployment_target_select.vue';
import {
DEPLOYMENT_TARGET_SELECTIONS,
DEPLOYMENT_TARGET_LABEL,
DEPLOYMENT_TARGET_EVENT,
NEW_PROJECT_FORM,
} from '~/projects/new/constants';
describe('Deployment target select', () => {
let wrapper;
let trackingSpy;
const findFormGroup = () => wrapper.findComponent(GlFormGroup);
const findSelect = () => wrapper.findComponent(GlFormSelect);
const createdWrapper = () => {
wrapper = shallowMount(DeploymentTargetSelect, {
stubs: {
GlFormSelect,
},
});
};
const createForm = () => {
setFixtures(`
<form id="${NEW_PROJECT_FORM}">
</form>
`);
};
beforeEach(() => {
createForm();
createdWrapper();
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
afterEach(() => {
wrapper.destroy();
});
it('renders the correct label', () => {
expect(findFormGroup().attributes('label')).toBe('Project deployment target (optional)');
});
it('renders a select with the disabled default option', () => {
expect(findSelect().find('option').text()).toBe('Select the deployment target');
expect(findSelect().find('option').attributes('disabled')).toBe('disabled');
});
describe.each`
selectedTarget | formSubmitted | eventSent
${null} | ${true} | ${false}
${DEPLOYMENT_TARGET_SELECTIONS[0]} | ${false} | ${false}
${DEPLOYMENT_TARGET_SELECTIONS[0]} | ${true} | ${true}
`('Snowplow tracking event', ({ selectedTarget, formSubmitted, eventSent }) => {
beforeEach(() => {
findSelect().vm.$emit('input', selectedTarget);
if (formSubmitted) {
const form = document.getElementById(NEW_PROJECT_FORM);
form.dispatchEvent(new Event('submit'));
}
});
if (eventSent) {
it(`is sent, when the the selectedTarget is ${selectedTarget} and the formSubmitted is ${formSubmitted} `, () => {
expect(trackingSpy).toHaveBeenCalledWith(undefined, DEPLOYMENT_TARGET_EVENT, {
label: DEPLOYMENT_TARGET_LABEL,
property: selectedTarget,
});
});
} else {
it(`is not sent, when the the selectedTarget is ${selectedTarget} and the formSubmitted is ${formSubmitted} `, () => {
expect(trackingSpy).toHaveBeenCalledTimes(0);
});
}
});
});
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