feat: render iframe with xblocks (#1375)

This refactors the CourseUnit component by removing the DraggableList and CourseXBlock components and replacing them with a simpler XBlockContainerIframe. Additionally, it introduces new constants for iframe handling.
This commit is contained in:
Peter Kulko
2024-11-08 15:53:32 +02:00
committed by GitHub
parent f9ef00e29f
commit e59f2846e3
26 changed files with 591 additions and 1343 deletions

View File

@@ -0,0 +1,2 @@
// eslint-disable-next-line import/prefer-default-export
export { useEventListener } from './useEventListener';

View File

@@ -0,0 +1,62 @@
import { renderHook } from '@testing-library/react-hooks';
import { useEventListener } from '../useEventListener';
describe('useEventListener', () => {
let addEventListenerSpy: jest.SpyInstance;
let removeEventListenerSpy: jest.SpyInstance;
beforeEach(() => {
addEventListenerSpy = jest.spyOn(global, 'addEventListener');
removeEventListenerSpy = jest.spyOn(global, 'removeEventListener');
});
afterEach(() => {
jest.clearAllMocks();
});
it('should add event listener on mount', () => {
const handler = jest.fn();
renderHook(() => useEventListener('click', handler));
expect(addEventListenerSpy).toHaveBeenCalledWith('click', handler);
});
it('should remove event listener on unmount', () => {
const handler = jest.fn();
const { unmount } = renderHook(() => useEventListener('click', handler));
unmount();
expect(removeEventListenerSpy).toHaveBeenCalledWith('click', handler);
});
it('should update event listener when handler changes', () => {
const handler1 = jest.fn();
const handler2 = jest.fn();
const { rerender } = renderHook(({ handler }: {
handler: (event: Event) => void
}) => useEventListener('click', handler), {
initialProps: { handler: handler1 },
});
rerender({ handler: handler2 });
expect(removeEventListenerSpy).toHaveBeenCalledWith('click', handler1);
expect(addEventListenerSpy).toHaveBeenCalledWith('click', handler2);
});
it('should update event listener when type changes', () => {
const handler = jest.fn();
const { rerender } = renderHook(({ type }: {
type: keyof WindowEventMap
}) => useEventListener(type, handler), {
initialProps: { type: 'click' },
});
rerender({ type: 'scroll' });
expect(removeEventListenerSpy).toHaveBeenCalledWith('click', handler);
expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', handler);
});
});

View File

@@ -0,0 +1,27 @@
import { useEffect, useRef, MutableRefObject } from 'react';
// eslint-disable-next-line import/prefer-default-export
export function useEventListener<K extends keyof WindowEventMap>(
type: K,
handler: (event: WindowEventMap[K]) => void,
) {
// We use this ref so that we can hold a reference to the currently active event listener.
const eventListenerRef = useRef<(event: WindowEventMap[K]) => void | null>(null);
useEffect(() => {
// If we currently have an event listener, remove it.
if (eventListenerRef.current !== null) {
global.removeEventListener(type, eventListenerRef.current);
}
// Now add our new handler as the event listener.
global.addEventListener(type, handler);
// And then save it to our ref for next time.
(eventListenerRef as MutableRefObject<(event: WindowEventMap[K]) => void>).current = handler;
// When the component finally unmounts, use the ref to remove the correct handler.
return () => {
if (eventListenerRef.current !== null) {
global.removeEventListener(type, eventListenerRef.current);
}
};
}, [type, handler]);
}