diff options
Diffstat (limited to 'app/assets/javascripts/content_editor/components')
11 files changed, 429 insertions, 191 deletions
diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue index 9a51def7075..a372233e543 100644 --- a/app/assets/javascripts/content_editor/components/content_editor.vue +++ b/app/assets/javascripts/content_editor/components/content_editor.vue @@ -1,45 +1,111 @@ <script> -import { GlAlert } from '@gitlab/ui'; +import { GlLoadingIcon } from '@gitlab/ui'; import { EditorContent as TiptapEditorContent } from '@tiptap/vue-2'; -import { ContentEditor } from '../services/content_editor'; +import { LOADING_CONTENT_EVENT, LOADING_SUCCESS_EVENT, LOADING_ERROR_EVENT } from '../constants'; +import { createContentEditor } from '../services/create_content_editor'; +import ContentEditorError from './content_editor_error.vue'; +import ContentEditorProvider from './content_editor_provider.vue'; +import EditorStateObserver from './editor_state_observer.vue'; +import FormattingBubbleMenu from './formatting_bubble_menu.vue'; import TopToolbar from './top_toolbar.vue'; export default { components: { - GlAlert, + GlLoadingIcon, + ContentEditorError, + ContentEditorProvider, TiptapEditorContent, TopToolbar, + FormattingBubbleMenu, + EditorStateObserver, }, props: { - contentEditor: { - type: ContentEditor, + renderMarkdown: { + type: Function, required: true, }, + uploadsPath: { + type: String, + required: true, + }, + extensions: { + type: Array, + required: false, + default: () => [], + }, + serializerConfig: { + type: Object, + required: false, + default: () => {}, + }, }, data() { return { - error: '', + isLoadingContent: false, + focused: false, }; }, - mounted() { - this.contentEditor.tiptapEditor.on('error', (error) => { - this.error = error; + created() { + const { renderMarkdown, uploadsPath, extensions, serializerConfig } = this; + + // This is a non-reactive attribute intentionally since this is a complex object. + this.contentEditor = createContentEditor({ + renderMarkdown, + uploadsPath, + extensions, + serializerConfig, }); + + this.contentEditor.on(LOADING_CONTENT_EVENT, this.displayLoadingIndicator); + this.contentEditor.on(LOADING_SUCCESS_EVENT, this.hideLoadingIndicator); + this.contentEditor.on(LOADING_ERROR_EVENT, this.hideLoadingIndicator); + this.$emit('initialized', this.contentEditor); + }, + beforeDestroy() { + this.contentEditor.dispose(); + this.contentEditor.off(LOADING_CONTENT_EVENT, this.displayLoadingIndicator); + this.contentEditor.off(LOADING_SUCCESS_EVENT, this.hideLoadingIndicator); + this.contentEditor.off(LOADING_ERROR_EVENT, this.hideLoadingIndicator); + }, + methods: { + displayLoadingIndicator() { + this.isLoadingContent = true; + }, + hideLoadingIndicator() { + this.isLoadingContent = false; + }, + focus() { + this.focused = true; + }, + blur() { + this.focused = false; + }, + notifyChange() { + this.$emit('change', { + empty: this.contentEditor.empty, + }); + }, }, }; </script> <template> - <div> - <gl-alert v-if="error" class="gl-mb-6" variant="danger" @dismiss="error = ''"> - {{ error }} - </gl-alert> - <div - data-testid="content-editor" - class="md-area" - :class="{ 'is-focused': contentEditor.tiptapEditor.isFocused }" - > - <top-toolbar ref="toolbar" class="gl-mb-4" :content-editor="contentEditor" /> - <tiptap-editor-content class="md" :editor="contentEditor.tiptapEditor" /> + <content-editor-provider :content-editor="contentEditor"> + <div> + <editor-state-observer @docUpdate="notifyChange" @focus="focus" @blur="blur" /> + <content-editor-error /> + <div + data-testid="content-editor" + data-qa-selector="content_editor_container" + class="md-area" + :class="{ 'is-focused': focused }" + > + <top-toolbar ref="toolbar" class="gl-mb-4" /> + <formatting-bubble-menu /> + <div v-if="isLoadingContent" class="gl-w-full gl-display-flex gl-justify-content-center"> + <gl-loading-icon size="sm" /> + </div> + <tiptap-editor-content v-else class="md" :editor="contentEditor.tiptapEditor" /> + </div> </div> - </div> + </content-editor-provider> </template> diff --git a/app/assets/javascripts/content_editor/components/content_editor_error.vue b/app/assets/javascripts/content_editor/components/content_editor_error.vue new file mode 100644 index 00000000000..031ea92a7e9 --- /dev/null +++ b/app/assets/javascripts/content_editor/components/content_editor_error.vue @@ -0,0 +1,31 @@ +<script> +import { GlAlert } from '@gitlab/ui'; +import EditorStateObserver from './editor_state_observer.vue'; + +export default { + components: { + GlAlert, + EditorStateObserver, + }, + data() { + return { + error: null, + }; + }, + methods: { + displayError({ error }) { + this.error = error; + }, + dismissError() { + this.error = null; + }, + }, +}; +</script> +<template> + <editor-state-observer @error="displayError"> + <gl-alert v-if="error" class="gl-mb-6" variant="danger" @dismiss="dismissError"> + {{ error }} + </gl-alert> + </editor-state-observer> +</template> diff --git a/app/assets/javascripts/content_editor/components/content_editor_provider.vue b/app/assets/javascripts/content_editor/components/content_editor_provider.vue new file mode 100644 index 00000000000..630aff9858f --- /dev/null +++ b/app/assets/javascripts/content_editor/components/content_editor_provider.vue @@ -0,0 +1,24 @@ +<script> +export default { + provide() { + // We can't use this.contentEditor due to bug in vue-apollo when + // provide is called in beforeCreate + // See https://github.com/vuejs/vue-apollo/pull/1153 for details + const { contentEditor } = this.$options.propsData; + + return { + contentEditor, + tiptapEditor: contentEditor.tiptapEditor, + }; + }, + props: { + contentEditor: { + type: Object, + required: true, + }, + }, + render() { + return this.$slots.default; + }, +}; +</script> diff --git a/app/assets/javascripts/content_editor/components/editor_state_observer.vue b/app/assets/javascripts/content_editor/components/editor_state_observer.vue new file mode 100644 index 00000000000..2eeb0719096 --- /dev/null +++ b/app/assets/javascripts/content_editor/components/editor_state_observer.vue @@ -0,0 +1,40 @@ +<script> +import { debounce } from 'lodash'; + +export const tiptapToComponentMap = { + update: 'docUpdate', + selectionUpdate: 'selectionUpdate', + transaction: 'transaction', + focus: 'focus', + blur: 'blur', + error: 'error', +}; + +const getComponentEventName = (tiptapEventName) => tiptapToComponentMap[tiptapEventName]; + +export default { + inject: ['tiptapEditor'], + created() { + this.disposables = []; + + Object.keys(tiptapToComponentMap).forEach((tiptapEvent) => { + const eventHandler = debounce((params) => this.handleTipTapEvent(tiptapEvent, params), 100); + + this.tiptapEditor?.on(tiptapEvent, eventHandler); + + this.disposables.push(() => this.tiptapEditor?.off(tiptapEvent, eventHandler)); + }); + }, + beforeDestroy() { + this.disposables.forEach((dispose) => dispose()); + }, + methods: { + handleTipTapEvent(tiptapEvent, params) { + this.$emit(getComponentEventName(tiptapEvent), params); + }, + }, + render() { + return this.$slots.default; + }, +}; +</script> diff --git a/app/assets/javascripts/content_editor/components/formatting_bubble_menu.vue b/app/assets/javascripts/content_editor/components/formatting_bubble_menu.vue new file mode 100644 index 00000000000..6c00480b87e --- /dev/null +++ b/app/assets/javascripts/content_editor/components/formatting_bubble_menu.vue @@ -0,0 +1,67 @@ +<script> +import { GlButtonGroup } from '@gitlab/ui'; +import { BubbleMenu } from '@tiptap/vue-2'; +import { BUBBLE_MENU_TRACKING_ACTION } from '../constants'; +import trackUIControl from '../services/track_ui_control'; +import ToolbarButton from './toolbar_button.vue'; + +export default { + components: { + BubbleMenu, + GlButtonGroup, + ToolbarButton, + }, + inject: ['tiptapEditor'], + methods: { + trackToolbarControlExecution({ contentType, value }) { + trackUIControl({ action: BUBBLE_MENU_TRACKING_ACTION, property: contentType, value }); + }, + }, +}; +</script> +<template> + <bubble-menu class="gl-shadow gl-rounded-base" :editor="tiptapEditor"> + <gl-button-group> + <toolbar-button + data-testid="bold" + content-type="bold" + icon-name="bold" + editor-command="toggleBold" + category="primary" + size="medium" + :label="__('Bold text')" + @execute="trackToolbarControlExecution" + /> + <toolbar-button + data-testid="italic" + content-type="italic" + icon-name="italic" + editor-command="toggleItalic" + category="primary" + size="medium" + :label="__('Italic text')" + @execute="trackToolbarControlExecution" + /> + <toolbar-button + data-testid="strike" + content-type="strike" + icon-name="strikethrough" + editor-command="toggleStrike" + category="primary" + size="medium" + :label="__('Strikethrough')" + @execute="trackToolbarControlExecution" + /> + <toolbar-button + data-testid="code" + content-type="code" + icon-name="code" + editor-command="toggleCode" + category="primary" + size="medium" + :label="__('Code')" + @execute="trackToolbarControlExecution" + /> + </gl-button-group> + </bubble-menu> +</template> diff --git a/app/assets/javascripts/content_editor/components/toolbar_button.vue b/app/assets/javascripts/content_editor/components/toolbar_button.vue index 0af12812f3b..cdb877152d4 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_button.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_button.vue @@ -1,23 +1,21 @@ <script> import { GlButton, GlTooltipDirective as GlTooltip } from '@gitlab/ui'; -import { Editor as TiptapEditor } from '@tiptap/vue-2'; +import EditorStateObserver from './editor_state_observer.vue'; export default { components: { GlButton, + EditorStateObserver, }, directives: { GlTooltip, }, + inject: ['tiptapEditor'], props: { iconName: { type: String, required: true, }, - tiptapEditor: { - type: TiptapEditor, - required: true, - }, contentType: { type: String, required: true, @@ -31,13 +29,31 @@ export default { required: false, default: '', }, - }, - computed: { - isActive() { - return this.tiptapEditor.isActive(this.contentType) && this.tiptapEditor.isFocused; + variant: { + type: String, + required: false, + default: 'default', }, + category: { + type: String, + required: false, + default: 'tertiary', + }, + size: { + type: String, + required: false, + default: 'small', + }, + }, + data() { + return { + isActive: null, + }; }, methods: { + updateActive({ editor }) { + this.isActive = editor.isActive(this.contentType) && editor.isFocused; + }, execute() { const { contentType } = this; @@ -51,15 +67,17 @@ export default { }; </script> <template> - <gl-button - v-gl-tooltip - category="tertiary" - size="small" - class="gl-mx-2" - :class="{ active: isActive }" - :aria-label="label" - :title="label" - :icon="iconName" - @click="execute" - /> + <editor-state-observer @transaction="updateActive"> + <gl-button + v-gl-tooltip + :variant="variant" + :category="category" + :size="size" + :class="{ active: isActive }" + :aria-label="label" + :title="label" + :icon="iconName" + @click="execute" + /> + </editor-state-observer> </template> diff --git a/app/assets/javascripts/content_editor/components/toolbar_image_button.vue b/app/assets/javascripts/content_editor/components/toolbar_image_button.vue index ebeee16dbec..649e23c29aa 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_image_button.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_image_button.vue @@ -8,9 +8,8 @@ import { GlDropdownItem, GlTooltipDirective as GlTooltip, } from '@gitlab/ui'; -import { Editor as TiptapEditor } from '@tiptap/vue-2'; -import { acceptedMimes } from '../extensions/image'; -import { getImageAlt } from '../services/utils'; +import { acceptedMimes } from '../services/upload_helpers'; +import { extractFilename } from '../services/utils'; export default { components: { @@ -24,12 +23,7 @@ export default { directives: { GlTooltip, }, - props: { - tiptapEditor: { - type: TiptapEditor, - required: true, - }, - }, + inject: ['tiptapEditor'], data() { return { imgSrc: '', @@ -47,7 +41,7 @@ export default { .setImage({ src: this.imgSrc, canonicalSrc: this.imgSrc, - alt: getImageAlt(this.imgSrc), + alt: extractFilename(this.imgSrc), }) .run(); @@ -64,7 +58,7 @@ export default { this.tiptapEditor .chain() .focus() - .uploadImage({ + .uploadAttachment({ file: e.target.files[0], }) .run(); @@ -73,7 +67,7 @@ export default { this.emitExecute('upload'); }, }, - acceptedMimes, + acceptedMimes: acceptedMimes.image, }; </script> <template> @@ -104,6 +98,7 @@ export default { name="content_editor_image" :accept="$options.acceptedMimes" class="gl-display-none" + data-qa-selector="file_upload_field" @change="onFileSelect" /> </gl-dropdown> diff --git a/app/assets/javascripts/content_editor/components/toolbar_link_button.vue b/app/assets/javascripts/content_editor/components/toolbar_link_button.vue index 8f57959a73f..ff525e52873 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_link_button.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_link_button.vue @@ -8,10 +8,9 @@ import { GlDropdownItem, GlTooltipDirective as GlTooltip, } from '@gitlab/ui'; -import { Editor as TiptapEditor } from '@tiptap/vue-2'; +import Link from '../extensions/link'; import { hasSelection } from '../services/utils'; - -export const linkContentType = 'link'; +import EditorStateObserver from './editor_state_observer.vue'; export default { components: { @@ -21,34 +20,32 @@ export default { GlDropdownDivider, GlDropdownItem, GlButton, + EditorStateObserver, }, directives: { GlTooltip, }, - props: { - tiptapEditor: { - type: TiptapEditor, - required: true, - }, - }, + inject: ['tiptapEditor'], data() { return { linkHref: '', + isActive: false, }; }, - computed: { - isActive() { - return this.tiptapEditor.isActive(linkContentType); + methods: { + resetFields() { + this.imgSrc = ''; + this.$refs.fileSelector.value = ''; }, - }, - mounted() { - this.tiptapEditor.on('selectionUpdate', ({ editor }) => { - const { canonicalSrc, href } = editor.getAttributes(linkContentType); + openFileUpload() { + this.$refs.fileSelector.click(); + }, + updateLinkState({ editor }) { + const { canonicalSrc, href } = editor.getAttributes(Link.name); + this.isActive = editor.isActive(Link.name); this.linkHref = canonicalSrc || href; - }); - }, - methods: { + }, updateLink() { this.tiptapEditor .chain() @@ -60,45 +57,70 @@ export default { }) .run(); - this.$emit('execute', { contentType: linkContentType }); + this.$emit('execute', { contentType: Link.name }); }, selectLink() { const { tiptapEditor } = this; // a selection has already been made by the user, so do nothing if (!hasSelection(tiptapEditor)) { - tiptapEditor.chain().focus().extendMarkRange(linkContentType).run(); + tiptapEditor.chain().focus().extendMarkRange(Link.name).run(); } }, removeLink() { this.tiptapEditor.chain().focus().unsetLink().run(); - this.$emit('execute', { contentType: linkContentType }); + this.$emit('execute', { contentType: Link.name }); + }, + onFileSelect(e) { + this.tiptapEditor + .chain() + .focus() + .uploadAttachment({ + file: e.target.files[0], + }) + .run(); + + this.resetFields(); + this.$emit('execute', { contentType: Link.name }); }, }, }; </script> <template> - <gl-dropdown - v-gl-tooltip - :aria-label="__('Insert link')" - :title="__('Insert link')" - :toggle-class="{ active: isActive }" - size="small" - category="tertiary" - icon="link" - @show="selectLink()" - > - <gl-dropdown-form class="gl-px-3!"> - <gl-form-input-group v-model="linkHref" :placeholder="__('Link URL')"> - <template #append> - <gl-button variant="confirm" @click="updateLink()">{{ __('Apply') }}</gl-button> - </template> - </gl-form-input-group> - </gl-dropdown-form> - <gl-dropdown-divider v-if="isActive" /> - <gl-dropdown-item v-if="isActive" @click="removeLink()"> - {{ __('Remove link') }} - </gl-dropdown-item> - </gl-dropdown> + <editor-state-observer @transaction="updateLinkState"> + <gl-dropdown + v-gl-tooltip + :aria-label="__('Insert link')" + :title="__('Insert link')" + :toggle-class="{ active: isActive }" + size="small" + category="tertiary" + icon="link" + @show="selectLink()" + > + <gl-dropdown-form class="gl-px-3!"> + <gl-form-input-group v-model="linkHref" :placeholder="__('Link URL')"> + <template #append> + <gl-button variant="confirm" @click="updateLink">{{ __('Apply') }}</gl-button> + </template> + </gl-form-input-group> + </gl-dropdown-form> + <gl-dropdown-divider /> + <gl-dropdown-item v-if="isActive" @click="removeLink"> + {{ __('Remove link') }} + </gl-dropdown-item> + <gl-dropdown-item v-else @click="openFileUpload"> + {{ __('Upload file') }} + </gl-dropdown-item> + + <input + ref="fileSelector" + type="file" + name="content_editor_attachment" + class="gl-display-none" + @change="onFileSelect" + /> + </gl-dropdown> + </editor-state-observer> </template> diff --git a/app/assets/javascripts/content_editor/components/toolbar_table_button.vue b/app/assets/javascripts/content_editor/components/toolbar_table_button.vue index 49d3006e9bf..46db806da94 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_table_button.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_table_button.vue @@ -1,29 +1,23 @@ <script> import { GlDropdown, GlDropdownDivider, GlDropdownForm, GlButton } from '@gitlab/ui'; -import { Editor as TiptapEditor } from '@tiptap/vue-2'; import { __, sprintf } from '~/locale'; import { clamp } from '../services/utils'; export const tableContentType = 'table'; -const MIN_ROWS = 3; -const MIN_COLS = 3; -const MAX_ROWS = 8; -const MAX_COLS = 8; +const MIN_ROWS = 5; +const MIN_COLS = 5; +const MAX_ROWS = 10; +const MAX_COLS = 10; export default { components: { + GlButton, GlDropdown, GlDropdownDivider, GlDropdownForm, - GlButton, - }, - props: { - tiptapEditor: { - type: TiptapEditor, - required: true, - }, }, + inject: ['tiptapEditor'], data() { return { maxRows: MIN_ROWS, @@ -68,22 +62,22 @@ export default { }; </script> <template> - <gl-dropdown size="small" category="tertiary" icon="table"> - <gl-dropdown-form class="gl-px-3! gl-w-auto!"> - <div class="gl-w-auto!"> - <div v-for="r of list(maxRows)" :key="r" class="gl-display-flex"> - <gl-button - v-for="c of list(maxCols)" - :key="c" - :data-testid="`table-${r}-${c}`" - :class="{ 'gl-bg-blue-50!': r <= rows && c <= cols }" - :aria-label="getButtonLabel(r, c)" - class="gl-display-inline! gl-px-0! gl-w-5! gl-h-5! gl-rounded-0!" - @mouseover="setRowsAndCols(r, c)" - @click="insertTable()" - /> - </div> - <gl-dropdown-divider /> + <gl-dropdown size="small" category="tertiary" icon="table" class="table-dropdown"> + <gl-dropdown-form class="gl-px-3!"> + <div v-for="r of list(maxRows)" :key="r" class="gl-display-flex"> + <gl-button + v-for="c of list(maxCols)" + :key="c" + :data-testid="`table-${r}-${c}`" + :class="{ 'active gl-bg-blue-50!': r <= rows && c <= cols }" + :aria-label="getButtonLabel(r, c)" + class="table-creator-grid-item gl-display-inline gl-rounded-0! gl-w-6! gl-h-6! gl-p-0!" + @mouseover="setRowsAndCols(r, c)" + @click="insertTable()" + /> + </div> + <gl-dropdown-divider class="gl-my-3! gl-mx-n3!" /> + <div class="gl-px-1"> {{ getButtonLabel(rows, cols) }} </div> </gl-dropdown-form> diff --git a/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue b/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue index 473fc472c1b..13728d4001d 100644 --- a/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue +++ b/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue @@ -1,29 +1,25 @@ <script> import { GlDropdown, GlDropdownItem, GlTooltipDirective as GlTooltip } from '@gitlab/ui'; -import { Editor as TiptapEditor } from '@tiptap/vue-2'; import { __ } from '~/locale'; import { TEXT_STYLE_DROPDOWN_ITEMS } from '../constants'; +import EditorStateObserver from './editor_state_observer.vue'; export default { components: { GlDropdown, GlDropdownItem, + EditorStateObserver, }, directives: { GlTooltip, }, - props: { - tiptapEditor: { - type: TiptapEditor, - required: true, - }, + inject: ['tiptapEditor'], + data() { + return { + activeItem: null, + }; }, computed: { - activeItem() { - return TEXT_STYLE_DROPDOWN_ITEMS.find((item) => - this.tiptapEditor.isActive(item.contentType, item.commandParams), - ); - }, activeItemLabel() { const { activeItem } = this; @@ -31,6 +27,11 @@ export default { }, }, methods: { + updateActiveItem({ editor }) { + this.activeItem = TEXT_STYLE_DROPDOWN_ITEMS.find((item) => + editor.isActive(item.contentType, item.commandParams), + ); + }, execute(item) { const { editorCommand, contentType, commandParams } = item; const value = commandParams?.level; @@ -38,8 +39,8 @@ export default { if (editorCommand) { this.tiptapEditor .chain() - .focus() [editorCommand](commandParams || {}) + .focus() .run(); } @@ -56,20 +57,25 @@ export default { }; </script> <template> - <gl-dropdown - v-gl-tooltip="$options.i18n.placeholder" - size="small" - :disabled="!activeItem" - :text="activeItemLabel" - > - <gl-dropdown-item - v-for="(item, index) in $options.items" - :key="index" - is-check-item - :is-checked="isActive(item)" - @click="execute(item)" + <editor-state-observer @transaction="updateActiveItem"> + <gl-dropdown + v-gl-tooltip="$options.i18n.placeholder" + size="small" + data-qa-selector="text_style_dropdown" + :disabled="!activeItem" + :text="activeItemLabel" > - {{ item.label }} - </gl-dropdown-item> - </gl-dropdown> + <gl-dropdown-item + v-for="(item, index) in $options.items" + :key="index" + is-check-item + :is-checked="isActive(item)" + data-qa-selector="text_style_menu_item" + :data-qa-text-style="item.label" + @click="execute(item)" + > + {{ item.label }} + </gl-dropdown-item> + </gl-dropdown> + </editor-state-observer> </template> diff --git a/app/assets/javascripts/content_editor/components/top_toolbar.vue b/app/assets/javascripts/content_editor/components/top_toolbar.vue index fafc7a660e7..82a449ae6af 100644 --- a/app/assets/javascripts/content_editor/components/top_toolbar.vue +++ b/app/assets/javascripts/content_editor/components/top_toolbar.vue @@ -1,7 +1,5 @@ <script> -import Tracking from '~/tracking'; -import { CONTENT_EDITOR_TRACKING_LABEL, TOOLBAR_CONTROL_TRACKING_ACTION } from '../constants'; -import { ContentEditor } from '../services/content_editor'; +import trackUIControl from '../services/track_ui_control'; import Divider from './divider.vue'; import ToolbarButton from './toolbar_button.vue'; import ToolbarImageButton from './toolbar_image_button.vue'; @@ -9,10 +7,6 @@ import ToolbarLinkButton from './toolbar_link_button.vue'; import ToolbarTableButton from './toolbar_table_button.vue'; import ToolbarTextStyleDropdown from './toolbar_text_style_dropdown.vue'; -const trackingMixin = Tracking.mixin({ - label: CONTENT_EDITOR_TRACKING_LABEL, -}); - export default { components: { ToolbarButton, @@ -22,19 +16,9 @@ export default { ToolbarImageButton, Divider, }, - mixins: [trackingMixin], - props: { - contentEditor: { - type: ContentEditor, - required: true, - }, - }, methods: { - trackToolbarControlExecution({ contentType: property, value }) { - this.track(TOOLBAR_CONTROL_TRACKING_ACTION, { - property, - value, - }); + trackToolbarControlExecution({ contentType, value }) { + trackUIControl({ property: contentType, value }); }, }, }; @@ -45,7 +29,6 @@ export default { > <toolbar-text-style-dropdown data-testid="text-styles" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <divider /> @@ -53,99 +36,91 @@ export default { data-testid="bold" content-type="bold" icon-name="bold" + class="gl-mx-2" editor-command="toggleBold" :label="__('Bold text')" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <toolbar-button data-testid="italic" content-type="italic" icon-name="italic" + class="gl-mx-2" editor-command="toggleItalic" :label="__('Italic text')" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <toolbar-button data-testid="strike" content-type="strike" icon-name="strikethrough" + class="gl-mx-2" editor-command="toggleStrike" :label="__('Strikethrough')" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <toolbar-button data-testid="code" content-type="code" icon-name="code" + class="gl-mx-2" editor-command="toggleCode" :label="__('Code')" - :tiptap-editor="contentEditor.tiptapEditor" - @execute="trackToolbarControlExecution" - /> - <toolbar-link-button - data-testid="link" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> + <toolbar-link-button data-testid="link" @execute="trackToolbarControlExecution" /> <divider /> <toolbar-image-button ref="imageButton" data-testid="image" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <toolbar-button data-testid="blockquote" content-type="blockquote" icon-name="quote" + class="gl-mx-2" editor-command="toggleBlockquote" :label="__('Insert a quote')" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <toolbar-button data-testid="code-block" content-type="codeBlock" icon-name="doc-code" + class="gl-mx-2" editor-command="toggleCodeBlock" :label="__('Insert a code block')" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <toolbar-button data-testid="bullet-list" content-type="bulletList" icon-name="list-bulleted" + class="gl-mx-2" editor-command="toggleBulletList" :label="__('Add a bullet list')" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <toolbar-button data-testid="ordered-list" content-type="orderedList" icon-name="list-numbered" + class="gl-mx-2" editor-command="toggleOrderedList" :label="__('Add a numbered list')" - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> <toolbar-button data-testid="horizontal-rule" content-type="horizontalRule" icon-name="dash" + class="gl-mx-2" editor-command="setHorizontalRule" :label="__('Add a horizontal rule')" - :tiptap-editor="contentEditor.tiptapEditor" - @execute="trackToolbarControlExecution" - /> - <toolbar-table-button - :tiptap-editor="contentEditor.tiptapEditor" @execute="trackToolbarControlExecution" /> + <toolbar-table-button @execute="trackToolbarControlExecution" /> </div> </template> <style> |