diff --git a/config/sync/core.entity_form_display.paragraph.registration_tool.default.yml b/config/sync/core.entity_form_display.paragraph.registration_tool.default.yml index ab910fcb9..72acc2da9 100644 --- a/config/sync/core.entity_form_display.paragraph.registration_tool.default.yml +++ b/config/sync/core.entity_form_display.paragraph.registration_tool.default.yml @@ -3,24 +3,15 @@ langcode: en status: true dependencies: config: - - field.field.paragraph.registration_tool.field_dropdown_label - field.field.paragraph.registration_tool.field_form_heading - field.field.paragraph.registration_tool.field_heading - - field.field.paragraph.registration_tool.field_submit_button_label + - field.field.paragraph.registration_tool.field_placeholder - paragraphs.paragraphs_type.registration_tool id: paragraph.registration_tool.default targetEntityType: paragraph bundle: registration_tool mode: default content: - field_dropdown_label: - type: string_textfield - weight: 2 - region: content - settings: - size: 60 - placeholder: '' - third_party_settings: { } field_form_heading: type: string_textfield weight: 1 @@ -37,16 +28,16 @@ content: size: 60 placeholder: '' third_party_settings: { } - field_submit_button_label: + field_placeholder: type: string_textfield - weight: 3 + weight: 2 region: content settings: size: 60 placeholder: '' third_party_settings: { } translation: - weight: 4 + weight: 3 region: content settings: { } third_party_settings: { } diff --git a/config/sync/core.entity_view_display.paragraph.registration_tool.default.yml b/config/sync/core.entity_view_display.paragraph.registration_tool.default.yml index 48dc532d2..87dbdad68 100644 --- a/config/sync/core.entity_view_display.paragraph.registration_tool.default.yml +++ b/config/sync/core.entity_view_display.paragraph.registration_tool.default.yml @@ -3,24 +3,15 @@ langcode: en status: true dependencies: config: - - field.field.paragraph.registration_tool.field_dropdown_label - field.field.paragraph.registration_tool.field_form_heading - field.field.paragraph.registration_tool.field_heading - - field.field.paragraph.registration_tool.field_submit_button_label + - field.field.paragraph.registration_tool.field_placeholder - paragraphs.paragraphs_type.registration_tool id: paragraph.registration_tool.default targetEntityType: paragraph bundle: registration_tool mode: default content: - field_dropdown_label: - type: string - label: hidden - settings: - link_to_entity: false - third_party_settings: { } - weight: 2 - region: content field_form_heading: type: string label: hidden @@ -37,12 +28,12 @@ content: third_party_settings: { } weight: 0 region: content - field_submit_button_label: + field_placeholder: type: string label: hidden settings: link_to_entity: false third_party_settings: { } - weight: 3 + weight: 2 region: content hidden: { } diff --git a/config/sync/field.field.paragraph.registration_tool.field_form_heading.yml b/config/sync/field.field.paragraph.registration_tool.field_form_heading.yml index 6bf2ef2fe..f43d1f681 100644 --- a/config/sync/field.field.paragraph.registration_tool.field_form_heading.yml +++ b/config/sync/field.field.paragraph.registration_tool.field_form_heading.yml @@ -9,7 +9,7 @@ id: paragraph.registration_tool.field_form_heading field_name: field_form_heading entity_type: paragraph bundle: registration_tool -label: 'Form heading' +label: 'Field Label' description: '' required: true translatable: true diff --git a/config/sync/field.field.paragraph.registration_tool.field_dropdown_label.yml b/config/sync/field.field.paragraph.registration_tool.field_placeholder.yml similarity index 57% rename from config/sync/field.field.paragraph.registration_tool.field_dropdown_label.yml rename to config/sync/field.field.paragraph.registration_tool.field_placeholder.yml index 2c6182ff7..dccf61a37 100644 --- a/config/sync/field.field.paragraph.registration_tool.field_dropdown_label.yml +++ b/config/sync/field.field.paragraph.registration_tool.field_placeholder.yml @@ -1,15 +1,15 @@ -uuid: 5e35facf-9d13-428e-b66e-94ae072633a5 +uuid: bf81dc6a-e290-4bda-9c22-4790a0b0055a langcode: en status: true dependencies: config: - - field.storage.paragraph.field_dropdown_label + - field.storage.paragraph.field_placeholder - paragraphs.paragraphs_type.registration_tool -id: paragraph.registration_tool.field_dropdown_label -field_name: field_dropdown_label +id: paragraph.registration_tool.field_placeholder +field_name: field_placeholder entity_type: paragraph bundle: registration_tool -label: 'Dropdown label' +label: Placeholder description: '' required: true translatable: true diff --git a/config/sync/field.field.paragraph.registration_tool.field_submit_button_label.yml b/config/sync/field.field.paragraph.registration_tool.field_submit_button_label.yml deleted file mode 100644 index b57c124aa..000000000 --- a/config/sync/field.field.paragraph.registration_tool.field_submit_button_label.yml +++ /dev/null @@ -1,19 +0,0 @@ -uuid: 0efabcd4-3307-46a0-a889-d25954d32662 -langcode: en -status: true -dependencies: - config: - - field.storage.paragraph.field_submit_button_label - - paragraphs.paragraphs_type.registration_tool -id: paragraph.registration_tool.field_submit_button_label -field_name: field_submit_button_label -entity_type: paragraph -bundle: registration_tool -label: 'Submit button label' -description: '' -required: true -translatable: true -default_value: { } -default_value_callback: '' -settings: { } -field_type: string diff --git a/config/sync/field.storage.paragraph.field_dropdown_label.yml b/config/sync/field.storage.paragraph.field_placeholder.yml similarity index 72% rename from config/sync/field.storage.paragraph.field_dropdown_label.yml rename to config/sync/field.storage.paragraph.field_placeholder.yml index e047c38ec..edd2404e8 100644 --- a/config/sync/field.storage.paragraph.field_dropdown_label.yml +++ b/config/sync/field.storage.paragraph.field_placeholder.yml @@ -1,11 +1,11 @@ -uuid: 95c1bafc-87b1-453b-a65b-845186b30004 +uuid: e6b21b32-160e-4cd9-b228-6bf2352a4d85 langcode: en status: true dependencies: module: - paragraphs -id: paragraph.field_dropdown_label -field_name: field_dropdown_label +id: paragraph.field_placeholder +field_name: field_placeholder entity_type: paragraph type: string settings: diff --git a/config/sync/field.storage.paragraph.field_submit_button_label.yml b/config/sync/field.storage.paragraph.field_submit_button_label.yml deleted file mode 100644 index ffd2e239c..000000000 --- a/config/sync/field.storage.paragraph.field_submit_button_label.yml +++ /dev/null @@ -1,21 +0,0 @@ -uuid: 83f1e755-433e-45aa-98c6-b01df3bae159 -langcode: en -status: true -dependencies: - module: - - paragraphs -id: paragraph.field_submit_button_label -field_name: field_submit_button_label -entity_type: paragraph -type: string -settings: - max_length: 255 - case_sensitive: false - is_ascii: false -module: core -locked: false -cardinality: 1 -translatable: true -indexes: { } -persist_with_no_fields: false -custom_storage: false diff --git a/config/sync/views.view.nvrf_api_taxonomy.yml b/config/sync/views.view.nvrf_api_taxonomy.yml index 2dbd0875d..3bbf54071 100644 --- a/config/sync/views.view.nvrf_api_taxonomy.yml +++ b/config/sync/views.view.nvrf_api_taxonomy.yml @@ -9,6 +9,7 @@ dependencies: - field.storage.taxonomy_term.field_instructions - field.storage.taxonomy_term.field_nvrf_machine_name - field.storage.taxonomy_term.field_options + - field.storage.taxonomy_term.field_section_alert - field.storage.taxonomy_term.field_section_description - taxonomy.vocabulary.nvrf_field - user.role.authenticated @@ -608,6 +609,68 @@ display: multi_type: separator separator: ', ' field_api_classes: false + field_section_alert: + id: field_section_alert + table: taxonomy_term__field_section_alert + field: field_section_alert + relationship: none + group_type: group + admin_label: '' + plugin_id: field + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: text_default + settings: { } + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false field_options_export: id: field_options_export table: taxonomy_term__field_options @@ -821,6 +884,7 @@ display: - 'config:field.storage.taxonomy_term.field_instructions' - 'config:field.storage.taxonomy_term.field_nvrf_machine_name' - 'config:field.storage.taxonomy_term.field_options' + - 'config:field.storage.taxonomy_term.field_section_alert' - 'config:field.storage.taxonomy_term.field_section_description' api_nvrf_nvrf_fields: id: api_nvrf_nvrf_fields @@ -868,6 +932,9 @@ display: field_section_description: alias: section_description raw_output: false + field_section_alert: + alias: section_alert + raw_output: false field_options_export: alias: options raw_output: false @@ -888,4 +955,5 @@ display: - 'config:field.storage.taxonomy_term.field_instructions' - 'config:field.storage.taxonomy_term.field_nvrf_machine_name' - 'config:field.storage.taxonomy_term.field_options' + - 'config:field.storage.taxonomy_term.field_section_alert' - 'config:field.storage.taxonomy_term.field_section_description' diff --git a/config/sync/views.view.nvrf_reports.yml b/config/sync/views.view.nvrf_reports.yml index cb6459e53..7f5e53f64 100644 --- a/config/sync/views.view.nvrf_reports.yml +++ b/config/sync/views.view.nvrf_reports.yml @@ -8,6 +8,7 @@ dependencies: - field.storage.taxonomy_term.field_help_text - field.storage.taxonomy_term.field_instructions - field.storage.taxonomy_term.field_options + - field.storage.taxonomy_term.field_section_alert - field.storage.taxonomy_term.field_section_description - taxonomy.vocabulary.nvrf_field - user.role.authenticated @@ -488,6 +489,68 @@ display: multi_type: separator separator: ', ' field_api_classes: false + field_section_alert: + id: field_section_alert + table: taxonomy_term__field_section_alert + field: field_section_alert + relationship: none + group_type: group + admin_label: '' + plugin_id: field + label: 'Section Alert' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: text_default + settings: { } + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false edit_taxonomy_term: id: edit_taxonomy_term table: taxonomy_term_data @@ -688,6 +751,7 @@ display: - 'config:field.storage.taxonomy_term.field_help_text' - 'config:field.storage.taxonomy_term.field_instructions' - 'config:field.storage.taxonomy_term.field_options' + - 'config:field.storage.taxonomy_term.field_section_alert' - 'config:field.storage.taxonomy_term.field_section_description' page_1: id: page_1 @@ -710,4 +774,5 @@ display: - 'config:field.storage.taxonomy_term.field_help_text' - 'config:field.storage.taxonomy_term.field_instructions' - 'config:field.storage.taxonomy_term.field_options' + - 'config:field.storage.taxonomy_term.field_section_alert' - 'config:field.storage.taxonomy_term.field_section_description' diff --git a/config/sync/views.view.nvrf_state_reports.yml b/config/sync/views.view.nvrf_state_reports.yml index 0ffe5c992..96b752a38 100644 --- a/config/sync/views.view.nvrf_state_reports.yml +++ b/config/sync/views.view.nvrf_state_reports.yml @@ -1048,10 +1048,37 @@ display: offset: false offset_label: Offset quantity: 9 + filters: + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '' + type: + id: type + table: node_field_data + field: type + entity_type: node + entity_field: type + plugin_id: bundle + value: + state_territory: state_territory + filter_groups: + operator: AND + groups: + 1: AND defaults: title: false pager: false fields: false + filters: false + filter_groups: false display_extenders: { } path: admin/nvrf-state-report/fields cache_metadata: @@ -1059,7 +1086,6 @@ display: contexts: - 'languages:language_content' - 'languages:language_interface' - - url - url.query_args - 'user.node_grants:view' - user.roles diff --git a/web/themes/custom/votegov/src/js/registration-tool.js b/web/themes/custom/votegov/src/js/registration-tool.js new file mode 100644 index 000000000..ff0e7dd28 --- /dev/null +++ b/web/themes/custom/votegov/src/js/registration-tool.js @@ -0,0 +1,175 @@ +/* + Dropdown functionality for state selector component. + */ + +(() => { + const stateComboBox = document.getElementById("state-combo-box"); + const stateInput = document.getElementById("state-input"); + const stateDropdownBtn = document.getElementById("state-dropdown-btn"); + const stateResultsContainer = document.getElementById("state-results-container"); + const stateFilteredOptions = stateComboBox ? stateResultsContainer.getElementsByTagName('a') : null; + + // Store dynamic filtered results. + let stateListResults = []; + + // Show state dropdown. + function stateListShow() { + stateResultsContainer.removeAttribute('hidden'); + } + + // Hide state dropdown. + function stateListHide() { + stateResultsContainer.setAttribute('hidden', ''); + } + + // Toggle state dropdown. + function stateListToggle() { + if (!stateResultsContainer.getAttribute('data-empty')) { + stateResultsContainer.toggleAttribute('hidden'); + } + } + + // Toggle state dropdown if empty. + function stateListToggleEmpty(empty) { + if (empty) { + stateListHide(); + stateResultsContainer.setAttribute('data-empty', true); + } else { + stateListShow(); + stateResultsContainer.removeAttribute('data-empty'); + } + } + + // Filter dropdown results based on user input. + function stateListFilter() { + let filter, txtValue, wordTxtValues, keyValue; + filter = stateInput.value.toUpperCase(); + txtValue = ""; + wordTxtValues = []; + keyValue = ""; + stateListResults = []; + + for (let i = 0; i < stateFilteredOptions.length; i++) { + let li = stateFilteredOptions[i].parentNode; + txtValue = li.textContent.trim() || li.innerText.trim(); + wordTxtValues = txtValue.split(' '); + keyValue = li.firstElementChild.getAttribute('key'); + + // Match user input with the start of the state name or state abbrev. + if (wordTxtValues.some((elem) => elem.length > 2 && elem.toUpperCase().startsWith(filter)) + || (filter.length === 2 && keyValue.toUpperCase() === filter)) { + li.removeAttribute('hidden'); + stateListResults.push(stateFilteredOptions[i]); + } else { + li.setAttribute('hidden', ''); + } + } + + toggleDataFiltered(filter); + stateListToggleEmpty(!stateListResults.length); + } + + // Focus on previous option in dropdown. + function stateListPrevious(option) { + stateListResults.find((element, index) => { + if (element === option) { + if (index === 0) { + stateInput.focus(); + } + else { + stateListResults[index - 1].focus(); + } + } + }); + } + + // Focus on next option in dropdown. + function stateListNext(option) { + stateListResults.find((element, index) => { + if (element === option) { + if (index === stateListResults.length - 1) { + stateListResults[0].focus(); + } + else { + stateListResults[index + 1].focus(); + } + } + }); + } + + // Toggle data attribute on results container. + function toggleDataFiltered(filter) { + if (filter === '') { + stateResultsContainer.removeAttribute('data-filtered'); + } + else { + stateResultsContainer.setAttribute('data-filtered', 'true'); + } + } + + // Redirect user if value is an exact match to a option. + function quickLinkToState(value) { + stateListResults.find((element) => { + let resultTxt = element.textContent || element.innerText; + if (resultTxt.toUpperCase() === value) { + window.location.href = element.href; + } + }); + } + + // Initialize event listeners if combobox loaded. + if (stateComboBox) { + // Attach events for combobox component. + stateComboBox.addEventListener('focusout', (e) => { + if (!e.currentTarget.contains(e.relatedTarget)) { + stateListHide(); + } + }); + stateComboBox.addEventListener('submit', (e) => { + e.preventDefault(); + let value = stateInput.value.toUpperCase(); + quickLinkToState(value); + }) + + // Attach events for state input field. + stateInput.addEventListener('focus', stateListShow); + stateInput.addEventListener('keydown', (e) => { + if (e.key === "ArrowDown") { + stateListResults[0].focus(); + } + }); + stateInput.addEventListener('keyup', (e) => { + if (e.key !== "ArrowDown") { + stateListFilter(); + } + }); + + // Attach events for state links in dropdown. + for (let i = 0; i < stateFilteredOptions.length; i++) { + stateListResults.push(stateFilteredOptions[i]); + stateFilteredOptions[i].addEventListener('keydown', (e) => { + // stateFilteredOptions = stateResultsContainer.getElementsByTagName('a'); + if (e.key === "ArrowDown") { + e.preventDefault(); + let option = stateFilteredOptions[i]; + stateListNext(option); + } + }); + + stateFilteredOptions[i].addEventListener('keyup', (e) => { + if (e.key === "ArrowUp") { + e.preventDefault(); + let option = stateFilteredOptions[i]; + stateListPrevious(option); + } + }); + } + + // Attach events for dropdown toggle button. + stateDropdownBtn.addEventListener('click', (e) => { + e.preventDefault(); + stateListToggle(); + }); + } + +})(); diff --git a/web/themes/custom/votegov/templates/paragraph/paragraph--registration-tool.html.twig b/web/themes/custom/votegov/templates/paragraph/paragraph--registration-tool.html.twig new file mode 100644 index 000000000..adb864d80 --- /dev/null +++ b/web/themes/custom/votegov/templates/paragraph/paragraph--registration-tool.html.twig @@ -0,0 +1,43 @@ +{# +/** + * @file + * Default theme implementation to display a registration tool paragraph. + * + * @ingroup themeable + */ +#} + +{{ attach_library('votegov/registration_tool') }} + +{% block paragraph %} +
+
+

{{ content.field_heading | field_value }}

+
+ +
+ +
+ +
+ +
+
+ +
+ +
+{% endblock %} diff --git a/web/themes/custom/votegov/votegov.libraries.yml b/web/themes/custom/votegov/votegov.libraries.yml index ef0213618..8fe1311ad 100644 --- a/web/themes/custom/votegov/votegov.libraries.yml +++ b/web/themes/custom/votegov/votegov.libraries.yml @@ -25,3 +25,8 @@ external_links: version: VERSION js: dist/js/external-links.js: { minified: true } + +registration_tool: + version: VERSION + js: + dist/js/registration-tool.js: { minified: true }