forked from KaotoIO/kaoto
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds support for anyOf & oneOf so it better reflects the Caml YAML DSL schema. fix: KaotoIO#1934 fix: KaotoIO#1913 fix: KaotoIO#794
- Loading branch information
Showing
32 changed files
with
2,660 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
packages/ui/src/components/Visualization/Canvas/FormV2/KaotoForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Form } from '@patternfly/react-core'; | ||
import { FunctionComponent, useCallback } from 'react'; | ||
import { KaotoSchemaDefinition } from '../../../../models'; | ||
import { isDefined, ROOT_PATH } from '../../../../utils'; | ||
import { AutoField } from './fields/AutoField'; | ||
import { FormComponentFactoryProvider } from './providers/FormComponentFactoryProvider'; | ||
import { ModelContextProvider } from './providers/ModelProvider'; | ||
import { SchemaDefinitionsProvider } from './providers/SchemaDefinitionsProvider'; | ||
import { SchemaProvider } from './providers/SchemaProvider'; | ||
|
||
interface FormProps { | ||
schema?: KaotoSchemaDefinition['schema']; | ||
onChange: (propName: string, value: any) => void; | ||
model: any; | ||
} | ||
|
||
export const KaotoForm: FunctionComponent<FormProps> = ({ schema, onChange, model }) => { | ||
const onPropertyChange = useCallback( | ||
(propName: string, value: any) => { | ||
console.log('KaotoForm.onPropertyChange', propName, value); | ||
onChange(propName, value); | ||
}, | ||
[onChange], | ||
); | ||
|
||
if (!isDefined(schema)) { | ||
return <div>Schema not defined</div>; | ||
} | ||
|
||
return ( | ||
<FormComponentFactoryProvider> | ||
<SchemaDefinitionsProvider schema={schema}> | ||
<SchemaProvider schema={schema}> | ||
<ModelContextProvider model={model} onPropertyChange={onPropertyChange}> | ||
<Form> | ||
<AutoField propName={ROOT_PATH} /> | ||
</Form> | ||
</ModelContextProvider> | ||
</SchemaProvider> | ||
</SchemaDefinitionsProvider> | ||
</FormComponentFactoryProvider> | ||
); | ||
}; |
31 changes: 31 additions & 0 deletions
31
packages/ui/src/components/Visualization/Canvas/FormV2/fields/AnyOfField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { FunctionComponent, useContext } from 'react'; | ||
import { KaotoSchemaDefinition } from '../../../../../models'; | ||
import { SchemaContext, SchemaProvider } from '../providers/SchemaProvider'; | ||
import { FieldProps } from '../typings'; | ||
import { AutoField } from './AutoField'; | ||
|
||
interface AnyOfFieldProps extends FieldProps { | ||
anyOf: KaotoSchemaDefinition['schema']['anyOf']; | ||
} | ||
|
||
export const AnyOfField: FunctionComponent<AnyOfFieldProps> = ({ propName, anyOf }) => { | ||
const { schema } = useContext(SchemaContext); | ||
|
||
if (!Array.isArray(schema.anyOf) || schema.anyOf.length === 0) { | ||
return null; | ||
} else if (!schema) { | ||
return <div>AnyOfField - Schema not defined</div>; | ||
} | ||
|
||
return ( | ||
<> | ||
{anyOf?.map((schema, index) => { | ||
return ( | ||
<SchemaProvider key={index} schema={schema}> | ||
<AutoField propName={propName} /> | ||
</SchemaProvider> | ||
); | ||
})} | ||
</> | ||
); | ||
}; |
20 changes: 20 additions & 0 deletions
20
packages/ui/src/components/Visualization/Canvas/FormV2/fields/AutoField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { FunctionComponent, useContext } from 'react'; | ||
import { FormComponentFactoryContext } from '../providers/FormComponentFactoryProvider'; | ||
import { SchemaContext } from '../providers/SchemaProvider'; | ||
import { FieldProps } from '../typings'; | ||
|
||
export const AutoField: FunctionComponent<FieldProps> = ({ propName, required }) => { | ||
const { schema } = useContext(SchemaContext); | ||
const formComponentFactory = useContext(FormComponentFactoryContext); | ||
|
||
if (!schema) { | ||
return <div>AutoField - Schema not defined</div>; | ||
} | ||
if (!formComponentFactory) { | ||
return <div>AutoField - Form component factory not defined</div>; | ||
} | ||
|
||
const FieldComponent = formComponentFactory(schema); | ||
|
||
return <FieldComponent propName={propName} required={required} />; | ||
}; |
48 changes: 48 additions & 0 deletions
48
packages/ui/src/components/Visualization/Canvas/FormV2/fields/BooleanField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { Checkbox, FormGroup, FormGroupLabelHelp, Popover } from '@patternfly/react-core'; | ||
import { FunctionComponent, useContext } from 'react'; | ||
import { useFieldValue } from '../hooks/field-value'; | ||
import { SchemaContext } from '../providers/SchemaProvider'; | ||
import { FieldProps } from '../typings'; | ||
|
||
export const BooleanField: FunctionComponent<FieldProps> = ({ propName, required }) => { | ||
const { schema } = useContext(SchemaContext); | ||
const { value, onChange } = useFieldValue<boolean>(propName); | ||
const onFieldChange = (_event: unknown, checked: boolean) => { | ||
onChange(checked); | ||
}; | ||
|
||
if (!schema) { | ||
return <div>BooleanField - Schema not defined</div>; | ||
} | ||
|
||
const id = `${propName}-popover`; | ||
|
||
return ( | ||
<FormGroup | ||
fieldId={propName} | ||
label={`${schema.title} (${propName})`} | ||
isRequired={required} | ||
labelHelp={ | ||
<Popover | ||
id={id} | ||
headerContent={<p>{schema.title}</p>} | ||
bodyContent={<p>{schema.description}</p>} | ||
footerContent={<p>Default: {schema.default?.toString() ?? 'no default value'}</p>} | ||
triggerAction="hover" | ||
withFocusTrap={false} | ||
> | ||
<FormGroupLabelHelp aria-label={`More info for ${schema.title} field`} /> | ||
</Popover> | ||
} | ||
> | ||
<Checkbox | ||
id={propName} | ||
name={propName} | ||
aria-describedby={id} | ||
isChecked={value} | ||
checked={value} | ||
onChange={onFieldChange} | ||
/> | ||
</FormGroup> | ||
); | ||
}; |
10 changes: 10 additions & 0 deletions
10
packages/ui/src/components/Visualization/Canvas/FormV2/fields/DisabledField.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { render } from '@testing-library/react'; | ||
import { DisabledField } from './DisabledField'; | ||
|
||
describe('DisabledField', () => { | ||
it('should render', () => { | ||
const { container } = render(<DisabledField data-testid="disabled-field-id" propName="test" />); | ||
|
||
expect(container).toMatchSnapshot(); | ||
}); | ||
}); |
22 changes: 22 additions & 0 deletions
22
packages/ui/src/components/Visualization/Canvas/FormV2/fields/DisabledField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Card, CardBody, CardTitle } from '@patternfly/react-core'; | ||
import { FunctionComponent } from 'react'; | ||
import { IDataTestID } from '../../../../../models'; | ||
import { FieldProps } from '../typings'; | ||
import { CustomExpandableSection } from '../../../../Form/customField/CustomExpandableSection'; | ||
|
||
export const DisabledField: FunctionComponent<IDataTestID & FieldProps> = (props) => { | ||
return ( | ||
<Card> | ||
<CardTitle>{props.propName}</CardTitle> | ||
<CardBody> | ||
<p>Configuring this field is not yet supported</p> | ||
|
||
<CustomExpandableSection groupName={props.propName}> | ||
<code> | ||
<pre>{JSON.stringify(props, null, 2)}</pre> | ||
</code> | ||
</CustomExpandableSection> | ||
</CardBody> | ||
</Card> | ||
); | ||
}; |
23 changes: 23 additions & 0 deletions
23
packages/ui/src/components/Visualization/Canvas/FormV2/fields/ObjectField/ObjectField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { FunctionComponent, useContext } from 'react'; | ||
import { ROOT_PATH } from '../../../../../../utils'; | ||
import { SchemaContext } from '../../providers/SchemaProvider'; | ||
import { FieldProps } from '../../typings'; | ||
import { ObjectFieldInner } from './ObjectFieldInner'; | ||
import { ObjectFieldWrapper } from './ObjectFieldWrapper'; | ||
|
||
export const ObjectField: FunctionComponent<FieldProps> = ({ propName }) => { | ||
const { schema } = useContext(SchemaContext); | ||
if (!schema) { | ||
return <div>ObjectField - Schema not defined</div>; | ||
} | ||
|
||
if (propName === ROOT_PATH || !schema.title) { | ||
return <ObjectFieldInner propName={propName} />; | ||
} | ||
|
||
return ( | ||
<ObjectFieldWrapper title={schema.title}> | ||
<ObjectFieldInner propName={propName} /> | ||
</ObjectFieldWrapper> | ||
); | ||
}; |
37 changes: 37 additions & 0 deletions
37
...ges/ui/src/components/Visualization/Canvas/FormV2/fields/ObjectField/ObjectFieldInner.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { FunctionComponent, useContext } from 'react'; | ||
import { isDefined } from '../../../../../../utils'; | ||
import { SchemaContext, SchemaProvider } from '../../providers/SchemaProvider'; | ||
import { FieldProps } from '../../typings'; | ||
import { AnyOfField } from '../AnyOfField'; | ||
import { AutoField } from '../AutoField'; | ||
|
||
export const ObjectFieldInner: FunctionComponent<FieldProps> = ({ propName }) => { | ||
const { schema } = useContext(SchemaContext); | ||
if (!schema) { | ||
return <div>ObjectField - Schema not defined</div>; | ||
} | ||
|
||
const requiredProperties = Array.isArray(schema.required) ? schema.required : []; | ||
|
||
return ( | ||
<> | ||
{Object.entries(schema.properties ?? {}) | ||
.filter(([_, propertySchema]) => { | ||
/** Remove empty properties like `csimple: {}` */ | ||
return isDefined(propertySchema) && Object.keys(propertySchema).length > 0; | ||
}) | ||
.map(([propertyName, propertySchema]) => { | ||
const name = `${propName}.${propertyName}`; | ||
const required = requiredProperties.includes(propertyName); | ||
|
||
return ( | ||
<SchemaProvider key={name} schema={propertySchema}> | ||
<AutoField propName={name} required={required} /> | ||
</SchemaProvider> | ||
); | ||
})} | ||
|
||
{Array.isArray(schema.anyOf) && <AnyOfField propName={propName} anyOf={schema.anyOf} />} | ||
</> | ||
); | ||
}; |
19 changes: 19 additions & 0 deletions
19
...s/ui/src/components/Visualization/Canvas/FormV2/fields/ObjectField/ObjectFieldWrapper.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Card, CardBody, CardTitle } from '@patternfly/react-core'; | ||
import { FunctionComponent, PropsWithChildren } from 'react'; | ||
|
||
interface ObjectFieldWrapperProps { | ||
title: string; | ||
} | ||
|
||
export const ObjectFieldWrapper: FunctionComponent<PropsWithChildren<ObjectFieldWrapperProps>> = ({ | ||
title, | ||
children, | ||
}) => { | ||
return ( | ||
<Card> | ||
<CardTitle>{title}</CardTitle> | ||
|
||
<CardBody className="pf-v6-c-form">{children}</CardBody> | ||
</Card> | ||
); | ||
}; |
36 changes: 36 additions & 0 deletions
36
packages/ui/src/components/Visualization/Canvas/FormV2/fields/OneOfField/OneOfField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { FunctionComponent, useContext, useMemo, useState } from 'react'; | ||
import { getAppliedSchemaIndexV2 } from '../../../../../../utils/get-applied-schema-index'; | ||
import { getOneOfSchemaListV2, OneOfSchemas } from '../../../../../../utils/get-oneof-schema-list'; | ||
import { useFieldValue } from '../../hooks/field-value'; | ||
import { SchemaContext, SchemaProvider } from '../../providers/SchemaProvider'; | ||
import { FieldProps } from '../../typings'; | ||
import { AutoField } from '../AutoField'; | ||
import { SchemaList } from './SchemaList'; | ||
|
||
export const OneOfField: FunctionComponent<FieldProps> = ({ propName }) => { | ||
const { schema, definitions } = useContext(SchemaContext); | ||
const { value } = useFieldValue<unknown>(propName); | ||
|
||
const oneOfSchemas: OneOfSchemas[] = useMemo( | ||
() => getOneOfSchemaListV2(schema.oneOf ?? [], definitions), | ||
[definitions, schema.oneOf], | ||
); | ||
const appliedSchemaIndex = getAppliedSchemaIndexV2(value, oneOfSchemas, definitions); | ||
const presetSchema = appliedSchemaIndex === -1 ? undefined : oneOfSchemas[appliedSchemaIndex]; | ||
const [selectedOneOfSchema, setSelectedOneOfSchema] = useState<OneOfSchemas | undefined>(presetSchema); | ||
|
||
return ( | ||
<SchemaList | ||
propName={propName} | ||
selectedSchema={selectedOneOfSchema} | ||
schemas={oneOfSchemas} | ||
onChange={setSelectedOneOfSchema} | ||
> | ||
{selectedOneOfSchema && ( | ||
<SchemaProvider schema={selectedOneOfSchema.schema}> | ||
<AutoField propName={propName} /> | ||
</SchemaProvider> | ||
)} | ||
</SchemaList> | ||
); | ||
}; |
Oops, something went wrong.