Skip to content

Commit

Permalink
Merge pull request #6823 from ampproject/bug/6115-skip-link
Browse files Browse the repository at this point in the history
Add skip link for Full Site Editing themes
  • Loading branch information
westonruter authored Jan 26, 2022
2 parents a5f6909 + 03f03d8 commit b311b57
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 1 deletion.
2 changes: 1 addition & 1 deletion includes/class-amp-theme-support.php
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ public static function add_hooks() {
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );

// Temporary workarounds for <https://github.com/ampproject/amp-wp/issues/6115>.
// The AMP version of the skip link is implemented by AMP_Accessibility_Sanitizer::add_skip_link().
remove_action( 'wp_footer', 'gutenberg_the_skip_link' );
remove_action( 'wp_footer', 'the_block_template_skip_link' );

Expand Down
93 changes: 93 additions & 0 deletions includes/sanitizers/class-amp-accessibility-sanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
* @package AmpProject\AmpWP
*/

use AmpProject\Dom\Element;
use AmpProject\Html\Attribute;
use AmpProject\Html\Role;
use AmpProject\Html\Tag;

/**
* Sanitizes attributes required for AMP accessibility requirements.
Expand All @@ -21,6 +23,7 @@ class AMP_Accessibility_Sanitizer extends AMP_Base_Sanitizer {
*/
public function sanitize() {
$this->add_role_and_tabindex_to_on_tap_actors();
$this->add_skip_link();
}

/**
Expand Down Expand Up @@ -66,4 +69,94 @@ static function ( $predicate ) {
}
}
}

/**
* Add skip link markup and style.
*
* This is the implementation of the non-AMP logic in `the_block_template_skip_link()` which is unhooked in
* `AMP_Theme_Support::add_hooks()` to prevent validation errors from being raised.
*
* @see AMP_Theme_Support::add_hooks()
* @see the_block_template_skip_link()
* @return void
*/
public function add_skip_link() {

// Early exit if not a block theme.
if ( ! current_theme_supports( 'block-templates' ) ) {
return;
}

// Early exit if not a block template.
global $_wp_current_template_content;
if ( ! $_wp_current_template_content ) {
return;
}

$main_tag = $this->dom->getElementsByTagName( Tag::MAIN )->item( 0 );
if ( ! $main_tag instanceof Element ) {
return;
}

$skip_link_target = $this->dom->getElementId( $main_tag, 'wp--skip-link--target' );

// Style for skip link.
$style_content = '
.skip-link.screen-reader-text {
border: 0;
clip: rect(1px,1px,1px,1px);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute !important;
width: 1px;
word-wrap: normal !important;
}
.skip-link.screen-reader-text:focus {
background-color: #eee;
clip: auto !important;
clip-path: none;
color: #444;
display: block;
font-size: 1em;
height: auto;
left: 5px;
line-height: normal;
padding: 15px 23px 14px;
text-decoration: none;
top: 5px;
width: auto;
z-index: 100000;
}
';

$style_node = AMP_DOM_Utils::create_node(
$this->dom,
Tag::STYLE,
[
Attribute::ID => 'amp-skip-link-styles',
]
);

$style_node->appendChild( $this->dom->createTextNode( $style_content ) );

// Skip link node.
$skip_link = AMP_DOM_Utils::create_node(
$this->dom,
Tag::A,
[
Attribute::CLASS_ => 'skip-link screen-reader-text',
Attribute::HREF => "#$skip_link_target",
]
);

$skip_link->appendChild( $this->dom->createTextNode( __( 'Skip to content', 'amp' ) ) );

$body = $this->dom->body;
$body->insertBefore( $skip_link, $body->firstChild );
$body->insertBefore( $style_node, $body->firstChild );
}
}
150 changes: 150 additions & 0 deletions tests/php/test-class-amp-accessibility-sanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,47 @@

/**
* Tests the accessibility sanitizer class.
*
* @coversDefaultClass AMP_Accessibility_Sanitizer
*/
class AMP_Accessibility_Sanitizer_Test extends TestCase {

use MarkupComparison;

/** @var string */
private $original_wp_current_template_content;

/** @var array */
private $original_wp_theme_features;

/**
* Setup.
*
* @inheritDoc
*/
public function setUp() {

parent::setUp();

global $_wp_current_template_content, $_wp_theme_features;
$this->original_wp_current_template_content = $_wp_current_template_content;
$this->original_wp_theme_features = $_wp_theme_features;
}

/**
* Tear down.
*
* @inheritDoc
*/
public function tearDown() {

parent::tearDown();

global $_wp_current_template_content, $_wp_theme_features;
$_wp_current_template_content = $this->original_wp_current_template_content;
$_wp_theme_features = $this->original_wp_theme_features;
}

public function get_sanitize_test_data() {
return [
'valid markup remains unchanged' => [
Expand Down Expand Up @@ -68,4 +104,118 @@ public function test_sanitize( $source, $expected ) {

$this->assertEqualMarkup( $expected, $actual );
}

/**
* Data provider for $this->test_add_skip_link()
*
* @return string[][]
*/
public function get_skip_link_test_data() {

$style_element = '<style id="amp-skip-link-styles">
.skip-link.screen-reader-text {
border: 0;
clip: rect(1px,1px,1px,1px);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute !important;
width: 1px;
word-wrap: normal !important;
}
.skip-link.screen-reader-text:focus {
background-color: #eee;
clip: auto !important;
clip-path: none;
color: #444;
display: block;
font-size: 1em;
height: auto;
left: 5px;
line-height: normal;
padding: 15px 23px 14px;
text-decoration: none;
top: 5px;
width: auto;
z-index: 100000;
}
</style>';

return [
'with_id' => [
'source' => '<html><body><main id="main-container">Hello World!</main></body></html>',
'expected' => $style_element . '<a class="skip-link screen-reader-text" href="#main-container">Skip to content</a><main id="main-container">Hello World!</main>',
],
'without_id' => [
'source' => '<html><body><main>Hello World!</main></body></html>',
'expected' => $style_element . '<a class="skip-link screen-reader-text" href="#wp--skip-link--target-0">Skip to content</a><main id="wp--skip-link--target-0">Hello World!</main>',
],
'without_main_element' => [
'source' => '<html><body><div id="main-container">Hello World!</div></body></html>',
'expected' => '<div id="main-container">Hello World!</div>',
],
];

}

/**
* @dataProvider get_skip_link_test_data
*
* @covers ::add_skip_link()
*
* @param string $source Source HTML.
* @param string $expected Expected target HTML.
*/
public function test_add_skip_link( $source, $expected ) {

global $_wp_current_template_content, $_wp_theme_features;

$_wp_current_template_content = true;
$_wp_theme_features['block-templates'] = true;

$dom = AMP_DOM_Utils::get_dom_from_content( $source );

$sanitizer = new AMP_Accessibility_Sanitizer( $dom );
$sanitizer->add_skip_link();

$actual = AMP_DOM_Utils::get_content_from_dom( $dom );

$this->assertEqualMarkup( $expected, $actual );
}

/**
* @covers ::add_skip_link()
*/
public function test_add_skip_link_for_none_fse_theme() {

global $_wp_current_template_content, $_wp_theme_features;

$source = '<html><body><main id="main-container">Hello World!</main></body></html>';
$expected = '<main id="main-container">Hello World!</main>';

// Test 1: If it's not block theme.
unset( $_wp_theme_features['block-templates'] );

$dom = AMP_DOM_Utils::get_dom_from_content( $source );
$sanitizer = new AMP_Accessibility_Sanitizer( $dom );
$sanitizer->add_skip_link();
$actual = AMP_DOM_Utils::get_content_from_dom( $dom );

$this->assertEqualMarkup( $expected, $actual );

// Test 2: If it's not block template.
$_wp_theme_features['block-templates'] = true;
$_wp_current_template_content = false;

$dom = AMP_DOM_Utils::get_dom_from_content( $source );
$sanitizer = new AMP_Accessibility_Sanitizer( $dom );
$sanitizer->add_skip_link();
$actual = AMP_DOM_Utils::get_content_from_dom( $dom );

$this->assertEqualMarkup( $expected, $actual );

}
}

0 comments on commit b311b57

Please sign in to comment.