Skip to content

fix(cdk/drag-drop): preserve checked state for grouped radio inputs #20237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 19, 2020
Merged
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
12 changes: 11 additions & 1 deletion src/cdk/drag-drop/clone-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,19 @@ function transferData<T extends Element>(selector: string, node: HTMLElement, cl
}
}

// Counter for unique cloned radio button names.
let cloneUniqueId = 0;

/** Transfers the data of one input element to another. */
function transferInputData(source: Element & {value: string}, clone: Element & {value: string}) {
function transferInputData(source: Element & {value: string},
clone: Element & {value: string; name: string; type: string}) {
clone.value = source.value;
// Radio button `name` attributes must be unique for radio button groups
// otherwise original radio buttons can lose their checked state
// once the clone is inserted in the DOM.
if (clone.type === 'radio' && clone.name) {
clone.name = `mat-clone-${clone.name}-${cloneUniqueId++}`;
}
}

/** Transfers the data of one canvas element to another. */
Expand Down
56 changes: 56 additions & 0 deletions src/cdk/drag-drop/directives/drag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2272,6 +2272,37 @@ describe('CdkDrag', () => {
expect(previewSelect.value).toBe(value);
}));

it('should preserve checked state for radio inputs in the content', fakeAsync(() => {
const fixture = createComponent(DraggableWithRadioInputsInDropZone);
fixture.detectChanges();
const item = fixture.componentInstance.dragItems.toArray()[2].element.nativeElement;
const sourceRadioInput =
item.querySelector<HTMLInputElement>('input[type="radio"]')!;

expect(sourceRadioInput.checked).toBeTruthy();

startDraggingViaMouse(fixture, item);

const preview = document.querySelector('.cdk-drag-preview')!;
const previewRadioInput = preview.querySelector<HTMLInputElement>('input[type="radio"]')!;
expect(previewRadioInput.checked).toBeTruthy(
'Expected cloned radio input in preview has the same state as original radio input');

const placeholder = document.querySelector('.cdk-drag-placeholder')!;
const placeholderRadioInput =
placeholder.querySelector<HTMLInputElement>('input[type="radio"]')!;
expect(placeholderRadioInput.checked).toBeTruthy(
'Expected cloned radio input in placeholder has the same state as original radio input');

dispatchMouseEvent(document, 'mouseup');
// Important to tick with 0 since we don't want to flush any pending timeouts.
// It also makes sure that all clones have been removed from the DOM.
tick(0);

expect(sourceRadioInput.checked)
.toBeTruthy('Expected original radio input has preserved its original checked state');
}));

it('should clear the ids from descendants of the preview', fakeAsync(() => {
const fixture = createComponent(DraggableInDropZone);
fixture.detectChanges();
Expand Down Expand Up @@ -6463,6 +6494,31 @@ class DraggableWithInputsInDropZone extends DraggableInDropZone {
}


@Component({
template: `
<div
cdkDropList
class="drop-list scroll-container"
[cdkDropListData]="items">
<div
*ngFor="let item of items"
cdkDrag
[cdkDragData]="item">
{{item.id}}
<input type="radio" name="radio" [checked]="item.checked"/>
</div>
</div>
`
})
class DraggableWithRadioInputsInDropZone {
@ViewChildren(CdkDrag) dragItems: QueryList<CdkDrag>;
items = [
{id: 1, checked: false},
{id: 2, checked: false},
{id: 3, checked: true},
];
}

/**
* Drags an element to a position on the page using the mouse.
* @param fixture Fixture on which to run change detection.
Expand Down