Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't dupe JsonToReact and DataTypes #366

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
"material-icons": "^0.1.0",
"notistack": "^1.0.10",
"prop-types": "^15.7.2",
"react-ace": "^13.0.0",
"react-beautiful-dnd": "^13.1.1",
"react-color": "^2.19.3",
"react-table": "^7.8.0",
"react-virtualized": "^9.22.3",
"react-virtualized-tree": "^3.4.1",
"react-virtuoso": "^4.6.3",
Expand All @@ -74,6 +78,7 @@
"@material-ui/lab": "^4.0.0-alpha.61",
"@material-ui/pickers": "^3.3.10",
"@material-ui/styles": "^4.11.5",
"@mov-ai/mov-fe-lib-code-editor": "1.1.4-1",
"@mov-ai/mov-fe-lib-core": "^1.2.3-1",
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-controls": "^6.4.19",
Expand Down
578 changes: 461 additions & 117 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions src/Components/ConfigurationSelector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { useState, useRef } from "react";
import PropTypes from "prop-types";
import { IconButton, InputAdornment, TextField } from "@material-ui/core";
import { SCOPES } from "../Utils/Constants";
import SelectScopeModal from "./Modal/SelectScopeModal";
import CodeIcon from "@material-ui/icons/Code";

function formatValue(value) {
return value.split("/").pop();
}

const ConfigurationSelector = (props) => {
// Props
const { rowProps = {} } = props;
// State Hooks
const [openModal, setOpenModal] = useState(false);
const [selected, setSelected] = useState(rowProps.rowData?.value);
// Refs
const inputTextRef = useRef();

/**
* On Configuration selected
* @param {string} selectedConfiguration
*/
const onSubmit = (selectedConfiguration) => {
const formatted = formatValue(selectedConfiguration);
rowProps.onChange(formatted);
setSelected(formatted);
setOpenModal(false);
// Set cursor position
globalThis.setImmediate(() => {
if (!inputTextRef.current) return;
const inputText = inputTextRef.current.querySelector("input");
inputText.focus();
const endPosition = inputText.value.length;
inputText.setSelectionRange(endPosition, endPosition);
});
};

//========================================================================================
/* *
* Render *
* */
//========================================================================================

return (
<TextField
style={{ width: "100%" }}
value={selected || ""}
data-testid="selector-text-input"
onChange={(evt) => {
setSelected(evt.target.value);
rowProps.onChange(evt.target.value);
}}
InputProps={{
ref: inputTextRef,
endAdornment: (
<InputAdornment position="end">
<IconButton
data-testid="open-selector-btn"
aria-label="Select configuration"
onClick={() => setOpenModal(true)}
disabled={props.rowProps.disabled}
onMouseDown={(evt) => evt.preventDefault()}
>
<CodeIcon></CodeIcon>
</IconButton>
<SelectScopeModal
open={openModal}
onCancel={() => setOpenModal(false)}
onSubmit={onSubmit}
scopeList={[SCOPES.CONFIGURATION]}
selected={selected}
allowArchive={false}
></SelectScopeModal>
</InputAdornment>
),
}}
/>
);
};

ConfigurationSelector.propTypes = {
rowProps: {
disabled: PropTypes.bool,
onChange: PropTypes.func,
rowData: PropTypes.shape({
value: PropTypes.string,
}),
},
};

export default ConfigurationSelector;
88 changes: 88 additions & 0 deletions src/Components/ConfigurationSelector/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useRef } from "react";
import { IconButton, InputAdornment, TextField } from "@material-ui/core";
import { SelectScopeModal } from "./../Modal/SelectScopeModal";
import { Document } from "@mov-ai/mov-fe-lib-core";
import CodeIcon from "@material-ui/icons/Code";
import PropTypes from "prop-types";

const ConfigurationSelector = (props) => {
const [openModal, setOpenModal] = React.useState(false);
const [selected, setSelected] = React.useState(null);
const inputTextRef = useRef();
const rowProps = props.rowProps;
const rowData = props.rowProps?.rowData;

const formatConfigurationValue = (configuration) => {
const document = Document.parsePath(configuration, SCOPES.Configuration);
// Temporary validation if document is from archive
// TO BE REMOVED AFTER STANDARDIZATION OF PARSING PROCESS
if (document.workspace !== "global") {
props.interface.alert(
"Please note that only configurations from 'global' workspace are accepted at the moment.",
props.interface.ALERTS.warning,
);
}
// Return formatted config name
return document.name;
};

return (
<TextField
style={{ width: "100%" }}
value={rowData?.value || ""}
data-testid="selector-text-input"
onChange={(evt) => rowProps?.onChange(evt.target.value)}
InputProps={{
ref: inputTextRef,
endAdornment: (
<InputAdornment position="end">
<IconButton
data-testid="open-selector-btn"
aria-label="Select configuration"
onClick={() => setOpenModal(true)}
disabled={props.rowProps.disabled}
onMouseDown={(evt) => evt.preventDefault()}
>
<CodeIcon></CodeIcon>
</IconButton>
<SelectScopeModal
open={openModal}
onCancel={() => setOpenModal(false)}
onSubmit={(selectedConfiguration) => {
const formatted = formatConfigurationValue(
selectedConfiguration,
);
rowProps.onChange(formatted);
setSelected(selectedConfiguration);
setOpenModal(false);
// Set cursor position
setImmediate(() => {
if (!inputTextRef.current) return;
const inputText = inputTextRef.current.querySelector("input");
inputText.focus();
const endPosition = inputText.value.length;
inputText.setSelectionRange(endPosition, endPosition);
});
}}
scopeList={[SCOPES.Configuration]}
selected={selected}
allowArchive={false}
></SelectScopeModal>
</InputAdornment>
),
}}
/>
);
};

const SCOPES = {
Configuration: "Configuration",
};

ConfigurationSelector.defaultProps = {};

ConfigurationSelector.propTypes = {
interface: PropTypes.object.isRequired,
};

export default ConfigurationSelector;
43 changes: 43 additions & 0 deletions src/Components/ConfigurationSelector/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// import React from "react";
// import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
// import ConfigurationSelector from "../ConfigurationSelector";
// import { DATA_TYPES } from "../../Utils/DataTypes";

const mockedRowData = { key: "key", type: "string", value: "value" };
const mockedProps = {
rowData: mockedRowData,
onChange: (val) => {
mockedRowData.value = val;
},
};

describe("Test the component ConfigurationSelector", () => {
it("renders the component (smoke test)", () => {
// const { container } = render(
// <ConfigurationSelector rowProps={mockedProps}></ConfigurationSelector>
// );
// const input = screen.getByTestId("selector-text-input");
// const iconButton = screen.getByTestId("open-selector-btn");
// // Validate if main elements are present in component (container, input and iconButton)
// expect(container).toBeInTheDocument();
// expect(input).toBeInTheDocument();
// expect(iconButton).toBeInTheDocument();
});

it("Open SelectScopeModal and validate rowData.value format", () => {
// render(
// <ConfigurationSelector rowProps={mockedProps}></ConfigurationSelector>
// );
// // simulate click to open modal
// // The mocked SelectScopeModal triggers the onSubmit prop when is opened
// const iconButton = screen.getByTestId("open-selector-btn");
// iconButton.click();
// // Check input value
// setImmediate(() => {
// const value = mockedProps.rowData.value;
// const isValidFormat = DATA_TYPES["config"].validation(value);
// expect(isValidFormat).toBeTruthy();
// });
});
});
52 changes: 52 additions & 0 deletions src/Components/CustomMaterialTable.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useEffect, useRef } from "react";
import MaterialTable from "@material-table/core";

export default function CustomMaterialTable(props) {
const tableRef = useRef();
const openedPanels = useRef({});
const oldFunction = useRef();

useEffect(() => {
if (!oldFunction.current) {
oldFunction.current = tableRef.current?.onToggleDetailPanel;
}

if (oldFunction.current === tableRef.current?.onToggleDetailPanel) {
tableRef.current.onToggleDetailPanel = (path, render) => {
if (tableRef.current.props.data[path[0]]?.tableData?.showDetailPanel) {
delete openedPanels.current[path[0]];
} else {
openedPanels.current = {
...openedPanels.current,
[path[0]]: true,
};
}

oldFunction.current(path, render);
};
}
}, [tableRef]);

return (
<MaterialTable
tableRef={tableRef}
{...props}
data={
props.data?.map((d, i) => {
const detailPanelFunction =
typeof props.detailPanel === "function"
? props.detailPanel
: (rowData) => props.detailPanel[0](rowData).render();
return {
...d,
tableData: {
showDetailPanel: openedPanels.current[i]
? detailPanelFunction
: null,
},
};
}) || []
}
/>
);
}
65 changes: 65 additions & 0 deletions src/Components/JsonToReact/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# JsonToReact

## Introduction

The main motivation for JsonToReact component is to build forms in a easy way. The easy way is to create a function:Json -> Form.
This was implemented through a react component called JsonToReact. The whole concept and json semantics is based on [this project](https://github.com/rjsf-team/react-jsonschema-form).

## Json Semantics

In order to define the form you need 3 Json files:

- schema
- uiSchema
- formData

### Schema

Defines the main structure of the form. It has the following structure

```javascript
Root = {
title: "A Title",
description: "A description",
type: "object",
properties: {
ObjectA: Root || Atom,
...
}
};

Atom = {
AtomName: {
type: "a type",
title: "a title"
},
}
```

### UiSchema

Defines the types of objects.

```javascript
R = {
ObjectName: {
UiAtom || R
}
...
}

UiAtom = {
"ui:<type>": value
}

<type> := disable || widget || options
```

### Data

```javascript
R = {
ObjectA: R || value,
...
}
```
Loading
Loading