Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impl. clearable dropdown #2209

Merged
merged 17 commits into from
Jan 10, 2025
2 changes: 1 addition & 1 deletion demos/_unit-test/grid-rowclick.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@
// this emulation is not perfect, as it works even with event.preventDefault() called
$grid->table->js(true)->find('a')->on(
'click',
new JsFunction(['event'], [new JsExpression('window.location.href = \'#test\'; event.preventDefault();')])
new JsFunction([], [new JsExpression('window.location.href = \'#test\''), 'preventDefault' => true])
);
6 changes: 3 additions & 3 deletions demos/form-control/dropdown-plus.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@
$form->addControl('values', [
Form\Control\Dropdown::class,
'caption' => 'Using values with default text',
'empty' => 'Choose an option',
'placeholder' => 'Choose an option',
'values' => ['default' => 'Default', 'option1' => 'Option 1', 'option2' => 'Option 2', 'option3' => 'Option 3'],
]);

$form->addControl('icon', [
Form\Control\Dropdown::class,
'caption' => 'Using icon',
'empty' => 'Choose an icon',
'placeholder' => 'Choose an icon',
'values' => [
'tag' => ['Tag', 'icon' => 'tag'],
'globe' => ['Globe', 'icon' => 'globe'],
Expand All @@ -99,7 +99,7 @@
$form->addControl('multi', [
Form\Control\Dropdown::class,
'caption' => 'Multiple selection',
'empty' => 'Choose has many options needed',
'placeholder' => 'Choose has many options needed',
'multiple' => true,
'values' => ['default' => 'Default', 'option1' => 'Option 1', 'option2' => 'Option 2'],
]);
Expand Down
72 changes: 36 additions & 36 deletions src/Behat/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ protected function unquoteStepArgument(string $argument): string
/**
* Sleep for a certain time in ms.
*
* @Then I wait :arg1 ms
* @When I wait :arg1 ms
*/
public function iWait(int $ms): void
{
Expand Down Expand Up @@ -349,7 +349,7 @@ public function iClickLink(string $label): void
}

/**
* @Then I click using selector :selector
* @When I click using selector :selector
*/
public function iClickUsingSelector(string $selector): void
{
Expand All @@ -362,7 +362,7 @@ public function iClickUsingSelector(string $selector): void
*
* One solution can be waiting for AJAX after each \WebDriver\AbstractWebDriver::curl() call.
*
* @Then PATCH DRIVER I click using selector :selector
* @When PATCH DRIVER I click using selector :selector
*/
public function iClickPatchedUsingSelector(string $selector): void
{
Expand All @@ -378,7 +378,7 @@ public function iClickPatchedUsingSelector(string $selector): void
}

/**
* @Then I click paginator page :arg1
* @When I click paginator page :arg1
*/
public function iClickPaginatorPage(string $pageNumber): void
{
Expand All @@ -400,7 +400,7 @@ public function iFillField(string $selector, string $value): void
// {{{ modal

/**
* @Then I press Modal button :arg
* @When I press Modal button :arg
*/
public function iPressModalButton(string $buttonLabel): void
{
Expand Down Expand Up @@ -436,7 +436,7 @@ public function iFillModalField(string $fieldName, string $value): void
}

/**
* @Then I click close modal
* @When I click close modal
*/
public function iClickCloseModal(): void
{
Expand All @@ -446,7 +446,7 @@ public function iClickCloseModal(): void
}

/**
* @Then I hide js modal
* @When I hide js modal
*/
public function iHideJsModal(): void
{
Expand Down Expand Up @@ -487,7 +487,7 @@ public function iFillPanelField(string $fieldName, string $value): void
}

/**
* @Then I press Panel button :arg
* @When I press Panel button :arg
*/
public function iPressPanelButton(string $buttonLabel): void
{
Expand All @@ -501,7 +501,7 @@ public function iPressPanelButton(string $buttonLabel): void
// {{{ tab

/**
* @Given I click tab with title :arg1
* @When I click tab with title :arg1
*/
public function iClickTabWithTitle(string $tabTitle): void
{
Expand Down Expand Up @@ -541,7 +541,7 @@ public function inputValueShouldStartWith(string $inputName, string $text): void
}

/**
* @Then I search grid for :arg1
* @When I search grid for :arg1
*/
public function iSearchGridFor(string $text): void
{
Expand All @@ -550,23 +550,25 @@ public function iSearchGridFor(string $text): void
}

/**
* @Then I select value :arg1 in lookup :arg2
* @When I select value :arg1 in lookup :arg2
*/
public function iSelectValueInLookup(string $value, string $inputName): void
{
$isSelectorXpath = $this->parseSelector($inputName)[0] === 'xpath';

// get dropdown item from Fomantic-UI which is direct parent of input HTML element
$isSelectorXpath = $this->parseSelector($inputName)[0] === 'xpath';
$lookupElem = $this->findElement(null, ($isSelectorXpath ? $inputName : '//input[@name="' . $inputName . '"]') . '/parent::div');

if ($value === '') {
$this->findElement($lookupElem, 'i.remove.icon')->click();

return;
}

// open dropdown and wait till fully opened (just a click is not triggering it)
$this->getSession()->executeScript('$(arguments[0]).dropdown(\'show\')', [$lookupElem]);
$this->jqueryWait('$(arguments[0]).hasClass(\'visible\')', [$lookupElem]);

// select value
if ($value === '') { // TODO impl. native clearable - https://github.com/atk4/ui/issues/572
$value = "\u{00a0}";
}
$valueElem = $this->findElement($lookupElem, '//div[text()="' . $value . '"]');
$this->getSession()->executeScript('$(arguments[0]).dropdown(\'set selected\', arguments[1]);', [$lookupElem, $valueElem->getAttribute('data-value')]);
$this->jqueryWait();
Expand Down Expand Up @@ -678,31 +680,29 @@ public function scopeBuilderBoolRule(string $name, string $value): void
}

/**
* @Then ~^I check if input value for "([^"]*)" match text "([^"]*)"~
* @Then ~^I check if input value for "([^"]*)" match text in "([^"]*)"$~
*/
public function compareInputValueToText(string $selector, string $text): void
public function compareInputValueText(string $compareSelector, string $compareToSelector): void
{
$selector = $this->unquoteStepArgument($selector);
$text = $this->unquoteStepArgument($text);
$compareSelector = $this->unquoteStepArgument($compareSelector);
$compareToSelector = $this->unquoteStepArgument($compareToSelector);

$inputValue = $this->findElement(null, $selector)->getValue();
if ($inputValue !== $text) {
throw new \Exception('Input value does not match: ' . $inputValue . ', expected: ' . $text);
if ($this->findElement(null, $compareSelector)->getValue() !== $this->findElement(null, $compareToSelector)->getText()) {
throw new \Exception('Input value does not match between: ' . $compareSelector . ' and ' . $compareToSelector);
}
}

/**
* @Then ~^I check if input value for "([^"]*)" match text in "([^"]*)"$~
* @Then ~^I check if input value for "([^"]*)" match text "([^"]*)"$~
*/
public function compareInputValueToElementText(string $inputName, string $selector): void
public function compareInputValueToText(string $selector, string $text): void
{
$inputName = $this->unquoteStepArgument($inputName);
$selector = $this->unquoteStepArgument($selector);
$text = $this->unquoteStepArgument($text);

$expectedText = $this->findElement(null, $selector)->getText();
$input = $this->findElement(null, 'input[name="' . $inputName . '"]');
if ($expectedText !== $input->getValue()) {
throw new \Exception('Input value does not match: ' . $input->getValue() . ', expected: ' . $expectedText);
$inputValue = $this->findElement(null, $selector)->getValue();
if ($inputValue !== $text) {
throw new \Exception('Input value does not match: ' . $inputValue . ', expected: ' . $text);
}
}

Expand All @@ -720,7 +720,7 @@ public function dump(string $arg1): void
}

/**
* @Then I click filter column name :arg1
* @When I click filter column name :arg1
*/
public function iClickFilterColumnName(string $columnName): void
{
Expand All @@ -747,15 +747,15 @@ public function containerShouldHaveNumberOfItem(string $selector, int $numberOfi
}

/**
* @Then I scroll to top
* @When I scroll to top
*/
public function iScrollToTop(): void
{
$this->getSession()->executeScript('window.scrollTo(0, 0)');
}

/**
* @Then I scroll to bottom
* @When I scroll to bottom
*/
public function iScrollToBottom(): void
{
Expand Down Expand Up @@ -801,7 +801,7 @@ public function assertUrlRegExp(string $pattern): void
}

/**
* @Then ~^I check if text in "([^"]*)" match text in "([^"]*)"~
* @Then ~^I check if text in "([^"]*)" match text in "([^"]*)"$~
*/
public function compareElementText(string $compareSelector, string $compareToSelector): void
{
Expand All @@ -814,7 +814,7 @@ public function compareElementText(string $compareSelector, string $compareToSel
}

/**
* @Then ~^I check if text in "([^"]*)" match text "([^"]*)"~
* @Then ~^I check if text in "([^"]*)" match text "([^"]*)"$~
*/
public function textInContainerShouldMatch(string $selector, string $text): void
{
Expand All @@ -827,7 +827,7 @@ public function textInContainerShouldMatch(string $selector, string $text): void
}

/**
* @Then ~^I check if text in "([^"]*)" match regex "([^"]*)"~
* @Then ~^I check if text in "([^"]*)" match regex "([^"]*)"$~
*/
public function textInContainerShouldMatchRegex(string $selector, string $regex): void
{
Expand Down
25 changes: 7 additions & 18 deletions src/Form/Control/Dropdown.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ class Dropdown extends Input
*/
public array $values;

/** @var string The string to set as an empty values. */
public $empty = "\u{00a0}"; // Unicode NBSP

/** @var array<string, mixed> Dropdown options as per Fomantic-UI dropdown options. */
public $dropdownOptions = [];

Expand Down Expand Up @@ -161,18 +158,16 @@ protected function jsDropdown($when = false, $action = null): JsExpressionable

protected function jsRenderDropdown(): JsExpressionable
{
return $this->jsDropdown(true)->dropdown($this->dropdownOptions);
$dropdownOptions = $this->dropdownOptions;
if ($this->entityField === null || ($this->entityField->getField()->nullable || !$this->entityField->getField()->required)) {
$dropdownOptions['clearable'] = true;
}

return $this->jsDropdown(true)->dropdown($dropdownOptions);
}

protected function htmlRenderValue(): void
{
// add selection only if no value is required and Dropdown has no multiple selections enabled
if ($this->entityField !== null && !$this->entityField->getField()->required && !$this->multiple) {
$this->_tItem->set('value', '');
$this->_tItem->set('title', $this->empty);
$this->template->dangerouslyAppendHtml('Item', $this->_tItem->renderToHtml());
}

// model set? use this, else values property
if ($this->model !== null) {
if ($this->renderRowFunction) {
Expand Down Expand Up @@ -202,12 +197,6 @@ protected function renderView(): void
$this->template->dangerouslySetHtml('multipleClass', 'multiple');
}

if ($this->disabled || $this->readOnly) {
if ($this->multiple) {
$this->jsDropdown(true)->find('a i.delete.icon')->attr('class', 'disabled');
}
}

if ($this->disabled) {
$this->template->set('disabledClass', 'disabled');
$this->template->dangerouslySetHtml('disabled', 'disabled="disabled"');
Expand All @@ -216,7 +205,7 @@ protected function renderView(): void
$this->template->dangerouslySetHtml('disabled', 'readonly="readonly"');
}

$this->template->set('DefaultText', $this->empty);
$this->template->set('DefaultText', $this->placeholder);

$this->htmlRenderValue();
$this->jsRenderDropdown();
Expand Down
7 changes: 1 addition & 6 deletions src/Form/Control/DropdownCascade.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ public function set($value = null)

/**
* Generate new dropdown values based on cascadeInput model selected ID.
* Return an empty value set if ID is null.
*
* @param mixed $id
*
Expand All @@ -76,11 +75,7 @@ public function set($value = null)
public function getNewValues($id): array
{
if ($id === null) {
return [[
'value' => '',
'text' => $this->empty,
'name' => $this->empty,
]];
return [];
}

$model = $this->cascadeFrom->model->load($id)->ref($this->reference);
Expand Down
16 changes: 7 additions & 9 deletions src/Form/Control/Lookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ class Lookup extends Input
/** @var CallbackLater Object used to capture requests from the browser. */
public $callback;

/** @var string Set this to true, to permit "empty" selection. If you set it to string, it will be used as a placeholder for empty value. */
public $empty = "\u{00a0}"; // Unicode NBSP

/**
* Either set this to array of fields which must be searched (e.g. "name", "surname"), or define this
* as a callback to be executed callback($model, $query);.
Expand Down Expand Up @@ -112,8 +109,9 @@ class Lookup extends Input
public $settings = [];

/**
* Define callback for generating the row data
* If left empty default callback Lookup::defaultRenderRow is used.
* Define callback for generating the row data.
*
* When null default callback Lookup::defaultRenderRow is used.
*
* @var \Closure<T of Model>($this, T): array{title: mixed}
*/
Expand Down Expand Up @@ -197,10 +195,6 @@ public function getData($limit = true): array
$data[] = $this->renderRow($row);
}

if (!$this->multiple && $this->empty) {
array_unshift($data, ['value' => '', 'title' => $this->empty]);
}

return $data;
}

Expand Down Expand Up @@ -363,6 +357,10 @@ protected function initDropdown($chain): void
'apiSettings' => array_merge(['url' => $this->getCallbackUrl() . '&q={query}'], $this->apiConfig),
], $this->settings);

if ($this->entityField === null || ($this->entityField->getField()->nullable || !$this->entityField->getField()->required)) {
$settings['clearable'] = true;
}

$chain->dropdown($settings);
}

Expand Down
4 changes: 3 additions & 1 deletion src/Form/Control/ScopeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,8 @@ public function detectDelimiter(string $value): string

$max = array_keys($matches, max($matches), true);

return $max !== [] ? reset($max) : reset(static::$listDelimiters);
return $max !== []
? reset($max)
: reset(static::$listDelimiters);
}
}
Loading
Loading