Skip to content

fix: enable search in document select/multiselect editables by keeping label as string#3357

Open
daniel-travix wants to merge 3 commits intopimcore:2025.4from
daniel-travix:fix/select-editable-search-filter
Open

fix: enable search in document select/multiselect editables by keeping label as string#3357
daniel-travix wants to merge 3 commits intopimcore:2025.4from
daniel-travix:fix/select-editable-search-filter

Conversation

@daniel-travix
Copy link
Copy Markdown

Problem

pimcore_select and pimcore_multiselect render antd Select / CreatableSelect with showSearch + optionFilterProp="label", but typing into the dropdown never narrows the options - every query returns zero matches.

{{ pimcore_select('pick', {
    'store': [['a', 'Apple'], ['b', 'Banana'], ['c', 'Cherry']]
}) }}

Open in the document editor, click the dropdown, type any character: list becomes empty.

Scope: document editables only (SelectEditable, MultiSelectEditable). DataObject selects and grid cell selects are unaffected - they already keep the label as a string when there is no HTML.

Root cause

transformDocumentEditableStoreToOptions wraps every label in <SanitizeHtml>:

return {
  value: String(value),
  label: <SanitizeHtml html={ label } />   // React element, not string
}

antd filters via String(option.label).toLowerCase().includes(input). For a React element that is "[object Object]", so nothing ever matches. The transform is shared by both editables, so both are broken.

Regression from #2031 (merged 2025-09-08), which introduced <SanitizeHtml> inside the transform. No existing issue found.

Fix

Keep label as a plain string, move sanitization to render time via a shared renderSanitizedLabel helper, wire it into both consumers' labelRender / optionRender. Helper lives in utils/select-options.tsx next to the transform, so there is one place for XSS logic.

Applying render props to both editables is required: changing the transform alone would silently drop XSS protection from the multiselect, which used to rely on <SanitizeHtml> being embedded in the label.

Diff (trimmed)

utils/select-options.tsx:

-      label: <SanitizeHtml html={ label } />
+      label: String(label)

+export const renderSanitizedLabel = (label: React.ReactNode): React.JSX.Element => (
+  <SanitizeHtml html={ typeof label === 'string' ? label : String(label ?? '') } />
+)

select-editable.tsx and multiselect-editable.tsx:

+import { renderSanitizedLabel } from '../../utils/select-options'

+        labelRender={ ({ label }) => renderSanitizedLabel(label) }
         optionFilterProp="label"
+        optionRender={ (option) => renderSanitizedLabel(option.label) }

Screenshots

Test brick with four pimcore_select / pimcore_multiselect instances, typing fr in the multiselect:

Before:
select-search-failed

After:
select-search-working

Testing

Covered both transform branches (array-of-pairs and plain list) for both editables:

  • pimcore_select with [['at','Austria'], ...] and with ['Apple','Banana',...]
  • pimcore_multiselect with [['at','Austria'], ...] and with ['Apple','Banana',...]

Before: all four return 0 hits on any input. After: all four filter correctly.

XSS check with store: [['xss', '<b>X</b><script>alert(1)</script>']]: <b> renders bold, <script> dropped from DOM, no alert. Works for both editables.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 19, 2026

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

@daniel-travix daniel-travix force-pushed the fix/select-editable-search-filter branch from 0f0f10f to 975c981 Compare April 19, 2026 19:29
@daniel-travix
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@markus-moser
Copy link
Copy Markdown
Contributor

markus-moser commented Apr 20, 2026

Hi,
thanks for your fix. Could you please rebase it to our bugfix branch (2025.4)? Also created a CONTRIBUTING.md in this PR to clarify the branch for future PRs (#3361).

Thanks a lot!

The select editable rendered by pimcore_select with a store configuration
shows a search box (showSearch + optionFilterProp="label"), but typing
never narrows the options — every query returns zero matches.

Root cause: transformDocumentEditableStoreToOptions wraps every label in
<SanitizeHtml>, producing a React element. antd filters options by
String(option.label), so for a React element the comparison becomes
'[object Object]' and no user query ever matches.

Fix: keep label as a plain string so the built-in filter works; move
HTML sanitization to render time via a new renderSanitizedLabel helper,
exported alongside the transform and wired into the SelectEditable via
labelRender / optionRender. XSS protection is unchanged — the label is
still wrapped in <SanitizeHtml> at every point it becomes DOM.

The helper is exported (not defined locally in the component) so the
adjacent MultiSelectEditable, which shares the same transform, can
reuse it without duplication.
Follow-up to the previous commit. The multiselect editable uses the
same transformDocumentEditableStoreToOptions as the single select,
so after switching the transform to return plain strings, its labels
would render unsanitized without explicit render props.

Wire the shared renderSanitizedLabel helper into labelRender and
optionRender, restoring XSS protection for the multi-select case
while keeping the built-in search filter working
(optionFilterProp="label" against a plain string).
@daniel-travix daniel-travix force-pushed the fix/select-editable-search-filter branch from bd3f052 to 7f457c9 Compare April 20, 2026 12:53
@markus-moser markus-moser changed the base branch from 2026.x to 2025.4 April 20, 2026 12:57
@markus-moser markus-moser added this to the 2025.4.1 milestone Apr 20, 2026
@daniel-travix daniel-travix force-pushed the fix/select-editable-search-filter branch from 5872002 to 7f457c9 Compare April 20, 2026 13:55
@sonarqubecloud
Copy link
Copy Markdown

@daniel-travix
Copy link
Copy Markdown
Author

Thanks @markus-moser! I hadn't noticed you already changed the base branch to 2025.4 yourself - my last force-pushes were me trying to clean up the local branch and re-target 2025.4 (hence the extra pipeline runs). All good now from my end, ready when you are.

@markus-moser
Copy link
Copy Markdown
Contributor

@daniel-travix Yes, looks good now - thanks a lot. Will check it and then merge it if everything is fine!

@markus-moser markus-moser self-assigned this Apr 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants