diff --git a/.dev/demo.html b/.dev/demo.html new file mode 100644 index 0000000..90c079d --- /dev/null +++ b/.dev/demo.html @@ -0,0 +1,155 @@ + +

Each section below uses one Toggle Block feature. Every button controls a block by its HTML anchor.

+ + + +

Hidden until toggled

+ + + +

The panel starts with the toggle-block-hidden class. It holds a link and a button to check keyboard focus.

+ + + + + + + +
+

These details were hidden. Read the WordPress documentation or use the button below.

+ + + +
+ +
+
+ + + +

Visible until toggled

+ + + +

Without the hidden class the target starts open, and the first press hides it.

+ + + + + + + +
+

This notice is visible on load.

+
+ + + +

Screen reader text

+ + + +

This toggle sets an aria-label that extends its visible text.

+ + + + + + + +
+

Toggle Block renders a button with aria-controls and aria-expanded.

+
+ + + +

Toggle group

+ + + +

Toggles inside a block with the toggle-block-group class close each other. "Overview" is the default toggle, so it opens on load and again when another panel closes.

+ + + +
+
+ + + + + + + + + +
+ + + +
+

Overview panel.

+
+ + + +
+

Features panel.

+
+ + + +
+

Support panel with a link to the issue tracker.

+
+
+ + + +

Body class

+ + + +

This toggle adds toggle-block-demo-dark to the body while its panel is open. The Custom HTML block below styles that class.

+ + + + + + + + + + + +
+

The body has the toggle-block-demo-dark class while this panel is open.

+
+ + + +

Open and close buttons

+ + + +

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.

+ + + + + + + + + diff --git a/.dev/navigation.html b/.dev/navigation.html new file mode 100644 index 0000000..c8bce15 --- /dev/null +++ b/.dev/navigation.html @@ -0,0 +1,15 @@ + +

The navigation block below holds a Toggle Block that opens a search panel.

+ + + + + + + + + + + + + diff --git a/.dev/seed.php b/.dev/seed.php new file mode 100644 index 0000000..5f064b0 --- /dev/null +++ b/.dev/seed.php @@ -0,0 +1,65 @@ + '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 ) ); diff --git a/.distignore b/.distignore index 4b061f6..674a634 100644 --- a/.distignore +++ b/.distignore @@ -1,6 +1,9 @@ .claude +.dev .git .github +.reviews +.wordpress-org assets node_modules src @@ -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 diff --git a/.github/workflows/js-checks.yml b/.github/workflows/js-checks.yml new file mode 100644 index 0000000..d1287a9 --- /dev/null +++ b/.github/workflows/js-checks.yml @@ -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 diff --git a/.github/workflows/php-checks.yml b/.github/workflows/php-checks.yml new file mode 100644 index 0000000..e0b2c8b --- /dev/null +++ b/.github/workflows/php-checks.yml @@ -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 }} diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml deleted file mode 100644 index f990f89..0000000 --- a/.github/workflows/phpcs.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Run PHP Code Sniffer (PHPCS) and comment on the pull request with any issues. -# -# This file is managed in https://github.com/happyprime/projects -name: PHPCS (Default, PHP 8.3) - -on: - pull_request: - # A change to another workflow file is no reason to run this one. - paths: - - '**' - - '!.github/workflows/**' - - .github/workflows/phpcs.yml - -# The GITHUB_TOKEN used by Dependabot has read-only permissions by default. We -# provide write permissions to this workflow so that comments can be left on -# the pull request. -# -# @see https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions#changing-github_token-permissions -permissions: - contents: read - pull-requests: write - -jobs: - call-workflow: - uses: happyprime/workflows/.github/workflows/phpcs.yml@trunk - with: - php-version: '8.3' diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml deleted file mode 100644 index 8f2cfc9..0000000 --- a/.github/workflows/phpstan.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Run PHPStan and comment on the pull request with any issues. -# -# This file is managed in https://github.com/happyprime/projects -name: PHPStan (Default, PHP 8.3) - -on: - pull_request: - # A change to another workflow file is no reason to run this one. - paths: - - '**' - - '!.github/workflows/**' - - .github/workflows/phpstan.yml - -# The GITHUB_TOKEN used by Dependabot has read-only permissions by default. We -# provide write permissions to this workflow so that comments can be left on -# the pull request. -# -# @see https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions#changing-github_token-permissions -permissions: - contents: read - pull-requests: write - -jobs: - call-workflow: - uses: happyprime/workflows/.github/workflows/phpstan.yml@trunk - with: - php-version: '8.3' diff --git a/.github/workflows/plugin-check.yml b/.github/workflows/plugin-check.yml index a6a23cd..c3715e6 100644 --- a/.github/workflows/plugin-check.yml +++ b/.github/workflows/plugin-check.yml @@ -1,4 +1,4 @@ -# Run WP.org's Plugin Check. +# Run WP.org's plugin check. # # This file is managed in https://github.com/happyprime/projects name: WP.org Plugin Check @@ -21,6 +21,19 @@ on: - '!.github/workflows/**' - .github/workflows/plugin-check.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' }} + jobs: call-workflow: uses: happyprime/workflows/.github/workflows/plugin-check.yml@trunk + with: + php-version: '8.3' diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index 32843b1..146f2b8 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -9,6 +9,17 @@ on: - '!.github/workflows/**' - .github/workflows/verify-build.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 GITHUB_TOKEN used by Dependabot has read-only permissions by default. We # provide write permissions to this workflow so that comments can be left on # the pull request. diff --git a/.gitignore b/.gitignore index 4c23d5a..6b6afb3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,8 @@ build/**/*.LICENSE.txt .claude/ .reviews/ +.wp-env.override.json +.phpunit.result.cache +/artifacts +/playwright-report +/test-results diff --git a/.npmpackagejsonlintrc.json b/.npmpackagejsonlintrc.json new file mode 100644 index 0000000..2d323d3 --- /dev/null +++ b/.npmpackagejsonlintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "@wordpress/npm-package-json-lint-config" +} diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist deleted file mode 100644 index bcd03a9..0000000 --- a/.phpcs.xml.dist +++ /dev/null @@ -1,5 +0,0 @@ - - - . - - diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..31aa45d --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "@happyprime/stylelint-config" +} diff --git a/.wp-env.json b/.wp-env.json index 3dbf5d8..e9b02eb 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1,6 +1,9 @@ { - "core": "WordPress/WordPress#7.0", - "phpVersion": "8.2", + "$schema": "https://schemas.wp.org/trunk/wp-env.json", + "core": "WordPress/WordPress#7.1-branch", + "phpVersion": "8.4", + "port": 8980, + "testsEnvironment": false, "plugins": [ "." ], diff --git a/README.md b/README.md index 80c682b..712ffa5 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,34 @@ Toggle Block is a very rudimentary block. It renders as a `