Skip to content

Commit 9df107f

Browse files
kasparsdkasparsdcarolinanMamadukakarmatosed
committed
Query Loop: Add 'menu_order' as sorting option (#68781)
Co-authored-by: kasparsd <[email protected]> Co-authored-by: carolinan <[email protected]> Co-authored-by: Mamaduka <[email protected]> Co-authored-by: karmatosed <[email protected]> Co-authored-by: Humanify-nl <[email protected]> Co-authored-by: mikeritter <[email protected]>
1 parent c82893b commit 9df107f

File tree

9 files changed

+132
-31
lines changed

9 files changed

+132
-31
lines changed

packages/block-library/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Enhancements
6+
7+
- Query Loop Block: Enable custom order or `menu_order` ordering option for post types that support it. ([#68781](https://github.com/WordPress/gutenberg/pull/68781))
8+
59
## 9.19.0 (2025-02-28)
610

711
## 9.18.0 (2025-02-12)

packages/block-library/src/query/edit/inspector-controls/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
useAllowedControls,
3636
isControlAllowed,
3737
useTaxonomies,
38+
useOrderByOptions,
3839
} from '../../utils';
3940
import { useToolsPanelDropdownMenuProps } from '../../../utils/hooks';
4041

@@ -111,6 +112,7 @@ export default function QueryInspectorControls( props ) {
111112
return onChangeDebounced.cancel;
112113
}, [ querySearch, onChangeDebounced ] );
113114

115+
const orderByOptions = useOrderByOptions( postType );
114116
const showInheritControl =
115117
! isSingular && isControlAllowed( allowedControls, 'inherit' );
116118
const showPostTypeControl =
@@ -329,7 +331,7 @@ export default function QueryInspectorControls( props ) {
329331
isShownByDefault
330332
>
331333
<OrderControl
332-
{ ...{ order, orderBy } }
334+
{ ...{ order, orderBy, orderByOptions } }
333335
onChange={ setQuery }
334336
/>
335337
</ToolsPanelItem>

packages/block-library/src/query/edit/inspector-controls/order-control.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { SelectControl } from '@wordpress/components';
55
import { __ } from '@wordpress/i18n';
66

7-
const orderOptions = [
7+
const defaultOrderByOptions = [
88
{
99
label: __( 'Newest to oldest' ),
1010
value: 'date/desc',
@@ -24,14 +24,20 @@ const orderOptions = [
2424
value: 'title/desc',
2525
},
2626
];
27-
function OrderControl( { order, orderBy, onChange } ) {
27+
28+
function OrderControl( {
29+
order,
30+
orderBy,
31+
orderByOptions = defaultOrderByOptions,
32+
onChange,
33+
} ) {
2834
return (
2935
<SelectControl
3036
__nextHasNoMarginBottom
3137
__next40pxDefaultSize
3238
label={ __( 'Order by' ) }
3339
value={ `${ orderBy }/${ order }` }
34-
options={ orderOptions }
40+
options={ orderByOptions }
3541
onChange={ ( value ) => {
3642
const [ newOrderBy, newOrder ] = value.split( '/' );
3743
onChange( { order: newOrder, orderBy: newOrderBy } );

packages/block-library/src/query/utils.js

+58
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import { useMemo } from '@wordpress/element';
66
import { store as coreStore } from '@wordpress/core-data';
77
import { store as blockEditorStore } from '@wordpress/block-editor';
88
import { decodeEntities } from '@wordpress/html-entities';
9+
import { __ } from '@wordpress/i18n';
910
import {
1011
cloneBlock,
1112
getBlockSupport,
1213
store as blocksStore,
1314
} from '@wordpress/blocks';
1415

1516
/** @typedef {import('@wordpress/blocks').WPBlockVariation} WPBlockVariation */
17+
/** @typedef {import('@wordpress/components/build-types/query-controls/types').OrderByOption} OrderByOption */
1618

1719
/**
1820
* @typedef IHasNameAndId
@@ -186,6 +188,62 @@ export function useIsPostTypeHierarchical( postType ) {
186188
);
187189
}
188190

191+
/**
192+
* List of avaiable options to order by.
193+
*
194+
* @param {string} postType The post type to check.
195+
* @return {OrderByOption[]} List of order options.
196+
*/
197+
export function useOrderByOptions( postType ) {
198+
const supportsCustomOrder = useSelect(
199+
( select ) => {
200+
const type = select( coreStore ).getPostType( postType );
201+
return !! type?.supports?.[ 'page-attributes' ];
202+
},
203+
[ postType ]
204+
);
205+
206+
return useMemo( () => {
207+
const orderByOptions = [
208+
{
209+
label: __( 'Newest to oldest' ),
210+
value: 'date/desc',
211+
},
212+
{
213+
label: __( 'Oldest to newest' ),
214+
value: 'date/asc',
215+
},
216+
{
217+
/* translators: Label for ordering posts by title in ascending order. */
218+
label: __( 'A → Z' ),
219+
value: 'title/asc',
220+
},
221+
{
222+
/* translators: Label for ordering posts by title in descending order. */
223+
label: __( 'Z → A' ),
224+
value: 'title/desc',
225+
},
226+
];
227+
228+
if ( supportsCustomOrder ) {
229+
orderByOptions.push(
230+
{
231+
/* translators: Label for ordering posts by ascending menu order. */
232+
label: __( 'Ascending by order' ),
233+
value: 'menu_order/asc',
234+
},
235+
{
236+
/* translators: Label for ordering posts by descending menu order. */
237+
label: __( 'Descending by order' ),
238+
value: 'menu_order/desc',
239+
}
240+
);
241+
}
242+
243+
return orderByOptions;
244+
}, [ supportsCustomOrder ] );
245+
}
246+
189247
/**
190248
* Hook that returns the query properties' names defined by the active
191249
* block variation, to determine which block's filters to show.

packages/components/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Enhancement
6+
7+
- `QueryControls`: Add menu_order sorting option if supported by the post type. ([#68781](https://github.com/WordPress/gutenberg/pull/68781)).
8+
59
## 29.5.0 (2025-02-28)
610

711
### Documentation

packages/components/src/query-controls/README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const QUERY_DEFAULTS = {
3636

3737
const MyQueryControls = () => {
3838
const [ query, setQuery ] = useState( QUERY_DEFAULTS );
39-
const { category, categories, maxItems, minItems, numberOfItems, order, orderBy } = query;
39+
const { category, categories, maxItems, minItems, numberOfItems, order, orderBy } = query;
4040

4141
const updateQuery = ( newQuery ) => {
4242
setQuery( { ...query, ...newQuery } );
@@ -213,7 +213,14 @@ The order in which to retrieve posts.
213213
- Required: No
214214
- Platform: Web
215215

216-
#### `orderBy`: `'date' | 'title'`
216+
#### `orderBy`: `'date' | 'title' | 'menu_order'`
217+
218+
The meta key by which to order posts.
219+
220+
- Required: No
221+
- Platform: Web
222+
223+
#### `orderByOptions`: `OrderByOption[]`
217224

218225
The meta key by which to order posts.
219226

@@ -246,4 +253,4 @@ The selected category for the `categoriesList` prop.
246253
Start opting into the larger default height that will become the default size in a future version.
247254

248255
- Required: No
249-
- Default: `false`
256+
- Default: `false`

packages/components/src/query-controls/index.native.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import CategorySelect from './category-select';
1414
const DEFAULT_MIN_ITEMS = 1;
1515
const DEFAULT_MAX_ITEMS = 100;
1616

17-
const options = [
17+
const defaultOrderByOptions = [
1818
{
1919
label: __( 'Newest to oldest' ),
2020
value: 'date/desc',
@@ -42,6 +42,7 @@ const QueryControls = memo(
4242
numberOfItems,
4343
order,
4444
orderBy,
45+
orderByOptions = defaultOrderByOptions,
4546
maxItems = DEFAULT_MAX_ITEMS,
4647
minItems = DEFAULT_MIN_ITEMS,
4748
onCategoryChange,
@@ -68,7 +69,7 @@ const QueryControls = memo(
6869
<SelectControl
6970
label={ __( 'Order by' ) }
7071
value={ `${ orderBy }/${ order }` }
71-
options={ options }
72+
options={ orderByOptions }
7273
onChange={ onChange }
7374
hideCancelButton
7475
/>

packages/components/src/query-controls/index.tsx

+25-21
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
QueryControlsProps,
1717
QueryControlsWithMultipleCategorySelectionProps,
1818
QueryControlsWithSingleCategorySelectionProps,
19+
OrderByOption,
1920
} from './types';
2021

2122
const DEFAULT_MIN_ITEMS = 1;
@@ -34,13 +35,34 @@ function isMultipleCategorySelection(
3435
return 'categorySuggestions' in props;
3536
}
3637

38+
const defaultOrderByOptions: OrderByOption[] = [
39+
{
40+
label: __( 'Newest to oldest' ),
41+
value: 'date/desc',
42+
},
43+
{
44+
label: __( 'Oldest to newest' ),
45+
value: 'date/asc',
46+
},
47+
{
48+
/* translators: Label for ordering posts by title in ascending order. */
49+
label: __( 'A → Z' ),
50+
value: 'title/asc',
51+
},
52+
{
53+
/* translators: Label for ordering posts by title in descending order. */
54+
label: __( 'Z → A' ),
55+
value: 'title/desc',
56+
},
57+
];
58+
3759
/**
3860
* Controls to query for posts.
3961
*
4062
* ```jsx
4163
* const MyQueryControls = () => (
4264
* <QueryControls
43-
* { ...{ maxItems, minItems, numberOfItems, order, orderBy } }
65+
* { ...{ maxItems, minItems, numberOfItems, order, orderBy, orderByOptions } }
4466
* onOrderByChange={ ( newOrderBy ) => {
4567
* updateQuery( { orderBy: newOrderBy } )
4668
* }
@@ -65,6 +87,7 @@ export function QueryControls( {
6587
numberOfItems,
6688
order,
6789
orderBy,
90+
orderByOptions = defaultOrderByOptions,
6891
maxItems = DEFAULT_MAX_ITEMS,
6992
minItems = DEFAULT_MIN_ITEMS,
7093
onAuthorChange,
@@ -89,26 +112,7 @@ export function QueryControls( {
89112
? undefined
90113
: `${ orderBy }/${ order }`
91114
}
92-
options={ [
93-
{
94-
label: __( 'Newest to oldest' ),
95-
value: 'date/desc',
96-
},
97-
{
98-
label: __( 'Oldest to newest' ),
99-
value: 'date/asc',
100-
},
101-
{
102-
/* translators: Label for ordering posts by title in ascending order. */
103-
label: __( 'A → Z' ),
104-
value: 'title/asc',
105-
},
106-
{
107-
/* translators: Label for ordering posts by title in descending order. */
108-
label: __( 'Z → A' ),
109-
value: 'title/desc',
110-
},
111-
] }
115+
options={ orderByOptions }
112116
onChange={ ( value ) => {
113117
if ( typeof value !== 'string' ) {
114118
return;

packages/components/src/query-controls/types.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,18 @@ export type AuthorSelectProps = Pick<
4545
};
4646

4747
type Order = 'asc' | 'desc';
48-
type OrderBy = 'date' | 'title';
48+
type OrderBy = 'date' | 'title' | 'menu_order';
49+
50+
export type OrderByOption = {
51+
/**
52+
* The label to be shown to the user.
53+
*/
54+
label: string;
55+
/**
56+
* Option value passed to `onChange` when the option is selected.
57+
*/
58+
value: `${ OrderBy }/${ Order }`;
59+
};
4960

5061
type BaseQueryControlsProps = {
5162
/**
@@ -99,6 +110,10 @@ type BaseQueryControlsProps = {
99110
* The meta key by which to order posts.
100111
*/
101112
orderBy?: OrderBy;
113+
/**
114+
* List of available ordering options.
115+
*/
116+
orderByOptions?: OrderByOption[];
102117
/**
103118
* The selected author ID.
104119
*/

0 commit comments

Comments
 (0)