-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support configuring expression in Canvas form
Fixes: #270
- Loading branch information
1 parent
365f7a0
commit 29a822c
Showing
16 changed files
with
926 additions
and
126 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
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
74 changes: 74 additions & 0 deletions
74
packages/ui/src/components/Visualization/Canvas/ExpressionEditor.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,74 @@ | ||
import * as yamlDslSchema from '@kaoto-next/camel-catalog/camelYamlDsl.json'; | ||
import * as languageCatalog from '@kaoto-next/camel-catalog/camel-catalog-aggregate-languages.json'; | ||
import { fireEvent, render, screen } from '@testing-library/react'; | ||
import { ExpressionEditor } from './ExpressionEditor'; | ||
import { CamelCatalogService } from '../../../models/visualization/flows'; | ||
import { CatalogKind, ICamelLanguageDefinition } from '../../../models'; | ||
import { CanvasNode } from './canvas.models'; | ||
import { JSONSchemaType } from 'ajv'; | ||
import { IVisualizationNode, VisualComponentSchema } from '../../../models/visualization/base-visual-entity'; | ||
import { useSchemasStore } from '../../../store'; | ||
import { act } from 'react-dom/test-utils'; | ||
|
||
describe('ExpressionEditor', () => { | ||
let mockNode: CanvasNode; | ||
beforeAll(() => { | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
delete (yamlDslSchema as any).default; | ||
delete (languageCatalog as any).default; | ||
CamelCatalogService.setCatalogKey( | ||
CatalogKind.Language, | ||
languageCatalog as unknown as Record<string, ICamelLanguageDefinition>, | ||
); | ||
|
||
act(() => { | ||
useSchemasStore.setState({ | ||
schemas: { | ||
camelYamlDsl: { | ||
name: 'camelYamlDsl', | ||
tags: ['camel'], | ||
version: '1.0.0', | ||
uri: '', | ||
schema: yamlDslSchema as unknown as Record<string, unknown>, | ||
}, | ||
}, | ||
}); | ||
}); | ||
|
||
const visualComponentSchema: VisualComponentSchema = { | ||
title: 'My Node', | ||
schema: { | ||
type: 'object', | ||
properties: { | ||
name: { | ||
type: 'string', | ||
}, | ||
}, | ||
} as unknown as JSONSchemaType<unknown>, | ||
definition: { | ||
name: 'my node', | ||
}, | ||
}; | ||
|
||
mockNode = { | ||
id: '1', | ||
type: 'node', | ||
data: { | ||
vizNode: { | ||
getComponentSchema: () => visualComponentSchema, | ||
updateModel: (_value: unknown) => {}, | ||
} as IVisualizationNode, | ||
}, | ||
}; | ||
}); | ||
|
||
it('should render', () => { | ||
render(<ExpressionEditor selectedNode={mockNode} />); | ||
const buttons = screen.getAllByRole('button'); | ||
fireEvent.click(buttons[1]); | ||
const jsonpath = screen.getByTestId('expression-dropdownitem-jsonpath'); | ||
fireEvent.click(jsonpath.getElementsByTagName('button')[0]); | ||
const form = screen.getByTestId('metadata-editor-form-expression'); | ||
expect(form.innerHTML).toContain('Suppress Exceptions'); | ||
}); | ||
}); |
126 changes: 126 additions & 0 deletions
126
packages/ui/src/components/Visualization/Canvas/ExpressionEditor.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,126 @@ | ||
import { FunctionComponent, Ref, useCallback, useContext, useMemo, useState } from 'react'; | ||
import { | ||
Card, | ||
CardBody, | ||
CardExpandableContent, | ||
CardHeader, | ||
CardTitle, | ||
Dropdown, | ||
DropdownItem, | ||
DropdownList, | ||
MenuToggle, | ||
MenuToggleElement, | ||
} from '@patternfly/react-core'; | ||
import { MetadataEditor } from '../../MetadataEditor'; | ||
import { EntitiesContext } from '../../../providers'; | ||
import { CanvasNode } from './canvas.models'; | ||
import { ExpressionService } from './expression.service'; | ||
import { useSchemasStore } from '../../../store'; | ||
|
||
interface ExpressionEditorProps { | ||
selectedNode: CanvasNode; | ||
} | ||
|
||
export const ExpressionEditor: FunctionComponent<ExpressionEditorProps> = (props) => { | ||
const entitiesContext = useContext(EntitiesContext); | ||
const [isOpen, setIsOpen] = useState(false); | ||
const [isExpanded, setIsExpanded] = useState(true); | ||
const camelYamlDslSchema = useSchemasStore((state) => state.schemas['camelYamlDsl'].schema) as Record< | ||
string, | ||
unknown | ||
>; | ||
const languageCatalogMap = useMemo(() => { | ||
return ExpressionService.getLanguageMap(camelYamlDslSchema); | ||
}, [camelYamlDslSchema]); | ||
|
||
const visualComponentSchema = props.selectedNode.data?.vizNode?.getComponentSchema(); | ||
if (visualComponentSchema) { | ||
if (!visualComponentSchema.definition) { | ||
visualComponentSchema.definition = {}; | ||
} | ||
} | ||
const model = visualComponentSchema?.definition; | ||
const { language, model: expressionModel } = ExpressionService.parseExpressionModel(languageCatalogMap, model); | ||
const languageSchema = useMemo(() => { | ||
return ExpressionService.getLanguageSchema(language!); | ||
}, [language]); | ||
|
||
const onToggleClick = useCallback(() => { | ||
setIsOpen(!isOpen); | ||
}, [isOpen]); | ||
|
||
const handleOnChange = useCallback( | ||
(selectedLanguage: string, newExpressionModel: Record<string, unknown>) => { | ||
if (!model) return; | ||
ExpressionService.setExpressionModel(languageCatalogMap, model, selectedLanguage, newExpressionModel); | ||
props.selectedNode.data?.vizNode?.updateModel(model); | ||
entitiesContext?.updateCodeFromEntities(); | ||
}, | ||
[entitiesContext, languageCatalogMap, model, props.selectedNode.data?.vizNode], | ||
); | ||
|
||
const onSelect = useCallback( | ||
(_event: React.MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => { | ||
setIsOpen(false); | ||
if (value === language!.language.modelName) return; | ||
handleOnChange(value as string, {}); | ||
}, | ||
[handleOnChange, language], | ||
); | ||
|
||
const toggle = useCallback( | ||
(toggleRef: Ref<MenuToggleElement>) => ( | ||
<MenuToggle ref={toggleRef} onClick={onToggleClick} isExpanded={isOpen}> | ||
{language!.language.title} | ||
</MenuToggle> | ||
), | ||
[language, isOpen, onToggleClick], | ||
); | ||
|
||
return ( | ||
languageCatalogMap && | ||
model && | ||
language && ( | ||
<Card isCompact={true} isExpanded={isExpanded}> | ||
<CardHeader onExpand={() => setIsExpanded(!isExpanded)}> | ||
<CardTitle>Expression</CardTitle> | ||
</CardHeader> | ||
<CardExpandableContent> | ||
<CardBody> | ||
<Dropdown | ||
id="expression-select" | ||
data-testid="expression-dropdown" | ||
isOpen={isOpen} | ||
selected={language.language.modelName} | ||
onSelect={onSelect} | ||
toggle={toggle} | ||
isScrollable={true} | ||
> | ||
<DropdownList data-testid="expression-dropdownlist"> | ||
{Object.values(languageCatalogMap).map((language) => { | ||
return ( | ||
<DropdownItem | ||
data-testid={`expression-dropdownitem-${language.language.modelName}`} | ||
key={language.language.title} | ||
value={language.language.modelName} | ||
description={language.language.description} | ||
> | ||
{language.language.title} | ||
</DropdownItem> | ||
); | ||
})} | ||
</DropdownList> | ||
</Dropdown> | ||
<MetadataEditor | ||
data-testid="expression-editor" | ||
name={'expression'} | ||
schema={languageSchema} | ||
metadata={expressionModel} | ||
onChangeModel={(model) => handleOnChange(language.language.modelName, model)} | ||
/> | ||
</CardBody> | ||
</CardExpandableContent> | ||
</Card> | ||
) | ||
); | ||
}; |
Oops, something went wrong.