Commit 832ba3a5 authored by borisson_'s avatar borisson_ Committed by borisson_

Issue #2658678 by borisson_, StryKaizer, Evaldas Užkuras: Checkbox widget does...

Issue #2658678 by borisson_, StryKaizer, Evaldas Užkuras: Checkbox widget does not enable multiple facet's values at a time
parent c661eb28
......@@ -14,10 +14,17 @@ drupal.facets.edit-facet:
dependencies:
- core/jquery
- core/drupal
- core/jquery.once
drupal.facets.admin_css:
version: VERSION
css:
theme:
css/facets.admin.css: {}
drupal.facets.checkbox-widget:
version: VERSION
js:
js/checkbox-widget.js: {}
dependencies:
- core/jquery
- core/drupal
/**
* @file
* Transforms links into checkboxes.
*/
(function ($) {
"use strict";
Drupal.facets = {};
Drupal.behaviors.facetsCheckboxWidget = {
attach: function (context, settings) {
Drupal.facets.makeCheckboxes();
}
};
/**
* Turns all facet links into checkboxes.
*/
Drupal.facets.makeCheckboxes = function () {
// Find all checkbox facet links and give them a checkbox.
var $links = $('.js-facets-checkbox-links .facet-item a');
$links.once('facets-checkbox-transform').each(Drupal.facets.makeCheckbox);
};
/**
* Replace a link with a checked checkbox.
*/
Drupal.facets.makeCheckbox = function () {
var $link = $(this);
var active = $link.hasClass('is-active');
var description = $link.html();
var href = $link.attr('href');
var id = $link.data('facet-id');
var checkbox = $('<input type="checkbox" class="facets-checkbox" id="' + id + '" data-facetsredir="' + href + '" />');
var label = $('<label for="' + id + '">' + description + '</label>');
checkbox.change(function (e) {
Drupal.facets.disableFacet($link.parents('.js-facets-checkbox-links'));
window.location.href = $(this).data('facetsredir');
});
if (active) {
checkbox.attr('checked', true);
label.find('.facet-deactivate').remove();
}
$link.before(checkbox).before(label).hide();
};
/**
* Disable all facet checkboxes in the facet and apply a 'disabled' class.
*/
Drupal.facets.disableFacet = function ($facet) {
$facet.addClass('facets-disabled');
$('input.facets-checkbox').click(Drupal.facets.preventDefault);
$('input.facetapi-checkbox', $facet).attr('disabled', true);
};
/**
* Event listener for easy prevention of event propagation.
*/
Drupal.facets.preventDefault = function (e) {
e.preventDefault();
}
})(jQuery);
<?php
namespace Drupal\facets\Form;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\facets\FacetInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* The checkbox / radios widget form.
*/
class CheckboxWidgetForm implements BaseFormIdInterface {
/**
* The facet to build the checkbox form for.
*
* @var FacetInterface $facet
*/
protected $facet;
/**
* Class constructor.
*
* @param \Drupal\facets\FacetInterface $facet
* The facet to build the form for.
*/
public function __construct(FacetInterface $facet) {
$this->facet = $facet;
}
/**
* {@inheritdoc}
*/
public function getBaseFormId() {
return 'facets_checkbox_widget';
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return $this->getBaseFormId() . '__' . $this->facet->id();
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$facet = $this->facet;
/** @var \Drupal\facets\Result\Result[] $results */
$results = $facet->getResults();
$configuration = $facet->getWidgetConfigs();
$show_numbers = (bool) isset($configuration['show_numbers']) ? $configuration['show_numbers'] : FALSE;
$form[$facet->getFieldAlias()] = [
'#type' => 'checkboxes',
'#title' => $facet->getName(),
];
$options = [];
foreach ($results as $result) {
$text = $result->getDisplayValue();
if ($show_numbers) {
$text .= ' (' . $result->getCount() . ')';
}
$options[$result->getRawValue()] = $text;
if ($result->isActive()) {
$form[$facet->getFieldAlias()]['#default_value'][] = $result->getRawValue();
}
}
$form[$facet->getFieldAlias()]['#options'] = $options;
$form[$facet->id() . '_submit'] = [
'#type' => 'submit',
'#value' => 'submit',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$facet = $this->facet;
$result_link = FALSE;
$active_items = [];
foreach ($values[$facet->getFieldAlias()] as $key => $value) {
if ($value !== 0) {
$active_items[] = $value;
}
}
foreach ($facet->getResults() as $result) {
if (in_array($result->getRawValue(), $active_items)) {
$result_link = $result->getUrl();
}
}
// We have an active item, so we redirect to the page that has that facet
// selected. This should be an absolute link because RedirectResponse is a
// symfony class that requires a full URL.
if ($result_link instanceof Url) {
$result_link->setAbsolute();
$form_state->setResponse(new RedirectResponse($result_link->toString()));
return;
}
// The form was submitted but nothing was active in the form, we should
// still redirect, but the url for the new page can't come from a result.
// So we're redirecting to the facet source's page.
$path = $facet->getFacetSource()->getPath();
if (substr($path, 0, 1) !== '/') {
$path = '/' . $path;
}
$link = Url::fromUserInput($path);
$link->setAbsolute();
$form_state->setResponse(new RedirectResponse($link->toString()));
}
}
......@@ -2,11 +2,8 @@
namespace Drupal\facets\Plugin\facets\widget;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\facets\FacetInterface;
use Drupal\facets\Form\CheckboxWidgetForm;
use Drupal\facets\Widget\WidgetInterface;
use Drupal\facets\Result\ResultInterface;
/**
* The checkbox / radios widget.
......@@ -17,44 +14,61 @@ use Drupal\facets\Widget\WidgetInterface;
* description = @Translation("A configurable widget that shows a list of checkboxes"),
* )
*/
class CheckboxWidget implements WidgetInterface {
use StringTranslationTrait;
class CheckboxWidget extends LinksWidget {
/**
* {@inheritdoc}
* The facet the widget is being built for.
*
* @var \Drupal\facets\FacetInterface
*/
public function build(FacetInterface $facet) {
$form_builder = \Drupal::getContainer()->get('form_builder');
$form_object = new CheckboxWidgetForm($facet);
return $form_builder->getForm($form_object);
}
protected $facet;
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state, $config) {
public function build(FacetInterface $facet) {
$this->facet = $facet;
$form['show_numbers'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show the amount of results'),
];
/** @var \Drupal\facets\Result\Result[] $results */
$results = $facet->getResults();
$items = [];
$configuration = $facet->getWidgetConfigs();
$this->showNumbers = empty($configuration['show_numbers']) ? FALSE : (bool) $configuration['show_numbers'];
if (!is_null($config)) {
$widget_configs = $config->get('widget_configs');
if (isset($widget_configs['show_numbers'])) {
$form['show_numbers']['#default_value'] = $widget_configs['show_numbers'];
foreach ($results as $result) {
if (is_null($result->getUrl())) {
$text = $this->extractText($result);
$items[] = ['#markup' => $text];
}
else {
$items[] = $this->buildListItems($result);
}
}
return $form;
$build = [
'#theme' => 'item_list',
'#items' => $items,
'#attributes' => ['class' => ['js-facets-checkbox-links']],
'#cache' => [
'contexts' => [
'url.path',
'url.query_args',
],
],
];
$build['#attached']['library'][] = 'facets/drupal.facets.checkbox-widget';
return $build;
}
/**
* {@inheritdoc}
*/
public function getQueryType($query_types) {
return $query_types['string'];
protected function buildListItems(ResultInterface $result) {
$items = parent::buildListItems($result);
$items['#attributes']['data-facet-id'] = $this->facet->getUrlAlias() . '-' . $result->getRawValue();
return $items;
}
}
......@@ -69,80 +69,8 @@ class WidgetIntegrationTest extends WebTestBase {
$this->drupalPlaceBlock($block_values['plugin_id'], $block_values['settings']);
$this->drupalGet('search-api-test-fulltext');
$this->drupalPostForm(NULL, array('type[item]' => 'item'), $this->t('submit'));
$this->assertFieldChecked('edit-type-item');
}
/**
* Tests multiple checkbox widgets.
*/
public function testMultipleCheckboxWidget() {
$facet_add_page = 'admin/config/search/facets/add-facet';
$id = 'type';
$name = 'Northern hawk-owl | type';
$id_2 = 'keywords';
$name_2 = 'Papuan hawk-owl | keywords';
// Add a new facet.
$form_values = [
'id' => $id,
'status' => 1,
'name' => $name,
'facet_source_id' => 'search_api_views:search_api_test_view:page_1',
'facet_source_configs[search_api_views:search_api_test_view:page_1][field_identifier]' => 'type',
];
$this->drupalGet($facet_add_page);
$this->drupalPostForm(NULL, ['facet_source_id' => 'search_api_views:search_api_test_view:page_1'], $this->t('Configure facet source'));
$this->drupalPostForm(NULL, $form_values, $this->t('Save'));
$this->drupalPostForm(NULL, ['widget' => 'checkbox'], $this->t('Save'));
// Add a new facet.
$form_values = [
'id' => $id_2,
'status' => 1,
'name' => $name_2,
'facet_source_id' => 'search_api_views:search_api_test_view:page_1',
'facet_source_configs[search_api_views:search_api_test_view:page_1][field_identifier]' => 'keywords',
];
$this->drupalGet($facet_add_page);
$this->drupalPostForm(NULL, ['facet_source_id' => 'search_api_views:search_api_test_view:page_1'], $this->t('Configure facet source'));
$this->drupalPostForm(NULL, $form_values, $this->t('Save'));
$this->drupalPostForm(NULL, ['widget' => 'checkbox'], $this->t('Save'));
// Place facets as blocks.
$block_values = ['region' => 'footer', 'id' => str_replace('_', '-', $id)];
$this->drupalPlaceBlock('facet_block:' . $id, $block_values);
$block_values = ['region' => 'footer', 'id' => str_replace('_', '-', $id_2)];
$this->drupalPlaceBlock('facet_block:' . $id_2, $block_values);
// Go to the test view and test that both facets are shown on the page.
$this->drupalGet('search-api-test-fulltext');
$this->assertText($name);
$this->assertText($name_2);
$this->assertText('item');
$this->assertText('apple');
// Submit the facet form and check that the form is submitted and the
// checkbox is now checked.
$edit = array('type[item]' => 'item');
$this->drupalPostForm(NULL, $edit, $this->t('submit'));
$this->assertText($name);
$this->assertText($name_2);
$this->assertText('item');
$this->assertText('apple');
$this->assertFieldChecked('edit-type-item');
// Submit the second facet form and check that the form is submitted and the
// checkbox is now checked.
$edit = array('keywords[apple]' => 'apple');
$this->drupalPostForm(NULL, $edit, $this->t('submit'));
$this->assertText($name);
$this->assertText($name_2);
$this->assertText('item');
$this->assertText('apple');
$this->assertFieldChecked('edit-type-item');
$this->assertFieldChecked('edit-keywords-apple');
$this->assertLink('item');
$this->assertLink('article');
}
/**
......
<?php
namespace Drupal\Tests\facets\Unit\Form;
use Drupal\Core\Form\FormState;
use Drupal\Core\Url;
use Drupal\facets\Entity\Facet;
use Drupal\facets\Form\CheckboxWidgetForm;
use Drupal\facets\Result\Result;
use Drupal\Tests\UnitTestCase;
/**
* Unit test for the checkbox widget form.
*
* @group facets
*/
class CheckboxWidgetFormTest extends UnitTestCase {
/**
* An array containing the results before the processor has ran.
*
* @var \Drupal\facets\Result\Result[]
*/
protected $originalResults;
/**
* Creates a new processor object for use in the tests.
*/
protected function setUp() {
parent::setUp();
/** @var \Drupal\facets\Result\Result[] $original_results */
$original_results = [
new Result('llama', 'Llama', 10),
new Result('badger', 'Badger', 20),
new Result('duck', 'Duck', 15),
new Result('alpaca', 'Alpaca', 9),
];
foreach ($original_results as $original_result) {
$original_result->setUrl(new Url('test'));
}
$original_results[1]->setActiveState(TRUE);
$this->originalResults = $original_results;
}
/**
* Tests widget form with default settings.
*/
public function testDefaultSettings() {
$facet = new Facet(['id' => 'zoo_animal'], 'facet');
$facet->setResults($this->originalResults);
$facet->setFieldIdentifier('zoo_animal');
$form_state = new FormState();
$form_state->addBuildInfo('args', [$facet]);
$form = [];
$widget_form = new CheckboxWidgetForm($facet);
$built_form = $widget_form->buildForm($form, $form_state);
$this->assertInternalType('array', $built_form);
$this->assertCount(4, $built_form['zoo_animal']['#options']);
$this->assertEquals('checkboxes', $built_form['zoo_animal']['#type']);
$expected_links = [
'llama' => 'Llama',
'badger' => 'Badger',
'duck' => 'Duck',
'alpaca' => 'Alpaca',
];
foreach ($expected_links as $index => $value) {
$this->assertEquals($value, $built_form['zoo_animal']['#options'][$index]);
}
$this->assertEquals(array('zoo_animal', 'zoo_animal_submit'), array_keys($built_form));
}
/**
* Tests widget form, make sure hiding and showing numbers works.
*/
public function testHideNumbers() {
$facet = new Facet([], 'facet');
$facet->setResults($this->originalResults);
$facet->setFieldIdentifier('zoo__animal');
$facet->setWidgetConfigs(['show_numbers' => 0]);
$form_state = new FormState();
$form_state->addBuildInfo('args', [$facet]);
$form = [];
$widget_form = new CheckboxWidgetForm($facet);
$built_form = $widget_form->buildForm($form, $form_state);
$this->assertInternalType('array', $built_form);
$this->assertCount(4, $built_form['zoo__animal']['#options']);
$expected_links = [
'llama' => 'Llama',
'badger' => 'Badger',
'duck' => 'Duck',
'alpaca' => 'Alpaca',
];
foreach ($expected_links as $index => $value) {
$this->assertEquals($value, $built_form['zoo__animal']['#options'][$index]);
}
// Enable the 'show_numbers' setting again to make sure that the switch
// between those settings works.
$facet->setWidgetConfigs(['show_numbers' => 1]);
$built_form = $widget_form->buildForm($form, $form_state);
$this->assertInternalType('array', $built_form);
$this->assertCount(4, $built_form['zoo__animal']['#options']);
$expected_links = [
'llama' => 'Llama (10)',
'badger' => 'Badger (20)',
'duck' => 'Duck (15)',
'alpaca' => 'Alpaca (9)',
];
foreach ($expected_links as $index => $value) {
$this->assertEquals($value, $built_form['zoo__animal']['#options'][$index]);
}
}
/**
* Tests form default methods.
*/
public function testForm() {
$facet = new Facet(['id' => 'donkey'], 'facet');
$facet->setResults($this->originalResults);
$facet->setFieldIdentifier('donkey');
$form = new CheckboxWidgetForm($facet);
$this->assertEquals('facets_checkbox_widget', $form->getBaseFormId());
$this->assertEquals('facets_checkbox_widget__donkey', $form->getFormId());
}
}
......@@ -2,12 +2,12 @@
namespace Drupal\Tests\facets\Unit\Plugin\widget;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Url;
use Drupal\facets\Entity\Facet;
use Drupal\facets\Plugin\facets\widget\CheckboxWidget;
use Drupal\facets\Result\Result;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Unit test for widget.
......@@ -49,35 +49,63 @@ class CheckboxWidgetTest extends UnitTestCase {
}
$this->originalResults = $original_results;
$form_builder = $this->getMockBuilder('\Drupal\Core\Form\FormBuilder')
->disableOriginalConstructor()
->getMock();
$form_builder->expects($this->once())
->method('getForm')
->willReturn('build');
$string_translation = $this->getMockBuilder('\Drupal\Core\StringTranslation\TranslationManager')
->disableOriginalConstructor()
->getMock();
$container_builder = new ContainerBuilder();
$container_builder->set('form_builder', $form_builder);
$container_builder->set('string_translation', $string_translation);
\Drupal::setContainer($container_builder);
$this->widget = new CheckboxWidget();
}
/**
* Tests widget with default settings.
* Tests widget without filters.
*/
public function testDefaultSettings() {
public function testNoFilterResults() {
$facet = new Facet([], 'facet');
$facet->setResults($this->originalResults);
$facet->setFieldIdentifier('test_field');
$facet->setWidgetConfigs(['show_numbers' => 1]);
$output = $this->widget->build($facet);
$this->assertInternalType('array', $output);
$this->assertCount(4, $output['#items']);
$this->assertEquals(['js-facets-checkbox-links'], $output['#attributes']['class']);
$expected_links = [
$this->buildLinkAssertion('Llama', 10),
$this->buildLinkAssertion('Badger', 20),
$this->buildLinkAssertion('Duck', 15),
$this->buildLinkAssertion('Alpaca', 9),
];
foreach ($expected_links as $index => $value) {
$this->assertInternalType('array', $output['#items'][$index]);
$this->assertEquals($value, $output['#items'][$index]['#title']);
$this->assertInstanceOf(FormattableMarkup::class, $output['#items'][$index]['#title']);
$this->assertEquals('link', $output['#items'][$index]['#type']);
$this->assertEquals(['facet-item'], $output['#items'][$index]['#wrapper_attributes']['class']);
}
}
$built_form = $this->widget->build($facet);
$this->assertEquals('build', $built_form);
/**
* Build a formattable markup object to use in the other tests.
*
* @param string $text
* Text to display.
* @param int $count
* Number of results.
* @param bool $active
* Link is active.
* @param bool $show_numbers
* Numbers are displayed.
*
* @return \Drupal\Component\Render\FormattableMarkup
* Formattable markup object for link.
*/
private function buildLinkAssertion($text, $count = 0, $active = FALSE, $show_numbers = TRUE) {
$text = new FormattableMarkup('@text', ['@text' => $text, '@count' => $count]);
if ($show_numbers !== FALSE) {
$text->string .= ' <span class="facet-count">(@count)</span>';
}
if ($active) {
$text->string = '<span class="facet-deactivate">(-)</span> ' . $text->string;
}
return $text;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment