From 4970c8dd5590938821fd17ba9df9d2a6d8947a90 Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Sun, 4 Jan 2026 19:48:26 +0530 Subject: [PATCH 01/14] Implement transition animation duration setting with validation and real-time preview --- plugins/view-transitions/includes/admin.php | 3 + .../view-transitions/includes/settings.php | 68 ++++++++++++++++++- plugins/view-transitions/tests/test-theme.php | 32 +++++++++ 3 files changed, 100 insertions(+), 3 deletions(-) diff --git a/plugins/view-transitions/includes/admin.php b/plugins/view-transitions/includes/admin.php index 83e81e45e3..2bb299847d 100644 --- a/plugins/view-transitions/includes/admin.php +++ b/plugins/view-transitions/includes/admin.php @@ -27,9 +27,12 @@ function plvt_print_view_transitions_admin_style(): void { if ( ! isset( $options['enable_admin_transitions'] ) || true !== $options['enable_admin_transitions'] ) { return; } + + $duration_seconds = absint( $options['default_transition_animation_duration'] ) / 1000; ?> 'string', 'enum' => array_keys( plvt_get_view_transition_animation_labels() ), ), + 'default_transition_animation_duration' => array( + 'description' => __( 'Duration of the view transition animation in milliseconds.', 'view-transitions' ), + 'type' => 'integer', + 'minimum' => PLVT_MIN_ANIMATION_DURATION, + 'maximum' => PLVT_MAX_ANIMATION_DURATION, + ), ), 'additionalProperties' => false, ), @@ -473,10 +498,47 @@ function plvt_render_settings_field( array $args ): void { + " + value="" + class="small-text" + min="" + max="" + step="50" + + aria-describedby="" + + > + + ' . esc_html( (string) $seconds ) . 's' + ); + ?> + + id="" name="" value="" diff --git a/plugins/view-transitions/tests/test-theme.php b/plugins/view-transitions/tests/test-theme.php index 7879c72f73..c675ccd903 100644 --- a/plugins/view-transitions/tests/test-theme.php +++ b/plugins/view-transitions/tests/test-theme.php @@ -48,4 +48,36 @@ public function test_plvt_load_view_transitions(): void { $this->assertTrue( wp_style_is( 'plvt-view-transitions', 'registered' ) ); $this->assertTrue( wp_style_is( 'plvt-view-transitions', 'enqueued' ) ); } + + /** + * @covers ::plvt_inject_animation_duration + */ + public function test_plvt_inject_animation_duration_with_existing_css(): void { + $css = '::view-transition-old(*) { animation-name: test; }'; + $result = plvt_inject_animation_duration( $css, 500 ); + + $this->assertStringContainsString( '--plvt-view-transition-animation-duration: 0.5s', $result ); + $this->assertStringContainsString( $css, $result ); + } + + /** + * @covers ::plvt_inject_animation_duration + */ + public function test_plvt_inject_animation_duration_with_empty_css(): void { + $result = plvt_inject_animation_duration( '', 400 ); + + $this->assertStringContainsString( 'animation-duration: 0.4s', $result ); + $this->assertStringNotContainsString( '--plvt-view-transition-animation-duration', $result ); + } + + /** + * @covers ::plvt_inject_animation_duration + */ + public function test_plvt_inject_animation_duration_converts_milliseconds_to_seconds(): void { + $result = plvt_inject_animation_duration( '', 1000 ); + $this->assertStringContainsString( '1s', $result ); + + $result = plvt_inject_animation_duration( '', 250 ); + $this->assertStringContainsString( '0.25s', $result ); + } } From 07de264c348cb061c80909c8ee08d06d55dcc698 Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Sun, 18 Jan 2026 21:54:03 +0530 Subject: [PATCH 02/14] Addressed feedback changes and added unit test for better coverage --- .../view-transitions/includes/settings.php | 76 ++++++++++------ plugins/view-transitions/tests/test-admin.php | 60 +++++++++++++ .../view-transitions/tests/test-settings.php | 89 +++++++++++++++++++ 3 files changed, 200 insertions(+), 25 deletions(-) create mode 100644 plugins/view-transitions/tests/test-admin.php create mode 100644 plugins/view-transitions/tests/test-settings.php diff --git a/plugins/view-transitions/includes/settings.php b/plugins/view-transitions/includes/settings.php index 1db2008e62..d920fe2810 100644 --- a/plugins/view-transitions/includes/settings.php +++ b/plugins/view-transitions/includes/settings.php @@ -350,9 +350,14 @@ static function (): void { 'description' => __( 'Choose the animation that is used for the default view transition type.', 'view-transitions' ), ), 'default_transition_animation_duration' => array( - 'section' => 'plvt_view_transitions', - 'title' => __( 'Transition Animation Duration', 'view-transitions' ), - 'description' => __( 'Control the duration of the view transition. Enter the value in milliseconds (e.g., 500, 1000, 2000).', 'view-transitions' ), + 'section' => 'plvt_view_transitions', + 'title' => __( 'Transition Animation Duration', 'view-transitions' ), + 'description' => __( 'Control the duration of the view transition. Enter the value in milliseconds (e.g., 500, 1000, 2000).', 'view-transitions' ), + 'min' => PLVT_MIN_ANIMATION_DURATION, + 'max' => PLVT_MAX_ANIMATION_DURATION, + 'step' => 50, + 'unit' => 'ms', + 'show_seconds' => true, ), 'header_selector' => array( 'section' => 'plvt_view_transitions', @@ -499,19 +504,29 @@ function plvt_render_settings_field( array $args ): void { " value="" class="small-text" - min="" - max="" - step="50" + min="" + + max="" + + step="" + aria-describedby="" @@ -519,23 +534,34 @@ class="small-text" } ?> > - - ' . esc_html( (string) $seconds ) . 's' - ); - ?> - + + ' . esc_html( (string) $seconds ) . 's' + ); + wp_print_inline_script_tag( + sprintf( + 'document.getElementById( %s ).addEventListener( "input", function( e ) { document.getElementById( %s ).textContent = ( parseInt( e.target.value, 10 ) / 1000 ) + "s"; } );', + wp_json_encode( $field_id ), + wp_json_encode( $field_id . '-seconds' ) + ) + ); + } else { + echo esc_html( $args['unit'] ); + } + ?> + + false ) ); + + ob_start(); + plvt_print_view_transitions_admin_style(); + $output = ob_get_clean(); + + $this->assertEmpty( $output ); + } + + /** + * @covers ::plvt_print_view_transitions_admin_style + */ + public function test_plvt_print_view_transitions_admin_style_enabled(): void { + update_option( + 'plvt_view_transitions', + array( + 'enable_admin_transitions' => true, + 'default_transition_animation_duration' => 500, + ) + ); + + ob_start(); + plvt_print_view_transitions_admin_style(); + $output = ob_get_clean(); + + $this->assertStringContainsString( '@view-transition { navigation: auto; }', $output ); + $this->assertStringContainsString( 'animation-duration: 0.5s', $output ); + } + + /** + * @covers ::plvt_print_view_transitions_admin_style + */ + public function test_plvt_print_view_transitions_admin_style_uses_default_duration(): void { + update_option( + 'plvt_view_transitions', + array( 'enable_admin_transitions' => true ) + ); + + ob_start(); + plvt_print_view_transitions_admin_style(); + $output = ob_get_clean(); + + // Default duration is 400ms = 0.4s. + $this->assertStringContainsString( 'animation-duration: 0.4s', $output ); + } +} diff --git a/plugins/view-transitions/tests/test-settings.php b/plugins/view-transitions/tests/test-settings.php new file mode 100644 index 0000000000..93954fce1c --- /dev/null +++ b/plugins/view-transitions/tests/test-settings.php @@ -0,0 +1,89 @@ +assertSame( plvt_get_setting_default(), plvt_sanitize_setting( null ) ); + $this->assertSame( plvt_get_setting_default(), plvt_sanitize_setting( 'string' ) ); + $this->assertSame( plvt_get_setting_default(), plvt_sanitize_setting( 123 ) ); + } + + /** + * @covers ::plvt_sanitize_setting + */ + public function test_plvt_sanitize_setting_clamps_duration_minimum(): void { + $input = array( 'default_transition_animation_duration' => 50 ); + $result = plvt_sanitize_setting( $input ); + $this->assertSame( PLVT_MIN_ANIMATION_DURATION, $result['default_transition_animation_duration'] ); + } + + /** + * @covers ::plvt_sanitize_setting + */ + public function test_plvt_sanitize_setting_clamps_duration_maximum(): void { + $input = array( 'default_transition_animation_duration' => 10000 ); + $result = plvt_sanitize_setting( $input ); + $this->assertSame( PLVT_MAX_ANIMATION_DURATION, $result['default_transition_animation_duration'] ); + } + + /** + * @covers ::plvt_sanitize_setting + */ + public function test_plvt_sanitize_setting_accepts_valid_duration(): void { + $input = array( 'default_transition_animation_duration' => 500 ); + $result = plvt_sanitize_setting( $input ); + $this->assertSame( 500, $result['default_transition_animation_duration'] ); + } + + /** + * @covers ::plvt_sanitize_setting + */ + public function test_plvt_sanitize_setting_handles_negative_duration(): void { + $input = array( 'default_transition_animation_duration' => -500 ); + $result = plvt_sanitize_setting( $input ); + // absint converts negative to positive, then clamps. + $this->assertSame( 500, $result['default_transition_animation_duration'] ); + } + + /** + * @covers ::plvt_sanitize_setting + */ + public function test_plvt_sanitize_setting_handles_string_duration(): void { + $input = array( 'default_transition_animation_duration' => '750' ); + $result = plvt_sanitize_setting( $input ); + $this->assertSame( 750, $result['default_transition_animation_duration'] ); + } + + /** + * @covers ::plvt_get_setting_default + */ + public function test_plvt_get_setting_default_has_valid_duration(): void { + $defaults = plvt_get_setting_default(); + $this->assertArrayHasKey( 'default_transition_animation_duration', $defaults ); + $this->assertIsInt( $defaults['default_transition_animation_duration'] ); + $this->assertGreaterThanOrEqual( PLVT_MIN_ANIMATION_DURATION, $defaults['default_transition_animation_duration'] ); + $this->assertLessThanOrEqual( PLVT_MAX_ANIMATION_DURATION, $defaults['default_transition_animation_duration'] ); + } + + /** + * @covers ::plvt_sanitize_setting + */ + public function test_plvt_sanitize_setting_validates_animation_type(): void { + $input = array( 'default_transition_animation' => 'invalid-animation' ); + $result = plvt_sanitize_setting( $input ); + $this->assertSame( 'fade', $result['default_transition_animation'] ); + + $input = array( 'default_transition_animation' => 'slide-from-right' ); + $result = plvt_sanitize_setting( $input ); + $this->assertSame( 'slide-from-right', $result['default_transition_animation'] ); + } +} From 58eee135611822bd8ab8d06900345ce3e84a94a8 Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Sun, 18 Jan 2026 22:04:23 +0530 Subject: [PATCH 03/14] Fixed phpstan errors --- plugins/view-transitions/includes/admin.php | 2 +- plugins/view-transitions/includes/settings.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/view-transitions/includes/admin.php b/plugins/view-transitions/includes/admin.php index 2bb299847d..c5132b7ab2 100644 --- a/plugins/view-transitions/includes/admin.php +++ b/plugins/view-transitions/includes/admin.php @@ -32,7 +32,7 @@ function plvt_print_view_transitions_admin_style(): void { ?> > Date: Sun, 25 Jan 2026 16:37:12 +0530 Subject: [PATCH 04/14] Removed seconds option from view transition duration --- plugins/view-transitions/includes/admin.php | 4 +- .../view-transitions/includes/settings.php | 40 ++++--------------- plugins/view-transitions/tests/test-admin.php | 6 +-- 3 files changed, 13 insertions(+), 37 deletions(-) diff --git a/plugins/view-transitions/includes/admin.php b/plugins/view-transitions/includes/admin.php index c5132b7ab2..bb3f3afd71 100644 --- a/plugins/view-transitions/includes/admin.php +++ b/plugins/view-transitions/includes/admin.php @@ -28,11 +28,11 @@ function plvt_print_view_transitions_admin_style(): void { return; } - $duration_seconds = absint( $options['default_transition_animation_duration'] ) / 1000; + $duration = absint( $options['default_transition_animation_duration'] ); ?> __( 'Choose the animation that is used for the default view transition type.', 'view-transitions' ), ), 'default_transition_animation_duration' => array( - 'section' => 'plvt_view_transitions', - 'title' => __( 'Transition Animation Duration', 'view-transitions' ), - 'description' => __( 'Control the duration of the view transition. Enter the value in milliseconds (e.g., 500, 1000, 2000).', 'view-transitions' ), - 'min' => PLVT_MIN_ANIMATION_DURATION, - 'max' => PLVT_MAX_ANIMATION_DURATION, - 'step' => 50, - 'unit' => 'ms', - 'show_seconds' => true, + 'section' => 'plvt_view_transitions', + 'title' => __( 'Transition Animation Duration', 'view-transitions' ), + 'description' => __( 'Control the duration of the view transition. Enter the value in milliseconds (e.g., 500, 1000, 2000).', 'view-transitions' ), + 'min' => PLVT_MIN_ANIMATION_DURATION, + 'max' => PLVT_MAX_ANIMATION_DURATION, + 'step' => 50, + 'unit' => 'ms', ), 'header_selector' => array( 'section' => 'plvt_view_transitions', @@ -537,29 +535,7 @@ class="small-text" - - ' . esc_html( (string) $seconds ) . 's' - ); - wp_print_inline_script_tag( - sprintf( - 'document.getElementById( %s ).addEventListener( "input", function( e ) { document.getElementById( %s ).textContent = ( parseInt( e.target.value, 10 ) / 1000 ) + "s"; } );', - wp_json_encode( $field_id ), - wp_json_encode( $field_id . '-seconds' ) - ) - ); - } else { - echo esc_html( $args['unit'] ); - } - ?> - + assertStringContainsString( '@view-transition { navigation: auto; }', $output ); - $this->assertStringContainsString( 'animation-duration: 0.5s', $output ); + $this->assertStringContainsString( '--plvt-view-transition-animation-duration: 500ms', $output ); } /** @@ -54,7 +54,7 @@ public function test_plvt_print_view_transitions_admin_style_uses_default_durati plvt_print_view_transitions_admin_style(); $output = ob_get_clean(); - // Default duration is 400ms = 0.4s. - $this->assertStringContainsString( 'animation-duration: 0.4s', $output ); + // Default duration is 400ms. + $this->assertStringContainsString( '--plvt-view-transition-animation-duration: 400ms', $output ); } } From 384c1ef1333a3b92e84e8400991c51e47ef5100e Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Sun, 25 Jan 2026 22:45:30 +0530 Subject: [PATCH 05/14] Add real-time CSS selector validation to View Transitions settings --- .../css/validator-selector.css | 34 ++++++ plugins/view-transitions/hooks.php | 1 + plugins/view-transitions/includes/admin.php | 35 ++++++ .../view-transitions/includes/settings.php | 17 +++ .../view-transitions/js/validator-selector.js | 108 ++++++++++++++++++ 5 files changed, 195 insertions(+) create mode 100644 plugins/view-transitions/css/validator-selector.css create mode 100644 plugins/view-transitions/js/validator-selector.js diff --git a/plugins/view-transitions/css/validator-selector.css b/plugins/view-transitions/css/validator-selector.css new file mode 100644 index 0000000000..c680f1e35c --- /dev/null +++ b/plugins/view-transitions/css/validator-selector.css @@ -0,0 +1,34 @@ +/** + * CSS Selector Validation Styles + * + * Styles for visual feedback of CSS selector validation. + * + * @since n.e.x.t + */ + +/* Valid selector indicator */ +input.plvt-selector-valid { + border-color: #46b450 !important; + background-color: #f0fdf4; +} + +input.plvt-selector-valid:focus { + border-color: #46b450 !important; +} + +/* Invalid selector indicator */ +input.plvt-selector-invalid { + border-color: #dc2626 !important; + background-color: #fdf2f2; +} + +input.plvt-selector-invalid:focus { + border-color: #dc2626 !important; +} + +/* Error message styling */ +.plvt-selector-error { + color: #dc2626; + margin-top: 5px; + font-style: italic; +} diff --git a/plugins/view-transitions/hooks.php b/plugins/view-transitions/hooks.php index 260cc7381a..b57106ec36 100644 --- a/plugins/view-transitions/hooks.php +++ b/plugins/view-transitions/hooks.php @@ -39,4 +39,5 @@ function plvt_render_generator(): void { add_action( 'init', 'plvt_register_setting' ); add_action( 'init', 'plvt_apply_settings_to_theme_support' ); add_action( 'load-options-reading.php', 'plvt_add_setting_ui' ); +add_action( 'admin_enqueue_scripts', 'plvt_enqueue_selector_validation' ); add_filter( 'plugin_action_links_' . plugin_basename( VIEW_TRANSITIONS_MAIN_FILE ), 'plvt_add_settings_action_link' ); diff --git a/plugins/view-transitions/includes/admin.php b/plugins/view-transitions/includes/admin.php index bb3f3afd71..d38d5f8772 100644 --- a/plugins/view-transitions/includes/admin.php +++ b/plugins/view-transitions/includes/admin.php @@ -37,3 +37,38 @@ function plvt_print_view_transitions_admin_style(): void { id ) { + return; + } + + // Enqueue validation CSS. + wp_enqueue_style( + 'plvt-selector-validator', + plugin_dir_url( VIEW_TRANSITIONS_MAIN_FILE ) . 'css/validator-selector.css', + array(), + VIEW_TRANSITIONS_VERSION + ); + + // Enqueue validation JS. + wp_enqueue_script( + 'plvt-selector-validator', + plugin_dir_url( VIEW_TRANSITIONS_MAIN_FILE ) . 'js/validator-selector.js', + array(), + VIEW_TRANSITIONS_VERSION, + array( 'in_footer' => false ) + ); +} diff --git a/plugins/view-transitions/includes/settings.php b/plugins/view-transitions/includes/settings.php index 911c5f9e1b..51fe736bba 100644 --- a/plugins/view-transitions/includes/settings.php +++ b/plugins/view-transitions/includes/settings.php @@ -539,6 +539,18 @@ class="small-text" + data-plvt-validate-selector + aria-describedby="" diff --git a/plugins/view-transitions/js/validator-selector.js b/plugins/view-transitions/js/validator-selector.js new file mode 100644 index 0000000000..e4a07f9b90 --- /dev/null +++ b/plugins/view-transitions/js/validator-selector.js @@ -0,0 +1,108 @@ +/** + * CSS Selector Validation for View Transitions Settings + * + * This script provides real-time validation for CSS selector input fields + * in the View Transitions settings panel. + * + * @since n.e.x.t + */ + +( () => { + /** + * Validates a CSS selector by attempting to use it with document.querySelector. + * + * @param {string} selector The CSS selector to validate. + * @return {Object} Object with 'valid' boolean and optional 'message' string. + */ + function validateSelector( selector ) { + // Empty selectors are allowed (they reset to default) + if ( '' === selector.trim() ) { + return { valid: true }; + } + + try { + document.querySelector( selector ); + return { valid: true }; + } catch ( error ) { + return { + valid: false, + message: 'Invalid CSS selector: ' + error.message, + }; + } + } + + /** + * Sets custom validity for a selector input field. + * + * @param {HTMLInputElement} input The input element to validate. + */ + function updateValidation( input ) { + const result = validateSelector( input.value ); + + if ( result.valid ) { + input.setCustomValidity( '' ); + input.classList.remove( 'plvt-selector-invalid' ); + input.classList.add( 'plvt-selector-valid' ); + + // Remove any existing error message + const existingError = input.parentNode.querySelector( + '.plvt-selector-error' + ); + if ( existingError ) { + existingError.remove(); + } + } else { + input.setCustomValidity( result.message ); + input.classList.remove( 'plvt-selector-valid' ); + input.classList.add( 'plvt-selector-invalid' ); + + // Show error message + let errorElement = input.parentNode.querySelector( + '.plvt-selector-error' + ); + if ( ! errorElement ) { + errorElement = document.createElement( 'p' ); + errorElement.className = 'plvt-selector-error description'; + input.parentNode.insertBefore( + errorElement, + input.nextSibling + ); + } + errorElement.textContent = result.message; + } + } + + /** + * Initializes validation for all selector input fields. + */ + function initValidation() { + // Target all text inputs for selectors + const selectorInputs = document.querySelectorAll( + 'input[data-plvt-validate-selector]' + ); + + selectorInputs.forEach( ( input ) => { + // Validate on blur + input.addEventListener( 'blur', () => { + updateValidation( input ); + } ); + + // Validate on input for real-time feedback + input.addEventListener( 'input', () => { + updateValidation( input ); + } ); + + // Validate on page load if field has a value + if ( '' !== input.value.trim() ) { + updateValidation( input ); + } + } ); + } + + // Initialize when DOM is ready + if ( 'loading' === document.readyState ) { + document.addEventListener( 'DOMContentLoaded', initValidation ); + } else { + initValidation(); + } +} )(); From 1004171c4d21c1b72e422e76104293be91e6b1d6 Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Sun, 25 Jan 2026 22:51:58 +0530 Subject: [PATCH 06/14] Fix js lint error --- plugins/view-transitions/js/validator-selector.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/view-transitions/js/validator-selector.js b/plugins/view-transitions/js/validator-selector.js index e4a07f9b90..43fd7f7d3b 100644 --- a/plugins/view-transitions/js/validator-selector.js +++ b/plugins/view-transitions/js/validator-selector.js @@ -81,7 +81,9 @@ 'input[data-plvt-validate-selector]' ); - selectorInputs.forEach( ( input ) => { + selectorInputs.forEach( ( element ) => { + const input = /** @type {HTMLInputElement} */ ( element ); + // Validate on blur input.addEventListener( 'blur', () => { updateValidation( input ); From f9fba526fc10a2fa05cfe813bd81691069f47efd Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Sun, 25 Jan 2026 23:20:25 +0530 Subject: [PATCH 07/14] Fix issue of failing unit test case --- plugins/view-transitions/includes/admin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/view-transitions/includes/admin.php b/plugins/view-transitions/includes/admin.php index d38d5f8772..1e00401da3 100644 --- a/plugins/view-transitions/includes/admin.php +++ b/plugins/view-transitions/includes/admin.php @@ -24,7 +24,7 @@ */ function plvt_print_view_transitions_admin_style(): void { $options = plvt_get_stored_setting_value(); - if ( ! isset( $options['enable_admin_transitions'] ) || true !== $options['enable_admin_transitions'] ) { + if ( ! isset( $options['enable_admin_transitions'] ) || ! (bool) $options['enable_admin_transitions'] ) { return; } From 988d6629d5b47be406ca560d3efb340725ad8ce9 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 25 Jan 2026 20:59:27 -0800 Subject: [PATCH 08/14] Skip tests where view transitions are already in the admin (on trunk) --- plugins/view-transitions/tests/test-admin.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/view-transitions/tests/test-admin.php b/plugins/view-transitions/tests/test-admin.php index 6c731d6e32..5224c4ad9f 100644 --- a/plugins/view-transitions/tests/test-admin.php +++ b/plugins/view-transitions/tests/test-admin.php @@ -25,6 +25,10 @@ public function test_plvt_print_view_transitions_admin_style_disabled(): void { * @covers ::plvt_print_view_transitions_admin_style */ public function test_plvt_print_view_transitions_admin_style_enabled(): void { + if ( function_exists( 'wp_enqueue_view_transitions_admin_css' ) ) { + $this->markTestSkipped(); + } + update_option( 'plvt_view_transitions', array( @@ -45,6 +49,10 @@ public function test_plvt_print_view_transitions_admin_style_enabled(): void { * @covers ::plvt_print_view_transitions_admin_style */ public function test_plvt_print_view_transitions_admin_style_uses_default_duration(): void { + if ( function_exists( 'wp_enqueue_view_transitions_admin_css' ) ) { + $this->markTestSkipped(); + } + update_option( 'plvt_view_transitions', array( 'enable_admin_transitions' => true ) From 02690713cb18f753b1f7afee412ccfde7fb6bdd6 Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Fri, 13 Feb 2026 09:21:23 +0530 Subject: [PATCH 09/14] Use CSS.supports for returning valid css selector --- plugins/view-transitions/includes/admin.php | 11 ++++++++--- .../view-transitions/js/validator-selector.js | 16 +++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/plugins/view-transitions/includes/admin.php b/plugins/view-transitions/includes/admin.php index b647e0109b..3eac423a78 100644 --- a/plugins/view-transitions/includes/admin.php +++ b/plugins/view-transitions/includes/admin.php @@ -33,11 +33,10 @@ function plvt_print_view_transitions_admin_style(): void { return; } - $duration = absint( $options['default_transition_animation_duration'] ); ?> false ) ); + + // Set up translations for the script. + wp_set_script_translations( + 'plvt-selector-validator', + 'view-transitions' + ); } diff --git a/plugins/view-transitions/js/validator-selector.js b/plugins/view-transitions/js/validator-selector.js index 43fd7f7d3b..fa363b79ae 100644 --- a/plugins/view-transitions/js/validator-selector.js +++ b/plugins/view-transitions/js/validator-selector.js @@ -12,7 +12,7 @@ * Validates a CSS selector by attempting to use it with document.querySelector. * * @param {string} selector The CSS selector to validate. - * @return {Object} Object with 'valid' boolean and optional 'message' string. + * @return {boolean} Whether the selector is valid. */ function validateSelector( selector ) { // Empty selectors are allowed (they reset to default) @@ -20,15 +20,7 @@ return { valid: true }; } - try { - document.querySelector( selector ); - return { valid: true }; - } catch ( error ) { - return { - valid: false, - message: 'Invalid CSS selector: ' + error.message, - }; - } + return CSS.supports( `selector(${ selector })` ); } /** @@ -52,7 +44,9 @@ existingError.remove(); } } else { - input.setCustomValidity( result.message ); + input.setCustomValidity( + wp.i18n.__( 'Invalid CSS selector', 'view-transitions' ) + ); input.classList.remove( 'plvt-selector-valid' ); input.classList.add( 'plvt-selector-invalid' ); From 6b831ac5066ecea1ff1d8ceed93bbeda9f5f8ed3 Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Fri, 13 Feb 2026 09:39:40 +0530 Subject: [PATCH 10/14] fix js linting error --- .../view-transitions/js/validator-selector.js | 174 ++++++++++-------- 1 file changed, 94 insertions(+), 80 deletions(-) diff --git a/plugins/view-transitions/js/validator-selector.js b/plugins/view-transitions/js/validator-selector.js index fa363b79ae..6245688fe6 100644 --- a/plugins/view-transitions/js/validator-selector.js +++ b/plugins/view-transitions/js/validator-selector.js @@ -7,98 +7,112 @@ * @since n.e.x.t */ -( () => { - /** - * Validates a CSS selector by attempting to use it with document.querySelector. - * - * @param {string} selector The CSS selector to validate. - * @return {boolean} Whether the selector is valid. - */ - function validateSelector( selector ) { - // Empty selectors are allowed (they reset to default) - if ( '' === selector.trim() ) { - return { valid: true }; - } +/** + * @typedef {Object} WP + * @property {Object} i18n - WordPress internationalization object + * @property {Function} i18n.__ - Translation function + */ - return CSS.supports( `selector(${ selector })` ); - } +/** + * WordPress global object. + * + * @type {WP} + */ - /** - * Sets custom validity for a selector input field. - * - * @param {HTMLInputElement} input The input element to validate. - */ - function updateValidation( input ) { - const result = validateSelector( input.value ); +( + () => { + /** + * Validates a CSS selector by attempting to use it with document.querySelector. + * + * @param {string} selector The CSS selector to validate. + * @return {boolean} Whether the selector is valid. + */ + function validateSelector( selector ) { + // Empty selectors are allowed (they reset to default) + if ( '' === selector.trim() ) { + return true; + } - if ( result.valid ) { - input.setCustomValidity( '' ); - input.classList.remove( 'plvt-selector-invalid' ); - input.classList.add( 'plvt-selector-valid' ); + return CSS.supports( `selector(${ selector })` ); + } - // Remove any existing error message - const existingError = input.parentNode.querySelector( - '.plvt-selector-error' - ); - if ( existingError ) { - existingError.remove(); - } - } else { - input.setCustomValidity( - wp.i18n.__( 'Invalid CSS selector', 'view-transitions' ) - ); - input.classList.remove( 'plvt-selector-valid' ); - input.classList.add( 'plvt-selector-invalid' ); + /** + * Sets custom validity for a selector input field. + * + * @param {HTMLInputElement} input The input element to validate. + */ + function updateValidation( input ) { + const isValid = validateSelector( input.value ); - // Show error message - let errorElement = input.parentNode.querySelector( - '.plvt-selector-error' - ); - if ( ! errorElement ) { - errorElement = document.createElement( 'p' ); - errorElement.className = 'plvt-selector-error description'; - input.parentNode.insertBefore( - errorElement, - input.nextSibling + if ( isValid ) { + input.setCustomValidity( '' ); + input.classList.remove( 'plvt-selector-invalid' ); + input.classList.add( 'plvt-selector-valid' ); + + const existingError = input.parentNode.querySelector( + '.plvt-selector-error' + ); + if ( existingError ) { + existingError.remove(); + } + } else { + const errorMessage = wp.i18n.__( + 'Invalid CSS selector', + 'view-transitions' + ); + input.setCustomValidity( errorMessage ); + input.classList.remove( 'plvt-selector-valid' ); + input.classList.add( 'plvt-selector-invalid' ); + + let errorElement = input.parentNode.querySelector( + '.plvt-selector-error' ); + if ( ! errorElement ) { + errorElement = document.createElement( 'p' ); + errorElement.className = 'plvt-selector-error description'; + input.parentNode.insertBefore( + errorElement, + input.nextSibling + ); + } + errorElement.textContent = errorMessage; } - errorElement.textContent = result.message; } - } - /** - * Initializes validation for all selector input fields. - */ - function initValidation() { - // Target all text inputs for selectors - const selectorInputs = document.querySelectorAll( - 'input[data-plvt-validate-selector]' - ); + /** + * Initializes validation for all selector input fields. + */ + function initValidation() { + // Target all text inputs for selectors + const selectorInputs = document.querySelectorAll( + 'input[data-plvt-validate-selector]' + ); - selectorInputs.forEach( ( element ) => { - const input = /** @type {HTMLInputElement} */ ( element ); + selectorInputs.forEach( ( element ) => { + const input = /** @type {HTMLInputElement} */ ( element ); - // Validate on blur - input.addEventListener( 'blur', () => { - updateValidation( input ); - } ); + // Validate on blur + input.addEventListener( 'blur', () => { + updateValidation( input ); + } ); - // Validate on input for real-time feedback - input.addEventListener( 'input', () => { - updateValidation( input ); - } ); + // Validate on input for real-time feedback + input.addEventListener( 'input', () => { + updateValidation( input ); + } ); - // Validate on page load if field has a value - if ( '' !== input.value.trim() ) { - updateValidation( input ); - } - } ); - } + // Validate on page load if field has a value + if ( '' !== input.value.trim() ) { + updateValidation( input ); + } + } ); + } - // Initialize when DOM is ready - if ( 'loading' === document.readyState ) { - document.addEventListener( 'DOMContentLoaded', initValidation ); - } else { - initValidation(); + // Initialize when DOM is ready + if ( 'loading' === document.readyState ) { + document.addEventListener( 'DOMContentLoaded', initValidation ); + } else { + initValidation(); + } } -} )(); +)(); From 3aab50e0fe79d16ce7624396a3fc50663c21621e Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Fri, 13 Feb 2026 09:49:03 +0530 Subject: [PATCH 11/14] fix linting error --- plugins/view-transitions/js/validator-selector.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/view-transitions/js/validator-selector.js b/plugins/view-transitions/js/validator-selector.js index 6245688fe6..b63b0f7832 100644 --- a/plugins/view-transitions/js/validator-selector.js +++ b/plugins/view-transitions/js/validator-selector.js @@ -14,10 +14,11 @@ */ /** - * WordPress global object. + * Declare the global `wp` object. * * @type {WP} */ +declare const wp: WP; ( () => { From 6ace88b3b8edb19f14003bcd0485f3f581fda77c Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Fri, 13 Feb 2026 09:58:21 +0530 Subject: [PATCH 12/14] Fix linting issues --- plugins/view-transitions/js/validator-selector.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/view-transitions/js/validator-selector.js b/plugins/view-transitions/js/validator-selector.js index b63b0f7832..0f0180b379 100644 --- a/plugins/view-transitions/js/validator-selector.js +++ b/plugins/view-transitions/js/validator-selector.js @@ -14,11 +14,10 @@ */ /** - * Declare the global `wp` object. + * Global `wp` object. * * @type {WP} */ -declare const wp: WP; ( () => { From 77422374f923ab703a47758f7c922ed93e8098a2 Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Fri, 13 Feb 2026 10:05:51 +0530 Subject: [PATCH 13/14] Ignore linting error since wp is already available in global script --- plugins/view-transitions/js/validator-selector.js | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/view-transitions/js/validator-selector.js b/plugins/view-transitions/js/validator-selector.js index 0f0180b379..e5d2b62999 100644 --- a/plugins/view-transitions/js/validator-selector.js +++ b/plugins/view-transitions/js/validator-selector.js @@ -56,6 +56,7 @@ existingError.remove(); } } else { + // @ts-ignore const errorMessage = wp.i18n.__( 'Invalid CSS selector', 'view-transitions' From fd64beb6e3de9b6b2ffdd8d7f33ba1f30168dc48 Mon Sep 17 00:00:00 2001 From: Sarthak Jaiswal Date: Fri, 13 Feb 2026 10:10:12 +0530 Subject: [PATCH 14/14] Fix JS lint error --- .../view-transitions/js/validator-selector.js | 170 +++++++++--------- 1 file changed, 81 insertions(+), 89 deletions(-) diff --git a/plugins/view-transitions/js/validator-selector.js b/plugins/view-transitions/js/validator-selector.js index e5d2b62999..eb7491facf 100644 --- a/plugins/view-transitions/js/validator-selector.js +++ b/plugins/view-transitions/js/validator-selector.js @@ -13,107 +13,99 @@ * @property {Function} i18n.__ - Translation function */ -/** - * Global `wp` object. - * - * @type {WP} - */ - -( - () => { - /** - * Validates a CSS selector by attempting to use it with document.querySelector. - * - * @param {string} selector The CSS selector to validate. - * @return {boolean} Whether the selector is valid. - */ - function validateSelector( selector ) { - // Empty selectors are allowed (they reset to default) - if ( '' === selector.trim() ) { - return true; - } - - return CSS.supports( `selector(${ selector })` ); +( () => { + /** + * Validates a CSS selector by attempting to use it with document.querySelector. + * + * @param {string} selector The CSS selector to validate. + * @return {boolean} Whether the selector is valid. + */ + function validateSelector( selector ) { + // Empty selectors are allowed (they reset to default) + if ( '' === selector.trim() ) { + return true; } - /** - * Sets custom validity for a selector input field. - * - * @param {HTMLInputElement} input The input element to validate. - */ - function updateValidation( input ) { - const isValid = validateSelector( input.value ); + return CSS.supports( `selector(${ selector })` ); + } - if ( isValid ) { - input.setCustomValidity( '' ); - input.classList.remove( 'plvt-selector-invalid' ); - input.classList.add( 'plvt-selector-valid' ); + /** + * Sets custom validity for a selector input field. + * + * @param {HTMLInputElement} input The input element to validate. + */ + function updateValidation( input ) { + const isValid = validateSelector( input.value ); - const existingError = input.parentNode.querySelector( - '.plvt-selector-error' - ); - if ( existingError ) { - existingError.remove(); - } - } else { - // @ts-ignore - const errorMessage = wp.i18n.__( - 'Invalid CSS selector', - 'view-transitions' - ); - input.setCustomValidity( errorMessage ); - input.classList.remove( 'plvt-selector-valid' ); - input.classList.add( 'plvt-selector-invalid' ); + if ( isValid ) { + input.setCustomValidity( '' ); + input.classList.remove( 'plvt-selector-invalid' ); + input.classList.add( 'plvt-selector-valid' ); - let errorElement = input.parentNode.querySelector( - '.plvt-selector-error' - ); - if ( ! errorElement ) { - errorElement = document.createElement( 'p' ); - errorElement.className = 'plvt-selector-error description'; - input.parentNode.insertBefore( - errorElement, - input.nextSibling - ); - } - errorElement.textContent = errorMessage; + const existingError = input.parentNode.querySelector( + '.plvt-selector-error' + ); + if ( existingError ) { + existingError.remove(); } - } + } else { + // @ts-ignore + const errorMessage = wp.i18n.__( + 'Invalid CSS selector', + 'view-transitions' + ); + input.setCustomValidity( errorMessage ); + input.classList.remove( 'plvt-selector-valid' ); + input.classList.add( 'plvt-selector-invalid' ); - /** - * Initializes validation for all selector input fields. - */ - function initValidation() { - // Target all text inputs for selectors - const selectorInputs = document.querySelectorAll( - 'input[data-plvt-validate-selector]' + let errorElement = input.parentNode.querySelector( + '.plvt-selector-error' ); + if ( ! errorElement ) { + errorElement = document.createElement( 'p' ); + errorElement.className = 'plvt-selector-error description'; + input.parentNode.insertBefore( + errorElement, + input.nextSibling + ); + } + errorElement.textContent = errorMessage; + } + } - selectorInputs.forEach( ( element ) => { - const input = /** @type {HTMLInputElement} */ ( element ); + /** + * Initializes validation for all selector input fields. + */ + function initValidation() { + // Target all text inputs for selectors + const selectorInputs = document.querySelectorAll( + 'input[data-plvt-validate-selector]' + ); - // Validate on blur - input.addEventListener( 'blur', () => { - updateValidation( input ); - } ); + selectorInputs.forEach( ( element ) => { + const input = /** @type {HTMLInputElement} */ ( element ); - // Validate on input for real-time feedback - input.addEventListener( 'input', () => { - updateValidation( input ); - } ); + // Validate on blur + input.addEventListener( 'blur', () => { + updateValidation( input ); + } ); - // Validate on page load if field has a value - if ( '' !== input.value.trim() ) { - updateValidation( input ); - } + // Validate on input for real-time feedback + input.addEventListener( 'input', () => { + updateValidation( input ); } ); - } - // Initialize when DOM is ready - if ( 'loading' === document.readyState ) { - document.addEventListener( 'DOMContentLoaded', initValidation ); - } else { - initValidation(); - } + // Validate on page load if field has a value + if ( '' !== input.value.trim() ) { + updateValidation( input ); + } + } ); + } + + // Initialize when DOM is ready + if ( 'loading' === document.readyState ) { + document.addEventListener( 'DOMContentLoaded', initValidation ); + } else { + initValidation(); } -)(); +} )();