Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot
2022-01-14 06:14:22 +00:00
parent 4df4bbed3e
commit d11616c828
11 changed files with 178 additions and 19 deletions

View File

@ -2,6 +2,7 @@ import Vue from 'vue';
import { mapGetters } from 'vuex';
import errorTrackingStore from '~/error_tracking/store';
import { parseBoolean } from '~/lib/utils/common_utils';
import { scrollToTargetOnResize } from '~/lib/utils/resize_observer';
import IssueApp from './components/app.vue';
import HeaderActions from './components/header_actions.vue';
import IncidentTabs from './components/incidents/incident_tabs.vue';
@ -73,6 +74,10 @@ export function initIssueApp(issueData, store) {
return undefined;
}
if (gon?.features?.fixCommentScroll) {
scrollToTargetOnResize();
}
bootstrapApollo({ ...issueState, issueType: el.dataset.issueType });
const { canCreateIncident, ...issueProps } = issueData;

View File

@ -181,6 +181,7 @@ export const contentTop = () => {
},
() => getOuterHeight('.merge-request-tabs'),
() => getOuterHeight('.js-diff-files-changed'),
() => getOuterHeight('.issue-sticky-header.gl-fixed'),
({ desktop }) => {
const diffsTabIsActive = window.mrTabs?.currentAction === 'diffs';
let size;

View File

@ -0,0 +1,58 @@
import { contentTop } from './common_utils';
const interactionEvents = ['mousedown', 'touchstart', 'keydown', 'wheel'];
export function createResizeObserver() {
return new ResizeObserver((entries) => {
entries.forEach((entry) => {
entry.target.dispatchEvent(new CustomEvent(`ResizeUpdate`, { detail: { entry } }));
});
});
}
// watches for change in size of a container element (e.g. for lazy-loaded images)
// and scroll the target element to the top of the content area
// stop watching after any user input. So if user opens sidebar or manually
// scrolls the page we don't hijack their scroll position
export function scrollToTargetOnResize({
target = window.location.hash,
container = '#content-body',
} = {}) {
if (!target) return null;
const ro = createResizeObserver();
const containerEl = document.querySelector(container);
let interactionListenersAdded = false;
function keepTargetAtTop() {
const anchorEl = document.querySelector(target);
if (!anchorEl) return;
const anchorTop = anchorEl.getBoundingClientRect().top + window.scrollY;
const top = anchorTop - contentTop();
document.documentElement.scrollTo({
top,
});
if (!interactionListenersAdded) {
interactionEvents.forEach((event) =>
// eslint-disable-next-line no-use-before-define
document.addEventListener(event, removeListeners),
);
interactionListenersAdded = true;
}
}
function removeListeners() {
interactionEvents.forEach((event) => document.removeEventListener(event, removeListeners));
ro.unobserve(containerEl);
containerEl.removeEventListener('ResizeUpdate', keepTargetAtTop);
}
containerEl.addEventListener('ResizeUpdate', keepTargetAtTop);
ro.observe(containerEl);
return ro;
}

View File

@ -53,6 +53,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:confidential_notes, project&.group, default_enabled: :yaml)
push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml)
push_frontend_feature_flag(:paginated_issue_discussions, @project, default_enabled: :yaml)
push_frontend_feature_flag(:fix_comment_scroll, @project, default_enabled: :yaml)
end
around_action :allow_gitaly_ref_name_caching, only: [:discussions]

View File

@ -41,9 +41,7 @@ module Clusters
end
def prepare_uninstall
::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.allow_cross_database_modification_within_transaction(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/350180') do
runner&.update!(active: false)
end
# No op, see https://gitlab.com/gitlab-org/gitlab/-/issues/350180.
end
def post_uninstall

View File

@ -0,0 +1,8 @@
---
name: fix_comment_scroll
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76340
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349638
milestone: '14.7'
type: development
group: group::project management
default_enabled: false

View File

@ -35,7 +35,7 @@ sudo -u git -H bundle exec rails console -e production
**For Kubernetes deployments**
The console is in the task-runner pod. Refer to our [Kubernetes cheat sheet](../troubleshooting/kubernetes_cheat_sheet.md#gitlab-specific-kubernetes-information) for details.
The console is in the toolbox pod. Refer to our [Kubernetes cheat sheet](../troubleshooting/kubernetes_cheat_sheet.md#gitlab-specific-kubernetes-information) for details.
To exit the console, type: `quit`.

View File

@ -147,10 +147,10 @@ Secrets such as credential files, SSH private keys, and other files containing s
GitLab enables you to turn on a predefined denylist of files which can't be
pushed to a repository. The list stops those commits from reaching the remote repository.
By selecting the checkbox *Prevent committing secrets to Git*, GitLab prevents
By selecting the checkbox *Prevent pushing secret files*, GitLab prevents
pushes to the repository when a file matches a regular expression as read from
[`files_denylist.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) (make sure you are at the right branch
as your GitLab version when viewing this file).
as your GitLab version when viewing this file). This checkbox is able to be set globally via **Admin Area > Push Rules**, or per-project via **Settings > Repository > Push Rules**.
NOTE:
Files already committed aren't restricted by this push rule.

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'User scrolls to deep-linked note' do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:comment_1) { create(:note_on_issue, noteable: issue, project: project, note: 'written first') }
let_it_be(:comments) { create_list(:note_on_issue, 20, noteable: issue, project: project, note: 'spacer note') }
context 'on issue page', :js do
it 'on comment' do
visit project_issue_path(project, issue, anchor: "note_#{comment_1.id}")
wait_for_requests
expect(first_comment).to have_content(comment_1.note)
bottom_of_title = find('.issue-sticky-header.gl-fixed').evaluate_script("this.getBoundingClientRect().bottom;")
top = first_comment.evaluate_script("this.getBoundingClientRect().top;")
expect(top).to be_within(1).of(bottom_of_title)
end
end
def all_comments
all('.timeline > .note.timeline-entry')
end
def first_comment
all_comments.first
end
end

View File

@ -0,0 +1,68 @@
import { contentTop } from '~/lib/utils/common_utils';
import { scrollToTargetOnResize } from '~/lib/utils/resize_observer';
jest.mock('~/lib/utils/common_utils');
function mockStickyHeaderSize(val) {
contentTop.mockReturnValue(val);
}
describe('ResizeObserver Utility', () => {
let observer;
const triggerResize = () => {
const entry = document.querySelector('#content-body');
entry.dispatchEvent(new CustomEvent(`ResizeUpdate`, { detail: { entry } }));
};
beforeEach(() => {
mockStickyHeaderSize(90);
jest.spyOn(document.documentElement, 'scrollTo');
setFixtures(`<div id="content-body"><div class="target">element to scroll to</div></div>`);
const target = document.querySelector('.target');
jest.spyOn(target, 'getBoundingClientRect').mockReturnValue({ top: 200 });
observer = scrollToTargetOnResize({
target: '.target',
container: '#content-body',
});
});
afterEach(() => {
contentTop.mockReset();
});
describe('Observer behavior', () => {
it('returns null for empty target', () => {
observer = scrollToTargetOnResize({
target: '',
container: '#content-body',
});
expect(observer).toBe(null);
});
it('returns ResizeObserver instance', () => {
expect(observer).toBeInstanceOf(ResizeObserver);
});
it('scrolls body so anchor is just below sticky header (contentTop)', () => {
triggerResize();
expect(document.documentElement.scrollTo).toHaveBeenCalledWith({ top: 110 });
});
const interactionEvents = ['mousedown', 'touchstart', 'keydown', 'wheel'];
it.each(interactionEvents)('does not hijack scroll after user input from %s', (eventType) => {
const event = new Event(eventType);
document.dispatchEvent(event);
triggerResize();
expect(document.documentElement.scrollTo).not.toHaveBeenCalledWith();
});
});
});

View File

@ -101,19 +101,6 @@ RSpec.describe Clusters::Applications::Runner do
end
end
describe '#prepare_uninstall' do
it 'pauses associated runner' do
active_runner = create(:ci_runner, contacted_at: 1.second.ago)
expect(active_runner.active).to be_truthy
application_runner = create(:clusters_applications_runner, :scheduled, runner: active_runner)
application_runner.prepare_uninstall
expect(active_runner.active).to be_falsey
end
end
describe '#make_uninstalling!' do
subject { create(:clusters_applications_runner, :scheduled, runner: ci_runner) }