From cca95abe8de1624f15d5502380aead1312cbde21 Mon Sep 17 00:00:00 2001
From: Andrew Duthie <andrew@andrewduthie.com>
Date: Thu, 6 Sep 2018 13:12:34 -0400
Subject: [PATCH] Editor: Add isCleanNewPost selector

---
 docs/data/data-core-editor.md               | 16 ++++++
 docs/reference/deprecated.md                |  2 +-
 packages/editor/CHANGELOG.md                |  2 +-
 packages/editor/src/store/selectors.js      | 15 ++++++
 packages/editor/src/store/test/selectors.js | 54 +++++++++++++++++++++
 5 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/docs/data/data-core-editor.md b/docs/data/data-core-editor.md
index dc8293ce2636b..df8e8f85be10f 100644
--- a/docs/data/data-core-editor.md
+++ b/docs/data/data-core-editor.md
@@ -49,6 +49,22 @@ false if the editing state matches the saved or new post.
 
 Whether unsaved values exist.
 
+### isCleanNewPost
+
+Returns true if there are no unsaved values for the current edit session and
+if the currently edited post is new (has never been saved before).
+
+Note: This selector is not currently used by the editor package, but is made
+available as an assumed-useful selector for external integrations.
+
+*Parameters*
+
+ * state: Global application state.
+
+*Returns*
+
+Whether new post and unsaved values exist.
+
 ### getCurrentPost
 
 Returns the post currently being edited in its last known saved state, not
diff --git a/docs/reference/deprecated.md b/docs/reference/deprecated.md
index f362cf53fd32d..c77e7496c3e63 100644
--- a/docs/reference/deprecated.md
+++ b/docs/reference/deprecated.md
@@ -22,7 +22,7 @@ Gutenberg's deprecation policy is intended to support backwards-compatibility fo
  - `wp.components.withContext` has been removed. Please use `wp.element.createContext` instead. See: https://reactjs.org/docs/context.html.
  - `wp.coreBlocks.registerCoreBlocks` has been removed. Please use `wp.blockLibrary.registerCoreBlocks` instead.
  - `wp.editor.DocumentTitle` component has been removed.
- - `isCleanNewPost` and `getDocumentTitle` selectors (`core/editor`) have been removed.
+ - `getDocumentTitle` selector (`core/editor`) has been removed.
 
 ## 3.7.0
 
diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md
index e249b16800883..07ce67eb21fcc 100644
--- a/packages/editor/CHANGELOG.md
+++ b/packages/editor/CHANGELOG.md
@@ -20,7 +20,7 @@
 - `editorMediaUpload` has been removed. Use `mediaUpload` instead.
 - Change how required built-ins are polyfilled with Babel 7 ([#9171](https://github.com/WordPress/gutenberg/pull/9171)).  If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods.
 - `wp.editor.DocumentTitle` component has been removed.
-- `isCleanNewPost` and `getDocumentTitle` selectors (`core/editor`) have been removed.
+- `getDocumentTitle` selector (`core/editor`) has been removed.
 
 ### Deprecation
 
diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js
index a96e7d1ef4cd8..c16b3dabd1632 100644
--- a/packages/editor/src/store/selectors.js
+++ b/packages/editor/src/store/selectors.js
@@ -98,6 +98,21 @@ export function isEditedPostDirty( state ) {
 	return state.editor.isDirty || inSomeHistory( state, isEditedPostDirty );
 }
 
+/**
+ * Returns true if there are no unsaved values for the current edit session and
+ * if the currently edited post is new (has never been saved before).
+ *
+ * Note: This selector is not currently used by the editor package, but is made
+ * available as an assumed-useful selector for external integrations.
+ *
+ * @param {Object} state Global application state.
+ *
+ * @return {boolean} Whether new post and unsaved values exist.
+ */
+export function isCleanNewPost( state ) {
+	return ! isEditedPostDirty( state ) && isEditedPostNew( state );
+}
+
 /**
  * Returns the post currently being edited in its last known saved state, not
  * including unsaved edits. Returns an object containing relevant default post
diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js
index 1efd15ce8462f..fcaf08737c528 100644
--- a/packages/editor/src/store/test/selectors.js
+++ b/packages/editor/src/store/test/selectors.js
@@ -20,6 +20,7 @@ const {
 	hasEditorRedo,
 	isEditedPostNew,
 	isEditedPostDirty,
+	isCleanNewPost,
 	getCurrentPost,
 	getCurrentPostId,
 	getCurrentPostLastRevisionId,
@@ -282,6 +283,59 @@ describe( 'selectors', () => {
 		} );
 	} );
 
+	describe( 'isCleanNewPost', () => {
+		it( 'should return true when the post is not dirty and has not been saved before', () => {
+			const state = {
+				editor: {
+					isDirty: false,
+				},
+				currentPost: {
+					id: 1,
+					status: 'auto-draft',
+				},
+				saving: {
+					requesting: false,
+				},
+			};
+
+			expect( isCleanNewPost( state ) ).toBe( true );
+		} );
+
+		it( 'should return false when the post is not dirty but the post has been saved', () => {
+			const state = {
+				editor: {
+					isDirty: false,
+				},
+				currentPost: {
+					id: 1,
+					status: 'draft',
+				},
+				saving: {
+					requesting: false,
+				},
+			};
+
+			expect( isCleanNewPost( state ) ).toBe( false );
+		} );
+
+		it( 'should return false when the post is dirty but the post has not been saved', () => {
+			const state = {
+				editor: {
+					isDirty: true,
+				},
+				currentPost: {
+					id: 1,
+					status: 'auto-draft',
+				},
+				saving: {
+					requesting: false,
+				},
+			};
+
+			expect( isCleanNewPost( state ) ).toBe( false );
+		} );
+	} );
+
 	describe( 'getCurrentPost', () => {
 		it( 'should return the current post', () => {
 			const state = {