diff --git a/ghost/admin/app/components/gh-koenig-editor-lexical.hbs b/ghost/admin/app/components/gh-koenig-editor-lexical.hbs index 50d763684a..2933f6000c 100644 --- a/ghost/admin/app/components/gh-koenig-editor-lexical.hbs +++ b/ghost/admin/app/components/gh-koenig-editor-lexical.hbs @@ -59,18 +59,22 @@ {{#if (feature 'editorSubtitle')}} -
+ {{#if @excerptErrorMessage}} +
+ {{@excerptErrorMessage}} +
+ {{/if}} +
{{/if}} diff --git a/ghost/admin/app/components/gh-koenig-editor-lexical.js b/ghost/admin/app/components/gh-koenig-editor-lexical.js index 4836f7c80f..643dc8cdf4 100644 --- a/ghost/admin/app/components/gh-koenig-editor-lexical.js +++ b/ghost/admin/app/components/gh-koenig-editor-lexical.js @@ -153,18 +153,8 @@ export default class GhKoenigEditorReactComponent extends Component { // Subhead ("excerpt") Actions ------------------------------------------- @action - updateExcerpt(event) { - this.args.onExcerptChange?.(event.target.value); - } - - @action - focusExcerpt() { - this.args.onExcerptFocus?.(); - } - - @action - blurExcerpt() { - this.args.onExcerptBlur?.(); + onExcerptInput(event) { + this.args.setExcerpt?.(event.target.value); } @action diff --git a/ghost/admin/app/controllers/lexical-editor.js b/ghost/admin/app/controllers/lexical-editor.js index b0240287bb..d42bdd6c36 100644 --- a/ghost/admin/app/controllers/lexical-editor.js +++ b/ghost/admin/app/controllers/lexical-editor.js @@ -285,8 +285,13 @@ export default class LexicalEditorController extends Controller { } @action - updateExcerptScratch(excerpt) { - this.set('post.customExcerptScratch', excerpt); + updateExcerpt(excerpt) { + this.post.customExcerpt = excerpt; + this.post.validate({property: 'customExcerpt'}); + } + + get excerptErrorMessage() { + return this.post.errors.errorsFor('customExcerpt')?.[0]?.message; } // updates local willPublish/Schedule values, does not get applied to diff --git a/ghost/admin/app/styles/layouts/editor.css b/ghost/admin/app/styles/layouts/editor.css index 6b0f8545c8..e7c0e1bef5 100644 --- a/ghost/admin/app/styles/layouts/editor.css +++ b/ghost/admin/app/styles/layouts/editor.css @@ -811,10 +811,22 @@ body[data-user-is-dragging] .gh-editor-feature-image-dropzone { border: none !important; } +.gh-editor-subtitle-error { + margin-top: .8rem; + color: var(--red-d1); + font-size: 1.4rem; + font-weight: 400; +} + .gh-editor-title-divider { margin: 1.6rem 0 4.8rem; } +.gh-editor-title-divider-error { + margin: .4rem 0 4.8rem; + border-top: 1px solid var(--red); +} + .gh-editor .tk-indicator { position: absolute; top: 15px; diff --git a/ghost/admin/app/templates/lexical-editor.hbs b/ghost/admin/app/templates/lexical-editor.hbs index 8db8681d03..77847a47eb 100644 --- a/ghost/admin/app/templates/lexical-editor.hbs +++ b/ghost/admin/app/templates/lexical-editor.hbs @@ -62,11 +62,12 @@
diff --git a/ghost/admin/app/validators/post.js b/ghost/admin/app/validators/post.js index ec32addd34..90f8a2e96d 100644 --- a/ghost/admin/app/validators/post.js +++ b/ghost/admin/app/validators/post.js @@ -62,7 +62,11 @@ export default BaseValidator.create({ customExcerpt(model) { if (!validator.isLength(model.customExcerpt || '', 0, 300)) { - model.errors.add('customExcerpt', 'Excerpt cannot be longer than 300 characters.'); + if (model.feature.editorSubtitle) { + model.errors.add('customExcerpt', 'Please keep the subtitle under 300 characters.'); + } else { + model.errors.add('customExcerpt', 'Excerpt cannot be longer than 300 characters.'); + } this.invalidate(); } }, diff --git a/ghost/admin/tests/acceptance/editor-test.js b/ghost/admin/tests/acceptance/editor-test.js index 612f2e7ddc..841164eafa 100644 --- a/ghost/admin/tests/acceptance/editor-test.js +++ b/ghost/admin/tests/acceptance/editor-test.js @@ -5,6 +5,7 @@ import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-sup import {beforeEach, describe, it} from 'mocha'; import {blur, click, currentRouteName, currentURL, fillIn, find, findAll, triggerEvent, typeIn} from '@ember/test-helpers'; import {datepickerSelect} from 'ember-power-datepicker/test-support'; +import {enableLabsFlag} from '../helpers/labs-flag'; import {expect} from 'chai'; import {selectChoose} from 'ember-power-select/test-support'; import {setupApplicationTest} from 'ember-mocha'; @@ -490,6 +491,38 @@ describe('Acceptance: Editor', function () { ).to.equal(0); }); + it('handles in-editor excerpt update and validation', async function () { + enableLabsFlag(this.server, 'editorSubtitle'); + + let post = this.server.create('post', {authors: [author], customExcerpt: 'Existing excerpt'}); + + await visit(`/editor/post/${post.id}`); + + expect(find('[data-test-textarea="subtitle"]'), 'initial textarea').to.be.visible; + expect(find('[data-test-textarea="subtitle"]'), 'initial textarea').to.have.value('Existing excerpt'); + + await fillIn('[data-test-textarea="subtitle"]', 'New excerpt'); + expect(find('[data-test-textarea="subtitle"]'), 'updated textarea').to.have.value('New excerpt'); + + await triggerEvent('[data-test-textarea="subtitle"]', 'keydown', { + key: 's', + keyCode: 83, // s + metaKey: ctrlOrCmd === 'command', + ctrlKey: ctrlOrCmd === 'ctrl' + }); + + expect(post.customExcerpt, 'saved excerpt').to.equal('New excerpt'); + + await fillIn('[data-test-textarea="subtitle"]', Array(302).join('a')); + + expect(find('[data-test-error="subtitle"]'), 'subtitle error').to.exist; + expect(find('[data-test-error="subtitle"]')).to.have.trimmed.text('Please keep the subtitle under 300 characters.'); + + await fillIn('[data-test-textarea="subtitle"]', Array(300).join('a')); + + expect(find('[data-test-error="subtitle"]'), 'subtitle error').to.not.exist; + }); + // https://github.com/TryGhost/Ghost/issues/11786 // NOTE: Flaky test with moving to Lexical editor, skipping for now it.skip('save shortcut works when tags/authors field is focused', async function () {