Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
155 changes: 155 additions & 0 deletions .dev/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<!-- wp:paragraph -->
<p>Each section below uses one Toggle Block feature. Every button controls a block by its HTML anchor.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Hidden until toggled</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The panel starts with the <code>toggle-block-hidden</code> class. It holds a link and a button to check keyboard focus.</p>
<!-- /wp:paragraph -->

<!-- wp:happyprime/toggle-block {"buttonText":"Show details","controlsId":"details-panel"} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="details-panel"><span>Show details</span></button>
<!-- /wp:happyprime/toggle-block -->

<!-- wp:group {"className":"toggle-block-hidden","layout":{"type":"constrained"}} -->
<div id="details-panel" class="wp-block-group toggle-block-hidden"><!-- wp:paragraph -->
<p>These details were hidden. Read <a href="https://wordpress.org/documentation/">the WordPress documentation</a> or use the button below.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button -->
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="#details-panel">A focusable button</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div>
<!-- /wp:group -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Visible until toggled</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Without the hidden class the target starts open, and the first press hides it.</p>
<!-- /wp:paragraph -->

<!-- wp:happyprime/toggle-block {"buttonText":"Hide notice","controlsId":"notice-panel"} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="notice-panel"><span>Hide notice</span></button>
<!-- /wp:happyprime/toggle-block -->

<!-- wp:group {"style":{"spacing":{"padding":{"top":"var:preset|spacing|30","bottom":"var:preset|spacing|30","left":"var:preset|spacing|30","right":"var:preset|spacing|30"}}},"backgroundColor":"accent-5","layout":{"type":"constrained"}} -->
<div id="notice-panel" class="wp-block-group has-accent-5-background-color has-background" style="padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)"><!-- wp:paragraph -->
<p>This notice is visible on load.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Screen reader text</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This toggle sets an <code>aria-label</code> that extends its visible text.</p>
<!-- /wp:paragraph -->

<!-- wp:happyprime/toggle-block {"buttonText":"More","controlsId":"more-panel","labelText":"More about Toggle Block"} -->
<button class="wp-block-happyprime-toggle-block" aria-label="More about Toggle Block" aria-controls="more-panel"><span>More</span></button>
<!-- /wp:happyprime/toggle-block -->

<!-- wp:group {"className":"toggle-block-hidden","layout":{"type":"constrained"}} -->
<div id="more-panel" class="wp-block-group toggle-block-hidden"><!-- wp:paragraph -->
<p>Toggle Block renders a button with <code>aria-controls</code> and <code>aria-expanded</code>.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Toggle group</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Toggles inside a block with the <code>toggle-block-group</code> class close each other. "Overview" is the default toggle, so it opens on load and again when another panel closes.</p>
<!-- /wp:paragraph -->

<!-- wp:group {"className":"toggle-block-group","layout":{"type":"constrained"}} -->
<div class="wp-block-group toggle-block-group"><!-- wp:group {"layout":{"type":"flex","flexWrap":"wrap"}} -->
<div class="wp-block-group"><!-- wp:happyprime/toggle-block {"buttonText":"Overview","controlsId":"tab-overview","defaultToggle":true} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="tab-overview" data-default-toggle="true"><span>Overview</span></button>
<!-- /wp:happyprime/toggle-block -->

<!-- wp:happyprime/toggle-block {"buttonText":"Features","controlsId":"tab-features"} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="tab-features"><span>Features</span></button>
<!-- /wp:happyprime/toggle-block -->

<!-- wp:happyprime/toggle-block {"buttonText":"Support","controlsId":"tab-support"} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="tab-support"><span>Support</span></button>
<!-- /wp:happyprime/toggle-block --></div>
<!-- /wp:group -->

<!-- wp:group {"className":"toggle-block-hidden","layout":{"type":"constrained"}} -->
<div id="tab-overview" class="wp-block-group toggle-block-hidden"><!-- wp:paragraph -->
<p>Overview panel.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->

<!-- wp:group {"className":"toggle-block-hidden","layout":{"type":"constrained"}} -->
<div id="tab-features" class="wp-block-group toggle-block-hidden"><!-- wp:paragraph -->
<p>Features panel.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->

<!-- wp:group {"className":"toggle-block-hidden","layout":{"type":"constrained"}} -->
<div id="tab-support" class="wp-block-group toggle-block-hidden"><!-- wp:paragraph -->
<p>Support panel with <a href="https://github.com/happyprime/toggle-block/issues">a link to the issue tracker</a>.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group --></div>
<!-- /wp:group -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Body class</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This toggle adds <code>toggle-block-demo-dark</code> to the body while its panel is open. The Custom HTML block below styles that class.</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
<style>body.toggle-block-demo-dark{background:#1e1e1e;color:#f0f0f0}</style>
<!-- /wp:html -->

<!-- wp:happyprime/toggle-block {"bodyClass":"toggle-block-demo-dark","buttonText":"Dark mode","controlsId":"dark-panel"} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="dark-panel" data-body-class="toggle-block-demo-dark"><span>Dark mode</span></button>
<!-- /wp:happyprime/toggle-block -->

<!-- wp:group {"className":"toggle-block-hidden","layout":{"type":"constrained"}} -->
<div id="dark-panel" class="wp-block-group toggle-block-hidden"><!-- wp:paragraph -->
<p>The body has the <code>toggle-block-demo-dark</code> class while this panel is open.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:group -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Open and close buttons</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Two toggles control one panel. Opening moves focus to the close button inside the panel, and closing returns it to the open button. Escape closes the panel from anywhere inside it.</p>
<!-- /wp:paragraph -->

<!-- wp:happyprime/toggle-block {"buttonText":"Open menu","controlsId":"menu-panel"} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="menu-panel"><span>Open menu</span></button>
<!-- /wp:happyprime/toggle-block -->

<!-- wp:group {"className":"toggle-block-hidden","layout":{"type":"constrained"}} -->
<div id="menu-panel" class="wp-block-group toggle-block-hidden"><!-- wp:happyprime/toggle-block {"buttonText":"Close menu","controlsId":"menu-panel"} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="menu-panel"><span>Close menu</span></button>
<!-- /wp:happyprime/toggle-block -->

<!-- wp:list -->
<ul class="wp-block-list"><!-- wp:list-item -->
<li><a href="#menu-panel">First menu link</a></li>
<!-- /wp:list-item -->

<!-- wp:list-item -->
<li><a href="#menu-panel">Second menu link</a></li>
<!-- /wp:list-item --></ul>
<!-- /wp:list --></div>
<!-- /wp:group -->
15 changes: 15 additions & 0 deletions .dev/navigation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- wp:paragraph -->
<p>The navigation block below holds a Toggle Block that opens a search panel.</p>
<!-- /wp:paragraph -->

<!-- wp:navigation {"overlayMenu":"never"} -->
<!-- wp:navigation-link {"label":"Demo","url":"/","kind":"custom"} /-->

<!-- wp:happyprime/toggle-block {"buttonText":"Search","controlsId":"nav-search-panel"} -->
<button class="wp-block-happyprime-toggle-block" aria-controls="nav-search-panel"><span>Search</span></button>
<!-- /wp:happyprime/toggle-block -->
<!-- /wp:navigation -->

<!-- wp:group {"className":"toggle-block-hidden","layout":{"type":"constrained"}} -->
<div id="nav-search-panel" class="wp-block-group toggle-block-hidden"><!-- wp:search {"label":"Search","buttonText":"Search"} /--></div>
<!-- /wp:group -->
65 changes: 65 additions & 0 deletions .dev/seed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php
/**
* Seeds demo content for local Toggle Block testing.
*
* Run with `npm run env:seed`. It is safe to run more than once.
*
* @package toggle-block
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Creates or updates a published page and returns its ID.
*
* @param string $slug Page slug.
* @param string $title Page title.
* @param string $content Block markup.
* @return int Page ID, or zero on failure.
*/
function toggle_block_dev_upsert_page( string $slug, string $title, string $content ): int {
$page = get_page_by_path( $slug );

$args = [
'post_type' => 'page',
'post_status' => 'publish',
'post_name' => $slug,
'post_title' => $title,
// wp_insert_post() unslashes, which would break quotes in block attribute JSON.
'post_content' => wp_slash( $content ),
];

if ( $page ) {
$args['ID'] = $page->ID;
}

$page_id = wp_insert_post( $args, true );

return is_wp_error( $page_id ) ? 0 : (int) $page_id;
}

switch_theme( 'twentytwentyfive' );

update_option( 'permalink_structure', '/%postname%/' );
flush_rewrite_rules( false );

$toggle_block_demo_id = toggle_block_dev_upsert_page(
'toggle-block-demo',
'Toggle Block demo',
(string) file_get_contents( __DIR__ . '/demo.html' ) // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file.
);

$toggle_block_nav_id = toggle_block_dev_upsert_page(
'toggle-block-navigation',
'Toggle Block in navigation',
(string) file_get_contents( __DIR__ . '/navigation.html' ) // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file.
);

if ( $toggle_block_demo_id ) {
update_option( 'show_on_front', 'page' );
update_option( 'page_on_front', $toggle_block_demo_id );
}

WP_CLI::success( sprintf( 'Seeded demo page %d and navigation page %d.', $toggle_block_demo_id, $toggle_block_nav_id ) );
8 changes: 7 additions & 1 deletion .distignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
.claude
.dev
.git
.github
.reviews
.wordpress-org
assets
node_modules
src
Expand All @@ -9,15 +12,18 @@ vendor
.distignore
.editorconfig
.gitignore
.phpcs.xml.dist
.npmpackagejsonlintrc.json
.prettierrc.js
.stylelintrc.json
.wp-env.json
.wp-env.override.json
blueprint.json
composer.json
composer.lock
eslint.config.mjs
package-lock.json
package.json
phpcs.xml.dist
phpstan.neon.dist
README.md
webpack.config.js
40 changes: 40 additions & 0 deletions .github/workflows/js-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Run ESLint, Stylelint, package.json linting, and unit tests in a single job.
#
# Each check runs only when the project defines an npm script for it, so this
# file is safe to sync to a repository that has only some of them. A check the
# project is half set up for is reported on the pull request rather than failed.
#
# This file is managed in https://github.com/happyprime/projects
name: JavaScript checks

on:
pull_request:
# A change to another workflow file is no reason to run this one.
paths:
- '**'
- '!.github/workflows/**'
- .github/workflows/js-checks.yml

# Cancel a run that a newer push to the same pull request has superseded.
# GitHub bills per job by wall-clock time rounded up to the minute, so a run
# nobody is waiting on still costs full price.
#
# `cancel-in-progress` is conditional rather than `true`: these workflows also
# run on pushes to trunk in some projects, and a trunk run is the record of
# what is on the default branch. Only pull request runs are cancelled.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# The consolidated workflow comments on the pull request when a project is set
# up for a check it cannot run. A workflow level permissions block caps what a
# reusable workflow receives, so the write scope is granted here.
permissions:
contents: read
pull-requests: write

jobs:
call-workflow:
uses: happyprime/workflows/.github/workflows/js-checks.yml@trunk
with:
node-version: 24
47 changes: 47 additions & 0 deletions .github/workflows/php-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Run PHPCS, PHPStan, and PHPUnit in a single job.
#
# Each check runs only when the project is configured for it, so this file is
# safe to sync to a repository that has only some of them. A check the project
# is configured for but cannot run is reported on the pull request rather than
# failed.
#
# This file is managed in https://github.com/happyprime/projects
name: PHP checks

on:
pull_request:
branches:
- trunk
- develop
# A change to another workflow file is no reason to run this one.
paths:
- '**'
- '!.github/workflows/**'
- .github/workflows/php-checks.yml

# Cancel a run that a newer push to the same pull request has superseded.
# GitHub bills per job by wall-clock time rounded up to the minute, so a run
# nobody is waiting on still costs full price.
#
# `cancel-in-progress` is conditional rather than `true`: these workflows also
# run on pushes to trunk in some projects, and a trunk run is the record of
# what is on the default branch. Only pull request runs are cancelled.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# The consolidated workflow comments on the pull request when a project is
# configured for a check it cannot run. A workflow level permissions block caps
# what a reusable workflow receives, so the write scope is granted here.
permissions:
contents: read
pull-requests: write

jobs:
call-workflow:
uses: happyprime/workflows/.github/workflows/php-checks.yml@trunk
with:
php-version: '8.4'
wp-versions: 'latest trunk'
secrets:
composer-auth-token: ${{ secrets.COMPOSER_AUTH_TOKEN }}
27 changes: 0 additions & 27 deletions .github/workflows/phpcs.yml

This file was deleted.

Loading
Loading