-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds single state UI component (#53)
Signed-off-by: Drew Baugher <[email protected]>
- Loading branch information
Showing
4 changed files
with
480 additions
and
2 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
86 changes: 86 additions & 0 deletions
86
public/pages/VisualCreatePolicy/components/States/State.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,86 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
* | ||
* Modifications Copyright OpenSearch Contributors. See | ||
* GitHub history for details. | ||
*/ | ||
|
||
import React from "react"; | ||
import "@testing-library/jest-dom/extend-expect"; | ||
import { screen, render } from "@testing-library/react"; | ||
import State from "./State"; | ||
import { State as StateData } from "../../../../../models/interfaces"; | ||
import { ModalProvider, ModalRoot } from "../../../../components/Modal"; | ||
import { ServicesConsumer, ServicesContext } from "../../../../services"; | ||
import { browserServicesMock } from "../../../../../test/mocks"; | ||
import { fireEvent, waitFor } from "@testing-library/dom"; | ||
|
||
describe("<State /> spec", () => { | ||
it("renders the component", () => { | ||
const state: StateData = { | ||
name: "some_name", | ||
actions: [{ close: {} }, { open: {} }, { delete: {} }], | ||
transitions: [{ state_name: "elsewhere", conditions: {} }], | ||
}; | ||
const { container } = render( | ||
<State state={state} isInitialState={true} idx={2} onClickEditState={() => {}} onClickDeleteState={() => {}} /> | ||
); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
|
||
it("does not show initial state when it is not the initial state", async () => { | ||
const state: StateData = { | ||
name: "some_name", | ||
actions: [{ close: {} }, { open: {} }, { delete: {} }], | ||
transitions: [{ state_name: "elsewhere", conditions: {} }], | ||
}; | ||
render(<State state={state} isInitialState={false} idx={2} onClickEditState={() => {}} onClickDeleteState={() => {}} />); | ||
|
||
await waitFor(() => screen.getByText("some_name", { exact: false })); | ||
|
||
expect(screen.queryByText("Initial state", { exact: false })).toBeNull(); | ||
}); | ||
|
||
it("shows no transitions message and no actions message if none defined", async () => { | ||
const state: StateData = { | ||
name: "some_name", | ||
actions: [], | ||
transitions: [], | ||
}; | ||
render(<State state={state} isInitialState={false} idx={2} onClickEditState={() => {}} onClickDeleteState={() => {}} />); | ||
|
||
await waitFor(() => screen.getByText("some_name", { exact: false })); | ||
|
||
expect(screen.queryByText("No transitions. Edit state to add transitions.", { exact: false })).not.toBeNull(); | ||
expect(screen.queryByText("No actions. Edit state to add actions.", { exact: false })).not.toBeNull(); | ||
}); | ||
|
||
it("renders delete modal when deleting a state", async () => { | ||
const state: StateData = { | ||
name: "some_name", | ||
actions: [{ close: {} }, { open: {} }, { delete: {} }], | ||
transitions: [{ state_name: "elsewhere", conditions: {} }], | ||
}; | ||
const onClickDeleteState = jest.fn(); | ||
const { getByTestId } = render( | ||
<ServicesContext.Provider value={browserServicesMock}> | ||
<ModalProvider> | ||
<ServicesConsumer>{(services) => services && <ModalRoot services={services} />}</ServicesConsumer> | ||
<State state={state} isInitialState={true} idx={2} onClickEditState={() => {}} onClickDeleteState={onClickDeleteState} /> | ||
</ModalProvider> | ||
</ServicesContext.Provider> | ||
); | ||
|
||
fireEvent.click(getByTestId("state-delete-button")); | ||
|
||
await waitFor(() => screen.getByText("Deleting the state will result in deleting all transitions.", { exact: false })); | ||
|
||
fireEvent.click(getByTestId("confirmationModalActionButton")); | ||
|
||
expect(onClickDeleteState).toHaveBeenCalled(); | ||
}); | ||
}); |
145 changes: 145 additions & 0 deletions
145
public/pages/VisualCreatePolicy/components/States/State.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,145 @@ | ||
/* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
* | ||
* Modifications Copyright OpenSearch Contributors. See | ||
* GitHub history for details. | ||
*/ | ||
|
||
import React from "react"; | ||
import { EuiAccordion, EuiText, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiButtonIcon } from "@elastic/eui"; | ||
import "brace/theme/github"; | ||
import "brace/mode/json"; | ||
import { State as StateData } from "../../../../../models/interfaces"; | ||
import { ModalConsumer } from "../../../../components/Modal"; | ||
import ConfirmationModal from "../../../../components/ConfirmationModal"; | ||
import Badge from "../Badge"; | ||
import TransitionContent from "../Transition/TransitionContent"; | ||
import { makeId } from "../../../../utils/helpers"; | ||
import { getUIActionFromData } from "../../utils/helpers"; | ||
|
||
interface StateProps { | ||
state: StateData; | ||
isInitialState: boolean; | ||
idx: number; | ||
onClickEditState: (state: StateData) => void; | ||
onClickDeleteState: (idx: number) => void; | ||
} | ||
|
||
const State = ({ state, isInitialState, idx, onClickEditState, onClickDeleteState }: StateProps) => ( | ||
<EuiAccordion | ||
style={{ padding: "15px" }} | ||
id={state.name} | ||
buttonContent={ | ||
<EuiFlexGroup justifyContent="center" alignItems="center"> | ||
<EuiFlexItem grow={false}> | ||
<EuiText> | ||
<strong>{state.name}</strong> | ||
</EuiText> | ||
</EuiFlexItem> | ||
{isInitialState && ( | ||
<EuiFlexItem grow={false}> | ||
<EuiPanel paddingSize="none"> | ||
<EuiText size="xs" style={{ padding: "0px 10px" }}> | ||
Initial state | ||
</EuiText> | ||
</EuiPanel> | ||
</EuiFlexItem> | ||
)} | ||
{!!state.actions?.length && ( | ||
<EuiFlexItem grow={false}> | ||
<Badge text="Actions" number={state.actions.length} /> | ||
</EuiFlexItem> | ||
)} | ||
{!!state.transitions?.length && ( | ||
<EuiFlexItem grow={false}> | ||
<Badge text="Transitions" number={state.transitions.length} /> | ||
</EuiFlexItem> | ||
)} | ||
</EuiFlexGroup> | ||
} | ||
extraAction={ | ||
<EuiFlexGroup gutterSize="m"> | ||
<EuiFlexItem grow={false}> | ||
<ModalConsumer> | ||
{({ onShow, onClose }) => ( | ||
<EuiButtonIcon | ||
iconType="trash" | ||
aria-label="Delete" | ||
color="danger" | ||
onClick={() => | ||
onShow(ConfirmationModal, { | ||
title: "Delete state", | ||
bodyMessage: ( | ||
<EuiText> | ||
<span> | ||
Delete "<strong>{state.name}</strong>" permanently? Deleting the state will result in deleting all transitions. | ||
</span> | ||
</EuiText> | ||
), | ||
actionMessage: "Delete state", | ||
actionProps: { color: "danger" }, | ||
modalProps: { maxWidth: 600 }, | ||
onAction: () => onClickDeleteState(idx), | ||
onClose, | ||
}) | ||
} | ||
data-test-subj="state-delete-button" | ||
/> | ||
)} | ||
</ModalConsumer> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiButtonIcon iconType="pencil" aria-label="Edit" color="primary" onClick={() => onClickEditState(state)} /> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
} | ||
paddingSize="l" | ||
> | ||
<EuiFlexGroup direction="column" style={{ backgroundColor: "#f8f9fc" }}> | ||
<EuiFlexItem grow={false}> | ||
<EuiText> | ||
<h4>Actions</h4> | ||
</EuiText> | ||
</EuiFlexItem> | ||
<EuiFlexItem> | ||
{!state.actions?.length ? ( | ||
<EuiText>No actions. Edit state to add actions.</EuiText> | ||
) : ( | ||
<EuiFlexGroup> | ||
{state.actions.map((action) => ( | ||
<EuiFlexItem grow={false} key={makeId()}> | ||
<EuiPanel>{getUIActionFromData(action).content()}</EuiPanel> | ||
</EuiFlexItem> | ||
))} | ||
</EuiFlexGroup> | ||
)} | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiText> | ||
<h4>Transitions</h4> | ||
</EuiText> | ||
</EuiFlexItem> | ||
<EuiFlexItem> | ||
{!state.transitions?.length ? ( | ||
<EuiText>No transitions. Edit state to add transitions.</EuiText> | ||
) : ( | ||
<EuiFlexGroup> | ||
{state.transitions.map((transition) => ( | ||
<EuiFlexItem grow={false} key={makeId()}> | ||
<EuiPanel> | ||
<TransitionContent transition={transition} /> | ||
</EuiPanel> | ||
</EuiFlexItem> | ||
))} | ||
</EuiFlexGroup> | ||
)} | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiAccordion> | ||
); | ||
|
||
export default State; |
Oops, something went wrong.