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 () {