feat(scaffolder): add new ui:options autoSelect tag (#32912)

* feat(scaffolder): add new ui:autoSelect tag

Signed-off-by: Jonathan Sundquist <jonathan.sundquist@factset.com>

* Update .changeset/long-hairs-throw.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Jonathan Sundquist <jsundquist@gmail.com>
Signed-off-by: Jonathan Sundquist <jonathan.sundquist@factset.com>

* Adding missing annotations in the schema file

Signed-off-by: Jonathan Sundquist <jonathan.sundquist@factset.com>

* Adding missing tests and documentation

Signed-off-by: Jonathan Sundquist <jonathan.sundquist@factset.com>

* adding in api report files

Signed-off-by: Jonathan Sundquist <jonathan.sundquist@factset.com>

* Fixing tests and ensure all required files have the necessary updates

Signed-off-by: Jonathan Sundquist <jonathan.sundquist@factset.com>

---------

Signed-off-by: Jonathan Sundquist <jonathan.sundquist@factset.com>
Signed-off-by: Jonathan Sundquist <jsundquist@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Jonathan Sundquist
2026-02-19 01:19:38 -06:00
committed by GitHub
parent 3cf3120eaa
commit bd5b8426af
6 changed files with 141 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Added a new `ui:autoSelect` option to the EntityPicker field that controls whether an entity is automatically selected when the field loses focus. When set to `false`, the field will remain empty if the user closes it without explicitly selecting an entity, preventing unintentional selections. Defaults to `true` for backward compatibility.
@@ -119,6 +119,36 @@ entity:
defaultNamespace: payment
```
### `autoSelect`
Whether to automatically select the highlighted option when the input loses focus. Defaults to `true`.
When set to `false`, users must explicitly select an option from the dropdown by clicking or pressing Enter. This prevents accidental selections when typing to filter options.
- Default behavior with `autoSelect` as `true` (auto-selects on blur)
```yaml
entity:
title: Entity
type: string
description: Entity of the component
ui:field: EntityPicker
ui:options:
autoSelect: true
```
- Require explicit selection with `autoSelect` as `false`
```yaml
entity:
title: Entity
type: string
description: Entity of the component
ui:field: EntityPicker
ui:options:
autoSelect: false
```
## MultiEntityPicker
The input props that can be specified under `ui:options` for the `MultiEntityPicker` field extension.
+4
View File
@@ -74,6 +74,7 @@ export const EntityNamePickerFieldExtension: FieldExtensionComponent_2<
export const EntityPickerFieldExtension: FieldExtensionComponent_2<
string,
{
autoSelect?: boolean | undefined;
defaultKind?: string | undefined;
defaultNamespace?: string | false | undefined;
catalogFilter?:
@@ -103,6 +104,7 @@ export const EntityPickerFieldExtension: FieldExtensionComponent_2<
export const EntityPickerFieldSchema: FieldSchema_2<
string,
{
autoSelect?: boolean | undefined;
defaultKind?: string | undefined;
defaultNamespace?: string | false | undefined;
catalogFilter?:
@@ -249,6 +251,7 @@ export type MyGroupsPickerUiOptions = NonNullable<
export const OwnedEntityPickerFieldExtension: FieldExtensionComponent_2<
string,
{
autoSelect?: boolean | undefined;
defaultKind?: string | undefined;
defaultNamespace?: string | false | undefined;
catalogFilter?:
@@ -278,6 +281,7 @@ export const OwnedEntityPickerFieldExtension: FieldExtensionComponent_2<
export const OwnedEntityPickerFieldSchema: FieldSchema_2<
string,
{
autoSelect?: boolean | undefined;
defaultKind?: string | undefined;
defaultNamespace?: string | false | undefined;
catalogFilter?:
@@ -378,6 +378,100 @@ describe('<EntityPicker />', () => {
});
});
describe('ui:autoSelect behavior', () => {
beforeEach(() => {
uiSchema = {
'ui:options': {
defaultKind: 'Group',
},
};
props = {
onChange,
schema,
required,
uiSchema,
rawErrors,
formData,
} as unknown as FieldProps<any>;
catalogApi.getEntities.mockResolvedValue({ items: entities });
});
it('default behavior', async () => {
const { getByRole } = await renderInTestApp(
<Wrapper>
<EntityPicker {...props} />
</Wrapper>,
);
const input = getByRole('textbox');
// Type partial match and blur
fireEvent.change(input, { target: { value: 'team' } });
fireEvent.blur(input);
// Default behavior with freeSolo enabled processes the typed value
expect(onChange).toHaveBeenCalledWith('group:default/team');
});
it('does not autoSelect value onBlur', async () => {
uiSchema = {
'ui:options': {
defaultKind: 'Group',
autoSelect: false,
},
};
props = {
...props,
uiSchema,
} as unknown as FieldProps<any>;
const { getByRole } = await renderInTestApp(
<Wrapper>
<EntityPicker {...props} />
</Wrapper>,
);
const input = getByRole('textbox');
// Type and blur - with autoSelect=false, the autocomplete won't auto-select on blur
fireEvent.change(input, { target: { value: 'team' } });
fireEvent.blur(input);
// With autoSelect=false, onChange should not be called on blur
// This is the key difference - users must explicitly select an option
expect(onChange).not.toHaveBeenCalled();
});
it('autoSelects entity onBlur', async () => {
uiSchema = {
'ui:options': {
defaultKind: 'Group',
autoSelect: true,
},
};
props = {
...props,
uiSchema,
} as unknown as FieldProps<any>;
const { getByRole } = await renderInTestApp(
<Wrapper>
<EntityPicker {...props} />
</Wrapper>,
);
const input = getByRole('textbox');
// Type and blur
fireEvent.change(input, { target: { value: 'squad' } });
fireEvent.blur(input);
// With autoSelect=true and freeSolo, processes the typed value
expect(onChange).toHaveBeenCalledWith('group:default/squad');
});
});
describe('uses full entity ref', () => {
beforeEach(() => {
uiSchema = {
@@ -74,6 +74,7 @@ export const EntityPicker = (props: EntityPickerProps) => {
const defaultKind = uiSchema['ui:options']?.defaultKind;
const defaultNamespace =
uiSchema['ui:options']?.defaultNamespace || undefined;
const autoSelect = uiSchema?.['ui:options']?.autoSelect ?? true;
const isDisabled = uiSchema?.['ui:disabled'] ?? false;
const catalogApi = useApi(catalogApiRef);
@@ -209,7 +210,7 @@ export const EntityPicker = (props: EntityPickerProps) => {
: entities?.entityRefToPresentation.get(stringifyEntityRef(option))
?.entityRef!
}
autoSelect
autoSelect={autoSelect}
freeSolo={allowArbitraryValues}
renderInput={params => (
<TextField
@@ -61,6 +61,12 @@ export const EntityPickerFieldSchema = makeFieldSchema({
)
.optional()
.describe('List of key-value filter expression for entities'),
autoSelect: z
.boolean()
.optional()
.describe(
'Whether to automatically select an option on blur. Defaults to true.',
),
}),
});