diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-14 03:10:09 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-14 03:10:09 +0300 |
commit | 8aea332821a78e83ce93f5b3bac7de5f95a3c7b8 (patch) | |
tree | 722cb345de067f3b62bbe0f849178c74dec657eb /app | |
parent | 4f4b85e1c7f7a5518f12a6981709cf3ef3f0f653 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
38 files changed, 317 insertions, 1386 deletions
diff --git a/app/assets/images/select2-spinner.gif b/app/assets/images/select2-spinner.gif Binary files differdeleted file mode 100644 index 5b33f7e54f4..00000000000 --- a/app/assets/images/select2-spinner.gif +++ /dev/null diff --git a/app/assets/images/select2.png b/app/assets/images/select2.png Binary files differdeleted file mode 100644 index 1d804ffb996..00000000000 --- a/app/assets/images/select2.png +++ /dev/null diff --git a/app/assets/images/select2x2.png b/app/assets/images/select2x2.png Binary files differdeleted file mode 100644 index 4bdd5c961d4..00000000000 --- a/app/assets/images/select2x2.png +++ /dev/null diff --git a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_modal.vue index b13f7dae2c2..1d6a06a08dc 100644 --- a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_modal.vue +++ b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_modal.vue @@ -453,7 +453,7 @@ export default { data-testid="aws-guidance-tip" @dismiss="dismissTip" > - <div class="gl-display-flex gl-flex-direction-row gl-flex-wrap-wrap gl-md-flex-wrap-nowrap"> + <div class="gl-display-flex gl-flex-direction-row gl-md-flex-wrap-nowraps gl-gap-3"> <div> <p> <gl-sprintf :message="$options.i18n.awsTipMessage"> diff --git a/app/assets/javascripts/ml/experiment_tracking/constants.js b/app/assets/javascripts/ml/experiment_tracking/constants.js index 4092180b988..15462b519e1 100644 --- a/app/assets/javascripts/ml/experiment_tracking/constants.js +++ b/app/assets/javascripts/ml/experiment_tracking/constants.js @@ -15,6 +15,8 @@ export const BASE_SORT_FIELDS = Object.freeze([ }, ]); +export const EMPTY_STATE_SVG = '/assets/illustrations/empty-state/empty-dag-md.svg'; + export const FEATURE_NAME = s__('MlExperimentTracking|Machine learning experiment tracking'); export const FEATURE_FEEDBACK_ISSUE = 'https://gitlab.com/gitlab-org/gitlab/-/issues/381660'; diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/components/ml_experiments_index.vue b/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/components/ml_experiments_index.vue new file mode 100644 index 00000000000..4f2b8db3c00 --- /dev/null +++ b/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/components/ml_experiments_index.vue @@ -0,0 +1,85 @@ +<script> +import { GlTableLite, GlEmptyState, GlLink } from '@gitlab/ui'; +import IncubationAlert from '~/vue_shared/components/incubation/incubation_alert.vue'; +import Pagination from '~/vue_shared/components/incubation/pagination.vue'; +import { + FEATURE_NAME, + FEATURE_FEEDBACK_ISSUE, + EMPTY_STATE_SVG, +} from '~/ml/experiment_tracking/constants'; +import * as constants from '~/ml/experiment_tracking/routes/experiments/index/constants'; +import * as translations from '~/ml/experiment_tracking/routes/experiments/index/translations'; + +export default { + name: 'MlExperimentsIndexApp', + components: { + Pagination, + IncubationAlert, + GlTableLite, + GlEmptyState, + GlLink, + }, + props: { + experiments: { + type: Array, + required: true, + }, + pageInfo: { + type: Object, + required: true, + }, + }, + tableFields: constants.EXPERIMENTS_TABLE_FIELDS, + i18n: translations, + computed: { + hasExperiments() { + return this.experiments.length > 0; + }, + tableItems() { + return this.experiments.map((exp) => ({ + nameColumn: { name: exp.name, path: exp.path }, + candidateCountColumn: exp.candidate_count, + })); + }, + }, + constants: { + FEATURE_NAME, + FEATURE_FEEDBACK_ISSUE, + EMPTY_STATE_SVG, + ...constants, + }, +}; +</script> + +<template> + <div v-if="hasExperiments"> + <h1 class="page-title gl-font-size-h-display"> + {{ $options.i18n.TITLE_LABEL }} + </h1> + + <incubation-alert + :feature-name="$options.constants.FEATURE_NAME" + :link-to-feedback-issue="$options.constants.FEATURE_FEEDBACK_ISSUE" + /> + + <gl-table-lite :items="tableItems" :fields="$options.tableFields"> + <template #cell(nameColumn)="data"> + <gl-link :href="data.value.path"> + {{ data.value.name }} + </gl-link> + </template> + </gl-table-lite> + + <pagination v-if="hasExperiments" v-bind="pageInfo" /> + </div> + + <gl-empty-state + v-else + :title="$options.i18n.EMPTY_STATE_TITLE_LABEL" + :primary-button-text="$options.i18n.CREATE_NEW_LABEL" + :primary-button-link="$options.constants.CREATE_EXPERIMENT_HELP_PATH" + :svg-path="$options.constants.EMPTY_STATE_SVG" + :description="$options.i18n.EMPTY_STATE_DESCRIPTION_LABEL" + class="gl-py-8" + /> +</template> diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/constants.js b/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/constants.js new file mode 100644 index 00000000000..3026bce0972 --- /dev/null +++ b/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/constants.js @@ -0,0 +1,17 @@ +import { s__ } from '~/locale'; +import { helpPagePath } from '~/helpers/help_page_helper'; + +export const CREATE_EXPERIMENT_HELP_PATH = helpPagePath( + 'user/project/ml/experiment_tracking/index.md', + { + anchor: 'tracking-new-experiments-and-trials', + }, +); + +export const EXPERIMENTS_TABLE_FIELDS = Object.freeze([ + { key: 'nameColumn', label: s__('MlExperimentTracking|Experiment') }, + { + key: 'candidateCountColumn', + label: s__('MlExperimentTracking|Logged candidates for experiment'), + }, +]); diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/index.js b/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/index.js new file mode 100644 index 00000000000..b40735ebe22 --- /dev/null +++ b/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/index.js @@ -0,0 +1,3 @@ +import MlExperimentsIndex from './components/ml_experiments_index.vue'; + +export default MlExperimentsIndex; diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/translations.js b/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/translations.js new file mode 100644 index 00000000000..e954c054cf5 --- /dev/null +++ b/app/assets/javascripts/ml/experiment_tracking/routes/experiments/index/translations.js @@ -0,0 +1,11 @@ +import { s__ } from '~/locale'; + +export const TITLE_LABEL = s__('MlExperimentTracking|Model experiments'); + +export const CREATE_NEW_LABEL = s__('MlExperimentTracking|Create a new experiment'); + +export const EMPTY_STATE_TITLE_LABEL = s__('MlExperimentTracking|No experiments'); + +export const EMPTY_STATE_DESCRIPTION_LABEL = s__( + 'MlExperimentTracking|There are no logged experiments for this project. Create a new experiment using the MLflow client.', +); diff --git a/app/assets/javascripts/pages/admin/application_settings/index.js b/app/assets/javascripts/pages/admin/application_settings/index.js index f1e92cf195a..366be334e87 100644 --- a/app/assets/javascripts/pages/admin/application_settings/index.js +++ b/app/assets/javascripts/pages/admin/application_settings/index.js @@ -1,5 +1,4 @@ import initVariableList from '~/ci/ci_variable_list'; -import projectSelect from '~/project_select'; import initSearchSettings from '~/search_settings'; import selfMonitor from '~/self_monitor'; import initSettingsPanels from '~/settings_panels'; @@ -8,5 +7,4 @@ initVariableList('js-instance-variables'); selfMonitor(); // Initialize expandable settings panels initSettingsPanels(); -projectSelect(); initSearchSettings(); diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/table/admin_jobs_table_app.vue b/app/assets/javascripts/pages/admin/jobs/index/components/table/admin_jobs_table_app.vue new file mode 100644 index 00000000000..c5a0509b625 --- /dev/null +++ b/app/assets/javascripts/pages/admin/jobs/index/components/table/admin_jobs_table_app.vue @@ -0,0 +1,19 @@ +<script> +export default { + inject: { + jobStatuses: { + default: null, + }, + url: { + default: '', + }, + emptyStateSvgPath: { + default: '', + }, + }, +}; +</script> + +<template> + <div>{{ __('Jobs') }}</div> +</template> diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js index f9bc097da4c..9df52557212 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/index.js +++ b/app/assets/javascripts/pages/admin/jobs/index/index.js @@ -3,6 +3,7 @@ import { BV_SHOW_MODAL } from '~/lib/utils/constants'; import Translate from '~/vue_shared/translate'; import { CANCEL_JOBS_MODAL_ID } from './components/constants'; import CancelJobsModal from './components/cancel_jobs_modal.vue'; +import AdminJobsTableApp from './components/table/admin_jobs_table_app.vue'; Vue.use(Translate); @@ -34,4 +35,28 @@ function initJobs() { } } -initJobs(); +export function initAdminJobsApp() { + const containerEl = document.getElementById('admin-jobs-app'); + + if (!containerEl) return false; + + const { jobStatuses, emptyStateSvgPath, url } = containerEl.dataset; + + return new Vue({ + el: containerEl, + provide: { + url, + emptyStateSvgPath, + jobStatuses: JSON.parse(jobStatuses), + }, + render(createElement) { + return createElement(AdminJobsTableApp); + }, + }); +} + +if (gon.features.adminJobsVue) { + initAdminJobsApp(); +} else { + initJobs(); +} diff --git a/app/assets/javascripts/pages/dashboard/issues/index.js b/app/assets/javascripts/pages/dashboard/issues/index.js index 659982fcc3a..2ca11e96f69 100644 --- a/app/assets/javascripts/pages/dashboard/issues/index.js +++ b/app/assets/javascripts/pages/dashboard/issues/index.js @@ -3,7 +3,6 @@ import { mountIssuesDashboardApp } from '~/issues/dashboard'; import initManualOrdering from '~/issues/manual_ordering'; import { FILTERED_SEARCH } from '~/filtered_search/constants'; import initFilteredSearch from '~/pages/search/init_filtered_search'; -import projectSelect from '~/project_select'; import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown'; initFilteredSearch({ @@ -12,7 +11,6 @@ initFilteredSearch({ useDefaultState: true, }); -projectSelect(); initNewResourceDropdown(); initManualOrdering(); diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js index f86dc2e2e30..a8c59ea6f3d 100644 --- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js +++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js @@ -2,7 +2,6 @@ import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; import { FILTERED_SEARCH } from '~/filtered_search/constants'; import initFilteredSearch from '~/pages/search/init_filtered_search'; -import projectSelect from '~/project_select'; import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown'; import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants'; import searchUserProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_merge_requests_enabled.query.graphql'; @@ -15,7 +14,6 @@ initFilteredSearch({ useDefaultState: true, }); -projectSelect(); initNewResourceDropdown({ resourceType: RESOURCE_TYPE_MERGE_REQUEST, query: searchUserProjectsWithMergeRequestsEnabled, diff --git a/app/assets/javascripts/pages/dashboard/milestones/index/index.js b/app/assets/javascripts/pages/dashboard/milestones/index/index.js index 951941cc83d..88061d9ca22 100644 --- a/app/assets/javascripts/pages/dashboard/milestones/index/index.js +++ b/app/assets/javascripts/pages/dashboard/milestones/index/index.js @@ -1,9 +1,7 @@ -import projectSelect from '~/project_select'; import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown'; import { RESOURCE_TYPE_MILESTONE } from '~/vue_shared/components/new_resource_dropdown/constants'; import searchUserGroupsAndProjects from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_groups_and_projects.query.graphql'; -projectSelect(); initNewResourceDropdown({ resourceType: RESOURCE_TYPE_MILESTONE, query: searchUserGroupsAndProjects, diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js index 91a0ef07bc4..dec06fe6f4d 100644 --- a/app/assets/javascripts/pages/groups/edit/index.js +++ b/app/assets/javascripts/pages/groups/edit/index.js @@ -6,7 +6,6 @@ import { initGroupSelects } from '~/vue_shared/components/entity_select/init_gro import { initProjectSelects } from '~/vue_shared/components/entity_select/init_project_selects'; import { initCascadingSettingsLockPopovers } from '~/namespaces/cascading_settings'; import mountBadgeSettings from '~/pages/shared/mount_badge_settings'; -import projectSelect from '~/project_select'; import initSearchSettings from '~/search_settings'; import initSettingsPanels from '~/settings_panels'; import initConfirmDanger from '~/init_confirm_danger'; @@ -26,7 +25,5 @@ initGroupSelects(); // Initialize project selectors initProjectSelects(); -projectSelect(); - initSearchSettings(); initCascadingSettingsLockPopovers(); diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js index 40b4c289ab0..2cf75fcf666 100644 --- a/app/assets/javascripts/pages/groups/merge_requests/index.js +++ b/app/assets/javascripts/pages/groups/merge_requests/index.js @@ -3,7 +3,6 @@ import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered import { FILTERED_SEARCH } from '~/filtered_search/constants'; import { initBulkUpdateSidebar } from '~/issuable'; import initFilteredSearch from '~/pages/search/init_filtered_search'; -import projectSelect from '~/project_select'; import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown'; import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants'; import searchUserGroupProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_group_projects_with_merge_requests_enabled.query.graphql'; @@ -19,7 +18,6 @@ initFilteredSearch({ useDefaultState: true, filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, }); -projectSelect(); initNewResourceDropdown({ resourceType: RESOURCE_TYPE_MERGE_REQUEST, query: searchUserGroupProjectsWithMergeRequestsEnabled, diff --git a/app/assets/javascripts/pages/projects/ml/experiments/index/index.js b/app/assets/javascripts/pages/projects/ml/experiments/index/index.js new file mode 100644 index 00000000000..e9ffd4b528b --- /dev/null +++ b/app/assets/javascripts/pages/projects/ml/experiments/index/index.js @@ -0,0 +1,24 @@ +import Vue from 'vue'; +import MlExperimentsIndex from '~/ml/experiment_tracking/routes/experiments/index'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; + +const initIndexMlExperiments = () => { + const element = document.querySelector('#js-project-ml-experiments-index'); + if (!element) { + return undefined; + } + + const props = { + experiments: JSON.parse(element.dataset.experiments), + pageInfo: convertObjectPropsToCamelCase(JSON.parse(element.dataset.pageInfo)), + }; + + return new Vue({ + el: element, + render(h) { + return h(MlExperimentsIndex, { props }); + }, + }); +}; + +initIndexMlExperiments(); diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js index 4c9eb830ff6..5773737c41b 100644 --- a/app/assets/javascripts/pages/projects/project.js +++ b/app/assets/javascripts/pages/projects/project.js @@ -9,7 +9,6 @@ import axios from '~/lib/utils/axios_utils'; import { serializeForm } from '~/lib/utils/forms'; import { mergeUrlParams } from '~/lib/utils/url_utility'; import { __ } from '~/locale'; -import projectSelect from '~/project_select'; const BRANCH_REF_TYPE = 'heads'; const TAG_REF_TYPE = 'tags'; @@ -44,13 +43,6 @@ export default class Project { $(this).parents('.auto-devops-implicitly-enabled-banner').remove(); return e.preventDefault(); }); - - Project.projectSelectDropdown(); - } - - static projectSelectDropdown() { - projectSelect(); - $('.project-item-select').on('click', (e) => Project.changeProject($(e.currentTarget).val())); } static changeProject(url) { diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js deleted file mode 100644 index 705234537a8..00000000000 --- a/app/assets/javascripts/project_select.js +++ /dev/null @@ -1,128 +0,0 @@ -/* eslint-disable func-names */ - -import $ from 'jquery'; -import { createAlert } from '~/flash'; -import Api from './api'; -import { loadCSSFile } from './lib/utils/css_utils'; -import { s__ } from './locale'; -import ProjectSelectComboButton from './project_select_combo_button'; - -const projectSelect = async () => { - await loadCSSFile(gon.select2_css_path); - - $('.ajax-project-select').each(function (i, select) { - let placeholder; - const simpleFilter = $(select).data('simpleFilter') || false; - const isInstantiated = $(select).data('select2'); - this.groupId = $(select).data('groupId'); - this.userId = $(select).data('userId'); - this.includeGroups = $(select).data('includeGroups'); - this.allProjects = $(select).data('allProjects') || false; - this.orderBy = $(select).data('orderBy') || 'id'; - this.withIssuesEnabled = $(select).data('withIssuesEnabled'); - this.withMergeRequestsEnabled = $(select).data('withMergeRequestsEnabled'); - this.withShared = - $(select).data('withShared') === undefined ? true : $(select).data('withShared'); - this.includeProjectsInSubgroups = $(select).data('includeProjectsInSubgroups') || false; - this.allowClear = $(select).data('allowClear') || false; - - placeholder = s__('ProjectSelect|Search for project'); - if (this.includeGroups) { - placeholder += s__('ProjectSelect| or group'); - } - - $(select).select2({ - placeholder, - minimumInputLength: 0, - query: (query) => { - let projectsCallback; - const finalCallback = function (projects) { - const data = { - results: projects, - }; - return query.callback(data); - }; - if (this.includeGroups) { - projectsCallback = function (projects) { - const groupsCallback = function (groups) { - const data = groups.concat(projects); - return finalCallback(data); - }; - return Api.groups(query.term, {}, groupsCallback); - }; - } else { - projectsCallback = finalCallback; - } - if (this.groupId) { - return Api.groupProjects( - this.groupId, - query.term, - { - with_issues_enabled: this.withIssuesEnabled, - with_merge_requests_enabled: this.withMergeRequestsEnabled, - with_shared: this.withShared, - include_subgroups: this.includeProjectsInSubgroups, - order_by: 'similarity', - simple: true, - }, - projectsCallback, - ).catch(() => { - createAlert({ - message: s__('ProjectSelect|Something went wrong while fetching projects'), - }); - }); - } else if (this.userId) { - return Api.userProjects( - this.userId, - query.term, - { - with_issues_enabled: this.withIssuesEnabled, - with_merge_requests_enabled: this.withMergeRequestsEnabled, - with_shared: this.withShared, - include_subgroups: this.includeProjectsInSubgroups, - }, - projectsCallback, - ); - } - return Api.projects( - query.term, - { - order_by: this.orderBy, - with_issues_enabled: this.withIssuesEnabled, - with_merge_requests_enabled: this.withMergeRequestsEnabled, - membership: !this.allProjects, - }, - projectsCallback, - ); - }, - id(project) { - if (simpleFilter) return project.id; - return JSON.stringify({ - name: project.name, - url: project.web_url, - }); - }, - text(project) { - return project.name_with_namespace || project.name; - }, - - initSelection(el, callback) { - return Api.project(el.val()).then(({ data }) => callback(data)); - }, - - allowClear: this.allowClear, - - dropdownCssClass: 'ajax-project-dropdown', - }); - if (isInstantiated || simpleFilter) return select; - return new ProjectSelectComboButton(select); - }); -}; - -export default () => { - if ($('.ajax-project-select').length) { - import(/* webpackChunkName: 'select2' */ 'select2/select2') - .then(projectSelect) - .catch(() => {}); - } -}; diff --git a/app/assets/javascripts/project_select_combo_button.js b/app/assets/javascripts/project_select_combo_button.js deleted file mode 100644 index ad80032c551..00000000000 --- a/app/assets/javascripts/project_select_combo_button.js +++ /dev/null @@ -1,122 +0,0 @@ -import $ from 'jquery'; -import { sprintf, __ } from '~/locale'; -import { sanitizeUrl } from '~/lib/utils/url_utility'; -import AccessorUtilities from './lib/utils/accessor'; -import { loadCSSFile } from './lib/utils/css_utils'; - -export default class ProjectSelectComboButton { - constructor(select) { - this.projectSelectInput = $(select); - this.newItemBtn = $('.js-new-project-item-link'); - this.resourceType = this.newItemBtn.data('type'); - this.resourceLabel = this.newItemBtn.data('label'); - this.formattedText = this.deriveTextVariants(); - this.groupId = this.projectSelectInput.data('groupId'); - this.bindEvents(); - this.initLocalStorage(); - } - - bindEvents() { - this.projectSelectInput - .siblings('.new-project-item-select-button') - .on('click', (e) => this.openDropdown(e)); - - this.newItemBtn.on('click', (e) => { - if (!this.getProjectFromLocalStorage()) { - e.preventDefault(); - this.openDropdown(e); - } - }); - - this.projectSelectInput.on('change', () => this.selectProject()); - } - - initLocalStorage() { - const localStorageIsSafe = AccessorUtilities.canUseLocalStorage(); - - if (localStorageIsSafe) { - this.localStorageKey = [ - 'group', - this.groupId, - this.formattedText.localStorageItemType, - 'recent-project', - ].join('-'); - this.setBtnTextFromLocalStorage(); - } - } - - // eslint-disable-next-line class-methods-use-this - openDropdown(event) { - import(/* webpackChunkName: 'select2' */ 'select2/select2') - .then(() => { - // eslint-disable-next-line promise/no-nesting - loadCSSFile(gon.select2_css_path) - .then(() => { - $(event.currentTarget).siblings('.project-item-select').select2('open'); - }) - .catch(() => {}); - }) - .catch(() => {}); - } - - selectProject() { - const selectedProjectData = JSON.parse(this.projectSelectInput.val()); - const projectUrl = `${selectedProjectData.url}/${this.projectSelectInput.data('relativePath')}`; - const projectName = selectedProjectData.name; - - const projectMeta = { - url: projectUrl, - name: projectName, - }; - - this.setNewItemBtnAttributes(projectMeta); - this.setProjectInLocalStorage(projectMeta); - } - - setBtnTextFromLocalStorage() { - const cachedProjectData = this.getProjectFromLocalStorage(); - - this.setNewItemBtnAttributes(cachedProjectData); - } - - setNewItemBtnAttributes(project) { - if (project) { - this.newItemBtn.attr('href', sanitizeUrl(project.url)); - this.newItemBtn.text( - sprintf(__('New %{type} in %{project}'), { - type: this.resourceLabel, - project: project.name, - }), - ); - } else { - this.newItemBtn.text( - sprintf(__('Select project to create %{type}'), { - type: this.formattedText.presetTextSuffix, - }), - ); - } - } - - getProjectFromLocalStorage() { - const projectString = localStorage.getItem(this.localStorageKey); - - return JSON.parse(projectString); - } - - setProjectInLocalStorage(projectMeta) { - const projectString = JSON.stringify(projectMeta); - - localStorage.setItem(this.localStorageKey, projectString); - } - - deriveTextVariants() { - // the trailing slice call depluralizes each of these strings (e.g. new-issues -> new-issue) - const localStorageItemType = `new-${this.resourceType.split('_').join('-').slice(0, -1)}`; - const presetTextSuffix = this.resourceType.split('_').join(' ').slice(0, -1); - - return { - localStorageItemType, // new-issue / new-merge-request - presetTextSuffix, // issue / merge request - }; - } -} diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 0bc920b1f73..cc7a45e1c82 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -301,12 +301,6 @@ img.emoji { height: 4px; } -.project-item-select-holder { - .project-item-select { - min-width: 250px; - } -} - .gl-accessibility { &:focus { display: flex; diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index a131ab667e1..7baf84198e4 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -63,11 +63,6 @@ $search-input-field-x-min-width: 200px; @include gl-focus($focus-ring: $focus-ring-dark); } } - - .project-item-select { - right: auto; - left: 0; - } } .dropdown.open { diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss index 7e0a601223d..5ba0b1d0828 100644 --- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss +++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss @@ -179,11 +179,6 @@ display: inline-block; } - .project-item-select-holder { - margin: 0; - width: 100%; - } - &.inline { display: flex; flex-flow: row wrap; @@ -367,13 +362,3 @@ } } } - -.project-item-select-holder.btn-group { - .new-project-item-select-button { - max-width: 32px; - } -} - -.empty-state .project-item-select-holder.btn-group { - max-width: 320px; -} diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index f136a8c3a08..c616915073e 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -788,12 +788,6 @@ $stat-graph-selection-fill: #333; $stat-graph-selection-stroke: #333; /* -* Selects -*/ -$select2-drop-shadow1: rgba(76, 86, 103, 0.247059); -$select2-drop-shadow2: rgba(31, 37, 50, 0.317647); - -/* * Typography */ $body-text-shadow: rgba(255, 255, 255, 0.01); diff --git a/app/assets/stylesheets/lazy_bundles/select2.scss b/app/assets/stylesheets/lazy_bundles/select2.scss deleted file mode 100644 index f2c372020ef..00000000000 --- a/app/assets/stylesheets/lazy_bundles/select2.scss +++ /dev/null @@ -1,654 +0,0 @@ -/* -Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014 -Updated 2020-10-05 by TimZ -*/ -.select2-container { - margin: 0; - position: relative; - display: inline-block; -} - -.select2-container, -.select2-drop, -.select2-search, -.select2-search input { - box-sizing: border-box; -} - -.select2-container .select2-choice { - display: block; - height: 26px; - padding: 0 0 0 8px; - overflow: hidden; - position: relative; - - border: 1px solid #aaa; - white-space: nowrap; - line-height: 26px; - color: #444; - text-decoration: none; - - border-radius: 4px; - - background-clip: padding-box; - - user-select: none; - - background-color: #fff; - background-image: linear-gradient(to top, #eee 0%, #fff 50%); -} - -html[dir='rtl'] .select2-container .select2-choice { - padding: 0 8px 0 0; -} - -.select2-container.select2-drop-above .select2-choice { - border-bottom-color: #aaa; - - border-radius: 0 0 4px 4px; - - background-image: linear-gradient(to bottom, #eee 0%, #fff 90%); -} - -.select2-container.select2-allowclear .select2-choice .select2-chosen { - margin-right: 42px; -} - -.select2-container .select2-choice > .select2-chosen { - margin-right: 26px; - display: block; - overflow: hidden; - - white-space: nowrap; - - text-overflow: ellipsis; - float: none; - width: auto; -} - -html[dir='rtl'] .select2-container .select2-choice > .select2-chosen { - margin-left: 26px; - margin-right: 0; -} - -.select2-container .select2-choice abbr { - display: none; - width: 12px; - height: 12px; - position: absolute; - right: 24px; - top: 8px; - - font-size: 1px; - text-decoration: none; - - border: 0; - /* stylelint-disable-next-line function-url-quotes */ - background: url(image-path('select2.png')) right top no-repeat; - cursor: pointer; - outline: 0; -} - -.select2-container.select2-allowclear .select2-choice abbr { - display: inline-block; -} - -.select2-container .select2-choice abbr:hover { - background-position: right -11px; - cursor: pointer; -} - -.select2-drop-mask { - border: 0; - margin: 0; - padding: 0; - position: fixed; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 9998; - /* styles required for IE to work */ - background-color: #fff; - filter: alpha(opacity=0); -} - -.select2-drop { - width: 100%; - margin-top: -1px; - position: absolute; - z-index: 9999; - top: 100%; - - background: #fff; - color: #000; - border: 1px solid #aaa; - border-top: 0; - - border-radius: 0 0 4px 4px; - - box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15); -} - -.select2-drop.select2-drop-above { - margin-top: 1px; - border-top: 1px solid #aaa; - border-bottom: 0; - - border-radius: 4px 4px 0 0; - - box-shadow: 0 -4px 5px rgba(0, 0, 0, 0.15); -} - -.select2-drop-active { - border: 1px solid #5897fb; - border-top: 0; -} - -.select2-drop.select2-drop-above.select2-drop-active { - border-top: 1px solid #5897fb; -} - -.select2-drop-auto-width { - border-top: 1px solid #aaa; - width: auto; -} - -.select2-drop-auto-width .select2-search { - padding-top: 4px; -} - -.select2-container .select2-choice .select2-arrow { - display: inline-block; - width: 18px; - height: 100%; - position: absolute; - right: 0; - top: 0; - - border-left: 1px solid #aaa; - border-radius: 0 4px 4px 0; - - background-clip: padding-box; - - background: #ccc; - background-image: linear-gradient(to top, #ccc 0%, #eee 60%); -} - -html[dir='rtl'] .select2-container .select2-choice .select2-arrow { - left: 0; - right: auto; - - border-left: 0; - border-right: 1px solid #aaa; - border-radius: 4px 0 0 4px; -} - -.select2-container .select2-choice .select2-arrow b { - display: block; - width: 100%; - height: 100%; - /* stylelint-disable-next-line function-url-quotes */ - background: url(image-path("select2.png")) no-repeat 0 1px; -} - -html[dir='rtl'] .select2-container .select2-choice .select2-arrow b { - background-position: 2px 1px; -} - -.select2-search { - display: inline-block; - width: 100%; - min-height: 26px; - margin: 0; - padding-left: 4px; - padding-right: 4px; - - position: relative; - z-index: 10000; - - white-space: nowrap; -} - -.select2-search input { - width: 100%; - height: auto !important; - min-height: 26px; - padding: 4px 20px 4px 5px; - margin: 0; - - outline: 0; - font-family: sans-serif; - font-size: 1em; - - border: 1px solid #aaa; - border-radius: 0; - - box-shadow: none; - /* stylelint-disable-next-line function-url-quotes */ - background: url(image-path('select2.png')) no-repeat 100% -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0; -} - -html[dir='rtl'] .select2-search input { - padding: 4px 5px 4px 20px; - /* stylelint-disable-next-line function-url-quotes */ - background: url(image-path('select2.png')) no-repeat -37px -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0; -} - -.select2-drop.select2-drop-above .select2-search input { - margin-top: 4px; -} - -.select2-search input.select2-active { - /* stylelint-disable-next-line function-url-quotes */ - background: url(image-path('select2-spinner.gif')) no-repeat 100%, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0; -} - -.select2-container-active .select2-choice, -.select2-container-active .select2-choices { - border: 1px solid #5897fb; - outline: none; - - box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); -} - -.select2-dropdown-open .select2-choice { - border-bottom-color: transparent; - box-shadow: 0 1px 0 #fff inset; - - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - - background-color: #eee; - background-image: linear-gradient(to top, #fff 0%, #eee 50%); -} - -.select2-dropdown-open.select2-drop-above .select2-choice, -.select2-dropdown-open.select2-drop-above .select2-choices { - border: 1px solid #5897fb; - border-top-color: transparent; - - background-image: linear-gradient(to bottom, #fff 0%, #eee 50%); -} - -.select2-dropdown-open .select2-choice .select2-arrow { - background: transparent; - border-left: 0; - filter: none; -} - -html[dir='rtl'] .select2-dropdown-open .select2-choice .select2-arrow { - border-right: 0; -} - -.select2-dropdown-open .select2-choice .select2-arrow b { - background-position: -18px 1px; -} - -html[dir='rtl'] .select2-dropdown-open .select2-choice .select2-arrow b { - background-position: -16px 1px; -} - -.select2-hidden-accessible { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} - -/* results */ -.select2-results { - max-height: 200px; - padding: 0 0 0 4px; - margin: 4px 4px 4px 0; - position: relative; - overflow-x: hidden; - overflow-y: auto; -} - -html[dir='rtl'] .select2-results { - padding: 0 4px 0 0; - margin: 4px 0 4px 4px; -} - -.select2-results ul.select2-result-sub { - margin: 0; - padding-left: 0; -} - -.select2-results li { - list-style: none; - display: list-item; - background-image: none; -} - -.select2-results li.select2-result-with-children > .select2-result-label { - font-weight: bold; -} - -.select2-results .select2-result-label { - padding: 3px 7px 4px; - margin: 0; - cursor: pointer; - - min-height: 1em; - - user-select: none; -} - -.select2-results-dept-1 .select2-result-label { padding-left: 20px; } -.select2-results-dept-2 .select2-result-label { padding-left: 40px; } -.select2-results-dept-3 .select2-result-label { padding-left: 60px; } -.select2-results-dept-4 .select2-result-label { padding-left: 80px; } -.select2-results-dept-5 .select2-result-label { padding-left: 100px; } -.select2-results-dept-6 .select2-result-label { padding-left: 110px; } -.select2-results-dept-7 .select2-result-label { padding-left: 120px; } - -.select2-results .select2-highlighted { - background: #3875d7; - color: #fff; -} - -.select2-results li em { - background: #feffde; - font-style: normal; -} - -.select2-results .select2-highlighted em { - background: transparent; -} - -.select2-results .select2-highlighted ul { - background: #fff; - color: #000; -} - -.select2-results .select2-no-results, -.select2-results .select2-searching, -.select2-results .select2-ajax-error, -.select2-results .select2-selection-limit { - background: #f4f4f4; - display: list-item; - padding-left: 5px; -} - -/* -disabled look for disabled choices in the results dropdown -*/ -.select2-results .select2-disabled.select2-highlighted { - color: #666; - background: #f4f4f4; - display: list-item; - cursor: default; -} - -.select2-results .select2-disabled { - background: #f4f4f4; - display: list-item; - cursor: default; -} - -.select2-results .select2-selected { - display: none; -} - -.select2-more-results.select2-active { - /* stylelint-disable-next-line function-url-quotes */ - background: #f4f4f4 url(image-path('select2-spinner.gif')) no-repeat 100%; -} - -.select2-results .select2-ajax-error { - background: rgba(255, 50, 50, 0.2); -} - -.select2-more-results { - background: #f4f4f4; - display: list-item; -} - -/* disabled styles */ - -.select2-container.select2-container-disabled .select2-choice { - background-color: #f4f4f4; - background-image: none; - border: 1px solid #ddd; - cursor: default; -} - -.select2-container.select2-container-disabled .select2-choice .select2-arrow { - background-color: #f4f4f4; - background-image: none; - border-left: 0; -} - -.select2-container.select2-container-disabled .select2-choice abbr { - display: none; -} - - -/* multiselect */ - -.select2-container-multi .select2-choices { - height: auto !important; - height: 1%; - margin: 0; - padding: 0 5px 0 0; - position: relative; - - border: 1px solid #aaa; - cursor: text; - overflow: hidden; - - background-color: #fff; - background-image: linear-gradient(to bottom, #eee 1%, #fff 15%); -} - -html[dir='rtl'] .select2-container-multi .select2-choices { - padding: 0 0 0 5px; -} - -.select2-locked { - padding: 3px 5px !important; -} - -.select2-container-multi .select2-choices { - min-height: 26px; -} - -.select2-container-multi.select2-container-active .select2-choices { - border: 1px solid #5897fb; - outline: none; - - box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); -} - -.select2-container-multi .select2-choices li { - float: left; - list-style: none; -} - -html[dir='rtl'] .select2-container-multi .select2-choices li { - float: right; -} - -.select2-container-multi .select2-choices .select2-search-field { - margin: 0; - padding: 0; - white-space: nowrap; -} - -.select2-container-multi .select2-choices .select2-search-field input { - padding: 5px; - margin: 1px 0; - - font-family: sans-serif; - font-size: 100%; - color: #666; - outline: 0; - border: 0; - - box-shadow: none; - background: transparent !important; -} - -.select2-container-multi .select2-choices .select2-search-field input.select2-active { - /* stylelint-disable-next-line function-url-quotes */ - background: #fff url(image-path('select2-spinner.gif')) no-repeat 100% !important; -} - -.select2-default { - color: #999 !important; -} - -.select2-container-multi .select2-choices .select2-search-choice { - padding: 3px 5px 3px 18px; - margin: 3px 0 3px 5px; - position: relative; - - line-height: 13px; - color: #333; - cursor: default; - border: 1px solid #aaa; - - border-radius: 3px; - - box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05); - - background-clip: padding-box; - - user-select: none; - - background-color: #e4e4e4; - background-image: linear-gradient(to bottom, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); -} - -html[dir='rtl'] .select2-container-multi .select2-choices .select2-search-choice { - margin: 3px 5px 3px 0; - padding: 3px 18px 3px 5px; -} - -.select2-container-multi .select2-choices .select2-search-choice .select2-chosen { - cursor: default; -} - -.select2-container-multi .select2-choices .select2-search-choice-focus { - background: #d4d4d4; -} - -.select2-search-choice-close { - display: block; - width: 12px; - height: 13px; - position: absolute; - right: 3px; - top: 4px; - - font-size: 1px; - outline: none; - /* stylelint-disable-next-line function-url-quotes */ - background: url(image-path('select2.png')) right top no-repeat; -} - -html[dir='rtl'] .select2-search-choice-close { - right: auto; - left: 3px; -} - -.select2-container-multi .select2-search-choice-close { - left: 3px; -} - -html[dir='rtl'] .select2-container-multi .select2-search-choice-close { - left: auto; - right: 2px; -} - -.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover { - background-position: right -11px; -} - -.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close { - background-position: right -11px; -} - -/* disabled styles */ -.select2-container-multi.select2-container-disabled .select2-choices { - background-color: #f4f4f4; - background-image: none; - border: 1px solid #ddd; - cursor: default; -} - -.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice { - padding: 3px 5px; - border: 1px solid #ddd; - background-image: none; - background-color: #f4f4f4; -} - -.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { - display: none; - background: none; -} -/* end multiselect */ - - -.select2-result-selectable .select2-match, -.select2-result-unselectable .select2-match { - text-decoration: underline; -} - -.select2-offscreen, -.select2-offscreen:focus { - clip: rect(0 0 0 0) !important; - width: 1px !important; - height: 1px !important; - border: 0 !important; - margin: 0 !important; - padding: 0 !important; - overflow: hidden !important; - position: absolute !important; - outline: 0 !important; - left: 0 !important; - top: 0 !important; -} - -.select2-display-none { - display: none; -} - -.select2-measure-scrollbar { - position: absolute; - top: -10000px; - left: -10000px; - width: 100px; - height: 100px; - overflow: scroll; -} - -@media only screen and (min-resolution: 120dpi) { - .select2-search input, - .select2-search-choice-close, - .select2-container .select2-choice abbr, - .select2-container .select2-choice .select2-arrow b { - /* stylelint-disable-next-line function-url-quotes */ - background-image: url(image-path("select2x2.png")) !important; - background-repeat: no-repeat !important; - background-size: 60px 40px !important; - } - - .select2-search input { - background-position: 100% -21px !important; - } -} - -/* End of select2.css */ - -@import './select2_overrides'; diff --git a/app/assets/stylesheets/lazy_bundles/select2_overrides.scss b/app/assets/stylesheets/lazy_bundles/select2_overrides.scss deleted file mode 100644 index e3cec187fab..00000000000 --- a/app/assets/stylesheets/lazy_bundles/select2_overrides.scss +++ /dev/null @@ -1,341 +0,0 @@ -@import 'page_bundles/mixins_and_variables_and_functions'; -/** Select2 selectbox style override **/ -.select2-container { - width: 100% !important; - - &.input-md, - &.input-lg { - display: block; - } -} - -.select2-container, -.select2-container.select2-drop-above { - .select2-choice { - background: var(--white, $white); - color: var(--gl-text-color, $gl-text-color); - border-color: var(--gray-400, $gray-400); - height: 34px; - padding: $gl-vert-padding $gl-input-padding; - font-size: $gl-font-size; - line-height: 1.42857143; - border-radius: $gl-border-radius-base; - - .select2-arrow { - padding-top: 12px; - padding-right: 20px; - /* stylelint-disable-next-line function-url-quotes */ - background: url(asset_path('chevron-down.png')) no-repeat 2px 8px; - - .gl-dark & { - filter: invert(0.9); - } - - b { - display: none; - } - } - - .select2-chosen { - margin-right: 15px; - } - - &:hover { - border-color: var(--gray-400, $gray-400); - color: var(--gl-text-color, $gl-text-color); - } - } - - // Essentially we’re doing @include form-control-focus here (from - // bootstrap/scss/mixins/_forms.scss), except that the bootstrap mixin adds a - // `&:focus` selector and we’re never actually focusing the .select2-choice - // link nor the .select2-container, the Select2 library focuses an off-screen - // .select2-focusser element instead. - &.select2-container-active:not(.select2-dropdown-open) { - .select2-choice { - color: var(--gray-700, $gray-700); - background-color: var(--white, $white); - border-color: $input-focus-border-color; - outline: 0; - } - - // Reusable focus “glow” box-shadow - @mixin form-control-focus-glow { - @if $enable-shadows { - box-shadow: $input-box-shadow, $input-focus-box-shadow; - } @else { - box-shadow: $input-focus-box-shadow; - } - } - - // Apply the focus “glow” shadow to the .select2-container if it also has - // the .block-truncated class as that applies an overflow: hidden, thereby - // hiding the glow of the nested .select2-choice element. - &.block-truncated { - @include form-control-focus-glow; - } - - // Apply the glow directly to the .select2-choice link if we’re not - // block-truncating the container. - &:not(.block-truncated) .select2-choice { - @include form-control-focus-glow; - } - } - - &.is-invalid { - ~ .invalid-feedback { - display: block; - } - - .select2-choices, - .select2-choice { - border-color: var(--red-500, $red-500); - } - } -} - -.select2-drop, -.select2-drop.select2-drop-above { - background: var(--white, $white); - box-shadow: 0 2px 4px $dropdown-shadow-color; - border-radius: $gl-border-radius-base; - border: 1px solid var(--gray-400, $gray-400); - min-width: 175px; - color: var(--gl-text-color, $gl-text-color); - z-index: 999; - - .modal-open & { - z-index: $zindex-modal + 200; - } -} - -.select2-drop-mask { - z-index: 998; - - .modal-open & { - z-index: $zindex-modal + 100; - } -} - -.select2-drop.select2-drop-above.select2-drop-active { - border-top: 1px solid var(--gray-400, $gray-400); - margin-top: -6px; -} - -.select2-container-active { - .select2-choice, - .select2-choices { - box-shadow: none; - } -} - -.select2-dropdown-open, -.select2-dropdown-open.select2-drop-above { - .select2-choice { - border-color: var(--gray-400, $gray-400); - outline: 0; - } -} - -.select2-container-multi { - .select2-choices { - border-radius: $border-radius-default; - border-color: var(--gray-400, $gray-400); - background: none; - - .select2-search-field input { - padding: 5px $gl-input-padding; - height: auto; - font-family: inherit; - font-size: inherit; - } - - .select2-search-choice { - margin: 5px 0 0 8px; - box-shadow: none; - border-color: var(--gray-400, $gray-400); - color: var(--gl-text-color, $gl-text-color); - line-height: 15px; - background-color: var(--gray-50, $gray-50); - background-image: none; - padding: 3px 18px 3px 5px; - - .select2-search-choice-close { - top: 5px; - left: initial; - right: 3px; - } - - &.select2-search-choice-focus { - border-color: var(--gray-400, $gray-400); - } - } - } -} - -.select2-drop-active { - margin-top: $dropdown-vertical-offset; - font-size: 14px; - - .select2-results { - max-height: 350px; - } -} - -.select2-search { - padding: $grid-size; - - .select2-drop-auto-width & { - padding: $grid-size; - } - - input { - padding: $grid-size; - background: transparent image-url('select2.png'); - color: var(--gl-text-color, $gl-text-color); - background-clip: content-box; - background-origin: content-box; - background-repeat: no-repeat; - background-position: right 0 bottom 0 !important; - border: 1px solid var(--gray-400, $gray-400); - border-radius: $border-radius-default; - line-height: 16px; - transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; - - &:focus { - border-color: var(--blue-300, $blue-300); - } - - &.select2-active { - background-color: var(--white, $white); - background-image: image-url('select2-spinner.gif') !important; - background-origin: content-box; - background-repeat: no-repeat; - background-position: right 6px center !important; - background-size: 16px 16px !important; - } - } - - + .select2-results { - padding-top: 0; - } -} - -.select2-results { - margin: 0; - padding: #{$gl-padding / 2} 0; - - .select2-no-results, - .select2-searching, - .select2-ajax-error, - .select2-selection-limit { - background: transparent; - padding: #{$gl-padding / 2} $gl-padding; - } - - .select2-result-label, - .select2-more-results { - padding: #{$gl-padding / 2} $gl-padding; - } - - .select2-highlighted { - background: transparent; - color: var(--gl-text-color, $gl-text-color); - - .select2-result-label { - background: var(--gray-50, $gray-50); - } - } - - .select2-result { - padding: 0 1px; - } - - li.select2-result-with-children > .select2-result-label { - font-weight: $gl-font-weight-bold; - color: var(--gl-text-color, $gl-text-color); - } -} - -.select2-highlighted { - .group-result { - .group-path { - color: var(--gray-700, $gray-700); - } - } -} - -.select2-result-selectable, -.select2-result-unselectable { - .select2-match { - font-weight: $gl-font-weight-bold; - text-decoration: none; - } -} - -.input-group { - .select2-container { - display: table-cell; - max-width: 180px; - } -} - -.file-editor { - .select2 { - float: right; - } -} - -.import-namespace-select { - > .select2-choice { - border-radius: $border-radius-default 0 0 $border-radius-default; - position: relative; - left: 1px; - } -} - -.issue-form { - .select2-container { - width: 250px !important; - } -} - -.new_project, -.edit-project, -.import-project { - .input-group { - .select2-container { - display: unset; - max-width: unset; - flex-grow: 1; - } - } - - .input-group-prepend, - .input-group-append { - + .select2 a { - border-radius: 0 $gl-border-radius-base $gl-border-radius-base 0; - } - } -} - -.project-path { - .select2-choice { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } -} - -.right-sidebar { - .block { - .select2-container span { - margin-top: 0; - } - } -} - -.block-truncated { - > div:not(.block):not(.select2-display-none) { - display: inline; - } -} diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index c2ac4f32480..75c81b74ba7 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -101,12 +101,6 @@ ul.related-merge-requests > li gl-emoji { } } -.issue-form { - .select2-container { - width: 250px !important; - } -} - .issues-nav-controls { .btn-group:empty { display: none; diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb index ef9264d1615..5ea8c672993 100644 --- a/app/controllers/admin/jobs_controller.rb +++ b/app/controllers/admin/jobs_controller.rb @@ -6,6 +6,10 @@ class Admin::JobsController < Admin::ApplicationController feature_category :continuous_integration urgency :low + before_action do + push_frontend_feature_flag(:admin_jobs_vue) + end + def index # We need all builds for tabs counters @all_builds = Ci::JobsFinder.new(current_user: current_user).execute diff --git a/app/controllers/projects/ml/experiments_controller.rb b/app/controllers/projects/ml/experiments_controller.rb index 20f5b39bac7..00b965542f6 100644 --- a/app/controllers/projects/ml/experiments_controller.rb +++ b/app/controllers/projects/ml/experiments_controller.rb @@ -13,7 +13,12 @@ module Projects MAX_CANDIDATES_PER_PAGE = 30 def index - @experiments = ::Ml::Experiment.by_project_id(@project.id).page(params[:page]).per(MAX_EXPERIMENTS_PER_PAGE) + paginator = ::Ml::Experiment.by_project_id(@project.id) + .with_candidate_count + .keyset_paginate(cursor: params[:cursor], per_page: MAX_EXPERIMENTS_PER_PAGE) + + @experiments = paginator.records + @page_info = page_info(paginator) end def show diff --git a/app/helpers/projects/ml/experiments_helper.rb b/app/helpers/projects/ml/experiments_helper.rb index 9f41db2f8b9..8467ee61b35 100644 --- a/app/helpers/projects/ml/experiments_helper.rb +++ b/app/helpers/projects/ml/experiments_helper.rb @@ -33,7 +33,7 @@ module Projects iid: candidate.iid, path_to_artifact: link_to_artifact(candidate), experiment_name: candidate.experiment.name, - path_to_experiment: link_to_experiment(candidate), + path_to_experiment: link_to_experiment(candidate.project, candidate.experiment), status: candidate.status }, metadata: candidate.metadata @@ -42,6 +42,18 @@ module Projects Gitlab::Json.generate(data) end + def experiments_as_data(project, experiments) + data = experiments.map do |exp| + { + name: exp.name, + path: link_to_experiment(project, exp), + candidate_count: exp.candidate_count + } + end + + Gitlab::Json.generate(data) + end + def page_info(paginator) { has_next_page: paginator.has_next_page?, @@ -67,10 +79,8 @@ module Projects project_ml_candidate_path(candidate.project, candidate.iid) end - def link_to_experiment(candidate) - experiment = candidate.experiment - - project_ml_experiment_path(experiment.project, experiment.iid) + def link_to_experiment(project, experiment) + project_ml_experiment_path(project, experiment.iid) end def user_info(candidate) diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb deleted file mode 100644 index 7ee40c28bad..00000000000 --- a/app/helpers/selects_helper.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module SelectsHelper - def project_select_tag(id, opts = {}) - opts[:class] = [*opts[:class], 'ajax-project-select'].join(' ') - - unless opts.delete(:scope) == :all - if @group - opts['data-group-id'] = @group.id - end - end - - with_feature_enabled_data_attribute = - case opts.delete(:with_feature_enabled) - when 'issues' then 'data-with-issues-enabled' - when 'merge_requests' then 'data-with-merge-requests-enabled' - end - - opts[with_feature_enabled_data_attribute] = true - - hidden_field_tag(id, opts[:selected], opts) - end - - def select2_tag(id, opts = {}) - klass_opts = [opts[:class]] - klass_opts << 'multiselect' if opts[:multiple] - - opts[:class] = klass_opts.join(' ') - value = opts[:selected] || '' - - hidden_field_tag(id, value, opts) - end -end - -SelectsHelper.prepend_mod_with('SelectsHelper') diff --git a/app/services/export_csv/base_service.rb b/app/services/export_csv/base_service.rb index 98ab33d4c33..84d44fd75fc 100644 --- a/app/services/export_csv/base_service.rb +++ b/app/services/export_csv/base_service.rb @@ -5,9 +5,10 @@ module ExportCsv # Target attachment size before base64 encoding TARGET_FILESIZE = 15.megabytes - def initialize(relation, resource_parent) + def initialize(relation, resource_parent, fields = []) @objects = relation @resource_parent = resource_parent + @fields = fields end def csv_data @@ -18,18 +19,25 @@ module ExportCsv raise NotImplementedError end + def invalid_fields + ::ExportCsv::MapExportFieldsService.new(fields, header_to_value_hash).invalid_fields + end + private - attr_reader :resource_parent, :objects + attr_reader :resource_parent, :objects, :fields # rubocop: disable CodeReuse/ActiveRecord def csv_builder - @csv_builder ||= + @csv_builder ||= begin + data_hash = MapExportFieldsService.new(fields, header_to_value_hash).execute + if preload_associations_in_batches? - CsvBuilder.new(objects, header_to_value_hash, associations_to_preload) + CsvBuilder.new(objects, data_hash, associations_to_preload) else - CsvBuilder.new(objects.preload(associations_to_preload), header_to_value_hash, []) + CsvBuilder.new(objects.preload(associations_to_preload), data_hash, []) end + end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/services/export_csv/map_export_fields_service.rb b/app/services/export_csv/map_export_fields_service.rb new file mode 100644 index 00000000000..d4f46c65328 --- /dev/null +++ b/app/services/export_csv/map_export_fields_service.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module ExportCsv + class MapExportFieldsService < BaseService + attr_reader :fields, :data + + def initialize(fields, data) + @fields = fields + @data = data + end + + def execute + return data if fields.empty? + + selected_fields_to_hash + end + + def invalid_fields + fields.reject { |field| permitted_field?(field) } + end + + private + + def selected_fields_to_hash + data.select { |key| requested_field?(key) } + end + + def requested_field?(field) + field.downcase.in?(fields.map(&:downcase)) + end + + def permitted_field?(field) + field.downcase.in?(keys.map(&:downcase)) + end + + def keys + data.keys + end + end +end diff --git a/app/services/work_items/export_csv_service.rb b/app/services/work_items/export_csv_service.rb new file mode 100644 index 00000000000..e0a1b90b597 --- /dev/null +++ b/app/services/work_items/export_csv_service.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module WorkItems + class ExportCsvService < ExportCsv::BaseService + def email(mail_to_user) + # TODO - will be implemented as part of https://gitlab.com/gitlab-org/gitlab/-/issues/379082 + end + + private + + def associations_to_preload + [:work_item_type, :author] + end + + def header_to_value_hash + { + 'Id' => 'iid', + 'Title' => 'title', + 'Type' => ->(work_item) { work_item.work_item_type.name }, + 'Author' => 'author_name', + 'Author Username' => ->(work_item) { work_item.author.username }, + 'Created At (UTC)' => ->(work_item) { work_item.created_at.to_s(:csv) } + } + end + end +end diff --git a/app/views/admin/jobs/index.html.haml b/app/views/admin/jobs/index.html.haml index 667c90f0228..7b00019cc21 100644 --- a/app/views/admin/jobs/index.html.haml +++ b/app/views/admin/jobs/index.html.haml @@ -4,21 +4,25 @@ - breadcrumb_title _("Jobs") - page_title _("Jobs") -.top-area - .scrolling-tabs-container.inner-page-scroll-tabs.gl-flex-grow-1.gl-min-w-0.gl-w-full - .fade-left= sprite_icon('chevron-lg-left', size: 12) - .fade-right= sprite_icon('chevron-lg-right', size: 12) - - build_path_proc = ->(scope) { admin_jobs_path(scope: scope) } - = render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope +- if Feature.enabled?(:admin_jobs_vue) + #admin-jobs-app{ data: { job_statuses: job_statuses.to_json, empty_state_svg_path: image_path('jobs-empty-state.svg'), url: cancel_all_admin_jobs_path } } - - if @all_builds.running_or_pending.any? - #js-stop-jobs-modal - .nav-controls - = render Pajamas::ButtonComponent.new(variant: :danger, button_options: { id: 'js-stop-jobs-button', data: { url: cancel_all_admin_jobs_path } }) do - = s_('AdminArea|Stop all jobs') +- else + .top-area + .scrolling-tabs-container.inner-page-scroll-tabs.gl-flex-grow-1.gl-min-w-0.gl-w-full + .fade-left= sprite_icon('chevron-lg-left', size: 12) + .fade-right= sprite_icon('chevron-lg-right', size: 12) + - build_path_proc = ->(scope) { admin_jobs_path(scope: scope) } + = render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope -.row-content-block.second-block - #{(@scope || 'all').capitalize} jobs + - if @all_builds.running_or_pending.any? + #js-stop-jobs-modal + .nav-controls + = render Pajamas::ButtonComponent.new(variant: :danger, button_options: { id: 'js-stop-jobs-button', data: { url: cancel_all_admin_jobs_path } }) do + = s_('AdminArea|Stop all jobs') -%ul.content-list.builds-content-list.admin-builds-table - = render "projects/jobs/table", builds: @builds, admin: true + .row-content-block.second-block + #{(@scope || 'all').capitalize} jobs + + %ul.content-list.builds-content-list.admin-builds-table + = render "projects/jobs/table", builds: @builds, admin: true diff --git a/app/views/projects/ml/experiments/index.html.haml b/app/views/projects/ml/experiments/index.html.haml index a84cb15d940..dd064239e36 100644 --- a/app/views/projects/ml/experiments/index.html.haml +++ b/app/views/projects/ml/experiments/index.html.haml @@ -1,11 +1,7 @@ -- breadcrumb_title _('ML Experiments') -- page_title _('ML Experiments') +- breadcrumb_title s_('MlExperimentTracking|Model experiments') +- page_title s_('MlExperimentTracking|Model experiments') -.page-title-holder.d-flex.align-items-center - %h1.page-title.gl-font-size-h-display= _('Machine Learning Experiments') - -= render "incubation_banner" - -%div{ class: container_class } - .content-list.builds-content-list - = render "experiment_list", experiments: @experiments, project: @project +#js-project-ml-experiments-index{ data: { + experiments: experiments_as_data(@project, @experiments), + page_info: formatted_page_info(@page_info) +} } diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml deleted file mode 100644 index 2249fb03f62..00000000000 --- a/app/views/shared/_new_project_item_select.html.haml +++ /dev/null @@ -1,9 +0,0 @@ --# This view is used to initialize a select2-based selector, which we are migrating away from. --# To initialize the Vue selector, use the new_project_item_vue_select view instead. --# Refer to https://gitlab.com/gitlab-org/gitlab/-/issues/374098 for more information. -- if any_projects?(@projects) - .dropdown.b-dropdown.gl-dropdown.btn-group.project-item-select-holder{ class: 'gl-display-inline-flex!' } - %a.btn.gl-button.btn-confirm.split-content-button.js-new-project-item-link.block-truncated{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] } } - = gl_loading_icon(inline: true, color: 'light') - = project_select_tag :project_path, class: "project-item-select gl-absolute! gl-visibility-hidden", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path], with_shared: local_assigns[:with_shared], include_projects_in_subgroups: local_assigns[:include_projects_in_subgroups] }, with_feature_enabled: local_assigns[:with_feature_enabled] - %button.btn.dropdown-toggle.btn-confirm.btn-md.gl-button.gl-dropdown-toggle.dropdown-toggle-split.new-project-item-select-button{ 'aria-label': _('Toggle project select') } |