diff --git a/app/javascript/packages/document-capture/components/acuant-capture.jsx b/app/javascript/packages/document-capture/components/acuant-capture.jsx index 54dddca2561..43f6a51dbf4 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.jsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.jsx @@ -102,6 +102,11 @@ import './acuant-capture.scss'; * @prop {string} name Prefix to prepend to user action analytics labels. */ +/** + * A noop function. + */ +const noop = () => {}; + /** * Returns true if the given Acuant capture failure was caused by the user declining access to the * camera, or false otherwise. @@ -339,15 +344,16 @@ function AcuantCapture( * @template {(...args: any[]) => any} T * * @param {string} source Click source. + * @param {{isDrop: boolean}=} metadata Additional payload metadata to log. * * @return {(fn: T) => (...args: Parameters) => ReturnType} */ - function withLoggedClick(source) { + function withLoggedClick(source, metadata = { isDrop: false }) { return (fn) => (...args) => { if (!isSuppressingClickLogging.current) { addPageAction({ label: `IdV: ${name} image clicked`, - payload: { source }, + payload: { source, ...metadata }, }); } @@ -521,6 +527,7 @@ function AcuantCapture( value={value} errorMessage={ownErrorMessage ?? errorMessage} onClick={withLoggedClick('placeholder')(startCaptureOrTriggerUpload)} + onDrop={withLoggedClick('placeholder', { isDrop: true })(noop)} onChange={onUpload} onError={() => setOwnErrorMessage(null)} /> diff --git a/app/javascript/packages/document-capture/components/file-input.jsx b/app/javascript/packages/document-capture/components/file-input.jsx index 00a13acdd5d..97f7388998a 100644 --- a/app/javascript/packages/document-capture/components/file-input.jsx +++ b/app/javascript/packages/document-capture/components/file-input.jsx @@ -14,6 +14,7 @@ import useInstanceId from '../hooks/use-instance-id'; import usePrevious from '../hooks/use-previous'; /** @typedef {import('react').MouseEvent} ReactMouseEvent */ +/** @typedef {import('react').DragEvent} ReactDragEvent */ /** @typedef {import('react').ChangeEvent} ReactChangeEvent */ /** @typedef {import('react').RefAttributes} ReactRefAttributes */ /** @typedef {import('react').ReactNode} ReactNode */ @@ -31,6 +32,7 @@ import usePrevious from '../hooks/use-previous'; * @prop {Blob|string|null|undefined} value Current value. * @prop {ReactNode=} errorMessage Error to show. * @prop {(event:ReactMouseEvent)=>void=} onClick Input click handler. + * @prop {(event:ReactDragEvent)=>void=} onDrop Input drop handler. * @prop {(nextValue:File?)=>void=} onChange Input change handler. * @prop {(message:ReactNode)=>void=} onError Callback to trigger if upload error occurs. */ @@ -108,7 +110,8 @@ function FileInput(props, ref) { capture, value, errorMessage, - onClick = () => {}, + onClick, + onDrop, onChange = () => {}, onError = () => {}, } = props; @@ -278,6 +281,7 @@ function FileInput(props, ref) { onChange={onChangeIfValid} capture={capture} onClick={onClick} + onDrop={onDrop} accept={accept ? accept.join() : undefined} aria-describedby={hint ? hintId : undefined} /> diff --git a/spec/javascripts/packages/document-capture/components/acuant-capture-spec.jsx b/spec/javascripts/packages/document-capture/components/acuant-capture-spec.jsx index 743e1843402..1248f3b3d4b 100644 --- a/spec/javascripts/packages/document-capture/components/acuant-capture-spec.jsx +++ b/spec/javascripts/packages/document-capture/components/acuant-capture-spec.jsx @@ -867,18 +867,43 @@ describe('document-capture/components/acuant-capture', () => { label: 'IdV: test image clicked', payload: { source: 'placeholder', + isDrop: false, }, }); expect(addPageAction.getCall(1)).to.have.been.calledWith({ label: 'IdV: test image clicked', payload: { source: 'button', + isDrop: false, }, }); expect(addPageAction.getCall(2)).to.have.been.calledWith({ label: 'IdV: test image clicked', payload: { source: 'upload', + isDrop: false, + }, + }); + }); + + it('logs drag-and-drop as click interaction', () => { + const addPageAction = sinon.stub(); + const { getByLabelText } = render( + + + + + , + ); + + const input = getByLabelText('Image'); + fireEvent.drop(input); + + expect(addPageAction.getCall(0)).to.have.been.calledWith({ + label: 'IdV: test image clicked', + payload: { + source: 'placeholder', + isDrop: true, }, }); }); diff --git a/spec/javascripts/packages/document-capture/components/file-input-spec.jsx b/spec/javascripts/packages/document-capture/components/file-input-spec.jsx index 0df7062fd26..61fa91b988e 100644 --- a/spec/javascripts/packages/document-capture/components/file-input-spec.jsx +++ b/spec/javascripts/packages/document-capture/components/file-input-spec.jsx @@ -232,6 +232,26 @@ describe('document-capture/components/file-input', () => { expect(onChange.getCall(0).args[0]).to.equal(file); }); + it('calls onClick when clicked', () => { + const onClick = sinon.stub(); + const { getByLabelText } = render(); + + const input = getByLabelText('File'); + userEvent.click(input); + + expect(onClick).to.have.been.calledOnce(); + }); + + it('calls onDrop when receiving drop event', () => { + const onDrop = sinon.stub(); + const { getByLabelText } = render(); + + const input = getByLabelText('File'); + fireEvent.drop(input); + + expect(onDrop).to.have.been.calledOnce(); + }); + it('allows changing the selected value', () => { const file2 = new window.File([file], 'file2.jpg'); const onChange = sinon.stub();