mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
Merge pull request #23139 from opf/merge-release/17.4-20260508131130
Merge release/17.4 into dev
This commit is contained in:
@@ -297,7 +297,7 @@ module OpenProject
|
||||
{
|
||||
# Existing callers share one mirror container target on the page until
|
||||
# parent-specific DnD handling is extracted in follow-up work.
|
||||
generic_drag_and_drop_target: "container mirrorContainer",
|
||||
generic_drag_and_drop_target: "container",
|
||||
target_container_accessor: ":scope > ul",
|
||||
target_id: drag_and_drop.fetch(:target_id),
|
||||
target_allowed_drag_type: drag_and_drop.fetch(:allowed_drag_type)
|
||||
|
||||
@@ -89,7 +89,15 @@ Click **Continue** to proceed.
|
||||
|
||||
### Define project details
|
||||
|
||||
Next, enter the **name** and an optional **description** for your project. You can also integrate the project into your existing project hierarchy by selecting a **parent project**, which will make the new project a **subproject**.
|
||||
Next, enter the **name** for your project.
|
||||
|
||||
Based on the project name, OpenProject automatically suggests an **identifier**. You can edit the suggested identifier manually if needed. The identifier is validated automatically to ensure it follows the required rules.
|
||||
|
||||
> [!NOTE]
|
||||
> The identifier suggestion updates automatically when you change the project name.
|
||||
> If you manually edit the identifier and later go back to change the project name again, OpenProject updates the identifier suggestion once more.
|
||||
|
||||
You can also optionally enter a **description** for your project. You can also integrate the project into your existing project hierarchy by selecting a **parent project**, which will make the new project a **subproject**.
|
||||
|
||||
Click **Complete** to finish the setup.
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 42 KiB |
@@ -63,12 +63,6 @@ describe('GenericDragAndDropController', () => {
|
||||
return ariaPressedTarget.call(controller, el);
|
||||
}
|
||||
|
||||
function callResolveMirrorContainer():Element {
|
||||
const resolveMirrorContainer = Reflect.get(controller, 'resolveMirrorContainer') as (this:GenericDragAndDropController) => Element;
|
||||
|
||||
return resolveMirrorContainer.call(controller);
|
||||
}
|
||||
|
||||
describe('canStartDrag', () => {
|
||||
it('allows dragging a draggable row in handle-less mode', () => {
|
||||
const row = draggableRow();
|
||||
@@ -146,21 +140,4 @@ describe('GenericDragAndDropController', () => {
|
||||
expect(callAriaPressedTarget(row)).toBe(handle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveMirrorContainer', () => {
|
||||
it('returns the configured mirror container target when present', () => {
|
||||
const mirrorContainer = document.createElement('div');
|
||||
|
||||
Object.defineProperty(controller, 'hasMirrorContainerTarget', { value: true, configurable: true });
|
||||
Object.defineProperty(controller, 'mirrorContainerTarget', { value: mirrorContainer, configurable: true });
|
||||
|
||||
expect(callResolveMirrorContainer()).toBe(mirrorContainer);
|
||||
});
|
||||
|
||||
it('falls back to document.body when no mirror container target exists', () => {
|
||||
Object.defineProperty(controller, 'hasMirrorContainerTarget', { value: false, configurable: true });
|
||||
|
||||
expect(callResolveMirrorContainer()).toBe(document.body);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,12 +43,10 @@ interface TargetConfig {
|
||||
}
|
||||
|
||||
export default class GenericDragAndDropController extends Controller {
|
||||
static targets = ['container', 'scrollContainer', 'mirrorContainer'];
|
||||
static targets = ['container', 'scrollContainer'];
|
||||
|
||||
containerTargets:HTMLElement[];
|
||||
scrollContainerTargets:HTMLElement[];
|
||||
declare readonly hasMirrorContainerTarget:boolean;
|
||||
declare readonly mirrorContainerTarget:HTMLElement;
|
||||
|
||||
static values = {
|
||||
handle: { type: Boolean, default: true },
|
||||
@@ -129,7 +127,6 @@ export default class GenericDragAndDropController extends Controller {
|
||||
moves: (el, _source, handle, _sibling) => this.canStartDrag(el, handle),
|
||||
accepts: (el:Element, target:Element, source:Element, sibling:Element) => this.accepts(el, target, source, sibling),
|
||||
revertOnSpill: true, // enable reverting of elements if they are dropped outside of a valid target
|
||||
mirrorContainer: this.resolveMirrorContainer(),
|
||||
},
|
||||
)
|
||||
.on('cloned', (clone, _original, type) => {
|
||||
@@ -260,10 +257,6 @@ export default class GenericDragAndDropController extends Controller {
|
||||
return container;
|
||||
}
|
||||
|
||||
private resolveMirrorContainer():Element {
|
||||
return this.hasMirrorContainerTarget ? this.mirrorContainerTarget : document.body;
|
||||
}
|
||||
|
||||
// Returns the data-draggable-id of the element preceding el in its container,
|
||||
// or null if el is the first item (signals "move to top").
|
||||
private resolveTargetPrevious(el:Element):string|null {
|
||||
|
||||
@@ -119,7 +119,7 @@ RSpec.describe Backlogs::BucketComponent, type: :component do
|
||||
|
||||
it "wires the bucket drop-target data on the box" do
|
||||
expect(rendered_component).to have_css(".Box") do |box|
|
||||
expect(box["data-generic-drag-and-drop-target"]).to eq("container mirrorContainer")
|
||||
expect(box["data-generic-drag-and-drop-target"]).to eq("container")
|
||||
expect(box["data-target-id"]).to eq("backlog_bucket:#{backlog_bucket.id}")
|
||||
expect(box["data-target-allowed-drag-type"]).to eq("story")
|
||||
end
|
||||
|
||||
@@ -64,7 +64,7 @@ RSpec.describe Backlogs::InboxComponent, type: :component do
|
||||
|
||||
it "wires drop-target data attributes for the inbox" do
|
||||
expect(page).to have_css(".Box#inbox_project_#{project.id}") do |box|
|
||||
expect(box["data-generic-drag-and-drop-target"]).to eq("container mirrorContainer")
|
||||
expect(box["data-generic-drag-and-drop-target"]).to eq("container")
|
||||
expect(box["data-target-id"]).to eq("inbox")
|
||||
expect(box["data-target-allowed-drag-type"]).to eq("story")
|
||||
end
|
||||
|
||||
@@ -94,7 +94,7 @@ RSpec.describe Backlogs::SprintComponent, type: :component do
|
||||
|
||||
it "wires drop-target data attributes for the sprint" do
|
||||
expect(rendered_component).to have_css(".Box") do |box|
|
||||
expect(box["data-generic-drag-and-drop-target"]).to eq("container mirrorContainer")
|
||||
expect(box["data-generic-drag-and-drop-target"]).to eq("container")
|
||||
expect(box["data-target-container-accessor"]).to eq(":scope > ul")
|
||||
expect(box["data-target-id"]).to eq("sprint:#{sprint.id}")
|
||||
expect(box["data-target-allowed-drag-type"]).to eq("story")
|
||||
|
||||
@@ -106,26 +106,6 @@ RSpec.describe "Backlogs::Backlog", :skip_csrf, type: :rails_request do
|
||||
expect(response.body).to include('id="sprint_backlogs_container"')
|
||||
end
|
||||
end
|
||||
|
||||
it "uses the inbox border box as the drag mirror container" do
|
||||
get "/projects/#{project.identifier}/backlogs/backlog", headers: { "Turbo-Frame" => "backlogs_container" }
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include(%(id="inbox_project_#{project.id}"))
|
||||
expect(response.body).to include('data-generic-drag-and-drop-target="container mirrorContainer"')
|
||||
end
|
||||
|
||||
context "with backlog buckets enabled", with_flag: { backlog_buckets: true } do
|
||||
shared_let(:backlog_bucket) { create(:backlog_bucket, project:) }
|
||||
|
||||
it "uses each backlog bucket border box as the drag mirror container" do
|
||||
get "/projects/#{project.identifier}/backlogs/backlog", headers: { "Turbo-Frame" => "backlogs_container" }
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include(%(data-test-selector="backlog-bucket-#{backlog_bucket.id}"))
|
||||
expect(response.body).to include('data-generic-drag-and-drop-target="container mirrorContainer"')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ RSpec.describe OpenProject::Common::WorkPackageCardBoxComponent, type: :componen
|
||||
|
||||
it "uses the configured drag-and-drop data" do
|
||||
expect(rendered_component).to have_css(".Box") do |box|
|
||||
expect(box["data-generic-drag-and-drop-target"]).to eq("container mirrorContainer")
|
||||
expect(box["data-generic-drag-and-drop-target"]).to eq("container")
|
||||
expect(box["data-target-container-accessor"]).to eq(":scope > ul")
|
||||
expect(box["data-target-id"]).to eq("sprint:#{sprint.id}")
|
||||
expect(box["data-target-allowed-drag-type"]).to eq("story")
|
||||
|
||||
Reference in New Issue
Block a user