This issue requests the ability to print option labels either before or after form-item DIVs for the grid component. This is useful for responsive design where a site builder may use CSS to change the display of the table into stacked blocks for mobile viewports.

Currently, each radio input is labeled with the concatenation of the question label and the option label - for example, 'Question 1 - Option 3'. This behavior was modified in #1982056: The radio button labels in grid elements are not descriptive enough for the purpose of accessibility. Reprinting the the question label in each option label is useful for the purposes of accessibility but is undesirable for display on screen as it's repetitive.

Proposed Solution

This issue proposes a solution where two form fields are added to the grid component form:

  • Option Description (option_descr)
  • Type: Checkbox
    • Description: Print the description for each option. This option is useful for responsive design.
  • Option Description Placement (option_descr_plcmt)
    • Type: Select
    • Description: Place this option description before or after the form input.
    • Options
      • Before (key: 'prefix')
      • After (key: 'suffix')
    • States
      • Invisible if 'option_descr' is not checked

function: _webform_edit_grid

When 'option_descr' is enabled, then a description DIV is printed either before or after the form-item DIV based on the selection in the 'option_descr_plcmt' field.

function: theme_webform_grid

Comments

chris burge’s picture

Table
table

Styled as Block with Existing Label Element
concatenated option labels

Styled as Block with Proposed Solution
actual option lables

chris burge’s picture

Status: Active » Needs review
StatusFileSize
new2.86 KB

Patch attached.

danchadwick’s picture

Can't this be fairly easily accomplished with a theme override?

chris burge’s picture

I could override the theme_webform_grid function. That would be easy; however, the purpose of this issue is to provide a solution that is more accessible to site builders, especially beginners.

Right now we're looking at a situation where everyone who wants to use the grid component for a new [responsive] website must use a theme override. Currently, a site builder can't use Webform out-of-the-box to implement the grid component in responsive design. The vast majority of new websites are responsive, so I think it's worth solving with a solution that can be implemented out-of-the-box.

The existing markup that is generated simply won't work, so I considered four options:

1. Override theme function (i.e. do nothing)
Pros: Simple
Cons: Doesn't help the community; future changes to theme function may break override function

2. Revert #1982056: The radio button labels in grid elements are not descriptive enough
Pros: Simple
Cons: Diminishes accessibility

3. Print option description and hide by adding 'element-hidden' class
Pros: Preserves accessibility; no additional settings
Cons: Additional markup may not be universally necessary; site builder unable to decide placement before or after input

4. Provide option description configuration
Pros: Preserves accessibility; provides option for but does not mandate additional markup necessary for responsive design
Cons: More settings

danchadwick’s picture

Status: Needs review » Closed (works as designed)

I'm not feeling the warm fuzzies about converting a webform grid component into a series of vertical radio buttons in the name of mobile happiness. I would certainly listen to a groundswell of support from the user base, or contrary opinions from the other maintainers.

Let's close this for now, but remain receptive to additional input.

chris burge’s picture

I'm not feeling the warm fuzzies about converting a webform grid component into a series of vertical radio buttons in the name of mobile happiness.

Reformatting tables into stacked blocks on mobile is already commonplace, especially among survey tools:

Matrix questions now slimlined for smartphones
https://www.demographix.com/research-blog/2013/Matrix-questions-now-slim...

Building Mobile-Friendly Surveys
http://help.surveygizmo.com/help/article/link/mobile-friendly-surveys
(See 'Grid Questions' section)

Responsive Tables in Pure CSS
https://techblog.livingsocial.com/blog/2015/04/06/responsive-tables-in-p...

Responsive Data Tables
https://css-tricks.com/responsive-data-tables/

For anyone who wants to use the grid component on a responsive website, you'll need to override the 'theme_webform_grid' function to modify the generated markup. See the 'Overriding theme functions' section on this doc page: https://www.drupal.org/node/457740.

Note that a single line of markup is added to this function to print the option description after the input:

     // Render each radio button in the row.
     $radios = form_process_radios($question_element);
     foreach (element_children($radios) as $key) {
+      $radios[$key]['#suffix'] = '<div class="option-description">' . $radios[$key]['#title'] . '</div>';
       $radios[$key]['#title'] = $question_element['#title'] . ' - ' . $radios[$key]['#title'];
       $radios[$key]['#title_display'] = 'invisible';

The 'option-description' DIVs are hidden on desktop and tablet using CSS. On mobile, the table is converted into stacked blocks using CSS. The header cells are hidden and the 'option-description' DIVs are shown. See this example provided by livingsocial.com: http://codepen.io/anon/pen/QwPVNW

chris burge’s picture

Title: Allow Option Description for Grid Component » Add Custom Label Attribute to Grid Component Option TD Elements
Status: Closed (works as designed) » Active

Let's change direction. I see the solution from the original patch at a dead end. The solution from the livingsocial.com blog post is better to begin with, especially in terms of semantic HTML.

The solution would be to add a custom attribute to each cell (e.g. data-label) with the value being the option description. It would eliminate the need for any additional settings (so it doesn't further clutter the UI), and it minimize changes to the rendered markup (no extra element). The description can then be printed using CSS pseudo elements.

Suggested Change to the 'theme_webform_grid' Function

    // Render each radio button in the row.
    $radios = form_process_radios($question_element);
    foreach (element_children($radios) as $key) {
+     $radio_title = $radios[$key]['#title'];
+     $radios[$key]['#title'] = $question_element['#title'] . ' - ' . $radio_title;
-     $radios[$key]['#title'] = $question_element['#title'] . ' - ' . $radios[$key]['#title'];
      $radios[$key]['#title_display'] = 'invisible';
+     $row[] = array('data' => drupal_render($radios[$key]), 'class' => array('checkbox', 'webform-grid-option'), 'data-label' => array($radio_title));
-     $row[] = array('data' => drupal_render($radios[$key]), 'class' => array('checkbox', 'webform-grid-option'));
    }

(I should mention that the Picture module makes use of custom attributes.)

Rendered HTML:

<tr class="even">
  <td class="webform-grid-question">Marla Singer</td>
+ <td class="checkbox webform-grid-option" data-label="1 - Excellent">
- <td class="checkbox webform-grid-option">
    <div class="form-item form-type-radio form-item-submitted-grid-singer">
      <label class="element-invisible" for="edit-submitted-grid-singer-1">Marla Singer - 1 - Excellent </label>
      <input type="radio" id="edit-submitted-grid-singer-1" name="submitted[grid][singer]" value="1" class="form-radio">
    </div>
chris burge’s picture

Status: Active » Needs review
StatusFileSize
new1.06 KB

New patch attached.

danchadwick’s picture

Status: Needs review » Needs work

I like this. The select component should have this too, at least for radio and checkboxes, no? EDIT: Wait, they have the text in the radio's label.

Is the inconsistent markup an issue?

chris burge’s picture

That's a good point. I looked at WCAG best practices, and I think the issue is broader. WCAG recommends grouping controls in the case of radios and checkboxes (http://www.w3.org/WAI/tutorials/forms/grouping/) using FIELDSET and LEGEND elements. This forced me to rethink the issue. Neither the Select component nor the Grid component make use of such groupings at present.

Select component

Below is some example markup from a rendered Select component:

<div class="form-item webform-component webform-component-radios webform-component--select---radio">
  <label for="edit-submitted-days-of-the-week">Days of the Week </label>
  <div id="edit-submitted-days-of-the-week" class="form-radios">
    <div class="form-item form-type-radio form-item-submitted-select---radio">
      <input type="radio" id="edit-submitted-days-of-the-week-1" name="submitted[select___radio]" value="sunday" class="form-radio">
      <label class="option" for="edit-submitted-days-of-the-week-1">Sunday </label>
    </div>
    <div class="form-item form-type-radio form-item-submitted-select---radio">
      <input type="radio" id="edit-submitted-days-of-the-week-2" name="submitted[select___radio]" value="monday" class="form-radio">
      <label class="option" for="edit-submitted-days-of-the-week-2">Monday </label>
    </div>

The 'edit-submitted-days-of-the-week' DIV is labeled with a LABEL element - "Days of the Week". Inside this DIV are the various options. Each INPUT element is labeled with a LABEL element. For example, the 'edit-submitted-days-of-the-week-1' input is labeled as 'Sunday'. The only change that would be needed here is to replace the 'form-item' DIV with a FIELDSET and its LABEL with a LEGEND.

Grid component

The Grid component is basically a collection of Select components that share the same options and that are organized into a table for the purpose of presentation. Each Select component is rendered as a table row.

Below is some sample markup from a rendered Grid component:

<div class="form-item webform-component webform-component-grid webform-component--grid">
  <label for="edit-submitted-grid">Grid </label>
  <table class="webform-grid webform-grid-6 sticky-enabled tableheader-processed sticky-table">
    <thead>
    <tr>
      <th class="webform-grid-question"></th>
      <th class="checkbox webform-grid-option">1 - Excellent</th>
      <th class="checkbox webform-grid-option">2</th>
      <th class="checkbox webform-grid-option">3 - Average</th>
      <th class="checkbox webform-grid-option">4</th>
      <th class="checkbox webform-grid-option">5 - Poor</th>
      <th class="checkbox webform-grid-option">N/A</th>
    </tr>
  </thead>
  <tbody>
  <tr class="odd">
    <td class="webform-grid-question">Tyler Duren</td>
    <td class="checkbox webform-grid-option" data-label="1 - Excellent">
      <div class="form-item form-type-radio form-item-submitted-grid-durden">
        <label class="element-invisible" for="edit-submitted-grid-durden-1">Tyler Duren - 1 - Excellent </label>
        <input type="radio" id="edit-submitted-grid-durden-1" name="submitted[grid][durden]" value="1" class="form-radio">
      </div>
    </td>
    <td class="checkbox webform-grid-option" data-label="2">
      <div class="form-item form-type-radio form-item-submitted-grid-durden">
        <label class="element-invisible" for="edit-submitted-grid-durden-2">Tyler Duren - 2 </label>
        <input type="radio" id="edit-submitted-grid-durden-2" name="submitted[grid][durden]" value="2" class="form-radio">
      </div>
    </td>

The changes needed for the Grid component are much more substantial. The TABLE, THEAD, and TBODY elements would be replaced with DIV elements. Each TR would be replaced with a FIELDSET. The first TD in each TR would be replaced with a LEGEND. Each subsequent TD would be merged into its child DIV. All would be styled with CSS to appear as it appears now. This would meet both presentation and accessibility objectives without the need to prepend the option label with the question label.

Better to Rework Markup in Webform 7.x-5.x?

These are substantive changes, and I'm guessing it would be better to make them during a major release (e.g. 7.x-5.x).

If so, then where does that leave us for Webform 7.x-4.x?

In the case of the Select component, the option labels are already being printed and displayed. The option labels are not being modified. In the case of the Grid component, the option labels are printed but hidden. The option labels are prepended with the question label.

I think the reason the option labels were prepended with the question label is because table markup isn't semantic. The question label is printed in the first TD element in the row. All options for the question are printed in subsequent TD elements in the same row. There's nothing that relates the options to their question. The solution from #1982056: The radio button labels in grid elements are not descriptive enough was to prepend the question label to each option label. It was a quick fix that didn't address the underlying issue with the markup, but it required only a minimal change to the markup. Adding the 'data-label' attribute may also be a quick fix, but it also only requires a minor change to the markup.

In the short-term, I think the best solution for the Grid component is to add the 'data-label' attribute. I don't think the Select component needs modified because its option labels are already printed and are unmodified.

liam morland’s picture

How about putting span's with a class around each of $question_element['#title'] and $radios[$key]['#title'] so that these can be hidden or adjusted with CSS if desired?

chris burge’s picture

The existing markup is sufficient for CSS styling. The problem that raised this issue is that the printed option label is a concatenation of the question label and the option label. For example, instead of labeling a radio input as 'Option B', it is labeled 'Question 2 - Option B'. What is desired is just the option label without the question label. The original solution I proposed was to add a new element to the rendered markup. The current solution I'm proposing is to add an attribute to the TD elements, which can be printed as a pseudo elements with CSS. The last post suggested that for true accessibility the rendered markup should be changed from a table to DIVs, FIELDSETs, and LEGENDSs and styled with CSS to display as a table. This may need to wait until Webform 7.x-5.x.

I'm unsure what we would gain by adding SPANs.

liam morland’s picture

If the $question_element['#title'] had a span around it, that could be visually hidden and it would look like the image above of your proposed solution.

The table semantics do associate the options to each radio button. I think the questions should also be th elements, though that is another issue.

chris burge’s picture

@Liam Morland - Do you mean something like this?

    // Render each radio button in the row.
    $radios = form_process_radios($question_element);
    foreach (element_children($radios) as $key) {
-     $radios[$key]['#title'] = $question_element['#title'] . ' - ' . $radios[$key]['#title'];
+     $radios[$key]['#title'] = '<span class="question">' . $question_element['#title'] . ' - </span>' . $radios[$key]['#title'];
      $radios[$key]['#title_display'] = 'invisible';
      $row[] = array('data' => drupal_render($radios[$key]), 'class' => array('checkbox', 'webform-grid-option'));
    }

Rendered HTML:

<tr class="even">
  <td class="webform-grid-question">Marla Singer</td>
  <td class="checkbox webform-grid-option">
    <div class="form-item form-type-radio form-item-submitted-grid-singer">
-     <label class="element-invisible" for="edit-submitted-grid-singer-1">Marla Singer - 1 - Excellent </label>
+     <label class="element-invisible" for="edit-submitted-grid-singer-1"><span class="question">Marla Singer - </span>1 - Excellent </label>
      <input type="radio" id="edit-submitted-grid-singer-1" name="submitted[grid][singer]" value="1" class="form-radio">
    </div>
liam morland’s picture

@Chris Burge: Yes, I think that would do it. Just add CSS to give .question the same rules as .element-invisible.

danchadwick’s picture

I wonder if the ' - ' should be added via CSS with a :after pseudo element.

I also wonder if we are going to break any themes that might be searching for $question . ' - ' and now won't find it because of the spans and/or pseudo element (two different questions).

chris burge’s picture

Either solution - 1) adding the 'data-label' attribute or 2) adding a span/pseudo element - will resolve the original issue for 7.x-4.x. There aren't any risks with option 1. With option 2, there may be a risk of affecting existing themes. Given #16, my preference is for option 1.

Do we agree that the markup should be looked at for the next major release (separately from this issue)?

danchadwick’s picture

Chris -- Please create a Task / Postponed issue for D8 to look at the markup for grids and reference this issue. The D8 branch maintainer, fenstrat, is busy getting getting the existing functionality ported and will look at issues such as this before the first stable release.

I have a slight preference for 'data-label' solution unless Liam has a reason to the contrary. The patch for that is in #8.

liam morland’s picture

I am OK with adding @data-label.

danchadwick’s picture

Status: Needs work » Fixed

Chris -- Thanks for the patch. It's easier if they are named according to the Drupal standard:
https://www.drupal.org/patch/submit

Committed #9 to 7.x-4.x and 8.x.

Thanks guys work working through this with me. I think we have a good solution.

Status: Fixed » Closed (fixed)

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