Commit 0e93b2da authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '215121-style-toast-ui-editor' into 'master'

Style ToastUI editor to match design mockups

See merge request gitlab-org/gitlab!31351
parents 0895fbbd 22f42fce
import { __ } from '~/locale';
import { generateToolbarItem } from './toolbar_service';
/* eslint-disable @gitlab/require-i18n-strings */
const TOOLBAR_ITEM_CONFIGS = [
{ icon: 'heading', event: 'openHeadingSelect', classes: 'tui-heading', tooltip: __('Headings') },
{ icon: 'bold', command: 'Bold', tooltip: __('Add bold text') },
{ icon: 'italic', command: 'Italic', tooltip: __('Add italic text') },
{ icon: 'strikethrough', command: 'Strike', tooltip: __('Add strikethrough text') },
{ isDivider: true },
{ icon: 'quote', command: 'Blockquote', tooltip: __('Insert a quote') },
{ icon: 'link', event: 'openPopupAddLink', tooltip: __('Add a link') },
{ icon: 'doc-code', command: 'CodeBlock', tooltip: __('Insert a code block') },
{ isDivider: true },
{ icon: 'list-bulleted', command: 'UL', tooltip: __('Add a bullet list') },
{ icon: 'list-numbered', command: 'OL', tooltip: __('Add a numbered list') },
{ icon: 'list-task', command: 'Task', tooltip: __('Add a task list') },
{ icon: 'list-indent', command: 'Indent', tooltip: __('Indent') },
{ icon: 'list-outdent', command: 'Outdent', tooltip: __('Outdent') },
{ isDivider: true },
{ icon: 'dash', command: 'HR', tooltip: __('Add a line') },
{ icon: 'table', event: 'openPopupAddTable', classes: 'tui-table', tooltip: __('Add a table') },
{ isDivider: true },
{ icon: 'code', command: 'Code', tooltip: __('Insert inline code') },
];
export const EDITOR_OPTIONS = { export const EDITOR_OPTIONS = {
toolbarItems: [ toolbarItems: TOOLBAR_ITEM_CONFIGS.map(config => generateToolbarItem(config)),
'heading',
'bold',
'italic',
'strike',
'divider',
'quote',
'link',
'codeblock',
'divider',
'ul',
'ol',
'task',
'divider',
'hr',
'table',
'divider',
'code',
],
}; };
export const EDITOR_TYPES = { export const EDITOR_TYPES = {
...@@ -25,3 +33,5 @@ export const EDITOR_TYPES = { ...@@ -25,3 +33,5 @@ export const EDITOR_TYPES = {
}; };
export const EDITOR_HEIGHT = '100%'; export const EDITOR_HEIGHT = '100%';
export const EDITOR_PREVIEW_STYLE = 'horizontal';
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import 'codemirror/lib/codemirror.css'; import 'codemirror/lib/codemirror.css';
import '@toast-ui/editor/dist/toastui-editor.css'; import '@toast-ui/editor/dist/toastui-editor.css';
import { EDITOR_OPTIONS, EDITOR_TYPES, EDITOR_HEIGHT } from './constants'; import { EDITOR_OPTIONS, EDITOR_TYPES, EDITOR_HEIGHT, EDITOR_PREVIEW_STYLE } from './constants';
export default { export default {
components: { components: {
...@@ -31,6 +31,11 @@ export default { ...@@ -31,6 +31,11 @@ export default {
required: false, required: false,
default: EDITOR_HEIGHT, default: EDITOR_HEIGHT,
}, },
previewStyle: {
type: String,
required: false,
default: EDITOR_PREVIEW_STYLE,
},
}, },
computed: { computed: {
editorOptions() { editorOptions() {
...@@ -52,6 +57,7 @@ export default { ...@@ -52,6 +57,7 @@ export default {
ref="editor" ref="editor"
:initial-value="value" :initial-value="value"
:options="editorOptions" :options="editorOptions"
:preview-style="previewStyle"
:initial-edit-type="initialEditType" :initial-edit-type="initialEditType"
:height="height" :height="height"
@change="onContentChanged" @change="onContentChanged"
......
<script>
import { GlIcon } from '@gitlab/ui';
export default {
components: {
GlIcon,
},
props: {
icon: {
type: String,
required: true,
},
},
};
</script>
<template>
<button class="p-0 gl-display-flex toolbar-button">
<gl-icon class="gl-mx-auto" :name="icon" />
</button>
</template>
import Vue from 'vue';
import ToolbarItem from './toolbar_item.vue';
const buildWrapper = propsData => {
const instance = new Vue({
render(createElement) {
return createElement(ToolbarItem, propsData);
},
});
instance.$mount();
return instance.$el;
};
// eslint-disable-next-line import/prefer-default-export
export const generateToolbarItem = config => {
const { icon, classes, event, command, tooltip, isDivider } = config;
if (isDivider) {
return 'divider';
}
return {
type: 'button',
options: {
el: buildWrapper({ props: { icon }, class: classes }),
event,
command,
tooltip,
},
};
};
// Overrides styles from ToastUI editor
.tui-editor-defaultUI-toolbar .toolbar-button {
color: $gl-gray-600;
border: 0;
&:hover,
&:active {
color: $blue-500;
border: 0;
}
}
...@@ -1188,6 +1188,9 @@ msgstr "" ...@@ -1188,6 +1188,9 @@ msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message." msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "" msgstr ""
msgid "Add a line"
msgstr ""
msgid "Add a link" msgid "Add a link"
msgstr "" msgstr ""
...@@ -1275,6 +1278,9 @@ msgstr "" ...@@ -1275,6 +1278,9 @@ msgstr ""
msgid "Add request manually" msgid "Add request manually"
msgstr "" msgstr ""
msgid "Add strikethrough text"
msgstr ""
msgid "Add system hook" msgid "Add system hook"
msgstr "" msgstr ""
...@@ -10943,6 +10949,9 @@ msgstr "" ...@@ -10943,6 +10949,9 @@ msgstr ""
msgid "Header message" msgid "Header message"
msgstr "" msgstr ""
msgid "Headings"
msgstr ""
msgid "Health" msgid "Health"
msgstr "" msgstr ""
...@@ -11463,6 +11472,9 @@ msgstr "" ...@@ -11463,6 +11472,9 @@ msgstr ""
msgid "Incompatible options set!" msgid "Incompatible options set!"
msgstr "" msgstr ""
msgid "Indent"
msgstr ""
msgid "Index all projects" msgid "Index all projects"
msgstr "" msgstr ""
...@@ -11487,12 +11499,18 @@ msgstr "" ...@@ -11487,12 +11499,18 @@ msgstr ""
msgid "Input your repository URL" msgid "Input your repository URL"
msgstr "" msgstr ""
msgid "Insert a code block"
msgstr ""
msgid "Insert a quote" msgid "Insert a quote"
msgstr "" msgstr ""
msgid "Insert code" msgid "Insert code"
msgstr "" msgstr ""
msgid "Insert inline code"
msgstr ""
msgid "Insert suggestion" msgid "Insert suggestion"
msgstr "" msgstr ""
...@@ -14653,6 +14671,9 @@ msgstr "" ...@@ -14653,6 +14671,9 @@ msgstr ""
msgid "OutdatedBrowser|You can provide feedback %{feedback_link_start}on this issue%{feedback_link_end} or via your usual support channels." msgid "OutdatedBrowser|You can provide feedback %{feedback_link_start}on this issue%{feedback_link_end} or via your usual support channels."
msgstr "" msgstr ""
msgid "Outdent"
msgstr ""
msgid "Overridden" msgid "Overridden"
msgstr "" msgstr ""
......
...@@ -13,6 +13,9 @@ export const Editor = { ...@@ -13,6 +13,9 @@ export const Editor = {
height: { height: {
type: String, type: String,
}, },
previewStyle: {
type: String,
},
}, },
render(h) { render(h) {
return h('div'); return h('div');
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import RichContentEditor from '~/vue_shared/components/rich_content_editor/rich_content_editor.vue'; import RichContentEditor from '~/vue_shared/components/rich_content_editor/rich_content_editor.vue';
import {
EDITOR_OPTIONS,
EDITOR_TYPES,
EDITOR_HEIGHT,
EDITOR_PREVIEW_STYLE,
} from '~/vue_shared/components/rich_content_editor/constants';
describe('Rich Content Editor', () => { describe('Rich Content Editor', () => {
let wrapper; let wrapper;
const editorOptions = {
toolbarItems: [
'heading',
'bold',
'italic',
'strike',
'divider',
'quote',
'link',
'codeblock',
'divider',
'ul',
'ol',
'task',
'divider',
'hr',
'table',
'divider',
'code',
],
};
const value = '## Some Markdown'; const value = '## Some Markdown';
const findEditor = () => wrapper.find({ ref: 'editor' }); const findEditor = () => wrapper.find({ ref: 'editor' });
...@@ -44,15 +29,19 @@ describe('Rich Content Editor', () => { ...@@ -44,15 +29,19 @@ describe('Rich Content Editor', () => {
}); });
it('provides the correct editor options', () => { it('provides the correct editor options', () => {
expect(findEditor().props().options).toEqual(editorOptions); expect(findEditor().props().options).toEqual(EDITOR_OPTIONS);
});
it('has the correct preview style', () => {
expect(findEditor().props().previewStyle).toBe(EDITOR_PREVIEW_STYLE);
}); });
it('has the correct initial edit type', () => { it('has the correct initial edit type', () => {
expect(findEditor().props().initialEditType).toBe('wysiwyg'); expect(findEditor().props().initialEditType).toBe(EDITOR_TYPES.wysiwyg);
}); });
it('has the correct height', () => { it('has the correct height', () => {
expect(findEditor().props().height).toBe('100%'); expect(findEditor().props().height).toBe(EDITOR_HEIGHT);
}); });
}); });
......
import { shallowMount } from '@vue/test-utils';
import { GlIcon } from '@gitlab/ui';
import ToolbarItem from '~/vue_shared/components/rich_content_editor/toolbar_item.vue';
describe('Toolbar Item', () => {
let wrapper;
const findIcon = () => wrapper.find(GlIcon);
const findButton = () => wrapper.find('button');
const buildWrapper = propsData => {
wrapper = shallowMount(ToolbarItem, { propsData });
};
describe.each`
icon
${'heading'}
${'bold'}
${'italic'}
${'strikethrough'}
${'quote'}
${'link'}
${'doc-code'}
${'list-bulleted'}
${'list-numbered'}
${'list-task'}
${'list-indent'}
${'list-outdent'}
${'dash'}
${'table'}
${'code'}
`('toolbar item component', ({ icon }) => {
beforeEach(() => buildWrapper({ icon }));
it('renders a toolbar button', () => {
expect(findButton().exists()).toBe(true);
});
it(`renders the ${icon} icon`, () => {
expect(findIcon().exists()).toBe(true);
expect(findIcon().props().name).toBe(icon);
});
});
});
import { generateToolbarItem } from '~/vue_shared/components/rich_content_editor/toolbar_service';
describe('Toolbar Service', () => {
const config = {
icon: 'bold',
command: 'some-command',
tooltip: 'Some Tooltip',
event: 'some-event',
};
const generatedItem = generateToolbarItem(config);
it('generates the correct command', () => {
expect(generatedItem.options.command).toBe(config.command);
});
it('generates the correct tooltip', () => {
expect(generatedItem.options.tooltip).toBe(config.tooltip);
});
it('generates the correct event', () => {
expect(generatedItem.options.event).toBe(config.event);
});
it('generates a divider when isDivider is set to true', () => {
const isDivider = true;
expect(generateToolbarItem({ isDivider })).toBe('divider');
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment