🎨 Moved default focus in editor from body to title for new posts

refs https://github.com/TryGhost/Team/issues/707

Placing focus in the title aligns better with typical editorial process.

- switched `autofocus` attribute from body to title for new posts
- added a default value to the `mobiledoc` attr on the Post model, without it the autosave when moving from title to editor creates a forced re-render and clears the focus whilst you're typing
This commit is contained in:
Kevin Ansfield 2021-06-14 15:35:15 +01:00
parent bb825dfa13
commit 65ed9cc69c
6 changed files with 18 additions and 47 deletions

View File

@ -26,6 +26,7 @@
<GhTextarea
@class="gh-editor-title"
@placeholder={{@titlePlaceholder}}
@shouldFocus={{or @titleAutofocus false}}
@tabindex="1"
@autoExpand=".gh-koenig-editor"
@value={{readonly this.title}}
@ -39,7 +40,6 @@
<KoenigEditor
@mobiledoc={{@body}}
@placeholder={{@bodyPlaceholder}}
@autofocus={{or @bodyAutofocus false}}
@spellcheck={{true}}
@onChange={{@onBodyChange}}
@didCreateEditor={{this.onEditorCreated}}

View File

@ -97,7 +97,7 @@ export default Controller.extend({
/* public properties -----------------------------------------------------*/
leaveEditorTransition: null,
shouldFocusEditor: false,
shouldFocusTitle: false,
showDeletePostModal: false,
showLeaveEditorModal: false,
showReAuthenticateModal: false,
@ -436,7 +436,7 @@ export default Controller.extend({
this.set('post.emailSubject', this.get('post.emailSubjectScratch'));
if (!this.get('post.slug')) {
this.saveTitle.cancelAll();
this.saveTitleTask.cancelAll();
yield this.generateSlug.perform();
}
@ -595,12 +595,12 @@ export default Controller.extend({
return post;
}),
saveTitle: task(function* () {
saveTitleTask: task(function* () {
let post = this.post;
let currentTitle = post.get('title');
let newTitle = post.get('titleScratch').trim();
if (currentTitle && newTitle && newTitle === currentTitle) {
if ((currentTitle && newTitle && newTitle === currentTitle) || (!currentTitle && !newTitle)) {
return;
}
@ -662,8 +662,7 @@ export default Controller.extend({
setPost(post) {
// don't do anything else if we're setting the same post
if (post === this.post) {
// set autofocus as change signal to the persistent editor on new->edit
this.set('shouldFocusEditor', post.get('isNew'));
this.set('shouldFocusTitle', post.get('isNew'));
return;
}
@ -673,8 +672,8 @@ export default Controller.extend({
this.set('post', post);
this.backgroundLoader.perform();
// autofocus the editor if we have a new post
this.set('shouldFocusEditor', post.get('isNew'));
// autofocus the title if we have a new post
this.set('shouldFocusTitle', post.get('isNew'));
// need to set scratch values because they won't be present on first
// edit of the post
@ -770,7 +769,7 @@ export default Controller.extend({
this.set('post', null);
this.set('hasDirtyAttributes', false);
this.set('shouldFocusEditor', false);
this.set('shouldFocusTitle', false);
this.set('leaveEditorTransition', null);
this.set('showLeaveEditorModal', false);
this.set('showPostPreviewModal', false);

View File

@ -5,6 +5,7 @@ import boundOneWay from 'ghost-admin/utils/bound-one-way';
import moment from 'moment';
import {compare} from '@ember/utils';
// eslint-disable-next-line ghost/ember/no-observers
import {BLANK_DOC} from 'koenig-editor/components/koenig-editor';
import {computed, observer} from '@ember/object';
import {equal, filterBy, reads} from '@ember/object/computed';
import {isBlank} from '@ember/utils';
@ -94,7 +95,7 @@ export default Model.extend(Comparable, ValidationEngine, {
visibility: attr('string'),
metaDescription: attr('string'),
metaTitle: attr('string'),
mobiledoc: attr('json-string'),
mobiledoc: attr('json-string', {defaultValue: () => JSON.parse(JSON.stringify(BLANK_DOC))}),
plaintext: attr('string'),
publishedAtUTC: attr('moment-utc'),
slug: attr('string'),

View File

@ -68,12 +68,12 @@
--}}
<GhKoenigEditor
@title={{readonly this.post.titleScratch}}
@titleAutofocus={{this.shouldFocusTitle}}
@titlePlaceholder={{concat (capitalize this.post.displayName) " Title"}}
@onTitleChange={{action "updateTitleScratch"}}
@onTitleBlur={{action (perform this.saveTitle)}}
@onTitleBlur={{action (perform this.saveTitleTask)}}
@body={{readonly this.post.scratch}}
@bodyPlaceholder={{concat "Begin writing your " this.post.displayName "..."}}
@bodyAutofocus={{this.shouldFocusEditor}}
@onBodyChange={{action "updateScratch"}}
@headerOffset={{editor.headerHeight}}
@scrollContainerSelector=".gh-koenig-editor"

View File

@ -574,35 +574,6 @@ describe('Acceptance: Editor', function () {
expect(savedAuthors[1].name).to.equal('Waldo');
});
it('autosaves when title loses focus', async function () {
let role = this.server.create('role', {name: 'Administrator'});
this.server.create('user', {name: 'Admin', roles: [role]});
await visit('/editor');
// NOTE: there were checks here for the title element having focus
// but they were very temperamental whilst running tests in the
// browser so they've been left out for now
expect(
currentURL(),
'url on initial visit'
).to.equal('/editor/post');
await click('[data-test-editor-title-input]');
await blur('[data-test-editor-title-input]');
expect(
find('[data-test-editor-title-input]').value,
'title value after autosave'
).to.equal('');
expect(
currentURL(),
'url after autosave'
).to.equal('/editor/post/1');
});
it('saves post settings fields', async function () {
let post = this.server.create('post', {authors: [author]});

View File

@ -50,7 +50,7 @@ describe('Unit: Controller: editor', function () {
});
});
describe('saveTitle', function () {
describe('saveTitleTask', function () {
beforeEach(function () {
this.controller = this.owner.lookup('controller:editor');
this.controller.set('target', {send() {}});
@ -70,7 +70,7 @@ describe('Unit: Controller: editor', function () {
expect(controller.get('post.titleScratch')).to.not.be.ok;
controller.set('post.titleScratch', 'test');
await controller.get('saveTitle').perform();
await controller.get('saveTitleTask').perform();
expect(controller.get('post.titleScratch')).to.equal('test');
expect(controller.get('post.slug')).to.equal('test-slug');
@ -91,7 +91,7 @@ describe('Unit: Controller: editor', function () {
controller.set('post.titleScratch', 'New Title');
await controller.get('saveTitle').perform();
await controller.get('saveTitleTask').perform();
expect(controller.get('post.titleScratch')).to.equal('New Title');
expect(controller.get('post.slug')).to.equal('test-slug');
@ -115,7 +115,7 @@ describe('Unit: Controller: editor', function () {
expect(controller.get('post.titleScratch')).to.not.be.ok;
controller.set('post.titleScratch', 'test');
await controller.get('saveTitle').perform();
await controller.get('saveTitleTask').perform();
expect(controller.get('post.titleScratch')).to.equal('test');
expect(controller.get('post.slug')).to.not.be.ok;
@ -135,7 +135,7 @@ describe('Unit: Controller: editor', function () {
expect(controller.get('post.title')).to.not.be.ok;
controller.set('post.titleScratch', 'title');
await controller.get('saveTitle').perform();
await controller.get('saveTitleTask').perform();
expect(controller.get('post.titleScratch')).to.equal('title');
expect(controller.get('post.slug')).to.not.be.ok;