diff options
author | saul.gill <saul.gill@est.tech> | 2021-07-12 17:10:10 +0100 |
---|---|---|
committer | saul.gill <saul.gill@est.tech> | 2021-07-19 16:08:22 +0100 |
commit | 3f99e6d9f007477fe6074f470048440d2b29c1ff (patch) | |
tree | f8c35c32fa163fc6f21f04cb61f2c3b6db5545de /gui-clamp/ui-react/src/components | |
parent | 71d6358a8f787c5d2688a485d42ff9514dc58a56 (diff) |
Added Commissioning Template Editor
Added a new modal to allow editing of the tosca template
Json schema from the backend generates a for the user
Added Decommissioning capability while viewing template
Added service for decommissioning
Issue-ID: POLICY-3439
Change-Id: Ic5bec9ea26aa8df468d36a893faa06f88d248dd5
Signed-off-by: saul.gill <saul.gill@est.tech>
Diffstat (limited to 'gui-clamp/ui-react/src/components')
13 files changed, 514 insertions, 13 deletions
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.js new file mode 100644 index 0000000..6baa06c --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.js @@ -0,0 +1,144 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +import Modal from "react-bootstrap/Modal"; +import Button from "react-bootstrap/Button"; +import React, { useEffect, useState } from "react"; +import styled from "styled-components"; +import { JSONEditor } from "@json-editor/json-editor"; +import ControlLoopService from "../../../api/ControlLoopService"; +import OnapConstant from "../../../utils/OnapConstants"; + +const ModalStyled = styled(Modal)` + @media (min-width: 800px) { + .modal-xl { + max-width: 96%; + } + } + background-color: transparent; +` + +const StyledMessagesDiv = styled.div` + overflow: auto; + min-width: 100%; + max-height: 300px; + padding: 5px 5px 0px 5px; + text-align: center; +` + +const CommissioningModal = (props) => { + const [windowLocationPathName, setWindowLocationPathName] = useState(''); + const [toscaInitialValues, setToscaInitialValues] = useState({}); + const [toscaJsonSchema, setToscaJsonSchema] = useState({}); + const [jsonEditor, setJsonEditor] = useState({}); + const [show, setShow] = useState(true); + + const handleClose = () => { + console.log('handleClose called'); + setShow(false); + props.history.push('/'); + } + + const getToscaServiceTemplateHandler = async (toscaServiceTemplateResponse) => { + + if (!toscaServiceTemplateResponse.ok) { + const toscaData = await toscaServiceTemplateResponse; + setToscaInitialValues(toscaData); + } else { + const toscaData = await toscaServiceTemplateResponse.json(); + setToscaInitialValues(toscaData); + } + } + + const getToscaSchemaHandler = async (toscaSchemaResponse) => { + + if (!toscaSchemaResponse.ok) { + const toscaSchemaData = await toscaSchemaResponse; + setToscaJsonSchema(toscaSchemaData); + } else { + const toscaSchemaData = await toscaSchemaResponse.json(); + setToscaJsonSchema(toscaSchemaData); + } + } + + useEffect(async () => { + setWindowLocationPathName(window.location.pathname); + + const toscaTemplateResponse = await ControlLoopService.getToscaControlLoopDefinitions(windowLocationPathName) + .catch(error => error.message); + + const toscaSchemaResponse = await ControlLoopService.getToscaServiceTemplateSchema('node_templates', windowLocationPathName); + + createJsonEditor(await toscaSchemaResponse.json(), await toscaTemplateResponse.json()); + + }, []); + + // TODO Need to Hook this up to a new camel endpoint to get it working + const createJsonEditor = (toscaModel, editorData) => { + JSONEditor.defaults.options.collapse = true; + + return new JSONEditor(document.getElementById("editor"), + { + schema: toscaModel, + startval: editorData, + theme: 'bootstrap4', + iconlib: 'fontawesome5', + object_layout: 'normal', + disable_properties: false, + disable_edit_json: false, + disable_array_reorder: true, + disable_array_delete_last_row: true, + disable_array_delete_all_rows: false, + array_controls_top: true, + keep_oneof_values: false, + collapsed: true, + show_errors: 'always', + display_required_only: false, + show_opt_in: false, + prompt_before_delete: true, + required_by_default: false, + }) + } + + return ( + <ModalStyled size="xl" + show={ show } + onHide={ handleClose } + backdrop="static" + keyboard={ false }> + <Modal.Header closeButton> + <Modal.Title>Edit Control Loop Parameters</Modal.Title> + </Modal.Header> + <br/> + <div style={ { padding: '5px 5px 0px 5px' } }> + <Modal.Body> + <div id="editor"/> + </Modal.Body> + </div> + <Modal.Footer> + <Button variant="primary" + >Submit</Button> + <Button variant="secondary" + onClick={ handleClose }>Close</Button> + </Modal.Footer> + </ModalStyled> + ); +} + +export default CommissioningModal; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.test.js new file mode 100644 index 0000000..f189c58 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.test.js @@ -0,0 +1,69 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import ReadAndConvertYaml from './ReadAndConvertYaml'; +import toJson from "enzyme-to-json"; +import { act } from "react-dom/test-utils"; +import { createMemoryHistory } from "history"; +import CommissioningModal from "./CommissioningModal"; + +describe('Verify ReadAndConvertYaml', () => { + + it("renders without crashing", () => { + shallow(<CommissioningModal/>); + }); + + it("renders correctly", () => { + const tree = shallow(<CommissioningModal/>); + expect(toJson(tree)).toMatchSnapshot(); + }); + + it('should have two Button elements', () => { + const container = shallow(<CommissioningModal/>) + expect(container.find('Button').length).toEqual(2); + }); + + it('handleClose called when bottom button clicked', () => { + const history = createMemoryHistory(); + const component = mount(<CommissioningModal history={ history }/>) + const logSpy = jest.spyOn(console, 'log'); + + + act(() => { + component.find('[variant="secondary"]').simulate('click'); + expect(logSpy).toHaveBeenCalledWith('handleClose called'); + }); + }); + + it('handleClose called when top-right button clicked', () => { + const history = createMemoryHistory(); + const component = mount(<CommissioningModal history={ history }/>) + const logSpy = jest.spyOn(console, 'log'); + + + act(() => { + component.find('[size="xl"]').get(0).props.onHide(); + expect(logSpy).toHaveBeenCalledWith('handleClose called'); + }); + }); + +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/DeleteToscaTemplate.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/DeleteToscaTemplate.js new file mode 100644 index 0000000..c641272 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/DeleteToscaTemplate.js @@ -0,0 +1,54 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import React, { useState } from "react"; +import Button from "react-bootstrap/Button"; +import ControlLoopService from "../../../api/ControlLoopService"; + +const DeleteToscaTemplate = props => { + + const [windowLocationPathName, setWindowLocationPathname] = useState(''); + + const deleteTemplateHandler = async () => { + console.log('deleteTemplateHandler called'); + setWindowLocationPathname(window.location.pathname); + + const response = await ControlLoopService.deleteToscaTemplate(props.templateName, props.templateVersion, windowLocationPathName) + .catch(error => error.message); + + console.log('Response is ok: ' + response.ok); + + props.onDeleteToscaServiceTemplate(response); + + } + + return ( + <React.Fragment> + <Button variant="danger" + type="submit" + style={{float: "right"}} + onClick={ deleteTemplateHandler }>Delete Tosca Service Template</Button> + </React.Fragment> + ); + + +} + +export default DeleteToscaTemplate; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/DeleteToscaTemplate.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/DeleteToscaTemplate.test.js new file mode 100644 index 0000000..e4d437a --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/DeleteToscaTemplate.test.js @@ -0,0 +1,55 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import GetToscaTemplate from './GetToscaTemplate'; +import toJson from "enzyme-to-json"; +import DeleteToscaTemplate from "./DeleteToscaTemplate"; + +describe('Verify DeleteToscaTemplate', () => { + + it("renders without crashing", () => { + shallow(<DeleteToscaTemplate/>); + }); + + it("renders correctly", () => { + const tree = shallow(<DeleteToscaTemplate/>); + expect(toJson(tree)).toMatchSnapshot(); + }); + + it('should have a Button element', () => { + const container = shallow(<DeleteToscaTemplate/>) + expect(container.find('Button').length).toEqual(1); + }); + + it('button should call deleteTemplateHandler when clicked', async () => { + const component = mount(<DeleteToscaTemplate/>) + const logSpy = jest.spyOn(console, 'log'); + + component.find('[variant="danger"]').simulate('click'); + expect(logSpy).toHaveBeenCalledWith('deleteTemplateHandler called'); + }); + + it('should have a Button element with specified text', () => { + const container = shallow(<DeleteToscaTemplate/>) + expect(container.find('Button').text()).toBe('Delete Tosca Service Template'); + }); +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.js index 7da8c13..273957f 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.js @@ -40,7 +40,7 @@ const GetToscaTemplate = (props) => { <React.Fragment> <Button variant="primary" type="submit" - onClick={ getTemplateHandler }>Get Tosca Service Template</Button> + onClick={ getTemplateHandler }>Pull Tosca Service Template</Button> </React.Fragment> ); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.test.js index ce2a398..38da005 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.test.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.test.js @@ -48,6 +48,6 @@ describe('Verify GetToscaTemplate', () => { it('should have a Button element with specified text', () => { const container = shallow(<GetToscaTemplate/>) - expect(container.find('Button').text()).toBe('Get Tosca Service Template'); + expect(container.find('Button').text()).toBe('Pull Tosca Service Template'); }); }); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.js index 53b541c..0ff8833 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.js @@ -24,6 +24,7 @@ import Button from "react-bootstrap/Button"; import { Alert } from "react-bootstrap"; import styled from 'styled-components'; +import DeleteToscaTemplate from "./DeleteToscaTemplate"; const ModalStyled = styled(Modal)` background-color: transparent; @@ -43,7 +44,9 @@ const PreStyled = styled.pre` const ReadAndConvertYaml = (props) => { const [show, setShow] = useState(true); const [toscaTemplateData, setToscaTemplateData] = useState(); - const [responeOk, setResponseOk] = useState(true); + const [deleteToscaTemplateData, setDeleteToscaTemplateData] = useState(); + const [responseOk, setResponseOk] = useState(true); + const [deleteResponseOk, setDeleteResponseOk] = useState(true); const name = 'ToscaServiceTemplateSimple'; const version = '1.0.0'; @@ -57,8 +60,9 @@ const ReadAndConvertYaml = (props) => { // console.log('getToscaServiceTemplateHandler called: ' + toscaServiceTemplate); if (!toscaServiceTemplateResponse.ok) { + console.log('Response is not ok'); setResponseOk(false); - const toscaData = await toscaServiceTemplateResponse; + const toscaData = await toscaServiceTemplateResponse.json(); setToscaTemplateData(toscaData); } else { setResponseOk(true); @@ -67,6 +71,20 @@ const ReadAndConvertYaml = (props) => { } } + const deleteToscaServiceTemplateHandler = async (deleteToscaServiceTemplateResponse) => { + + if (!deleteToscaServiceTemplateResponse.ok) { + console.log('Delete response not ok'); + setDeleteResponseOk(false); + const deleteToscaData = await deleteToscaServiceTemplateResponse; + setDeleteToscaTemplateData(deleteToscaData); + } else { + setDeleteResponseOk(true); + const deleteToscaData = await deleteToscaServiceTemplateResponse.json(); + setDeleteToscaTemplateData(deleteToscaData); + } + } + return ( <ModalStyled size="xl" show={ show } @@ -80,17 +98,28 @@ const ReadAndConvertYaml = (props) => { <GetToscaTemplate templateName={ name } templateVersion={ version } onGetToscaServiceTemplate={ getToscaServiceTemplateHandler }/> - { responeOk && <PreStyled> { JSON.stringify(toscaTemplateData, null, 2) } </PreStyled> } - <AlertStyled show={ !responeOk } - variant="danger">{ toscaTemplateData }</AlertStyled> + { responseOk && <PreStyled> { JSON.stringify(toscaTemplateData, null, 2) } </PreStyled> } + <AlertStyled show={ !responseOk } + variant="danger">{ JSON.stringify(toscaTemplateData, null, 2) }</AlertStyled> + { deleteResponseOk && responseOk && toscaTemplateData != null && + <DeleteToscaTemplate templateName={ name } + templateVersion={ version } + onDeleteToscaServiceTemplate={ deleteToscaServiceTemplateHandler } + /> + } + <AlertStyled show={ !deleteResponseOk } + variant="danger">{ deleteToscaTemplateData }</AlertStyled> + <AlertStyled show={ deleteResponseOk && deleteToscaTemplateData != null } + variant="success"><h2>Delete Successful</h2><PreStyled>{ JSON.stringify(deleteToscaTemplateData, null, 2) }</PreStyled></AlertStyled> </Modal.Body> <Modal.Footer> <Button variant="secondary" type="null" - onClick={ handleClose }>Cancel</Button> + onClick={ handleClose }>Close</Button> </Modal.Footer> </ModalStyled> ); -}; +} +; export default ReadAndConvertYaml; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/CommissioningModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/CommissioningModal.test.js.snap new file mode 100644 index 0000000..78c8983 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/CommissioningModal.test.js.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify ReadAndConvertYaml renders correctly 1`] = ` +<Styled(Modal) + backdrop="static" + keyboard={false} + onHide={[Function]} + show={true} + size="xl" +> + <ModalHeader + closeButton={true} + closeLabel="Close" + > + <ModalTitle> + Edit Control Loop Parameters + </ModalTitle> + </ModalHeader> + <br /> + <div + style={ + Object { + "padding": "5px 5px 0px 5px", + } + } + > + <ModalBody> + <div + id="editor" + /> + </ModalBody> + </div> + <ModalFooter> + <Button + active={false} + disabled={false} + variant="primary" + > + Submit + </Button> + <Button + active={false} + disabled={false} + onClick={[Function]} + variant="secondary" + > + Close + </Button> + </ModalFooter> +</Styled(Modal)> +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/DeleteToscaTemplate.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/DeleteToscaTemplate.test.js.snap new file mode 100644 index 0000000..cd2288e --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/DeleteToscaTemplate.test.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify DeleteToscaTemplate renders correctly 1`] = ` +<Fragment> + <Button + active={false} + disabled={false} + onClick={[Function]} + style={ + Object { + "float": "right", + } + } + type="submit" + variant="danger" + > + Delete Tosca Service Template + </Button> +</Fragment> +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetToscaTemplate.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetToscaTemplate.test.js.snap index 2c591d9..873afa7 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetToscaTemplate.test.js.snap +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetToscaTemplate.test.js.snap @@ -9,7 +9,7 @@ exports[`Verify GetToscaTemplate renders correctly 1`] = ` type="submit" variant="primary" > - Get Tosca Service Template + Pull Tosca Service Template </Button> </Fragment> `; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/ReadAndConvertYaml.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/ReadAndConvertYaml.test.js.snap index fab7801..d515474 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/ReadAndConvertYaml.test.js.snap +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/ReadAndConvertYaml.test.js.snap @@ -30,6 +30,19 @@ exports[`Verify ReadAndConvertYaml renders correctly 1`] = ` show={false} variant="danger" /> + <Styled(Alert) + show={false} + variant="danger" + /> + <Styled(Alert) + show={false} + variant="success" + > + <h2> + Delete Successful + </h2> + <styled.pre /> + </Styled(Alert)> </ModalBody> <ModalFooter> <Button @@ -39,7 +52,7 @@ exports[`Verify ReadAndConvertYaml renders correctly 1`] = ` type="null" variant="secondary" > - Cancel + Close </Button> </ModalFooter> </Styled(Modal)> diff --git a/gui-clamp/ui-react/src/components/menu/MenuBar.js b/gui-clamp/ui-react/src/components/menu/MenuBar.js index f32d6c8..650196c 100644 --- a/gui-clamp/ui-react/src/components/menu/MenuBar.js +++ b/gui-clamp/ui-react/src/components/menu/MenuBar.js @@ -119,8 +119,9 @@ export default class MenuBar extends React.Component { <NavDropdown.Item as={ StyledLink } to="/uploadToscaInstantiation">Upload Tosca Instantiation</NavDropdown.Item> <NavDropdown.Divider /> <NavDropdown.Header>Commissioning</NavDropdown.Header> - <NavDropdown.Item as={ StyledLink } to="/readToscaTemplate">View Tosca Template</NavDropdown.Item> + <NavDropdown.Item as={ StyledLink } to="/readToscaTemplate">Manage Commissioned Tosca Template</NavDropdown.Item> <NavDropdown.Item as={ StyledLink } to="/uploadToscaFile">Upload Tosca to Commissioning</NavDropdown.Item> + <NavDropdown.Item as={ StyledLink } to="/getJsonSchema">Edit Control Loop Type Definitions</NavDropdown.Item> </StyledNavDropdown> <StyledNavDropdown title="Help"> <StyledNavLink href="https://wiki.onap.org/" target="_blank">Wiki</StyledNavLink> diff --git a/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap b/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap index 9543c12..a1705ee 100644 --- a/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap +++ b/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap @@ -1211,7 +1211,7 @@ exports[`Verify MenuBar Test the render method 1`] = ` disabled={false} to="/readToscaTemplate" > - View Tosca Template + Manage Commissioned Tosca Template </DropdownItem> <DropdownItem as={ @@ -1278,6 +1278,71 @@ exports[`Verify MenuBar Test the render method 1`] = ` > Upload Tosca to Commissioning </DropdownItem> + <DropdownItem + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "attrs": Array [], + "componentStyle": e { + "baseHash": -715527839, + "baseStyle": undefined, + "componentId": "sc-bdnxRM", + "isStatic": false, + "rules": Array [ + " + color: ", + [Function], + "; + background-color: ", + [Function], + "; + font-weight: normal; + display: block; + width: 100%; + padding: .25rem 1.5rem; + clear: both; + text-align: inherit; + white-space: nowrap; + border: 0; + + :hover { + text-decoration: none; + background-color: ", + [Function], + "; + color: ", + [Function], + "; + } +", + ], + "staticRulesId": "", + }, + "foldedComponentIds": Array [], + "render": [Function], + "shouldForwardProp": undefined, + "styledComponentId": "sc-bdnxRM", + "target": Object { + "$$typeof": Symbol(react.forward_ref), + "propTypes": Object { + "innerRef": [Function], + "onClick": [Function], + "replace": [Function], + "target": [Function], + "to": [Function], + }, + "render": [Function], + }, + "toString": [Function], + "warnTooManyClasses": [Function], + "withComponent": [Function], + } + } + disabled={false} + to="/getJsonSchema" + > + Edit Control Loop Type Definitions + </DropdownItem> </Styled(NavDropdown)> <Styled(NavDropdown) title="Help" |