Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1002,9 +1002,11 @@ describe('WorkPackageCardDragAndDropService — real two-list directive fixture'
standalone: true,
imports: [OpSortableListsDirective, TestCardListHostComponent],
template: `
<div class="root" opSortableLists>
<op-test-sortable-card-list listId="list-a" [mobile]="mobileA()" />
<op-test-sortable-card-list listId="list-b" [mobile]="mobileB()" />
<div class="scroll-host" style="overflow: auto;">
<div class="root" opSortableLists>
<op-test-sortable-card-list listId="list-a" [mobile]="mobileA()" />
<op-test-sortable-card-list listId="list-b" [mobile]="mobileB()" />
</div>
</div>
`,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,9 @@ import {
type SortableListsRemovedEvent,
} from './sortable-lists.directive';

// Deliberately does NOT `vi.mock` Pragmatic's autoscroll module: under the
// repo's `isolate:false` vitest config, spec files share a module registry
// within a worker, and this specifier is also `vi.mock`-ed (independently)
// by `sortable-lists-engine.spec.ts` for its own, unrelated engine-level
// tests. Two spec files independently mocking the same specifier crashes the
// shared worker (an unhandled "error when mocking a module" rejection,
// reproducible regardless of factory shape); the mock is also effectively
// global for the whole worker once registered, so even a "compatible" mock
// here would fight with that file's synthetic (non-real) replacement in
// combined runs. Instead this suite observes the REAL implementation's own
// documented side effects directly:
// Deliberately does NOT `vi.mock` Pragmatic's autoscroll module: this suite
// observes the REAL implementation's own documented side effects, which is
// what makes the scroll-container assertions below meaningful.
// - the scroll-fallback tests below assert on `data-auto-scrollable`, the
// attribute Pragmatic's real `autoScrollForElements` sets on registration
// and removes on cleanup (see `@atlaskit/pragmatic-drag-and-drop-auto-scroll`'s
Expand All @@ -71,20 +63,22 @@ describe('sortable-lists directive group behavior', () => {
@Component({
imports: [OpSortableListsDirective, OpSortableListsItemDirective],
template: `
<div
class="root"
opSortableLists
(opSortableListsDrop)="drops.push($event)"
style="padding-bottom: 40px;"
>
@for (entry of items(); track entry.id) {
<div
class="item"
[opSortableListsItem]="entry.id"
[opSortableListsItemCanDrag]="entry.canDrag"
style="height: 40px; width: 200px;"
>{{ entry.id }}</div>
}
<div class="scroll-host" style="overflow: auto;">
<div
class="root"
opSortableLists
(opSortableListsDrop)="drops.push($event)"
style="padding-bottom: 40px;"
>
@for (entry of items(); track entry.id) {
<div
class="item"
[opSortableListsItem]="entry.id"
[opSortableListsItemCanDrag]="entry.canDrag"
style="height: 40px; width: 200px;"
>{{ entry.id }}</div>
}
</div>
</div>
`,
})
Expand Down Expand Up @@ -225,10 +219,12 @@ describe('sortable-lists directive group behavior', () => {
@Component({
imports: [OpSortableListsDirective, OpSortableListsListDirective, OpSortableListsItemDirective],
template: `
<div class="root" opSortableLists opSortableListsList>
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
<div class="scroll-host" style="overflow: auto;">
<div class="root" opSortableLists opSortableListsList>
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
</div>
`,
})
Expand Down Expand Up @@ -291,29 +287,31 @@ describe('sortable-lists directive group behavior', () => {
@Component({
imports: [OpSortableListsDirective, OpSortableListsListDirective, OpSortableListsItemDirective],
template: `
<div class="root" opSortableLists>
<div
class="list-a"
opSortableListsList
opSortableListsListId="list-a"
(opSortableListsDrop)="events.push({ type: 'drop', listId: 'a', event: $event })"
(opSortableListsRemoved)="events.push({ type: 'removed', listId: 'a', event: $event })"
>
@for (id of idsA(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
<div class="scroll-host" style="overflow: auto;">
<div class="root" opSortableLists>
<div
class="list-a"
opSortableListsList
opSortableListsListId="list-a"
(opSortableListsDrop)="events.push({ type: 'drop', listId: 'a', event: $event })"
(opSortableListsRemoved)="events.push({ type: 'removed', listId: 'a', event: $event })"
>
@for (id of idsA(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>

<div
class="list-b"
opSortableListsList
opSortableListsListId="list-b"
(opSortableListsDrop)="events.push({ type: 'drop', listId: 'b', event: $event })"
(opSortableListsRemoved)="events.push({ type: 'removed', listId: 'b', event: $event })"
>
@for (id of idsB(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
<div
class="list-b"
opSortableListsList
opSortableListsListId="list-b"
(opSortableListsDrop)="events.push({ type: 'drop', listId: 'b', event: $event })"
(opSortableListsRemoved)="events.push({ type: 'removed', listId: 'b', event: $event })"
>
@for (id of idsB(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
</div>
</div>
`,
Expand Down Expand Up @@ -409,22 +407,24 @@ describe('sortable-lists directive group behavior', () => {
@Component({
imports: [OpSortableListsDirective, OpSortableListsListDirective, OpSortableListsItemDirective],
template: `
<div class="root" opSortableLists (opSortableListsDrop)="rootDrops.push($event)">
@if (explicit()) {
<div
class="list"
opSortableListsList
(opSortableListsDrop)="listDrops.push($event)"
>
<div class="scroll-host" style="overflow: auto;">
<div class="root" opSortableLists (opSortableListsDrop)="rootDrops.push($event)">
@if (explicit()) {
<div
class="list"
opSortableListsList
(opSortableListsDrop)="listDrops.push($event)"
>
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
} @else {
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
} @else {
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
}
</div>
</div>
`,
})
Expand Down Expand Up @@ -495,16 +495,23 @@ describe('sortable-lists directive group behavior', () => {
});

describe('nested roots', () => {
// Two independent roots each need their own closest-scrollable-ancestor,
// or both walks terminate on the same element and Pragmatic ends up with
// two registrations on one node — see sortable-lists-engine.ts:142-149.
@Component({
imports: [OpSortableListsDirective, OpSortableListsItemDirective],
template: `
<div class="outer-root" opSortableLists (opSortableListsDrop)="outerDrops.push($event)">
<div class="outer-item" opSortableListsItem="outer-a" style="height: 40px; width: 200px;">outer-a</div>

<div class="inner-root" opSortableLists (opSortableListsDrop)="innerDrops.push($event)">
@for (id of innerIds(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
<div class="scroll-host" style="overflow: auto;">
<div class="outer-root" opSortableLists (opSortableListsDrop)="outerDrops.push($event)">
<div class="outer-item" opSortableListsItem="outer-a" style="height: 40px; width: 200px;">outer-a</div>

<div class="inner-scroll-host" style="overflow: auto;">
<div class="inner-root" opSortableLists (opSortableListsDrop)="innerDrops.push($event)">
@for (id of innerIds(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
</div>
</div>
</div>
`,
Expand Down Expand Up @@ -548,27 +555,35 @@ describe('sortable-lists directive group behavior', () => {
// list's id — DI's nearest-list lookup does not stop at the inner root,
// so without the ownership check it would register against the inner
// engine under an id that engine never registered a list for.
//
// The inner wrapper also gives the inner root its own scrollable
// ancestor, distinct from the outer root's, so the two don't collide on
// one Pragmatic registration — see sortable-lists-engine.ts:142-149.
@Component({
imports: [OpSortableListsDirective, OpSortableListsListDirective, OpSortableListsItemDirective],
template: `
<div class="outer-root" opSortableLists>
<div
class="outer-list"
opSortableListsList
opSortableListsListId="outer-list"
(opSortableListsDrop)="outerListDrops.push($event)"
(opSortableListsRemoved)="outerListRemoved.push($event)"
>
<div class="scroll-host" style="overflow: auto;">
<div class="outer-root" opSortableLists>
<div
class="inner-root"
opSortableLists
(opSortableListsDrop)="innerDrops.push($event)"
(opSortableListsRemoved)="innerRemoved.push($event)"
style="padding-bottom: 40px;"
class="outer-list"
opSortableListsList
opSortableListsListId="outer-list"
(opSortableListsDrop)="outerListDrops.push($event)"
(opSortableListsRemoved)="outerListRemoved.push($event)"
>
@for (id of innerIds(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
<div class="inner-scroll-host" style="overflow: auto;">
<div
class="inner-root"
opSortableLists
(opSortableListsDrop)="innerDrops.push($event)"
(opSortableListsRemoved)="innerRemoved.push($event)"
style="padding-bottom: 40px;"
>
@for (id of innerIds(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -637,10 +652,12 @@ describe('sortable-lists directive group behavior', () => {
@Component({
imports: [OpSortableListsDirective, OpSortableListsListDirective, OpSortableListsItemDirective],
template: `
<div class="root" opSortableLists opSortableListsList>
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
<div class="scroll-host" style="overflow: auto;">
<div class="root" opSortableLists opSortableListsList>
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
</div>
`,
})
Expand Down Expand Up @@ -778,20 +795,22 @@ describe('sortable-lists directive group behavior', () => {
@Component({
imports: [OpSortableListsDirective, OpSortableListsItemDirective],
template: `
<div
class="root"
opSortableLists
[opSortableListsAxis]="'horizontal'"
(opSortableListsDrop)="drops.push($event)"
style="white-space: nowrap;"
>
@for (id of ids(); track id) {
<div
class="item"
[opSortableListsItem]="id"
style="display: inline-block; width: 40px; height: 20px;"
>{{ id }}</div>
}
<div class="scroll-host" style="overflow: auto;">
<div
class="root"
opSortableLists
[opSortableListsAxis]="'horizontal'"
(opSortableListsDrop)="drops.push($event)"
style="white-space: nowrap;"
>
@for (id of ids(); track id) {
<div
class="item"
[opSortableListsItem]="id"
style="display: inline-block; width: 40px; height: 20px;"
>{{ id }}</div>
}
</div>
</div>
`,
})
Expand All @@ -804,10 +823,12 @@ describe('sortable-lists directive group behavior', () => {
@Component({
imports: [OpSortableListsDirective, OpSortableListsItemDirective],
template: `
<div class="root" opSortableLists (opSortableListsDrop)="drops.push($event)">
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
<div class="scroll-host" style="overflow: auto;">
<div class="root" opSortableLists (opSortableListsDrop)="drops.push($event)">
@for (id of ids(); track id) {
<div class="item" [opSortableListsItem]="id" style="height: 40px; width: 200px;">{{ id }}</div>
}
</div>
</div>
`,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ function buildRow(id:string, opts:{ handle?:boolean } = {}):HTMLElement {

function buildContainer(ids:string[], opts:{ handle?:boolean } = {}):{ container:HTMLElement; rows:HTMLElement[] } {
const container = document.createElement('div');
container.style.cssText = 'width:200px;';
// `overflow-y` with no height keeps the container a scroll container for
// Pragmatic's computed-overflow check. This also computes overflow-x to
// `auto`, but rows are exactly 200px wide in a 200px container.
container.style.cssText = 'width:200px; overflow-y:auto;';
Comment thread
myabc marked this conversation as resolved.
const rows = ids.map((id) => {
const row = buildRow(id, opts);
container.appendChild(row);
Expand Down Expand Up @@ -159,7 +162,7 @@ describe('DragAndDropService', () => {
describe('container append', () => {
it('reports a drop below the rows as a null-target append intent', async () => {
const { container, rows } = buildContainer(['a0', 'a1']);
container.style.cssText = 'width:200px; padding-bottom:40px;';
container.style.cssText = 'width:200px; padding-bottom:40px; overflow-y:auto;';
const onMoved = vi.fn();
service.register(buildMember(container, { onMoved }));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ describe('createSortableRoot drag preview offset', () => {

function setup():{ rows:HTMLElement[] } {
const root = document.createElement('div');
// The engine attaches auto-scroll to this element directly, with no
// ancestor walk, so the overflow has to sit here. This also computes
// overflow-x to auto; 600px rows just happen to fit the viewport.
root.style.cssText = 'overflow-y:auto;';
Comment thread
myabc marked this conversation as resolved.
const rows = ['a', 'b'].map((id) => {
const row = document.createElement('div');
row.style.cssText = 'height:40px; width:600px;';
Expand Down
Loading