Skip to content

feat(personas): introduce new keytag custom field implementation#34625

Open
nicobytes wants to merge 7 commits intomainfrom
34450-task-migrate-persona-key-tag-field-to-new-custom-field-api
Open

feat(personas): introduce new keytag custom field implementation#34625
nicobytes wants to merge 7 commits intomainfrom
34450-task-migrate-persona-key-tag-field-to-new-custom-field-api

Conversation

@nicobytes
Copy link
Contributor

@nicobytes nicobytes commented Feb 12, 2026

Migrate Custom Fields to New Edit Mode and Consolidate Shared Styles

Summary

This PR migrates multiple custom field implementations to the new edit mode architecture, replacing legacy Dojo-based code with modern DOM manipulation and the DotCustomFieldApi framework. Additionally, shared dropdown styles have been extracted and consolidated for reusability across field types. Comprehensive accessibility improvements and code quality fixes have been applied throughout.

Changes

Custom Field Migrations

1. Personas KeyTag Field (keytag_custom_field)

  • New Implementation: keytag_custom_field_new.vtl

    • Replaces Dojo framework with native DOM and DotCustomFieldApi
    • Implements camelCase transformation with proper casing (first letter uppercase, remaining lowercase)
    • Real-time field validation and synchronization with persona name input
    • Modern event listeners (click, blur) instead of inline handlers
    • Accessibility improvements: Added aria-label for screen readers
    • PrimeNG button styling with p-button p-button-text classes
  • Legacy Preservation: keytag_custom_field_old.vtl retained for backward compatibility

  • Switcher Logic: keytag_custom_field.vtl conditionally routes based on isNewEditModeEnabled()

2. File Browser Field (file_browser_field_render)

  • New Implementation: file_browser_field_render_new.vtl

    • Replaces legacy dijit FilteringSelect with native input and DotCustomFieldApi browser modal
    • Native file/page/folder browser integration
    • Accessibility improvements: Added placeholder for URI input field
  • Legacy Preservation: file_browser_field_render_old.vtl retained for backward compatibility

  • Switcher Logic: file_browser_field_render.vtl conditionally routes based on isNewEditModeEnabled()

3. Tag Storage Field (tag_storage_field_creation)

  • New Implementation: tag_storage_field_creation_new.vtl

    • Replaces legacy dijit dropdown with custom native select component
    • Null-safe Velocity templating (guards against missing attributes)
    • Custom dropdown with keyboard navigation and accessibility attributes (aria-label, aria-haspopup)
  • Legacy Preservation: tag_storage_field_creation_old.vtl retained for backward compatibility

  • Switcher Logic: tag_storage_field_creation.vtl (FIXED)

    • Corrected inconsistent #parse directive paths to use leading slash (/static/...) for consistency

DEMO

Screen.Recording.2026-02-13.at.5.21.12.PM.mov

### Summary
This commit adds a new implementation for the keytag custom field, enhancing the user experience by allowing dynamic updates based on the name field input.

### Changes
- **New File**: Added `keytag_custom_field_new.vtl` which includes JavaScript functions for camelizing the name input and updating the keytag field accordingly.
- **Old Implementation**: The previous keytag custom field logic has been preserved in `keytag_custom_field_old.vtl` for backward compatibility.
- **Integration**: Updated `keytag_custom_field.vtl` to conditionally parse the new or old implementation based on the `isNewEditModeEnabled` flag.

### Benefits
This new approach improves the usability of the keytag field by allowing real-time updates and editing capabilities, enhancing the overall functionality of the personas feature.
…dering to support new edit mode

### Summary
This commit refactors the file browser and tag storage field rendering templates to conditionally include new implementations based on the  flag.

### Changes
- **File Browser**: Removed old JavaScript and HTML structure from , now conditionally parses either  or .
- **Tag Storage Field**: Similar changes made in , parsing either  or .

### Benefits
This update streamlines the rendering logic for both components, allowing for easier maintenance and improved user experience in the new edit mode.
@adrianjm-dotCMS adrianjm-dotCMS marked this pull request as ready for review February 13, 2026 20:16
Copilot AI review requested due to automatic review settings February 13, 2026 20:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request migrates multiple custom fields to the new Custom Field API, implementing a conditional rendering pattern based on the edit mode. The primary focus is on the persona keytag custom field (as mentioned in issue #34450), but it also includes migrations for tag storage and file browser fields, plus a refactoring of shared dropdown styles.

Changes:

  • Migrated persona keytag custom field to new Custom Field API with modern JavaScript and styling
  • Migrated tag storage field to new Custom Field API with dropdown implementation
  • Migrated file browser field to new Custom Field API with modal integration
  • Refactored shared dropdown CSS from template_custom_field_new.vtl to native-field.component.scss

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
keytag_custom_field.vtl Main switcher that conditionally loads new/old implementation
keytag_custom_field_old.vtl Preserved legacy Dojo-based keytag implementation
keytag_custom_field_new.vtl New DotCustomFieldApi-based keytag implementation
tag_storage_field_creation.vtl Main switcher for tag storage field
tag_storage_field_creation_old.vtl Preserved legacy Dojo-based tag storage dropdown
tag_storage_field_creation_new.vtl New custom dropdown with DotCustomFieldApi integration
file_browser_field_render.vtl Main switcher for file browser field
file_browser_field_render_old.vtl Preserved legacy Dojo-based file browser
file_browser_field_render_new.vtl New modal-based file browser implementation
template_custom_field_new.vtl Removed redundant CSS styles (moved to SCSS)
native-field.component.scss Added shared dropdown/select styles for custom fields

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

DotCustomFieldApi.ready(() => {
const field = DotCustomFieldApi.getField("${field.velocityVarName}");
const vlUriInput = document.getElementById("vlUri");
const browseButton = document.getElementById("browseButton");
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null safety checks for DOM elements. The code directly uses DOM elements (vlUriInput, browseButton) retrieved via getElementById without checking if they exist. If any element is missing, calling methods like addEventListener will throw errors. For consistency with tag_storage_field_creation_new.vtl (lines 64-67) which includes null checks, add validation after line 5: if (!vlUriInput || !browseButton) return;

Suggested change
const browseButton = document.getElementById("browseButton");
const browseButton = document.getElementById("browseButton");
if (!vlUriInput || !browseButton) {
return;
}

Copilot uses AI. Check for mistakes.
}

&.selected {
background-color: #dbeafe;
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded color value instead of using SCSS variable. The line uses #dbeafe directly, but this color is defined as $color-palette-blue-tint in the codebase (core-web/libs/dotcms-scss/shared/_colors.scss:190). For consistency and maintainability, use the SCSS variable instead of the hardcoded hex value.

Suggested change
background-color: #dbeafe;
background-color: $color-palette-blue-tint;

Copilot uses AI. Check for mistakes.
line-height: 1.5;
color: $color-palette-gray-700;
background-color: $white;
border: 1px solid;
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete border declaration missing color value. The border property is set to "1px solid" without specifying a color, which will result in the browser using the default color (usually black). Based on the removed code from template_custom_field_new.vtl which had "border: 2px solid var(--color-palette-primary-500)", this should specify a border color. Consider adding a color such as "$color-palette-primary-500" to match the previous implementation and the hover/focus styles on lines 172 and 179.

Suggested change
border: 1px solid;
border: 1px solid $color-palette-primary-500;

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +126
<script type="application/javascript">
(function() {
function initTagStorageField() {
var field;
try {
field = DotCustomFieldApi.getField("tagStorage");
} catch (e) {
return;
}
var holder = document.getElementById("tagStorageHolder");
var button = document.getElementById("tagStorageSelectButton");
var dropdown = document.getElementById("tagStorageDropdown");
if (!holder || !button || !dropdown) return;

var options = dropdown.querySelectorAll(".tag-storage-custom-option");
var selectedOption = dropdown.querySelector(".tag-storage-custom-option.selected");
if (selectedOption && selectedOption.dataset.label) {
button.textContent = selectedOption.dataset.label;
button.classList.remove("placeholder");
}
if (field && selectedOption && selectedOption.dataset.identifier) {
field.setValue(selectedOption.dataset.identifier);
} else if (field) {
field.setValue("");
}

function closeDropdown() {
dropdown.classList.remove("open");
}

function openDropdown() {
dropdown.classList.add("open");
}

function toggleDropdown() {
if (button.disabled) return;
if (dropdown.classList.contains("open")) {
closeDropdown();
} else {
openDropdown();
}
}

button.addEventListener("click", function (e) {
e.stopPropagation();
toggleDropdown();
});

options.forEach(function(opt) {
opt.addEventListener("click", function () {
var identifier = this.dataset.identifier;
var label = this.dataset.label;
if (field) field.setValue(identifier || "");
button.textContent = label || "Select host";
button.classList.remove("placeholder");
options.forEach(function(o) { o.classList.remove("selected"); });
this.classList.add("selected");
this.setAttribute("aria-selected", "true");
closeDropdown();
});
});

document.addEventListener("click", function (e) {
if (holder && !holder.contains(e.target)) {
closeDropdown();
}
});
}

DotCustomFieldApi.ready(initTagStorageField);
})();
</script>
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing keyboard navigation implementation. The PR description claims "Custom dropdown with keyboard navigation and accessibility attributes" for the tag storage field, but the implementation does not include any keyboard event listeners (e.g., Enter, Escape, Arrow keys). While the dropdown has ARIA attributes (role="listbox", role="option", aria-selected), users cannot navigate or select options using the keyboard. Consider adding keyboard event handlers similar to standard accessible dropdown patterns, such as Enter/Space to toggle, Escape to close, and Arrow keys to navigate options.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +29
<button
type="button"
id="tagStorageSelectButton"
class="custom-select-button tag-storage-custom-select-button placeholder"
aria-label="Select tag storage host"
aria-haspopup="listbox"
#if($isCopyingHost && $UtilMethods.isSet($tagStorageFromURL)) disabled="disabled" #end
>Select host</button>
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing aria-expanded attribute for dropdown button. The button has aria-haspopup="listbox" but is missing the aria-expanded attribute, which is required to indicate whether the dropdown is currently open or closed to screen reader users. The attribute should be set to "false" initially and toggled to "true"/"false" in the openDropdown() and closeDropdown() functions (lines 81-87). For example: button.setAttribute("aria-expanded", "true") when opening and button.setAttribute("aria-expanded", "false") when closing.

Copilot uses AI. Check for mistakes.
const showKeyTagInput = document.getElementById("showKeyTag");
const keyTagWrapper = document.getElementById("keyTagWrapper");
const editButton = document.getElementById("keyTagEditButton");

Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null safety checks for DOM elements. The code directly uses DOM elements (showKeyTagInput, keyTagWrapper, editButton) retrieved via getElementById without checking if they exist. If any element is missing, calling methods like addEventListener or classList operations will throw errors. For consistency with tag_storage_field_creation_new.vtl (lines 64-67) which includes null checks, add validation: if (!showKeyTagInput || !keyTagWrapper || !editButton) return;

Suggested change
if (!showKeyTagInput || !keyTagWrapper || !editButton) {
return;
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[TASK] Migrate Persona Key Tag Field to new Custom Field API

2 participants