Add headers and "Show more templates" button to template dropdown#1215
Conversation
|
It looks like these changes are breaking a test https://github.com/portagenetwork/roadmap/actions/runs/18754947740/job/53504332739?pr=1215. This will be a heavily used feature, so it might be wise to also add a test to verify the headers and new dropdown behaviour. The new test could probably be added in |
c058e00 to
e4294ad
Compare
There was a problem hiding this comment.
I wonder if we should address the following:
After selecting an organisation, the select template dropdown is rendered. The "Please select a template" instruction is selected as the default option and I'm able to click "Create plan" with that default option. Doing so re-renders the "Create plan" page, as well as the flash message depicted in the second screenshot.
The error handling is doing what it should, but it might be even better to keep the keep the "Create plan" button disabled to start?
Yes, I noticed that too and think it's worth addressing. The previous behaviour did make sure the "Create plan" button was disabled until a template was picked. |
I added a fix here that should address this. The issue was that the template dropdown was not resetting the value and menu items after the org was changed or the "No research organisation" checkbox was toggled. |
- select_tag has an issue where a dropdown cannot be kept open after a button is clicked. In order for the "show more templates" button to eventually work intuitively, this behaviour has to be changed. - Bootstrap dropdowns can be kept open after a certain selection, and so that was the chosen replacement.
- Edit toggleSubmit() so that both select tags and bootstrap dropdowns can still work with the function. The previous behaviour only worked with select tags. - Edit the success() function to work with the bootstrap dropdown.
`Rails.application.config.default_funder_id` is used multiple times in this controller, so defining a variable for it is best practice.
- Templates will need to be tagged appropriately to allow for organization into groups with headers in the template dropdown. - The tag_templates function is used to add a source tag to each template.
- All possible error conditions are checked at the beginning to simplify conditional logic. - `templates` is defined as a variable for `data.templates` - `only` variable is renamed to `only_template`
- Define arrays for ordering template groups - Add appendGroup helper function to add templates to proper groups
- Add the `showMore` button if there are any remaining templates that can be displayed - Add each template to a hiddenContainer that is displayed after the button is clicked
- Four helper functions were added to break up success() - `categorizeTemplates()` - separates templates by their source - `createTemplateItem()` - creates clickable template item - `appendGroup()` - appends templates into appropriate groups - `appendShowMoreSection()` - adds "Show more templates" button and hidden container to dropdown
@use 'template_dropdown_menu'; already exists in app/assets/stylesheets/dmp-assistant/blocks/_index.scss
- The test was failing since it was using the old select behaviour. It has been fixed by making sure the test opens the dropdown and finds the correct CSS section. - The header for organisations should be 'organiSational templates' not 'organisZational templates'
- Moved 'Rails.application.config.default_funder_id = @funding_org.id' such that @funding_org.id and the default_funder_id are the same in the test - Added another funder template to test "show more templates" - Tested for headers appearing correctly in dropdown
- If no template is selected, tmplt has not value and thus this condition can be used to prevent the 'Create Plan' button from being clickable until a template has been selected. - Defined submitButton to improve readability of the function.
- Add e.preventDefault() inside createTemplateItem. Without this, selecting a template might result in the browser jumping back to the top of the "Create plan" page. - Assign const templates = data.templates; such that there is no way it can throw an error. - Rename only_template to follow camelCase
- The function resetTemplateDropdown() resets the template dropdown's value and menu items. - It will be used when the 'No research organisation' checkbox is toggled and when the org is changed in the 'Create Plan' page
913c6ac to
f631669
Compare
1c71f0c to
4525c42
Compare
Simplify template handling by returning a structured JSON payload:
- Backend now returns:
{
templates: {
org_templates: [...],
priority_templates: [...],
other_templates: [...]
},
total_templates: <count>
}
- Eliminates the need to tag each template individually with a source.
- Frontend no longer needs to categorize templates manually.
- Minor frontend adjustments made to consume the new payload structure.
These changes address an unnecessary number of templates being created inside the `before do` block. Some redundancies have been removed from the creation of `@extra_funder_template` as well.
- The previous hardcoded max-height was not working properly, as sometimes the dropdown's max height would extend past the footer. - Helper function adjustDropdownMaxHeight() was added to dynamically assign the max-height to always be the middle of the footer. - Function is called in success() after template dropdown becomes visible (as helper cannot work without the values from the DOM)
- Removed select_tag-specific code from toggleSubmit:
- No longer checks `:selected` since `plan_template_id` is now a hidden field.
- Removed dead lines related to the old select_tag:
- `$('#plan_template_id').find(':selected').removeAttr('selected')`
- `$('#plan_template_id option').remove()`
- Simplifies and clarifies template selection handling with the new dropdown implementation.
- Removed `#research-org-controls input.autocomplete-result` change handler: - Its functionality is already handled by `handleComboboxChange`. - Avoids unnecessary JSON parsing and constant firing on every keystroke. - Added `resetTemplateDropdown()` call inside `handleComboboxChange`: - Simplifies the function by removing explicit lines and instead handling them conditionally inside `resetTemplateDropdown()` - Consolidates template dropdown reset logic in a single place, reducing redundant event handlers.
Cache redundant DOM queries with variables (In adjustDropdownMaxHeight) Replace redundant DOM queries with unwrapping cached jQuery objects using `.get(0)` (https://api.jquery.com/get/).
Instead of binding click handlers to each individual template item, this change creates a single delegated handler on `templateDropdownMenu`. - Listens at the container level; clicks on child elements bubble up to the handler. - Adds `data-title` attribute to template items. (Allows us to still set `templateDropdown.text(template.title)`) - Improves performance and reduces memory usage.
- Consolidate all template dropdown height logic into `adjustDropdownMaxHeight()`. - Simplifies `success()` by removing inline dropdown sizing logic.
bb74ce1 to
410ccb8
Compare
- Stop using raw HTML templates for dropdown items. - Use jQuery’s attribute map and `.text()` to properly escape content and keep `data-*` attributes on the element.
Refactored templateDropdown strings to support in-app translation. - These values must be defined and translated on the Rails side first.
- Renamed `adjustDropdownMaxHeight()` to `resizeTemplateDropdownMenu()` - Set templateDropdownMenu.outerWidth(templateDropdown.outerWidth()) to match dropdown button width, preventing resize when "Show more..." is clicked - Simplified the `isVisible` check - Updated template dropdown button ID and references from `templateDropdown` to `template-dropdown` for consistency with other selectors
410ccb8 to
6393023
Compare
| padding: 0.5rem 0.75rem; | ||
| cursor: pointer; | ||
| } | ||
| } |
There was a problem hiding this comment.
This is just a comment / possible future to-do:
I prefer this styling over what currently exists for the other dropdowns. It might be best to eventually generalise some of these ideas to all of the other dropdowns, dropdown items, etc.
| const resizeTemplateDropdownMenu = () => { | ||
| const templateDropdownEl = templateDropdown.get(0); | ||
| const templateDropdownMenuEl = templateDropdownMenu.get(0); | ||
| const footerEl = document.getElementById('footer-navbar'); | ||
|
|
||
| // Return early if any required element is missing or dropdown is not visible | ||
| if (!(templateDropdownEl && templateDropdownMenuEl && footerEl && templateDropdown.is(':visible'))) return; | ||
|
|
||
| // Set width | ||
| templateDropdownMenu.outerWidth(templateDropdown.outerWidth()); | ||
|
|
||
| // Set maxHeight | ||
| const dropdownRect = templateDropdownEl.getBoundingClientRect(); | ||
| const footerRect = footerEl.getBoundingClientRect(); | ||
|
|
||
| // Ideal location for dropdown to end is middle of the footer | ||
| const footerMiddle = (footerRect.bottom + footerRect.top) / 2; | ||
|
|
||
| const spaceAvailable = footerMiddle - dropdownRect.bottom; | ||
|
|
||
| if (spaceAvailable > 0) { | ||
| templateDropdownMenuEl.style.maxHeight = `${spaceAvailable}px`; | ||
| } | ||
| }; |
There was a problem hiding this comment.
This is a good addition. Other dropdowns could also benefit from this extra handling. In the future, we could bind the resizing to window.resize so it adjusts automatically.
`#available-templates` was momentarily visible before JavaScript hid it. Set it hidden in the markup (`display: none`) to ensure it remains invisible until shown intentionally. This eliminates the visual flash during page load.
- Added isValidTemplateResponse helper to validate the structure and content of AJAX template responses. - Checks now ensure `data.total_templates` is a number greater than 0 and that each template group (`org_templates`, `priority_templates`, `other_templates`) is an array. - Added a guard to ensure onlyTemplate is defined before accessing its properties. - Improves safety against malformed or unexpected server responses.
- Add extractTemplateData() helper to handle destructuring of total_templates and template groups (org_templates, priority_templates, other_templates). - Now, rather than dealing with the template response structure, success() works with clearly defined variables returned from extractTemplateData().
Restrict templates that can be associated with newly created plans. Only templates that are published and unarchived are now allowed.
…ation-on-plan-creation Update template validation on plan creation



Fixes #1196.
Changes proposed in this PR:
select_tagto a bootstrapdropdownin order to allow for the "Show more templates" button to be clicked without closing the dropdown.templation_options_controller(previously edited in this PR) to simplify template handling by returning a structured JSON payload.plans_controllerto restrict templates that can be associated with newly created plans. Now, only templates that are published and unarchived are allowed.Screencast.from.2025-11-12.11-23-36.webm