diff --git a/css/yamlform.element.options.css b/css/yamlform.element.options.css new file mode 100755 index 0000000..b1c7a4f --- /dev/null +++ b/css/yamlform.element.options.css @@ -0,0 +1,47 @@ +/** + * @file + * Styles for YAML form options element. + */ + +.form-type-yamlform-options table td { + white-space: nowrap; +} + +.form-type-yamlform-options table th:last-child, +.form-type-yamlform-options table td:last-child { + padding: 0 4px; + vertical-align: middle; + text-align: center; + width: 24px; +} + +.form-type-yamlform-options table td input[type="text"] { + width: 95%; +} + +.form-type-yamlform-options table td input[type="image"] { + padding: 0; + width: 16px; + height: 16px; +} + +.form-type-yamlform-options .container-inline input[type="submit"] { + width: inherit !important; +} + +/** + * Hide AJAX throbber. + */ +.form-type-yamlform-options .ajax-progress-throbber { + display: none; +} + +/** + * Suppress table drag warnings. + */ +.form-type-yamlform-options .tabledrag-changed-warning, +.form-type-yamlform-options .tabledrag-changed{ + display: none !important; +} + + diff --git a/css/yamlform.theme.seven.css b/css/yamlform.theme.seven.css index 6566e8b..042bb49 100755 --- a/css/yamlform.theme.seven.css +++ b/css/yamlform.theme.seven.css @@ -3,8 +3,8 @@ * YAML form seven theme styling. */ -td { - vertical-align: top !important; +table td { + vertical-align: top; } .yamlform-help-details { diff --git a/modules/yamlform_ui/src/Form/YamlFormUiElementFormBase.php b/modules/yamlform_ui/src/Form/YamlFormUiElementFormBase.php index 5742710..6e93908 100644 --- a/modules/yamlform_ui/src/Form/YamlFormUiElementFormBase.php +++ b/modules/yamlform_ui/src/Form/YamlFormUiElementFormBase.php @@ -41,13 +41,6 @@ abstract class YamlFormUiElementFormBase extends FormBase { protected $yamlform; /** - * A YAML form element. - * - * @var \Drupal\yamlform\YamlFormElementInterface - */ - protected $yamlformElement; - - /** * The YAML form element. * * @var array @@ -95,10 +88,9 @@ abstract class YamlFormUiElementFormBase extends FormBase { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, YamlFormInterface $yamlform = NULL, $key = NULL, $parent_key = '') { - $plugin_id = $this->elementManager->getElementPluginId($this->element); - $this->yamlform = $yamlform; - $this->yamlformElement = $this->elementManager->createInstance($plugin_id, $this->element); + + $yamlform_element = $this->getYamlFormElement(); $form['parent_key'] = [ '#type' => 'value', @@ -122,13 +114,13 @@ abstract class YamlFormUiElementFormBase extends FormBase { $form['key']['#description'] = NULL; } - $form['properties'] = $this->yamlformElement->buildConfigurationForm([], $form_state); + $form['properties'] = $yamlform_element->buildConfigurationForm([], $form_state); // Add type to the general details. $form['properties']['general']['type'] = [ '#type' => 'item', '#title' => $this->t('Type'), - '#markup' => $this->yamlformElement->getPluginLabel(), + '#markup' => $yamlform_element->getPluginLabel(), '#weight' => -100, '#parents' => ['type'], ]; @@ -144,6 +136,7 @@ abstract class YamlFormUiElementFormBase extends FormBase { '#type' => 'submit', '#value' => $this->t('Save'), '#button_type' => 'primary', + '#_validate_form' => TRUE, ]; $form = $this->buildDialog($form, $form_state); @@ -155,13 +148,21 @@ abstract class YamlFormUiElementFormBase extends FormBase { * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { + // Only validate the submit button. + $button = $form_state->getTriggeringElement(); + if (empty($button['#_validate_form'])) { + return; + } + + $yamlform_element = $this->getYamlFormElement(); + // The YAML form element configuration is stored in the 'properties' key in // the form, pass that through for validation. $element_form_state = (new FormState())->setValues($form_state->getValue('properties') ?: []); $element_form_state->setFormObject($this); // Validate configuration form and set form errors. - $this->yamlformElement->validateConfigurationForm($form, $element_form_state); + $yamlform_element->validateConfigurationForm($form, $element_form_state); $element_errors = $element_form_state->getErrors(); foreach ($element_errors as $element_error) { $form_state->setErrorByName(NULL, $element_error); @@ -173,17 +174,19 @@ abstract class YamlFormUiElementFormBase extends FormBase { } // Set element properties. - $properties = $this->yamlformElement->getConfigurationFormProperties($form, $element_form_state); + $properties = $yamlform_element->getConfigurationFormProperties($form, $element_form_state); $parent_key = $form_state->getValue('parent_key'); $key = $form_state->getValue('key'); - $this->yamlform->setElementProperties($key, $properties, $parent_key); - - // Validate elements. - if ($messages = $this->elementsValidator->validate($this->yamlform)) { - $t_args = [':href' => Url::fromRoute('entity.yamlform.source_form', ['yamlform' => $this->yamlform->id()])->toString()]; - $form_state->setErrorByName('elements', $this->t('There has been error validating the elements. You may need to edit the YAML source to resolve the issue.', $t_args)); - foreach ($messages as $message) { - drupal_set_message($message, 'error'); + if ($key) { + $this->yamlform->setElementProperties($key, $properties, $parent_key); + + // Validate elements. + if ($messages = $this->elementsValidator->validate($this->yamlform)) { + $t_args = [':href' => Url::fromRoute('entity.yamlform.source_form', ['yamlform' => $this->yamlform->id()])->toString()]; + $form_state->setErrorByName('elements', $this->t('There has been error validating the elements. You may need to edit the YAML source to resolve the issue.', $t_args)); + foreach ($messages as $message) { + drupal_set_message($message, 'error'); + } } } } @@ -192,6 +195,8 @@ abstract class YamlFormUiElementFormBase extends FormBase { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + $yamlform_element = $this->getYamlFormElement(); + if ($response = $this->validateDialog($form, $form_state)) { return $response; } @@ -199,7 +204,7 @@ abstract class YamlFormUiElementFormBase extends FormBase { // The YAML form element configuration is stored in the 'properties' key in // the form, pass that through for submission. $element_data = (new FormState())->setValues($form_state->getValue('properties')); - $this->yamlformElement->submitConfigurationForm($form, $element_data); + $yamlform_element->submitConfigurationForm($form, $element_data); $this->yamlform->save(); // Display status message. @@ -245,7 +250,8 @@ abstract class YamlFormUiElementFormBase extends FormBase { * A YAML form element. */ public function getYamlFormElement() { - return $this->yamlformElement; + $plugin_id = $this->elementManager->getElementPluginId($this->element); + return $this->elementManager->createInstance($plugin_id, $this->element); } } diff --git a/src/Element/YamlFormElementOptions.php b/src/Element/YamlFormElementOptions.php index 27898ee..4019c77 100755 --- a/src/Element/YamlFormElementOptions.php +++ b/src/Element/YamlFormElementOptions.php @@ -6,7 +6,7 @@ use Drupal\Component\Serialization\Yaml; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\FormElement; use Drupal\Core\Url; -use Drupal\yamlform\Entity\YamlFormOptions; +use Drupal\yamlform\Entity\YamlFormOptions as YamlFormOptionsEntity; use Drupal\yamlform\Utility\YamlFormElementHelper; /** @@ -41,23 +41,27 @@ class YamlFormElementOptions extends FormElement { */ public static function valueCallback(&$element, $input, FormStateInterface $form_state) { if ($input === FALSE) { - - $default_value = isset($element['#default_value']) ? $element['#default_value'] : NULL; - - if (!$default_value) { - return $element; - } - - if (is_string($default_value) && YamlFormOptions::load($default_value)) { - $element['options']['#default_value'] = $default_value; + if (isset($element['#default_value'])) { + if(is_string($element['#default_value'])) { + return (YamlFormOptionsEntity::load($element['#default_value'])) ? $element['#default_value'] : []; + } + else { + return $element['#default_value']; + } } else { - $element['options']['#default_value'] = self::CUSTOM_OPTION; - $element['custom']['#default_value'] = (is_array($default_value)) ? Yaml::encode($default_value) : $default_value; + return []; } - return $element; } - return NULL; + elseif (!empty($input['options'])) { + return $input['options']; + } + elseif (isset($input['custom']['options'])) { + return $input['custom']['options']; + } + else { + return []; + } } /** @@ -69,7 +73,7 @@ class YamlFormElementOptions extends FormElement { // Predefined options. // @see (/admin/structure/yamlform/settings/options/manage) $options = []; - $yamlform_options = YamlFormOptions::loadMultiple(); + $yamlform_options = YamlFormOptionsEntity::loadMultiple(); foreach ($yamlform_options as $id => $yamlform_option) { // Filter likert options for answers to the likert element. if ($element['#likert'] && strpos($id, 'likert_') !== 0) { @@ -79,36 +83,40 @@ class YamlFormElementOptions extends FormElement { } asort($options); - $element['options']['#type'] = 'select'; - $element['options']['#options'] = [ - self::CUSTOM_OPTION => t('Custom...'), - ] + $options; - $element['options']['#attributes']['class'][] = 'js-' . $element['#id'] . '-options'; - $element['options']['#error_no_message'] = TRUE; $t_args = [ '@type' => ($element['#likert']) ? t('answers') : t('options'), ':href' => Url::fromRoute('entity.yamlform_options.collection')->toString(), ]; - $element['options']['#description'] = t('Please select predefined @type or enter custom @type.', $t_args); - // Custom options. - $element['custom']['#title'] = $element['#title']; - $element['custom']['#title_display'] = 'invisible'; - $element['custom']['#type'] = 'yamlform_codemirror'; - $element['custom']['#mode'] = 'yaml'; - $element['custom']['#states'] = [ - 'visible' => [ - 'select.js-' . $element['#id'] . '-options' => ['value' => ''], + // Select options. + $element['options'] = [ + '#type' => 'select', + '#description' => t('Please select predefined @type or enter custom @type.', $t_args), + '#options' => [ + self::CUSTOM_OPTION => t('Custom...'), + ] + $options, + '#attributes' => [ + 'class' => ['js-' . $element['#id'] . '-options'], ], + '#error_no_message' => TRUE, + '#default_value' => (!is_array($element['#default_value'])) ? $element['#default_value'] : '', ]; - $element['custom']['#error_no_message'] = TRUE; - $t_args = [ - '@type' => ($element['#likert']) ? t('answer') : t('option'), + + // Custom options. + $element['custom'] = [ + '#type' => 'yamlform_options', + '#title' => $element['#title'], + '#title_display' => 'invisible', + '#label' => ($element['#likert']) ? t('answer') : t('option'), + '#labels' => ($element['#likert']) ? t('answers') : t('options'), + '#states' => [ + 'visible' => [ + 'select.js-' . $element['#id'] . '-options' => ['value' => ''], + ], + ], + '#error_no_message' => TRUE, + '#default_value' => (!is_string($element['#default_value'])) ? $element['#default_value'] : [], ]; - $element['custom']['#description'] = t('Key-value pairs MUST be specified as "safe_key: \'Some readable @type\'". Use of only alphanumeric characters and underscores is recommended in keys. One @type per line.', $t_args); - if (!$element['#likert']) { - $element['custom']['#description'] .= t('Option groups can be created by using just the group name followed by indented group options.'); - } $element['#element_validate'] = [[get_called_class(), 'validateYamlFormElementOptions']]; @@ -128,7 +136,7 @@ class YamlFormElementOptions extends FormElement { $value = $options_value; if ($options_value == self::CUSTOM_OPTION) { try { - $value = Yaml::decode($custom_value); + $value = (is_string($custom_value)) ? Yaml::decode($custom_value) : $custom_value; } catch (\Exception $exception) { // Do nothing since the 'yamlform_codemirror' element will have already @@ -136,9 +144,7 @@ class YamlFormElementOptions extends FormElement { } } - $is_empty = ($value === '' || $value === NULL) ? TRUE : FALSE; - - if ($element['#required'] && $is_empty) { + if ($element['#required'] && empty($value)) { $form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']])); } diff --git a/src/Element/YamlFormOptions.php b/src/Element/YamlFormOptions.php new file mode 100644 index 0000000..50d2fa7 --- /dev/null +++ b/src/Element/YamlFormOptions.php @@ -0,0 +1,437 @@ + TRUE, + '#size' => 60, + '#label' => t('option'), + '#labels' => t('options'), + '#empty_options' => 1, + '#process' => [ + [$class, 'processOptions'], + ], + '#theme_wrappers' => ['form_element'], + ]; + } + + /** + * {@inheritdoc} + */ + public static function valueCallback(&$element, $input, FormStateInterface $form_state) { + if ($input === FALSE) { + if (isset($element['#default_value'])) { + return (is_string($element['#default_value'])) ? Yaml::decode($element['#default_value']) : $element['#default_value']; + } + else { + return []; + } + } + elseif (is_array($input) && isset($input['options'])) { + return (is_string($input['options'])) ? Yaml::decode($input['options']) : self::convertValuesToOptions($input['options']); + } + else { + return NULL; + } + } + + /** + * Expand an email confirm field into two HTML5 email elements. + */ + public static function processOptions(&$element, FormStateInterface $form_state, &$complete_form) { + if (isset($element['#default_value']) && is_array($element['#default_value']) && self::hasOptGroup($element['#default_value'])) { + // Build table. + $element['options'] = [ + '#type' => 'yamlform_codemirror', + '#mode' => 'yaml', + '#default_value' => Yaml::encode($element['#default_value']), + '#description' => t('Key-value pairs MUST be specified as "safe_key: \'Some readable options\'". Use of only alphanumeric characters and underscores is recommended in keys. One option per line.') . '
' . + t('Option groups can be created by using just the group name followed by indented group options.'), + ]; + + // Add validate callback that extracts the associative array of options. + $element['#element_validate'] = [[get_called_class(), 'validateYamlFormOptions']]; + + // Wrap this $element in a
that handle #states. + YamlFormElementHelper::fixStates($element); + + return $element; + } + + + // Get unique key used to store the current number of options. + $number_of_options_storage_key = self::getStorageKey($element, 'number_of_options'); + + // Store the number of options which is the number of + // #default_values + number of empty_options. + if ($form_state->get($number_of_options_storage_key) === NULL) { + $number_of_default_values = (isset($element['#default_value']) && is_array($element['#default_value'])) ? count($element['#default_value']) : 0; + $number_of_empty_options = (int) $element['#empty_options']; + $form_state->set($number_of_options_storage_key, $number_of_default_values + $number_of_empty_options); + } + + $number_of_options = $form_state->get($number_of_options_storage_key); + + $table_id = implode('_', $element['#parents']) . '-table'; + $ajax_settings = [ + 'callback' => [get_called_class(), 'ajaxCallback'], + 'wrapper' => $table_id, + ]; + + // DEBUG: Disable AJAX callback so that we can see any errors. + // $ajax_settings = []; + + $element['#tree'] = TRUE; + + // Build header. + $t_args = ['@label' => Unicode::ucfirst($element['#label'])]; + $header = [ + t('@label value', $t_args), + t('@label text', $t_args), + t('Weight'), + '', + ]; + + // Build rows. + $row_index = 0; + $weight = 0; + $rows = []; + if (!$form_state->isRebuilding() && isset($element['#default_value']) && is_array($element['#default_value'])) { + foreach ($element['#default_value'] as $value => $label) { + $rows[$row_index] = self::buildOptionRow($table_id, $row_index, $value, $label, $weight++, $ajax_settings); + $row_index++; + } + } + while ($row_index < $number_of_options) { + $rows[$row_index] = self::buildOptionRow($table_id, $row_index, '', '', $weight++, $ajax_settings); + $row_index++; + } + + // Build table. + $element['options'] = [ + '#prefix' => '
', + '#suffix' => '
', + '#type' => 'table', + '#header' => $header, + '#tabledrag' => [ + [ + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'yamlform-options-sort-weight', + ], + ], + ] + $rows; + + // Build add options actions. + $element['add'] = [ + '#prefix' => '
', + '#suffix' => '
', + ]; + $element['add']['submit'] = [ + '#type' => 'submit', + '#value' => t('Add'), + '#limit_validation_errors' => array(), + '#submit' => [[get_called_class(), 'addOptionsSubmit']], + '#ajax' => $ajax_settings, + '#name' => $table_id . '-add', + ]; + $element['add']['more_options'] = [ + '#type' => 'number', + '#min' => 1, + '#max' => 100, + '#default_value' => $element['#empty_options'], + '#field_suffix' => t('more @labels', ['@labels' => $element['#labels']]), + ]; + + // Add validate callback that extracts the associative array of options. + $element['#element_validate'] = [[get_called_class(), 'validateYamlFormOptions']]; + + $element['#attached']['library'][] = 'yamlform/yamlform.element.options'; + + // Wrap this $element in a
that handle #states. + YamlFormElementHelper::fixStates($element); + + return $element; + } + + /** + * Build an option's row that contains the options value, text, and weight. + * + * @param string $table_id + * The option element's table id. + * @param int $row_index + * The option's row index. + * @param string $value + * The option's value. + * @param string $text + * The option's text. + * @param int $weight + * The option's weight. + * @param array $ajax_settings + * An array containing AJAX callback settings. + * + * @return array + * A render array containing inputs for an option's value, text, and weight. + */ + public static function buildOptionRow($table_id, $row_index, $value, $text, $weight, array $ajax_settings) { + $row = []; + $row['value'] = [ + '#type' => 'textfield', + '#title' => t('Option value'), + '#title_display' => 'invisible', + '#size' => 25, + '#placeholder' => t('Enter value'), + '#default_value' => $value, + ]; + $row['text'] = [ + '#type' => 'textfield', + '#title' => t('Option text'), + '#title_display' => 'invisible', + '#size' => 25, + '#placeholder' => t('Enter text'), + '#default_value' => $text, + ]; + $row['weight'] = [ + '#type' => 'weight', + '#delta' => 1000, + '#title' => t('Option weight'), + '#title_display' => 'invisible', + '#attributes' => [ + 'style' => 'width:4em', + 'class' => ['yamlform-options-sort-weight'], + ], + '#default_value' => $weight, + ]; + + $row['remove'] = [ + '#type' => 'image_button', + '#src' => 'core/misc/icons/787878/ex.svg', + '#limit_validation_errors' => array(), + '#submit' => [[get_called_class(), 'removeOptionSubmit']], + '#ajax' => $ajax_settings, + // Issue #1342066 Document that buttons with the same #value need a unique + // #name for the form API to distinguish them, or change the form API to + // assign unique #names automatically + '#row_index' => $row_index, + '#name' => $table_id . '_remove_' . $row_index, + ]; + + $row['#weight'] = $weight; + $row['#attributes']['class'][] = 'draggable'; + return $row; + } + + /****************************************************************************/ + // Callbacks. + /****************************************************************************/ + + /** + * Form submission handler for adding more options. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public static function addOptionsSubmit(array &$form, FormStateInterface $form_state) { + // Get the YAML form options element by going up two levels. + $button = $form_state->getTriggeringElement(); + $element =& NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2)); + + // Add more options to the number of options. + $number_of_options_storage_key = self::getStorageKey($element, 'number_of_options'); + $number_of_options = $form_state->get($number_of_options_storage_key); + $more_options = (int) $element['add']['more_options']['#value']; + $form_state->set($number_of_options_storage_key, $number_of_options + $more_options); + + // Reset values. + $element['options']['#value'] = array_values($element['options']['#value']); + $form_state->setValueForElement($element['options'], $element['options']['#value']); + NestedArray::setValue($form_state->getUserInput(), $element['options']['#parents'], $element['options']['#value']); + + // Rebuild the form. + $form_state->setRebuild(); + } + + /** + * Form submission handler for adding more options. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public static function removeOptionSubmit(array &$form, FormStateInterface $form_state) { + $button = $form_state->getTriggeringElement(); + $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -3)); + + // Remove options. + unset($element['options']['#value'][$button['#row_index']]); + + // Remove one option from the 'number of options'. + $number_of_options_storage_key = self::getStorageKey($element, 'number_of_options'); + $number_of_options = $form_state->get($number_of_options_storage_key); + $form_state->set($number_of_options_storage_key, $number_of_options - 1); + + // Reset values. + $element['options']['#value'] = array_values($element['options']['#value']); + $form_state->setValueForElement($element['options'], $element['options']['#value']); + NestedArray::setValue($form_state->getUserInput(), $element['options']['#parents'], $element['options']['#value']); + + // Rebuild the form. + $form_state->setRebuild(); + } + + /** + * Form submission AJAX callback the returns the options table. + */ + public function ajaxCallback(array &$form, FormStateInterface $form_state) { + $button = $form_state->getTriggeringElement(); + $parent_length = (isset($button['#row_index'])) ? -3 : -2; + $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, $parent_length)); + return $element['options']; + } + + /** + * Validates YAML form options element. + */ + public static function validateYamlFormOptions(&$element, FormStateInterface $form_state, &$complete_form) { + if (isset($element['options']['#value']) && is_string($element['options']['#value'])) { + $options = Yaml::decode($element['options']['#value']); + } + else { + // Collect the option values in a sortable array. + $values = []; + foreach (Element::children($element['options']) as $child_key) { + $row = $element['options'][$child_key]; + $values[] = [ + 'value' => $row['value']['#value'], + 'text' => $row['text']['#value'], + 'weight' => $row['weight']['#value'], + ]; + } + + // Sort the option values. + uasort($values, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']); + + // Convert values to options. + $options = self::convertValuesToOptions($values); + + } + + // Validate required options. + if (!empty($element['#required']) && empty($options)) { + $form_state->setError($element, t('@name field is required.', array('@name' => $element['#title']))); + return $element; + } + + // Clear the element's value by setting it to NULL. + $form_state->setValueForElement($element, NULL); + + // Now, set the sorted options as the element's value. + $form_state->setValueForElement($element, $options); + + return $element; + } + + /****************************************************************************/ + // Helper functions. + /****************************************************************************/ + + /** + * Get unique key used to store the number of options for an element. + * + * @param array $element + * An element. + * + * @return string + * A unique key used to store the number of options for an element. + */ + public static function getStorageKey(array $element, $name) { + return 'yamlform_options__' . $element['#name'] . '__' . $name; + } + + /** + * Convert an array containing of option value, text, and weight to an associative array of options. + * + * @param array $values + * An array containing of option value, text, and weight. + * + * @return array + * An associative array of options. + */ + public static function convertValuesToOptions(array $values) { + // Sort the option values. + uasort($values, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']); + + // Now build the associative array of options. + $options = []; + foreach ($values as $value) { + $option_value = $value['value']; + $option_text = $value['text']; + // Skip completely empty options. + if ($option_value === '' && $option_text === '') { + continue; + } + + // Populate empty option value or option text. + if ($option_value === '') { + $option_value = $option_text; + } + elseif ($option_text === '') { + $option_text = $option_value; + } + + $options[$option_value] = $option_text; + } + + return $options; + } + + /** + * Determine if options array contains an OptGroup. + * + * @param array $options + * An array of options. + * + * @return bool + * TRUE if options array contains an OptGroup. + */ + public static function hasOptGroup(array $options) { + foreach ($options as $option_value => $option_text) { + if (is_array($option_text)) { + return TRUE; + } + } + return FALSE; + } + +} diff --git a/src/Plugin/YamlFormElement/YamlFormLikert.php b/src/Plugin/YamlFormElement/YamlFormLikert.php index 96e8b34..3a183a5 100644 --- a/src/Plugin/YamlFormElement/YamlFormLikert.php +++ b/src/Plugin/YamlFormElement/YamlFormLikert.php @@ -302,10 +302,10 @@ class YamlFormLikert extends YamlFormElementBase { '#open' => TRUE, ]; $form['likert']['questions'] = [ - '#title' => $this->t('Questions (YAML)'), - '#type' => 'yamlform_codemirror', - '#mode' => 'yaml', - '#description' => $this->t('Key-value pairs MUST be specified as "safe_key: \'Some readable @type\'". Use of only alphanumeric characters and underscores is recommended in keys. One @type per line.', ['@type' => 'question']), + '#title' => $this->t('Questions'), + '#type' => 'yamlform_options', + '#label' => $this->t('question'), + '#labels' => $this->t('questions'), '#required' => TRUE, ]; $form['likert']['answers'] = [ diff --git a/src/YamlFormEntitySettingsForm.php b/src/YamlFormEntitySettingsForm.php index eb63fea..ab4df75 100644 --- a/src/YamlFormEntitySettingsForm.php +++ b/src/YamlFormEntitySettingsForm.php @@ -476,15 +476,19 @@ class YamlFormEntitySettingsForm extends EntityForm { /** @var \Drupal\yamlform\YamlFormInterface $yamlform */ $yamlform = $this->getEntity(); - // Remove main properties. - unset( - $values['id'], - $values['title'], - $values['description'], - $values['template'], - $values['status'], - $values['uid'] - ); + /* Set properties and remove them from the values array. */ + + // Set template. + $yamlform->set('template', (bool) $values['template']); + unset($values['template']); + + // Set status. + $yamlform->set('status', $values['status']); + unset($values['status']); + + // Set owner. + $yamlform->set('uid', (int) $values['uid']); + unset($values['uid']); // Set settings and save the form. $yamlform->setSettings($values)->save(); diff --git a/tests/modules/yamlform_test/config/install/yamlform.yamlform.test_element_options.yml b/tests/modules/yamlform_test/config/install/yamlform.yamlform.test_element_options.yml new file mode 100644 index 0000000..a6ac8a8 --- /dev/null +++ b/tests/modules/yamlform_test/config/install/yamlform.yamlform.test_element_options.yml @@ -0,0 +1,124 @@ +langcode: en +status: true +dependencies: { } +uid: null +template: false +id: test_element_options +title: 'Test: Element: Options' +description: 'Test options element.' +elements: | + yamlform_options_elements: + '#type': details + '#title': 'YAML form options' + '#open': true + yamlform_options: + '#type': yamlform_options + '#title': 'yamlform_options' + yamlform_options_default_value: + '#type': yamlform_options + '#title': 'yamlform_options (default_value)' + '#default_value': + one: One + two: Two + three: Three + yamlform_options_optgroup: + '#type': yamlform_options + '#title': 'yamlform_options (optgroup)' + '#required': true + '#default_value': + 'Group One': + one: One + 'Group Two': + two: Two + 'Group Three': + three: Three + yamlform_element_options_elements: + '#type': details + '#title': 'YAML form element options' + '#open': true + yamlform_element_options_entity: + '#type': yamlform_element_options + '#title': 'yamlform_element_options (entity)' + '#required': true + '#default_value': 'yes_no' + yamlform_element_options_custom: + '#type': yamlform_element_options + '#title': 'yamlform_element_options (custom)' + '#required': true + '#default_value': + one: One + two: Two + three: Three +settings: + page: true + page_submit_path: '' + page_confirm_path: '' + form_closed_message: '' + form_exception_message: '' + form_submit_label: '' + form_confidential: false + form_confidential_message: '' + form_prepopulate: false + form_novalidate: false + wizard_progress_bar: true + wizard_progress_pages: false + wizard_progress_percentage: false + wizard_prev_button_label: '' + wizard_next_button_label: '' + wizard_complete: true + wizard_start_label: '' + wizard_complete_label: '' + preview: 0 + preview_next_button_label: '' + preview_prev_button_label: '' + preview_message: '' + draft: false + draft_auto_save: false + draft_button_label: '' + draft_saved_message: '' + draft_loaded_message: '' + confirmation_type: message + confirmation_message: '' + confirmation_url: '' + limit_total: null + entity_limit_total: null + limit_total_message: '' + limit_user: null + entity_limit_user: null + limit_user_message: '' + results_disabled: true +access: + create: + roles: + - anonymous + - authenticated + users: { } + view_any: + roles: { } + users: { } + update_any: + roles: { } + users: { } + delete_any: + roles: { } + users: { } + purge_any: + roles: { } + users: { } + view_own: + roles: { } + users: { } + update_own: + roles: { } + users: { } + delete_own: + roles: { } + users: { } +handlers: + debug: + id: debug + label: Debug + handler_id: debug + status: true + weight: 0 + settings: { } diff --git a/yamlform.install b/yamlform.install index bdb27c3..5b29d39 100644 --- a/yamlform.install +++ b/yamlform.install @@ -836,18 +836,3 @@ function yamlform_update_8036() { \Drupal::configFactory()->getEditable($yamlform_config_name)->setData($data)->save(); } } - -/** - * Issue #2781713: Remove id, title, and description from setting. - */ -function yamlform_update_8037() { - // Update 'yamlform.yamlform.*' configuration. - $config_factory = \Drupal::configFactory(); - foreach ($config_factory->listAll('yamlform.yamlform.') as $yamlform_config_name) { - $yamlform_config = $config_factory->getEditable($yamlform_config_name); - $yamlform_config->clear('settings.id'); - $yamlform_config->clear('settings.title'); - $yamlform_config->clear('settings.description'); - $yamlform_config->save(TRUE); - } -} diff --git a/yamlform.libraries.yml b/yamlform.libraries.yml index b4a8bb3..fda3eec 100755 --- a/yamlform.libraries.yml +++ b/yamlform.libraries.yml @@ -141,6 +141,12 @@ yamlform.element.likert: component: css/yamlform.element.likert.css: {} +yamlform.element.options: + version: 1.x + css: + component: + css/yamlform.element.options.css: {} + yamlform.element.rating: version: 1.x css: