Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7db49154e7
commit
0a362b6da1
|
|
@ -44,6 +44,7 @@ export default {
|
|||
|
||||
getFormDataAsObject() {
|
||||
const assigneeIds = this.form.find('input[name="update[assignee_ids][]"]').val();
|
||||
const statusId = this.form.find('input[name="update[status]"]')?.val();
|
||||
const formData = {
|
||||
update: {
|
||||
state_event: this.form.find('input[name="update[state_event]"]').val(),
|
||||
|
|
@ -58,6 +59,12 @@ export default {
|
|||
confidential: this.form.find('input[name="update[confidentiality]"]').val(),
|
||||
},
|
||||
};
|
||||
|
||||
if (statusId) {
|
||||
// when the FF is disabled or license check fails the status dropdown is not visible
|
||||
formData.update.status = statusId;
|
||||
}
|
||||
|
||||
if (assigneeIds) {
|
||||
formData.update.assignee_ids = [assigneeIds];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,11 +73,19 @@ export default class IssuableBulkUpdateSidebar {
|
|||
// runtime this block won't execute.
|
||||
if (IS_EE) {
|
||||
import('ee_else_ce/sidebar/mount_sidebar')
|
||||
.then(({ mountEpicDropdown, mountHealthStatusDropdown, mountIterationDropdown }) => {
|
||||
mountEpicDropdown();
|
||||
mountHealthStatusDropdown();
|
||||
mountIterationDropdown();
|
||||
})
|
||||
.then(
|
||||
({
|
||||
mountEpicDropdown,
|
||||
mountHealthStatusDropdown,
|
||||
mountIterationDropdown,
|
||||
mountSidebarCustomStatusWidget,
|
||||
}) => {
|
||||
mountEpicDropdown();
|
||||
mountHealthStatusDropdown();
|
||||
mountIterationDropdown();
|
||||
mountSidebarCustomStatusWidget();
|
||||
},
|
||||
)
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ export class DiffFile extends HTMLElement {
|
|||
trigger;
|
||||
/** @type {Object} Storage for intermediate state used by adapters */
|
||||
sink = {};
|
||||
_hookTeardowns = {
|
||||
[events.MOUNTED]: new Set(),
|
||||
};
|
||||
|
||||
static findByFileHash(hash) {
|
||||
return document.querySelector(`diff-file[id="${hash}"]`);
|
||||
|
|
@ -37,11 +40,19 @@ export class DiffFile extends HTMLElement {
|
|||
this.observeVisibility();
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
this.trigger = this._trigger.bind(this);
|
||||
this.trigger(events.MOUNTED);
|
||||
this.trigger(events.MOUNTED, this.registerMountedTeardowns.bind(this));
|
||||
this.dispatchEvent(new CustomEvent(DIFF_FILE_MOUNTED, { bubbles: true }));
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const mountedTeardowns = this._hookTeardowns[events.MOUNTED];
|
||||
mountedTeardowns.forEach((cb) => {
|
||||
cb();
|
||||
});
|
||||
mountedTeardowns.clear();
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
this._hookTeardowns = undefined;
|
||||
// app might be missing if the file was destroyed before mounting
|
||||
// for example: changing view settings in the middle of the streaming
|
||||
if (this.app) this.unobserveVisibility();
|
||||
|
|
@ -60,6 +71,11 @@ export class DiffFile extends HTMLElement {
|
|||
this.adapters.forEach((adapter) => adapter[event]?.call?.(this.adapterContext, ...args));
|
||||
}
|
||||
|
||||
registerMountedTeardowns(callback) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
this._hookTeardowns[events.MOUNTED].add(callback);
|
||||
}
|
||||
|
||||
observeVisibility() {
|
||||
if (!this.adapters.some((adapter) => adapter[events.VISIBLE] || adapter[events.INVISIBLE]))
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { COLLAPSE_FILE, EXPAND_FILE } from '~/rapid_diffs/events';
|
||||
import { COLLAPSE_FILE, EXPAND_FILE, MOUNTED } from '~/rapid_diffs/events';
|
||||
|
||||
function getDetails(root) {
|
||||
return root.querySelector('[data-file-body]');
|
||||
}
|
||||
|
||||
function getOppositeToggleButton(clicked) {
|
||||
const isOpened = clicked.dataset.opened;
|
||||
|
|
@ -12,15 +16,13 @@ function getOppositeToggleButton(clicked) {
|
|||
function collapse(root = this.diffElement) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
root.dataset.collapsed = true;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
root.querySelector('[data-file-body]').hidden = true;
|
||||
getDetails(root).removeAttribute('open');
|
||||
}
|
||||
|
||||
function expand(root = this.diffElement) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete root.dataset.collapsed;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
root.querySelector('[data-file-body]').hidden = false;
|
||||
getDetails(root).open = true;
|
||||
}
|
||||
|
||||
function stopTransition(element) {
|
||||
|
|
@ -51,4 +53,18 @@ export const ToggleFileAdapter = {
|
|||
[COLLAPSE_FILE]() {
|
||||
collapse.call(this);
|
||||
},
|
||||
[MOUNTED](onUnmounted) {
|
||||
const details = getDetails(this.diffElement);
|
||||
const handleToggle = () => {
|
||||
if (details.open) {
|
||||
expand.call(this);
|
||||
} else {
|
||||
collapse.call(this);
|
||||
}
|
||||
};
|
||||
details.addEventListener('toggle', handleToggle);
|
||||
onUnmounted(() => {
|
||||
details.removeEventListener('toggle', handleToggle);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ export default {
|
|||
},
|
||||
},
|
||||
i18n: {
|
||||
dropdownTitle: __('Change status'),
|
||||
defaultDropdownText: __('Select status'),
|
||||
dropdownTitle: __('Change state'),
|
||||
defaultDropdownText: __('Select state'),
|
||||
resetText: __('Reset'),
|
||||
},
|
||||
statusDropdownOptions,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
--rd-diff-file-border-radius: #{$gl-border-radius-base};
|
||||
}
|
||||
|
||||
.rd-diff-file-details > summary {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rd-diff-file[data-virtual]:not([data-collapsed]) {
|
||||
// header top/bottom borders + body border
|
||||
$total-borders: constants.$diff-file-border-width * 3;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
%diff-file.rd-diff-file-component{ id: id, data: { testid: 'rd-diff-file', file_data: file_data.to_json } }
|
||||
%article.rd-diff-file{ data: { virtual: virtual? }, style: ("--total-rows: #{total_rows}" if virtual?), aria: { labelledby: heading_id } }
|
||||
= header || default_header
|
||||
-# extra wrapper needed so content-visibility: hidden doesn't require removing border or other styles
|
||||
%div{ data: { file_body: '' } }
|
||||
-# extra wrapper needed so hidden state doesn't require removing border or other styles
|
||||
%details.rd-diff-file-details{ open: true, data: { file_body: '' } }
|
||||
%summary>
|
||||
.rd-diff-file-body
|
||||
= render viewer_component.new(diff_file: @diff_file)
|
||||
%diff-file-mounted
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@
|
|||
= _('Update selected')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-bulk-update-menu-hide gl-float-right' }) do
|
||||
= _('Cancel')
|
||||
- if is_issue
|
||||
= render_if_exists 'shared/issuable/status_dropdown', parent: @project.group
|
||||
.block.js-status-dropdown-container
|
||||
.title
|
||||
= _('Status')
|
||||
= _('State')
|
||||
.js-status-dropdown
|
||||
.block
|
||||
.title
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ not be replicated.
|
|||
{{< /alert >}}
|
||||
|
||||
These instructions help set up a single PostgreSQL database, which creates a single point of failure. To avoid this, you can configure your own clustered
|
||||
PostgreSQL. Support for PostgreSQL replication and failover using the Linux package is proposed in [epic 7814](https://gitlab.com/groups/gitlab-org/-/epics/7814).
|
||||
PostgreSQL.
|
||||
Clustered database support for other databases (for example, Praefect and Geo databases) is proposed in
|
||||
[issue 7292](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7292).
|
||||
|
||||
|
|
|
|||
|
|
@ -45,12 +45,12 @@ For more information, see [GitLab CI/CD variables](../variables/_index.md).
|
|||
```yaml
|
||||
variables:
|
||||
# Configure mysql environment variables (https://hub.docker.com/_/mysql/)
|
||||
MYSQL_DATABASE: $MYSQL_DATABASE
|
||||
MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD
|
||||
MYSQL_DATABASE: $MYSQL_DB
|
||||
MYSQL_ROOT_PASSWORD: $MYSQL_PASS
|
||||
```
|
||||
|
||||
The MySQL container uses `MYSQL_DATABASE` and `MYSQL_ROOT_PASSWORD` to connect to the database.
|
||||
Pass these values by using variables (`$MYSQL_DB` and `$MYSQL_PASS`),
|
||||
Pass these values by using [GitLab CI/CD variables](../variables/_index.md) (`$MYSQL_DB` and `$MYSQL_PASS` in the example above),
|
||||
[rather than calling them directly](https://gitlab.com/gitlab-org/gitlab/-/issues/30178).
|
||||
|
||||
1. Configure your application to use the database, for example:
|
||||
|
|
|
|||
|
|
@ -8,12 +8,6 @@ title: Dependency Proxy
|
|||
The Dependency Proxy is a pull-through-cache for public registry images from DockerHub. This document describes how this
|
||||
feature is constructed in GitLab.
|
||||
|
||||
{{< alert type="note" >}}
|
||||
|
||||
Support for private registry images is proposed in [issue 331741](https://gitlab.com/gitlab-org/gitlab/-/issues/331741).
|
||||
|
||||
{{< /alert >}}
|
||||
|
||||
## Container registry
|
||||
|
||||
The Dependency Proxy for the container registry acts a stand-in for a remote container registry. In our case,
|
||||
|
|
|
|||
|
|
@ -809,8 +809,6 @@ To ensure that both pipeline execution policies and scan execution policies are
|
|||
- Consider using a different strategy for pipeline execution policies, such as `inject_policy`.
|
||||
- If you must use `override_project_ci`, include the scanner templates that you require in your pipeline execution policy to maintain the desired security scans.
|
||||
|
||||
Support for improvements in the integration between these policy types is proposed in [issue 504434](https://gitlab.com/gitlab-org/gitlab/-/issues/504434).
|
||||
|
||||
## Examples
|
||||
|
||||
These examples demonstrate what you can achieve with pipeline execution policies.
|
||||
|
|
|
|||
|
|
@ -683,7 +683,7 @@ Audit event types belong to the following product categories.
|
|||
| Type name | Event triggered when | Saved to database | Introduced in | Scope |
|
||||
|:----------|:---------------------|:------------------|:--------------|:------|
|
||||
| [`email_confirmation_sent`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129261) | Users add or change an email address and it must be confirmed | {{< icon name="dotted-circle" >}} No | GitLab [16.3](https://gitlab.com/gitlab-org/gitlab/-/issues/377625) | User |
|
||||
| [`remove_ssh_key`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65615) | A SSH key is removed | {{< icon name="check-circle" >}} Yes | GitLab [14.1](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) | User |
|
||||
| [`remove_ssh_key`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65615) | An SSH key is removed from a user's profile. Group scope was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195390) for enterprise users in GitLab 18.2. | {{< icon name="check-circle" >}} Yes | GitLab [14.1](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) | User, Group |
|
||||
| [`user_admin_status_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65168) | A user is either made an administrator or removed as an administrator | {{< icon name="check-circle" >}} Yes | GitLab [14.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323905) | User |
|
||||
| [`user_auditor_status_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136456) | A user is either made an auditor or removed as an auditor | {{< icon name="check-circle" >}} Yes | GitLab [16.6](https://gitlab.com/gitlab-org/gitlab/-/issues/430235) | User |
|
||||
| [`user_email_address_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2103) | A user updates their email address | {{< icon name="check-circle" >}} Yes | GitLab [10.1](https://gitlab.com/gitlab-org/gitlab-ee/issues/1370) | User |
|
||||
|
|
|
|||
|
|
@ -605,7 +605,7 @@ Support for creating a retention policy for job logs is proposed in [issue 37471
|
|||
|
||||
### Delete old pipelines
|
||||
|
||||
Pipelines do not add to the overall storage usage, but if required you can automate their deletion.
|
||||
Pipelines do not add to the overall storage usage, but if required you can [automate their deletion](../ci/pipelines/settings.md#automatic-pipeline-cleanup).
|
||||
|
||||
To delete pipelines based on a specific date, specify the `created_at` key.
|
||||
You can use the date to calculate the difference between the current date and
|
||||
|
|
@ -802,8 +802,6 @@ the `created_at` attribute to implement a similar algorithm that compares the jo
|
|||
|
||||
{{< /tabs >}}
|
||||
|
||||
Automatic deletion of old pipelines is proposed in [issue 338480](https://gitlab.com/gitlab-org/gitlab/-/issues/338480).
|
||||
|
||||
### List expiry settings for job artifacts
|
||||
|
||||
To manage artifact storage, you can update or configure when an artifact expires.
|
||||
|
|
|
|||
|
|
@ -12501,7 +12501,7 @@ msgstr ""
|
|||
msgid "Change severity"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change status"
|
||||
msgid "Change state"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change subscription"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ RSpec.shared_context "with diff file component tests" do
|
|||
render_component
|
||||
expect(page).to have_selector(web_component_selector)
|
||||
expect(page).to have_selector("#{web_component_selector}-mounted")
|
||||
expect(page).to have_selector("details[data-file-body]")
|
||||
end
|
||||
|
||||
it "renders server data" do
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ RSpec.describe 'Multiple issue updating from issues#index', :js, feature_categor
|
|||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'status' do
|
||||
context 'state' do
|
||||
it 'sets to closed', :js do
|
||||
visit project_issues_path(project)
|
||||
|
||||
click_button 'Bulk edit'
|
||||
check 'Select all'
|
||||
select_from_listbox('Closed', from: 'Select status')
|
||||
select_from_listbox('Closed', from: 'Select state')
|
||||
|
||||
click_update_issues_button
|
||||
expect(page).to have_selector('.issue', count: 0)
|
||||
|
|
@ -37,7 +37,7 @@ RSpec.describe 'Multiple issue updating from issues#index', :js, feature_categor
|
|||
|
||||
click_button 'Bulk edit'
|
||||
check 'Select all'
|
||||
select_from_listbox('Open', from: 'Select status')
|
||||
select_from_listbox('Open', from: 'Select state')
|
||||
|
||||
click_update_issues_button
|
||||
expect(page).to have_selector('.issue', count: 0)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ RSpec.describe 'Merge requests > User mass updates', :js, feature_category: :cod
|
|||
|
||||
click_button 'Bulk edit'
|
||||
|
||||
expect(page).not_to have_button 'Select status'
|
||||
expect(page).not_to have_button 'Select state'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ RSpec.describe 'Merge requests > User mass updates', :js, feature_category: :cod
|
|||
def change_status(text)
|
||||
click_button 'Bulk edit'
|
||||
check 'Select all'
|
||||
select_from_listbox(text, from: 'Select status')
|
||||
select_from_listbox(text, from: 'Select state')
|
||||
click_update_merge_requests_button
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ describe('DiffFile Web Component', () => {
|
|||
`;
|
||||
let app;
|
||||
let adapter;
|
||||
let adapterMountedCleanup;
|
||||
|
||||
const getDiffElement = () => document.querySelector('[id=foo]');
|
||||
const getWebComponentElement = () => document.querySelector('diff-file');
|
||||
|
|
@ -29,7 +30,10 @@ describe('DiffFile Web Component', () => {
|
|||
},
|
||||
visible: jest.fn(),
|
||||
invisible: jest.fn(),
|
||||
mounted: jest.fn(),
|
||||
mounted: jest.fn((onUnmounted) => {
|
||||
adapterMountedCleanup = jest.fn();
|
||||
onUnmounted(adapterMountedCleanup);
|
||||
}),
|
||||
});
|
||||
|
||||
const initRapidDiffsApp = (currentAdapter = createDefaultAdapter(), appData = {}) => {
|
||||
|
|
@ -77,6 +81,10 @@ describe('DiffFile Web Component', () => {
|
|||
customElements.define('diff-file', DiffFile);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
adapterMountedCleanup = undefined;
|
||||
});
|
||||
|
||||
it('observes diff element', () => {
|
||||
mount();
|
||||
expect(app.observe).toHaveBeenCalledWith(getWebComponentElement());
|
||||
|
|
@ -98,6 +106,7 @@ describe('DiffFile Web Component', () => {
|
|||
const element = getWebComponentElement();
|
||||
document.body.innerHTML = '';
|
||||
expect(app.unobserve).toHaveBeenCalledWith(element);
|
||||
expect(adapterMountedCleanup).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can self replace', () => {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ describe('Diff File Toggle Behavior', () => {
|
|||
<button data-opened="" data-click="toggleFile" aria-label="Hide file contents" type="button"></button>
|
||||
<button data-closed="" data-click="toggleFile" aria-label="Show file contents" type="button"></button>
|
||||
</div>
|
||||
<div data-file-body=""><!-- body content --></div>
|
||||
<details open data-file-body=""><summary></summary><div><!-- body content --></div></details>
|
||||
</div>
|
||||
</diff-file>
|
||||
`;
|
||||
|
|
@ -67,19 +67,19 @@ describe('Diff File Toggle Behavior', () => {
|
|||
|
||||
delegatedClick(hide);
|
||||
|
||||
expect(body.hidden).toEqual(true);
|
||||
expect(body.open).toEqual(false);
|
||||
expect(document.activeElement).toEqual(show);
|
||||
});
|
||||
|
||||
it('collapses file', () => {
|
||||
get('file').trigger(COLLAPSE_FILE);
|
||||
expect(get('body').hidden).toEqual(true);
|
||||
expect(get('body').open).toEqual(false);
|
||||
expect(get('file').diffElement.dataset.collapsed).toEqual('true');
|
||||
});
|
||||
|
||||
it('expands file', () => {
|
||||
get('file').trigger(EXPAND_FILE);
|
||||
expect(get('body').hidden).toEqual(false);
|
||||
expect(get('body').open).toEqual(true);
|
||||
expect(get('file').diffElement.dataset.collapsed).not.toEqual('true');
|
||||
});
|
||||
|
||||
|
|
@ -99,4 +99,10 @@ describe('Diff File Toggle Behavior', () => {
|
|||
tick();
|
||||
expect(get('hide').style.transition).toBe('');
|
||||
});
|
||||
|
||||
it('removes events listeners from body', () => {
|
||||
const spy = jest.spyOn(get('body'), 'removeEventListener');
|
||||
get('file').remove();
|
||||
expect(spy).toHaveBeenCalledWith('toggle', expect.any(Function));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ describe('SubscriptionsDropdown component', () => {
|
|||
});
|
||||
|
||||
it('renders default text', () => {
|
||||
expect(findDropdown().props('toggleText')).toBe('Select status');
|
||||
expect(findDropdown().props('toggleText')).toBe('Select state');
|
||||
});
|
||||
|
||||
it('renders dropdown items with `isSelected` prop set to `false`', () => {
|
||||
|
|
@ -74,7 +74,7 @@ describe('SubscriptionsDropdown component', () => {
|
|||
|
||||
expect(dropdownItems.at(0).props('isSelected')).toBe(false);
|
||||
expect(dropdownItems.at(1).props('isSelected')).toBe(false);
|
||||
expect(findDropdown().props('toggleText')).toBe('Select status');
|
||||
expect(findDropdown().props('toggleText')).toBe('Select state');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue