diff --git a/.changeset/famous-wombats-explain.md b/.changeset/famous-wombats-explain.md
new file mode 100644
index 0000000000..e1ac88a534
--- /dev/null
+++ b/.changeset/famous-wombats-explain.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-scaffolder': minor
+---
+
+Allowed passing `ui:disabled` for disabling the input field of all the pickers.
diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx
index 3f8b1adc5a..612c5b7600 100644
--- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx
@@ -261,6 +261,73 @@ describe('', () => {
});
});
+ describe('ui:disabled EntityPicker', () => {
+ beforeEach(() => {
+ uiSchema = {
+ 'ui:options': {
+ catalogFilter: [
+ {
+ kind: ['Group'],
+ 'metadata.name': 'test-entity',
+ },
+ {
+ kind: ['User'],
+ 'metadata.name': 'test-entity',
+ },
+ ],
+ },
+ };
+ props = {
+ onChange,
+ schema,
+ required: true,
+ uiSchema,
+ rawErrors,
+ formData,
+ } as unknown as FieldProps;
+
+ catalogApi.getEntities.mockResolvedValue({ items: entities });
+ });
+ it('Prevents user from modifying input when ui:disabled is true', async () => {
+ props.uiSchema = { 'ui:disabled': true };
+ props.formData = 'component:default/myentity';
+
+ await renderInTestApp(
+
+
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+
+ // Expect input to be disabled
+ expect(input).toBeDisabled();
+ expect(input).toHaveValue('component:default/myentity');
+ });
+
+ it('Allows user to edit when ui:disabled is false', async () => {
+ props.uiSchema = { 'ui:disabled': false };
+ props.formData = 'component:default/myentity';
+
+ await renderInTestApp(
+
+
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+ expect(input).not.toBeDisabled();
+
+ fireEvent.change(input, {
+ target: { value: 'component:default/mynewentity' },
+ });
+ fireEvent.blur(input);
+
+ expect(input).toHaveValue('component:default/mynewentity');
+ expect(onChange).toHaveBeenCalledWith('component:default/mynewentity');
+ });
+ });
+
describe('catalogFilter should take precedence over allowedKinds', () => {
beforeEach(() => {
uiSchema = {
diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx
index e1f34638dc..32cdcd73f4 100644
--- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx
@@ -73,6 +73,7 @@ export const EntityPicker = (props: EntityPickerProps) => {
const defaultKind = uiSchema['ui:options']?.defaultKind;
const defaultNamespace =
uiSchema['ui:options']?.defaultNamespace || undefined;
+ const isDisabled = uiSchema?.['ui:disabled'] ?? false;
const catalogApi = useApi(catalogApiRef);
const entityPresentationApi = useApi(entityPresentationApiRef);
@@ -185,9 +186,10 @@ export const EntityPicker = (props: EntityPickerProps) => {
>
{
FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }}
variant="outlined"
required={required}
+ disabled={isDisabled}
InputProps={params.InputProps}
/>
)}
diff --git a/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx b/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx
index 25b5d926a5..ed09e03c13 100644
--- a/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx
@@ -45,6 +45,8 @@ export const EntityTagsPicker = (props: EntityTagsPickerProps) => {
const kinds = uiSchema['ui:options']?.kinds;
const showCounts = uiSchema['ui:options']?.showCounts;
const helperText = uiSchema['ui:options']?.helperText;
+ const isDisabled = uiSchema?.['ui:disabled'] ?? false;
+
const { t } = useTranslationRef(scaffolderTranslationRef);
const { loading, value: existingTags } = useAsync(async () => {
@@ -101,6 +103,7 @@ export const EntityTagsPicker = (props: EntityTagsPickerProps) => {
freeSolo
filterSelectedOptions
onChange={setTags}
+ disabled={isDisabled}
value={formData || []}
inputValue={inputValue}
loading={loading}
@@ -113,6 +116,7 @@ export const EntityTagsPicker = (props: EntityTagsPickerProps) => {
setInputValue(e.target.value)}
error={inputError}
helperText={helperText ?? t('fields.entityTagsPicker.description')}
diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx
index 74b9dadda3..2839ce1731 100644
--- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx
@@ -407,6 +407,38 @@ describe('', () => {
});
});
+ describe('ui:disabled MultiEntityPicker', () => {
+ beforeEach(() => {
+ uiSchema = {
+ 'ui:options': {
+ allowArbitraryValues: true,
+ },
+ 'ui:disabled': true,
+ };
+ props = {
+ onChange,
+ schema,
+ required: true,
+ uiSchema,
+ rawErrors,
+ formData,
+ } as unknown as FieldProps;
+
+ catalogApi.getEntities.mockResolvedValue({ items: entities });
+ });
+ it('Prevents user from modifying input when ui:disabled is true', async () => {
+ props.formData = ['component/default:myentity'];
+ await renderInTestApp(
+
+
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+ expect(input).toBeDisabled();
+ });
+ });
+
describe('Optional MultiEntityPicker', () => {
beforeEach(() => {
uiSchema = {
diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx
index bf2bb34ab6..bcc2e6e773 100644
--- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx
@@ -61,10 +61,12 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => {
formData,
idSchema,
} = props;
+
const catalogFilter = buildCatalogFilter(uiSchema);
const defaultKind = uiSchema['ui:options']?.defaultKind;
const defaultNamespace =
uiSchema['ui:options']?.defaultNamespace || undefined;
+ const isDisabled = uiSchema?.['ui:disabled'] ?? false;
const [noOfItemsSelected, setNoOfItemsSelected] = useState(0);
const catalogApi = useApi(catalogApiRef);
@@ -151,7 +153,10 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => {
multiple
filterSelectedOptions
disabled={
- required && !allowArbitraryValues && entities?.entities?.length === 1
+ isDisabled ||
+ (required &&
+ !allowArbitraryValues &&
+ entities?.entities?.length === 1)
}
id={idSchema?.$id}
defaultValue={formData}
@@ -175,6 +180,7 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => {
({
apiVersion: 'backstage.io/v1beta1',
@@ -114,6 +115,73 @@ describe('', () => {
});
});
+ describe('ui:disabled OwnerPicker', () => {
+ beforeEach(() => {
+ uiSchema = {
+ 'ui:options': {
+ catalogFilter: [
+ {
+ kind: ['Group'],
+ 'metadata.name': 'test-entity',
+ },
+ {
+ kind: ['User'],
+ 'metadata.name': 'test-entity',
+ },
+ ],
+ },
+ };
+ props = {
+ onChange,
+ schema,
+ required: true,
+ uiSchema,
+ rawErrors,
+ formData,
+ } as unknown as FieldProps;
+
+ catalogApi.getEntities.mockResolvedValue({ items: entities });
+ });
+ it('Prevents user from modifying input when ui:disabled is true', async () => {
+ props.uiSchema = { 'ui:disabled': true };
+ props.formData = 'group:default/myentity';
+
+ await renderInTestApp(
+
+
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+
+ // Expect input to be disabled
+ expect(input).toBeDisabled();
+ expect(input).toHaveValue('group:default/myentity');
+ });
+
+ it('Allows user to edit when ui:disabled is false', async () => {
+ props.uiSchema = { 'ui:disabled': false };
+ props.formData = 'group:default/myentity';
+
+ await renderInTestApp(
+
+
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+ expect(input).not.toBeDisabled();
+
+ fireEvent.change(input, {
+ target: { value: 'group:default/mynewentity' },
+ });
+ fireEvent.blur(input);
+
+ expect(input).toHaveValue('group:default/mynewentity');
+ expect(onChange).toHaveBeenCalledWith('group:default/mynewentity');
+ });
+ });
+
describe('with allowedKinds', () => {
beforeEach(() => {
uiSchema = { 'ui:options': { allowedKinds: ['User'] } };
diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/BitbucketRepoBranchPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/BitbucketRepoBranchPicker.test.tsx
index 40496a66af..f0d92d86ba 100644
--- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/BitbucketRepoBranchPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/BitbucketRepoBranchPicker.test.tsx
@@ -20,7 +20,13 @@ import {
scaffolderApiRef,
} from '@backstage/plugin-scaffolder-react';
import { BitbucketRepoBranchPicker } from './BitbucketRepoBranchPicker';
-import { act, fireEvent, render, waitFor } from '@testing-library/react';
+import {
+ act,
+ fireEvent,
+ render,
+ waitFor,
+ screen,
+} from '@testing-library/react';
import { TestApiProvider } from '@backstage/test-utils';
import userEvent from '@testing-library/user-event';
@@ -44,6 +50,25 @@ describe('BitbucketRepoBranchPicker', () => {
expect(getByRole('textbox')).toHaveValue('main');
});
+ it('input field disabled', () => {
+ render(
+
+
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+
+ // Expect input to be disabled
+ expect(input).toBeDisabled();
+ expect(input).toHaveValue('main');
+ });
+
it('calls onChange when the input field changes', () => {
const onChange = jest.fn();
diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/BitbucketRepoBranchPicker.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/BitbucketRepoBranchPicker.tsx
index 5777f95ed2..0e37c10567 100644
--- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/BitbucketRepoBranchPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/BitbucketRepoBranchPicker.tsx
@@ -36,6 +36,7 @@ export const BitbucketRepoBranchPicker = ({
state,
rawErrors,
accessToken,
+ isDisabled,
required,
}: BaseRepoBranchPickerProps<{
accessToken?: string;
@@ -86,9 +87,15 @@ export const BitbucketRepoBranchPicker = ({
onChange={(_, newValue) => {
onChange({ branch: newValue || '' });
}}
+ disabled={isDisabled}
options={availableBranches}
renderInput={params => (
-
+
)}
freeSolo
autoSelect
diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/DefaultRepoBranchPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/DefaultRepoBranchPicker.test.tsx
index 77589e4478..952260ec81 100644
--- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/DefaultRepoBranchPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/DefaultRepoBranchPicker.test.tsx
@@ -15,7 +15,7 @@
*/
import React from 'react';
-import { fireEvent, render } from '@testing-library/react';
+import { fireEvent, render, screen } from '@testing-library/react';
import { DefaultRepoBranchPicker } from './DefaultRepoBranchPicker';
@@ -33,6 +33,23 @@ describe('DefaultRepoBranchPicker', () => {
expect(getByRole('textbox')).toHaveValue('main');
});
+ it('input field disabled', () => {
+ render(
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+
+ // Expect input to be disabled
+ expect(input).toBeDisabled();
+ expect(input).toHaveValue('main');
+ });
+
it('calls onChange when the input field changes', () => {
const onChange = jest.fn();
diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/DefaultRepoBranchPicker.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/DefaultRepoBranchPicker.tsx
index 0fecfb7947..b06bdfa50c 100644
--- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/DefaultRepoBranchPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/DefaultRepoBranchPicker.tsx
@@ -32,6 +32,7 @@ export const DefaultRepoBranchPicker = ({
onChange,
state,
rawErrors,
+ isDisabled,
required,
}: BaseRepoBranchPickerProps) => {
const { branch } = state;
@@ -45,6 +46,7 @@ export const DefaultRepoBranchPicker = ({
onChange({ branch: e.target.value })}
value={branch}
/>
diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/GitHubRepoBranchPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/GitHubRepoBranchPicker.test.tsx
index 3e89b13aa7..56c65e6cf7 100644
--- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/GitHubRepoBranchPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/GitHubRepoBranchPicker.test.tsx
@@ -20,7 +20,13 @@ import {
scaffolderApiRef,
} from '@backstage/plugin-scaffolder-react';
import { GitHubRepoBranchPicker } from './GitHubRepoBranchPicker';
-import { act, fireEvent, render, waitFor } from '@testing-library/react';
+import {
+ act,
+ fireEvent,
+ render,
+ waitFor,
+ screen,
+} from '@testing-library/react';
import { TestApiProvider } from '@backstage/test-utils';
import userEvent from '@testing-library/user-event';
@@ -44,6 +50,25 @@ describe('GitHubRepoBranchPicker', () => {
expect(getByRole('textbox')).toHaveValue('main');
});
+ it('input field disabled', () => {
+ render(
+
+
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+
+ // Expect input to be disabled
+ expect(input).toBeDisabled();
+ expect(input).toHaveValue('main');
+ });
+
it('calls onChange when the input field changes', () => {
const onChange = jest.fn();
diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/GitHubRepoBranchPicker.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/GitHubRepoBranchPicker.tsx
index e4995ceeaa..cfcc3ad900 100644
--- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/GitHubRepoBranchPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/GitHubRepoBranchPicker.tsx
@@ -36,6 +36,7 @@ export const GitHubRepoBranchPicker = ({
state,
rawErrors,
accessToken,
+ isDisabled,
required,
}: BaseRepoBranchPickerProps<{
accessToken?: string;
@@ -86,9 +87,15 @@ export const GitHubRepoBranchPicker = ({
onChange={(_, newValue) => {
onChange({ branch: newValue || '' });
}}
+ disabled={isDisabled}
options={availableBranches}
renderInput={params => (
-
+
)}
freeSolo
autoSelect
diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.test.tsx
index f9c23be407..7b97a836be 100644
--- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.test.tsx
@@ -31,7 +31,7 @@ import {
useTemplateSecrets,
ScaffolderRJSFField,
} from '@backstage/plugin-scaffolder-react';
-import { act, fireEvent } from '@testing-library/react';
+import { act, fireEvent, screen } from '@testing-library/react';
import { RepoBranchPicker } from './RepoBranchPicker';
describe('RepoBranchPicker', () => {
@@ -92,6 +92,40 @@ describe('RepoBranchPicker', () => {
);
});
+ it('should disable the picker when ui:disabled', async () => {
+ const onSubmit = jest.fn();
+
+ await renderInTestApp(
+
+
+
+ ,
+ );
+
+ const input = screen.getByRole('textbox');
+
+ expect(input).toBeDisabled();
+ });
+
it('should render properly with title and description', async () => {
const { getByText } = await renderInTestApp(
{
uiSchema?.['ui:options']?.requestUserCredentials?.secretsKey &&
secrets[uiSchema['ui:options'].requestUserCredentials.secretsKey]
}
+ isDisabled={uiSchema?.['ui:disabled'] ?? false}
required={required}
/>
);
@@ -146,6 +147,7 @@ export const RepoBranchPicker = (props: RepoBranchPickerProps) => {
uiSchema?.['ui:options']?.requestUserCredentials?.secretsKey &&
secrets[uiSchema['ui:options'].requestUserCredentials.secretsKey]
}
+ isDisabled={uiSchema?.['ui:disabled'] ?? false}
required={required}
/>
);
@@ -155,6 +157,7 @@ export const RepoBranchPicker = (props: RepoBranchPickerProps) => {
onChange={updateLocalState}
state={state}
rawErrors={rawErrors}
+ isDisabled={uiSchema?.['ui:disabled'] ?? false}
required={required}
/>
);
diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/types.ts b/plugins/scaffolder/src/components/fields/RepoBranchPicker/types.ts
index a89b1461bc..ff0bfd7a9c 100644
--- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/types.ts
+++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/types.ts
@@ -26,5 +26,6 @@ export type BaseRepoBranchPickerProps = T & {
onChange: (state: RepoBranchPickerState) => void;
state: RepoBranchPickerState;
rawErrors: string[];
+ isDisabled?: boolean;
required?: boolean;
};
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.test.tsx
index 904fb615eb..3db86a7512 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.test.tsx
@@ -30,6 +30,23 @@ describe('AzureRepoPicker', () => {
expect(allInputs).toHaveLength(2);
});
+ it('disables input fields when isDisabled is true', async () => {
+ const { getAllByRole } = await renderInTestApp(
+ ,
+ );
+
+ const allInputs = getAllByRole('textbox');
+
+ allInputs.forEach(input => {
+ expect(input).toBeDisabled();
+ });
+ });
+
describe('org field', () => {
it('calls onChange when the organisation changes', async () => {
const onChange = jest.fn();
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.tsx
index 59e666482a..7f886db0bc 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.tsx
@@ -35,6 +35,7 @@ export const AzureRepoPicker = (
rawErrors,
state,
onChange,
+ isDisabled,
} = props;
const { t } = useTranslationRef(scaffolderTranslationRef);
@@ -63,7 +64,7 @@ export const AzureRepoPicker = (
onChange={s =>
onChange({ organization: String(Array.isArray(s) ? s[0] : s) })
}
- disabled={allowedOrganizations.length === 1}
+ disabled={isDisabled || allowedOrganizations.length === 1}
selected={organization}
items={organizationItems}
/>
@@ -77,6 +78,7 @@ export const AzureRepoPicker = (
label={t('fields.azureRepoPicker.organization.title')}
onChange={e => onChange({ organization: e.target.value })}
helperText={t('fields.azureRepoPicker.organization.description')}
+ disabled={isDisabled}
value={organization}
/>
)}
@@ -94,7 +96,7 @@ export const AzureRepoPicker = (
onChange={s =>
onChange({ project: String(Array.isArray(s) ? s[0] : s) })
}
- disabled={allowedProject.length === 1}
+ disabled={isDisabled || allowedProject.length === 1}
selected={project}
items={projectItems}
/>
@@ -108,6 +110,7 @@ export const AzureRepoPicker = (
label={t('fields.azureRepoPicker.project.title')}
onChange={e => onChange({ project: e.target.value })}
value={project}
+ disabled={isDisabled}
helperText={t('fields.azureRepoPicker.project.description')}
/>
)}
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx
index af3952ff51..a39a660e72 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx
@@ -271,4 +271,50 @@ describe('BitbucketRepoPicker', () => {
);
});
});
+
+ describe('BitbucketRepoPicker - isDisabled', () => {
+ it('disables workspace and project inputs when isDisabled is true', async () => {
+ const { getAllByRole } = await renderInTestApp(
+
+
+ ,
+ );
+
+ const inputs = getAllByRole('textbox');
+ expect(inputs).toHaveLength(2);
+ expect(inputs[0]).toBeDisabled();
+ expect(inputs[1]).toBeDisabled();
+ });
+
+ it('does not disable workspace and project inputs when isDisabled is false', async () => {
+ const { getAllByRole } = await renderInTestApp(
+
+
+ ,
+ );
+
+ const inputs = getAllByRole('textbox');
+ expect(inputs).toHaveLength(2);
+ expect(inputs[0]).not.toBeDisabled();
+ expect(inputs[1]).not.toBeDisabled();
+ });
+ });
});
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx
index 45a6439a63..c8a73eee2d 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx
@@ -49,6 +49,7 @@ export const BitbucketRepoPicker = (
rawErrors,
state,
accessToken,
+ isDisabled,
} = props;
const { t } = useTranslationRef(scaffolderTranslationRef);
@@ -176,7 +177,7 @@ export const BitbucketRepoPicker = (
onChange={s =>
onChange({ workspace: String(Array.isArray(s) ? s[0] : s) })
}
- disabled={allowedOwners.length === 1}
+ disabled={isDisabled || allowedOwners.length === 1}
selected={workspace}
items={ownerItems}
/>
@@ -191,9 +192,11 @@ export const BitbucketRepoPicker = (
)}
+ disabled={isDisabled}
freeSolo
autoSelect
/>
@@ -215,7 +218,7 @@ export const BitbucketRepoPicker = (
onChange={s =>
onChange({ project: String(Array.isArray(s) ? s[0] : s) })
}
- disabled={allowedProjects.length === 1}
+ disabled={isDisabled || allowedProjects.length === 1}
selected={project}
items={projectItems}
/>
@@ -226,10 +229,12 @@ export const BitbucketRepoPicker = (
onChange({ project: newValue || '' });
}}
options={availableProjects}
+ disabled={isDisabled}
renderInput={params => (
)}
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx
index f00e286418..54fb0e0e3f 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx
@@ -20,6 +20,23 @@ import { fireEvent } from '@testing-library/react';
import { renderInTestApp } from '@backstage/test-utils';
describe('GerritRepoPicker', () => {
+ it('disables input fields when isDisabled is true', async () => {
+ const { getAllByRole } = await renderInTestApp(
+ ,
+ );
+
+ const allInputs = getAllByRole('textbox');
+
+ allInputs.forEach(input => {
+ expect(input).toBeDisabled();
+ });
+ });
+
describe('owner input field', () => {
it('calls onChange when the owner input changes', async () => {
const onChange = jest.fn();
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx
index bcc665976f..20d601bf45 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx
@@ -21,7 +21,7 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../../translation';
export const GerritRepoPicker = (props: BaseRepoUrlPickerProps) => {
- const { onChange, rawErrors, state } = props;
+ const { onChange, rawErrors, state, isDisabled } = props;
const { t } = useTranslationRef(scaffolderTranslationRef);
const { workspace, owner } = state;
return (
@@ -32,6 +32,7 @@ export const GerritRepoPicker = (props: BaseRepoUrlPickerProps) => {
label={t('fields.gerritRepoPicker.owner.title')}
onChange={e => onChange({ owner: e.target.value })}
helperText={t('fields.gerritRepoPicker.owner.description')}
+ disabled={isDisabled}
value={owner}
/>
@@ -44,6 +45,7 @@ export const GerritRepoPicker = (props: BaseRepoUrlPickerProps) => {
id="parentInput"
label={t('fields.gerritRepoPicker.parent.title')}
onChange={e => onChange({ workspace: e.target.value })}
+ disabled={isDisabled}
value={workspace}
helperText={t('fields.gerritRepoPicker.parent.description')}
/>
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GiteaRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GiteaRepoPicker.test.tsx
index 056d8f0d11..885e5e3e44 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GiteaRepoPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GiteaRepoPicker.test.tsx
@@ -20,6 +20,22 @@ import { fireEvent } from '@testing-library/react';
import { renderInTestApp } from '@backstage/test-utils';
describe('GiteaRepoPicker', () => {
+ it('disables input fields when isDisabled is true', async () => {
+ const { getAllByRole } = await renderInTestApp(
+ ,
+ );
+
+ const allInputs = getAllByRole('textbox');
+
+ allInputs.forEach(input => {
+ expect(input).toBeDisabled();
+ });
+ });
describe('owner input field', () => {
it('calls onChange when the owner input changes', async () => {
const onChange = jest.fn();
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GiteaRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GiteaRepoPicker.tsx
index 9478d61838..92433e0796 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GiteaRepoPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GiteaRepoPicker.tsx
@@ -28,7 +28,7 @@ export const GiteaRepoPicker = (
allowedRepos?: string[];
}>,
) => {
- const { allowedOwners = [], state, onChange, rawErrors } = props;
+ const { allowedOwners = [], state, onChange, rawErrors, isDisabled } = props;
const { t } = useTranslationRef(scaffolderTranslationRef);
const ownerItems: SelectItem[] = allowedOwners
? allowedOwners.map(i => ({ label: i, value: i }))
@@ -55,7 +55,7 @@ export const GiteaRepoPicker = (
),
})
}
- disabled={allowedOwners.length === 1}
+ disabled={isDisabled || allowedOwners.length === 1}
selected={owner}
items={ownerItems}
/>
@@ -70,6 +70,7 @@ export const GiteaRepoPicker = (
label={t('fields.giteaRepoPicker.owner.inputTitle')}
onChange={e => onChange({ owner: e.target.value })}
helperText={t('fields.giteaRepoPicker.owner.description')}
+ disabled={isDisabled}
value={owner}
/>
>
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.test.tsx
index d5657606a7..92c6dc4090 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.test.tsx
@@ -170,4 +170,38 @@ describe('GithubRepoPicker', () => {
);
});
});
+
+ describe('GithubRepoPicker - isDisabled', () => {
+ it('disables all inputs when isDisabled is true', async () => {
+ const { getByLabelText } = await renderInTestApp(
+
+
+ ,
+ );
+
+ const ownerInput = getByLabelText(/owner/i);
+ expect(ownerInput).toBeDisabled();
+ });
+
+ it('does not disable inputs when isDisabled is false', async () => {
+ const { getByLabelText } = await renderInTestApp(
+
+
+ ,
+ );
+
+ const ownerInput = getByLabelText(/owner/i);
+ expect(ownerInput).not.toBeDisabled();
+ });
+ });
});
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx
index 66a662b530..c1d675d0b6 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx
@@ -34,7 +34,14 @@ export const GithubRepoPicker = (
accessToken?: string;
}>,
) => {
- const { allowedOwners = [], rawErrors, state, onChange, accessToken } = props;
+ const {
+ allowedOwners = [],
+ rawErrors,
+ state,
+ onChange,
+ accessToken,
+ isDisabled,
+ } = props;
const { t } = useTranslationRef(scaffolderTranslationRef);
const ownerItems: SelectItem[] = allowedOwners
? allowedOwners.map(i => ({ label: i, value: i }))
@@ -110,7 +117,7 @@ export const GithubRepoPicker = (
onChange={s =>
onChange({ owner: String(Array.isArray(s) ? s[0] : s) })
}
- disabled={allowedOwners.length === 1}
+ disabled={isDisabled || allowedOwners.length === 1}
selected={owner}
items={ownerItems}
/>
@@ -126,10 +133,12 @@ export const GithubRepoPicker = (
)}
freeSolo
+ disabled={isDisabled}
autoSelect
/>
)}
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.test.tsx
index a8fb42cae3..8a1604dfee 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.test.tsx
@@ -31,6 +31,73 @@ describe('GitlabRepoPicker', () => {
}),
),
};
+
+ describe('GitlabRepoPicker - isDisabled', () => {
+ it('disables owner input when isDisabled is true', async () => {
+ const { getByRole } = await renderInTestApp(
+
+
+ ,
+ );
+
+ expect(getByRole('textbox')).toBeDisabled();
+ });
+
+ it('does not disable owner input when isDisabled is false', async () => {
+ const { getByRole } = await renderInTestApp(
+
+
+ ,
+ );
+
+ expect(getByRole('textbox')).not.toBeDisabled();
+ });
+
+ it('disables select input when allowedOwners are provided and isDisabled is true', async () => {
+ const allowedOwners = ['owner1', 'owner2'];
+ const { getByRole } = await renderInTestApp(
+
+
+ ,
+ );
+
+ expect(getByRole('combobox')).toBeDisabled();
+ });
+
+ it('does not disable select input when allowedOwners are provided and isDisabled is false', async () => {
+ const allowedOwners = ['owner1', 'owner2'];
+ const { getByRole } = await renderInTestApp(
+
+
+ ,
+ );
+
+ expect(getByRole('combobox')).not.toBeDisabled();
+ });
+ });
+
describe('owner field', () => {
it('renders a select if there is a list of allowed owners', async () => {
const allowedOwners = ['owner1', 'owner2'];
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx
index d97c9431ee..0d7c8b365a 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx
@@ -33,7 +33,14 @@ export const GitlabRepoPicker = (
accessToken?: string;
}>,
) => {
- const { allowedOwners = [], state, onChange, rawErrors, accessToken } = props;
+ const {
+ allowedOwners = [],
+ state,
+ onChange,
+ rawErrors,
+ accessToken,
+ isDisabled,
+ } = props;
const [availableGroups, setAvailableGroups] = useState<
{ title: string; id: string }[]
>([]);
@@ -128,7 +135,7 @@ export const GitlabRepoPicker = (
),
})
}
- disabled={allowedOwners.length === 1}
+ disabled={isDisabled || allowedOwners.length === 1}
selected={owner}
items={ownerItems}
/>
@@ -147,10 +154,12 @@ export const GitlabRepoPicker = (
)}
freeSolo
+ disabled={isDisabled}
autoSelect
/>
)}
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx
index 163f397857..2d612f317d 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx
@@ -119,6 +119,36 @@ describe('RepoUrlPicker', () => {
);
});
+ it('should disable the picker when ui:disabled', async () => {
+ const onSubmit = jest.fn();
+ const { getAllByRole } = await renderInTestApp(
+
+
+
+ ,
+ );
+
+ const [ownerInput, repoInput] = getAllByRole('textbox');
+
+ expect(ownerInput).toBeDisabled();
+ expect(repoInput).toBeDisabled();
+ });
+
it('should render properly with allowedHosts', async () => {
const { getByRole } = await renderInTestApp(
uiSchema?.['ui:options']?.allowedRepos ?? [],
[uiSchema],
);
-
+ const isDisabled = useMemo(
+ () => uiSchema?.['ui:disabled'] ?? false,
+ [uiSchema],
+ );
const { owner, organization, project, repoName } = state;
useEffect(() => {
@@ -179,6 +182,7 @@ export const RepoUrlPicker = (
hosts={allowedHosts}
onChange={host => setState(prevState => ({ ...prevState, host }))}
rawErrors={rawErrors}
+ isDisabled={isDisabled}
/>
{hostType === 'github' && (
)}
@@ -207,6 +213,7 @@ export const RepoUrlPicker = (
rawErrors={rawErrors}
state={state}
onChange={updateLocalState}
+ isDisabled={isDisabled}
accessToken={
uiSchema?.['ui:options']?.requestUserCredentials?.secretsKey &&
secrets[uiSchema['ui:options'].requestUserCredentials.secretsKey]
@@ -220,6 +227,7 @@ export const RepoUrlPicker = (
rawErrors={rawErrors}
state={state}
onChange={updateLocalState}
+ isDisabled={isDisabled}
accessToken={
uiSchema?.['ui:options']?.requestUserCredentials?.secretsKey &&
secrets[uiSchema['ui:options'].requestUserCredentials.secretsKey]
@@ -232,6 +240,7 @@ export const RepoUrlPicker = (
allowedProject={allowedProjects}
rawErrors={rawErrors}
state={state}
+ isDisabled={isDisabled}
onChange={updateLocalState}
/>
)}
@@ -240,6 +249,7 @@ export const RepoUrlPicker = (
rawErrors={rawErrors}
state={state}
onChange={updateLocalState}
+ isDisabled={isDisabled}
/>
)}
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerHost.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerHost.test.tsx
index a070e0c6e4..0f85b6d037 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerHost.test.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerHost.test.tsx
@@ -100,4 +100,31 @@ describe('RepoUrlPickerHostField', () => {
expect(listbox.getAllByRole('option')).toHaveLength(2);
});
+
+ it('disables the host select when isDisabled is true', async () => {
+ const mockOnChange = jest.fn();
+ const mockScaffolderApi = {
+ getIntegrationsList: jest.fn().mockResolvedValue({
+ integrations: [
+ { host: 'github.com', title: 'github.com', type: 'github' },
+ { host: 'gitlab.com', title: 'gitlab.com', type: 'gitlab' },
+ ],
+ }),
+ };
+
+ const { getByTestId } = await renderInTestApp(
+
+
+ ,
+ );
+
+ const selectElement = getByTestId('host-select').querySelector('select');
+
+ expect(selectElement).toBeDisabled();
+ });
});
diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerHost.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerHost.tsx
index 90f8eaf449..787aa68987 100644
--- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerHost.tsx
+++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerHost.tsx
@@ -28,8 +28,9 @@ export const RepoUrlPickerHost = (props: {
hosts?: string[];
onChange: (host: string) => void;
rawErrors: string[];
+ isDisabled?: boolean;
}) => {
- const { host, hosts, onChange, rawErrors } = props;
+ const { host, hosts, onChange, rawErrors, isDisabled } = props;
const { t } = useTranslationRef(scaffolderTranslationRef);
const scaffolderApi = useApi(scaffolderApiRef);
@@ -75,7 +76,7 @@ export const RepoUrlPickerHost = (props: {
>