Skip to content

Commit

Permalink
Allow users to toggle word wrapping on the doc editor (#1454)
Browse files Browse the repository at this point in the history
* Enable word wrapping on the doc editor
* Update react-ace to 11.0.1 and ace-builds to 1.35.0
* Update braces to 3.0.3 to address CVE
  • Loading branch information
Antonio-Maranhao committed Jun 13, 2024
1 parent 6e19267 commit 55b75bf
Show file tree
Hide file tree
Showing 15 changed files with 168 additions and 46 deletions.
3 changes: 2 additions & 1 deletion app/addons/components/__tests__/stringEditModal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ describe('String Edit Modal', () => {
var string = "a string!";
var spy = sinon.spy();
const el = mount(
<ReactComponents.StringEditModal visible={true} onClose={stub} onSave={spy} value={string} />
<ReactComponents.StringEditModal visible={true} onClose={stub} onSave={spy}
value={string} wordWrapEnabled={false}/>
);
el.find('button#string-edit-save-btn').simulate('click');
sinon.assert.calledOnce(spy);
Expand Down
5 changes: 4 additions & 1 deletion app/addons/components/components/codeeditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class CodeEditor extends React.Component {
theme: 'idle_fingers',
fontSize: 13,

// this sets the default value for the editor. On the fly changes are stored in state in this component only. To
// defaultCode sets the default value for the editor. On the fly changes are stored in state in this component only. To
// change the editor content after initial construction use CodeEditor.setValue()
defaultCode: '',
disabled: false,
Expand All @@ -40,6 +40,7 @@ export class CodeEditor extends React.Component {
autoScrollEditorIntoView: true,
autoFocus: false,
stringEditModalEnabled: false,
wordWrapEnabled: false,

// these two options create auto-resizeable code editors, with a maximum number of lines
setHeightToLineCount: false,
Expand Down Expand Up @@ -366,6 +367,7 @@ export class CodeEditor extends React.Component {
fontSize={this.props.fontSize}
focus={this.props.autoFocus}
setOptions={{
wrap: this.props.wordWrapEnabled
}}/>
<button ref={node => this.stringEditIcon = node}
className="btn string-edit"
Expand All @@ -378,6 +380,7 @@ export class CodeEditor extends React.Component {
ref={node => this.stringEditModal = node}
visible={this.state.stringEditModalVisible}
value={this.state.stringEditModalValue}
wordWrapEnabled={this.props.wordWrapEnabled}
onSave={this.saveStringEditModal}
onClose={this.closeStringEditModal} />
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/addons/components/components/menudropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class MenuDropDown extends React.Component {
: CustomMenuLinkToggle;
return (
<Dropdown
id="dropdown-menu">
id={this.props.id ? this.props.id : "dropdown-menu"}>
<Dropdown.Toggle
as={CustomMenuToggle}
icon={this.props.icon}
Expand Down
4 changes: 3 additions & 1 deletion app/addons/components/components/stringeditmodal.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export class StringEditModal extends React.Component {
value: PropTypes.string.isRequired,
visible: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired
onSave: PropTypes.func.isRequired,
wordWrapEnabled: PropTypes.bool.isRequired,
};

static defaultProps = {
Expand All @@ -39,6 +40,7 @@ export class StringEditModal extends React.Component {
this.editor = ace.edit(dom_node);
this.editor.setShowPrintMargin(false);
this.editor.setOption('highlightActiveLine', true);
this.editor.setOption('wrap', !!this.props.wordWrapEnabled);
this.editor.setTheme('ace/theme/idle_fingers');
const val = Helpers.parseJSON(this.props.value);
this.editor.setValue(val, -1);
Expand Down
2 changes: 1 addition & 1 deletion app/addons/documents/assets/scss/doc-editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@

// hide the labels on the buttons when the screen is shrunk too small,
@media screen and (max-width: 1000px) {
.panel-section button span {
.panel-section button > span {
display: none;
}
.panel-section button i[class*=fonticon-] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ const defaultProps = {
hideUploadModal: () => {},
cancelUpload: () => {},
resetUploadModal: () => {},
uploadAttachment: () => {}
uploadAttachment: () => {},

wordWrapEnabled: false,
toggleWordWrap: () => {},
};

describe('DocEditorScreen', () => {
Expand Down Expand Up @@ -279,6 +282,26 @@ describe('DocEditorContainer', () => {
expect(wrapper.find(DocEditorScreen).prop('isUploadModalVisible')).toBe(true);
});

it('clicking word wrap option turns the feature on/off', async() => {
store.dispatch(loadDummyDocAction);
const wrapper = mount(
<Provider store={store}>
<DocEditorContainer
isNewDoc={false}
database={database} />
</Provider>
);
expect(wrapper.find(DocEditorScreen).prop('wordWrapEnabled')).toBe(false);
// open menu then click the dropdown item
wrapper.find('div#doc-editor-preferences-menu button.dropdown-toggle').simulate('click');
wrapper.find('div#doc-editor-preferences-menu button.dropdown-item').simulate('click');
expect(wrapper.find(DocEditorScreen).prop('wordWrapEnabled')).toBe(true);
// open menu then click the dropdown item again
wrapper.find('div#doc-editor-preferences-menu button.dropdown-toggle').simulate('click');
wrapper.find('div#doc-editor-preferences-menu button.dropdown-item').simulate('click');
expect(wrapper.find(DocEditorScreen).prop('wordWrapEnabled')).toBe(false);
});

it('clicking Clone button shows the clone doc dialog', () => {
store.dispatch(loadDummyDocAction);
const wrapper = mount(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,12 @@ describe('DocEditor Reducer', function () {
expect(newStateSavingDone.isSaving).toBe(false);
});

it('toggles the word wrap option', function () {
const newState = reducer(undefined, { type: ActionTypes.TOGGLE_WRAP_LINE_OPTION });
expect(newState.docEditorPreferences.wordWrapEnabled).toBe(true);

const newState2 = reducer(newState, { type: ActionTypes.TOGGLE_WRAP_LINE_OPTION });
expect(newState2.docEditorPreferences.wordWrapEnabled).toBe(false);
});

});
5 changes: 5 additions & 0 deletions app/addons/documents/doc-editor/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ const showUploadModal = () => (dispatch) => {
dispatch({ type: ActionTypes.SHOW_UPLOAD_MODAL });
};

const toggleWordWrap = () => (dispatch) => {
dispatch({ type: ActionTypes.TOGGLE_WRAP_LINE_OPTION });
};

const hideUploadModal = () => (dispatch) => {
dispatch({ type: ActionTypes.HIDE_UPLOAD_MODAL });
};
Expand Down Expand Up @@ -261,6 +265,7 @@ export default {
dispatchInitDocEditor,
initDocEditor,
saveDoc,
toggleWordWrap,

// clone doc
showCloneDocModal,
Expand Down
1 change: 1 addition & 0 deletions app/addons/documents/doc-editor/actiontypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ export default {
SET_FILE_UPLOAD_PERCENTAGE: 'SET_FILE_UPLOAD_PERCENTAGE',
SAVING_DOCUMENT: 'SAVING_DOCUMENT',
SAVING_DOCUMENT_COMPLETED: 'SAVING_DOCUMENT_COMPLETED',
TOGGLE_WRAP_LINE_OPTION: 'TOGGLE_WRAP_LINE_OPTION',
};
10 changes: 8 additions & 2 deletions app/addons/documents/doc-editor/components/DocEditorContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ const mapStateToProps = ({ docEditor, databases }, ownProps) => {
uploadInProgress: docEditor.uploadInProgress,
uploadPercentage: docEditor.uploadPercentage,
uploadErrorMessage: docEditor.uploadErrorMessage,
numFilesUploaded: docEditor.numFilesUploaded
numFilesUploaded: docEditor.numFilesUploaded,

wordWrapEnabled: docEditor.docEditorPreferences.wordWrapEnabled,
};
};

Expand Down Expand Up @@ -76,7 +78,11 @@ const mapDispatchToProps = (dispatch) => {
},
uploadAttachment: (params) => {
dispatch(Actions.uploadAttachment(params));
}
},

toggleWordWrap: () => {
dispatch(Actions.toggleWordWrap());
},
};
};

Expand Down
15 changes: 12 additions & 3 deletions app/addons/documents/doc-editor/components/DocEditorScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import AttachmentsPanelButton from './AttachmentsPanelButton';
import CloneDocModal from './CloneDocModal';
import PanelButton from './PanelButton';
import UploadModal from './UploadModal';
import PreferencesButton from './PreferencesButton';


export default class DocEditorScreen extends React.Component {
Expand Down Expand Up @@ -58,7 +59,10 @@ export default class DocEditorScreen extends React.Component {
hideUploadModal: PropTypes.func.isRequired,
cancelUpload: PropTypes.func.isRequired,
resetUploadModal: PropTypes.func.isRequired,
uploadAttachment: PropTypes.func.isRequired
uploadAttachment: PropTypes.func.isRequired,

wordWrapEnabled: PropTypes.bool.isRequired,
toggleWordWrap: PropTypes.func.isRequired,
};

getCodeEditor = () => {
Expand Down Expand Up @@ -89,7 +93,8 @@ export default class DocEditorScreen extends React.Component {
autoFocus={true}
editorCommands={editorCommands}
notifyUnsavedChanges={true}
stringEditModalEnabled={true} />
stringEditModalEnabled={true}
wordWrapEnabled={this.props.wordWrapEnabled}/>
);
};

Expand All @@ -99,7 +104,7 @@ export default class DocEditorScreen extends React.Component {
this.props.doc && this.props.doc.hasChanged() ||
(this.props.doc && nextProps.doc && this.props.doc.id !== nextProps.doc.id)) {
const editor = this.getEditor();
if (editor) {
if (editor && nextProps.doc) {
this.getEditor().setValue(JSON.stringify(nextProps.doc.attributes, null, ' '));
}
this.onSaveComplete();
Expand Down Expand Up @@ -158,6 +163,10 @@ export default class DocEditorScreen extends React.Component {
}
return (
<div>
<PreferencesButton
wordWrapEnabled={this.props.wordWrapEnabled}
toggleWordWrap={this.props.toggleWordWrap} />

<AttachmentsPanelButton
doc={this.props.doc}
isLoading={this.props.isLoading}
Expand Down
41 changes: 41 additions & 0 deletions app/addons/documents/doc-editor/components/PreferencesButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

import React from 'react';
import PropTypes from 'prop-types';
import Components from '../../../components/react-components';

const { MenuDropDown } = Components;

export default class PreferencesButton extends React.Component {
static defaultProps = {
wordWrapEnabled: false,
};
static propTypes = {
wordWrapEnabled: PropTypes.bool,
toggleWordWrap: PropTypes.func.isRequired,
};

constructor(props) {
super(props);
this.toggleWordWrap = this.toggleWordWrap.bind(this);
}

toggleWordWrap() {
this.props.toggleWordWrap();
}

render() {
const links = [
{
title: 'Word Wrap',
onClick: () => { this.toggleWordWrap(); },
icon: this.props.wordWrapEnabled ? 'fonticon-ok' : '',
}
];

return (
<div className='panel-section'>
<MenuDropDown id="doc-editor-preferences-menu" links={links} icon='fonticon-mixer' hideArrow={true} toggleType='button'/>
</div>
);
}
}
25 changes: 24 additions & 1 deletion app/addons/documents/doc-editor/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// License for the specific language governing permissions and limitations under
// the License.

import app from "../../../app";
import ActionTypes from "./actiontypes";

const initialState = {
Expand All @@ -25,9 +26,20 @@ const initialState = {
uploadInProgress: false,
uploadPercentage: 0,

docConflictCount: 0
docConflictCount: 0,
docEditorPreferences: loadDocEditorPreferences(),
};

function loadDocEditorPreferences() {
let savedPref = app.utils.localStorageGet('fauxton:doc_editor_preferences');
return {
// default values
wordWrapEnabled: false,
// merge saved preferences
...(savedPref ? savedPref : {}),
};
}

export default function docEditor (state = initialState, action) {
const { options, type } = action;
switch (type) {
Expand Down Expand Up @@ -131,6 +143,17 @@ export default function docEditor (state = initialState, action) {
isSaving: false,
};

case ActionTypes.TOGGLE_WRAP_LINE_OPTION:
const newPreferences = {
...state.docEditorPreferences,
wordWrapEnabled: !(state.docEditorPreferences.wordWrapEnabled),
};
app.utils.localStorageSet('fauxton:doc_editor_preferences', newPreferences);
return {
...state,
docEditorPreferences: newPreferences,
};

default:
return state;
}
Expand Down
Loading

0 comments on commit 55b75bf

Please sign in to comment.