Problem/Motivation

The pager by default uses an h4, this creates the `Heading levels should only increase by one` error in many use cases.

Steps to reproduce

Create a view with a pager that has no other content on the page.
Run axe devtools

Proposed resolution

Add ability to set the heading level per view pager.
Options should be h1 - h6

Sidenote: the initial approach discussed on this issue of moving the heading text into an aria-label attribute on the parent tag, and thereby removing the Heading element altogether will indeed help pass many accessibility tests, but is detrimental to actual users. See comment #30 from @andrewmacpherson for full details.

Remaining tasks

User interface changes

Pager Heading Level Setting

API changes

N/A

Data model changes

Add pagination_heading_level to views pager schema
Add pagination_heading_level variable to views mini pager and full pager

Release notes snippet

  • Add new pager heading level setting for views pagers.

  • Add new pager heading level variable for full pagers and views mini pagers.

  • Update all core themes except stable9 to utilize the new variable.

Issue fork drupal-3333401

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

nicxvan created an issue. See original summary.

nicxvan’s picture

The way I generally address this based on feedback from @chrisfromredfin is to remove the h4 and add the pagination to the aria label.

So this:

<nav class="pager" role="navigation" aria-labelledby="{{ heading_id }}">
<h4 id="{{ heading_id }}" class="visually-hidden">{{ 'Pagination'|t }}</h4>

Becomes this:

<nav class="pager" role="navigation" aria-labelledby="{{ heading_id }} {{ 'Pagination'|t }}">

If that's correct I am happy to make a merge request updating the core themes.

nicxvan’s picture

bnjmnm’s picture

aria-labelledby is used for referencing an element that has the desired label text. In this case, we want to get rid of that <h4>, so the solution in #2 would be referencing a nonexistent element.

Change that to aria-label, don't bother using heading_id at all (you're not using heading anymore), and you should be fine.. If the label is for assisitive tech only, it doesn't need to be contained in another element, you can define it directly with aria-label

Be sure to make the change to all views pager templates except those in stable9. Even though it is a visually hidden element, changing it is technically altering markup that would break the BC policy, though it could be argued (ideally in a followup so you can get the benefits ASAP) that it's an accessibility bugfix and should be applied to Stable9.

nicxvan’s picture

Status: Active » Needs review
StatusFileSize
new7.04 KB

Adding patch so testing will work due to current replication issues:

nicxvan’s picture

bnjmnm’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs accessibility review

To assistive tech, this solution works identically so it's not at all disruptive. It accomplishes the same identification, but without the header-order-contaminating <h4>

The aria-labelledby was referencing a visually hidden element, so switching to the simpler aria-label takes care of this without any tradeoffs. If the <h4> was visible, it would potentially be worth another approach, but in this case it's not needed.

This is not changed in Stable9, so any debates regarding regression vs. bugfix can happen in the Stable9 scoped followup issue #3333418: Fix pager h4 for accessibility on Stable9. The easy wins can happen here, and that issue can deal with the remaining ones that might require discussion.

This arguably makes the heading_id property unnecessary in pager templates, so I opened a followup to address that, which can be tackled once this issue lands: #3333449: [PP-1] Consider deprecating heading_id property from pagination templates

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 6: 3333401-6-h4-accessibility-pager.patch, failed testing. View results

nicxvan’s picture

Status: Needs work » Reviewed & tested by the community

Not sure why the retest failed, they were unrelated datetime failures. I retested and they all passed so I'm moving back to RTBC.

  • lauriii committed 01de0a84 on 10.1.x
    Issue #3333401 by nicxvan, bnjmnm: Pager h4 causes accessibility flag on...

  • lauriii committed 00b74b0c on 10.0.x
    Issue #3333401 by nicxvan, bnjmnm: Pager h4 causes accessibility flag on...

  • lauriii committed 78cf3677 on 9.5.x
    Issue #3333401 by nicxvan, bnjmnm: Pager h4 causes accessibility flag on...
lauriii’s picture

Status: Reviewed & tested by the community » Fixed

Committed 01de0a8 and pushed to 10.1.x. Cherry-picked to 10.0.x and 9.5.x since this is a non-disruptive bug fix. Thanks!

  • xjm committed e9cde297 on 9.5.x
    Revert "Issue #3333401 by nicxvan, bnjmnm: Pager h4 causes accessibility...
xjm’s picture

Version: 10.1.x-dev » 9.5.x-dev
Status: Fixed » Needs work

I think this broke 9.5.x: https://www.drupal.org/pift-ci-job/2576989

Reverted and queuing the patch against that branch.

xjm’s picture

StatusFileSize
new6.88 KB

 

nicxvan’s picture

I think this is due to how classy was removed from 10, there was a related issue: https://www.drupal.org/project/drupal/issues/3232222 that addressed the content hash, I'm not sure how to provide a MR that fixes tests in 9.5 that would have been removed in 10.
I closed the related issue yesterday when this was merged.

Let me know how I should proceed and I'm happy to address it.

nicxvan’s picture

StatusFileSize
new8.19 KB

I think I may have gotten it:

nicxvan’s picture

StatusFileSize
new8.03 KB

I used the wrong hashing function - here is the correct one.

nicxvan’s picture

StatusFileSize
new7.47 KB

For some reason the page.css file is now triggering a failure even though nothing changed, I regenerated the hash locally.

nicxvan’s picture

StatusFileSize
new7.55 KB

Not sure why that was failed this time.

nicxvan’s picture

StatusFileSize
new6.52 KB

Sorry for the back and forth on the patches, this is the first time I've generated content hashes and one of them was different than the test was generating, I think due to my local php version, this should be right.

nicxvan’s picture

StatusFileSize
new8.29 KB
nicxvan’s picture

StatusFileSize
new11.19 KB

I missed updating two themes.

nicxvan’s picture

StatusFileSize
new11.81 KB

I had inverted the pager hash

nicxvan’s picture

Ok I got it, the confusion on my end was I didn't realize more than one theme was failing so I flipped back and forth a couple of times.

Some of the folks on https://www.drupal.org/project/drupal/issues/3232222 should get credit cause I pulled from that issue to get this right.

johnpicozzi’s picture

Status: Needs work » Reviewed & tested by the community

Looks good to me. Tested on Simlytest.me and does what is expected. Moving to RTBC!

andrewmacpherson’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Active
Issue tags: +Accessibility

I Saw this on the Drupal 9.5.3 release notes yesterday, including the reverting commit.

I think the proposal here is rather brutal. It doesn't take account of well-attested user behaviours. I don't think we should remove the heading.

@nicxvan - Thanks for reporting the issue. I appreciate the work you've done to maintain the patch here.

I'd like to take issue with the appraisal so far. Comment #8 by @bnjmnm moved this to RTBC:

To assistive tech, this solution works identically so it's not at all disruptive.

Emphasis mine. This remark is concerned with the assistive technology software, rather than the software's human users. The proposal may not be disruptive to software, but it is likely to be disruptive for users.

Previously, the pagination could be found by screen reader users in a number of ways:

  1. Peruse headings. Many screen readers provide tools for this, but no widely used web browsers do so.
  2. Using the web browser's find-in-page tool. It can match the content of the heading element. (Aside, you don't need actually need a screen reader for this.)
  3. Peruse landmark regions. Many screen readers provide tools for this, but no widely used web browsers do so.

After implementing the proposal here, two of these methods no longer work:

  1. The heading is gone, so you can't find the pager by perusing headings.
  2. Using the browser's find-in-page tool no longer works. It doesn't match the content of the aria-label attribute.
  3. The only method still available is to peruse landmark regions.

Will this be disruptive for actual users? Let's look at some user data.

The WebAIM Screen Reader User Survey #9 asked respondents:

When trying to find information on a lengthy web page, which of the following are you most likely to do first?

  1. Navigate by headings was by far the most popular method, with approximately two-thirds of users preferring it.
  2. Using a find-in-page tool was the next most popular, with approx 15% of users.
  3. Navigating by landmark regions was the least popular method, at under 4% of users.

The same question was asked in earlier WebAIM surveys:

  • Screen reader user surveys #8, #7, #5, and #4 showed roughly similar results to survey #9. It's been very consistent for a decade.
  • Curiously, the question was omitted from survey #6.
  • In surveys #2 and #3, headings and find-in-page were also the most popular methods. These surveys didn't offer landmark regions as an answer.

But the proposal here puts the kibosh on the two most popular methods, and doubles down on the least popular method!

Concerning the report from the aXe tests:

The pager by default uses an h4, this creates the "Heading levels should only increase by one" error in many use cases.

  • Deque's documentation for the Heading levels should only increase by one test describes it as a "Deque best practice" of "moderate" impact.
  • Deque's commentary describes why a sensible heading outline is useful, and gives recommendations for writing effective heading text.
  • What the commentary doesn't address is: If a heading level is skipped, will any bad things happen? It completely fails to say how the skipped level presents a problem.
  • The commentary doesn't encourage the removal of headings as a way to address the skipped level. On the contrary, the article goes to a lot of effort to advocate for the use of headings.

It's also worth noting that the skipped heading level IS NOT a failure of any WCAG success criteria. For a detailed explanation, see David Swallow's article: Heading off confusion: When do headings fail WCAG?

So, what's the impact (for users) of skipping a heading level? It's actually not as bad as the aXe rule title implies.

Screen readers are quite forgiving of missing heading levels. The missing heading level isn't a complete disaster.

Screen reader users can interact with headings in various ways:

  1. All the screen readers I've ever used provide tools to move to the next/previous headings regardless of the heading level. Indeed, when using a touchscreen on a mobile OS, this is typically the only mechanism for moving amongst headings. The skipped heading level has no impact here whatsoever; the only thing that matters is the order of headings.
  2. Many screen readers also provide commands to move among headings of a particular level, when using a keyboard. Most desktop screen readers have this feature, as do iOS VoiceOver and Android Talkback when a Bluetooth keyboard is connected. In practice this is very useful for jumping to the major sections of the page, while ignoring the intervening lower levels. It isn't quite as much use for navigating among the lower nested levels of headings; sometimes you ask for the next H4, only to be told that there isn't one.
  3. A few screen readers (e.g. NVDA) provide a hierarchical tool to navigate headings as a tree. The NVDA tool is forgiving of skipped levels. It will show a H2 and H4, and still let you access the H4. AFAIK, no screen reader for a mobile OS provides a hierarchical heading browser, so the skipped heading level has no practical impact.

I'd like to stress that accessibility is about helping actual users, not satisfying opinionated best-practice rules in automated tests. It certainly isn't about keeping developer's dashboards green. Has any screen reader user asked for the heading to be removed?

Should we continue to provide a heading for the pagination? Yes. It facilitates the two most popular methods of finding information, as reported by the WebAim screen reader user survey.

Should we continue to use a landmark region? Yes. A moderate number of landmarks are very useful for finding the major sections of the page, and/or small-but-important tools on the page. Pagination for the main content is a good candidate for a landmark region.

Whilst landmark regions aren't the first tool that all users reach for, a simple landmark structure is a great accompaniment to a detailed heading outline. The belt-and-braces approach of using landmarks and headings offers choice for users.

For these reasons, I'd say no to the current proposal to remove the heading.

andrewmacpherson’s picture

Alternative proposal: keep the heading. Can we improve the heading level?

We could deal with the missing level by promoting it to h3, and aXe would stop complaining.

The devil is in the details though...

Headings assume ownership over ALL of the content which follows, until another heading of the same level (or higher) is encountered.

For example, consider the admin/content page, with the default configuration from Standard profile.

Currently it looks like the pagination (h4) is a sub-heading under the breadcrumb navigation (the preceding h2). This is clearly nonsense, and promoting the pagination heading to h3 wouldn't improve that at all. It would still be a misleading heading structure, even though we'd eliminated the missing level which aXe complained about.

We could promote the pagination to a h2 level. Then it would be a subheading of the main page heading (h1), and a sibling of the breadcrumb and primary tabs. Much better.

Yet, a h2 assumes that the pagination relates to the main content of the page. For a views block in a sidebar region, the h2 would be too important; the views block would have it's own h2. Or, the views block might have no heading at all. Either way, giving the pagination a h2 in a views block would produce a misleading outline, suggesting it related to the main content.

Managing heading levels with a template-driven publishing system is a pain-in-the-architecture. There is no single correct level for every use of the template.

A more ambitious approach would be to compute heading levels, for the context of the assembled page. I'm not aware of prior PHP work which does this, but there's an interesting article from Heydon Pickering about how to do it in a React Javascript application: Managing Heading Levels In Design Systems. I expect there are still devils lurking in the details there too. Heydon's use of aria-level could complicate our existing CSS, but a server-side PHP approach might avoid using aria-level if we computed the heading levels late in the page render process.

  • lauriii committed 7d78af80 on 10.0.x
    Revert "Issue #3333401 by nicxvan, bnjmnm: Pager h4 causes accessibility...

  • lauriii committed 197d8bae on 10.1.x
    Revert "Issue #3333401 by nicxvan, bnjmnm: Pager h4 causes accessibility...
lauriii’s picture

Thank you @andrewmacpherson for your comprehensive and insightful response. Your thoughts and experiences are greatly appreciated, and your point about the potential regressions with some assistive technology users seems to warrant a revert.

I completely agree with you that real users should always be our top priority, and their needs and experiences should always be at the forefront of our considerations. Automated tools and tests are certainly helpful, but they are ultimately secondary to the needs of the people who are using the system.

itmaybejj’s picture

If we were to revisit this and bring back the heading, my instinct would be to go via JS post-load -- querySelectorAll for H1, H2, H3, H4 and their aria equivalents. Then iterate the array and each time you hit a pagination heading, modify the pagination header's level to be one less than the previous level in the array.

That's what I do in Editoria11y -- I walk the tree comparing levels and flag things as needed.

And then...add a field to Views for "Pagination heading level." Let people manually pick a level if they know more than the machine, and have the default be auto, which pulls the JS library. To avoid breaking CSS changes to existing sites, the upgrade script could set existing views to "4" and only new views/installs would use the "auto..."

andrewmacpherson’s picture

Re. #34 - Aha... thanks lauriii. I saw in the D9.5.3 release notes it was reverted, but I didn't realize it was still there in the D10 branches.

Re. #35 - We still have the heading, barring a brief blip in D10.0.3

That JS logic could be useful to study. I think server-side logic to write the correct level in HTML would be preferable to JS though.

nicxvan’s picture

Thank you @andrewmacpherson for the detailed write up, I'll have to read through those survey's more closely.

I completely agree with you and @laurii that real users are the priority.

I think it is helpful prevent false negatives on almost every page of a real site if possible because it removes white noise when addressing accessibility issues. So if there is a way to address the heading issue without affecting usability then we should do that.

After reading your write up, there doesn't seem to really be a way to remove the heading without significantly affecting usability negatively which means we need to see if there is a way to set it in a way that responds to content as mentioned.

I do have a couple of questions / comments about the alternative solutions mentioned:
I'm not sure how this could be addressed at the template level since it's so contextual to what content the page and the pager could be split out from the content of the view. It could also be affected by rewriting fields.

That points closer to using javascript, but I'm not sure the javascript solution is much better as it's just the inverse of the false negative from the automated test. Also I'd be wary of adding js just to prevent an accessibility flag in axe tools.

It sounds like the correct solution is to do nothing in core and if we need to change heading levels on an individual case we just use separate templates for pagers as needed.

If there is something to be done still I'd be happy to take a crack at it.

andrewmacpherson’s picture

I don't really mind the white noise, or the Drupal core issues that result from it. It's nice to know people run automated tests.

aXe has some ways to turn off certain tests; the browser plugin has a button to disable the "best practice" category, and if you run it from the CLI you can exclude specific tests I think. What you can't do is flag a specific test result as a false flag.

Mind you, there are some accessibility testing dashboards out there which let you mark individual reports as resolved/wontfix, and they don't bug you about it later. SiteImprove was one, and I think perhaps Deque or TPGi offerings might have this too. They're commercial, and SiteImprove had too many zeros on the end of the price for my liking, but this is possible.

The thing is, the aXe report here ISN'T a false report. It really would be better if we didn't skip a heading level. It's just that the impact isn't actually very severe in practice, and the proposed cure was worse than the disease.

Also I'd be wary of adding js just to prevent an accessibility flag in axe tools.

Yeah, I think that would be folly, too. It would also make us subject to ridicule and suspicion in the wider accessibility community.

Aside: there have been some disturbing reports alleging that an accessibility overlay company interfered with the WAVE accessibility checker, and WAVE's developers noticed this. Some nice bedtime reading at #accessiBe Spoofs Automated Checkers.

There is absolutely no way we can allow JS in Drupal core just to silence a test suite.

nicxvan’s picture

Ah, great points!

After re-reading #35, maybe the solution is a new option to set the heading in the pager options in views. I missed that suggestion, so it would default to h4, but you could set the heading level like you do in fields.

nicxvan’s picture

I'm going to make an attempt at making the pager level configurable at the individual view level.

I'll make sure this tests both 9.5 and 10 so we don't have the same test failures.

nicxvan’s picture

nicxvan’s picture

Issue summary: View changes

I created a new branch since the previous one was merged and doesn't really match this approach at all.
I took a first pass at adding the option to the pager.
I added a new settings value for views so that we can restrict the tags to headings.
This only adds and saves the value in the pager form, it does not do anything yet to the templates, I had some questions.

I'd welcome feedback on naming / process so far.
Things I am not sure the best way to approach:
How do I get that pager option in the views.theme.inc when the pager is instantiated?
I assume I have to override it in theme.inc as well.
Where would the update hook to create that config go? views.install?

This only attempts to provide the fix in 10.x since I will have to regenerate the classy hashes again and those don't exist on this branch.

nicxvan’s picture

Version: 9.5.x-dev » 10.1.x-dev
itmaybejj’s picture

Simple, elegant; I like the approach.

Dodging the update script question...

  • I'd go for an on-the-nose name for the machine name for the variable. E.g.: "pagination-heading-level" and "heading level." And probably best to go with heading rather than header, unless there is local precedent. I tend to get these confused myself...
  • For the description; perhaps make it clear what people should do? E.g.: "Choose a heading level equal to or one lower than the section heading for your view."
  • Miiiight also make sense to change the default level for *new* views to H3. That is more likely be correct out of the box, and avoids breaking themes. The update script could apply H4 to all existing views.
  • Another approach that would dodge breaking existing sites would be leaving the H4 in code, and just modifying an attribute to override its level in the page outline: (aria-level="pagination-heading-level")
  • You can leave the H1 off the option list in my opinion. A pager is going to be descendant of something, so it shouldn't ever need to be an H1.
xurizaemon’s picture

Choose a heading level equal to or one lower than the section heading for your view.

This text made me realise that the functionality presumes the site admin can predict the context that the view will be rendered in. In some cases (eg a <drupal-view> embedded in content) this won't be the case. I'm not pitching the dynamic approach here, just that assigning a heading level at view config makes assumptions about the content structure the view will be used in later which can't be relied on. For the case of an embed, there are at least two cases:

  • The view appears several times (perhaps with different parameters) in a document which contains headings at several levels
  • The view appears in a content section which gets edited over time, so headings may change

Since views are configuration and commonly re-used, I expect there are other situations where the same might apply.

Instead of fixing to headings here, should we permit views to use headings or <nav>, allowing site admins to determine an appropriate method? That seems like it would fit within the changes to permit tag selection, if it's an acceptable change for accessibility requirements.

For the proposed description there's also the case where views have section headings disabled. If we stick with headings, I propose a less specific text for that description, eg "Choose a heading level appropriate to the context of the view placement". (It's not great, but it doesn't specify too much?)

xurizaemon’s picture

@nicxvan, I don't see the new branch mentioned in #42 here - did that get missed? The only MR branch showing on the issue (for me) is the previously merged one.

Taking the opportunity in this comment to accept your offer in #3333401-28: Pager h4 causes accessibility flag on many pages for credit from #3232222: Pager template h4 can be out of document hierarchy order previously. (Hmm ... that checkbox isn't "sticking" for me when I select it.)

nicxvan’s picture

@itmaybejj good points on terminology, I'll update that.

@xurizaemon The branch is at the top, I didn't create an MR yet since I need advice. But here is the branch: https://git.drupalcode.org/issue/drupal-3333401/-/tree/3333401-configura... and the Diff https://git.drupalcode.org/issue/drupal-3333401/-/compare/10.1.x...33334...

It's right below where it calls the fork.

nicxvan’s picture

I updated the pull request with most of the feedback.

I think updating the actual tag is better than just a aria heading.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mgifford’s picture

tsquared212’s picture

StatusFileSize
new11.56 KB

The attached patch expands on the work done in the issue fork - injecting the 'pagination_heading_level' into both full and mini pagers, as well as updating all core theme pager templates. For the life of me, I could not access 'pager_rewrite_elements' from the view config, so instead I added a method to filter out the heading elements from 'field_rewrite_elements'.

tsquared212’s picture

tsquared212’s picture

StatusFileSize
new11.56 KB

Blank line was missing necessary whitespace.

tsquared212’s picture

I think I may have a crlf issue

tsquared212’s picture

StatusFileSize
new11.33 KB
tsquared212’s picture

StatusFileSize
new11.33 KB
tsquared212’s picture

StatusFileSize
new12.08 KB
tsquared212’s picture

StatusFileSize
new12.08 KB
tsquared212’s picture

StatusFileSize
new12.13 KB
tsquared212’s picture

StatusFileSize
new12.13 KB
tsquared212’s picture

StatusFileSize
new12.13 KB
tsquared212’s picture

Status: Active » Needs review
StatusFileSize
new71.13 KB

Submitted patch works, but fails a test -- Drupal\Tests\datetime\Functional\Views\FilterDateTest, due to a schema error despite numerous attempts to patch (views.pager.schema.yml).

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Bug Smash Initiative, +Needs change record

Moving to NW for test failure

Also imagine this will need test coverage.

Also tagging for CR for changes to twig files.

tsquared212’s picture

StatusFileSize
new11.39 KB

Re-rolled patch to add guards for non-view pagers.

nicxvan’s picture

I'm going to port your changes back to the branch and take a crack at the tests. I think we can hide the 9.5 changes since 9 is end of life.

nicxvan’s picture

@tsquared212 I moved your changes to the branch again since that's where initial testing was going. I made two changes.

1. I added the pager rewrite schema back since it gives more flexibility in the future rather than using the field rewrite.
2. I changed the default to h4 since that is what it was before. Let me know if there was a specific reason for that change.

For 2 I think it makes sense to change if others do too, I think the first point is important to keep though.

nicxvan’s picture

I'm getting the same error in the gitlab ci. I tried adding the new option to the pager to see if that was it, but it did not fix it.

nicxvan’s picture

Failure copied here for convenience:

Fail      Other      phpunit-51.xml       0 Drupal\Tests\datetime\Functional\Vi
    PHPUnit Test failed to complete; Error: PHPUnit 9.6.13 by Sebastian
    Bergmann and contributors.
    
    Testing Drupal\Tests\datetime\Functional\Views\FilterDateTest
    .F                                                                  2 / 2
    (100%)
    
    Time: 00:34.826, Memory: 4.00 MB
    
    There was 1 failure:
    
    1)
    Drupal\Tests\datetime\Functional\Views\FilterDateTest::testExposedFilterWithPager
    Failed asserting that actual size 0 matches expected size 2.
    
    /builds/issue/drupal-3333401/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:121
    /builds/issue/drupal-3333401/vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php:55
    /builds/issue/drupal-3333401/core/modules/datetime/tests/src/Functional/Views/FilterDateTest.php:227
    /builds/issue/drupal-3333401/vendor/phpunit/phpunit/src/Framework/TestResult.php:728
    
    FAILURES!
    Tests: 2, Assertions: 38, Failures: 1.

This is the test that is failing: core/modules/datetime/tests/src/Functional/Views/FilterDateTest.php

/**
   * Tests exposed date filters with a pager.
   */
  public function testExposedFilterWithPager() {
    // Expose the empty and not empty operators in a grouped filter.
    $this->drupalGet('admin/structure/views/nojs/handler/test_filter_datetime/default/filter/' . $this->fieldName . '_value');
    $this->submitForm([], t('Expose filter'));

    $edit = [];
    $edit['options[operator]'] = '>';

    $this->submitForm($edit, 'Apply');

    // Expose the view and set the pager to 2 items.
    $path = 'test_filter_datetime-path';
    $this->drupalGet('admin/structure/views/view/test_filter_datetime/edit');
    $this->submitForm([], 'Add Page');
    $this->drupalGet('admin/structure/views/nojs/display/test_filter_datetime/page_1/path');
    $this->submitForm(['path' => $path], 'Apply');
    $this->drupalGet('admin/structure/views/nojs/display/test_filter_datetime/default/pager_options');
    $this->submitForm(['pager_options[items_per_page]' => 2, 'pager_options[pagination_heading_level]' => 'h4'], 'Apply');
    $this->submitForm([], t('Save'));

    // Assert the page without filters.
    $this->drupalGet($path);
    $results = $this->cssSelect('.views-row');
    $this->assertCount(2, $results);
    $this->assertSession()->pageTextContains('Next');

    // Assert the page with filter in the future, one results without pager.
    $page = $this->getSession()->getPage();
    $now = \Drupal::time()->getRequestTime();
    $page->fillField($this->fieldName . '_value', DrupalDateTime::createFromTimestamp($now + 1)->format('Y-m-d H:i:s'));
    $page->pressButton('Apply');

    $results = $this->cssSelect('.views-row');
    $this->assertCount(1, $results);
    $this->assertSession()->pageTextNotContains('Next');

    // Assert the page with filter in the past, 3 results with pager.
    $page->fillField($this->fieldName . '_value', DrupalDateTime::createFromTimestamp($now - 1000000)->format('Y-m-d H:i:s'));
    $this->getSession()->getPage()->pressButton('Apply');
    $results = $this->cssSelect('.views-row');
    $this->assertCount(2, $results);
    $this->assertSession()->pageTextContains('Next');
    $page->clickLink('2');
    $results = $this->cssSelect('.views-row');
    $this->assertCount(1, $results);

  }

Any guidance would be appreciated, I'm not sure why that would not be returning 2 rows

nicxvan’s picture

It looks like the pages are not rendering, if you want to see them you can go here https://git.drupalcode.org/issue/drupal-3333401/-/jobs/290017/artifacts/... and look for the ones with the id 93301068.
https://issue.pages.drupalcode.org/-/drupal-3333401/-/jobs/290017/artifa...

nicxvan’s picture

I found the error: https://issue.pages.drupalcode.org/-/drupal-3333401/-/jobs/290017/artifa...

Drupal\Core\Config\Schema\SchemaIncompleteException: Schema errors for views.view.test_filter_datetime with the following errors: views.view.test_filter_datetime:display.default.display_options.pager.options.pagination_heading_level missing schema, 0 [display.default.display_options.pager.options.pagination_heading_level] 'pagination_heading_level' is not a supported key. in Drupal\Core\Config\Development\ConfigSchemaChecker->onConfigSave() (line 94 of core/lib/Drupal/Core/Config/Development/ConfigSchemaChecker.php).

call_user_func(Array, Object, 'config.save', Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'config.save') (Line: 229)
Drupal\Core\Config\Config->save() (Line: 278)
Drupal\Core\Config\Entity\ConfigEntityStorage->doSave('test_filter_datetime', Object) (Line: 486)
Drupal\Core\Entity\EntityStorageBase->save(Object) (Line: 257)
Drupal\Core\Config\Entity\ConfigEntityStorage->save(Object) (Line: 352)
Drupal\Core\Entity\EntityBase->save() (Line: 609)
Drupal\Core\Config\Entity\ConfigEntityBase->save() (Line: 1001)
Drupal\views_ui\ViewUI->save() (Line: 345)
Drupal\views_ui\ViewEditForm->save(Array, Object)
call_user_func_array(Array, Array) (Line: 129)
Drupal\Core\Form\FormSubmitter->executeSubmitHandlers(Array, Object) (Line: 67)
Drupal\Core\Form\FormSubmitter->doSubmitForm(Array, Object) (Line: 597)
Drupal\Core\Form\FormBuilder->processForm('view_edit_form', Array, Object) (Line: 325)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 48)
Drupal\Core\Entity\EntityFormBuilder->getForm(Object, 'edit', Array) (Line: 230)
Drupal\views_ui\Controller\ViewsUIController->edit(Object, 'default')
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 627)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 121)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 181)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 76)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 37)
Drupal\Core\Test\StackMiddleware\TestWaitTerminateMiddleware->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle(Object, 1, 1) (Line: 704)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

nicxvan’s picture

Status: Needs work » Needs review

@tsquared212 I figured out the testing issue, this is ready for review. Please use the merge request for review.

nicxvan’s picture

smustgrave’s picture

Status: Needs review » Needs work

With the schema changes will require a post_update hook in addition to a change record.

nicxvan’s picture

Assigned: Unassigned » nicxvan
nicxvan’s picture

I removed the heading options from views.settings per conversation in slack with @lendude.

I am also adding a first pass at updating the pager config.
There is no test yet and the update is not working. This seems to be due to the pager returning multiple handlers and not being plural.

One schema question:
Does views_pager_sql need the pagination_heading_level setting?

Should I split processDisplayHandlerSingular or find a way to get processDisplayHandlers to work with pagers. (I would lean towards calling it process pager handlers)

nicxvan’s picture

This still has several items todo
1. Write a test that confirms changing the header style works
2. Investigate broken tests
3. Confirm the update test is working as expected
4. Typehint
5. Determine if the post update should exclude `some` pager plugin

nicxvan’s picture

Now that the options are moved to the pager options the default config tests are failing in the kernel. I updated the core views to include the correct config but they are still failing.

Here is the failure:
1) Drupal\KernelTests\Config\DefaultConfigTest::testModuleConfig with data
set "user" ('user')
Exception: views.view.user_admin_people:
\Drupal\Component\Diff\Engine\DiffOpAdd::__set_state(array(
'type' => 'add',
'orig' => false,
'closing' =>
array (
0 => ' pagination_heading_level: h4',
),
))

nicxvan’s picture

It looks like it's getting added in core/lib/Drupal/Component/Diff/Engine/DiffOpAdd.php I haven't tracked down why though.

nicxvan’s picture

Issue summary: View changes

Ok I figured out that there were two things that were triggering the kernel failures.

The new schema shouldn't be last and there were a few items missing the config.

I've updated the MR with Kernel fixes.

nicxvan’s picture

Issue summary: View changes

I've added a test for setting the value and for checking claro, olivero and starterkit for the full pager.

nicxvan’s picture

Issue summary: View changes
nicxvan’s picture

Issue summary: View changes
nicxvan’s picture

I'm working on the change record.

nicxvan’s picture

Here is the first draft of the change record for review: https://www.drupal.org/node/3402642

I also rebased and fixed the multilingual tests again.

There was a js functional failure, but I think that was random / unrelated so I'm running it again.

nicxvan’s picture

Assigned: nicxvan » Unassigned
Status: Needs work » Needs review

All tests are passing

smustgrave’s picture

Status: Needs review » Needs work

Great work on the update hook!

There's a change to a fixture that I don't believe needs to be there.

Also since this is introducing a new UI element could a screenshot be added the User interface section.

Again great work!

nicxvan’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record

Thanks! I learned so much digging into this!

I assume you mean the UI section of the change record, I've added the screenshot. If you mean somewhere else let me know.

That fixture update is intentional and necessary, I left a comment on the MR and resolved, but I'll comment here too. I needed to add the new schema setting to the views in that fixture cause the test loads them and looks for changes to config globally and only allows the system mail change.

If any other changes happen then the test fails. The update hook for this change adds the pagination heading value so those are detected as changes and fail the test.

I extracted the fixture, added the schema to the views and re compressed it. I had to pull from the latest on 11.x since it was updated last week.

nicxvan’s picture

After taking another look there was a missing test for stark. The changes I made would work for the full pager, but I created a new assertion for stark for the mini pager.

I ran the tests locally and they passed so this should too, but I'll watch the pipeline and move to needs work if they don't pass.

nicxvan’s picture

Tests pass, there was a random failure on the js side again but rerunning it succeeded.

nicxvan’s picture

Issue summary: View changes
StatusFileSize
new88.02 KB
nicxvan’s picture

Working on this

nicxvan’s picture

I've added more complete testing for pager types in the post update test.

nicxvan’s picture

Issue summary: View changes
smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Needs Review Queue Initiative

Before applying the MR I did an export of the config to compare the views. Obviously didn't have the header config changes.

Applied the MR and the update hooks ran without issue
Did another config export and all my views have the header settings now.

Using the content view for manual testing I see the default H4
Changed it in the config to H2 and that worked like a charm.

Running test-only feature shows a few failures so think test coverage is there https://git.drupalcode.org/issue/drupal-3333401/-/jobs/376849

Believe this one is good!

Great work!

larowlan’s picture

Status: Reviewed & tested by the community » Needs work

Added issue credits

@andrewmacpherson 💙💙💙 thank you so much for your thorough explanation, including data to support it. This community never ceases to amaze me.

Left some comments on the MR, great work here folks!

nicxvan’s picture

Status: Needs work » Needs review

I addressed all of larowlan's feedback and tests are all passing so putting back into needs review.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Appears feedback from @larowlan has been addressed.

larowlan’s picture

Issue credits

Rebased this, will commit if passes

Thanks for all the changes @nicxvan

  • larowlan committed 79b54368 on 11.x
    Issue #3333401 by nicxvan, tsquared212, xjm, larowlan, smustgrave,...
larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Committed to 11.x after rebase

I added an extra unit-test case for an invalid value and waited for a green run

diff --git a/core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php b/core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php
index b0fa383d800..24a12a53b47 100644
--- a/core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php
+++ b/core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php
@@ -158,4 +158,26 @@ public function testPaginationHeadingLevelSet() {
     $this->assertEquals('h5', $variables['pagination_heading_level']);
   }
 
+  /**
+   * Test template_preprocess_pager() with an invalid #pagination_heading_level.
+   *
+   * @covers ::template_preprocess_pager
+   */
+  public function testPaginationHeadingLevelInvalid() {
+    require_once $this->root . '/core/includes/theme.inc';
+    $variables = [
+      'pager' => [
+        '#element' => '2',
+        '#pagination_heading_level' => 'not-a-heading-element',
+        '#parameters' => [],
+        '#quantity' => '2',
+        '#route_name' => '',
+        '#tags' => '',
+      ],
+    ];
+    template_preprocess_pager($variables);
+
+    $this->assertEquals('h4', $variables['pagination_heading_level']);
+  }
+

Published the change record

Because this adds an update path, it can only be added in a minor so isn't eligible for backport.

Thanks again all.

nicxvan’s picture

Issue summary: View changes
nicxvan’s picture

Issue summary: View changes
nicxvan’s picture

Issue summary: View changes
jwilson3’s picture

Issue summary: View changes

Came here looking for explanation of why an additional option was added to Drupal, instead of the initial approach discussed on this issue of moving the heading text into an aria-label attribute on the parent tag, and thereby removing the Heading element altogether.

Had to dig for the answer buried in comment #30 and learned something in the process.

I've updated the issue summary with a sidenote about this point to help others not have to dig for the gold!

Thank you @andrewmacpherson; always enlightening.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

nicxvan’s picture

Issue tags: +10.3.0 release notes

Tagging for release note inclusion.

nicxvan’s picture

_kash_’s picture

.