Skip to content

Commit

Permalink
✨(front) add buttons in the toolbar of ashley for latex
Browse files Browse the repository at this point in the history
At the moment we can use mathematical latex syntax by pressing $ to
start Inline Tex or  CMD + M for Tex Block.
For users, it would be much easier, if they have access directly
in the toolbar to these commands. We, now, add two buttons to
suggest these new features.
  • Loading branch information
carofun committed Feb 7, 2023
1 parent 065fa9a commit c4c20c5
Show file tree
Hide file tree
Showing 19 changed files with 406 additions and 15 deletions.
14 changes: 7 additions & 7 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ jobs:
path: ~/fun
- restore_cache:
keys:
- v2-front-dependencies-{{ checksum "yarn.lock" }}
- v2-front-dependencies-
- v3-front-dependencies-{{ checksum "yarn.lock" }}
- v3-front-dependencies
# If the yarn.lock file is not up-to-date with the package.json file,
# using the --frozen-lockfile should fail.
- run:
Expand All @@ -358,7 +358,7 @@ jobs:
- save_cache:
paths:
- ./node_modules
key: v2-front-dependencies-{{ checksum "yarn.lock" }}
key: v3-front-dependencies-{{ checksum "yarn.lock" }}

build-front-production:
docker:
Expand All @@ -369,8 +369,8 @@ jobs:
path: ~/fun
- restore_cache:
keys:
- v2-front-dependencies-{{ checksum "yarn.lock" }}
- v2-front-dependencies-
- v3-front-dependencies-{{ checksum "yarn.lock" }}
- v3-front-dependencies-
- run:
name: Build front-end application in production mode
command: yarn build-production
Expand Down Expand Up @@ -404,7 +404,7 @@ jobs:
path: ~/fun
- restore_cache:
keys:
- v2-front-dependencies-{{ checksum "yarn.lock" }}
- v3-front-dependencies-{{ checksum "yarn.lock" }}
- run:
name: Lint code with tslint
command: yarn lint
Expand All @@ -421,7 +421,7 @@ jobs:
path: ~/fun
- restore_cache:
keys:
- v2-front-dependencies-{{ checksum "yarn.lock" }}
- v3-front-dependencies-{{ checksum "yarn.lock" }}
- run:
name: Run tests
# Circle CI needs the tests to be run sequentially, otherwise it hangs. See Jest docs below:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).

### Added

- Add buttons in the toolbar of Ashley to add inline and block
latex
- Enabling latex mathematical equations in forums

## [1.2.5] - 2022-10-14
Expand Down
Binary file modified src/ashley/locale/fr_FR/LC_MESSAGES/django.mo
Binary file not shown.
2 changes: 1 addition & 1 deletion src/ashley/locale/fr_FR/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: ashleyforum\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-10-05 16:40+0000\n"
"PO-Revision-Date: 2022-10-14 08:12\n"
"PO-Revision-Date: 2023-02-07 14:41\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Language: fr_FR\n"
Expand Down
10 changes: 10 additions & 0 deletions src/ashley/static/ashley/img/latex-block.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/ashley/static/ashley/img/latex-inline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/frontend/i18n/locales/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
"description": "Error message on uploading image, max size reached",
"message": "Une erreur est survenue lors du téléchargement de l'image, la taille du fichier est supérieure à {maxSize}Mo."
},
"components.AshleyEditor.LatexButton.toolTipAddLatexBlock": {
"description": "Message over the button to add a Latex block formula",
"message": "Ajouter une formule mathématique Latex dans un nouveau bloc"
},
"components.AshleyEditor.LatexButton.toolTipAddLatexInline": {
"description": "Message over the button to add a Latex inline formula",
"message": "Insérer une formule mathématique Latex à l'emplacement du curseur"
},
"components.AshleyEditor.linkPlaceholderEditor": {
"description": "Placeholder when adding a link in the editor",
"message": "Saisissez ou collez votre URL ici et appuyez sur Entrée"
Expand Down
1 change: 0 additions & 1 deletion src/frontend/js/components/AshleyEditor/ImageAdd/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { appFrontendContext } from './../../../data/frontEndData';
import { FileUploadError } from './../../../types/Enums';
import { uploadFile } from './../../../utils/uploadFile';
import { messagesEditor } from '../messages';
import { UploadedFile } from '../../../types/UploadedFile';
import { Spinner } from '../../Spinner';

interface ImageAddProps {
Expand Down
82 changes: 82 additions & 0 deletions src/frontend/js/components/AshleyEditor/LatexButton/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { EditorState, ContentState } from 'draft-js';
import { IntlProvider } from 'react-intl';

import {
insertInlineTeX,
insertTeXBlock,
} from 'draft-js-latex-plugin/lib/utils/insertTeX';
import { TypeLatexStyle } from '../../../types/Enums';
import { LatexButton } from '.';

const editor = EditorState.createWithContent(
ContentState.createFromText('Hello World!'),
);

const props = {
editorState: editor,
type: TypeLatexStyle.INLINE,
getEditorState: jest.fn(),
setEditorState: jest.fn(),
theme: {
button: '',
buttonWrapper: '',
active: 'active',
},
};
jest.mock('draft-js-latex-plugin/lib/utils/insertTeX', () => ({
insertInlineTeX: jest.fn(),
insertTeXBlock: jest.fn(),
}));

describe('LatexButton', () => {
afterEach(() => {
jest.resetAllMocks();
});
it('should handle adding inline latex', () => {
const { getByTestId } = render(<LatexButton {...props} />);
const button = getByTestId('addLatexinline');
fireEvent.click(button);
expect(props.setEditorState).toHaveBeenCalled();
expect(props.getEditorState).toHaveBeenCalled();
expect(insertInlineTeX).toHaveBeenCalled();
expect(insertTeXBlock).not.toHaveBeenCalled();
});

it('shows the tooltip text over inline latex button is be present', () => {
const { getByRole, getByTestId } = render(
<IntlProvider locale="en">
<LatexButton {...props} />
</IntlProvider>,
);
const button = getByTestId('addLatexinline');
fireEvent.mouseEnter(button);
getByRole('tooltip', { name: 'Add a Latex inline mathematical formula' });
});

it('should handle adding block latex', () => {
props.type = TypeLatexStyle.BLOCK;
const { getByTestId } = render(<LatexButton {...props} />);
const button = getByTestId('addLatexblock');
fireEvent.click(button);
expect(props.setEditorState).toHaveBeenCalled();
expect(props.getEditorState).toHaveBeenCalled();
expect(insertInlineTeX).not.toHaveBeenCalled();
expect(insertTeXBlock).toHaveBeenCalled();
});

it('shows the tooltip text over latex block button is be present', () => {
props.type = TypeLatexStyle.BLOCK;
const { getByRole, getByTestId } = render(
<IntlProvider locale="en">
<LatexButton {...props} />
</IntlProvider>,
);
const button = getByTestId('addLatexblock');
fireEvent.mouseEnter(button);
getByRole('tooltip', {
name: 'Add a Latex mathematical formula in a new block',
});
});
});
98 changes: 98 additions & 0 deletions src/frontend/js/components/AshleyEditor/LatexButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useMemo, useEffect, useRef, useState } from 'react';
import { DraftJsButtonTheme } from '@draft-js-plugins/buttons';
import { EditorState } from 'draft-js';
import {
insertInlineTeX,
insertTeXBlock,
} from 'draft-js-latex-plugin/lib/utils/insertTeX';
import { FormattedMessage } from 'react-intl';
import Tooltip from 'rc-tooltip';
import { messagesEditor } from '../messages';
import { DraftHandleValue, TypeLatexStyle } from '../../../types/Enums';

interface LatexButtonProps {
type: string;
theme: DraftJsButtonTheme;
editorState: EditorState;
getEditorState: () => EditorState;
setEditorState: (editorState: EditorState) => void;
}

export const LatexButton = (props: LatexButtonProps) => {
const node = useRef<HTMLDivElement>(null);
const { buttonWrapper = '', button, active } = props.theme;
const [isActive, setIsActive] = useState(false);

const isInlineTeXSelected = () => {
const selection = props.editorState.getSelection();
const contentState = props.editorState.getCurrentContent();
const blockKey = selection.getStartKey();
const block = contentState.getBlockForKey(blockKey);
const offset = selection.getIsBackward()
? selection.getStartOffset()
: selection.getEndOffset();
const entityKey = block?.getEntityAt(offset);
if (!entityKey) {
return false;
}
return contentState.getEntity(entityKey).getType() === 'INLINETEX';
};

const isLatexBlock = () => {
const selection = props.editorState.getSelection();
const block = props.editorState
.getCurrentContent()
.getBlockForKey(selection.getStartKey());
return block.getData().get('type') === 'TEXBLOCK';
};

useEffect(() => {
if (props.type === TypeLatexStyle.BLOCK) {
setIsActive(isLatexBlock());
} else if (props.type === TypeLatexStyle.INLINE) {
setIsActive(isInlineTeXSelected());
}
}, [props.editorState]);

const imageClassName = useMemo(
() => `latex-${props.type} ${button} ${isActive ? active : ''}`,
[props.type, isActive],
);

const handleAddLatex = (
e: React.MouseEvent<HTMLButtonElement>,
type: string,
) => {
e.preventDefault();
let newEditorState;
const editorState = props.getEditorState();
if (type === TypeLatexStyle.BLOCK) {
newEditorState = insertTeXBlock(editorState);
} else {
newEditorState = insertInlineTeX(editorState);
}
props.setEditorState(newEditorState);
return DraftHandleValue.HANDLED;
};
return (
<div className={buttonWrapper} ref={node}>
<Tooltip
overlay={
<FormattedMessage
{...(props.type === TypeLatexStyle.INLINE
? messagesEditor.toolTipAddLatexInline
: messagesEditor.toolTipAddLatexBlock)}
/>
}
placement="bottom"
>
<button
data-testid={`addLatex${props.type}`}
className={`${imageClassName}`}
onClick={(e) => handleAddLatex(e, props.type)}
type="button"
/>
</Tooltip>
</div>
);
};
40 changes: 40 additions & 0 deletions src/frontend/js/components/AshleyEditor/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ describe('AshleyEditor', () => {
name: //i,
});

// check if latex inline has been added to the toolbar plugin editor
screen.getByTestId('addLatexinline');

// check if latex block has been added to the toolbar plugin editor
screen.getByTestId('addLatexblock');

// check if quotation has been added to the toolbar plugin editor
const quotationButton = container.querySelector(
'path[d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"]',
Expand Down Expand Up @@ -231,6 +237,40 @@ describe('AshleyEditor', () => {
expect(latex).toHaveClass('TeXInput');
});

it('generates a textbox when the inline latex button is fired', () => {
const { container } = render(
<IntlProvider locale="en">
<AshleyEditor target="target" {...props} />
</IntlProvider>,
);

expect(screen.queryAllByRole('textbox').length).toEqual(1);

const button = screen.getByTestId('addLatexinline');
fireEvent.click(button);
expect(screen.queryAllByRole('textbox').length).toEqual(2);
const latex = screen.queryAllByRole('textbox')[1];
expect(latex).toHaveClass('TeXInput');
expect(latex.parentElement).toHaveClass('INLINETEX');
});

it('generates a textbox when the block latex button is fired', () => {
const { container } = render(
<IntlProvider locale="en">
<AshleyEditor target="target" {...props} />
</IntlProvider>,
);

expect(screen.queryAllByRole('textbox').length).toEqual(1);

const button = screen.getByTestId('addLatexblock');
fireEvent.click(button);
expect(screen.queryAllByRole('textbox').length).toEqual(2);
const latex = screen.queryAllByRole('textbox')[1];
expect(latex).toHaveClass('TeXInput');
expect(latex.parentElement).toHaveClass('TEXBLOCK');
});

it('renders the editor with a list of users to mention', () => {
// load the editor with no list of users to mention
render(
Expand Down
Loading

0 comments on commit c4c20c5

Please sign in to comment.