diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue index 8b7e894667a91f488683e8785fa35da658d13186..26f6d1d683a946605c75fae7b615096f1666400c 100644 --- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue @@ -62,21 +62,30 @@ export default { ), primaryAction: s__('WikiPage|Retry'), }, - useNewEditor: s__('WikiPage|Use new editor'), + useNewEditor: { + primaryLabel: s__('WikiPage|Use the new editor'), + secondaryLabel: s__('WikiPage|Try this later'), + title: s__('WikiPage|Get a richer editing experience'), + text: s__( + "WikiPage|Try the new visual Markdown editor. Read the %{linkStart}documentation%{linkEnd} to learn what's currently supported.", + ), + }, switchToOldEditor: { - label: s__('WikiPage|Switch to old editor'), - helpText: s__("WikiPage|Switching will discard any changes you've made in the new editor."), + label: s__('WikiPage|Switch me back to the classic editor.'), + helpText: s__( + "WikiPage|This editor is in beta and may not display the page's contents properly. Switching back to the classic editor will discard changes you've made in the new editor.", + ), modal: { - title: s__('WikiPage|Are you sure you want to switch to the old editor?'), - primary: s__('WikiPage|Switch to old editor'), + title: s__('WikiPage|Are you sure you want to switch back to the classic editor?'), + primary: s__('WikiPage|Switch to classic editor'), cancel: s__('WikiPage|Keep editing'), text: s__( - "WikiPage|Switching to the old editor will discard any changes you've made in the new editor.", + "WikiPage|Switching to the classic editor will discard any changes you've made in the new editor.", ), }, }, - helpText: s__( - "WikiPage|This editor is in beta and may not display the page's contents properly.", + feedbackTip: s__( + 'Tell us your experiences with the new Markdown editor %{linkStart}in this feedback issue%{linkEnd}.', ), }, linksHelpText: s__( @@ -95,6 +104,7 @@ export default { }, cancel: s__('WikiPage|Cancel'), }, + contentEditorFeedbackIssue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/332629', components: { GlAlert, GlForm, @@ -120,6 +130,7 @@ export default { title: this.pageInfo.title?.trim() || '', format: this.pageInfo.format || 'markdown', content: this.pageInfo.content || '', + isContentEditorAlertDismissed: false, isContentEditorLoading: true, useContentEditor: false, commitMessage: '', @@ -170,11 +181,14 @@ export default { wikiSpecificMarkdownHelpPath() { return setUrlFragment(this.pageInfo.markdownHelpPath, 'wiki-specific-markdown'); }, + contentEditorHelpPath() { + return setUrlFragment(this.pageInfo.helpPath, 'gitlab-flavored-markdown-support'); + }, isMarkdownFormat() { return this.format === 'markdown'; }, - showContentEditorButton() { - return this.isMarkdownFormat && !this.useContentEditor; + showContentEditorAlert() { + return this.isMarkdownFormat && !this.useContentEditor && !this.isContentEditorAlertDismissed; }, disableSubmitButton() { return this.noContent || !this.title || this.contentEditorRenderFailed; @@ -276,15 +290,19 @@ export default { } }, - async trackContentEditorLoaded() { - await this.track(CONTENT_EDITOR_LOADED_ACTION); + trackContentEditorLoaded() { + this.track(CONTENT_EDITOR_LOADED_ACTION); }, - async trackFormSubmit() { + trackFormSubmit() { if (this.isContentEditorActive) { - await this.track(SAVED_USING_CONTENT_EDITOR_ACTION); + this.track(SAVED_USING_CONTENT_EDITOR_ACTION); } }, + + dismissContentEditorAlert() { + this.isContentEditorAlertDismissed = true; + }, }, }; </script> @@ -302,11 +320,9 @@ export default { :dismissible="false" variant="danger" :primary-button-text="$options.i18n.contentEditor.renderFailed.primaryAction" - @primaryAction="retryInitContentEditor()" + @primaryAction="retryInitContentEditor" > - <p> - {{ $options.i18n.contentEditor.renderFailed.message }} - </p> + {{ $options.i18n.contentEditor.renderFailed.message }} </gl-alert> <input :value="csrfToken" type="hidden" name="authenticity_token" /> @@ -364,46 +380,50 @@ export default { {{ label }} </option> </select> - <div> - <gl-button - v-if="showContentEditorButton" - category="secondary" - variant="confirm" - class="gl-mt-4" - @click="initContentEditor" - >{{ $options.i18n.contentEditor.useNewEditor }}</gl-button - > - <div v-if="isContentEditorActive" class="gl-mt-4 gl-display-flex"> - <div class="gl-mr-4"> - <gl-button category="secondary" variant="confirm" @click="confirmSwitchToOldEditor">{{ - $options.i18n.contentEditor.switchToOldEditor.label - }}</gl-button> - </div> - <div class="gl-mt-2"> - <gl-icon name="warning" /> - {{ $options.i18n.contentEditor.switchToOldEditor.helpText }} - </div> - </div> - <gl-modal - ref="confirmSwitchToOldEditorModal" - modal-id="confirm-switch-to-old-editor" - :title="$options.i18n.contentEditor.switchToOldEditor.modal.title" - :action-primary="{ text: $options.i18n.contentEditor.switchToOldEditor.modal.primary }" - :action-cancel="{ text: $options.i18n.contentEditor.switchToOldEditor.modal.cancel }" - @primary="switchToOldEditor" - > - {{ $options.i18n.contentEditor.switchToOldEditor.modal.text }} - </gl-modal> - </div> </div> </div> - <div class="form-group row"> + <div class="form-group row" data-testid="wiki-form-content-fieldset"> <div class="col-sm-2 col-form-label"> <label class="control-label-full-width" for="wiki_content">{{ $options.i18n.content.label }}</label> </div> <div class="col-sm-10"> + <gl-alert + v-if="showContentEditorAlert" + class="gl-mb-6" + variant="info" + :primary-button-text="$options.i18n.contentEditor.useNewEditor.primaryLabel" + :secondary-button-text="$options.i18n.contentEditor.useNewEditor.secondaryLabel" + :dismiss-label="$options.i18n.contentEditor.useNewEditor.secondaryLabel" + :title="$options.i18n.contentEditor.useNewEditor.title" + @primaryAction="initContentEditor" + @secondaryAction="dismissContentEditorAlert" + @dismiss="dismissContentEditorAlert" + > + <gl-sprintf :message="$options.i18n.contentEditor.useNewEditor.text"> + <template + #link="// eslint-disable-next-line vue/no-template-shadow + { content }" + ><gl-link + :href="contentEditorHelpPath" + target="_blank" + data-testid="content-editor-help-link" + >{{ content }}</gl-link + ></template + > + </gl-sprintf> + </gl-alert> + <gl-modal + ref="confirmSwitchToOldEditorModal" + modal-id="confirm-switch-to-old-editor" + :title="$options.i18n.contentEditor.switchToOldEditor.modal.title" + :action-primary="{ text: $options.i18n.contentEditor.switchToOldEditor.modal.primary }" + :action-cancel="{ text: $options.i18n.contentEditor.switchToOldEditor.modal.cancel }" + @primary="switchToOldEditor" + > + {{ $options.i18n.contentEditor.switchToOldEditor.modal.text }} + </gl-modal> <markdown-field v-if="!isContentEditorActive" :markdown-preview-path="pageInfo.markdownPreviewPath" @@ -434,6 +454,20 @@ export default { </markdown-field> <div v-if="isContentEditorActive"> + <gl-alert class="gl-mb-6" variant="tip" :dismissable="false"> + <gl-sprintf :message="$options.i18n.contentEditor.feedbackTip"> + <template + #link="// eslint-disable-next-line vue/no-template-shadow + { content }" + ><gl-link + :href="$options.contentEditorFeedbackIssue" + target="_blank" + data-testid="wiki-markdown-help-link" + >{{ content }}</gl-link + ></template + > + </gl-sprintf> + </gl-alert> <gl-loading-icon v-if="isContentEditorLoading" class="bordered-box gl-w-full gl-py-6" /> <content-editor v-else :content-editor="contentEditor" /> <input id="wiki_content" v-model.trim="content" type="hidden" name="wiki[content]" /> @@ -459,7 +493,10 @@ export default { > </gl-sprintf> <span v-else> - {{ $options.i18n.contentEditor.helpText }} + {{ $options.i18n.contentEditor.switchToOldEditor.helpText }} + <gl-button variant="link" @click="confirmSwitchToOldEditor">{{ + $options.i18n.contentEditor.switchToOldEditor.label + }}</gl-button> </span> </div> </div> diff --git a/doc/user/project/wiki/img/content_editor_v14.0.png b/doc/user/project/wiki/img/content_editor_v14.0.png index 9f4a736ab760b119d02286b1919f5f75b7f1054c..b44a633073db627a558cff972af984425a5c995c 100644 Binary files a/doc/user/project/wiki/img/content_editor_v14.0.png and b/doc/user/project/wiki/img/content_editor_v14.0.png differ diff --git a/doc/user/project/wiki/img/use_new_editor_button_v14.0.png b/doc/user/project/wiki/img/use_new_editor_button_v14.0.png index ceb907871a375551f2c862f331eedd6ee24af482..d9a5cf833023290672b88595817962956df76420 100644 Binary files a/doc/user/project/wiki/img/use_new_editor_button_v14.0.png and b/doc/user/project/wiki/img/use_new_editor_button_v14.0.png differ diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md index 34a59bbed223af5d58f2f64bcf3e072034b8b7a0..ed6a51665bdead6910d6ae767bc4a8989d602dd0 100644 --- a/doc/user/project/wiki/index.md +++ b/doc/user/project/wiki/index.md @@ -292,7 +292,7 @@ experience in the Wiki. To opt in for the new editor: 1. Create a new wiki page, or edit an existing one. 1. Ensure the wiki page uses the Markdown format. Other formats are not yet supported. -1. Below the **Format** select box, select **Use new editor**: +1. Below the **Format** select box, select **Use the new editor**:  @@ -308,12 +308,20 @@ experience in the Wiki. To opt in for the new editor: ### Switch back to the old editor -1. *If you're editing the page in the content editor,* scroll to **Format**. -1. Select **Switch to old editor**. -1. Select **Switch to old editor** in the confirmation popup to confirm. +1. *If you're editing the page in the content editor,* scroll to **Content**. +1. Select **Switch me back to the classic editor**. +1. Select **Switch to classic editor** in the confirmation popup to confirm. When you switch back to the old editor, any unsaved changes are lost. +### GitLab Flavored Markdown support + +Supporting all GitLab Flavored Markdown content types in the Content Editor is a work in progress. +For the status of the ongoing development for CommonMark and GitLab Flavored Markdown support, read: + +- [Basic Markdown formatting extensions](https://gitlab.com/groups/gitlab-org/-/epics/5404) epic. +- [GitLab Flavored Markdown extensions](https://gitlab.com/groups/gitlab-org/-/epics/5438) epic. + ## Resources - [Wiki settings for administrators](../../../administration/wikis/index.md) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6157729f6fd1813b9a2f5880476272f861d93e47..a202e27153b3c41316293017faeb70141ef0486c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31950,6 +31950,9 @@ msgstr "" msgid "Telephone number" msgstr "" +msgid "Tell us your experiences with the new Markdown editor %{linkStart}in this feedback issue%{linkEnd}." +msgstr "" + msgid "Template" msgstr "" @@ -36927,7 +36930,7 @@ msgstr "" msgid "WikiPage|An error occured while trying to render the content editor. Please try again later." msgstr "" -msgid "WikiPage|Are you sure you want to switch to the old editor?" +msgid "WikiPage|Are you sure you want to switch back to the classic editor?" msgstr "" msgid "WikiPage|Cancel" @@ -36948,6 +36951,9 @@ msgstr "" msgid "WikiPage|Format" msgstr "" +msgid "WikiPage|Get a richer editing experience" +msgstr "" + msgid "WikiPage|Keep editing" msgstr "" @@ -36963,16 +36969,16 @@ msgstr "" msgid "WikiPage|Save changes" msgstr "" -msgid "WikiPage|Switch to old editor" +msgid "WikiPage|Switch me back to the classic editor." msgstr "" -msgid "WikiPage|Switching to the old editor will discard any changes you've made in the new editor." +msgid "WikiPage|Switch to classic editor" msgstr "" -msgid "WikiPage|Switching will discard any changes you've made in the new editor." +msgid "WikiPage|Switching to the classic editor will discard any changes you've made in the new editor." msgstr "" -msgid "WikiPage|This editor is in beta and may not display the page's contents properly." +msgid "WikiPage|This editor is in beta and may not display the page's contents properly. Switching back to the classic editor will discard changes you've made in the new editor." msgstr "" msgid "WikiPage|Tip: You can move this page by adding the path to the beginning of the title." @@ -36987,10 +36993,16 @@ msgstr "" msgid "WikiPage|To link to a (new) page, simply type %{linkExample}. More examples are in the %{linkStart}documentation%{linkEnd}." msgstr "" +msgid "WikiPage|Try the new visual Markdown editor. Read the %{linkStart}documentation%{linkEnd} to learn what's currently supported." +msgstr "" + +msgid "WikiPage|Try this later" +msgstr "" + msgid "WikiPage|Update %{pageTitle}" msgstr "" -msgid "WikiPage|Use new editor" +msgid "WikiPage|Use the new editor" msgstr "" msgid "WikiPage|Write your content or drag files here…" diff --git a/spec/docs_screenshots/wiki_docs.rb b/spec/docs_screenshots/wiki_docs.rb index ce30b07182c578d783666e132e81b6f7f4571fac..e7b71408b1c8b033876c412af2d2a94182d374db 100644 --- a/spec/docs_screenshots/wiki_docs.rb +++ b/spec/docs_screenshots/wiki_docs.rb @@ -21,18 +21,18 @@ context 'switching to content editor' do it 'user/project/wiki/img/use_new_editor_button' do - screenshot_area = find('.js-quick-submit') + screenshot_area = find('[data-testid="wiki-form-content-fieldset"]') scroll_to screenshot_area - expect(screenshot_area).to have_content 'Use new editor' - set_crop_data(screenshot_area, 10) + expect(screenshot_area).to have_content 'Use the new editor' + set_crop_data(screenshot_area, 0) end end context 'content editor' do it 'user/project/wiki/img/content_editor' do - content_editor_testid = '[data-testid="content-editor"]' + content_editor_testid = '[data-testid="wiki-form-content-fieldset"]' - click_button 'Use new editor' + click_button 'Use the new editor' expect(page).to have_css(content_editor_testid) @@ -41,7 +41,7 @@ find("#{content_editor_testid} [contenteditable]").send_keys '## Using the Content Editor' - set_crop_data(screenshot_area, 50) + set_crop_data(screenshot_area, 0) end end end diff --git a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js index ac3974362e88089e0e51768ef4e38d25a67044e3..f36d6262b5fd92e763105f56d0c35c2be0efe2dc 100644 --- a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js +++ b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js @@ -27,9 +27,11 @@ describe('WikiForm', () => { const findMessage = () => wrapper.find('#wiki_message'); const findSubmitButton = () => wrapper.findByTestId('wiki-submit-button'); const findCancelButton = () => wrapper.findByRole('link', { name: 'Cancel' }); - const findUseNewEditorButton = () => wrapper.findByRole('button', { name: 'Use new editor' }); + const findUseNewEditorButton = () => wrapper.findByRole('button', { name: 'Use the new editor' }); + const findDismissContentEditorAlertButton = () => + wrapper.findByRole('button', { name: 'Try this later' }); const findSwitchToOldEditorButton = () => - wrapper.findByRole('button', { name: 'Switch to old editor' }); + wrapper.findByRole('button', { name: 'Switch me back to the classic editor.' }); const findTitleHelpLink = () => wrapper.findByRole('link', { name: 'More Information.' }); const findMarkdownHelpLink = () => wrapper.findByTestId('wiki-markdown-help-link'); @@ -284,7 +286,7 @@ describe('WikiForm', () => { ${'markdown'} | ${true} ${'rdoc'} | ${false} `( - 'switch to new editor button exists: $buttonExists if format is $format', + 'gl-alert containing "use new editor" button exists: $buttonExists if format is $format', async ({ format, buttonExists }) => { setFormat(format); @@ -294,6 +296,12 @@ describe('WikiForm', () => { }, ); + it('gl-alert containing "use new editor" button is dismissed on clicking dismiss button', async () => { + await findDismissContentEditorAlertButton().trigger('click'); + + expect(findUseNewEditorButton().exists()).toBe(false); + }); + const assertOldEditorIsVisible = () => { expect(wrapper.findComponent(ContentEditor).exists()).toBe(false); expect(wrapper.findComponent(MarkdownField).exists()).toBe(true); @@ -307,7 +315,7 @@ describe('WikiForm', () => { ); }; - it('shows old editor by default', assertOldEditorIsVisible); + it('shows classic editor by default', assertOldEditorIsVisible); describe('switch format to rdoc', () => { beforeEach(async () => { @@ -316,7 +324,7 @@ describe('WikiForm', () => { await wrapper.vm.$nextTick(); }); - it('continues to show the old editor', assertOldEditorIsVisible); + it('continues to show the classic editor', assertOldEditorIsVisible); describe('switch format back to markdown', () => { beforeEach(async () => { @@ -326,7 +334,7 @@ describe('WikiForm', () => { }); it( - 'still shows the old editor and does not automatically switch to the content editor ', + 'still shows the classic editor and does not automatically switch to the content editor ', assertOldEditorIsVisible, ); }); @@ -351,12 +359,12 @@ describe('WikiForm', () => { expect(findSubmitButton().props('disabled')).toBe(true); }); - describe('clicking "switch to old editor"', () => { + describe('clicking "switch to classic editor"', () => { beforeEach(() => { return findSwitchToOldEditorButton().trigger('click'); }); - it('switches to old editor directly without showing a modal', () => { + it('switches to classic editor directly without showing a modal', () => { expect(wrapper.findComponent(ContentEditor).exists()).toBe(false); expect(wrapper.findComponent(MarkdownField).exists()).toBe(true); }); @@ -374,10 +382,11 @@ describe('WikiForm', () => { expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); + it('shows a tip to send feedback', () => { + expect(wrapper.text()).toContain('Tell us your experiences with the new Markdown editor'); + }); + it('shows warnings that the rich text editor is in beta and may not work properly', () => { - expect(wrapper.text()).toContain( - "Switching will discard any changes you've made in the new editor.", - ); expect(wrapper.text()).toContain( "This editor is in beta and may not display the page's contents properly.", ); @@ -456,7 +465,7 @@ describe('WikiForm', () => { expect(findContent().element.value).toBe('hello **world**'); }); - describe('clicking "switch to old editor"', () => { + describe('clicking "switch to classic editor"', () => { let modal; beforeEach(async () => { @@ -470,7 +479,7 @@ describe('WikiForm', () => { expect(modal.vm.show).toHaveBeenCalled(); }); - describe('confirming "switch to old editor" in the modal', () => { + describe('confirming "switch to classic editor" in the modal', () => { beforeEach(async () => { wrapper.vm.contentEditor.tiptapEditor.commands.setContent( '<p>hello __world__ from content editor</p>', @@ -482,7 +491,7 @@ describe('WikiForm', () => { await wrapper.vm.$nextTick(); }); - it('switches to old editor', () => { + it('switches to classic editor', () => { expect(wrapper.findComponent(ContentEditor).exists()).toBe(false); expect(wrapper.findComponent(MarkdownField).exists()).toBe(true); }); @@ -493,7 +502,7 @@ describe('WikiForm', () => { ); }); - it('the old editor retains its old value and does not use the content from the content editor', () => { + it('the classic editor retains its old value and does not use the content from the content editor', () => { expect(findContent().element.value).toBe(' My page content '); }); });