Commit 342b05df authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '332895-content-editor-messaging' into 'master'

Improve beta state communication of content editor

See merge request gitlab-org/gitlab!63775
parents 946eedbb e36a419e
......@@ -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>
......
......@@ -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**:
![Use new editor button image](img/use_new_editor_button_v14.0.png)
......@@ -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)
......
......@@ -31968,6 +31968,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 ""
......@@ -36945,7 +36948,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"
......@@ -36966,6 +36969,9 @@ msgstr ""
msgid "WikiPage|Format"
msgstr ""
msgid "WikiPage|Get a richer editing experience"
msgstr ""
msgid "WikiPage|Keep editing"
msgstr ""
......@@ -36981,16 +36987,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."
......@@ -37005,10 +37011,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…"
......
......@@ -21,18 +21,18 @@ RSpec.describe 'Wiki', :js do
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 @@ RSpec.describe 'Wiki', :js do
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
......@@ -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 ');
});
});
......
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