Implement choose editor modal for page CRUD flows
closes RCX-2444 flag=block_editor Test plan: - Create a page and check the editor choice modal is opened up. - Select the RCE and check the flow behaves normally - Create a new page and select the block editor and check the block editor flow works like it should - Create a page via module and edit it later, check the editor choice pops up, check both paths. - Edit a page already with some content and check the editor choice does not popup. - Try the page creation a couple times and check th remember my choice checkbox - Check the modal no longer show up - Clear the user preference that controls editor choice - Try again and check the modal shows up like before Change-Id: Ief8f5716ecca5e3f7d56a631c42cb3a284002ab5 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/360710 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Ed Schiebel <eschiebel@instructure.com> QA-Review: Ed Schiebel <eschiebel@instructure.com> Product-Review: Luis Oliveira <luis.oliveira@instructure.com>
This commit is contained in:
parent
5a74bc95b8
commit
b69e4c3bca
|
@ -1752,7 +1752,7 @@ class UsersController < ApplicationController
|
|||
create_user
|
||||
end
|
||||
|
||||
BOOLEAN_PREFS = %i[manual_mark_as_read collapse_global_nav collapse_course_nav hide_dashcard_color_overlays release_notes_badge_disabled comment_library_suggestions_enabled elementary_dashboard_disabled].freeze
|
||||
BOOLEAN_PREFS = %i[manual_mark_as_read collapse_global_nav collapse_course_nav hide_dashcard_color_overlays release_notes_badge_disabled comment_library_suggestions_enabled elementary_dashboard_disabled default_to_block_editor].freeze
|
||||
|
||||
# @API Update user settings.
|
||||
# Update an existing user's settings.
|
||||
|
@ -1975,6 +1975,41 @@ class UsersController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
# @API Update text editor preference
|
||||
# Updates a user's default choice for text editor. This allows
|
||||
# the Choose an Editor propmts to preload the user's preference.
|
||||
#
|
||||
#
|
||||
# @argument text_editor_preference [String, "block_editor"|"rce"|""]
|
||||
# The identifier for the editor.
|
||||
#
|
||||
# @example_request
|
||||
#
|
||||
# curl 'https://<canvas>/api/v1/users/<user_id>/prefered_editor \
|
||||
# -X PUT \
|
||||
# -F 'text_editor_preference=rce'
|
||||
# -H 'Authorization: Bearer <token>'
|
||||
#
|
||||
# @example_response
|
||||
# {
|
||||
# "text_editor_preference": "rce"
|
||||
# }
|
||||
def set_text_editor_preference
|
||||
user = api_find(User, params[:id])
|
||||
|
||||
return unless authorized_action(user, @current_user, [:manage, :manage_user_details])
|
||||
|
||||
raise ActiveRecord::RecordInvalid if %w[rce block_editor].exclude?(params[:text_editor_preference]) && params[:text_editor_preference] != ""
|
||||
|
||||
params[:text_editor_preference] = nil if params[:text_editor_preference] == ""
|
||||
|
||||
if user.set_preference(:text_editor_preference, params[:text_editor_preference])
|
||||
render(json: { text_editor_preference: user.reload.get_preference(:text_editor_preference) })
|
||||
else
|
||||
render(json: user.errors, status: :bad_request)
|
||||
end
|
||||
end
|
||||
|
||||
# @API Get dashboard positions
|
||||
#
|
||||
# Returns all dashboard positions that have been saved for a user.
|
||||
|
|
|
@ -141,6 +141,10 @@ class WikiPagesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def create_block_editor
|
||||
BlockEditor.create! root_account_id: @page.root_account_id, context: @page, editor_version: BlockEditor::LATEST_VERSION, blocks: BlockEditor::BLANK_PAGE
|
||||
end
|
||||
|
||||
def revisions
|
||||
if @page.grants_right?(@current_user, session, :read_revisions)
|
||||
if (!@context.conditional_release? && !Account.site_admin.feature_enabled?(:selective_release_backend)) || enforce_assignment_visible(@page)
|
||||
|
@ -175,6 +179,7 @@ class WikiPagesController < ApplicationController
|
|||
wiki_index_menu_tools: external_tools_display_hashes(:wiki_index_menu),
|
||||
DISPLAY_SHOW_ALL_LINK: tab_enabled?(context.class::TAB_PAGES, no_render: true) && !@k5_details_view,
|
||||
CAN_SET_TODO_DATE: context.grants_any_right?(@current_user, session, :manage_content, :manage_course_content_edit),
|
||||
text_editor_preference: @current_user&.reload&.get_preference(:text_editor_preference)
|
||||
}
|
||||
if Account.site_admin.feature_enabled?(:permanent_page_links)
|
||||
title_availability_path = context.is_a?(Course) ? api_v1_course_page_title_availability_path : api_v1_group_page_title_availability_path
|
||||
|
|
|
@ -24,6 +24,23 @@ class BlockEditor < ActiveRecord::Base
|
|||
|
||||
alias_attribute :version, :editor_version
|
||||
|
||||
LATEST_VERSION = "0.2"
|
||||
|
||||
BLANK_PAGE = {
|
||||
ROOT: {
|
||||
type: {
|
||||
resolvedName: "PageBlock"
|
||||
},
|
||||
isCanvas: true,
|
||||
props: {},
|
||||
displayName: "Page",
|
||||
custom: {},
|
||||
hidden: false,
|
||||
nodes: [],
|
||||
linkedNodes: {}
|
||||
}
|
||||
}.freeze
|
||||
|
||||
def set_root_account_id
|
||||
self.root_account_id = context&.root_account_id unless root_account_id
|
||||
end
|
||||
|
|
|
@ -1995,6 +1995,10 @@ class User < ActiveRecord::Base
|
|||
!!preferences[:collapse_course_nav]
|
||||
end
|
||||
|
||||
def text_editor_preference
|
||||
preferences[:text_editor_preference]
|
||||
end
|
||||
|
||||
# ***** OHI If you're going to add a lot of data into `preferences` here maybe take a look at app/models/user_preference_value.rb instead ***
|
||||
# it will store the data in a separate table on the db and lighten the load on poor `users`
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ class UserPreferenceValue < ActiveRecord::Base
|
|||
add_user_preference :unread_rubric_comments, use_sub_keys: true
|
||||
add_user_preference :module_links_default_new_tab
|
||||
add_user_preference :viewed_auto_subscribed_account_calendars
|
||||
add_user_preference :text_editor_preference
|
||||
|
||||
def self.settings
|
||||
@preference_settings ||= {}
|
||||
|
|
|
@ -157,6 +157,7 @@ CanvasRails::Application.routes.draw do
|
|||
concern :pages do
|
||||
resources :wiki_pages, path: :pages, except: %i[update destroy new], constraints: { id: %r{[^/]+} } do
|
||||
get "revisions" => "wiki_pages#revisions", :as => :revisions
|
||||
put "create_block_editor" => "wiki_pages#create_block_editor", :as => :create_block_editor
|
||||
end
|
||||
|
||||
get "wiki" => "wiki_pages#front_page", :as => :wiki
|
||||
|
@ -1628,6 +1629,8 @@ CanvasRails::Application.routes.draw do
|
|||
get "users/:id/colors/:asset_string", controller: "users", action: "get_custom_color"
|
||||
put "users/:id/colors/:asset_string", controller: "users", action: "set_custom_color"
|
||||
|
||||
put "users/:id/text_editor_preference", controller: "users", action: "set_text_editor_preference"
|
||||
|
||||
get "users/:id/new_user_tutorial_statuses", action: "get_new_user_tutorial_statuses"
|
||||
put "users/:id/new_user_tutorial_statuses/:page_name", action: "set_new_user_tutorial_status"
|
||||
|
||||
|
|
|
@ -2047,6 +2047,8 @@
|
|||
path: '/api/v1/users/:id/settings'
|
||||
- verb: POST
|
||||
path: '/api/v1/users/:id/split'
|
||||
- verb: PUT
|
||||
path: "/api/v1/users/:id/text_editor_preference"
|
||||
- verb: GET
|
||||
path: '/api/v1/users/:user_id/avatars'
|
||||
- verb: GET
|
||||
|
|
|
@ -22,6 +22,8 @@ module BlockEditorPage
|
|||
def create_wiki_page(course)
|
||||
get "/courses/#{course.id}/pages"
|
||||
f("a.new_page").click
|
||||
click_INSTUI_Select_option(f("[data-testid=\"choose-an-editor-dropdown\"]"), "Try the Block Editor")
|
||||
fj("button:contains('Continue')").click
|
||||
wait_for_block_editor
|
||||
end
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import {useScope as useI18nScope} from '@canvas/i18n'
|
|||
import WikiPage from '@canvas/wiki/backbone/models/WikiPage'
|
||||
import PaginatedCollectionView from '@canvas/pagination/backbone/views/PaginatedCollectionView'
|
||||
import WikiPageEditView from '@canvas/wiki/backbone/views/WikiPageEditView'
|
||||
import renderChooseEditorModal from '@canvas/block-editor/react/renderChooseEditorModal'
|
||||
import itemView from './WikiPageIndexItemView'
|
||||
import template from '../../jst/WikiPageIndex.handlebars'
|
||||
import {deletePages} from '../../react/apiClient'
|
||||
|
@ -45,12 +46,15 @@ export default class WikiPageIndexView extends PaginatedCollectionView {
|
|||
this.mixin({
|
||||
events: {
|
||||
'click .delete_pages': 'confirmDeletePages',
|
||||
'click .new_page': 'createNewPage',
|
||||
'keyclick .new_page': 'createNewPage',
|
||||
'click .new_page': 'openChooseEditorModalMaybe',
|
||||
'keyclick .new_page': 'openChooseEditorModalMaybe',
|
||||
'click .new_rce_page': 'openRCE',
|
||||
'keyclick .new_rce_page': 'openRCE',
|
||||
'click .new_block_editor_page': 'openBlockEditor',
|
||||
'keyclick .new_block_editor_page': 'openBlockEditor',
|
||||
'click .header-row a[data-sort-field]': 'sort',
|
||||
'click .header-bar-right .menu_tool_link': 'openExternalTool',
|
||||
'click .pages-mobile-header a[data-sort-mobile-field]': 'sortBySelect',
|
||||
'click #toggle_block_editor': 'toggleBlockEditor',
|
||||
},
|
||||
|
||||
els: {
|
||||
|
@ -104,8 +108,6 @@ export default class WikiPageIndexView extends PaginatedCollectionView {
|
|||
if (!this.selectedPages) this.selectedPages = {}
|
||||
this.itemViewOptions.selectedPages = this.selectedPages
|
||||
|
||||
this.createNewPageWithBlockEditor = !!ENV.FEATURES?.BLOCK_EDITOR
|
||||
|
||||
this.collection.on('fetch', () => {
|
||||
if (!this.fetched) {
|
||||
this.fetched = true
|
||||
|
@ -148,6 +150,29 @@ export default class WikiPageIndexView extends PaginatedCollectionView {
|
|||
}
|
||||
}
|
||||
|
||||
openRCE(e) {
|
||||
e.preventDefault()
|
||||
this.createNewPage(e, 'rce')
|
||||
}
|
||||
|
||||
openBlockEditor(e) {
|
||||
e.preventDefault()
|
||||
this.createNewPage(e, 'block_editor')
|
||||
}
|
||||
|
||||
openChooseEditorModalMaybe(e) {
|
||||
if (window.ENV.text_editor_preference != null) {
|
||||
return this.createNewPage(e, window.ENV.text_editor_preference)
|
||||
}
|
||||
|
||||
const createPageAction = editor => {
|
||||
this.createNewPage(e, editor)
|
||||
}
|
||||
ENV.FEATURES?.BLOCK_EDITOR
|
||||
? renderChooseEditorModal(e, createPageAction)
|
||||
: this.createNewPage(e)
|
||||
}
|
||||
|
||||
sortBySelect(event) {
|
||||
event.preventDefault()
|
||||
const {sortMobileField, sortMobileKey} = event.target.dataset
|
||||
|
@ -204,10 +229,6 @@ export default class WikiPageIndexView extends PaginatedCollectionView {
|
|||
}
|
||||
}
|
||||
|
||||
toggleBlockEditor(ev) {
|
||||
this.createNewPageWithBlockEditor = ev.target.checked
|
||||
}
|
||||
|
||||
confirmDeletePages(ev) {
|
||||
if (ev != null) {
|
||||
ev.preventDefault()
|
||||
|
@ -237,7 +258,7 @@ export default class WikiPageIndexView extends PaginatedCollectionView {
|
|||
$('.delete_pages').focus()
|
||||
}
|
||||
|
||||
createNewPage(ev) {
|
||||
createNewPage(ev, editor = 'rce') {
|
||||
if (ev != null) {
|
||||
ev.preventDefault()
|
||||
}
|
||||
|
@ -249,13 +270,14 @@ export default class WikiPageIndexView extends PaginatedCollectionView {
|
|||
this.editModel = new WikiPage(
|
||||
{
|
||||
editing_roles: this.default_editing_roles,
|
||||
editor: this.createNewPageWithBlockEditor ? 'block_editor' : 'rce',
|
||||
block_editor_attributes: this.createNewPageWithBlockEditor
|
||||
? {
|
||||
version: '1',
|
||||
blocks: undefined,
|
||||
}
|
||||
: null,
|
||||
editor,
|
||||
block_editor_attributes:
|
||||
editor !== 'rce'
|
||||
? {
|
||||
version: '0.2',
|
||||
blocks: undefined,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
{contextAssetString: this.contextAssetString}
|
||||
)
|
||||
|
@ -413,6 +435,10 @@ export default class WikiPageIndexView extends PaginatedCollectionView {
|
|||
json.hasWikiIndexPlacements = this.wikiIndexPlacements.length > 0
|
||||
json.wikiIndexPlacements = this.wikiIndexPlacements
|
||||
|
||||
json.block_editor_is_preferred = window.ENV.text_editor_preference === 'block_editor'
|
||||
json.rce_is_preferred = window.ENV.text_editor_preference === 'rce'
|
||||
json.no_preferred_editor = window.ENV.text_editor_preference === null
|
||||
|
||||
json.block_editor = !!ENV.FEATURES?.BLOCK_EDITOR
|
||||
return json
|
||||
}
|
||||
|
|
|
@ -4,31 +4,48 @@
|
|||
<div class="sticky-toolbar-with-right-side" data-sticky>
|
||||
<div class="header-bar">
|
||||
<div class="header-bar-right">
|
||||
{{#if block_editor}}
|
||||
<label class="checkbox" for="toggle_block_editor" style="margin-inline-end: 4px;">
|
||||
<input type="checkbox" value="1" id="toggle_block_editor" aria-label="toggle block editor" checked>
|
||||
{{#t}}Create with block editor{{/t}}
|
||||
</label>
|
||||
{{/if}}
|
||||
{{#if CAN.DELETE}}
|
||||
<button class="btn delete_pages" tabindex="0" aria-label="{{#t}}Delete selected pages{{/t}}" disabled>
|
||||
<i class="icon-trash" role="presentation"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#if CAN.CREATE}}
|
||||
<a class="btn btn-primary icon-plus new_page" role="button" tabindex="0" aria-label="{{#t 'buttons.new_page_label'}}Add a page{{/t}}">{{#t 'buttons.new_page'}}Page{{/t}}</a>
|
||||
{{#if hasWikiIndexPlacements}}
|
||||
<div class="inline-block">
|
||||
<a class="al-trigger btn" role="button" aria-haspopup="true" aria-owns="toolbar-1" href="#">
|
||||
<i class="icon-more" aria-hidden="true"></i>
|
||||
<span class="screenreader-only">{{#t}}Pages Settings{{/t}}</span>
|
||||
</a>
|
||||
|
||||
<ul id="toolbar-1" class="al-options" role="menu" aria-hidden="true" aria-expanded="false">
|
||||
{{>ui/shared/external-tools/jst/_external_tools_menu.handlebars wikiIndexPlacements}}
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
{{#if CAN.DELETE}}
|
||||
<button class="btn delete_pages" tabindex="0" aria-label="{{#t}}Delete selected pages{{/t}}" disabled>
|
||||
<i class="icon-trash" role="presentation"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#if CAN.CREATE}}
|
||||
<a class="btn btn-primary icon-plus new_page" role="button" tabindex="0" aria-label="{{#t 'buttons.new_page_label'}}Add a page{{/t}}">
|
||||
{{#if block_editor_is_preferred }} {{#t 'buttons.new_page'}}Page{{/t}} {{/if}}
|
||||
{{#if no_preferred_editor }} {{#t 'buttons.new_page'}}Page{{/t}} {{/if}}
|
||||
{{#if rce_is_preferred }} {{#t 'buttons.new_rce_page'}}RCE Page{{/t}} {{/if}}
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if CAN.CREATE}}
|
||||
<div style="margin-top: 15px; text-align: right;">
|
||||
{{#if block_editor_is_preferred }}
|
||||
<a href="#" class="new_rce_page">
|
||||
{{#t 'buttons.use_the_rce'}}Use the RCE{{/t}}
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
{{#if rce_is_preferred }}
|
||||
<a href="#" class="new_block_editor_page">
|
||||
{{#t 'buttons.try_the_block_editor'}}Try the Block Editor{{/t}}
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if hasWikiIndexPlacements}}
|
||||
<div class="inline-block">
|
||||
<a class="al-trigger btn" role="button" aria-haspopup="true" aria-owns="toolbar-1" href="#">
|
||||
<i class="icon-more" aria-hidden="true"></i>
|
||||
<span class="screenreader-only">{{#t}}Pages Settings{{/t}}</span>
|
||||
</a>
|
||||
|
||||
<ul id="toolbar-1" class="al-options" role="menu" aria-hidden="true" aria-expanded="false">
|
||||
{{>ui/shared/external-tools/jst/_external_tools_menu.handlebars wikiIndexPlacements}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -144,4 +161,5 @@
|
|||
{{/unless}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div id="choose-editor-mount-point"></div>
|
||||
</div>
|
||||
|
|
|
@ -27,6 +27,7 @@ import template from '../../jst/WikiPage.handlebars'
|
|||
import StickyHeaderMixin from '@canvas/wiki/backbone/views/StickyHeaderMixin'
|
||||
import WikiPageDeleteDialog from '@canvas/wiki/backbone/views/WikiPageDeleteDialog'
|
||||
import WikiPageReloadView from '@canvas/wiki/backbone/views/WikiPageReloadView'
|
||||
import renderChooseEditorModal from '@canvas/block-editor/react/renderChooseEditorModal'
|
||||
import PublishButtonView from '@canvas/publish-button-view'
|
||||
import {useScope as useI18nScope} from '@canvas/i18n'
|
||||
import htmlEscape from '@instructure/html-escape'
|
||||
|
@ -37,6 +38,7 @@ import DirectShareUserModal from '@canvas/direct-sharing/react/components/Direct
|
|||
import DirectShareCourseTray from '@canvas/direct-sharing/react/components/DirectShareCourseTray'
|
||||
import {renderFrontPagePill} from '@canvas/wiki/react/renderFrontPagePill'
|
||||
import ItemAssignToManager from '@canvas/context-modules/differentiated-modules/react/Item/ItemAssignToManager'
|
||||
import doFetchApi from "@canvas/do-fetch-api-effect";
|
||||
|
||||
const I18n = useI18nScope('pages')
|
||||
|
||||
|
@ -55,6 +57,8 @@ export default class WikiPageView extends Backbone.View {
|
|||
|
||||
this.prototype.events = {
|
||||
'click .delete_page': 'deleteWikiPage',
|
||||
'click .edit-wiki': 'openChooseEditorModalMaybe',
|
||||
'keyclick .edit-wiki': 'openChooseEditorModalMaybe',
|
||||
'click .use-as-front-page-menu-item': 'useAsFrontPage',
|
||||
'click .unset-as-front-page-menu-item': 'unsetAsFrontPage',
|
||||
'click .direct-share-send-to-menu-item': 'openSendTo',
|
||||
|
@ -179,6 +183,35 @@ export default class WikiPageView extends Backbone.View {
|
|||
this.maybeRenderBlockEditorContent()
|
||||
}
|
||||
|
||||
async openChooseEditorModalMaybe(e) {
|
||||
if (
|
||||
this.model.get('body') === null &&
|
||||
!this.model.get('block_editor_attributes')?.blocks &&
|
||||
ENV.FEATURES?.BLOCK_EDITOR
|
||||
) {
|
||||
if (window.ENV.text_editor_preference == null) {
|
||||
renderChooseEditorModal(e, async editor => {
|
||||
if (editor === 'block_editor') {
|
||||
await doFetchApi({
|
||||
path: `/courses/${this.course_id}/pages/${this.model.get('url')}/create_block_editor`,
|
||||
method: 'PUT',
|
||||
})
|
||||
}
|
||||
window.location.href = `${window.location.href.split('?')[0]}/edit?editor=${editor}`
|
||||
})
|
||||
} else if (window.ENV.text_editor_preference === 'block_editor') {
|
||||
e.preventDefault();
|
||||
await doFetchApi({
|
||||
path: `/courses/${this.course_id}/pages/${this.model.get('url')}/create_block_editor`,
|
||||
method: 'PUT',
|
||||
})
|
||||
window.location.href = `${window.location.href.split('?')[0]}/edit${
|
||||
window.location.href.split('?')[1] ? `?${window.location.href.split('?')[1]}` : ''
|
||||
}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
navigateToLinkAnchor() {
|
||||
const anchor_name = window.location.hash.replace(/^#/, '')
|
||||
if (anchor_name.length) {
|
||||
|
|
|
@ -110,4 +110,5 @@
|
|||
{{convertApiUserContent body}}
|
||||
{{/if}}
|
||||
<div id="assign-to-mount-point"></div>
|
||||
<div id="choose-editor-mount-point"></div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright (C) 2023 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, {useState} from 'react'
|
||||
import {useScope as useI18nScope} from '@canvas/i18n'
|
||||
import {Modal} from '@instructure/ui-modal'
|
||||
import {Button, CloseButton} from '@instructure/ui-buttons'
|
||||
import {IconExternalLinkLine} from '@instructure/ui-icons'
|
||||
import {Text} from '@instructure/ui-text'
|
||||
import {View} from '@instructure/ui-view'
|
||||
import {Heading} from '@instructure/ui-heading'
|
||||
import {SimpleSelect} from '@instructure/ui-simple-select'
|
||||
import {Checkbox} from '@instructure/ui-checkbox'
|
||||
import {Flex} from '@instructure/ui-flex'
|
||||
import {Link} from '@instructure/ui-link'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import doFetchApi from '@canvas/do-fetch-api-effect'
|
||||
import type {ChooseEditorModalProps, EditorPrefEnv, EditorTypes} from './types'
|
||||
import {type GlobalEnv} from '@canvas/global/env/GlobalEnv'
|
||||
|
||||
declare const ENV: GlobalEnv & EditorPrefEnv
|
||||
|
||||
const I18n = useI18nScope('block-editor')
|
||||
|
||||
const ChooseEditorModal = (props: ChooseEditorModalProps) => {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(true)
|
||||
const [rememberMyChoice, setRememberMyChoice] = useState<boolean>(
|
||||
ENV.text_editor_preference !== null
|
||||
)
|
||||
const [editorChoice, setEditorChoice] = useState<'rce' | 'block_editor' | ''>(
|
||||
ENV.text_editor_preference || ''
|
||||
)
|
||||
const [erroredForm, setErroredForm] = useState<boolean>(false)
|
||||
const close = () => {
|
||||
setIsOpen(false)
|
||||
props.onClose()
|
||||
}
|
||||
|
||||
const validEditorChoice = () => {
|
||||
if (['rce', 'block_editor'].includes(editorChoice)) {
|
||||
return true
|
||||
} else {
|
||||
setErroredForm(true)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const submitEditorChoice = async () => {
|
||||
if (validEditorChoice()) {
|
||||
await doFetchApi({
|
||||
method: 'PUT',
|
||||
path: `/api/v1/users/self/text_editor_preference`,
|
||||
body: {text_editor_preference: rememberMyChoice ? editorChoice : ''},
|
||||
})
|
||||
props.createPageAction(editorChoice)
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
label="New Way To Create"
|
||||
open={isOpen}
|
||||
onDismiss={close}
|
||||
size="small"
|
||||
themeOverride={{smallMaxWidth: '27.5rem'}}
|
||||
shouldCloseOnDocumentClick={true}
|
||||
>
|
||||
<Modal.Header>
|
||||
<Heading>{I18n.t('New Way to Create')}</Heading>
|
||||
<CloseButton placement="end" onClick={close} screenReaderLabel="Close" />
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Heading level="h3" margin="0 0 small 0">
|
||||
{I18n.t('Try the New Block Editor')}
|
||||
</Heading>
|
||||
<Text lineHeight="condensed">
|
||||
<div>
|
||||
{I18n.t(
|
||||
"We've introduced a new editor to give you more flexibility and power in content creation. Choose the editor that best suits your workflow."
|
||||
)}
|
||||
</div>
|
||||
</Text>
|
||||
<View
|
||||
as="div"
|
||||
borderRadius="medium"
|
||||
borderWidth="small"
|
||||
padding="small"
|
||||
margin="small 0 medium 0"
|
||||
>
|
||||
<Flex gap="small" justifyItems="space-between">
|
||||
<Flex.Item shouldShrink={true}>
|
||||
<Text size="x-small" lineHeight="condensed">
|
||||
<div>
|
||||
{I18n.t(
|
||||
'Read about key features and discover what you can create using the Block Editor.'
|
||||
)}
|
||||
</div>
|
||||
</Text>
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Link
|
||||
href="https://community.canvaslms.com/t5/Block-Editor-Beta/gh-p/block_editor"
|
||||
target="_blank"
|
||||
>
|
||||
<IconExternalLinkLine />
|
||||
</Link>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</View>
|
||||
<SimpleSelect
|
||||
onChange={(_e: React.SyntheticEvent, data: {value?: string | undefined | number}) => {
|
||||
setErroredForm(false)
|
||||
setEditorChoice(data.value as EditorTypes)
|
||||
}}
|
||||
renderLabel={
|
||||
<Flex>
|
||||
<Flex.Item>{I18n.t('Select an Editor')}</Flex.Item>
|
||||
<Flex.Item>*</Flex.Item>
|
||||
</Flex>
|
||||
}
|
||||
messages={erroredForm ? [{type: 'error', text: I18n.t('Please choose an editor')}] : []}
|
||||
placeholder={I18n.t('Select One')}
|
||||
defaultValue={editorChoice}
|
||||
data-testid="choose-an-editor-dropdown"
|
||||
>
|
||||
<SimpleSelect.Option id="block_editor" value="block_editor">
|
||||
{I18n.t('Try the Block Editor')}
|
||||
</SimpleSelect.Option>
|
||||
<SimpleSelect.Option id="rce" value="rce">
|
||||
{I18n.t('Use the RCE')}
|
||||
</SimpleSelect.Option>
|
||||
</SimpleSelect>
|
||||
<View as="div" padding="small 0 medium 0">
|
||||
<Checkbox
|
||||
checked={rememberMyChoice}
|
||||
onChange={() => {
|
||||
setRememberMyChoice(!rememberMyChoice)
|
||||
}}
|
||||
label={I18n.t('Remember my choice')}
|
||||
value="remember_my_choice"
|
||||
/>
|
||||
</View>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button margin="0 x-small 0 0" onClick={close}>
|
||||
{I18n.t('Cancel')}
|
||||
</Button>
|
||||
<Button color="primary" onClick={submitEditorChoice}>
|
||||
{I18n.t('Continue')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
const renderChooseEditorModal = (e: React.SyntheticEvent, createPageAction: () => {}) => {
|
||||
if (e != null) {
|
||||
e.preventDefault()
|
||||
}
|
||||
const rootElement = document.querySelector('#choose-editor-mount-point')
|
||||
if (rootElement) {
|
||||
const root = createRoot(rootElement)
|
||||
const editorModal = (
|
||||
<ChooseEditorModal
|
||||
createPageAction={createPageAction}
|
||||
onClose={() => {
|
||||
root.unmount()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
root.render(editorModal)
|
||||
return editorModal
|
||||
}
|
||||
}
|
||||
|
||||
export default renderChooseEditorModal
|
|
@ -61,6 +61,11 @@ export type BlockTemplateGridItemProps = {
|
|||
template?: BlockTemplate
|
||||
}
|
||||
|
||||
export type ChooseEditorModalProps = {
|
||||
createPageAction: any
|
||||
onClose: any
|
||||
}
|
||||
|
||||
export const SaveTemplateEvent = 'block-editor-save-block-template' as const
|
||||
export const DeleteTemplateEvent = 'block-editor-delete-block-template' as const
|
||||
export const PublishTemplateEvent = 'block-editor-publish-block-template' as const
|
||||
|
@ -69,3 +74,8 @@ export const dispatchTemplateEvent = (event: CustomEvent) => {
|
|||
const blockEditorEditor = document.querySelector('.block-editor-editor')
|
||||
blockEditorEditor?.dispatchEvent(event)
|
||||
}
|
||||
|
||||
export type EditorTypes = 'rce' | 'block_editor' | ''
|
||||
export type EditorPrefEnv = {
|
||||
text_editor_preference: EditorTypes
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ export default class WikiPageEditView extends ValidatedFormView {
|
|||
super.initialize(...arguments)
|
||||
if (!this.WIKI_RIGHTS) this.WIKI_RIGHTS = {}
|
||||
if (!this.PAGE_RIGHTS) this.PAGE_RIGHTS = {}
|
||||
this.queryParams = new URLSearchParams(window.location.search)
|
||||
this.enableAssignTo =
|
||||
window.ENV.FEATURES?.selective_release_ui_api &&
|
||||
ENV.COURSE_ID != null &&
|
||||
|
@ -148,6 +149,10 @@ export default class WikiPageEditView extends ValidatedFormView {
|
|||
json.show_assign_to = this.enableAssignTo
|
||||
json.edit_with_block_editor = this.model.get('editor') === 'block_editor'
|
||||
|
||||
if (this.queryParams.get('editor') === 'block_editor' && this.model.get('body') == null) {
|
||||
json.edit_with_block_editor = true
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue