Skip to content

Commit

Permalink
fix(form): prevent drop event propagating outside of EditPortal compo…
Browse files Browse the repository at this point in the history
…nent (#5813)

* feat(core): allow `onDrop` prop in `Dialog` component

* fix(form): prevent `drop` event propagating outside of `EditPortal` component

* feat(e2e): add e2e helper for creating `DataTransfer` instances for buffers

* test(core): ensure `drop` event does not propagate outside of `EditPortal` component

* test(core): fix typo

* test(core): reorder assertions for clarity
  • Loading branch information
juice49 authored Feb 29, 2024
1 parent 16a5434 commit fc73437
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 1 deletion.
5 changes: 5 additions & 0 deletions packages/sanity/src/core/form/components/EditPortal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ function onDragEnter(event: DragEvent<HTMLDivElement>) {
return event.stopPropagation()
}

function onDrop(event: DragEvent<HTMLDivElement>) {
return event.stopPropagation()
}

export function EditPortal(props: Props): ReactElement {
const {
children,
Expand Down Expand Up @@ -56,6 +60,7 @@ export function EditPortal(props: Props): ReactElement {
onClickOutside={onClose}
onClose={onClose}
onDragEnter={onDragEnter}
onDrop={onDrop}
width={width}
contentRef={setDocumentScrollElement}
__unstable_autoFocus={autofocus}
Expand Down
2 changes: 1 addition & 1 deletion packages/sanity/src/ui-components/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const Dialog = forwardRef(function Dialog(
footer,
padding = true,
...props
}: DialogProps & Pick<React.HTMLProps<HTMLDivElement>, 'onDragEnter'>,
}: DialogProps & Pick<React.HTMLProps<HTMLDivElement>, 'onDragEnter' | 'onDrop'>,
ref: React.Ref<HTMLDivElement>,
) {
const {t} = useTranslation()
Expand Down
33 changes: 33 additions & 0 deletions test/e2e/helpers/createFileDataTransferHandle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {type JSHandle, type Page} from '@playwright/test'

interface Context {
page: Page
}

interface Options {
buffer: Buffer
fileName: string
fileOptions: FilePropertyBag
}

/**
* Create a `DataTransfer` handle containing the provided buffer.
*
* @internal
**/
export function createFileDataTransferHandle(
{page}: Context,
options: Options,
): Promise<JSHandle<DataTransfer>> {
return page.evaluateHandle(
({fileData, fileName, fileOptions}) => {
const dataTransfer = new DataTransfer()
dataTransfer.items.add(new File([new Uint8Array(fileData)], fileName, fileOptions))
return dataTransfer
},
{
...options,
fileData: options.buffer.toJSON().data,
},
)
}
1 change: 1 addition & 0 deletions test/e2e/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './constants'
export * from './createFileDataTransferHandle'
export * from './createUniqueDocument'
export * from './sanityClient'
Binary file added test/e2e/resources/capybara.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions test/e2e/tests/inputs/array.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {readFileSync} from 'node:fs'
import path from 'node:path'

import {expect} from '@playwright/test'
import {test} from '@sanity/test'

import {createFileDataTransferHandle} from '../../helpers'

const fileName = 'capybara.jpg'
const image = readFileSync(path.join(__dirname, '..', '..', 'resources', fileName))

test(`file drop event should not propagate to dialog parent`, async ({
page,
createDraftDocument,
}) => {
await createDraftDocument('/test/content/input-standard;arraysTest')

const list = page.getByTestId('field-arrayOfMultipleTypes').locator('#arrayOfMultipleTypes')
const item = list.locator('[data-ui="Grid"] > div')

const dataTransfer = await createFileDataTransferHandle(
{page},
{
buffer: image,
fileName,
fileOptions: {
type: 'image/jpeg',
},
},
)

// Drop the file.
await list.dispatchEvent('drop', {dataTransfer})

// Ensure the list contains one item.
expect(item).toHaveCount(1)

// Open the dialog.
await page.getByRole('button', {name: fileName}).click()
await expect(page.getByRole('dialog')).toBeVisible()

// Drop the file again; this time, while the dialog is open.
//
// - The drop event should not propagate to the parent.
// - Therefore, the drop event should not cause the image to be added to the list again.
await page.getByRole('dialog').dispatchEvent('drop', {dataTransfer})

// Close the dialog.
await page.keyboard.press('Escape')
await expect(page.getByRole('dialog')).not.toBeVisible()

// Ensure the list still contains one item.
expect(item).toHaveCount(1)
})

0 comments on commit fc73437

Please sign in to comment.