diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-03 21:07:56 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-03 21:07:56 +0300 |
commit | c470e916040edf235ea4ae9322e6969159f44606 (patch) | |
tree | f25461d1386c9be926e4d2ba46843922bb6ab69c /app/assets | |
parent | 2f08c2b7d2cd32d0791986958d3242673ff037a9 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
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; } } |