Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
import { useContext, useEffect, useRef } from 'react';
import { useCallback, useContext, useEffect, useRef, useState } from 'react';

import { getAssetPath } from '@18f/identity-assets';
import { useI18n } from '@18f/identity-react-i18n';
import AcuantContext from '../context/acuant';
import {
defineObservableProperty,
stopObservingProperty,
} from '../higher-order/observable-property';
import { useObservableProperty } from '../hooks/use-observable-property';

function AcuantCaptureCanvas() {
const { isReady, acuantCaptureMode, setAcuantCaptureMode } = useContext(AcuantContext);
const { t } = useI18n();
const cameraRef = useRef(/** @type {HTMLDivElement?} */ (null));
const [canvas, setCanvas] = useState(/** @type {HTMLElement? } */ (null));

useEffect(() => {
function onAcuantCameraCreated() {
const canvas = document.getElementById('acuant-ui-canvas');
// Acuant SDK assigns a callback property to the canvas when it switches to its "Tap to
// Capture" mode (Acuant SDK v11.4.4, L158). Infer capture type by presence of the property.
defineObservableProperty(canvas, 'callback', (callback) => {
setAcuantCaptureMode(callback ? 'TAP' : 'AUTO');
});
}

const onAcuantCameraCreated = () => setCanvas(document.getElementById('acuant-ui-canvas'));
cameraRef.current?.addEventListener('acuantcameracreated', onAcuantCameraCreated);
return () => {
const canvas = document.getElementById('acuant-ui-canvas');
if (canvas) {
stopObservingProperty(canvas, 'callback');
}

return () =>
cameraRef.current?.removeEventListener('acuantcameracreated', onAcuantCameraCreated);
};
}, []);
}, [cameraRef.current]);

const onCallback = useCallback(
(callback) => {
setAcuantCaptureMode(callback ? 'TAP' : 'AUTO');
},
[setAcuantCaptureMode],
);

// Acuant SDK assigns a callback property to the canvas when it switches to its "Tap to
// Capture" mode (Acuant SDK v11.4.4, L158). Infer capture type by presence of the property.
useObservableProperty(canvas, 'callback', onCallback);

const clickCanvas = () => document.getElementById('acuant-ui-canvas')?.click();

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect } from 'react';

/**
* Defines a property on the given object as an effect,
* It will call the change callback when that property is set to
* a new value.
*
* No-ops if object is not present.
*
* @param object Object on which to define property.
* @param property Property name to observe.
* @param onChangeCallback Callback to trigger on change.
*/
export function useObservableProperty(
object: any,
property: string,
onChangeCallback: (nextValue: any) => void,
) {
useEffect(() => {
if (!object) {
return;
}

let currentValue: any;

Object.defineProperty(object, property, {
get() {
return currentValue;
},
set(nextValue) {
currentValue = nextValue;
onChangeCallback(nextValue);
},
configurable: true,
});

return () => {
const value = object[property];

Object.defineProperty(object, property, { value, writable: true });
};
}, [object, property, onChangeCallback]);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sinon from 'sinon';
import userEvent from '@testing-library/user-event';
import { act } from '@testing-library/react';
import { AcuantContextProvider, DeviceContext } from '@18f/identity-document-capture';
import AcuantCaptureCanvas from '@18f/identity-document-capture/components/acuant-capture-canvas';
import { render, useAcuant } from '../../../support/document-capture';
Expand All @@ -16,9 +17,10 @@ describe('document-capture/components/acuant-capture-canvas', () => {
</DeviceContext.Provider>,
);

initialize();
window.AcuantCameraUI.start();

act(() => {
initialize();
window.AcuantCameraUI.start();
});
const button = getByRole('button', { name: 'doc_auth.buttons.take_picture' });

expect(button.disabled).to.be.true();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import sinon from 'sinon';
import {
defineObservableProperty,
stopObservingProperty,
} from '@18f/identity-document-capture/higher-order/observable-property';
import { useObservableProperty } from '@18f/identity-document-capture/hooks/use-observable-property';
import { renderHook } from '@testing-library/react-hooks';

describe('document-capture/higher-order/observable-property', () => {
describe('defineObservableProperty', () => {
describe('document-capture/hooks/use-observable-property', () => {
describe('useObservableProperty', () => {
it('behaves like an object', () => {
const object = {} as { key?: string };
defineObservableProperty(object, 'key', () => {});

renderHook(() => useObservableProperty(object, 'key', () => {}));

object.key = 'value';

expect(object.key).to.equal('value');
Expand All @@ -17,22 +17,21 @@ describe('document-capture/higher-order/observable-property', () => {
it('calls the callback on changes, with the changed value', () => {
const callback = sinon.spy();
const object = {} as { key?: string };
defineObservableProperty(object, 'key', callback);

renderHook(() => useObservableProperty(object, 'key', callback));
object.key = 'value';

expect(callback).to.have.been.calledOnceWithExactly('value');
});
});

describe('stopObservingProperty', () => {
it('removes the defined property and set the last value as a plain value', () => {
it('returns a cleanup function that removes the observer', () => {
const object = {} as { key?: string };
const callback = sinon.spy();
defineObservableProperty(object, 'key', callback);
const { unmount } = renderHook(() => useObservableProperty(object, 'key', callback));

object.key = 'value';

stopObservingProperty(object, 'key');
unmount();
expect(object.key).to.equal('value');

object.key = 'second_value';
Expand Down