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

Implemented query panel in latest posts block. #3198

Merged
merged 1 commit into from
Dec 14, 2017

Conversation

jorgefilipecosta
Copy link
Member

Description

Ths PR aims to implement the query panel specified in issue #2662.

The panel is added to latest posts block. But this leaves a discussion point, it allows, for example, to show 8 oldest posts by setting sorting from oldest to newest.
To solve this we need to make a decision :
We rename this block to "Posts" allowing to show the most recent ones or oldest or other option the user chooses.
Or we maintain the most recent posts concept, we always retrieve from the database the N most recent posts and then we sort this recent posts in accordance with the sorting criteria the user selected.
Feel free to share your options.

How Has This Been Tested?

Try to change the values of the 3 different options in the query panel. Verify that the posts in the editor update in accordance with the option selected. Save the post on each option change, and open the post, verify the server side render also shows the selected option correctly.

Screenshots (jpeg or gifs if applicable):

screen shot 2017-10-27 at 12 16 01

screen shot 2017-10-27 at 12 16 35

screen shot 2017-10-27 at 12 16 23

Notes

There is a bug where component looks like loading forever if there are no posts that become more visible with this changes (because we can more easily select options without posts). A fix is being issued in #3180.

The function buildTermsTree in utils was extracted from HierarchicalTermSelector component. As a future PR, this component will be refactored to make use of the utils function (removing the duplicate version) and will also be simplified/refactored to make use of the new component TermTreeSelect created in this PR.

@jorgefilipecosta jorgefilipecosta added the [Status] In Progress Tracking issues with work in progress label Oct 27, 2017
@jorgefilipecosta jorgefilipecosta force-pushed the add/query-panel branch 2 times, most recently from 1ccd40e to fc9e2da Compare October 27, 2017 11:53
@jorgefilipecosta jorgefilipecosta changed the title Implemented query panel in latest posts block. WIP Implemented query panel in latest posts block. Oct 27, 2017
@jorgefilipecosta jorgefilipecosta self-assigned this Oct 27, 2017
@jorgefilipecosta jorgefilipecosta force-pushed the add/query-panel branch 5 times, most recently from d6618c4 to f11d90e Compare October 27, 2017 15:35
@jorgefilipecosta jorgefilipecosta changed the title WIP Implemented query panel in latest posts block. Implemented query panel in latest posts block. Oct 27, 2017
@jorgefilipecosta jorgefilipecosta added [Feature] Block API API that allows to express the block paradigm. Needs Design Feedback Needs general design feedback. [Feature] Inspector Controls The interface showing block settings and the controls available for each block [Type] Enhancement A suggestion for improvement. labels Oct 27, 2017
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.

Personally I'd have preferred to see the Latest Posts block updated to use withAPIData before enhancing it to handle additional data attributes.

'default' => 'date',
),
'categories' => array(
'type' => 'string',
Copy link
Member

Choose a reason for hiding this comment

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

Minor: Alignment on the arrow (for consistency at least)

value: 'title/asc',
},
{
label: __( 'Z → A' ),
Copy link
Member

Choose a reason for hiding this comment

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

Trying to think through localization impact here; would this be translated differently by language? We might consider at least including a translator comment to explain the intent here:

https://codex.wordpress.org/I18n_for_WordPress_Developers#Descriptions

onOrderChange,
onOrderByChange,
} ) {
return ( <div>
Copy link
Member

Choose a reason for hiding this comment

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

For styling consistency I'd recommend not ending lines with the opening tag of an element and rather having these on their own line. Especially in the case of the line below with <SelectControl it helps readability to associate props with the opening tag and not losing context with the condition in which it renders.

return (
	<div>
		{ ( onOrderChange || onOrderByChange ) && (
			<SelectControl
				label={ __( 'Order by' ) }

onChange={ ( value ) => {
const [ newOrderBy, newOrder ] = value.split( '/' );
if ( newOrder !== order ) {
( onOrderChange || noop )( newOrder );
Copy link
Member

Choose a reason for hiding this comment

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

We could probably test truthiness of the callback in the condition on the line above and avoid the dependency on noop. This also reads a little "clever".

import SelectControl from '../inspector-controls/select-control';

function getSelectOptions( terms, level = 0 ) {
return reduce( terms, ( options, term ) => (
Copy link
Member

Choose a reason for hiding this comment

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

You might consider Lodash's flatMap which I recently found in #3185 to perform much better than Array#reduce + Array#concat.

Further, we can use array spread syntax to simplify this a bit:

function getSelectOptions( terms, level = 0 ) {
	return flatMap( terms, ( term ) => [
		{
			value: term.id,
			label: repeat( '\u00A0', level * 3 ) + unescapeString( term.name ),
		},
		...getSelectOptions( term.children, level + 1 ),
	] );
}

}

export default function TermTreeSelect( { termsTree, label, noOptionLabel, selectedTerm, onChange } ) {
const options = ( noOptionLabel ? [ { value: '', label: noOptionLabel } ] : [] ).concat( getSelectOptions( termsTree ) );
Copy link
Member

@aduth aduth Oct 30, 2017

Choose a reason for hiding this comment

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

This is a long line and difficult to read, I would suggest breaking it down a bit.

const options = (
	noOptionLabel ? 
		[ { value: '', label: noOptionLabel } ] :
		[]
).concat( getSelectOptions( termsTree ) );

You could also use Lodash's compact to drop falsey values:

const options = compact( [
	noOptionLabel && { value: '', label: noOptionLabel },
	...getSelectOptions( termsTree ),
] );

@@ -0,0 +1,27 @@
/**
Copy link
Member

Choose a reason for hiding this comment

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

Can we get some unit tests for this?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added :) they were missing at the time because I was not certain if a utils function was right for this case.

@jorgefilipecosta jorgefilipecosta force-pushed the add/query-panel branch 7 times, most recently from 2290356 to dcb3e7a Compare October 31, 2017 22:43
@jorgefilipecosta
Copy link
Member Author

jorgefilipecosta commented Oct 31, 2017

Thank you for your suggestions @aduth. I think I addressed all of them excluding an alignment in a php file. I made some changes to make things look better, but type item in categories is still not aligned with other types of other attributes, as the other type elements are not siblings (they are "cousins") if we add more spaces we break a phpcs rule. Regarding the refactoring to withAPIData in latest posts, as postsCollection.fetch supported exactly the parameters we needed, I would prefer to do this change in a separate PR.

@jorgefilipecosta jorgefilipecosta removed the [Status] In Progress Tracking issues with work in progress label Nov 1, 2017
const { setAttributes } = this.props;

if ( postToShowCurrent === postToShowNext ) {
if ( this.props.attributes === nextProps.attributes ) {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we want to rely on a strict equality check here; these two objects may in fact be different references while containing the same values for postsToShow, order, orderBy, categories. If we need to, we can use Lodash's _.isEqual for a deep equality comparison.

categories: '/wp/v2/categories',
} ) );

export default flowRight( [
Copy link
Member

Choose a reason for hiding this comment

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

The flowRight here is unnecessary at this point (with a single higher-order component) and could be written as:

export default applyWithAPIData( CategorySelect );

onOrderByChange,
} ) {
return (
<div>
Copy link
Member

Choose a reason for hiding this comment

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

If the div is not serving a purpose on its own, wondering if we ought to just return as an array, with the only gotcha being we'd have to add keys to each rendered element, but this would reduce the total number of DOM nodes on the screen and reduce indentation in the component:

return [
	<SelectControl
		key="..."
	// ...
];

@jorgefilipecosta
Copy link
Member Author

Hi @aduth, thank you for your feedback 👍 , I rebased and addressed all the points.

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.

Requests incurred from unrelated attribute changes should be addressed.

'render_callback' => 'gutenberg_render_block_core_latest_posts',
) );
'attributes' =>
array(
Copy link
Member

Choose a reason for hiding this comment

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

Why was this changed to put array opening on a line of its own? This isn't a style I've seen common to WordPress, and adds quite a bit of excessive indentation and awkward arrow spacing.

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 was a try to make things look more aligned while respecting phpcs rules. I reverted this change but unfortunately, I'm not seeing a way to make things more aligned and respect phpcs.

const { setAttributes } = this.props;

if ( postToShowCurrent === postToShowNext ) {
if ( isEqual( this.props.attributes, nextProps.attributes ) ) {
Copy link
Member

Choose a reason for hiding this comment

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

Just noting that we really want to check that a change in the attributes specific to the fetching of posts are the only ones we want to trigger a new request for; as implemented, a change to the block's alignment, columns, layout will trigger a new request.

},
{

/* translators: label for ordering posts by title in ascending order, translation may be required for languages using non latin alphabet */
Copy link
Member

Choose a reason for hiding this comment

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

Kinda feel we can drop the rest of the comment after the first comma, that's really up to the translator to judge for themselves, and the first bit should be sufficient for understanding the intent of the string.

Copy link
Member

Choose a reason for hiding this comment

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

Personal preference would be to drop the newline prior to this comment, feels in line with this ESLint rule we have enabled:

https://eslint.org/docs/rules/padded-blocks (never)

onChange={ onCategoryChange }
/> ),
onNumberOfItemsChange &&
(
Copy link
Member

Choose a reason for hiding this comment

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

Inconsistency here with the previous condition, and also tabbed at least once more than necessary. Would suggest:

		onNumberOfItemsChange && (
			<RangeControl
				key="query-panel-range-control"
				label={ __( 'Number of items' ) }
				value={ numberOfItems }
				onChange={ onNumberOfItemsChange }
				min={ minItems }
				max={ maxItems }
			/>
		),

@jorgefilipecosta jorgefilipecosta force-pushed the add/query-panel branch 2 times, most recently from 448d807 to 3109a67 Compare November 10, 2017 18:46
Copy link
Member

@karmatosed karmatosed left a comment

Choose a reason for hiding this comment

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

Design wise approving. I did find a bug though in changing the number of items. It got stuck after going to higher number then 1.

@codecov
Copy link

codecov bot commented Nov 13, 2017

Codecov Report

Merging #3198 into master will decrease coverage by 0.2%.
The diff coverage is 26.47%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3198      +/-   ##
==========================================
- Coverage   37.85%   37.65%   -0.21%     
==========================================
  Files         279      283       +4     
  Lines        6778     6748      -30     
  Branches     1240     1227      -13     
==========================================
- Hits         2566     2541      -25     
+ Misses       3547     3545       -2     
+ Partials      665      662       -3
Impacted Files Coverage Δ
blocks/library/latest-posts/data.js 0% <ø> (ø) ⬆️
blocks/term-tree-select/index.js 0% <0%> (ø)
blocks/library/latest-posts/index.js 6.38% <0%> (-3.43%) ⬇️
utils/terms.js 100% <100%> (ø)
blocks/query-panel/index.js 25% <25%> (ø)
blocks/query-panel/category-select.js 33.33% <33.33%> (ø)
components/panel/color.js 0% <0%> (-100%) ⬇️
components/panel/row.js 0% <0%> (-100%) ⬇️
editor/components/warning/index.js 0% <0%> (-100%) ⬇️
editor/utils/mobile/index.js 57.14% <0%> (-17.86%) ⬇️
... and 33 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4d37adf...e17bef4. Read the comment docs.

@jorgefilipecosta
Copy link
Member Author

Hi @karmatosed, I was able to replicate something similar to what you described. When increasing number of posts to show the posts were not refreshing, leaving the user to be stuck in one post if a value of one was set before. It was introduced by a typo I made while addressing the last reviews and was fixed. Thank you for reporting the problem and for reviewing the design :)

Copy link
Contributor

@mcsf mcsf left a comment

Choose a reason for hiding this comment

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

I need to run, so here's a more drive-by review.

if ( isEqual(
pick( this.props.attributes, attrsTriggerRefresh ),
pick( nextProps.attributes, attrsTriggerRefresh )
) ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Personally not a fan of the name attrsTriggerRefresh vs. e.g. queryKeys/queryProps. Though the name could be skipped altogether:

const shouldRefetch = [ 'a', 'b' ].some( ( queryKey ) =>
  this.props.attributes[ queryKey ] !== nextProps.attributes[ queryKey ] );

(or the converse with shouldSkip and Array#every)

};

const ret = fillWithChildren( termsByParent[ 0 ] || [] );
return ret;
Copy link
Contributor

Choose a reason for hiding this comment

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

Leftover assignment of ret?

return (
<TermTreeSelect
{ ...{ label, noOptionLabel } }
termsTree={ buildTermsTree( categories.data ) }
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens while we're waiting for the data request? Should we do an early null return?

*/
import { unescape as unescapeString, repeat, flatMap, compact } from 'lodash';

import SelectControl from '../inspector-controls/select-control';
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing Internal dependencies comment (also, if you want to be really consistent, the External one should use lowercase d 😅 ).

function CategorySelect( { label, noOptionLabel, categories, selectedCategory, onChange } ) {
return (
<TermTreeSelect
{ ...{ label, noOptionLabel } }
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you have this spread, you can throw in onChange.

@@ -62,8 +65,12 @@ function gutenberg_render_block_core_latest_posts( $attributes ) {
return $block_content;
}

register_block_type( 'core/latest-posts', array(
register_block_type('core/latest-posts', array(
Copy link
Contributor

Choose a reason for hiding this comment

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

Should restore that space before the string 'core/latest-posts'.

if ( newOrder !== order && 'function' === typeof onOrderChange ) {
onOrderChange( newOrder );
}
if ( newOrderBy !== orderBy && 'function' === typeof onOrderByChange ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If onOrderChange and onOrderByChange default to Lodash's noop, the function checks here can be dropped.

} );
};

const ret = fillWithChildren( termsByParent[ 0 ] || [] );
Copy link
Contributor

Choose a reason for hiding this comment

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

Would termsByParent[ '0' ] be more correct? Since it's dict key.

@jorgefilipecosta jorgefilipecosta force-pushed the add/query-panel branch 3 times, most recently from 5f30b3e to b4e7465 Compare November 27, 2017 21:42
@jorgefilipecosta
Copy link
Member Author

jorgefilipecosta commented Nov 27, 2017

Hi @mcsf, thank you a lot for your review, your feedback was applied.

import { buildTermsTree } from '../terms';

describe( 'buildTermsTree()', () => {
describe( 'isHorizontalEdge', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Some copy-paste that went wrong? 😄

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 @mcsf I missed it, it was solved :D

@jorgefilipecosta jorgefilipecosta force-pushed the add/query-panel branch 3 times, most recently from eebf4c0 to dfe86f8 Compare November 28, 2017 16:17
@@ -2,15 +2,22 @@
* Returns a Promise with the latest posts or an error on failure.
*
* @param {Number} postsToShow Number of posts to display.
* @param {String} order Sort attribute ascending or descending.
Copy link
Contributor

Choose a reason for hiding this comment

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

I recommend enumerating the two options: desc, asc and rephrasing to something like: Whether to sort in ascending or descending order: 'asc'|'desc'.

@@ -2,15 +2,22 @@
* Returns a Promise with the latest posts or an error on failure.
*
* @param {Number} postsToShow Number of posts to display.
* @param {String} order Sort attribute ascending or descending.
* @param {String} orderBy Attribute specifying how to sort the posts.
Copy link
Contributor

Choose a reason for hiding this comment

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

Post parameter by which to sort posts.

/**
* Internal dependencies
*/
import { buildTermsTree } from '../../editor/utils/terms';
Copy link
Contributor

Choose a reason for hiding this comment

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

buildTermsTree is in @wordpress/editor, this should be a WordPress dependency.

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to @wordpress/utils :)

Copy link
Member

Choose a reason for hiding this comment

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

Good catch @mcsf, blocks library shouldn't depend on the editor. 👍

@jorgefilipecosta jorgefilipecosta force-pushed the add/query-panel branch 3 times, most recently from 988ae68 to 81189ae Compare November 30, 2017 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Inspector Controls The interface showing block settings and the controls available for each block Needs Design Feedback Needs general design feedback. [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants