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>2023-10-03 21:07:56 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-03 21:07:56 +0300
commitc470e916040edf235ea4ae9322e6969159f44606 (patch)
treef25461d1386c9be926e4d2ba46843922bb6ab69c /app/assets
parent2f08c2b7d2cd32d0791986958d3242673ff037a9 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/blob/components/blob_edit_header.vue2
-rw-r--r--app/assets/javascripts/graphql_shared/issuable_client.js10
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue1
-rw-r--r--app/assets/javascripts/lazy_loader.js2
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue6
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue167
-rw-r--r--app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js7
-rw-r--r--app/assets/javascripts/organizations/index/components/app.vue32
-rw-r--r--app/assets/javascripts/organizations/index/components/organizations_list.vue26
-rw-r--r--app/assets/javascripts/organizations/index/components/organizations_list_item.vue35
-rw-r--r--app/assets/javascripts/organizations/index/components/organizations_view.vue66
-rw-r--r--app/assets/javascripts/organizations/index/graphql/organizations.query.graphql14
-rw-r--r--app/assets/javascripts/organizations/index/index.js33
-rw-r--r--app/assets/javascripts/organizations/mock_data.js30
-rw-r--r--app/assets/javascripts/organizations/shared/graphql/resolvers.js23
-rw-r--r--app/assets/javascripts/pages/organizations/organizations/index/index.js3
-rw-r--r--app/assets/javascripts/snippets/components/edit.vue5
-rw-r--r--app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue3
-rw-r--r--app/assets/javascripts/snippets/components/snippet_blob_edit.vue4
-rw-r--r--app/assets/javascripts/snippets/components/snippet_description_edit.vue4
-rw-r--r--app/assets/javascripts/snippets/components/snippet_visibility_edit.vue2
-rw-r--r--app/assets/javascripts/super_sidebar/components/counter.vue2
-rw-r--r--app/assets/javascripts/super_sidebar/components/pinned_section.vue2
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql8
-rw-r--r--app/assets/stylesheets/page_bundles/build.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss2
26 files changed, 400 insertions, 91 deletions
diff --git a/app/assets/javascripts/blob/components/blob_edit_header.vue b/app/assets/javascripts/blob/components/blob_edit_header.vue
index 8fd3f03ff71..cd2872026c1 100644
--- a/app/assets/javascripts/blob/components/blob_edit_header.vue
+++ b/app/assets/javascripts/blob/components/blob_edit_header.vue
@@ -48,7 +48,7 @@ export default {
variant="danger"
category="secondary"
:disabled="!canDelete"
- data-qa-selector="delete_file_button"
+ data-testid="delete-file-button"
@click="$emit('delete')"
>{{ s__('Snippets|Delete file') }}</gl-button
>
diff --git a/app/assets/javascripts/graphql_shared/issuable_client.js b/app/assets/javascripts/graphql_shared/issuable_client.js
index 94385b6c214..78d58261589 100644
--- a/app/assets/javascripts/graphql_shared/issuable_client.js
+++ b/app/assets/javascripts/graphql_shared/issuable_client.js
@@ -87,14 +87,6 @@ export const config = {
const incomingWidget = incoming.find(
(w) => w.type && w.type === existingWidget.type,
);
- // We don't want to override existing notes or award emojis with empty widget on work item updates
- if (
- (incomingWidget?.type === WIDGET_TYPE_NOTES ||
- incomingWidget?.type === WIDGET_TYPE_AWARD_EMOJI) &&
- !context.variables.pageSize
- ) {
- return existingWidget;
- }
// we want to concat next page of awardEmoji to the existing ones
if (incomingWidget?.type === WIDGET_TYPE_AWARD_EMOJI && context.variables.after) {
@@ -126,7 +118,7 @@ export const config = {
};
}
- return incomingWidget || existingWidget;
+ return { ...existingWidget, ...incomingWidget };
});
},
},
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index 741845e3325..ba1258f8b50 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -176,7 +176,6 @@ export default {
type="text"
class="form-control"
data-testid="file-name-field"
- data-qa-selector="file_name_field"
:placeholder="placeholder"
/>
</form>
diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js
index 36f387205f8..4354785e585 100644
--- a/app/assets/javascripts/lazy_loader.js
+++ b/app/assets/javascripts/lazy_loader.js
@@ -170,7 +170,7 @@ export default class LazyLoader {
img.classList.remove('lazy');
img.classList.add('js-lazy-loaded');
// eslint-disable-next-line no-param-reassign
- img.dataset.qa_selector = 'js_lazy_loaded_content';
+ img.dataset.testid = 'js-lazy-loaded-content';
}
}
}
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
index 747e92b9e85..8c7460940a0 100644
--- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
+++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue
@@ -6,18 +6,12 @@ export default {
type: String,
required: true,
},
- sectionLabel: {
- type: String,
- required: false,
- default: '',
- },
},
};
</script>
<template>
<tr>
- <td class="gl-text-secondary gl-font-weight-bold">{{ sectionLabel }}</td>
<td class="gl-font-weight-bold">{{ label }}</td>
<td>
<slot></slot>
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
index a68fb7d340a..43d28e3d699 100644
--- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
+++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue
@@ -1,7 +1,9 @@
<script>
-import { GlAvatarLabeled, GlLink } from '@gitlab/ui';
+import { GlAvatarLabeled, GlLink, GlTableLite } from '@gitlab/ui';
+import { isEmpty, maxBy, range } from 'lodash';
import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue';
import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue';
+import { __, sprintf } from '~/locale';
import DetailRow from './components/candidate_detail_row.vue';
import {
@@ -22,6 +24,11 @@ import {
JOB_LABEL,
CI_USER_LABEL,
CI_MR_LABEL,
+ PERFORMANCE_LABEL,
+ NO_PARAMETERS_MESSAGE,
+ NO_METRICS_MESSAGE,
+ NO_METADATA_MESSAGE,
+ NO_CI_MESSAGE,
} from './translations';
export default {
@@ -32,6 +39,7 @@ export default {
DetailRow,
GlAvatarLabeled,
GlLink,
+ GlTableLite,
},
props: {
candidate: {
@@ -54,6 +62,14 @@ export default {
JOB_LABEL,
CI_USER_LABEL,
CI_MR_LABEL,
+ PARAMETERS_LABEL,
+ METRICS_LABEL,
+ METADATA_LABEL,
+ PERFORMANCE_LABEL,
+ NO_PARAMETERS_MESSAGE,
+ NO_METRICS_MESSAGE,
+ NO_METADATA_MESSAGE,
+ NO_CI_MESSAGE,
},
computed: {
info() {
@@ -62,21 +78,38 @@ export default {
ciJob() {
return Object.freeze(this.info.ci_job);
},
- sections() {
- return [
- {
- sectionName: PARAMETERS_LABEL,
- sectionValues: this.candidate.params,
- },
- {
- sectionName: METRICS_LABEL,
- sectionValues: this.candidate.metrics,
- },
- {
- sectionName: METADATA_LABEL,
- sectionValues: this.candidate.metadata,
- },
- ];
+ hasMetadata() {
+ return !isEmpty(this.candidate.metadata);
+ },
+ hasParameters() {
+ return !isEmpty(this.candidate.params);
+ },
+ hasMetrics() {
+ return !isEmpty(this.candidate.metrics);
+ },
+ metricsTableFields() {
+ const maxStep = maxBy(this.candidate.metrics, 'step').step;
+ const rowClass = 'gl-p-3!';
+
+ const cssClasses = { thClass: rowClass, tdClass: rowClass };
+
+ const fields = range(maxStep + 1).map((step) => ({
+ key: step.toString(),
+ label: sprintf(__('Step %{step}'), { step }),
+ ...cssClasses,
+ }));
+
+ return [{ key: 'name', label: __('Metric'), ...cssClasses }, ...fields];
+ },
+ metricsTableItems() {
+ const items = {};
+ this.candidate.metrics.forEach((metric) => {
+ const metricRow = items[metric.name] || { name: metric.name };
+ metricRow[metric.step] = metric.value;
+ items[metric.name] = metricRow;
+ });
+
+ return Object.values(items);
},
},
};
@@ -93,33 +126,37 @@ export default {
/>
</model-experiments-header>
- <table class="candidate-details gl-w-full">
- <tbody>
- <tr class="divider"></tr>
-
- <detail-row :label="$options.i18n.ID_LABEL" :section-label="$options.i18n.INFO_LABEL">
- {{ info.iid }}
- </detail-row>
+ <section class="gl-mb-6">
+ <table class="candidate-details">
+ <tbody>
+ <detail-row :label="$options.i18n.ID_LABEL">
+ {{ info.iid }}
+ </detail-row>
- <detail-row :label="$options.i18n.MLFLOW_ID_LABEL">{{ info.eid }}</detail-row>
+ <detail-row :label="$options.i18n.MLFLOW_ID_LABEL">{{ info.eid }}</detail-row>
- <detail-row :label="$options.i18n.STATUS_LABEL">{{ info.status }}</detail-row>
+ <detail-row :label="$options.i18n.STATUS_LABEL">{{ info.status }}</detail-row>
- <detail-row :label="$options.i18n.EXPERIMENT_LABEL">
- <gl-link :href="info.path_to_experiment">
- {{ info.experiment_name }}
- </gl-link>
- </detail-row>
+ <detail-row :label="$options.i18n.EXPERIMENT_LABEL">
+ <gl-link :href="info.path_to_experiment">
+ {{ info.experiment_name }}
+ </gl-link>
+ </detail-row>
- <detail-row v-if="info.path_to_artifact" :label="$options.i18n.ARTIFACTS_LABEL">
- <gl-link :href="info.path_to_artifact">
- {{ $options.i18n.ARTIFACTS_LABEL }}
- </gl-link>
- </detail-row>
+ <detail-row v-if="info.path_to_artifact" :label="$options.i18n.ARTIFACTS_LABEL">
+ <gl-link :href="info.path_to_artifact">
+ {{ $options.i18n.ARTIFACTS_LABEL }}
+ </gl-link>
+ </detail-row>
+ </tbody>
+ </table>
+ </section>
- <template v-if="ciJob">
- <tr class="divider"></tr>
+ <section class="gl-mb-6">
+ <h4>{{ $options.i18n.CI_SECTION_LABEL }}</h4>
+ <table v-if="ciJob" class="candidate-details">
+ <tbody>
<detail-row
:label="$options.i18n.JOB_LABEL"
:section-label="$options.i18n.CI_SECTION_LABEL"
@@ -142,21 +179,53 @@ export default {
!{{ ciJob.merge_request.iid }} {{ ciJob.merge_request.title }}
</gl-link>
</detail-row>
- </template>
+ </tbody>
+ </table>
- <template v-for="{ sectionName, sectionValues } in sections">
- <tr v-if="sectionValues" :key="sectionName" class="divider"></tr>
+ <div v-else class="gl-text-secondary">{{ $options.i18n.NO_CI_MESSAGE }}</div>
+ </section>
- <detail-row
- v-for="(item, index) in sectionValues"
- :key="item.name"
- :label="item.name"
- :section-label="index === 0 ? sectionName : ''"
- >
+ <section class="gl-mb-6">
+ <h4>{{ $options.i18n.PARAMETERS_LABEL }}</h4>
+
+ <table v-if="hasParameters" class="candidate-details">
+ <tbody>
+ <detail-row v-for="item in candidate.params" :key="item.name" :label="item.name">
{{ item.value }}
</detail-row>
- </template>
- </tbody>
- </table>
+ </tbody>
+ </table>
+
+ <div v-else class="gl-text-secondary">{{ $options.i18n.NO_PARAMETERS_MESSAGE }}</div>
+ </section>
+
+ <section class="gl-mb-6">
+ <h4>{{ $options.i18n.METADATA_LABEL }}</h4>
+
+ <table v-if="hasMetadata" class="candidate-details">
+ <tbody>
+ <detail-row v-for="item in candidate.metadata" :key="item.name" :label="item.name">
+ {{ item.value }}
+ </detail-row>
+ </tbody>
+ </table>
+
+ <div v-else class="gl-text-secondary">{{ $options.i18n.NO_METADATA_MESSAGE }}</div>
+ </section>
+
+ <section class="gl-mb-6">
+ <h4>{{ $options.i18n.PERFORMANCE_LABEL }}</h4>
+
+ <div v-if="hasMetrics" class="gl-overflow-x-auto">
+ <gl-table-lite
+ :items="metricsTableItems"
+ :fields="metricsTableFields"
+ class="gl-w-auto"
+ hover
+ />
+ </div>
+
+ <div v-else class="gl-text-secondary">{{ $options.i18n.NO_METRICS_MESSAGE }}</div>
+ </section>
</div>
</template>
diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js
index fa9518f3e27..98988e1db35 100644
--- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js
+++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js
@@ -9,13 +9,18 @@ export const EXPERIMENT_LABEL = s__('MlExperimentTracking|Experiment');
export const ARTIFACTS_LABEL = s__('MlExperimentTracking|Artifacts');
export const PARAMETERS_LABEL = s__('MlExperimentTracking|Parameters');
export const METRICS_LABEL = s__('MlExperimentTracking|Metrics');
+export const PERFORMANCE_LABEL = s__('MlExperimentTracking|Model performance');
export const METADATA_LABEL = s__('MlExperimentTracking|Metadata');
+export const NO_PARAMETERS_MESSAGE = s__('MlExperimentTracking|No logged parameters');
+export const NO_METRICS_MESSAGE = s__('MlExperimentTracking|No logged metrics');
+export const NO_METADATA_MESSAGE = s__('MlExperimentTracking|No logged metadata');
+export const NO_CI_MESSAGE = s__('MlExperimentTracking|Candidate not linked to a CI build');
export const DELETE_CANDIDATE_CONFIRMATION_MESSAGE = s__(
'MlExperimentTracking|Deleting this candidate will delete the associated parameters, metrics, and metadata.',
);
export const DELETE_CANDIDATE_PRIMARY_ACTION_LABEL = s__('MlExperimentTracking|Delete candidate');
export const DELETE_CANDIDATE_MODAL_TITLE = s__('MLExperimentTracking|Delete candidate?');
-export const CI_SECTION_LABEL = __('CI');
+export const CI_SECTION_LABEL = s__('MLExperimentTracking|CI Info');
export const JOB_LABEL = __('Job');
export const CI_USER_LABEL = s__('MlExperimentTracking|Triggered by');
export const CI_MR_LABEL = __('Merge request');
diff --git a/app/assets/javascripts/organizations/index/components/app.vue b/app/assets/javascripts/organizations/index/components/app.vue
new file mode 100644
index 00000000000..21a11c82196
--- /dev/null
+++ b/app/assets/javascripts/organizations/index/components/app.vue
@@ -0,0 +1,32 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
+import OrganizationsView from './organizations_view.vue';
+
+export default {
+ name: 'OrganizationsIndexApp',
+ i18n: {
+ organizations: __('Organizations'),
+ newOrganization: s__('Organization|New organization'),
+ },
+ components: {
+ GlButton,
+ OrganizationsView,
+ },
+ inject: ['newOrganizationUrl'],
+};
+</script>
+
+<template>
+ <section>
+ <div class="gl-display-flex gl-align-items-center">
+ <h1 class="gl-my-4 gl-font-size-h-display">{{ $options.i18n.organizations }}</h1>
+ <div class="gl-ml-auto">
+ <gl-button :href="newOrganizationUrl" variant="confirm">{{
+ $options.i18n.newOrganization
+ }}</gl-button>
+ </div>
+ </div>
+ <organizations-view />
+ </section>
+</template>
diff --git a/app/assets/javascripts/organizations/index/components/organizations_list.vue b/app/assets/javascripts/organizations/index/components/organizations_list.vue
new file mode 100644
index 00000000000..539a4fcfe29
--- /dev/null
+++ b/app/assets/javascripts/organizations/index/components/organizations_list.vue
@@ -0,0 +1,26 @@
+<script>
+import OrganizationsListItem from './organizations_list_item.vue';
+
+export default {
+ name: 'OrganizationsList',
+ components: {
+ OrganizationsListItem,
+ },
+ props: {
+ organizations: {
+ type: Array,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <ul class="gl-p-0 gl-list-style-none">
+ <organizations-list-item
+ v-for="organization in organizations"
+ :key="organization.id"
+ :organization="organization"
+ />
+ </ul>
+</template>
diff --git a/app/assets/javascripts/organizations/index/components/organizations_list_item.vue b/app/assets/javascripts/organizations/index/components/organizations_list_item.vue
new file mode 100644
index 00000000000..99fa41e3af8
--- /dev/null
+++ b/app/assets/javascripts/organizations/index/components/organizations_list_item.vue
@@ -0,0 +1,35 @@
+<script>
+import { GlAvatarLabeled } from '@gitlab/ui';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+
+export default {
+ name: 'OrganizationsListItem',
+ components: {
+ GlAvatarLabeled,
+ },
+ props: {
+ organization: {
+ type: Object,
+ required: true,
+ },
+ },
+ avatarSize: { default: 32, md: 48 },
+ getIdFromGraphQLId,
+};
+</script>
+
+<template>
+ <li class="gl-py-3 gl-border-b gl-display-flex gl-align-items-flex-start">
+ <gl-avatar-labeled
+ :size="$options.avatarSize"
+ :src="organization.avatarUrl"
+ :entity-id="$options.getIdFromGraphQLId(organization.id)"
+ :entity-name="organization.name"
+ :label="organization.name"
+ :label-link="organization.webUrl"
+ shape="rect"
+ >
+ <span class="gl-mt-2 gl-text-gray-500">{{ organization.description }}</span>
+ </gl-avatar-labeled>
+ </li>
+</template>
diff --git a/app/assets/javascripts/organizations/index/components/organizations_view.vue b/app/assets/javascripts/organizations/index/components/organizations_view.vue
new file mode 100644
index 00000000000..51aff482c8a
--- /dev/null
+++ b/app/assets/javascripts/organizations/index/components/organizations_view.vue
@@ -0,0 +1,66 @@
+<script>
+import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
+import { createAlert } from '~/alert';
+import { s__ } from '~/locale';
+import organizationsQuery from '../graphql/organizations.query.graphql';
+import OrganizationsList from './organizations_list.vue';
+
+export default {
+ name: 'OrganizationsView',
+ i18n: {
+ errorMessage: s__(
+ 'Organization|An error occurred loading user organizations. Please refresh the page to try again.',
+ ),
+ emptyStateTitle: s__('Organization|Get started with organizations'),
+ emptyStateDescription: s__(
+ 'Organization|Create an organization to contain all of your groups and projects.',
+ ),
+ emptyStateButtonText: s__('Organization|New organization'),
+ },
+ components: {
+ GlLoadingIcon,
+ OrganizationsList,
+ GlEmptyState,
+ },
+ inject: ['newOrganizationUrl', 'organizationsEmptyStateSvgPath'],
+ data() {
+ return {
+ organizations: [],
+ };
+ },
+ apollo: {
+ organizations: {
+ query: organizationsQuery,
+ update(data) {
+ return data.currentUser.organizations.nodes;
+ },
+ error(error) {
+ createAlert({ message: this.$options.i18n.errorMessage, error, captureError: true });
+ },
+ },
+ },
+ computed: {
+ isLoading() {
+ return this.$apollo.queries.organizations.loading;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-loading-icon v-if="isLoading" class="gl-mt-5" size="md" />
+ <organizations-list
+ v-else-if="organizations.length"
+ :organizations="organizations"
+ class="gl-border-t"
+ />
+ <gl-empty-state
+ v-else
+ :svg-height="144"
+ :svg-path="organizationsEmptyStateSvgPath"
+ :title="$options.i18n.emptyStateTitle"
+ :description="$options.i18n.emptyStateDescription"
+ :primary-button-link="newOrganizationUrl"
+ :primary-button-text="$options.i18n.emptyStateButtonText"
+ />
+</template>
diff --git a/app/assets/javascripts/organizations/index/graphql/organizations.query.graphql b/app/assets/javascripts/organizations/index/graphql/organizations.query.graphql
new file mode 100644
index 00000000000..560cd9dd578
--- /dev/null
+++ b/app/assets/javascripts/organizations/index/graphql/organizations.query.graphql
@@ -0,0 +1,14 @@
+query getCurrentUserOrganizations {
+ currentUser {
+ id
+ organizations @client {
+ nodes {
+ id
+ name
+ description
+ avatarUrl
+ webUrl
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/organizations/index/index.js b/app/assets/javascripts/organizations/index/index.js
new file mode 100644
index 00000000000..7cbb9c9165d
--- /dev/null
+++ b/app/assets/javascripts/organizations/index/index.js
@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import resolvers from '../shared/graphql/resolvers';
+import OrganizationsIndexApp from './components/app.vue';
+
+export const initOrganizationsIndex = () => {
+ const el = document.getElementById('js-organizations-index');
+
+ if (!el) return false;
+
+ const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(resolvers),
+ });
+
+ const { newOrganizationUrl, organizationsEmptyStateSvgPath } = convertObjectPropsToCamelCase(
+ el.dataset,
+ );
+
+ return new Vue({
+ el,
+ name: 'OrganizationsIndexRoot',
+ apolloProvider,
+ provide: {
+ newOrganizationUrl,
+ organizationsEmptyStateSvgPath,
+ },
+ render(createElement) {
+ return createElement(OrganizationsIndexApp);
+ },
+ });
+};
diff --git a/app/assets/javascripts/organizations/mock_data.js b/app/assets/javascripts/organizations/mock_data.js
index 17ab7bd1d34..60c446900c9 100644
--- a/app/assets/javascripts/organizations/mock_data.js
+++ b/app/assets/javascripts/organizations/mock_data.js
@@ -4,10 +4,32 @@
// https://gitlab.com/gitlab-org/gitlab/-/issues/420777
// https://gitlab.com/gitlab-org/gitlab/-/issues/421441
-export const organization = {
- id: 'gid://gitlab/Organization/1',
- __typename: 'Organization',
-};
+export const organizations = [
+ {
+ id: 'gid://gitlab/Organization/1',
+ name: 'My First Organization',
+ description: 'This is where an organization can be explained in detail',
+ avatarUrl: null,
+ webUrl: null,
+ __typename: 'Organization',
+ },
+ {
+ id: 'gid://gitlab/Organization/2',
+ name: 'Vegetation Co.',
+ description: 'Lorem ipsum dolor sit amet',
+ avatarUrl: null,
+ webUrl: null,
+ __typename: 'Organization',
+ },
+ {
+ id: 'gid://gitlab/Organization/3',
+ name: 'Dude where is my car?',
+ description: 'Bacon ipsum dolor amet short ribs',
+ avatarUrl: null,
+ webUrl: null,
+ __typename: 'Organization',
+ },
+];
export const organizationProjects = {
nodes: [
diff --git a/app/assets/javascripts/organizations/shared/graphql/resolvers.js b/app/assets/javascripts/organizations/shared/graphql/resolvers.js
index c78266b0476..318b41647bc 100644
--- a/app/assets/javascripts/organizations/shared/graphql/resolvers.js
+++ b/app/assets/javascripts/organizations/shared/graphql/resolvers.js
@@ -1,18 +1,31 @@
-import { organization, organizationProjects, organizationGroups } from '../../mock_data';
+import { organizations, organizationProjects, organizationGroups } from '../../mock_data';
+
+const simulateLoading = () => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, 1000);
+ });
+};
export default {
Query: {
organization: async () => {
// Simulate API loading
- await new Promise((resolve) => {
- setTimeout(resolve, 1000);
- });
+ await simulateLoading();
return {
- ...organization,
+ ...organizations[0],
projects: organizationProjects,
groups: organizationGroups,
};
},
},
+ UserCore: {
+ organizations: async () => {
+ await simulateLoading();
+
+ return {
+ nodes: organizations,
+ };
+ },
+ },
};
diff --git a/app/assets/javascripts/pages/organizations/organizations/index/index.js b/app/assets/javascripts/pages/organizations/organizations/index/index.js
new file mode 100644
index 00000000000..c7e087b81c6
--- /dev/null
+++ b/app/assets/javascripts/pages/organizations/organizations/index/index.js
@@ -0,0 +1,3 @@
+import { initOrganizationsIndex } from '~/organizations/index';
+
+initOrganizationsIndex();
diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue
index 9e80210de51..aa3f33989c8 100644
--- a/app/assets/javascripts/snippets/components/edit.vue
+++ b/app/assets/javascripts/snippets/components/edit.vue
@@ -232,8 +232,7 @@ export default {
<gl-form-input
id="snippet-title"
v-model="snippet.title"
- data-testid="snippet-title-input"
- data-qa-selector="snippet_title_field"
+ data-testid="snippet-title-input-field"
:autofocus="true"
/>
</gl-form-group>
@@ -261,7 +260,7 @@ export default {
category="primary"
type="submit"
variant="confirm"
- data-qa-selector="submit_button"
+ data-testid="submit-button"
:disabled="isUpdating"
>{{ saveButtonLabel }}</gl-button
>
diff --git a/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue
index 59f7c8d8d97..ca1d9f858a5 100644
--- a/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue
@@ -157,10 +157,9 @@ export default {
</gl-form-group>
<gl-button
:disabled="!canAdd"
- data-testid="add_button"
+ data-testid="add-button"
class="gl-my-3"
variant="dashed"
- data-qa-selector="add_file_button"
@click="addBlob"
>{{ addLabel }}</gl-button
>
diff --git a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue
index 021bd23781e..9b0a1db23f2 100644
--- a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue
@@ -69,11 +69,11 @@ export default {
};
</script>
<template>
- <div class="file-holder snippet" data-qa-selector="file_holder_container">
+ <div class="file-holder snippet" data-testid="file-holder-container">
<blob-header-edit
:id="inputId"
:value="blob.path"
- data-qa-selector="file_name_field"
+ data-testid="file-name-field"
:can-delete="canDelete"
:show-delete="showDelete"
@input="notifyAboutUpdates({ path: $event })"
diff --git a/app/assets/javascripts/snippets/components/snippet_description_edit.vue b/app/assets/javascripts/snippets/components/snippet_description_edit.vue
index 3ce7ea231ff..93d52890675 100644
--- a/app/assets/javascripts/snippets/components/snippet_description_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_description_edit.vue
@@ -36,7 +36,7 @@ export default {
<gl-form-input
class="form-control"
:placeholder="s__('Snippets|Describe what your snippet does or how to use it…')"
- data-qa-selector="description_placeholder"
+ data-testid="description-placeholder"
/>
</div>
<markdown-field
@@ -54,7 +54,7 @@ export default {
:value="value"
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
- data-qa-selector="snippet_description_field"
+ data-testid="snippet-description-field"
data-supports-quick-actions="false"
:aria-label="__('Description')"
:placeholder="__('Write a comment or drag your files here…')"
diff --git a/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue b/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue
index 24dd978585c..37d10cffc78 100644
--- a/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue
@@ -57,7 +57,7 @@ export default {
<gl-icon :size="16" :name="option.icon" />
<span
class="font-weight-bold ml-1 js-visibility-option"
- data-qa-selector="visibility_content"
+ data-testid="visibility-content"
:data-qa-visibility="option.label"
>{{ option.label }}</span
>
diff --git a/app/assets/javascripts/super_sidebar/components/counter.vue b/app/assets/javascripts/super_sidebar/components/counter.vue
index c0e1959fba4..49efc5ab5b9 100644
--- a/app/assets/javascripts/super_sidebar/components/counter.vue
+++ b/app/assets/javascripts/super_sidebar/components/counter.vue
@@ -15,7 +15,7 @@ export default {
href: {
type: String,
required: false,
- default: '',
+ default: null,
},
icon: {
type: String,
diff --git a/app/assets/javascripts/super_sidebar/components/pinned_section.vue b/app/assets/javascripts/super_sidebar/components/pinned_section.vue
index 5da45b52bf4..ea3e9e9df1f 100644
--- a/app/assets/javascripts/super_sidebar/components/pinned_section.vue
+++ b/app/assets/javascripts/super_sidebar/components/pinned_section.vue
@@ -102,7 +102,7 @@ export default {
<draggable
v-if="items.length > 0"
v-model="draggableItems"
- class="gl-p-0 gl-m-0"
+ class="gl-p-0 gl-m-0 gl-list-style-none"
data-testid="pinned-nav-items"
handle=".js-draggable-icon"
tag="ul"
diff --git a/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql
index f303a797e9c..d15e3086560 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql
@@ -52,4 +52,12 @@ fragment WorkItemMetadataWidgets on WorkItemWidget {
... on WorkItemWidgetAwardEmoji {
type
}
+
+ ... on WorkItemWidgetLinkedItems {
+ type
+ }
+
+ ... on WorkItemWidgetHierarchy {
+ type
+ }
}
diff --git a/app/assets/stylesheets/page_bundles/build.scss b/app/assets/stylesheets/page_bundles/build.scss
index 24bd8b0fd7f..16fc0e7ebae 100644
--- a/app/assets/stylesheets/page_bundles/build.scss
+++ b/app/assets/stylesheets/page_bundles/build.scss
@@ -102,7 +102,7 @@
.sidebar-container {
@include gl-sticky;
top: #{$top-bar-height - 1px};
- max-height: calc(100vh - #{$top-bar-height - 1px});
+ max-height: calc(100vh - #{$top-bar-height - 1px} - var(--performance-bar-height));
overflow-y: scroll;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
diff --git a/app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss b/app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss
index d6f71b12cd9..685719071b5 100644
--- a/app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss
+++ b/app/assets/stylesheets/page_bundles/ml_experiment_tracking.scss
@@ -15,6 +15,6 @@ table.ml-candidate-table {
table.candidate-details {
td {
- padding: $gl-spacing-scale-3;
+ padding: $gl-spacing-scale-3 $gl-spacing-scale-3 $gl-spacing-scale-3 0;
}
}