-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(CanvasToolbar): Add DSL Selector
In order to create new entities, a DSL Slector component needs to be added. This commit adds said component divided in 3 sections: 1. `ChangeDSLModal`: A modal to warn the user whenever the DSL is being changed since the existing integration will be removed 2. `DSLSelectorToggle`: The DSL Button itself 3. `DSLSelector`: The main entrypoint for the DSL Selector, this component is in charge of combining both `DSLSelectorToggle` and the `ChangeDSLModal` components relates: #1030
- Loading branch information
Showing
7 changed files
with
466 additions
and
0 deletions.
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
...omponents/Visualization/ContextToolbar/DSLSelector/ChangeDSLModal/ChangeDSLModal.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,43 @@ | ||
import { fireEvent, render } from '@testing-library/react'; | ||
import { ChangeDSLModal } from './ChangeDSLModal'; | ||
|
||
describe('ChangeDSLModal', () => { | ||
it('should be hidden when isOpen is false', () => { | ||
const wrapper = render(<ChangeDSLModal isOpen={false} onConfirm={jest.fn()} onCancel={jest.fn()} />); | ||
|
||
expect(wrapper.queryByTestId('confirmation-modal')).not.toBeInTheDocument(); | ||
}); | ||
|
||
it('should be visible when isOpen is true', () => { | ||
const wrapper = render(<ChangeDSLModal isOpen={true} onConfirm={jest.fn()} onCancel={jest.fn()} />); | ||
|
||
expect(wrapper.queryByTestId('confirmation-modal')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should call onConfirm when confirm button is clicked', () => { | ||
const onConfirm = jest.fn(); | ||
const wrapper = render(<ChangeDSLModal isOpen={true} onConfirm={onConfirm} onCancel={jest.fn()} />); | ||
|
||
fireEvent.click(wrapper.getByTestId('confirmation-modal-confirm')); | ||
|
||
expect(onConfirm).toBeCalled(); | ||
}); | ||
|
||
it('should call onCancel when cancel button is clicked', () => { | ||
const onCancel = jest.fn(); | ||
const wrapper = render(<ChangeDSLModal isOpen={true} onConfirm={jest.fn()} onCancel={onCancel} />); | ||
|
||
fireEvent.click(wrapper.getByTestId('confirmation-modal-cancel')); | ||
|
||
expect(onCancel).toBeCalled(); | ||
}); | ||
|
||
it('should call onCancel when close button is clicked', () => { | ||
const onCancel = jest.fn(); | ||
const wrapper = render(<ChangeDSLModal isOpen={true} onConfirm={jest.fn()} onCancel={onCancel} />); | ||
|
||
fireEvent.click(wrapper.getByLabelText('Close')); | ||
|
||
expect(onCancel).toBeCalled(); | ||
}); | ||
}); |
34 changes: 34 additions & 0 deletions
34
...src/components/Visualization/ContextToolbar/DSLSelector/ChangeDSLModal/ChangeDSLModal.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,34 @@ | ||
import { Button, Modal, ModalVariant } from '@patternfly/react-core'; | ||
import { FunctionComponent } from 'react'; | ||
|
||
interface ChangeDSLModalProps { | ||
isOpen: boolean; | ||
onConfirm: () => void; | ||
onCancel: () => void; | ||
} | ||
|
||
export const ChangeDSLModal: FunctionComponent<ChangeDSLModalProps> = (props) => { | ||
return ( | ||
<Modal | ||
variant={ModalVariant.small} | ||
title="Warning" | ||
data-testid="confirmation-modal" | ||
titleIconVariant="warning" | ||
onClose={props.onCancel} | ||
actions={[ | ||
<Button key="confirm" variant="primary" data-testid="confirmation-modal-confirm" onClick={props.onConfirm}> | ||
Confirm | ||
</Button>, | ||
<Button key="cancel" variant="link" data-testid="confirmation-modal-cancel" onClick={props.onCancel}> | ||
Cancel | ||
</Button>, | ||
]} | ||
isOpen={props.isOpen} | ||
> | ||
<p> | ||
This will remove any existing integration and you will lose your current work. Are you sure you would like to | ||
proceed? | ||
</p> | ||
</Modal> | ||
); | ||
}; |
81 changes: 81 additions & 0 deletions
81
packages/ui/src/components/Visualization/ContextToolbar/DSLSelector/DSLSelector.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,81 @@ | ||
import { act, fireEvent, render } from '@testing-library/react'; | ||
import { EntitiesContextResult } from '../../../../hooks'; | ||
import { KaotoSchemaDefinition } from '../../../../models'; | ||
import { SourceSchemaType, sourceSchemaConfig } from '../../../../models/camel'; | ||
import { EntitiesContext } from '../../../../providers/entities.provider'; | ||
import { SourceCodeApiContext } from '../../../../providers/source-code.provider'; | ||
import { DSLSelector } from './DSLSelector'; | ||
|
||
describe('DSLSelector.tsx', () => { | ||
const config = sourceSchemaConfig; | ||
config.config[SourceSchemaType.Integration].schema = { | ||
schema: { name: 'Integration', description: 'desc' } as KaotoSchemaDefinition['schema'], | ||
} as KaotoSchemaDefinition; | ||
config.config[SourceSchemaType.Pipe].schema = { | ||
schema: { name: 'Pipe', description: 'desc' } as KaotoSchemaDefinition['schema'], | ||
} as KaotoSchemaDefinition; | ||
config.config[SourceSchemaType.Kamelet].schema = { | ||
schema: { name: 'Kamelet', description: 'desc' } as KaotoSchemaDefinition['schema'], | ||
} as KaotoSchemaDefinition; | ||
config.config[SourceSchemaType.KameletBinding].schema = { | ||
name: 'kameletBinding', | ||
schema: { description: 'desc' }, | ||
} as KaotoSchemaDefinition; | ||
config.config[SourceSchemaType.Route].schema = { | ||
schema: { name: 'route', description: 'desc' } as KaotoSchemaDefinition['schema'], | ||
} as KaotoSchemaDefinition; | ||
|
||
const renderWithContext = () => { | ||
return render( | ||
<SourceCodeApiContext.Provider | ||
value={{ | ||
setCodeAndNotify: jest.fn(), | ||
}} | ||
> | ||
<EntitiesContext.Provider | ||
value={ | ||
{ | ||
currentSchemaType: SourceSchemaType.Integration, | ||
} as unknown as EntitiesContextResult | ||
} | ||
> | ||
<DSLSelector /> | ||
</EntitiesContext.Provider> | ||
</SourceCodeApiContext.Provider>, | ||
); | ||
}; | ||
|
||
it('should render all of the types', async () => { | ||
const wrapper = renderWithContext(); | ||
const trigger = await wrapper.findByTestId('dsl-list-dropdown'); | ||
|
||
/** Open Select */ | ||
act(() => { | ||
fireEvent.click(trigger); | ||
}); | ||
|
||
for (const name of ['Pipe', 'Camel Route']) { | ||
const element = await wrapper.findByText(name); | ||
expect(element).toBeInTheDocument(); | ||
} | ||
}); | ||
|
||
it('should warn the user when adding a different type of flow', async () => { | ||
const wrapper = renderWithContext(); | ||
const trigger = await wrapper.findByTestId('dsl-list-dropdown'); | ||
|
||
/** Open Select */ | ||
act(() => { | ||
fireEvent.click(trigger); | ||
}); | ||
|
||
/** Select an option */ | ||
act(() => { | ||
const element = wrapper.getByText('Pipe'); | ||
fireEvent.click(element); | ||
}); | ||
|
||
const modal = await wrapper.findByTestId('confirmation-modal'); | ||
expect(modal).toBeInTheDocument(); | ||
}); | ||
}); |
39 changes: 39 additions & 0 deletions
39
packages/ui/src/components/Visualization/ContextToolbar/DSLSelector/DSLSelector.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,39 @@ | ||
import { FunctionComponent, PropsWithChildren, useCallback, useContext, useState } from 'react'; | ||
import { SourceSchemaType } from '../../../../models/camel'; | ||
import { FlowTemplateService } from '../../../../models/visualization/flows/support/flow-templates-service'; | ||
import { SourceCodeApiContext } from '../../../../providers'; | ||
import { ChangeDSLModal } from './ChangeDSLModal/ChangeDSLModal'; | ||
import { DSLSelectorToggle } from './DSLSelectorToggle/DSLSelectorToggle'; | ||
|
||
export const DSLSelector: FunctionComponent<PropsWithChildren> = () => { | ||
const sourceCodeContextApi = useContext(SourceCodeApiContext); | ||
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false); | ||
const [proposedFlowType, setProposedFlowType] = useState<SourceSchemaType>(); | ||
|
||
const checkBeforeAddNewFlow = useCallback((flowType: SourceSchemaType) => { | ||
/** | ||
* If it is not the same DSL, this operation might result in | ||
* removing the existing flows, so then we warn the user first | ||
*/ | ||
setProposedFlowType(flowType); | ||
setIsConfirmationModalOpen(true); | ||
}, []); | ||
|
||
const onConfirm = useCallback(() => { | ||
if (proposedFlowType) { | ||
sourceCodeContextApi.setCodeAndNotify(FlowTemplateService.getFlowYamlTemplate(proposedFlowType)); | ||
setIsConfirmationModalOpen(false); | ||
} | ||
}, [proposedFlowType, sourceCodeContextApi]); | ||
|
||
const onCancel = useCallback(() => { | ||
setIsConfirmationModalOpen(false); | ||
}, []); | ||
|
||
return ( | ||
<> | ||
<DSLSelectorToggle onSelect={checkBeforeAddNewFlow} /> | ||
<ChangeDSLModal isOpen={isConfirmationModalOpen} onConfirm={onConfirm} onCancel={onCancel} /> | ||
</> | ||
); | ||
}; |
177 changes: 177 additions & 0 deletions
177
...nts/Visualization/ContextToolbar/DSLSelector/DSLSelectorToggle/DSLSelectorToggle.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,177 @@ | ||
import { act, fireEvent, render, waitFor } from '@testing-library/react'; | ||
import { FunctionComponent } from 'react'; | ||
import { EntitiesContextResult } from '../../../../../hooks'; | ||
import { KaotoSchemaDefinition } from '../../../../../models'; | ||
import { SourceSchemaType, sourceSchemaConfig } from '../../../../../models/camel'; | ||
import { EntitiesContext } from '../../../../../providers/entities.provider'; | ||
import { DSLSelectorToggle } from './DSLSelectorToggle'; | ||
|
||
const config = sourceSchemaConfig; | ||
config.config[SourceSchemaType.Pipe].schema = { | ||
name: 'Pipe', | ||
schema: { name: 'Pipe', description: 'desc' } as KaotoSchemaDefinition['schema'], | ||
} as KaotoSchemaDefinition; | ||
config.config[SourceSchemaType.Kamelet].schema = { | ||
name: 'Kamelet', | ||
schema: { name: 'Kamelet', description: 'desc' } as KaotoSchemaDefinition['schema'], | ||
} as KaotoSchemaDefinition; | ||
config.config[SourceSchemaType.Route].schema = { | ||
name: 'route', | ||
schema: { name: 'route', description: 'desc' } as KaotoSchemaDefinition['schema'], | ||
} as KaotoSchemaDefinition; | ||
|
||
describe('DSLSelectorToggle.tsx', () => { | ||
let onSelect: () => void; | ||
beforeEach(() => { | ||
onSelect = jest.fn(); | ||
}); | ||
|
||
const DSLSelectorWithContext: FunctionComponent<{ currentSchemaType?: SourceSchemaType }> = (props) => { | ||
const currentSchemaType = props.currentSchemaType ?? SourceSchemaType.Route; | ||
return ( | ||
<EntitiesContext.Provider key={Date.now()} value={{ currentSchemaType } as unknown as EntitiesContextResult}> | ||
<DSLSelectorToggle onSelect={onSelect} /> | ||
</EntitiesContext.Provider> | ||
); | ||
}; | ||
|
||
it('component renders', () => { | ||
const wrapper = render(<DSLSelectorWithContext />); | ||
const toggle = wrapper.queryByTestId('dsl-list-dropdown'); | ||
expect(toggle).toBeInTheDocument(); | ||
}); | ||
|
||
it('should call onSelect when clicking on the MenuToggleAction', async () => { | ||
const wrapper = render(<DSLSelectorWithContext />); | ||
|
||
/** Click on toggle */ | ||
const toggle = await wrapper.findByTestId('dsl-list-dropdown'); | ||
act(() => { | ||
fireEvent.click(toggle); | ||
}); | ||
|
||
/** Click on first element */ | ||
const element = await wrapper.findByText('Pipe'); | ||
act(() => { | ||
fireEvent.click(element); | ||
}); | ||
|
||
await waitFor(() => { | ||
expect(onSelect).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
it('should disable the MenuToggleAction if the DSL is already selected', async () => { | ||
const wrapper = render(<DSLSelectorWithContext currentSchemaType={SourceSchemaType.Route} />); | ||
|
||
/** Click on toggle */ | ||
const toggle = await wrapper.findByTestId('dsl-list-dropdown'); | ||
act(() => { | ||
fireEvent.click(toggle); | ||
}); | ||
|
||
/** Click on first element */ | ||
const element = await wrapper.findByText('Camel Route'); | ||
// act(() => { | ||
// fireEvent.click(element); | ||
// }); | ||
|
||
waitFor(() => { | ||
expect(element).toBeDisabled(); | ||
}); | ||
}); | ||
|
||
it('should toggle list of DSLs', async () => { | ||
const wrapper = render(<DSLSelectorWithContext />); | ||
const toggle = await wrapper.findByTestId('dsl-list-dropdown'); | ||
|
||
/** Click on toggle */ | ||
act(() => { | ||
fireEvent.click(toggle); | ||
}); | ||
|
||
const element = await wrapper.findByText('Pipe'); | ||
expect(element).toBeInTheDocument(); | ||
|
||
/** Close Select */ | ||
act(() => { | ||
fireEvent.click(toggle); | ||
}); | ||
|
||
waitFor(() => { | ||
expect(element).not.toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
it('should show selected value', async () => { | ||
const wrapper = render(<DSLSelectorWithContext />); | ||
const toggle = await wrapper.findByTestId('dsl-list-dropdown'); | ||
|
||
/** Open Select */ | ||
act(() => { | ||
fireEvent.click(toggle); | ||
}); | ||
|
||
/** Click on first element */ | ||
act(() => { | ||
const element = wrapper.getByText('Camel Route'); | ||
fireEvent.click(element); | ||
}); | ||
|
||
/** Open Select again */ | ||
act(() => { | ||
fireEvent.click(toggle); | ||
}); | ||
|
||
const element = await wrapper.findByRole('option', { selected: true }); | ||
expect(element).toBeInTheDocument(); | ||
expect(element).toHaveTextContent('Camel Route'); | ||
}); | ||
|
||
it('should have selected DSL if provided', async () => { | ||
const wrapper = render(<DSLSelectorWithContext />); | ||
const toggle = await wrapper.findByTestId('dsl-list-dropdown'); | ||
|
||
/** Open Select */ | ||
act(() => { | ||
fireEvent.click(toggle); | ||
}); | ||
|
||
waitFor(() => { | ||
const element = wrapper.queryByRole('option', { selected: true }); | ||
expect(element).toBeInTheDocument(); | ||
expect(element).toHaveTextContent('Pipe'); | ||
}); | ||
}); | ||
|
||
it('should close Select when pressing ESC', async () => { | ||
const wrapper = render(<DSLSelectorWithContext />); | ||
const toggle = await wrapper.findByTestId('dsl-list-dropdown'); | ||
|
||
/** Open Select */ | ||
act(() => { | ||
fireEvent.click(toggle); | ||
}); | ||
|
||
const menu = await wrapper.findByRole('listbox'); | ||
|
||
expect(menu).toBeInTheDocument(); | ||
|
||
/** Press Escape key to close the menu */ | ||
act(() => { | ||
fireEvent.focus(menu); | ||
fireEvent.keyDown(menu, { key: 'Escape', code: 'Escape', charCode: 27 }); | ||
}); | ||
|
||
waitFor(() => { | ||
/** The close panel is an async process */ | ||
expect(menu).not.toBeInTheDocument(); | ||
}); | ||
|
||
waitFor(() => { | ||
const element = wrapper.queryByRole('option', { selected: true }); | ||
expect(element).toBeInTheDocument(); | ||
expect(element).toHaveTextContent('Camel Route'); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.