diff --git a/packages/editable-html-tip-tap/src/__tests__/EditableHtml.test.jsx b/packages/editable-html-tip-tap/src/__tests__/EditableHtml.test.jsx
index bd8dd2e8b..9d72abb09 100644
--- a/packages/editable-html-tip-tap/src/__tests__/EditableHtml.test.jsx
+++ b/packages/editable-html-tip-tap/src/__tests__/EditableHtml.test.jsx
@@ -91,6 +91,9 @@ jest.mock('../extensions/responseArea', () => ({
InlineDropdownNode: {
configure: jest.fn(() => ({})),
},
+ MathTemplatedNode: {
+ configure: jest.fn(() => ({})),
+ },
ResponseAreaExtension: {
configure: jest.fn(() => ({})),
},
diff --git a/packages/editable-html-tip-tap/src/components/EditableHtml.jsx b/packages/editable-html-tip-tap/src/components/EditableHtml.jsx
index 6dabaf6fe..7a95b30bd 100644
--- a/packages/editable-html-tip-tap/src/components/EditableHtml.jsx
+++ b/packages/editable-html-tip-tap/src/components/EditableHtml.jsx
@@ -19,6 +19,7 @@ import {
DragInTheBlankNode,
ExplicitConstructedResponseNode,
InlineDropdownNode,
+ MathTemplatedNode,
ResponseAreaExtension,
} from '../extensions/responseArea';
import { MathNode } from '../extensions/math';
@@ -154,6 +155,7 @@ export const EditableHtml = (props) => {
ExplicitConstructedResponseNode.configure(props.responseAreaProps),
DragInTheBlankNode.configure(props.responseAreaProps),
InlineDropdownNode.configure(props.responseAreaProps),
+ MathTemplatedNode.configure(props.responseAreaProps),
MathNode.configure({
toolbarOpts: toolbarOptsToUse,
}),
diff --git a/packages/editable-html-tip-tap/src/components/respArea/MathTemplated.jsx b/packages/editable-html-tip-tap/src/components/respArea/MathTemplated.jsx
new file mode 100644
index 000000000..b507980b0
--- /dev/null
+++ b/packages/editable-html-tip-tap/src/components/respArea/MathTemplated.jsx
@@ -0,0 +1,124 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { NodeViewWrapper } from '@tiptap/react';
+import { mq } from '@pie-lib/math-input';
+import { styled } from '@mui/material/styles';
+
+const StyledSpanContainer = styled('span')(() => ({
+ display: 'inline-flex',
+ border: '1px solid #C0C3CF',
+ margin: '1px 5px',
+ cursor: 'pointer',
+ alignItems: 'center',
+ justifyContent: 'center',
+ minWidth: '50px',
+ minHeight: '36px',
+ height: 'fit-content',
+}));
+
+const StyledResponseBox = styled('div')(({ theme }) => ({
+ background: theme.palette.grey['A100'],
+ color: theme.palette.grey['A700'],
+ display: 'inline-flex',
+ borderRight: '2px solid #C0C3CF',
+ boxSizing: 'border-box',
+ overflow: 'hidden',
+ fontSize: '12px',
+ minHeight: '36px',
+ height: '100%',
+ alignItems: 'center',
+ fontFamily: 'Symbola, Times New Roman, serif',
+ padding: '0 2px',
+}));
+
+const StyledMathBlock = styled('div')(() => ({
+ flex: 8,
+ color: 'var(--pie-text, black)',
+ padding: '4px !important',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: 'var(--pie-background, rgba(255, 255, 255, 0))',
+ '& > .mq-math-mode sup.mq-nthroot': {
+ fontSize: '70% !important',
+ verticalAlign: '1em !important',
+ },
+ '& > .mq-math-mode .mq-sqrt-stem': {
+ borderTop: '0.07em solid',
+ marginLeft: '-1.5px',
+ marginTop: '-2px !important',
+ paddingTop: '5px !important',
+ },
+ '& .mq-overarrow-inner': {
+ paddingTop: '0 !important',
+ border: 'none !important',
+ },
+ '& .mq-overarrow.mq-arrow-both': {
+ marginTop: '0px',
+ minWidth: '1.23em',
+ '& *': {
+ lineHeight: '1 !important',
+ },
+ '&:before': {
+ top: '-0.4em',
+ left: '-1px',
+ },
+ '&:after': {
+ top: '0px !important',
+ position: 'absolute !important',
+ right: '-2px',
+ },
+ '&.mq-empty:after': {
+ top: '-0.45em',
+ },
+ },
+ '& .mq-overarrow.mq-arrow-right': {
+ '&:before': {
+ top: '-0.4em',
+ right: '-1px',
+ },
+ },
+ '& .mq-overarrow-inner-right': {
+ display: 'none !important',
+ },
+ '& .mq-overarrow-inner-left': {
+ display: 'none !important',
+ },
+}));
+const MathTemplated = (props) => {
+ const { node, options, selected } = props;
+ const { attrs: attributes } = node;
+ const { value, index } = attributes;
+
+ // add 1 to index to display R 1 instead of R 0
+ const keyToDisplay = `R ${parseInt(index) + 1}`;
+
+ // console.log({nodeProps.children})
+ return (
+
+
+ {keyToDisplay}
+
+
+
+
+
+ );
+};
+
+MathTemplated.propTypes = {
+ attributes: PropTypes.object,
+ value: PropTypes.string,
+ keyToDisplay: PropTypes.string,
+};
+
+export default MathTemplated;
diff --git a/packages/editable-html-tip-tap/src/components/respArea/__tests__/MathTemplated.test.jsx b/packages/editable-html-tip-tap/src/components/respArea/__tests__/MathTemplated.test.jsx
new file mode 100644
index 000000000..4a98a5b14
--- /dev/null
+++ b/packages/editable-html-tip-tap/src/components/respArea/__tests__/MathTemplated.test.jsx
@@ -0,0 +1,210 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import MathTemplated from '../MathTemplated';
+
+// Mock the dependencies
+jest.mock('@tiptap/react', () => ({
+ NodeViewWrapper: ({ children, ...props }) => (
+
+ {children}
+
+ ),
+}));
+
+jest.mock('@pie-lib/math-input', () => ({
+ mq: {
+ Static: ({ latex }) => (
+
+ {latex}
+
+ ),
+ },
+}));
+
+describe('MathTemplated', () => {
+ const defaultProps = {
+ node: {
+ attrs: {
+ value: 'x^2 + y^2 = r^2',
+ index: 0,
+ },
+ },
+ options: {},
+ selected: false,
+ };
+
+ it('renders without crashing', () => {
+ const { container } = render();
+ expect(container).toBeInTheDocument();
+ });
+
+ it('renders NodeViewWrapper with correct className', () => {
+ const { getByTestId } = render();
+ const wrapper = getByTestId('node-view-wrapper');
+ expect(wrapper).toHaveClass('math-templated');
+ });
+
+ it('displays correct response key for index 0', () => {
+ const { getByText } = render();
+ expect(getByText('R 1')).toBeInTheDocument();
+ });
+
+ it('displays correct response key for index 1', () => {
+ const props = {
+ ...defaultProps,
+ node: {
+ attrs: {
+ value: 'a + b',
+ index: 1,
+ },
+ },
+ };
+ const { getByText } = render();
+ expect(getByText('R 2')).toBeInTheDocument();
+ });
+
+ it('displays correct response key for index 5', () => {
+ const props = {
+ ...defaultProps,
+ node: {
+ attrs: {
+ value: 'c = d',
+ index: 5,
+ },
+ },
+ };
+ const { getByText } = render();
+ expect(getByText('R 6')).toBeInTheDocument();
+ });
+
+ it('renders LaTeX value correctly', () => {
+ const { getByTestId } = render();
+ const mqStatic = getByTestId('mq-static');
+ expect(mqStatic).toHaveAttribute('data-latex', 'x^2 + y^2 = r^2');
+ });
+
+ it('renders different LaTeX value', () => {
+ const props = {
+ ...defaultProps,
+ node: {
+ attrs: {
+ value: '\\frac{a}{b}',
+ index: 2,
+ },
+ },
+ };
+ const { getByTestId } = render();
+ const mqStatic = getByTestId('mq-static');
+ expect(mqStatic).toHaveAttribute('data-latex', '\\frac{a}{b}');
+ });
+
+ it('passes selected prop to NodeViewWrapper', () => {
+ const props = {
+ ...defaultProps,
+ selected: true,
+ };
+ const { getByTestId } = render();
+ const wrapper = getByTestId('node-view-wrapper');
+ expect(wrapper).toHaveAttribute('data-selected', 'true');
+ });
+
+ it('passes false selected prop to NodeViewWrapper', () => {
+ const { getByTestId } = render();
+ const wrapper = getByTestId('node-view-wrapper');
+ expect(wrapper).toHaveAttribute('data-selected', 'false');
+ });
+
+ it('applies correct inline styles to NodeViewWrapper', () => {
+ const { getByTestId } = render();
+ const wrapper = getByTestId('node-view-wrapper');
+ expect(wrapper).toHaveStyle({
+ display: 'inline-flex',
+ minHeight: '36px',
+ minWidth: '50px',
+ cursor: 'pointer',
+ });
+ });
+
+ it('handles string index correctly', () => {
+ const props = {
+ ...defaultProps,
+ node: {
+ attrs: {
+ value: 'test',
+ index: '3',
+ },
+ },
+ };
+ const { getByText } = render();
+ expect(getByText('R 4')).toBeInTheDocument();
+ });
+
+ it('handles empty value', () => {
+ const props = {
+ ...defaultProps,
+ node: {
+ attrs: {
+ value: '',
+ index: 0,
+ },
+ },
+ };
+ const { getByTestId } = render();
+ const mqStatic = getByTestId('mq-static');
+ expect(mqStatic).toHaveAttribute('data-latex', '');
+ });
+
+ it('renders all styled components', () => {
+ const { container, getByText } = render();
+
+ // Check for response box
+ expect(getByText('R 1')).toBeInTheDocument();
+
+ // Check for math block with LaTeX
+ const mqStatic = container.querySelector('[data-testid="mq-static"]');
+ expect(mqStatic).toBeInTheDocument();
+ });
+
+ it('handles zero index', () => {
+ const props = {
+ ...defaultProps,
+ node: {
+ attrs: {
+ value: 'x = 0',
+ index: 0,
+ },
+ },
+ };
+ const { getByText } = render();
+ expect(getByText('R 1')).toBeInTheDocument();
+ });
+
+ it('handles large index values', () => {
+ const props = {
+ ...defaultProps,
+ node: {
+ attrs: {
+ value: 'x = 100',
+ index: 99,
+ },
+ },
+ };
+ const { getByText } = render();
+ expect(getByText('R 100')).toBeInTheDocument();
+ });
+
+ it('renders with complex LaTeX expression', () => {
+ const props = {
+ ...defaultProps,
+ node: {
+ attrs: {
+ value: '\\sqrt{x^2 + y^2}',
+ index: 0,
+ },
+ },
+ };
+ const { getByTestId } = render();
+ const mqStatic = getByTestId('mq-static');
+ expect(mqStatic).toHaveAttribute('data-latex', '\\sqrt{x^2 + y^2}');
+ });
+});
diff --git a/packages/editable-html-tip-tap/src/extensions/responseArea.js b/packages/editable-html-tip-tap/src/extensions/responseArea.js
index b4e56b150..cc4f0ab7d 100644
--- a/packages/editable-html-tip-tap/src/extensions/responseArea.js
+++ b/packages/editable-html-tip-tap/src/extensions/responseArea.js
@@ -5,6 +5,7 @@ import { Node, ReactNodeViewRenderer } from '@tiptap/react';
import ExplicitConstructedResponse from '../components/respArea/ExplicitConstructedResponse';
import DragInTheBlank from '../components/respArea/DragInTheBlank/DragInTheBlank';
import InlineDropdown from '../components/respArea/InlineDropdown';
+import MathTemplated from '../components/respArea/MathTemplated';
const lastIndexMap = {};
@@ -305,7 +306,7 @@ export const MathTemplatedNode = Node.create({
];
},
addNodeView() {
- return ReactNodeViewRenderer(() => );
+ return ReactNodeViewRenderer((props) => );
},
});