openeuropa / oe_whitelabel

European Union Public License 1.2
2 stars 4 forks source link

Make slim_select work for single-value select elements #265

Open donquixote opened 5 months ago

donquixote commented 5 months ago

Currently, oe_whitelabel adds slim_select for multiple-value select elements.

See _oe_whitelabel_process_element_select(). https://github.com/openeuropa/oe_whitelabel/blob/3ec646ddd82fe1938bbab66d5ebd4dc0c322ee93/oe_whitelabel.theme#L210-L220

The slim_select library can also be useful for single-value selects with many options.

Currently there is one obstacle: The BCL targets a class 'placeholder' which is also used in slim_select, causing an undesirable side effect.

Other than that, we need to decide if it is safe to apply it to all single-value selects, or if we need to decide it case by case.

donquixote commented 5 months ago

A custom code solution can look like this:

This should be done on theme level, in a sub-theme of oe_whitelabel, if the slim-select library is not available in other themes.


/**
 * Implements hook_element_info_alter().
 *
 * Replaces a '#process' callback on the 'select' element type.
 *
 * @see oe_whitelabel_element_info_alter()
 */
function MYTHEME_element_info_alter(array &$info): void {
  if (isset($info['select']['#process'])) {
    // Find the callback added by oe_whitelabel.
    $key = array_search(
      '_oe_whitelabel_process_element_select',
      $info['select']['#process'],
      TRUE,
    );
    if ($key !== FALSE) {
      // Replace this callback added by oe_whitelabel.
      $info['select']['#process'][$key] = '_MYTHEME_process_element_select';
    }
  }
}

/**
 * Element '#process' callback for 'select' elements.
 *
 * This replaces a similar function from oe_whitelabel.
 * It adds slim_select also to single-value select elements with sufficient
 * number of options.
 *
 * See https://github.com/openeuropa/oe_whitelabel/issues/265
 *
 * @param array $element
 *   Original form element with '#type' => 'select'.
 *
 * @return array
 *   Processed form element.
 *
 * @see _oe_whitelabel_process_element_select()
 */
function _MYTHEME_process_element_select(array &$element): array {
  if ($element['#multiple']) {
    // This is a multiple-value select element.
    // Delegate to oe_whitelabel, to benefit from upstream changes.
    _oe_whitelabel_process_element_select($element);
  }
  elseif (count($element['#options']) > 5) {
    // This is a single-value select element with more than 5 options.
    // Trick oe_whitelabel into adding slim_select.
    $multiple_orig = $element['#multiple'];
    $element['#multiple'] = TRUE;
    // Delegate to oe_whitelabel, to benefit from upstream changes.
    _oe_whitelabel_process_element_select($element);
    // The original value of '#multiple' should always be boolean FALSE, if the
    // element type is used correctly.
    // However, to avoid any kind of side effect, we restore the original value,
    // which technically might be something like '' or 0 or NULL.
    $element['#multiple'] = $multiple_orig;
  }

  return $element;
}

Then to fix the CSS conflict:

/*
 * Styles to prevent a side effect of BCL styles on a placeholder element
 * created by slim_select.
 *
 * See https://github.com/openeuropa/oe_whitelabel/issues/265.
 */
:is(.ss-single-selected) > .placeholder {
  background-color: unset;
  cursor: inherit;
  display: unset;
  min-height: unset;
  opacity: unset;
  vertical-align: unset;
}

The :is(*) helps to reduce the selector weight, keeping it on the same level as the .placeholder style from BCL. This CSS needs to be included after the BCL css.

The inherit on cursor: inherit could probably be replaced with unset. In the end it does not matter because it is overridden by slim-select style.

For the .ss-single-selected I am not sure. It works for us, but perhaps we need a more general selector to cover other cases.

brummbar commented 3 months ago

We could add a configuration in the theme to enable slim select for all selects.