Commit 71263ab9 authored by Kerri Miller's avatar Kerri Miller

Merge branch 'master' into 'issue_324629_metadatable'

# Conflicts:
#   .rubocop_todo/gitlab/delegate_predicate_methods.yml
parents 518700a1 23230163
......@@ -53,7 +53,7 @@ docs-lint markdown:
docs-lint links:
extends:
- .docs:rules:docs-lint
image: registry.gitlab.com/gitlab-org/gitlab-docs/lint-html:alpine-3.14-ruby-2.7.4-0fae0f62
image: registry.gitlab.com/gitlab-org/gitlab-docs/lint-html:alpine-3.14-ruby-2.7.5-08847baa
stage: lint
needs: []
script:
......
......@@ -342,8 +342,8 @@ rspec fast_spec_helper minimal:
db:rollback:
extends: .db-job-base
script:
- bundle exec rake db:migrate:main VERSION=20181228175414
- bundle exec rake db:migrate:main SKIP_SCHEMA_VERSION_CHECK=true
- scripts/db_migrate VERSION=20181228175414
- scripts/db_migrate SKIP_SCHEMA_VERSION_CHECK=true
db:migrate:reset:
extends: .db-job-base
......@@ -368,7 +368,7 @@ db:migrate-from-previous-major-version:
- git checkout -f $CI_COMMIT_SHA
- SETUP_DB=false USE_BUNDLE_INSTALL=true bash scripts/prepare_build.sh
script:
- run_timed_command "bundle exec rake db:migrate:main"
- run_timed_command "scripts/db_migrate"
db:check-schema:
extends:
......@@ -377,7 +377,7 @@ db:check-schema:
variables:
TAG_TO_CHECKOUT: "v14.4.0"
script:
- run_timed_command "bundle exec rake db:migrate:main"
- run_timed_command "scripts/db_migrate"
- scripts/schema_changed.sh
- scripts/validate_migration_timestamps
......
......@@ -98,7 +98,7 @@ gemnasium-python-dependency_scanning:
yarn-audit-dependency_scanning:
extends: .ds-analyzer
image: "registry.gitlab.com/gitlab-org/security-products/analyzers/npm-audit:1.4.0"
image: "registry.gitlab.com/gitlab-org/security-products/analyzers/npm-audit:1.4.1"
variables:
TOOL: yarn
rules: !reference [".reports:rules:yarn-audit-dependency_scanning", rules]
......
......@@ -19,6 +19,7 @@ tasks:
# ensure gdk.yml has correct instance settings
gdk config set gitlab.rails.port 443
gdk config set gitlab.rails.https.enabled true
gdk config set webpack.host 127.0.0.1
# make documentation builds available
gdk config set gitlab_docs.enabled true
# reconfigure GDK
......@@ -47,6 +48,7 @@ tasks:
gdk config set gitlab.rails.hostname $(gp url 3000 | sed -e 's+^http[s]*://++')
gdk config set gitlab.rails.port 443
gdk config set gitlab.rails.https.enabled true
gdk config set webpack.host 127.0.0.1
# reconfigure GDK
echo "$(date) – Reconfiguring GDK" | tee -a /workspace/startup.log
gdk reconfigure
......
......@@ -2,13 +2,9 @@
Gitlab/DelegatePredicateMethods:
Exclude:
- app/models/clusters/cluster.rb
- app/models/clusters/platforms/kubernetes.rb
- app/models/concerns/diff_positionable_note.rb
- app/models/concerns/integrations/base_data_fields.rb
- app/models/concerns/resolvable_discussion.rb
- app/models/project.rb
- ee/app/models/concerns/ee/ci/metadatable.rb
- ee/app/models/ee/group.rb
- ee/app/models/ee/namespace.rb
- ee/app/models/license.rb
- lib/gitlab/ci/trace/stream.rb
......@@ -3,9 +3,6 @@ GraphQL/OrderedArguments:
Exclude:
- app/graphql/mutations/jira_import/start.rb
- app/graphql/mutations/merge_requests/accept.rb
- app/graphql/resolvers/base_issues_resolver.rb
- app/graphql/resolvers/design_management/designs_resolver.rb
- app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
- app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb
- app/graphql/resolvers/design_management/version_in_collection_resolver.rb
- app/graphql/resolvers/group_milestones_resolver.rb
......
......@@ -10,7 +10,6 @@ Performance/ActiveRecordSubtransactionMethods:
- app/models/design_management/design_collection.rb
- app/models/error_tracking/error.rb
- app/models/external_pull_request.rb
- app/models/gpg_signature.rb
- app/models/merge_request.rb
- app/models/plan.rb
- app/models/project.rb
......@@ -18,6 +17,7 @@ Performance/ActiveRecordSubtransactionMethods:
- app/models/x509_certificate.rb
- app/models/x509_commit_signature.rb
- app/models/x509_issuer.rb
- app/models/concerns/commit_signature.rb
- app/services/bulk_imports/relation_export_service.rb
- app/services/ci/update_build_state_service.rb
- app/services/event_create_service.rb
......
......@@ -3,9 +3,6 @@ Rails/SaveBang:
Exclude:
- ee/spec/initializers/fog_google_https_private_urls_spec.rb
- ee/spec/lib/analytics/merge_request_metrics_calculator_spec.rb
- ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb
- ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb
- ee/spec/lib/ee/gitlab/ci/pipeline/quota/activity_spec.rb
- ee/spec/lib/gitlab/auth/ldap/access_spec.rb
- ee/spec/lib/gitlab/auth/o_auth/user_spec.rb
- ee/spec/lib/gitlab/auth/saml/user_spec.rb
......
f9af7fbcbfda556c61dcbb2280cda6c6e210cb77
896612200106b19068dfdb4f8f67de0dd4807c0a
......@@ -194,7 +194,7 @@ end
gem 'state_machines-activerecord', '~> 0.8.0'
# Issue tags
gem 'acts-as-taggable-on', '~> 7.0'
gem 'acts-as-taggable-on', '~> 8.1'
# Background jobs
gem 'sidekiq', '~> 6.3'
......
......@@ -73,7 +73,7 @@ GEM
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
acts-as-taggable-on (7.0.0)
acts-as-taggable-on (8.1.0)
activerecord (>= 5.0, < 6.2)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
......@@ -1390,7 +1390,7 @@ DEPENDENCIES
RedCloth (~> 4.3.2)
acme-client (~> 2.0, >= 2.0.6)
activerecord-explain-analyze (~> 0.1)
acts-as-taggable-on (~> 7.0)
acts-as-taggable-on (~> 8.1)
addressable (~> 2.8)
akismet (~> 3.0)
apollo_upload_server (~> 2.1.0)
......
......@@ -76,7 +76,7 @@ export default {
},
},
safeHtmlConfig: {
ADD_TAGS: ['use', 'gl-emoji'],
ADD_TAGS: ['use', 'gl-emoji', 'copy-code'],
},
};
</script>
......
import { uniqueId } from 'lodash';
import { __ } from '~/locale';
import { spriteIcon } from '~/lib/utils/common_utils';
import { setAttributes } from '~/lib/utils/dom_utils';
class CopyCodeButton extends HTMLElement {
connectedCallback() {
this.for = uniqueId('code-');
this.parentNode.querySelector('pre').setAttribute('id', this.for);
this.appendChild(this.createButton());
}
createButton() {
const button = document.createElement('button');
setAttributes(button, {
type: 'button',
class: 'btn btn-default btn-md gl-button btn-icon has-tooltip',
'data-title': __('Copy to clipboard'),
'data-clipboard-target': `pre#${this.for}`,
});
button.innerHTML = spriteIcon('copy-to-clipboard');
return button;
}
}
function addCodeButton() {
[...document.querySelectorAll('pre.code.js-syntax-highlight')]
.filter((el) => !el.closest('.js-markdown-code'))
.forEach((el) => {
const copyCodeEl = document.createElement('copy-code');
copyCodeEl.setAttribute('for', uniqueId('code-'));
const wrapper = document.createElement('div');
wrapper.className = 'gl-relative markdown-code-block js-markdown-code';
wrapper.appendChild(el.cloneNode(true));
wrapper.appendChild(copyCodeEl);
el.parentNode.insertBefore(wrapper, el);
el.remove();
});
}
export const initCopyCodeButton = (selector = '#content-body') => {
if (!customElements.get('copy-code')) {
customElements.define('copy-code', CopyCodeButton);
}
const el = document.querySelector(selector);
if (!el) return () => {};
const observer = new MutationObserver(() => addCodeButton());
observer.observe(document.querySelector(selector), {
childList: true,
subtree: true,
});
return () => observer.disconnect();
};
......@@ -6,9 +6,9 @@ import { sortableStart, sortableEnd } from '~/boards/mixins/sortable_default_opt
import { sprintf, __ } from '~/locale';
import defaultSortableConfig from '~/sortable/sortable_config';
import Tracking from '~/tracking';
import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
import { toggleFormEventPrefix, DraggableItemTypes } from '../constants';
import eventHub from '../eventhub';
import listQuery from '../graphql/board_lists_deferred.query.graphql';
import BoardCard from './board_card.vue';
import BoardNewIssue from './board_new_issue.vue';
......
......@@ -17,10 +17,10 @@ import sidebarEventHub from '~/sidebar/event_hub';
import Tracking from '~/tracking';
import { formatDate } from '~/lib/utils/datetime_utility';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
import AccessorUtilities from '../../lib/utils/accessor';
import { inactiveId, LIST, ListType, toggleFormEventPrefix } from '../constants';
import eventHub from '../eventhub';
import listQuery from '../graphql/board_lists_deferred.query.graphql';
import ItemCount from './item_count.vue';
export default {
......
<script>
import { GlTable, GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
import { GlTable, GlButton, GlModalDirective, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import { s__, __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
......@@ -59,6 +59,7 @@ export default {
},
directives: {
GlModalDirective,
GlTooltip: GlTooltipDirective,
},
mixins: [glFeatureFlagsMixin()],
computed: {
......@@ -102,27 +103,38 @@ export default {
<col v-for="field in scope.fields" :key="field.key" :style="field.customStyle" />
</template>
<template #cell(key)="{ item }">
<div class="d-flex truncated-container">
<span :id="`ci-variable-key-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
item.key
}}</span>
<ci-variable-popover
:target="`ci-variable-key-${item.id}`"
:value="item.key"
:tooltip-text="__('Copy key')"
<div class="gl-display-flex truncated-container gl-align-items-center">
<span
:id="`ci-variable-key-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
>{{ item.key }}</span
>
<gl-button
v-gl-tooltip
category="tertiary"
icon="copy-to-clipboard"
:title="__('Copy key')"
:data-clipboard-text="item.key"
:aria-label="__('Copy to clipboard')"
/>
</div>
</template>
<template #cell(value)="{ item }">
<span v-if="valuesHidden">*********************</span>
<div v-else class="d-flex truncated-container">
<span :id="`ci-variable-value-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
item.value
}}</span>
<ci-variable-popover
:target="`ci-variable-value-${item.id}`"
:value="item.value"
:tooltip-text="__('Copy value')"
<div class="gl-display-flex gl-align-items-center truncated-container">
<span v-if="valuesHidden">*********************</span>
<span
v-else
:id="`ci-variable-value-${item.id}`"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
>{{ item.value }}</span
>
<gl-button
v-gl-tooltip
category="tertiary"
icon="copy-to-clipboard"
:title="__('Copy value')"
:data-clipboard-text="item.value"
:aria-label="__('Copy to clipboard')"
/>
</div>
</template>
......
......@@ -17,7 +17,7 @@ export default CodeBlockLowlight.extend({
};
},
renderHTML({ HTMLAttributes }) {
return ['pre', HTMLAttributes, ['code', {}, 0]];
return ['div', ['pre', HTMLAttributes, ['code', {}, 0]]];
},
}).configure({
lowlight,
......
import { Node } from '@tiptap/core';
import { PARSE_HTML_PRIORITY_LOWEST } from '../constants';
const getDiv = (element) => {
if (element.nodeName === 'DIV') return element;
return element.querySelector('div');
};
export default Node.create({
name: 'division',
content: 'block*',
group: 'block',
defining: true,
addAttributes() {
return {
className: {
default: null,
parseHTML: (element) => getDiv(element).className || null,
},
};
},
parseHTML() {
return [{ tag: 'div', priority: PARSE_HTML_PRIORITY_LOWEST }];
},
......
......@@ -138,7 +138,16 @@ const defaultSerializerConfig = {
state.write('```');
state.closeBlock(node);
},
[Division.name]: renderHTMLNode('div'),
[Division.name]: (state, node) => {
if (node.attrs.className?.includes('js-markdown-code')) {
state.renderInline(node);
} else {
const newNode = node;
delete newNode.attrs.className;
renderHTMLNode('div')(state, newNode);
}
},
[DescriptionList.name]: renderHTMLNode('dl', true),
[DescriptionItem.name]: (state, node, parent, index) => {
if (index === 1) state.ensureNewLine();
......
......@@ -7,6 +7,7 @@ import { escape } from 'lodash';
import csrf from '~/lib/utils/csrf';
import { __, s__, sprintf } from '~/locale';
import rollbackEnvironment from '../graphql/mutations/rollback_environment.mutation.graphql';
import eventHub from '../event_hub';
export default {
......@@ -40,10 +41,15 @@ export default {
required: false,
default: null,
},
graphql: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
modalTitle() {
const title = this.environment.isLastDeployment
const title = this.isLastDeployment
? s__('Environments|Re-deploy environment %{name}?')
: s__('Environments|Rollback environment %{name}?');
......@@ -53,6 +59,11 @@ export default {
},
commitShortSha() {
if (this.hasMultipleCommits) {
if (this.graphql) {
const { lastDeployment } = this.environment;
return this.commitData(lastDeployment, 'shortId');
}
const { last_deployment } = this.environment;
return this.commitData(last_deployment, 'short_id');
}
......@@ -61,6 +72,11 @@ export default {
},
commitUrl() {
if (this.hasMultipleCommits) {
if (this.graphql) {
const { lastDeployment } = this.environment;
return this.commitData(lastDeployment, 'commitPath');
}
const { last_deployment } = this.environment;
return this.commitData(last_deployment, 'commit_path');
}
......@@ -68,9 +84,7 @@ export default {
return this.environment.commitUrl;
},
modalActionText() {
return this.environment.isLastDeployment
? s__('Environments|Re-deploy')
: s__('Environments|Rollback');
return this.isLastDeployment ? s__('Environments|Re-deploy') : s__('Environments|Rollback');
},
primaryProps() {
let attributes = [{ variant: 'danger' }];
......@@ -84,20 +98,27 @@ export default {
attributes,
};
},
isLastDeployment() {
// eslint-disable-next-line @gitlab/require-i18n-strings
return this.environment?.isLastDeployment || this.environment?.lastDeployment?.['last?'];
},
},
methods: {
handleChange(event) {
this.$emit('change', event);
},
onOk() {
eventHub.$emit('rollbackEnvironment', this.environment);
if (this.graphql) {
this.$apollo.mutate({
mutation: rollbackEnvironment,
variables: { environment: this.environment },
});
} else {
eventHub.$emit('rollbackEnvironment', this.environment);
}
},
commitData(lastDeployment, key) {
if (lastDeployment && lastDeployment.commit) {
return lastDeployment.commit[key];
}
return '';
return lastDeployment?.commit?.[key] ?? '';
},
},
csrf,
......
......@@ -8,6 +8,7 @@
import { GlModalDirective, GlDropdownItem } from '@gitlab/ui';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
import setEnvironmentToRollback from '../graphql/mutations/set_environment_to_rollback.mutation.graphql';
export default {
components: {
......@@ -32,11 +33,12 @@ export default {
type: String,
required: true,
},
},
data() {
return {
isLoading: false,
};
graphql: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
......@@ -49,16 +51,18 @@ export default {
methods: {
onClick() {
eventHub.$emit('requestRollbackEnvironment', {
...this.environment,
retryUrl: this.retryUrl,
isLastDeployment: this.isLastDeployment,
});
eventHub.$on('rollbackEnvironment', (environment) => {
if (environment.id === this.environment.id) {
this.isLoading = true;
}
});
if (this.graphql) {
this.$apollo.mutate({
mutation: setEnvironmentToRollback,
variables: { environment: this.environment },
});
} else {
eventHub.$emit('requestRollbackEnvironment', {
...this.environment,
retryUrl: this.retryUrl,
isLastDeployment: this.isLastDeployment,
});
}
},
},
};
......
mutation SetEnvironmentToRollback($environment: Environment) {
setEnvironmentToRollback(environment: $environment) @client
}
query environmentToRollback {
environmentToRollback @client {
id
name
lastDeployment
}
}
......@@ -2,6 +2,7 @@ import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import pollIntervalQuery from './queries/poll_interval.query.graphql';
import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql';
const buildErrors = (errors = []) => ({
errors,
......@@ -84,6 +85,12 @@ export const resolvers = (endpoint) => ({
]);
});
},
setEnvironmentToRollback(_, { environment }, { client }) {
client.writeQuery({
query: environmentToRollbackQuery,
data: { environmentToRollback: environment },
});
},
cancelAutoStop(_, { environment: { autoStopPath } }) {
return axios
.post(autoStopPath)
......
......@@ -9,12 +9,29 @@ type LocalEnvironment {
autoStopPath: String
}
input LocalEnvironmentInput {
id: Int!
globalId: ID!
name: String!
folderPath: String
stopPath: String
deletePath: String
retryUrl: String
autoStopPath: String
}
type NestedLocalEnvironment {
name: String!
size: Int!
latest: LocalEnvironment!
}
input NestedLocalEnvironmentInput {
name: String!
size: Int!
latest: LocalEnvironmentInput!
}
type LocalEnvironmentFolder {
environments: [LocalEnvironment!]!
availableCount: Int!
......@@ -40,13 +57,15 @@ type LocalErrors {
extend type Query {
environmentApp: LocalEnvironmentApp
folder(environment: NestedLocalEnvironment): LocalEnvironmentFolder
folder(environment: NestedLocalEnvironmentInput): LocalEnvironmentFolder
environmentToRollback: LocalEnvironment
isLastDeployment: Boolean
}
extend type Mutation {
stopEnvironment(environment: LocalEnvironment): LocalErrors
deleteEnvironment(environment: LocalEnvironment): LocalErrors
rollbackEnvironment(environment: LocalEnvironment): LocalErrors
cancelAutoStop(environment: LocalEnvironment): LocalErrors
stopEnvironment(environment: LocalEnvironmentInput): LocalErrors
deleteEnvironment(environment: LocalEnvironmentInput): LocalErrors
rollbackEnvironment(environment: LocalEnvironmentInput): LocalErrors
cancelAutoStop(environment: LocalEnvironmentInput): LocalErrors
setEnvironmentToRollback(environment: LocalEnvironmentInput): LocalErrors
}
import { omitBy, isNil } from 'lodash';
import { objectToQuery } from '~/lib/utils/url_utility';
import {
......@@ -12,23 +13,29 @@ import {
} from '../constants';
export const searchQuery = (state) => {
const query = {
search: state.search,
nav_source: 'navbar',
project_id: state.searchContext.project?.id,
group_id: state.searchContext.group?.id,
scope: state.searchContext.scope,
};
const query = omitBy(
{
search: state.search,
nav_source: 'navbar',
project_id: state.searchContext.project?.id,
group_id: state.searchContext.group?.id,
scope: state.searchContext?.scope,
},
isNil,
);
return `${state.searchPath}?${objectToQuery(query)}`;
};
export const autocompleteQuery = (state) => {
const query = {
term: state.search,
project_id: state.searchContext.project?.id,
project_ref: state.searchContext.ref,
};
const query = omitBy(
{
term: state.search,
project_id: state.searchContext.project?.id,
project_ref: state.searchContext?.ref,
},
isNil,
);
return `${state.autocompletePath}?${objectToQuery(query)}`;
};
......@@ -82,42 +89,43 @@ export const defaultSearchOptions = (state, getters) => {
};
export const projectUrl = (state) => {
if (!state.searchContext.project || !state.searchContext.group) {
return null;
}
const query = {
search: state.search,
nav_source: 'navbar',
project_id: state.searchContext.project.id,
group_id: state.searchContext.group.id,
scope: state.searchContext.scope,
};
const query = omitBy(
{
search: state.search,
nav_source: 'navbar',
project_id: state.searchContext?.project?.id,
group_id: state.searchContext?.group?.id,
scope: state.searchContext?.scope,
},
isNil,
);
return `${state.searchPath}?${objectToQuery(query)}`;
};
export const groupUrl = (state) => {
if (!state.searchContext.group) {
return null;
}
const query = {
search: state.search,
nav_source: 'navbar',
group_id: state.searchContext.group.id,
scope: state.searchContext.scope,
};
const query = omitBy(
{
search: state.search,
nav_source: 'navbar',
group_id: state.searchContext?.group?.id,
scope: state.searchContext?.scope,
},
isNil,
);
return `${state.searchPath}?${objectToQuery(query)}`;
};
export const allUrl = (state) => {
const query = {
search: state.search,
nav_source: 'navbar',
scope: state.searchContext.scope,
};
const query = omitBy(
{
search: state.search,
nav_source: 'navbar',
scope: state.searchContext?.scope,
},
isNil,
);
return `${state.searchPath}?${objectToQuery(query)}`;
};
......
......@@ -117,7 +117,11 @@ export default {
</p>
<template v-if="showJiraIssuesIntegration">
<input name="service[issues_enabled]" type="hidden" :value="enableJiraIssues || false" />
<gl-form-checkbox v-model="enableJiraIssues" :disabled="isInheriting">
<gl-form-checkbox
v-model="enableJiraIssues"
:disabled="isInheriting"
data-qa-selector="service_jira_issues_enabled_checkbox"
>
{{ $options.i18n.enableCheckboxLabel }}
<template #help>
{{ $options.i18n.enableCheckboxHelp }}
......@@ -162,6 +166,7 @@ export default {
id="service_project_key"
v-model="projectKey"
name="service[project_key]"
data-qa-selector="service_jira_project_key_field"
:placeholder="$options.i18n.projectKeyPlaceholder"
:required="enableJiraIssues"
:state="validProjectKey"
......
import { __ } from '~/locale';
export const IssuableStatus = {
Closed: 'closed',
Open: 'opened',
Reopened: 'reopened',
};
export const IssuableStatusText = {
[IssuableStatus.Closed]: __('Closed'),
[IssuableStatus.Open]: __('Open'),
[IssuableStatus.Reopened]: __('Open'),
};
export const IssuableType = {
Issue: 'issue',
Epic: 'epic',
MergeRequest: 'merge_request',
Alert: 'alert',
};
export const WorkspaceType = {
project: 'project',
group: 'group',
};
......@@ -2,18 +2,11 @@
import { GlIcon, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
import Visibility from 'visibilityjs';
import createFlash from '~/flash';
import { IssuableStatus, IssuableStatusText, IssuableType } from '~/issues/constants';
import Poll from '~/lib/utils/poll';
import { visitUrl } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale';
import {
IssuableStatus,
IssuableStatusText,
IssuableType,
IssueTypePath,
IncidentTypePath,
IncidentType,
POLLING_DELAY,
} from '../constants';
import { IssueTypePath, IncidentTypePath, IncidentType, POLLING_DELAY } from '../constants';
import eventHub from '../event_hub';
import getIssueStateQuery from '../queries/get_issue_state.query.graphql';
import Service from '../services/index';
......
......@@ -133,7 +133,7 @@ export default {
}
},
},
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
safeHtmlConfig: { ADD_TAGS: ['gl-emoji', 'copy-code'] },
};
</script>
......
......@@ -2,7 +2,7 @@
import { GlAlert } from '@gitlab/ui';
import $ from 'jquery';
import Autosave from '~/autosave';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import eventHub from '../event_hub';
import EditActions from './edit_actions.vue';
import DescriptionField from './fields/description.vue';
......
......@@ -4,7 +4,8 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import createFlash, { FLASH_TYPES } from '~/flash';
import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
import { IssuableType } from '~/vue_shared/issuable/show/constants';
import { IssuableStatus, IssueStateEvent } from '~/issues/show/constants';
import { IssuableStatus } from '~/issues/constants';
import { IssueStateEvent } from '~/issues/show/constants';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { visitUrl } from '~/lib/utils/url_utility';
import { s__, __, sprintf } from '~/locale';
......
import { __ } from '~/locale';
export const IssuableStatus = {
Closed: 'closed',
Open: 'opened',
Reopened: 'reopened',
};
export const IssuableStatusText = {
[IssuableStatus.Closed]: __('Closed'),
[IssuableStatus.Open]: __('Open'),
[IssuableStatus.Reopened]: __('Open'),
};
export const IssuableType = {
Issue: 'issue',
Epic: 'epic',
MergeRequest: 'merge_request',
Alert: 'alert',
};
export const IssueStateEvent = {
Close: 'CLOSE',
Reopen: 'REOPEN',
......@@ -39,8 +20,3 @@ export const IncidentType = 'incident';
export const issueState = { issueType: undefined, isDirty: false };
export const POLLING_DELAY = 2000;
export const WorkspaceType = {
project: 'project',
group: 'group',
};
......@@ -3,7 +3,7 @@ import { getNormalizedURL, getBaseURL, relativePathToAbsolute } from '~/lib/util
const defaultConfig = {
// Safely allow SVG <use> tags
ADD_TAGS: ['use', 'gl-emoji'],
ADD_TAGS: ['use', 'gl-emoji', 'copy-code'],
// Prevent possible XSS attacks with data-* attributes used by @rails/ujs
// See https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1421
FORBID_ATTR: ['data-remote', 'data-url', 'data-type', 'data-method'],
......
......@@ -89,3 +89,17 @@ export const getParents = (element) => {
return parents;
};
/**
* This method takes a HTML element and an object of attributes
* to save repeated calls to `setAttribute` when multiple
* attributes need to be set.
*
* @param {HTMLElement} el
* @param {Object} attributes
*/
export const setAttributes = (el, attributes) => {
Object.keys(attributes).forEach((key) => {
el.setAttribute(key, attributes[key]);
});
};
......@@ -36,6 +36,7 @@ import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
import initBroadcastNotifications from './broadcast_notification';
import { initTopNav } from './nav';
import { initCopyCodeButton } from './behaviors/copy_code';
import 'ee_else_ce/main_ee';
import 'jh_else_ce/main_jh';
......@@ -97,6 +98,7 @@ function deferredInitialisation() {
initPersistentUserCallouts();
initDefaultTrackers();
initFeatureHighlight();
initCopyCodeButton();
if (gon.features?.newHeaderSearch) {
initHeaderSearchApp();
......
......@@ -149,7 +149,7 @@ export default {
},
},
safeHtmlConfig: {
ADD_TAGS: ['use', 'gl-emoji'],
ADD_TAGS: ['use', 'gl-emoji', 'copy-code'],
},
};
</script>
......
<script>
import { mapActions } from 'vuex';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { fetchPolicies } from '~/lib/graphql';
import { confidentialityQueries } from '~/sidebar/constants';
import { defaultClient as gqlClient } from '~/sidebar/graphql';
......
......@@ -12,7 +12,9 @@ export const COMMENT_FORM = {
epic: __('epic'),
bodyPlaceholder: __('Write a comment or drag your files here…'),
confidential: s__('Notes|Make this comment confidential'),
confidentialVisibility: s__('Notes|Confidential comments are only visible to project members'),
confidentialVisibility: s__(
'Notes|Confidential comments are only visible to members with the role of Reporter or higher',
),
discussionThatNeedsResolution: __(
'Discuss a specific suggestion or question that needs to be resolved.',
),
......
import { s__ } from '~/locale';
export const PackageType = {
CONAN: 'conan',
MAVEN: 'maven',
NPM: 'npm',
NUGET: 'nuget',
PYPI: 'pypi',
COMPOSER: 'composer',
RUBYGEMS: 'rubygems',
GENERIC: 'generic',
DEBIAN: 'debian',
HELM: 'helm',
};
// we want this separated from the main dictionary to avoid it being pulled in the search of package
export const TERRAFORM_PACKAGE_TYPE = 'terraform_module';
export const TrackingActions = {
DELETE_PACKAGE: 'delete_package',
REQUEST_DELETE_PACKAGE: 'request_delete_package',
CANCEL_DELETE_PACKAGE: 'cancel_delete_package',
PULL_PACKAGE: 'pull_package',
DELETE_PACKAGE_FILE: 'delete_package_file',
REQUEST_DELETE_PACKAGE_FILE: 'request_delete_package_file',
CANCEL_DELETE_PACKAGE_FILE: 'cancel_delete_package_file',
};
export const TrackingCategories = {
[PackageType.MAVEN]: 'MavenPackages',
[PackageType.NPM]: 'NpmPackages',
[PackageType.CONAN]: 'ConanPackages',
};
export const SHOW_DELETE_SUCCESS_ALERT = 'showSuccessDeleteAlert';
export const DELETE_PACKAGE_ERROR_MESSAGE = s__(
'PackageRegistry|Something went wrong while deleting the package.',
);
export const DELETE_PACKAGE_FILE_ERROR_MESSAGE = s__(
'PackageRegistry|Something went wrong while deleting the package file.',
);
export const DELETE_PACKAGE_FILE_SUCCESS_MESSAGE = s__(
'PackageRegistry|Package file deleted successfully',
);
export const PACKAGE_ERROR_STATUS = 'error';
export const PACKAGE_DEFAULT_STATUS = 'default';
export const PACKAGE_HIDDEN_STATUS = 'hidden';
export const PACKAGE_PROCESSING_STATUS = 'processing';
import { s__ } from '~/locale';
import { PackageType, TrackingCategories } from './constants';
export const packageTypeToTrackCategory = (type) =>
// eslint-disable-next-line @gitlab/require-i18n-strings
`UI::${TrackingCategories[type]}`;
export const beautifyPath = (path) => (path ? path.split('/').join(' / ') : '');
export const getPackageTypeLabel = (packageType) => {
switch (packageType) {
case PackageType.CONAN:
return s__('PackageRegistry|Conan');
case PackageType.MAVEN:
return s__('PackageRegistry|Maven');
case PackageType.NPM:
return s__('PackageRegistry|npm');
case PackageType.NUGET:
return s__('PackageRegistry|NuGet');
case PackageType.PYPI:
return s__('PackageRegistry|PyPI');
case PackageType.RUBYGEMS:
return s__('PackageRegistry|RubyGems');
case PackageType.COMPOSER:
return s__('PackageRegistry|Composer');
case PackageType.GENERIC:
return s__('PackageRegistry|Generic');
case PackageType.DEBIAN:
return s__('PackageRegistry|Debian');
case PackageType.HELM:
return s__('PackageRegistry|Helm');
default:
return null;
}
};
export const getCommitLink = ({ project_path: projectPath, pipeline = {} }, isGroup = false) => {
if (isGroup) {
return `/${projectPath}/commit/${pipeline.sha}`;
}
return `../commit/${pipeline.sha}`;
};
......@@ -17,9 +17,12 @@ import TerraformTitle from '~/packages_and_registries/infrastructure_registry/de
import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/details/components/terraform_installation.vue';
import Tracking from '~/tracking';
import PackageListRow from '~/packages_and_registries/infrastructure_registry/shared/package_list_row.vue';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
import { TrackingActions, SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants';
import { packageTypeToTrackCategory } from '~/packages/shared/utils';
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
import {
TRACKING_ACTIONS,
SHOW_DELETE_SUCCESS_ALERT,
} from '~/packages_and_registries/shared/constants';
import { TRACK_CATEGORY } from '~/packages_and_registries/infrastructure_registry/shared/constants';
import PackageFiles from './package_files.vue';
import PackageHistory from './package_history.vue';
......@@ -44,7 +47,7 @@ export default {
GlModal: GlModalDirective,
},
mixins: [Tracking.mixin()],
trackingActions: { ...TrackingActions },
trackingActions: { ...TRACKING_ACTIONS },
data() {
return {
fileToDelete: null,
......@@ -68,7 +71,7 @@ export default {
},
tracking() {
return {
category: packageTypeToTrackCategory(this.packageEntity.package_type),
category: TRACK_CATEGORY,
};
},
hasVersions() {
......@@ -86,7 +89,7 @@ export default {
}
},
async confirmPackageDeletion() {
this.track(TrackingActions.DELETE_PACKAGE);
this.track(TRACKING_ACTIONS.DELETE_PACKAGE);
await this.deletePackage();
const returnTo =
!this.groupListUrl || document.referrer.includes(this.projectName)
......@@ -96,12 +99,12 @@ export default {
window.location.replace(`${returnTo}?${modalQuery}`);
},
handleFileDelete(file) {
this.track(TrackingActions.REQUEST_DELETE_PACKAGE_FILE);
this.track(TRACKING_ACTIONS.REQUEST_DELETE_PACKAGE_FILE);
this.fileToDelete = { ...file };
this.$refs.deleteFileModal.show();
},
confirmFileDelete() {
this.track(TrackingActions.DELETE_PACKAGE_FILE);
this.track(TRACKING_ACTIONS.DELETE_PACKAGE_FILE);
this.deletePackageFile(this.fileToDelete.id);
this.fileToDelete = null;
},
......
......@@ -4,7 +4,7 @@ import {
DELETE_PACKAGE_ERROR_MESSAGE,
DELETE_PACKAGE_FILE_ERROR_MESSAGE,
DELETE_PACKAGE_FILE_SUCCESS_MESSAGE,
} from '~/packages/shared/constants';
} from '~/packages_and_registries/shared/constants';
import { FETCH_PACKAGE_VERSIONS_ERROR } from '../constants';
import * as types from './mutation_types';
......
......@@ -4,9 +4,9 @@ import { mapState, mapGetters } from 'vuex';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import PackagesListRow from '~/packages_and_registries/infrastructure_registry/shared/package_list_row.vue';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
import { TrackingActions } from '~/packages/shared/constants';
import { packageTypeToTrackCategory } from '~/packages/shared/utils';
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
import { TRACKING_ACTIONS } from '~/packages_and_registries/shared/constants';
import { TRACK_CATEGORY } from '~/packages_and_registries/infrastructure_registry/shared/constants';
export default {
components: {
......@@ -49,27 +49,24 @@ export default {
return this.itemToBeDeleted?.name ?? '';
},
tracking() {
const category = this.itemToBeDeleted
? packageTypeToTrackCategory(this.itemToBeDeleted.package_type)
: undefined;
return {
category,
category: TRACK_CATEGORY,
};
},
},
methods: {
setItemToBeDeleted(item) {
this.itemToBeDeleted = { ...item };
this.track(TrackingActions.REQUEST_DELETE_PACKAGE);
this.track(TRACKING_ACTIONS.REQUEST_DELETE_PACKAGE);
this.$refs.packageListDeleteModal.show();
},
deleteItemConfirmation() {
this.$emit('package:delete', this.itemToBeDeleted);
this.track(TrackingActions.DELETE_PACKAGE);
this.track(TRACKING_ACTIONS.DELETE_PACKAGE);
this.itemToBeDeleted = null;
},
deleteItemCanceled() {
this.track(TrackingActions.CANCEL_DELETE_PACKAGE);
this.track(TRACKING_ACTIONS.CANCEL_DELETE_PACKAGE);
this.itemToBeDeleted = null;
},
},
......
......@@ -4,8 +4,11 @@ import { mapActions, mapState } from 'vuex';
import createFlash from '~/flash';
import { historyReplaceState } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import {
SHOW_DELETE_SUCCESS_ALERT,
FILTERED_SEARCH_TERM,
} from '~/packages_and_registries/shared/constants';
import { getQueryParams, extractFilterAndSorting } from '~/packages_and_registries/shared/utils';
import InfrastructureTitle from '~/packages_and_registries/infrastructure_registry/list/components/infrastructure_title.vue';
import InfrastructureSearch from '~/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue';
......
import Api from '~/api';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages/shared/constants';
import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages_and_registries/shared/constants';
import {
FETCH_PACKAGES_LIST_ERROR_MESSAGE,
DELETE_PACKAGE_SUCCESS_MESSAGE,
......
import { beautifyPath } from '~/packages/shared/utils';
import { beautifyPath } from '~/packages_and_registries/shared/utils';
import { LIST_KEY_PROJECT } from '../constants';
export default (state) =>
......
......@@ -3,11 +3,13 @@ import { GlButton, GlLink, GlSprintf, GlTooltipDirective, GlTruncate } from '@gi
import { s__ } from '~/locale';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { PACKAGE_ERROR_STATUS, PACKAGE_DEFAULT_STATUS } from '~/packages/shared/constants';
import { getPackageTypeLabel } from '~/packages/shared/utils';
import PackagePath from '~/packages/shared/components/package_path.vue';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import PublishMethod from '~/packages/shared/components/publish_method.vue';
import {
PACKAGE_ERROR_STATUS,
PACKAGE_DEFAULT_STATUS,
} from '~/packages_and_registries/shared/constants';
import PackagePath from '~/packages_and_registries/shared/components/package_path.vue';
import PackageTags from '~/packages_and_registries/shared/components/package_tags.vue';
import PublishMethod from '~/packages_and_registries/shared/components/publish_method.vue';
import InfrastructureIconAndName from '~/packages_and_registries/infrastructure_registry/shared/infrastructure_icon_and_name.vue';
export default {
......@@ -53,9 +55,6 @@ export default {
},
},
computed: {
packageType() {
return getPackageTypeLabel(this.packageEntity.package_type);
},
hasPipeline() {
return Boolean(this.packageEntity.pipeline);
},
......@@ -120,9 +119,7 @@ export default {
</gl-sprintf>
</div>
<infrastructure-icon-and-name v-if="showPackageType">
{{ packageType }}
</infrastructure-icon-and-name>
<infrastructure-icon-and-name v-if="showPackageType" />
<package-path
v-if="hasProjectLink"
......
......@@ -15,7 +15,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { objectToQuery } from '~/lib/utils/url_utility';
import { s__, __ } from '~/locale';
import { packageTypeToTrackCategory } from '~/packages/shared/utils';
import { packageTypeToTrackCategory } from '~/packages_and_registries/package_registry/utils';
import AdditionalMetadata from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue';
import DependencyRow from '~/packages_and_registries/package_registry/components/details/dependency_row.vue';
import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.vue';
......
......@@ -3,7 +3,7 @@ import { GlIcon, GlSprintf, GlBadge, GlResizeObserverDirective } from '@gitlab/u
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import PackageTags from '~/packages_and_registries/shared/components/package_tags.vue';
import { PACKAGE_TYPE_NUGET } from '~/packages_and_registries/package_registry/constants';
import { getPackageTypeLabel } from '~/packages_and_registries/package_registry/utils';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
......
<script>
import { GlLink, GlSprintf, GlTruncate } from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import PublishMethod from '~/packages/shared/components/publish_method.vue';
import PackageTags from '~/packages_and_registries/shared/components/package_tags.vue';
import PublishMethod from '~/packages_and_registries/shared/components/publish_method.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { PACKAGE_DEFAULT_STATUS } from '../../constants';
......
......@@ -3,7 +3,7 @@ import { GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
import createFlash from '~/flash';
import { historyReplaceState } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages_and_registries/shared/constants';
import {
PROJECT_RESOURCE_TYPE,
GROUP_RESOURCE_TYPE,
......
......@@ -6,11 +6,11 @@ import {
PACKAGE_ERROR_STATUS,
PACKAGE_DEFAULT_STATUS,
} from '~/packages_and_registries/package_registry/constants';
import { getPackageTypeLabel } from '~/packages/shared/utils';
import PackagePath from '~/packages/shared/components/package_path.vue';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import { getPackageTypeLabel } from '~/packages_and_registries/package_registry/utils';
import PackagePath from '~/packages_and_registries/shared/components/package_path.vue';
import PackageTags from '~/packages_and_registries/shared/components/package_tags.vue';
import PublishMethod from '~/packages_and_registries/package_registry/components/list/publish_method.vue';
import PackageIconAndName from '~/packages/shared/components/package_icon_and_name.vue';
import PackageIconAndName from '~/packages_and_registries/shared/components/package_icon_and_name.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
......@@ -40,7 +40,7 @@ export default {
},
computed: {
packageType() {
return getPackageTypeLabel(this.packageEntity.packageType.toLowerCase());
return getPackageTypeLabel(this.packageEntity.packageType);
},
packageLink() {
const { project, id } = this.packageEntity;
......
......@@ -2,7 +2,7 @@
import { GlModal, GlSprintf, GlKeysetPagination } from '@gitlab/ui';
import { s__ } from '~/locale';
import PackagesListRow from '~/packages_and_registries/package_registry/components/list/package_list_row.vue';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
import {
DELETE_PACKAGE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
......
import { s__, __ } from '~/locale';
export {
DELETE_PACKAGE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
CANCEL_DELETE_PACKAGE_TRACKING_ACTION,
PULL_PACKAGE_TRACKING_ACTION,
DELETE_PACKAGE_FILE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION,
CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION,
} from '~/packages_and_registries/shared/constants';
export const PACKAGE_TYPE_CONAN = 'CONAN';
export const PACKAGE_TYPE_MAVEN = 'MAVEN';
export const PACKAGE_TYPE_NPM = 'NPM';
......@@ -11,14 +21,6 @@ export const PACKAGE_TYPE_GENERIC = 'GENERIC';
export const PACKAGE_TYPE_DEBIAN = 'DEBIAN';
export const PACKAGE_TYPE_HELM = 'HELM';
export const DELETE_PACKAGE_TRACKING_ACTION = 'delete_package';
export const REQUEST_DELETE_PACKAGE_TRACKING_ACTION = 'request_delete_package';
export const CANCEL_DELETE_PACKAGE_TRACKING_ACTION = 'cancel_delete_package';
export const PULL_PACKAGE_TRACKING_ACTION = 'pull_package';
export const DELETE_PACKAGE_FILE_TRACKING_ACTION = 'delete_package_file';
export const REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION = 'request_delete_package_file';
export const CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION = 'cancel_delete_package_file';
export const TRACKING_LABEL_CODE_INSTRUCTION = 'code_instruction';
export const TRACKING_LABEL_CONAN_INSTALLATION = 'conan_installation';
export const TRACKING_LABEL_MAVEN_INSTALLATION = 'maven_installation';
......
import { s__ } from '~/locale';
export const FILTERED_SEARCH_TERM = 'filtered-search-term';
export const FILTERED_SEARCH_TYPE = 'type';
export const HISTORY_PIPELINES_LIMIT = 5;
export const DELETE_PACKAGE_TRACKING_ACTION = 'delete_package';
export const REQUEST_DELETE_PACKAGE_TRACKING_ACTION = 'request_delete_package';
export const CANCEL_DELETE_PACKAGE_TRACKING_ACTION = 'cancel_delete_package';
export const PULL_PACKAGE_TRACKING_ACTION = 'pull_package';
export const DELETE_PACKAGE_FILE_TRACKING_ACTION = 'delete_package_file';
export const REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION = 'request_delete_package_file';
export const CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION = 'cancel_delete_package_file';
export const TRACKING_ACTIONS = {
DELETE_PACKAGE: DELETE_PACKAGE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE: REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
CANCEL_DELETE_PACKAGE: CANCEL_DELETE_PACKAGE_TRACKING_ACTION,
PULL_PACKAGE: PULL_PACKAGE_TRACKING_ACTION,
DELETE_PACKAGE_FILE: DELETE_PACKAGE_FILE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_FILE: REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION,
CANCEL_DELETE_PACKAGE_FILE: CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION,
};
export const SHOW_DELETE_SUCCESS_ALERT = 'showSuccessDeleteAlert';
export const DELETE_PACKAGE_ERROR_MESSAGE = s__(
'PackageRegistry|Something went wrong while deleting the package.',
);
export const DELETE_PACKAGE_FILE_ERROR_MESSAGE = s__(
'PackageRegistry|Something went wrong while deleting the package file.',
);
export const DELETE_PACKAGE_FILE_SUCCESS_MESSAGE = s__(
'PackageRegistry|Package file deleted successfully',
);
export const PACKAGE_ERROR_STATUS = 'error';
export const PACKAGE_DEFAULT_STATUS = 'default';
export const PACKAGE_HIDDEN_STATUS = 'hidden';
export const PACKAGE_PROCESSING_STATUS = 'processing';
......@@ -28,3 +28,13 @@ export const extractFilterAndSorting = (queryObject) => {
}
return { filters, sorting };
};
export const beautifyPath = (path) => (path ? path.split('/').join(' / ') : '');
export const getCommitLink = ({ project_path: projectPath, pipeline = {} }, isGroup = false) => {
if (isGroup) {
return `/${projectPath}/commit/${pipeline.sha}`;
}
return `../commit/${pipeline.sha}`;
};
......@@ -87,6 +87,9 @@ export default {
deleteModalTitle() {
return sprintf(__('Delete %{name}'), { name: this.name });
},
lockBtnQASelector() {
return this.canLock ? 'lock_button' : 'disabled_lock_button';
},
},
};
</script>
......@@ -102,7 +105,7 @@ export default {
:is-locked="isLocked"
:can-lock="canLock"
data-testid="lock"
data-qa-selector="lock_button"
:data-qa-selector="lockBtnQASelector"
/>
<gl-button v-gl-modal="replaceModalId" data-testid="replace">
{{ $options.i18n.replace }}
......
<script>
import produce from 'immer';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { assigneesQueries } from '~/sidebar/constants';
export default {
......
......@@ -3,7 +3,7 @@ import { GlDropdownItem } from '@gitlab/ui';
import { cloneDeep } from 'lodash';
import Vue from 'vue';
import createFlash from '~/flash';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { __, n__ } from '~/locale';
import SidebarAssigneesRealtime from '~/sidebar/components/assignees/assignees_realtime.vue';
import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees.vue';
......
<script>
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { __, sprintf } from '~/locale';
import AttentionRequestedToggle from '../attention_requested_toggle.vue';
import AssigneeAvatarLink from './assignee_avatar_link.vue';
......
<script>
import { GlSprintf, GlButton } from '@gitlab/ui';
import createFlash from '~/flash';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { __, sprintf } from '~/locale';
import { confidentialityQueries } from '~/sidebar/constants';
......
<script>
import { GlIcon, GlDatepicker, GlTooltipDirective, GlLink, GlPopover } from '@gitlab/ui';
import createFlash from '~/flash';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { dateInWords, formatDate, parsePikadayDate } from '~/lib/utils/datetime_utility';
import { __, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
......
......@@ -12,7 +12,7 @@ import {
} from '@gitlab/ui';
import createFlash from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { timeFor } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
......
<script>
import { GlIcon, GlLoadingIcon, GlToggle, GlTooltipDirective } from '@gitlab/ui';
import createFlash from '~/flash';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
......
<script>
import { GlIcon, GlLink, GlModal, GlModalDirective, GlLoadingIcon } from '@gitlab/ui';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { s__, __ } from '~/locale';
import { timeTrackingQueries } from '~/sidebar/constants';
......
import { s__, sprintf } from '~/locale';
import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
import { IssuableType, WorkspaceType } from '~/issues/show/constants';
import { IssuableType, WorkspaceType } from '~/issues/constants';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import epicConfidentialQuery from '~/sidebar/queries/epic_confidential.query.graphql';
import epicDueDateQuery from '~/sidebar/queries/epic_due_date.query.graphql';
......
import Vue from 'vue';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { parseBoolean } from '~/lib/utils/common_utils';
import timeTracker from './components/time_tracking/time_tracker.vue';
......
......@@ -5,7 +5,7 @@ import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import {
isInIssuePage,
isInDesignPage,
......
......@@ -16,6 +16,7 @@ import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
import { EXTENSION_ICON_CLASS, EXTENSION_ICONS } from '../../constants';
import StatusIcon from './status_icon.vue';
import Actions from './actions.vue';
import { generateText } from './utils';
export const LOADING_STATES = {
collapsedLoading: 'collapsedLoading',
......@@ -147,6 +148,9 @@ export default {
Sentry.captureException(e);
});
},
isArray(arr) {
return Array.isArray(arr);
},
appear(index) {
if (index === this.fullData.length - 1) {
this.showFade = false;
......@@ -157,6 +161,7 @@ export default {
this.showFade = true;
}
},
generateText,
},
EXTENSION_ICON_CLASS,
};
......@@ -177,7 +182,7 @@ export default {
<div class="gl-flex-grow-1">
<template v-if="isLoadingSummary">{{ widgetLoadingText }}</template>
<template v-else-if="hasFetchError">{{ widgetErrorText }}</template>
<div v-else v-safe-html="summary(collapsedData)"></div>
<div v-else v-safe-html="generateText(summary(collapsedData))"></div>
</div>
<actions
:widget="$options.label || $options.name"
......@@ -224,32 +229,59 @@ export default {
:class="{
'gl-border-b-solid gl-border-b-1 gl-border-gray-100': index !== fullData.length - 1,
}"
class="gl-display-flex gl-align-items-center gl-py-3 gl-pl-7"
class="gl-py-3 gl-pl-7"
data-testid="extension-list-item"
>
<status-icon v-if="data.icon" :icon-name="data.icon.name" :size="12" class="gl-pl-0" />
<gl-intersection-observer
:options="{ rootMargin: '100px', thresholds: 0.1 }"
class="gl-flex-wrap gl-display-flex gl-w-full"
@appear="appear(index)"
@disappear="disappear(index)"
>
<div
v-safe-html="data.text"
class="gl-mr-4 gl-display-flex gl-align-items-center"
></div>
<div v-if="data.link">
<gl-link :href="data.link.href">{{ data.link.text }}</gl-link>
<div class="gl-w-full">
<div v-if="data.header" class="gl-mb-2">
<template v-if="isArray(data.header)">
<component
:is="headerI === 0 ? 'strong' : 'span'"
v-for="(header, headerI) in data.header"
:key="headerI"
v-safe-html="generateText(header)"
class="gl-display-block"
/>
</template>
<strong v-else v-safe-html="generateText(data.header)"></strong>
</div>
<div class="gl-display-flex">
<status-icon
v-if="data.icon"
:icon-name="data.icon.name"
:size="12"
class="gl-pl-0"
/>
<gl-intersection-observer
:options="{ rootMargin: '100px', thresholds: 0.1 }"
class="gl-w-full"
@appear="appear(index)"
@disappear="disappear(index)"
>
<div class="gl-flex-wrap gl-display-flex gl-w-full">
<div class="gl-mr-4 gl-display-flex gl-align-items-center">
<p v-safe-html="generateText(data.text)" class="gl-m-0"></p>
</div>
<div v-if="data.link">
<gl-link :href="data.link.href">{{ data.link.text }}</gl-link>
</div>
<gl-badge v-if="data.badge" :variant="data.badge.variant || 'info'">
{{ data.badge.text }}
</gl-badge>
<actions
:widget="$options.label || $options.name"
:tertiary-buttons="data.actions"
class="gl-ml-auto"
/>
</div>
<p
v-if="data.subtext"
v-safe-html="generateText(data.subtext)"
class="gl-m-0 gl-font-sm"
></p>
</gl-intersection-observer>
</div>
<gl-badge v-if="data.badge" :variant="data.badge.variant || 'info'">
{{ data.badge.text }}
</gl-badge>
<actions
:widget="$options.label || $options.name"
:tertiary-buttons="data.actions"
class="gl-ml-auto"
/>
</gl-intersection-observer>
</div>
</li>
</smart-virtual-list>
<div
......
const TEXT_STYLES = {
success: {
start: '%{success_start}',
end: '%{success_end}',
},
danger: {
start: '%{danger_start}',
end: '%{danger_end}',
},
critical: {
start: '%{critical_start}',
end: '%{critical_end}',
},
same: {
start: '%{same_start}',
end: '%{same_end}',
},
strong: {
start: '%{strong_start}',
end: '%{strong_end}',
},
small: {
start: '%{small_start}',
end: '%{small_end}',
},
};
const getStartTag = (tag) => TEXT_STYLES[tag].start;
const textStyleTags = {
[getStartTag('success')]: '<span class="gl-font-weight-bold gl-text-green-500">',
[getStartTag('danger')]: '<span class="gl-font-weight-bold gl-text-red-500">',
[getStartTag('critical')]: '<span class="gl-font-weight-bold gl-text-red-800">',
[getStartTag('same')]: '<span class="gl-font-weight-bold gl-text-gray-700">',
[getStartTag('strong')]: '<span class="gl-font-weight-bold">',
[getStartTag('small')]: '<span class="gl-font-sm">',
};
export const generateText = (text) => {
if (typeof text !== 'string') return null;
return text
.replace(
new RegExp(
`(${Object.values(TEXT_STYLES)
.reduce((acc, i) => [...acc, ...Object.values(i)], [])
.join('|')})`,
'gi',
),
(replace) => {
const replacement = textStyleTags[replace];
// If the replacement tag ends with a `_end` then we can just return `</span>`
// unless we have a replacement, for cases were we want to change the HTML tag
if (!replacement && replace.endsWith('_end}')) {
return '</span>';
}
return replacement;
},
)
.replace(/%{([a-z]|_)+}/g, ''); // Filter out any tags we don't know about
};
......@@ -2,6 +2,7 @@
import { EXTENSION_ICONS } from '../constants';
import issuesCollapsedQuery from './issues_collapsed.query.graphql';
import issuesQuery from './issues.query.graphql';
import { n__, sprintf } from '~/locale';
export default {
// Give the extension a name
......@@ -20,7 +21,14 @@ export default {
// Small summary text to be displayed in the collapsed state
// Receives the collapsed data as an argument
summary(count) {
return 'Summary text<br/>Second line';
return sprintf(
n__(
'ciReport|Load performance test metrics detected %{strong_start}%{changesFound}%{strong_end} change',
'ciReport|Load performance test metrics detected %{strong_start}%{changesFound}%{strong_end} changes',
changesFound,
),
{ changesFound },
);
},
// Status icon to be used next to the summary text
// Receives the collapsed data as an argument
......@@ -57,9 +65,13 @@ export default {
.query({ query: issuesQuery, variables: { projectPath: targetProjectFullPath } })
.then(({ data }) => {
// Return some transformed data to be rendered in the expanded state
return data.project.issues.nodes.map((issue) => ({
return data.project.issues.nodes.map((issue, i) => ({
id: issue.id, // Required: The ID of the object
text: issue.title, // Required: The text to get used on each row
header: ['New', 'This is an %{strong_start}issue%{strong_end} row'],
text:
'%{critical_start}1 Critical%{critical_end}, %{danger_start}1 High%{danger_end}, and %{strong_start}1 Other%{strong_end}. %{small_start}Some smaller text%{small_end}', // Required: The text to get used on each row
subtext:
'Reported resource changes: %{strong_start}2%{strong_end} to add, 0 to change, 0 to delete', // Optional: The sub-text to get displayed below each rows main content
// Icon to get rendered on the side of each row
icon: {
// Required: Name maps to an icon in GitLabs SVG
......
......@@ -10,7 +10,11 @@ import {
import { debounce } from 'lodash';
import { DEBOUNCE_DELAY, FILTER_NONE_ANY, OPERATOR_IS_NOT } from '../constants';
import { getRecentlyUsedSuggestions, setTokenValueToRecentlyUsed } from '../filtered_search_utils';
import {
getRecentlyUsedSuggestions,
setTokenValueToRecentlyUsed,
stripQuotes,
} from '../filtered_search_utils';
export default {
components: {
......@@ -163,7 +167,14 @@ export default {
this.searchKey = data;
if (!this.suggestionsLoading && !this.activeTokenValue) {
const search = this.searchTerm ? this.searchTerm : data;
let search = this.searchTerm ? this.searchTerm : data;
if (search.startsWith('"') && search.endsWith('"')) {
search = stripQuotes(search);
} else if (search.startsWith('"')) {
search = search.slice(1, search.length);
}
this.$emit('fetch-suggestions', search);
}
}, DEBOUNCE_DELAY),
......
......@@ -39,6 +39,11 @@ export default {
required: false,
default: null,
},
isOverviewTab: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
...mapGetters(['getUserData']),
......@@ -46,9 +51,10 @@ export default {
return renderMarkdown(this.note.body);
},
avatarSize() {
if (this.line) {
return 16;
if (this.line && !this.isOverviewTab) {
return 24;
}
return 40;
},
},
......
......@@ -2,7 +2,7 @@
import { debounce } from 'lodash';
import { MutationOperationMode, getIdFromGraphQLId } from '~/graphql_shared/utils';
import createFlash from '~/flash';
import { IssuableType } from '~/issues/show/constants';
import { IssuableType } from '~/issues/constants';
import { __ } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import { issuableLabelsQueries } from '~/sidebar/constants';
......
......@@ -166,6 +166,8 @@ export default {
class="issue gl-display-flex! gl-px-5!"
:class="{ closed: issuable.closedAt, today: createdInPastDay }"
:data-labels="labelIdsString"
data-qa-selector="issuable_item_container"
:data-qa-issue-id="issuableId"
>
<gl-form-checkbox
v-if="showCheckbox"
......
......@@ -272,6 +272,7 @@ export default {
:show-checkbox="showBulkEditSidebar"
:checkbox-checked="allIssuablesChecked"
class="gl-flex-grow-1 gl-border-t-none row-content-block"
data-qa-selector="issuable_search_container"
@checked-input="handleAllIssuablesCheckedInput"
@onFilter="$emit('filter', $event)"
@onSort="$emit('sort', $event)"
......@@ -302,6 +303,8 @@ export default {
v-for="issuable in issuables"
:key="issuableId(issuable)"
:class="{ 'gl-cursor-grab': isManualOrdering }"
data-qa-selector="issuable_container"
:data-qa-issuable-title="issuable.title"
:issuable-symbol="issuableSymbol"
:issuable="issuable"
:enable-label-permalinks="enableLabelPermalinks"
......
......@@ -46,7 +46,9 @@ export default {
@click="$emit('click', tab.name)"
>
<template #title>
<span :title="tab.titleTooltip">{{ tab.title }}</span>
<span :title="tab.titleTooltip" :data-qa-selector="`${tab.name}_issuables_tab`">
{{ tab.title }}
</span>
<gl-badge
v-if="tabCounts && isTabCountNumeric(tab)"
variant="muted"
......
......@@ -100,7 +100,7 @@ export default {
</script>
<template>
<div class="issuable-show-container">
<div class="issuable-show-container" data-qa-selector="issuable_show_container">
<issuable-header
:status-badge-class="statusBadgeClass"
:status-icon="statusIcon"
......
#import './widget.fragment.graphql'
mutation createWorkItem($input: CreateWorkItemInput) {
createWorkItem(input: $input) @client {
mutation createWorkItem($input: LocalCreateWorkItemInput) {
localCreateWorkItem(input: $input) @client {
workItem {
id
type
widgets {
nodes {
...WidgetBase
... on TitleWidget {
... on LocalTitleWidget {
contentText
}
}
......
{"__schema":{"types":[{"kind":"INTERFACE","name":"WorkItemWidget","possibleTypes":[{"name":"TitleWidget"}]}]}}
{"__schema":{"types":[{"kind":"INTERFACE","name":"LocalWorkItemWidget","possibleTypes":[{"name":"LocalTitleWidget"}]}]}}
......@@ -28,14 +28,14 @@ export function createApolloProvider() {
},
data: {
workItem: {
__typename: 'WorkItem',
__typename: 'LocalWorkItem',
id: '1',
type: 'FEATURE',
widgets: {
__typename: 'WorkItemWidgetConnection',
__typename: 'LocalWorkItemWidgetConnection',
nodes: [
{
__typename: 'TitleWidget',
__typename: 'LocalTitleWidget',
type: 'TITLE',
enabled: true,
// eslint-disable-next-line @gitlab/require-i18n-strings
......
......@@ -3,17 +3,17 @@ import workItemQuery from './work_item.query.graphql';
export const resolvers = {
Mutation: {
createWorkItem(_, { input }, { cache }) {
localCreateWorkItem(_, { input }, { cache }) {
const id = uuids()[0];
const workItem = {
__typename: 'WorkItem',
__typename: 'LocalWorkItem',
type: 'FEATURE',
id,
widgets: {
__typename: 'WorkItemWidgetConnection',
__typename: 'LocalWorkItemWidgetConnection',
nodes: [
{
__typename: 'TitleWidget',
__typename: 'LocalTitleWidget',
type: 'TITLE',
enabled: true,
contentText: input.title,
......@@ -25,24 +25,24 @@ export const resolvers = {
cache.writeQuery({ query: workItemQuery, variables: { id }, data: { workItem } });
return {
__typename: 'CreateWorkItemPayload',
__typename: 'LocalCreateWorkItemPayload',
workItem,
};
},
updateWorkItem(_, { input }, { cache }) {
localUpdateWorkItem(_, { input }, { cache }) {
const workItemTitle = {
__typename: 'TitleWidget',
__typename: 'LocalTitleWidget',
type: 'TITLE',
enabled: true,
contentText: input.title,
};
const workItem = {
__typename: 'WorkItem',
__typename: 'LocalWorkItem',
type: 'FEATURE',
id: input.id,
widgets: {
__typename: 'WorkItemWidgetConnection',
__typename: 'LocalWorkItemWidgetConnection',
nodes: [workItemTitle],
},
};
......@@ -50,7 +50,7 @@ export const resolvers = {
cache.writeQuery({ query: workItemQuery, variables: { id: input.id }, data: { workItem } });
return {
__typename: 'UpdateWorkItemPayload',
__typename: 'LocalUpdateWorkItemPayload',
workItem,
};
},
......
enum WorkItemType {
enum LocalWorkItemType {
FEATURE
}
enum WidgetType {
enum LocalWidgetType {
TITLE
}
interface WorkItemWidget {
type: WidgetType!
interface LocalWorkItemWidget {
type: LocalWidgetType!
}
# Replicating Relay connection type for client schema
type WorkItemWidgetEdge {
type LocalWorkItemWidgetEdge {
cursor: String!
node: WorkItemWidget
node: LocalWorkItemWidget
}
type WorkItemWidgetConnection {
edges: [WorkItemWidgetEdge]
nodes: [WorkItemWidget]
type LocalWorkItemWidgetConnection {
edges: [LocalWorkItemWidgetEdge]
nodes: [LocalWorkItemWidget]
pageInfo: PageInfo!
}
type TitleWidget implements WorkItemWidget {
type: WidgetType!
type LocalTitleWidget implements LocalWorkItemWidget {
type: LocalWidgetType!
contentText: String!
}
type WorkItem {
type LocalWorkItem {
id: ID!
type: WorkItemType!
widgets: [WorkItemWidgetConnection]
type: LocalWorkItemType!
widgets: [LocalWorkItemWidgetConnection]
}
type CreateWorkItemInput {
input LocalCreateWorkItemInput {
title: String!
}
type UpdateWorkItemInput {
input LocalUpdateWorkItemInput {
id: ID!
title: String
}
type CreateWorkItemPayload {
workItem: WorkItem!
type LocalCreateWorkItemPayload {
workItem: LocalWorkItem!
}
type UpdateWorkItemPayload {
workItem: WorkItem!
type LocalUpdateWorkItemPayload {
workItem: LocalWorkItem!
}
extend type Query {
workItem(id: ID!): WorkItem!
workItem(id: ID!): LocalWorkItem!
}
extend type Mutation {
createWorkItem(input: CreateWorkItemInput!): CreateWorkItemPayload!
updateWorkItem(input: UpdateWorkItemInput!): UpdateWorkItemPayload!
localCreateWorkItem(input: LocalCreateWorkItemInput!): LocalCreateWorkItemPayload!
localUpdateWorkItem(input: LocalUpdateWorkItemInput!): LocalUpdateWorkItemPayload!
}
#import './widget.fragment.graphql'
mutation updateWorkItem($input: UpdateWorkItemInput) {
updateWorkItem(input: $input) @client {
mutation updateWorkItem($input: LocalUpdateWorkItemInput) {
localUpdateWorkItem(input: $input) @client {
workItem {
id
type
widgets {
nodes {
...WidgetBase
... on TitleWidget {
... on LocalTitleWidget {
contentText
}
}
......
fragment WidgetBase on WorkItemWidget {
fragment WidgetBase on LocalWorkItemWidget {
type
}
......@@ -7,7 +7,7 @@ query WorkItem($id: ID!) {
widgets {
nodes {
...WidgetBase
... on TitleWidget {
... on LocalTitleWidget {
contentText
}
}
......
......@@ -30,7 +30,7 @@ export default {
const {
data: {
createWorkItem: {
localCreateWorkItem: {
workItem: { id },
},
},
......@@ -60,11 +60,10 @@ export default {
class="gl-mr-3"
data-testid="create-button"
type="submit"
@click="createWorkItem"
>
{{ __('Create') }}
</gl-button>
<gl-button data-testid="cancel-button" @click="$router.go(-1)">
<gl-button type="button" data-testid="cancel-button" @click="$router.go(-1)">
{{ __('Cancel') }}
</gl-button>
</div>
......
......@@ -451,3 +451,16 @@ fieldset[disabled] .btn,
box-shadow: none;
border-width: 1px;
}
copy-code {
@include gl-absolute;
@include gl-transition-medium;
@include gl-opacity-0;
top: 7px;
right: $input-horizontal-padding;
.markdown-code-block:hover & {
@include gl-opacity-10;
}
}
......@@ -625,6 +625,7 @@ body {
/** CODE **/
pre {
@include gl-relative;
font-family: $monospace-font;
display: block;
padding: $gl-padding-8 $input-horizontal-padding;
......@@ -636,6 +637,11 @@ pre {
background-color: $gray-light;
border: 1px solid $gray-100;
border-radius: $border-radius-small;
// Select only code elements that will have the copy code button
.markdown-code-block & {
padding: $input-horizontal-padding;
}
}
code {
......
......@@ -94,7 +94,8 @@ $dark-il: #de935f;
}
.line-numbers,
.diff-line-num {
.diff-line-num,
.code-search-line {
background-color: $dark-main-bg;
}
......@@ -168,7 +169,8 @@ $dark-il: #de935f;
}
.diff-grid-left:hover,
.diff-grid-right:hover {
.diff-grid-right:hover,
&.code-search-line:hover {
.diff-line-num:not(.empty-cell) {
@include line-number-hover;
}
......
......@@ -95,7 +95,8 @@ $monokai-gh: #75715e;
}
.line-numbers,
.diff-line-num {
.diff-line-num,
.code-search-line {
background-color: $monokai-bg;
}
......@@ -169,7 +170,8 @@ $monokai-gh: #75715e;
}
.diff-grid-left:hover,
.diff-grid-right:hover {
.diff-grid-right:hover,
&.code-search-line:hover {
.diff-line-num:not(.empty-cell) {
@include line-number-hover;
}
......
......@@ -16,7 +16,8 @@
}
.line-numbers,
.diff-line-num {
.diff-line-num,
.code-search-line {
background-color: $gray-light;
}
......@@ -66,7 +67,8 @@
}
.diff-grid-left:hover,
.diff-grid-right:hover {
.diff-grid-right:hover,
&.code-search-line:hover {
.diff-line-num:not(.empty-cell) {
@include line-number-hover;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment