mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-29 17:49:14 +00:00
Fix: Superset add service form bug (#23569)
* fix: remove default provider from Superset connection schema * feat: add Superset form handling and connection testing functionality * Fix the unit test * Fix Search Index Application spec failure
This commit is contained in:
parent
06453a925d
commit
453c57df9f
@ -41,10 +41,7 @@
|
||||
{
|
||||
"$ref": "../database/mysqlConnection.json"
|
||||
}
|
||||
],
|
||||
"default": {
|
||||
"provider": "db"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dashboardFilterPattern": {
|
||||
"description": "Regex to exclude or include dashboards that matches the pattern.",
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { SupersetFormType } from '../support/interfaces/ServiceForm.interface';
|
||||
|
||||
export const supersetFormDetails1: SupersetFormType = {
|
||||
hostPort: 'http://localhost:8088',
|
||||
connectionType: 'SupersetApiConnection',
|
||||
connection: {
|
||||
provider: 'db',
|
||||
username: 'test-user-1',
|
||||
password: 'test-password-1',
|
||||
},
|
||||
};
|
||||
|
||||
export const supersetFormDetails2: SupersetFormType = {
|
||||
hostPort: 'http://localhost:8085',
|
||||
connectionType: 'SupersetApiConnection',
|
||||
connection: {
|
||||
provider: 'ldap',
|
||||
username: 'test-user-2',
|
||||
password: 'test-password-2',
|
||||
},
|
||||
};
|
||||
|
||||
export const supersetFormDetails3: SupersetFormType = {
|
||||
hostPort: 'http://localhost:8086',
|
||||
connectionType: 'PostgresConnection',
|
||||
connection: {
|
||||
username: 'test-user-3',
|
||||
password: 'test-password-3',
|
||||
hostPort: 'http://localhost:5432',
|
||||
database: 'test_db',
|
||||
scheme: 'postgresql+psycopg2',
|
||||
},
|
||||
};
|
||||
|
||||
export const supersetFormDetails4: SupersetFormType = {
|
||||
hostPort: 'http://localhost:8086',
|
||||
connectionType: 'MysqlConnection',
|
||||
connection: {
|
||||
username: 'test-user-3',
|
||||
password: 'test-password-3',
|
||||
hostPort: 'http://localhost:5432',
|
||||
scheme: 'mysql+pymysql',
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { expect } from '@playwright/test';
|
||||
import {
|
||||
supersetFormDetails1,
|
||||
supersetFormDetails2,
|
||||
supersetFormDetails3,
|
||||
supersetFormDetails4,
|
||||
} from '../../constant/serviceForm';
|
||||
import { redirectToHomePage } from '../../utils/common';
|
||||
import { waitForAllLoadersToDisappear } from '../../utils/entity';
|
||||
import { fillSupersetFormDetails } from '../../utils/serviceFormUtils';
|
||||
import { test } from '../fixtures/pages';
|
||||
|
||||
test.describe('Service form functionality', async () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await redirectToHomePage(page);
|
||||
});
|
||||
|
||||
test.describe('Superset', () => {
|
||||
test('Verify form selects are working properly', async ({ page }) => {
|
||||
await page.goto('/dashboardServices/add-service');
|
||||
await waitForAllLoadersToDisappear(page);
|
||||
await page.click(`[data-testid="Superset"]`);
|
||||
await page.click('[data-testid="next-button"]');
|
||||
|
||||
await page.fill('[data-testid="service-name"]', 'test-superset');
|
||||
await page.click('[data-testid="next-button"]');
|
||||
|
||||
// Fill superset form details - 1
|
||||
await fillSupersetFormDetails({ page, ...supersetFormDetails1 });
|
||||
|
||||
const testConnectionResponse1 = page.waitForResponse(
|
||||
'api/v1/automations/workflows'
|
||||
);
|
||||
|
||||
await page.getByTestId('test-connection-btn').click();
|
||||
|
||||
const testConnection1 = await (await testConnectionResponse1).json();
|
||||
|
||||
// Verify form details submission - 1
|
||||
expect(testConnection1.request.connection.config.hostPort).toEqual(
|
||||
supersetFormDetails1.hostPort
|
||||
);
|
||||
expect(
|
||||
testConnection1.request.connection.config.connection.username
|
||||
).toEqual(supersetFormDetails1.connection.username);
|
||||
expect(
|
||||
testConnection1.request.connection.config.connection.provider
|
||||
).toEqual(supersetFormDetails1.connection.provider);
|
||||
|
||||
await page
|
||||
.getByTestId('test-connection-modal')
|
||||
.getByRole('button', { name: 'OK' })
|
||||
.click();
|
||||
|
||||
await page.waitForSelector('[data-testid="test-connection-modal"]', {
|
||||
state: 'hidden',
|
||||
});
|
||||
|
||||
// Fill superset form details - 2
|
||||
await fillSupersetFormDetails({ page, ...supersetFormDetails2 });
|
||||
|
||||
const testConnectionResponse2 = page.waitForResponse(
|
||||
'api/v1/automations/workflows'
|
||||
);
|
||||
|
||||
await page.getByTestId('test-connection-btn').click();
|
||||
|
||||
const testConnection2 = await (await testConnectionResponse2).json();
|
||||
|
||||
// Verify form details submission - 2
|
||||
expect(testConnection2.request.connection.config.hostPort).toEqual(
|
||||
supersetFormDetails2.hostPort
|
||||
);
|
||||
expect(
|
||||
testConnection2.request.connection.config.connection.username
|
||||
).toEqual(supersetFormDetails2.connection.username);
|
||||
expect(
|
||||
testConnection2.request.connection.config.connection.provider
|
||||
).toEqual(supersetFormDetails2.connection.provider);
|
||||
|
||||
await page
|
||||
.getByTestId('test-connection-modal')
|
||||
.getByRole('button', { name: 'OK' })
|
||||
.click();
|
||||
|
||||
await page.waitForSelector('[data-testid="test-connection-modal"]', {
|
||||
state: 'hidden',
|
||||
});
|
||||
|
||||
// Fill superset form details - 3
|
||||
await fillSupersetFormDetails({ page, ...supersetFormDetails3 });
|
||||
|
||||
const testConnectionResponse3 = page.waitForResponse(
|
||||
'api/v1/automations/workflows'
|
||||
);
|
||||
|
||||
await page.getByTestId('test-connection-btn').click();
|
||||
|
||||
const testConnection3 = await (await testConnectionResponse3).json();
|
||||
|
||||
// Verify form details submission - 3
|
||||
expect(testConnection3.request.connection.config.hostPort).toEqual(
|
||||
supersetFormDetails3.hostPort
|
||||
);
|
||||
expect(
|
||||
testConnection3.request.connection.config.connection.username
|
||||
).toEqual(supersetFormDetails3.connection.username);
|
||||
expect(
|
||||
testConnection3.request.connection.config.connection.hostPort
|
||||
).toEqual(supersetFormDetails3.connection.hostPort);
|
||||
expect(
|
||||
testConnection3.request.connection.config.connection.database
|
||||
).toEqual(supersetFormDetails3.connection.database);
|
||||
expect(
|
||||
testConnection3.request.connection.config.connection.scheme
|
||||
).toEqual(supersetFormDetails3.connection.scheme);
|
||||
|
||||
await page
|
||||
.getByTestId('test-connection-modal')
|
||||
.getByRole('button', { name: 'OK' })
|
||||
.click();
|
||||
|
||||
await page.waitForSelector('[data-testid="test-connection-modal"]', {
|
||||
state: 'hidden',
|
||||
});
|
||||
|
||||
// Fill superset form details - 4
|
||||
await fillSupersetFormDetails({ page, ...supersetFormDetails4 });
|
||||
|
||||
const testConnectionResponse4 = page.waitForResponse(
|
||||
'api/v1/automations/workflows'
|
||||
);
|
||||
|
||||
await page.getByTestId('test-connection-btn').click();
|
||||
|
||||
const testConnection4 = await (await testConnectionResponse4).json();
|
||||
|
||||
// Verify form details submission - 4
|
||||
expect(testConnection4.request.connection.config.hostPort).toEqual(
|
||||
supersetFormDetails4.hostPort
|
||||
);
|
||||
expect(
|
||||
testConnection4.request.connection.config.connection.username
|
||||
).toEqual(supersetFormDetails4.connection.username);
|
||||
expect(
|
||||
testConnection4.request.connection.config.connection.hostPort
|
||||
).toEqual(supersetFormDetails4.connection.hostPort);
|
||||
expect(
|
||||
testConnection4.request.connection.config.connection.scheme
|
||||
).toEqual(supersetFormDetails4.connection.scheme);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -152,7 +152,9 @@ test('Search Index Application', async ({ page }) => {
|
||||
await clickOutside(page);
|
||||
await page.locator('[for="root/searchIndexMappingLanguage"]').click();
|
||||
|
||||
await page.getByTestId('select-widget').click();
|
||||
await page
|
||||
.getByTestId('select-widget-root/searchIndexMappingLanguage')
|
||||
.click();
|
||||
|
||||
await expect(page.getByTestId('select-option-JP')).toBeVisible();
|
||||
|
||||
|
||||
@ -53,8 +53,12 @@ class AirflowIngestionClass extends ServiceBaseClass {
|
||||
await page.locator('#root\\/hostPort').fill(airflowHostPort);
|
||||
|
||||
await page
|
||||
.locator('#root\\/connection__oneof_select')
|
||||
.selectOption('BackendConnection');
|
||||
.getByTestId('select-widget-root/connection__oneof_select')
|
||||
.getByRole('combobox')
|
||||
.click({ force: true });
|
||||
await page.click(
|
||||
'.ant-select-dropdown:visible [title="BackendConnection"]'
|
||||
);
|
||||
}
|
||||
|
||||
async deleteService(page: Page) {
|
||||
|
||||
@ -65,9 +65,12 @@ class BigQueryIngestionClass extends ServiceBaseClass {
|
||||
const projectIdTaxonomy =
|
||||
process.env.PLAYWRIGHT_BQ_PROJECT_ID_TAXONOMY ?? '';
|
||||
|
||||
await page.selectOption(
|
||||
'#root\\/credentials\\/gcpConfig__oneof_select',
|
||||
'GCP Credentials Values'
|
||||
await page
|
||||
.getByTestId('select-widget-root/credentials/gcpConfig__oneof_select')
|
||||
.getByRole('combobox')
|
||||
.click({ force: true });
|
||||
await page.click(
|
||||
'.ant-select-dropdown:visible [title="GCP Credentials Values"]'
|
||||
);
|
||||
await page.fill('#root\\/credentials\\/gcpConfig\\/projectId', projectId);
|
||||
await checkServiceFieldSectionHighlighting(page, 'projectId');
|
||||
|
||||
@ -131,10 +131,11 @@ class RedshiftWithDBTIngestionClass extends ServiceBaseClass {
|
||||
await page.click('[data-menu-id*="dbt"]');
|
||||
|
||||
await page.waitForSelector('#root\\/dbtConfigSource__oneof_select');
|
||||
await page.selectOption(
|
||||
'#root\\/dbtConfigSource__oneof_select',
|
||||
'DBT S3 Config'
|
||||
);
|
||||
await page
|
||||
.getByTestId('select-widget-root/dbtConfigSource__oneof_select')
|
||||
.getByRole('combobox')
|
||||
.click({ force: true });
|
||||
await page.click('.ant-select-dropdown:visible [title="DBT S3 Config"]');
|
||||
await page.fill(
|
||||
'#root\\/dbtConfigSource\\/dbtSecurityConfig\\/awsAccessKeyId',
|
||||
process.env.PLAYWRIGHT_S3_STORAGE_ACCESS_KEY_ID ?? ''
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
export interface SupersetFormType {
|
||||
hostPort: string;
|
||||
connectionType: string;
|
||||
connection: {
|
||||
username: string;
|
||||
password: string;
|
||||
provider?: string;
|
||||
hostPort?: string;
|
||||
database?: string;
|
||||
scheme?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FillSupersetFormProps extends SupersetFormType {
|
||||
page: Page;
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { FillSupersetFormProps } from '../support/interfaces/ServiceForm.interface';
|
||||
|
||||
export const fillSupersetFormDetails = async ({
|
||||
page,
|
||||
hostPort,
|
||||
connectionType,
|
||||
connection: {
|
||||
username,
|
||||
password,
|
||||
provider,
|
||||
hostPort: connectionHostPort,
|
||||
database,
|
||||
},
|
||||
}: FillSupersetFormProps) => {
|
||||
await page.locator('#root\\/hostPort').clear();
|
||||
await page.fill('#root\\/hostPort', hostPort);
|
||||
|
||||
if (connectionType === 'SupersetApiConnection') {
|
||||
await page
|
||||
.getByTestId('select-widget-root/connection__oneof_select')
|
||||
.getByRole('combobox')
|
||||
.click({ force: true });
|
||||
await page.click(
|
||||
`.ant-select-dropdown:visible [title="${connectionType}"]`
|
||||
);
|
||||
|
||||
if (provider) {
|
||||
await page
|
||||
.getByTestId('select-widget-root/connection/provider')
|
||||
.getByRole('combobox')
|
||||
.click({ force: true });
|
||||
await page.click(`.ant-select-dropdown:visible [title="${provider}"]`);
|
||||
}
|
||||
} else if (
|
||||
connectionType === 'PostgresConnection' ||
|
||||
connectionType === 'MysqlConnection'
|
||||
) {
|
||||
await page
|
||||
.getByTestId('select-widget-root/connection__oneof_select')
|
||||
.getByRole('combobox')
|
||||
.click({ force: true });
|
||||
await page.click(
|
||||
`.ant-select-dropdown:visible [title="${connectionType}"]`
|
||||
);
|
||||
|
||||
if (connectionHostPort) {
|
||||
await page.locator('#root\\/connection\\/hostPort').clear();
|
||||
await page.fill('#root\\/connection\\/hostPort', connectionHostPort, {
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (database) {
|
||||
await page.locator('#root\\/connection\\/database').clear();
|
||||
await page.fill('#root\\/connection\\/database', database, {
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await page.locator('#root\\/connection\\/username').clear();
|
||||
await page.fill('#root\\/connection\\/username', username, { force: true });
|
||||
if (connectionType === 'SupersetApiConnection') {
|
||||
await page.locator('#root\\/connection\\/password').clear();
|
||||
await page.fill('#root\\/connection\\/password', password, {
|
||||
force: true,
|
||||
});
|
||||
} else {
|
||||
await page.locator('#root\\/connection\\/authType\\/password').clear();
|
||||
await page.fill('#root\\/connection\\/authType\\/password', password, {
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -460,6 +460,7 @@ const AppRunsHistory = forwardRef(
|
||||
}
|
||||
width={800}>
|
||||
<FormBuilder
|
||||
capitalizeOptionLabel
|
||||
hideCancelButton
|
||||
readonly
|
||||
useSelectWidget
|
||||
|
||||
@ -60,6 +60,7 @@ const ApplicationConfiguration = ({
|
||||
|
||||
const formPanel = (
|
||||
<FormBuilder
|
||||
capitalizeOptionLabel
|
||||
useSelectWidget
|
||||
cancelText={t('label.back')}
|
||||
formData={appData?.appConfiguration ?? {}}
|
||||
|
||||
@ -157,6 +157,7 @@ const ConnectionConfigForm = ({
|
||||
<Fragment>
|
||||
<AirflowMessageBanner />
|
||||
<FormBuilder
|
||||
useSelectWidget
|
||||
cancelText={cancelText ?? ''}
|
||||
fields={customFields}
|
||||
formData={validConfig}
|
||||
|
||||
@ -56,7 +56,9 @@ describe('Test SelectWidget Component', () => {
|
||||
it('Should render select component', async () => {
|
||||
render(<SelectWidget {...mockSelectProps} />);
|
||||
|
||||
const selectInput = screen.getByTestId('select-widget');
|
||||
const selectInput = screen.getByTestId(
|
||||
'select-widget-root/searchIndexMappingLanguage'
|
||||
);
|
||||
const treeSelectWidget = screen.queryByText('TreeSelectWidget');
|
||||
|
||||
expect(selectInput).toBeInTheDocument();
|
||||
@ -67,7 +69,7 @@ describe('Test SelectWidget Component', () => {
|
||||
render(<SelectWidget {...mockSelectProps} disabled />);
|
||||
|
||||
const selectInput = await findByRole(
|
||||
screen.getByTestId('select-widget'),
|
||||
screen.getByTestId('select-widget-root/searchIndexMappingLanguage'),
|
||||
'combobox'
|
||||
);
|
||||
|
||||
@ -77,7 +79,9 @@ describe('Test SelectWidget Component', () => {
|
||||
it('Should call onFocus', async () => {
|
||||
render(<SelectWidget {...mockSelectProps} />);
|
||||
|
||||
const selectInput = screen.getByTestId('select-widget');
|
||||
const selectInput = screen.getByTestId(
|
||||
'select-widget-root/searchIndexMappingLanguage'
|
||||
);
|
||||
|
||||
fireEvent.focus(selectInput);
|
||||
|
||||
@ -87,7 +91,9 @@ describe('Test SelectWidget Component', () => {
|
||||
it('Should call onBlur', async () => {
|
||||
render(<SelectWidget {...mockSelectProps} />);
|
||||
|
||||
const selectInput = screen.getByTestId('select-widget');
|
||||
const selectInput = screen.getByTestId(
|
||||
'select-widget-root/searchIndexMappingLanguage'
|
||||
);
|
||||
|
||||
fireEvent.blur(selectInput);
|
||||
|
||||
@ -98,7 +104,7 @@ describe('Test SelectWidget Component', () => {
|
||||
render(<SelectWidget {...mockSelectProps} />);
|
||||
|
||||
const selectInput = await findByRole(
|
||||
screen.getByTestId('select-widget'),
|
||||
screen.getByTestId('select-widget-root/searchIndexMappingLanguage'),
|
||||
'combobox'
|
||||
);
|
||||
|
||||
@ -118,7 +124,9 @@ describe('Test SelectWidget Component', () => {
|
||||
it('Should render TreeSelectWidget component if uiFieldType is treeSelect', async () => {
|
||||
render(<SelectWidget {...mockTreeSelectProps} />);
|
||||
|
||||
const selectWidget = screen.queryByTestId('select-widget');
|
||||
const selectWidget = screen.queryByTestId(
|
||||
'select-widget-root/searchIndexMappingLanguage'
|
||||
);
|
||||
const treeSelectWidget = screen.getByText('TreeSelectWidget');
|
||||
|
||||
expect(treeSelectWidget).toBeInTheDocument();
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
*/
|
||||
import { WidgetProps } from '@rjsf/utils';
|
||||
import { Select } from 'antd';
|
||||
import { capitalize } from 'lodash';
|
||||
import { FC } from 'react';
|
||||
import { getPopupContainer } from '../../../../../utils/formUtils';
|
||||
import TreeSelectWidget from './TreeSelectWidget';
|
||||
@ -29,7 +28,7 @@ const SelectWidget: FC<WidgetProps> = (props) => {
|
||||
allowClear
|
||||
autoFocus={rest.autofocus}
|
||||
className="d-block w-full"
|
||||
data-testid="select-widget"
|
||||
data-testid={`select-widget-${rest.id}`}
|
||||
disabled={rest.disabled}
|
||||
getPopupContainer={getPopupContainer}
|
||||
id={rest.id}
|
||||
@ -45,11 +44,29 @@ const SelectWidget: FC<WidgetProps> = (props) => {
|
||||
data-testid={`select-option-${option.label}`}
|
||||
key={option.value}
|
||||
value={option.value}>
|
||||
{capitalize(option.label)}
|
||||
{rest.capitalizeOptionLabel
|
||||
? typeof option.label === 'string'
|
||||
? option.label.charAt(0).toUpperCase() + option.label.slice(1)
|
||||
: option.label
|
||||
: option.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectWidget;
|
||||
function withSelectWidget(WrappedComponent: FC<WidgetProps>) {
|
||||
return function SelectWidget({
|
||||
capitalizeOptionLabel = false,
|
||||
...props
|
||||
}: WidgetProps & { capitalizeOptionLabel?: boolean }) {
|
||||
return (
|
||||
<WrappedComponent
|
||||
{...props}
|
||||
capitalizeOptionLabel={capitalizeOptionLabel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default withSelectWidget(SelectWidget);
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
import { CheckOutlined } from '@ant-design/icons';
|
||||
import Form, { FormProps, IChangeEvent } from '@rjsf/core';
|
||||
import { WidgetProps } from '@rjsf/utils';
|
||||
import { Button } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { LoadingState } from 'Models';
|
||||
@ -42,6 +43,7 @@ export interface Props extends FormProps {
|
||||
status?: LoadingState;
|
||||
onCancel?: () => void;
|
||||
useSelectWidget?: boolean;
|
||||
capitalizeOptionLabel?: boolean;
|
||||
}
|
||||
|
||||
const FormBuilder = forwardRef<Form, Props>(
|
||||
@ -60,6 +62,7 @@ const FormBuilder = forwardRef<Form, Props>(
|
||||
uiSchema,
|
||||
onFocus,
|
||||
useSelectWidget = false,
|
||||
capitalizeOptionLabel = false,
|
||||
children,
|
||||
...props
|
||||
},
|
||||
@ -78,7 +81,14 @@ const FormBuilder = forwardRef<Form, Props>(
|
||||
autoComplete: AsyncSelectWidget,
|
||||
queryBuilder: QueryBuilderWidget,
|
||||
code: CodeWidget,
|
||||
...(useSelectWidget && { SelectWidget: SelectWidget }),
|
||||
...(useSelectWidget && {
|
||||
SelectWidget: (props: WidgetProps) => (
|
||||
<SelectWidget
|
||||
{...props}
|
||||
capitalizeOptionLabel={capitalizeOptionLabel}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user