Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-04-20 13:00:54 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-04-20 13:00:54 +0300
commit3cccd102ba543e02725d247893729e5c73b38295 (patch)
treef36a04ec38517f5deaaacb5acc7d949688d1e187 /app/assets/javascripts/projects
parent205943281328046ef7b4528031b90fbda70c75ac (diff)
Add latest changes from gitlab-org/gitlab@14-10-stable-eev14.10.0-rc42
Diffstat (limited to 'app/assets/javascripts/projects')
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue54
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue74
-rw-r--r--app/assets/javascripts/projects/commit_box/info/constants.js7
-rw-r--r--app/assets/javascripts/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql14
-rw-r--r--app/assets/javascripts/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql19
-rw-r--r--app/assets/javascripts/projects/commit_box/info/index.js3
-rw-r--r--app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js5
-rw-r--r--app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_status.js34
-rw-r--r--app/assets/javascripts/projects/commit_box/info/utils.js14
-rw-r--r--app/assets/javascripts/projects/components/shared/delete_button.vue1
-rw-r--r--app/assets/javascripts/projects/default_project_templates.js6
-rw-r--r--app/assets/javascripts/projects/new/components/deployment_target_select.vue33
-rw-r--r--app/assets/javascripts/projects/new/components/new_project_url_select.vue35
-rw-r--r--app/assets/javascripts/projects/new/constants.js5
-rw-r--r--app/assets/javascripts/projects/new/index.js1
-rw-r--r--app/assets/javascripts/projects/project_new.js47
-rw-r--r--app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue4
17 files changed, 329 insertions, 27 deletions
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
index da14b1e8470..8511f9bdb0f 100644
--- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
@@ -3,11 +3,20 @@ import { GlLoadingIcon } from '@gitlab/ui';
import createFlash from '~/flash';
import { __ } from '~/locale';
import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
+import {
+ getQueryHeaders,
+ toggleQueryPollingByVisibility,
+} from '~/pipelines/components/graph/utils';
+import { formatStages } from '../utils';
import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql';
+import getPipelineStagesQuery from '../graphql/queries/get_pipeline_stages.query.graphql';
+import { COMMIT_BOX_POLL_INTERVAL } from '../constants';
export default {
i18n: {
linkedPipelinesFetchError: __('There was a problem fetching linked pipelines.'),
+ stageConversionError: __('There was a problem handling the pipeline data.'),
+ stagesFetchError: __('There was a problem fetching the pipeline stages.'),
},
components: {
GlLoadingIcon,
@@ -22,6 +31,9 @@ export default {
iid: {
default: '',
},
+ graphqlResourceEtag: {
+ default: '',
+ },
},
props: {
stages: {
@@ -48,10 +60,31 @@ export default {
createFlash({ message: this.$options.i18n.linkedPipelinesFetchError });
},
},
+ pipelineStages: {
+ context() {
+ return getQueryHeaders(this.graphqlResourceEtag);
+ },
+ query: getPipelineStagesQuery,
+ pollInterval: COMMIT_BOX_POLL_INTERVAL,
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ iid: this.iid,
+ };
+ },
+ update({ project }) {
+ return project?.pipeline?.stages?.nodes || [];
+ },
+ error() {
+ createFlash({ message: this.$options.i18n.stagesFetchError });
+ },
+ },
},
data() {
return {
+ formattedStages: [],
pipeline: null,
+ pipelineStages: [],
};
},
computed: {
@@ -65,6 +98,25 @@ export default {
return this.pipeline?.upstream;
},
},
+ watch: {
+ pipelineStages() {
+ // pipelineStages are from GraphQL
+ // stages are from REST
+ // we do this to use dropdown_path for fetching jobs on stage click
+ try {
+ this.formattedStages = formatStages(this.pipelineStages, this.stages);
+ } catch (error) {
+ createFlash({
+ message: this.$options.i18n.stageConversionError,
+ captureError: true,
+ error,
+ });
+ }
+ },
+ },
+ mounted() {
+ toggleQueryPollingByVisibility(this.$apollo.queries.pipelineStages);
+ },
};
</script>
@@ -79,7 +131,7 @@ export default {
/>
<pipeline-mini-graph
- :stages="stages"
+ :stages="formattedStages"
class="gl-display-inline"
data-testid="commit-box-mini-graph"
/>
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue
new file mode 100644
index 00000000000..5a9d3129809
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue
@@ -0,0 +1,74 @@
+<script>
+import { GlLoadingIcon, GlLink } from '@gitlab/ui';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import createFlash from '~/flash';
+import {
+ getQueryHeaders,
+ toggleQueryPollingByVisibility,
+} from '~/pipelines/components/graph/utils';
+import getLatestPipelineStatusQuery from '../graphql/queries/get_latest_pipeline_status.query.graphql';
+import { COMMIT_BOX_POLL_INTERVAL, PIPELINE_STATUS_FETCH_ERROR } from '../constants';
+
+export default {
+ PIPELINE_STATUS_FETCH_ERROR,
+ components: {
+ CiIcon,
+ GlLoadingIcon,
+ GlLink,
+ },
+ inject: {
+ fullPath: {
+ default: '',
+ },
+ iid: {
+ default: '',
+ },
+ graphqlResourceEtag: {
+ default: '',
+ },
+ },
+ apollo: {
+ pipelineStatus: {
+ context() {
+ return getQueryHeaders(this.graphqlResourceEtag);
+ },
+ query: getLatestPipelineStatusQuery,
+ pollInterval: COMMIT_BOX_POLL_INTERVAL,
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ iid: this.iid,
+ };
+ },
+ update({ project }) {
+ return project?.pipeline?.detailedStatus || {};
+ },
+ error() {
+ createFlash({ message: this.$options.PIPELINE_STATUS_FETCH_ERROR });
+ },
+ },
+ },
+ data() {
+ return {
+ pipelineStatus: {},
+ };
+ },
+ computed: {
+ loading() {
+ return this.$apollo.queries.pipelineStatus.loading;
+ },
+ },
+ mounted() {
+ toggleQueryPollingByVisibility(this.$apollo.queries.pipelineStatus);
+ },
+};
+</script>
+
+<template>
+ <div class="gl-display-inline-block gl-vertical-align-middle gl-mr-2">
+ <gl-loading-icon v-if="loading" />
+ <gl-link v-else :href="pipelineStatus.detailsPath">
+ <ci-icon :status="pipelineStatus" :size="24" />
+ </gl-link>
+ </div>
+</template>
diff --git a/app/assets/javascripts/projects/commit_box/info/constants.js b/app/assets/javascripts/projects/commit_box/info/constants.js
new file mode 100644
index 00000000000..be0bf715314
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/constants.js
@@ -0,0 +1,7 @@
+import { __ } from '~/locale';
+
+export const COMMIT_BOX_POLL_INTERVAL = 10000;
+
+export const PIPELINE_STATUS_FETCH_ERROR = __(
+ 'There was a problem fetching the latest pipeline status.',
+);
diff --git a/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql
new file mode 100644
index 00000000000..cec96f82336
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_latest_pipeline_status.query.graphql
@@ -0,0 +1,14 @@
+query getLatestPipelineStatus($fullPath: ID!, $iid: ID!) {
+ project(fullPath: $fullPath) {
+ id
+ pipeline(iid: $iid) {
+ id
+ detailedStatus {
+ id
+ detailsPath
+ icon
+ group
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql
new file mode 100644
index 00000000000..69a29947b16
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql
@@ -0,0 +1,19 @@
+query getPipelineStages($fullPath: ID!, $iid: ID!) {
+ project(fullPath: $fullPath) {
+ id
+ pipeline(iid: $iid) {
+ id
+ stages {
+ nodes {
+ id
+ name
+ detailedStatus {
+ id
+ icon
+ group
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/projects/commit_box/info/index.js b/app/assets/javascripts/projects/commit_box/info/index.js
index 69fe2d30489..7500c152b6a 100644
--- a/app/assets/javascripts/projects/commit_box/info/index.js
+++ b/app/assets/javascripts/projects/commit_box/info/index.js
@@ -2,6 +2,7 @@ import { fetchCommitMergeRequests } from '~/commit_merge_requests';
import { initCommitPipelineMiniGraph } from './init_commit_pipeline_mini_graph';
import { initDetailsButton } from './init_details_button';
import { loadBranches } from './load_branches';
+import initCommitPipelineStatus from './init_commit_pipeline_status';
export const initCommitBoxInfo = () => {
// Display commit related branches
@@ -14,4 +15,6 @@ export const initCommitBoxInfo = () => {
initCommitPipelineMiniGraph();
initDetailsButton();
+
+ initCommitPipelineStatus();
};
diff --git a/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js b/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js
index 1d4ec4c110b..c206e648561 100644
--- a/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js
+++ b/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_mini_graph.js
@@ -5,7 +5,7 @@ import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(),
+ defaultClient: createDefaultClient({}, { useGet: true }),
});
export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipeline-mini-graph') => {
@@ -15,7 +15,7 @@ export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipelin
return;
}
- const { stages, fullPath, iid } = el.dataset;
+ const { stages, fullPath, iid, graphqlResourceEtag } = el.dataset;
// Some commits have no pipeline, code splitting to load the pipeline optionally
const { default: CommitBoxPipelineMiniGraph } = await import(
@@ -30,6 +30,7 @@ export const initCommitPipelineMiniGraph = async (selector = '.js-commit-pipelin
fullPath,
iid,
dataMethod: 'graphql',
+ graphqlResourceEtag,
},
render(createElement) {
return createElement(CommitBoxPipelineMiniGraph, {
diff --git a/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_status.js b/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_status.js
new file mode 100644
index 00000000000..d5e62531283
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/init_commit_pipeline_status.js
@@ -0,0 +1,34 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import CommitBoxPipelineStatus from './components/commit_box_pipeline_status.vue';
+
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient({}, { useGet: true }),
+});
+
+export default (selector = '.js-commit-pipeline-status') => {
+ const el = document.querySelector(selector);
+
+ if (!el) {
+ return;
+ }
+
+ const { fullPath, iid, graphqlResourceEtag } = el.dataset;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ apolloProvider,
+ provide: {
+ fullPath,
+ iid,
+ graphqlResourceEtag,
+ },
+ render(createElement) {
+ return createElement(CommitBoxPipelineStatus);
+ },
+ });
+};
diff --git a/app/assets/javascripts/projects/commit_box/info/utils.js b/app/assets/javascripts/projects/commit_box/info/utils.js
new file mode 100644
index 00000000000..ea7eb35cbaf
--- /dev/null
+++ b/app/assets/javascripts/projects/commit_box/info/utils.js
@@ -0,0 +1,14 @@
+export const formatStages = (graphQLStages = [], restStages = []) => {
+ if (graphQLStages.length !== restStages.length) {
+ throw new Error('Rest stages and graphQl stages must be the same length');
+ }
+
+ return graphQLStages.map((stage, index) => {
+ return {
+ name: stage.name,
+ status: stage.detailedStatus,
+ dropdown_path: restStages[index]?.dropdown_path || '',
+ title: restStages[index].title || '',
+ };
+ });
+};
diff --git a/app/assets/javascripts/projects/components/shared/delete_button.vue b/app/assets/javascripts/projects/components/shared/delete_button.vue
index fd71a246a26..277af2f281e 100644
--- a/app/assets/javascripts/projects/components/shared/delete_button.vue
+++ b/app/assets/javascripts/projects/components/shared/delete_button.vue
@@ -104,7 +104,6 @@ export default {
<gl-modal
ref="removeModal"
:modal-id="modalId"
- size="sm"
ok-variant="danger"
footer-class="gl-bg-gray-10 gl-p-5"
title-class="gl-text-red-500"
diff --git a/app/assets/javascripts/projects/default_project_templates.js b/app/assets/javascripts/projects/default_project_templates.js
index 0393d82ca36..6708b7bd9e2 100644
--- a/app/assets/javascripts/projects/default_project_templates.js
+++ b/app/assets/javascripts/projects/default_project_templates.js
@@ -57,9 +57,9 @@ export default {
text: s__('ProjectTemplates|Pages/Hexo'),
icon: '.template-option .icon-hexo',
},
- sse_middleman: {
- text: s__('ProjectTemplates|Static Site Editor/Middleman'),
- icon: '.template-option .icon-sse_middleman',
+ middleman: {
+ text: s__('ProjectTemplates|Pages/Middleman'),
+ icon: '.template-option .icon-middleman',
},
gitpod_spring_petclinic: {
text: s__('ProjectTemplates|Gitpod/Spring Petclinic'),
diff --git a/app/assets/javascripts/projects/new/components/deployment_target_select.vue b/app/assets/javascripts/projects/new/components/deployment_target_select.vue
index f3b7e39f148..0003134f15c 100644
--- a/app/assets/javascripts/projects/new/components/deployment_target_select.vue
+++ b/app/assets/javascripts/projects/new/components/deployment_target_select.vue
@@ -1,12 +1,15 @@
<script>
-import { GlFormGroup, GlFormSelect } from '@gitlab/ui';
+import { GlFormGroup, GlFormSelect, GlFormText, GlSprintf, GlLink } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import {
DEPLOYMENT_TARGET_SELECTIONS,
DEPLOYMENT_TARGET_LABEL,
DEPLOYMENT_TARGET_EVENT,
+ VISIT_DOCS_EVENT,
NEW_PROJECT_FORM,
+ K8S_OPTION,
} from '../constants';
const trackingMixin = Tracking.mixin({ label: DEPLOYMENT_TARGET_LABEL });
@@ -15,12 +18,21 @@ export default {
i18n: {
deploymentTargetLabel: s__('Deployment Target|Project deployment target (optional)'),
defaultOption: s__('Deployment Target|Select the deployment target'),
+ k8sEducationText: s__(
+ 'Deployment Target|%{linkStart}How to provision or deploy to Kubernetes clusters from GitLab?%{linkEnd}',
+ ),
},
deploymentTargets: DEPLOYMENT_TARGET_SELECTIONS,
+ VISIT_DOCS_EVENT,
+ DEPLOYMENT_TARGET_LABEL,
selectId: 'deployment-target-select',
+ helpPageUrl: helpPagePath('user/clusters/agent/index'),
components: {
GlFormGroup,
GlFormSelect,
+ GlFormText,
+ GlSprintf,
+ GlLink,
},
mixins: [trackingMixin],
data() {
@@ -29,6 +41,11 @@ export default {
formSubmitted: false,
};
},
+ computed: {
+ isK8sOptionSelected() {
+ return this.selectedTarget === K8S_OPTION;
+ },
+ },
mounted() {
const form = document.getElementById(NEW_PROJECT_FORM);
form.addEventListener('submit', () => {
@@ -52,10 +69,24 @@ export default {
:id="$options.selectId"
v-model="selectedTarget"
:options="$options.deploymentTargets"
+ class="input-lg"
>
<template #first>
<option :value="null" disabled>{{ $options.i18n.defaultOption }}</option>
</template>
</gl-form-select>
+
+ <gl-form-text v-if="isK8sOptionSelected">
+ <gl-sprintf :message="$options.i18n.k8sEducationText">
+ <template #link="{ content }">
+ <gl-link
+ :href="$options.helpPageUrl"
+ :data-track-action="$options.VISIT_DOCS_EVENT"
+ :data-track-label="$options.DEPLOYMENT_TARGET_LABEL"
+ >{{ content }}</gl-link
+ >
+ </template>
+ </gl-sprintf>
+ </gl-form-text>
</gl-form-group>
</template>
diff --git a/app/assets/javascripts/projects/new/components/new_project_url_select.vue b/app/assets/javascripts/projects/new/components/new_project_url_select.vue
index f4a21c6057c..506f1ec5ffd 100644
--- a/app/assets/javascripts/projects/new/components/new_project_url_select.vue
+++ b/app/assets/javascripts/projects/new/components/new_project_url_select.vue
@@ -13,6 +13,7 @@ import { MINIMUM_SEARCH_LENGTH } from '~/graphql_shared/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import Tracking from '~/tracking';
import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
+import { s__ } from '~/locale';
import searchNamespacesWhereUserCanCreateProjectsQuery from '../queries/search_namespaces_where_user_can_create_projects.query.graphql';
import eventHub from '../event_hub';
@@ -43,14 +44,7 @@ export default {
debounce: DEBOUNCE_DELAY,
},
},
- inject: [
- 'namespaceFullPath',
- 'namespaceId',
- 'rootUrl',
- 'trackLabel',
- 'userNamespaceFullPath',
- 'userNamespaceId',
- ],
+ inject: ['namespaceFullPath', 'namespaceId', 'rootUrl', 'trackLabel', 'userNamespaceId'],
data() {
return {
currentUser: {},
@@ -62,10 +56,11 @@ export default {
fullPath: this.namespaceFullPath,
}
: {
- id: this.userNamespaceId,
- fullPath: this.userNamespaceFullPath,
+ id: undefined,
+ fullPath: s__('ProjectsNew|Pick a group or namespace'),
},
shouldSkipQuery: true,
+ userNamespaceId: this.userNamespaceId,
};
},
computed: {
@@ -92,6 +87,9 @@ export default {
hasNoMatches() {
return !this.hasGroupMatches && !this.hasNamespaceMatches;
},
+ dropdownPlaceholderClass() {
+ return this.selectedNamespace.id ? '' : 'gl-text-gray-500!';
+ },
},
created() {
eventHub.$on('select-template', this.handleSelectTemplate);
@@ -130,11 +128,18 @@ export default {
</script>
<template>
- <gl-button-group class="input-lg">
- <gl-button class="gl-text-truncate" label :title="rootUrl">{{ rootUrl }}</gl-button>
+ <gl-button-group class="gl-w-full">
+ <gl-button
+ class="js-group-namespace-button gl-text-truncate gl-flex-grow-0!"
+ label
+ :title="rootUrl"
+ >{{ rootUrl }}</gl-button
+ >
+
<gl-dropdown
:text="selectedNamespace.fullPath"
- toggle-class="gl-rounded-top-right-base! gl-rounded-bottom-right-base! gl-w-20"
+ class="js-group-namespace-dropdown gl-flex-grow-1"
+ :toggle-class="`gl-rounded-top-right-base! gl-rounded-bottom-right-base! gl-w-20 ${dropdownPlaceholderClass}`"
data-qa-selector="select_namespace_dropdown"
@show="track('activate_form_input', { label: trackLabel, property: 'project_path' })"
@shown="handleDropdownShown"
@@ -166,11 +171,13 @@ export default {
</template>
</gl-dropdown>
+ <input type="hidden" name="project[selected_namespace_id]" :value="selectedNamespace.id" />
+
<input
id="project_namespace_id"
type="hidden"
name="project[namespace_id]"
- :value="selectedNamespace.id"
+ :value="selectedNamespace.id || userNamespaceId"
/>
</gl-button-group>
</template>
diff --git a/app/assets/javascripts/projects/new/constants.js b/app/assets/javascripts/projects/new/constants.js
index c5e6722981b..e52a84dc07e 100644
--- a/app/assets/javascripts/projects/new/constants.js
+++ b/app/assets/javascripts/projects/new/constants.js
@@ -1,7 +1,9 @@
import { s__ } from '~/locale';
+export const K8S_OPTION = s__('DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)');
+
export const DEPLOYMENT_TARGET_SELECTIONS = [
- s__('DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)'),
+ K8S_OPTION,
s__('DeploymentTarget|Managed container runtime (Fargate, Cloud Run, DigitalOcean App)'),
s__('DeploymentTarget|Self-managed container runtime (Podman, Docker Swarm, Docker Compose)'),
s__('DeploymentTarget|Heroku'),
@@ -18,3 +20,4 @@ export const DEPLOYMENT_TARGET_SELECTIONS = [
export const NEW_PROJECT_FORM = 'new_project';
export const DEPLOYMENT_TARGET_LABEL = 'new_project_deployment_target';
export const DEPLOYMENT_TARGET_EVENT = 'select_deployment_target';
+export const VISIT_DOCS_EVENT = 'visit_docs';
diff --git a/app/assets/javascripts/projects/new/index.js b/app/assets/javascripts/projects/new/index.js
index 4de9b8a6f47..a72172a4f5e 100644
--- a/app/assets/javascripts/projects/new/index.js
+++ b/app/assets/javascripts/projects/new/index.js
@@ -58,7 +58,6 @@ export function initNewProjectUrlSelect() {
namespaceId: el.dataset.namespaceId,
rootUrl: el.dataset.rootUrl,
trackLabel: el.dataset.trackLabel,
- userNamespaceFullPath: el.dataset.userNamespaceFullPath,
userNamespaceId: el.dataset.userNamespaceId,
},
render: (createElement) => createElement(NewProjectUrlSelect),
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index f1b7e3df7d6..3e1c471f015 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -3,6 +3,7 @@ import { debounce } from 'lodash';
import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '../lib/utils/constants';
+import { ENTER_KEY } from '../lib/utils/keys';
import axios from '../lib/utils/axios_utils';
import {
convertToTitleCase,
@@ -14,6 +15,7 @@ import {
let hasUserDefinedProjectPath = false;
let hasUserDefinedProjectName = false;
const invalidInputClass = 'gl-field-error-outline';
+const invalidDropdownClass = 'gl-inset-border-1-red-400!';
const cancelSource = axios.CancelToken.source();
const endpoint = `${gon.relative_url_root}/import/url/validate`;
@@ -50,6 +52,25 @@ const onProjectPathChange = ($projectNameInput, $projectPathInput, hasExistingPr
}
};
+const selectedNamespaceId = () => document.querySelector('[name="project[selected_namespace_id]"]');
+const dropdownButton = () => document.querySelector('.js-group-namespace-dropdown > button');
+const namespaceButton = () => document.querySelector('.js-group-namespace-button');
+const namespaceError = () => document.querySelector('.js-group-namespace-error');
+
+const validateGroupNamespaceDropdown = (e) => {
+ if (selectedNamespaceId() && !selectedNamespaceId().attributes.value) {
+ document.querySelector('input[data-qa-selector="project_name"]').reportValidity();
+ e.preventDefault();
+ dropdownButton().classList.add(invalidDropdownClass);
+ namespaceButton().classList.add(invalidDropdownClass);
+ namespaceError().classList.remove('gl-display-none');
+ } else {
+ dropdownButton().classList.remove(invalidDropdownClass);
+ namespaceButton().classList.remove(invalidDropdownClass);
+ namespaceError().classList.add('gl-display-none');
+ }
+};
+
const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
const specialRepo = document.querySelector('.js-user-readme-repo');
@@ -70,6 +91,10 @@ const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
$projectPathInput.val() !== $projectPathInput.data('username'),
);
});
+
+ document.querySelector('.js-create-project-button').addEventListener('click', (e) => {
+ validateGroupNamespaceDropdown(e);
+ });
};
const deriveProjectPathFromUrl = ($projectImportUrl) => {
@@ -158,7 +183,11 @@ const bindEvents = () => {
$projectTemplateButtons.addClass('hidden');
$projectFieldsForm.addClass('selected');
$selectedIcon.empty();
- const value = $(this).val();
+
+ const $selectedTemplate = $(this);
+ $selectedTemplate.prop('checked', true);
+
+ const value = $selectedTemplate.val();
const selectedTemplate = DEFAULT_PROJECT_TEMPLATES[value];
$selectedTemplateText.text(selectedTemplate.text);
@@ -170,7 +199,21 @@ const bindEvents = () => {
setProjectNamePathHandlers($activeTabProjectName, $activeTabProjectPath);
}
- $useTemplateBtn.on('change', chooseTemplate);
+ function toggleActiveClassOnLabel(event) {
+ const $label = $(event.target).parent();
+ $label.toggleClass('active');
+ }
+
+ function chooseTemplateOnEnter(event) {
+ if (event.code === ENTER_KEY) {
+ chooseTemplate.call(this);
+ }
+ }
+
+ $useTemplateBtn.on('click', chooseTemplate);
+
+ $useTemplateBtn.on('focus focusout', toggleActiveClassOnLabel);
+ $useTemplateBtn.on('keypress', chooseTemplateOnEnter);
$changeTemplateBtn.on('click', () => {
$projectTemplateButtons.removeClass('hidden');
diff --git a/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue b/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
index e8b0e95b142..d4c97cbf038 100644
--- a/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
+++ b/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
@@ -1,6 +1,7 @@
<script>
import { GlTokenSelector, GlAvatarLabeled } from '@gitlab/ui';
import { s__ } from '~/locale';
+import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import searchProjectTopics from '../queries/project_topics_search.query.graphql';
export default {
@@ -65,6 +66,7 @@ export default {
this.$emit('update', tokens);
},
},
+ AVATAR_SHAPE_OPTION_RECT,
};
</script>
<template>
@@ -85,7 +87,7 @@ export default {
:entity-name="dropdownItem.name"
:label="dropdownItem.name"
:size="32"
- shape="rect"
+ :shape="$options.AVATAR_SHAPE_OPTION_RECT"
/>
</template>
</gl-token-selector>