Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update document.title when post title changes #1802

Merged
merged 10 commits into from
Jul 20, 2017
Merged

Conversation

westonruter
Copy link
Member

When the page is loading, the tab looks like:

image

The post title is then prepended to the original title that was output by PHP.

When clicking “New Post” link the tab looks like:

image

Upon typing “Initial” the tab is live updated to:

image

If the title is then cleared out, on a new post or an existing post, then the tab appears as:

image

Fixes #1737.

@westonruter westonruter requested a review from nylen July 7, 2017 23:45
@@ -119,6 +119,7 @@ export default connect(
return {
onUpdate( title ) {
dispatch( editPost( { title } ) );
dispatch( editPostTitle( title ) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just rely on the editPost action instead and this beeing a side-effect if the title is in the edits maybe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Done in 1dcd6f880af805344f503c1b1f2e9bdd604cbe63.

@@ -74,6 +74,17 @@ export function isEditedPostDirty( state ) {
}

/**
* Returns true if there are unsaved values for the current edit session and if
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function does the opposite right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Done in 1f6601a8568261ec92f8dabc8d2b853895e9abdc.

*/
export function isCleanNewPost( state ) {
return ! isEditedPostDirty( state ) && isEditedPostNew( state );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a unit test for this selector?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Done in 1f6601a8568261ec92f8dabc8d2b853895e9abdc.

@coveralls
Copy link

Coverage Status

Changes Unknown when pulling 1f6601a8568261ec92f8dabc8d2b853895e9abdc on add/document-title into ** on master**.

lib/register.php Outdated
@@ -44,7 +44,7 @@ function gutenberg_menu() {

add_submenu_page(
'gutenberg',
__( 'New Post', 'gutenberg' ),
__( 'Gutenberg ', 'gutenberg' ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing space intentional?

let originalDocumentTitle;

function populateDocumentTitle( title, isCleanNew = false ) {
if ( ! title.trim() ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like some of this logic makes more sense in a selector, like getDocumentTitle.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 8cec2c9.

if ( ! title.trim() ) {
title = isCleanNew ? __( 'New post' ) : __( '(Untitled)' );
}
if ( ! originalDocumentTitle ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of tracking this as inferred from the original title, should not the client be able to generate the desired title at any point in time, including when the editor first initializes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially. However, that would require that we export a template from PHP that we can use in JS. This was a quicker way to achieve that goal.

The Customizer implements this via:

I don't feel it is entirely clear where we should be exporting data from PHP to JS generally.

let originalDocumentTitle;

function populateDocumentTitle( title, isCleanNew = false ) {
if ( ! title.trim() ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be using the trimmed title when assigning document.title ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unnecessary because document.title has its whitespace automatically normalized: https://html.spec.whatwg.org/multipage/dom.html#document.title

export default {
RESET_POST( action, store ) {
setTimeout( () => { // Next-tick to ensure action has been applied to state in store .
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aduth This next-tick feels hacky, but I didn't see a reliable way to access the store with the action applied to it without doing so.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related: #1816 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative here is to not treat these as effects of the action, but rather handled by logic which has subscribed to store updates (e.g. a connected component, or a store.subscribe callback).

This is the approach we had taken in the Calypso editor. Basically: Instead of binding to the specific actions that we think would cause a change to the document title selector value, we instead re-run the selector any time any dispatch occurs, and if the result is different, apply the new title value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aduth check out the latest changes. It feels much better. I looked at the DocumentHead component in Calypso and used it to inspire a DocumentTitle component.

@westonruter
Copy link
Member Author

Rebased from 8cec2c9d and amended tests to use Jest API.

@westonruter
Copy link
Member Author

Rebasing. Former HEAD was 3efc5b8.

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work well in my testing. I left a few minor comments to address if you wish.

Only other thought I have is: Do we really want the document title to be assigned as the post's title? Per #1737 and current editor behavior, seems "Edit Post" could work just as well. Was there any conversation or thoughts behind the choice to use post title?

}
}

render() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: I think we should stick with convention to have render be last method in component, roughly something like:

  • Constructor
  • Lifecycle
  • Helper methods
  • Render

return state.editor.edits.title === undefined
? get( state.currentPost, 'title.raw' )
: state.editor.edits.title;
const editedTitle = get( state.editor, 'edits.title' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor thoughts:

  • get with string as the second argument is significantly slower than array alternative (benchmark)
  • state.editor.edits is always an object (based on reducer default value) and we don't need get to test its truthiness, for a trivial performance improvement: state.editor.edits.title
  • We should compose selectors vs. accessing state directly when possible, i.e. getPostEdits instead of state.editor.edits, getCurrentPost instead of state.currentPost
  • get accepts a third argument for default value, so this could be expressed as a single return value, though maybe not as readable

export function getDocumentTitle( state ) {
let title = getEditedPostTitle( state );

if ( ! title || '' === title.trim() ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe getEditedPostTitle should return an empty string as its default? In which case we could consolidate this as if ( ! title.trim() ) { . Not sure if there's a performance benefit to testing truthiness before a trim, but readability benefit in testing in single pass.

@westonruter
Copy link
Member Author

@aduth:

Only other thought I have is: Do we really want the document title to be assigned as the post's title? Per #1737 and current editor behavior, seems "Edit Post" could work just as well. Was there any conversation or thoughts behind the choice to use post title?

Good point. In fact, I just assumed that “new post” would be replaced with the post title. I think I instinctively wanted to include the post title in the title because when you have multiple posts open in separate tabs, it is very annoying to easily identify the tab for the post you want to edit. Case in point with 4 tabs open for separate posts:

image

So this PR fixes that usability issue.

@westonruter
Copy link
Member Author

I'll fix up your other issues you pointed out. Thank you for reviewing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants