import React, { useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import DOMPurify from 'dompurify'; import { logError } from '@edx/frontend-platform/logging'; import { useDebounce } from '../discussions/data/hooks'; const defaultSanitizeOptions = { USE_PROFILES: { html: true }, ADD_ATTR: ['columnalign'], }; function HTMLLoader({ htmlNode, componentId, cssClassName, testId, delay, }) { const sanitizedMath = DOMPurify.sanitize(htmlNode, { ...defaultSanitizeOptions }); const previewRef = useRef(null); const debouncedPostContent = useDebounce(htmlNode, delay); useEffect(() => { let promise = Promise.resolve(); // Used to hold chain of typesetting calls function typeset(code) { promise = promise.then(() => { if (typeof window?.MathJax !== 'undefined' && typeof window?.MathJax.startup !== 'undefined') { window.MathJax.startup.defaultPageReady().then((window.MathJax?.typesetPromise(code()))); } return null; }) .catch((err) => logError(`Typeset failed: ${err.message}`)); return promise; } if (debouncedPostContent) { typeset(() => { if (previewRef.current !== null && typeof window?.MathJax !== 'undefined') { previewRef.current.innerHTML = sanitizedMath; } }); } }, [debouncedPostContent]); return (
); } HTMLLoader.propTypes = { htmlNode: PropTypes.node, componentId: PropTypes.string, cssClassName: PropTypes.string, testId: PropTypes.string, delay: PropTypes.number, }; HTMLLoader.defaultProps = { htmlNode: '', componentId: null, cssClassName: '', testId: '', delay: 0, }; export default HTMLLoader;