Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
773f2d3
add presentation field to widget type metadata
retrofox May 11, 2026
48c77ce
expose presentation through php widget registry
retrofox May 11, 2026
0efdb2e
pass presentation through wp-build manifest
retrofox May 11, 2026
9e02c75
map presentation in widget-types resolver
retrofox May 11, 2026
13fa997
honor full-bleed presentation in widget chrome
retrofox May 11, 2026
4f31f49
update hello-world to showcase full-bleed
retrofox May 11, 2026
9ce6e35
unify widget type identity in single source
retrofox May 12, 2026
72a54fd
dedupe presentation declarations per layer
retrofox May 12, 2026
46f60a0
add tsconfig umbrella for widgets/
retrofox May 12, 2026
b1e1bfe
add widgets/package.json
retrofox May 12, 2026
f2916e2
add on_this_day REST param to posts endpoint
retrofox May 12, 2026
c1612b7
add On This Day dashboard widget
retrofox May 12, 2026
ee5dacd
use motion design tokens for image reveal transition
retrofox May 12, 2026
239991a
add required metadata to widgets/package.json
retrofox May 12, 2026
50b8b0e
make widget framework types generic
retrofox May 12, 2026
75217e6
use generic widget types in on-this-day
retrofox May 12, 2026
6302570
declare full-bleed presentation on on-this-day
retrofox May 12, 2026
f9db55b
stop emitting types from widgets tsconfig
retrofox May 12, 2026
5ddf474
Merge branch 'trunk' into update/on-this-day-widget
retrofox May 13, 2026
c12d862
polish empty state
retrofox May 13, 2026
92fec46
add empty state CTA and rename style classes
retrofox May 13, 2026
07ac86b
wire empty state CTA to post-new admin entry
retrofox May 13, 2026
666ba13
Merge remote-tracking branch 'origin/trunk' into update/on-this-day-w…
retrofox May 13, 2026
1c8099a
show first-post CTA only when no posts exist
retrofox May 13, 2026
2798d5a
break line in memories comment
retrofox May 13, 2026
28714c4
Merge branch 'trunk' into update/on-this-day-widget
retrofox May 15, 2026
1bb2179
On This Day: improve text contrast over image
retrofox May 15, 2026
3859bd6
On This Day: default to dim-blur effect
retrofox May 15, 2026
29c9dd1
Merge branch 'trunk' into update/on-this-day-widget
retrofox May 15, 2026
d691d14
Merge branch 'trunk' into update/on-this-day-widget
retrofox May 16, 2026
adf08c5
replace scrim with backdrop-filter glass card
retrofox May 16, 2026
31c59c8
Merge branch 'trunk' into update/on-this-day-widget
retrofox May 18, 2026
ff2878e
Merge branch 'trunk' into update/on-this-day-widget
retrofox May 18, 2026
db121d1
Merge branch 'update/on-this-day-widget' of github.com:WordPress/gute…
retrofox May 18, 2026
a35b587
Merge remote-tracking branch 'origin/trunk' into update/on-this-day-w…
Copilot May 19, 2026
3a416c7
Merge branch 'trunk' into update/on-this-day-widget
retrofox May 20, 2026
7a03115
Merge branch 'trunk' into update/on-this-day-widget
retrofox May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions lib/experimental/on-this-day/on-this-day.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* Extends the native posts REST collection with an `on_this_day` query
* parameter that filters posts by month and day, regardless of year.
*
* The parameter accepts a `MM-DD` string. When present, the controller
* appends a `date_query` clause to the underlying `WP_Query`, which
* resolves to an efficient SQL filter on the month and day components
* of `post_date`.
*
* @package gutenberg
*/

/**
* Pattern enforced on the `on_this_day` REST parameter.
*
* Accepts months `01`-`12` and days `01`-`31`. The combination is not
* calendar-validated here; impossible dates simply return no results.
*/
const GUTENBERG_ON_THIS_DAY_PATTERN = '^(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$';

/**
* Advertises the `on_this_day` parameter on the posts REST collection.
*
* @param array $params Collection parameters keyed by name.
* @return array Collection parameters with `on_this_day` appended.
*/
function gutenberg_on_this_day_register_collection_param( $params ) {
$params['on_this_day'] = array(
'description' => __( 'Limit response to posts published on the given month and day (MM-DD), regardless of year.', 'gutenberg' ),
'type' => 'string',
'pattern' => GUTENBERG_ON_THIS_DAY_PATTERN,
);

return $params;
}
add_filter( 'rest_post_collection_params', 'gutenberg_on_this_day_register_collection_param' );

/**
* Translates the `on_this_day` REST parameter into a `date_query` clause.
*
* Skips the translation when the parameter is missing or fails the schema
* pattern. The schema-level validation in `rest_post_collection_params`
* normally catches invalid input before this filter runs, but the
* defensive check keeps the filter safe for callers that bypass the
* controller's argument validation.
*
* @param array $args `WP_Query` arguments built by the controller.
* @param WP_REST_Request $request REST request being handled.
* @return array `WP_Query` arguments, extended with a `date_query` clause when applicable.
*/
function gutenberg_on_this_day_apply_query( $args, $request ) {
$value = $request['on_this_day'] ?? null;

if ( ! is_string( $value ) || ! preg_match( '/' . GUTENBERG_ON_THIS_DAY_PATTERN . '/', $value ) ) {
return $args;
}

[ $month, $day ] = array_map( 'intval', explode( '-', $value ) );

if ( ! isset( $args['date_query'] ) || ! is_array( $args['date_query'] ) ) {
$args['date_query'] = array();
}

$args['date_query'][] = array(
'month' => $month,
'day' => $day,
);

return $args;
}

add_filter( 'rest_post_query', 'gutenberg_on_this_day_apply_query', 10, 2 );
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,5 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/dashboard-widgets/widget-types.php';
require __DIR__ . '/experimental/dashboard-widgets/dashboard-layout.php';
require __DIR__ . '/experimental/dashboard-widgets/default-layout-seed.php';
require __DIR__ . '/experimental/on-this-day/on-this-day.php';
}
158 changes: 158 additions & 0 deletions phpunit/experimental/on-this-day/on-this-day-test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php
/**
* Tests for the on_this_day REST parameter that extends the posts collection.
*
* @package gutenberg
*/
class On_This_Day_Test extends WP_UnitTestCase {

/**
* REST route under test.
*/
const ROUTE = '/wp/v2/posts';

/**
* Subscriber id with `read` capability.
*
* @var int
*/
protected static $subscriber_id;

/**
* Fixture post ids keyed by intent.
*
* @var array<string, int>
*/
protected static $post_ids = array();

public static function wpSetUpBeforeClass( $factory ) {
self::$subscriber_id = $factory->user->create( array( 'role' => 'subscriber' ) );

self::$post_ids['oldest_may_11'] = $factory->post->create(
array(
'post_status' => 'publish',
'post_date' => '2018-05-11 10:00:00',
'post_title' => 'Oldest May 11',
)
);
self::$post_ids['newer_may_11'] = $factory->post->create(
array(
'post_status' => 'publish',
'post_date' => '2020-05-11 10:00:00',
'post_title' => 'Newer May 11',
)
);
self::$post_ids['other_day'] = $factory->post->create(
array(
'post_status' => 'publish',
'post_date' => '2019-07-04 10:00:00',
'post_title' => 'Independence Day',
)
);
}

public function set_up() {
parent::set_up();
wp_set_current_user( self::$subscriber_id );
}

public function tear_down() {
wp_set_current_user( 0 );
parent::tear_down();
}

public function test_collection_param_is_advertised_in_schema() {
$routes = rest_get_server()->get_routes();
$this->assertArrayHasKey( self::ROUTE, $routes );

$get_route_args = null;
foreach ( $routes[ self::ROUTE ] as $route_handler ) {
if ( in_array( 'GET', (array) $route_handler['methods'], true ) || ! empty( $route_handler['methods']['GET'] ) ) {
$get_route_args = $route_handler['args'];
break;
}
}

$this->assertNotNull( $get_route_args );
$this->assertArrayHasKey( 'on_this_day', $get_route_args );
$this->assertSame( 'string', $get_route_args['on_this_day']['type'] );
$this->assertSame( GUTENBERG_ON_THIS_DAY_PATTERN, $get_route_args['on_this_day']['pattern'] );
}

public function test_filter_returns_only_posts_matching_month_day() {
$request = new WP_REST_Request( 'GET', self::ROUTE );
$request->set_param( 'on_this_day', '05-11' );
$request->set_param( 'orderby', 'date' );
$request->set_param( 'order', 'asc' );

$response = rest_do_request( $request );

$this->assertSame( 200, $response->get_status() );

$ids = array_column( $response->get_data(), 'id' );
sort( $ids );

$expected = array( self::$post_ids['oldest_may_11'], self::$post_ids['newer_may_11'] );
sort( $expected );

$this->assertSame( $expected, $ids );
}

public function test_orderby_ascending_returns_oldest_match_first() {
$request = new WP_REST_Request( 'GET', self::ROUTE );
$request->set_param( 'on_this_day', '05-11' );
$request->set_param( 'orderby', 'date' );
$request->set_param( 'order', 'asc' );
$request->set_param( 'per_page', 1 );

$response = rest_do_request( $request );

$this->assertSame( 200, $response->get_status() );

$data = $response->get_data();
$this->assertCount( 1, $data );
$this->assertSame( self::$post_ids['oldest_may_11'], $data[0]['id'] );
}

public function test_no_match_returns_empty_collection() {
$request = new WP_REST_Request( 'GET', self::ROUTE );
$request->set_param( 'on_this_day', '02-29' );

$response = rest_do_request( $request );

$this->assertSame( 200, $response->get_status() );
$this->assertSame( array(), $response->get_data() );
}

public function test_missing_param_returns_all_published_posts() {
$request = new WP_REST_Request( 'GET', self::ROUTE );

$response = rest_do_request( $request );

$this->assertSame( 200, $response->get_status() );

$ids = array_column( $response->get_data(), 'id' );
$this->assertContains( self::$post_ids['oldest_may_11'], $ids );
$this->assertContains( self::$post_ids['newer_may_11'], $ids );
$this->assertContains( self::$post_ids['other_day'], $ids );
}

public function test_invalid_format_is_rejected_with_400() {
$request = new WP_REST_Request( 'GET', self::ROUTE );
$request->set_param( 'on_this_day', '2026-05-11' );

$response = rest_do_request( $request );

$this->assertSame( 400, $response->get_status() );
}

public function test_query_filter_ignores_invalid_value_when_called_directly() {
$args = array( 'post_type' => 'post' );
$request = new WP_REST_Request( 'GET', self::ROUTE );
$request->set_param( 'on_this_day', 'not-a-date' );

$filtered = gutenberg_on_this_day_apply_query( $args, $request );

$this->assertSame( $args, $filtered );
}
}
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@
{ "path": "storybook" },
{ "path": "test/e2e" },
{ "path": "test/storybook-playwright" },
{ "path": "test/performance" }
{ "path": "test/performance" },
{ "path": "widgets" }
],
"files": []
}
1 change: 1 addition & 0 deletions widgets/on-this-day/components/on-this-day-view/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { OnThisDayView, type BackgroundEffect } from './on-this-day-view';
Loading
Loading