Merge pull request #457 from nowcommunity/editing-component-docs

Fix documentation links and update editing component docs
This commit is contained in:
Eero Antikainen
2024-11-04 10:01:45 +02:00
committed by GitHub
8 changed files with 45 additions and 37 deletions
+12 -12
View File
@@ -2,17 +2,17 @@
**Data tables**
- [TableView](../frontend/src/components/TableView/TableView.tsx) is a generic component that takes in column definitions, data, and renders a [material-react-table (click for docs)](https://www.material-react-table.com/). It also handles the tables state management: for example saving column filters into url, and restoring them on first render. Creating a new [tableview](./creating_new_tableview.md) in more detail.
- [TableView](../../frontend/src/components/TableView/TableView.tsx) is a generic component that takes in column definitions, data, and renders a [material-react-table (click for docs)](https://www.material-react-table.com/). It also handles the tables state management: for example saving column filters into url, and restoring them on first render. Creating a new [tableview](../guides/creating_new_tableview.md) in more detail.
- [DetailView](../frontend/src/components/DetailView/)-folder has components that are used by the detailed view: The view which allows looking at and editing a single data item, for example a locality. The DetailView-component is generic, allowing different data types to use it.
- [DetailView](../../frontend/src/components/DetailView/)-folder has components that are used by the detailed view: The view which allows looking at and editing a single data item, for example a locality. The DetailView-component is generic, allowing different data types to use it.
**Example of a detail view rendering tree**
- [LocalityDetails.tsx](../frontend/src/components/Locality/LocalityDetails.tsx) queries the data with a RTK Query hook. It then defines tabs, which have a title and a component to render, and gives the tabs and data to a DetailView.
- [LocalityDetails.tsx](../../frontend/src/components/Locality/LocalityDetails.tsx) queries the data with a RTK Query hook. It then defines tabs, which have a title and a component to render, and gives the tabs and data to a DetailView.
- **DetailView** then handles the state and renders the tabs, providing the tabs the [DetailContext](../frontend/src/components/DetailView/Context/DetailContext.tsx). This means that the tabs can access the state through the context, and the edited state is preserved even if the tab is changed.
- **DetailView** then handles the state and renders the tabs, providing the tabs the [DetailContext](../../frontend/src/components/DetailView/Context/DetailContext.tsx). This means that the tabs can access the state through the context, and the edited state is preserved even if the tab is changed.
- The tabs are where the actual layout of shown data is defined. The tab, as well as the components that it renders, can use the context to see if user is viewing or editing data. There are some ready hooks and components to display either a data or a component that allows editing the data. See [editingComponents-file](../frontend/src/components/DetailView/common/editingComponents.tsx). It has components for the commonly used layouts and functionalities. See example for usage in [AgeTab](../frontend/src/components/Locality/Tabs/AgeTab.tsx). Some important utility components in FormComponents-file:
- The tabs are where the actual layout of shown data is defined. The tab, as well as the components that it renders, can use the context to see if user is viewing or editing data. There are some ready hooks and components to display either a data or a component that allows editing the data. See [editingComponents-file](../../frontend/src/components/DetailView/common/editingComponents.tsx). It has components for the commonly used layouts and functionalities. See example for usage in [AgeTab](../../frontend/src/components/Locality/Tabs/AgeTab.tsx). Some important utility components in FormComponents-file:
- The **DataValue** renders, depending on the mode (reading or editing), the value or a component that allows the user to edit the value.
- The **ArrayFrame** allows you to write an entire "frame" (a grouping of data within a tab: for example in a Locality > Age tab > Age) as a two-dimensional array, which can contain for example labels or datavalues. See example in AgeTab. This is the best way to write most tabs.
@@ -20,7 +20,7 @@
Anytime a user edits some data (by typing a value in a text field, selecting a value from a dropdown list...), a corresponding property is edited in `editData`. `editData` is an object that has fields for all the properties of the edited item, and is given by the context. The following image shows a part of the `editData` object for a **locality**:
![abs](./images/editData.png)
![abs](../images/editData.png)
A **context** is basically the state of a given page. A context is initialized in a DetailView component, which is used in all the detail components (such as LocalityDetails or SpeciesDetails). For example, every LocalityDetails component has a DetailView component inside it, which has its own context, and because of this every tab inside that component can access and modify a single `editData` object. You cannot access the `editData` object of locality X from locality Y, because they are not in the same context.
@@ -45,12 +45,12 @@ The context can also be used to access, for example, the **setEditData** setter,
**Documentation for validators**
[Validators](../frontend/src/validators/README.md)
[Validators](../../frontend/src/validators/README.md)
**Detailed documentation for editingComponents**
- [RadioSelector](./editingComponents/RadioSelector.md)
- [DropDownSelector](./editingComponents/DropDownSelector.md)
- [FieldWithTableSelection](./editingComponents/FieldWithTableSelection.md)
- [TimeBoundSelection](./editingComponents/TimeBoundSelection.md)
- [EditableTextField](./editingComponents/EditableTextField.md)
- [RadioSelector](../editingComponents/RadioSelector.md)
- [DropDownSelector](../editingComponents/DropDownSelector.md)
- [FieldWithTableSelection](../editingComponents/FieldWithTableSelection.md)
- [TimeBoundSelection](../editingComponents/TimeBoundSelection.md)
- [EditableTextField](../editingComponents/EditableTextField.md)
@@ -7,6 +7,8 @@ The EditableTextField component is a basic text input field. The component is us
- a `round` value which controls how the value should be rounded if it's a number (Optional)
- a `big` boolean value that changes the field to a multiline field (Optional)
- a `disabled` boolean value (Optional)
- a `readonly` boolean value (Optional)
- a `handleSetEditData` function (Optional)
The component's `type` can be a lot of values, but the ones we use are `text` and `number`.
@@ -14,4 +16,8 @@ The `round` value controls how many values are shown after the decimal point. Th
If `disabled` is set to true, the EditableTextField cannot be clicked.
If `readonly` is set to true, the user can't change the value of the input field directly.
The `handleSetEditData` function is used to set the editData state using setEditData, instead of the default handling. This is useful if you want changing the text field to have an effect on some other input field, e.g. another component. An example use is `handleDmsLatChange` (and three similar functions) in [LocalityTab](../../frontend/src/components/Locality/Tabs/LocalityTab.tsx)
If the selected value is invalid, the EditableTextField will show a red text underneath it explaining the issue. This is done by getting the validator from the current context (if one exists), and then passing it the current `editData` (also from context) and the `field` value. The validator will check if the value in editData corresponding to `field` is valid.
@@ -6,10 +6,13 @@ The RadioSelector component is a group of one or more radio buttons. The compone
- a `name` (Required)
- a `field` value which is the field in editData that the component should change (Required)
- a `defaultValue` (Optional)
- a `handleSetEditData` function (Optional)
The `options` array consists of strings, numbers or objects of type DropDownOption. A DropDownOption is a simple object that contains the actual value, a display string which is used to show the option's name to the user, and an optional optionDisplay string which is an alternate name for the option used, for example, when the option is inside of a menu.
All options have to be of the same type!
If a `defaultValue` is not given, the RadioSelector will choose the first option's value as the default value. This means that a RadioSelector always has a value. The selector will also immediately add this default value to the editData.
The `handleSetEditData` function is used to set the editData state using setEditData, instead of the default handling. This is useful if you want changing the radio selector to have an effect on some other input field, e.g. another component. An example use is `handleDateMethChange` in [AgeTab](../../frontend/src/components/Locality/Tabs/AgeTab.tsx)
The values that the RadioSelector supports are strings, numbers, and booleans as strings. This means that strings 'true' or 'false' will be converted to booleans in the backend. This also means that the options cannot be 'true' or 'false' if you want to actually write those as strings to database.
@@ -4,4 +4,4 @@ The TimeBoundSelection component is almost exactly the same as `FieldWithTableSe
Should not be used for purposes other than time bound/time unit editing!
Check the documentation for [FieldWithTableSelection](./editingComponents/FieldWithTableSelection.md)
Check the documentation for [FieldWithTableSelection](./FieldWithTableSelection.md)
@@ -5,15 +5,12 @@ These ages can be either be an absolute values or a calculated ones. Calculated
In these cases, the maximum age of a Locality is the maximum value of the (fraction of the) selected Time Unit, and the minimum for the minimum age.
A Locality's maximum age and minumum age may be based on the same Time Unit, different Time Units or one is based on a Time Unit and other is an absolute value.
Time Units are build from two Time Bounds: a lower bound and an upper bound. Here the lower bound should be a higher value than the upper bound.
Time Bounds are point-like objects, although they have error margins.
There are validators for each of these entities to keep them from being contradicting within themselves, but a Time Unit may be used in tens of Localities and a Time Bound may be selected for multiple Time Units. It is crucial that editing any of these doesn't result in a Time Unit with upper bound higher than lower or a Locality with minimum age higher than maximum age. In addition to this, if editing a time entity should go through, all affected entities should be edited and these edits should be logged correctly.
At this moment, this is done for editing Time Units. When sending a PUT-request to the backend, the [route](../backend/src/routes/timeUnit.ts) will first check for user rights, then validate the Time Unit. If either of these fail, the process will not continue. If eligible user is editing with a valid Time Unit, the [write-service](../backend/src/services/write/timeUnit.ts) will create a writehandler for the Time Unit and update it without commiting yet. Then (in case of edit) cascading effects will be checked in [cascade handler](../backend/src/utils/cascadeHandler.ts). All Localities connected to the Time Unit in question are queried and new minimum and maximum ages are calculated for them. If any of these Localities would become contradicting, no changes will be made any tables and the user will be notified, which Localities are creating the problem. If all Localities would be ok, all of them would be updated and logged correctly, after which, the Time Unit edit would be also logged and committed.
At this moment, this is done for editing Time Units. When sending a PUT-request to the backend, the [route](../../backend/src/routes/timeUnit.ts) will first check for user rights, then validate the Time Unit. If either of these fail, the process will not continue. If eligible user is editing with a valid Time Unit, the [write-service](../../backend/src/services/write/timeUnit.ts) will create a writehandler for the Time Unit and update it without commiting yet. Then (in case of edit) cascading effects will be checked in [cascade handler](../../backend/src/utils/cascadeHandler.ts). All Localities connected to the Time Unit in question are queried and new minimum and maximum ages are calculated for them. If any of these Localities would become contradicting, no changes will be made any tables and the user will be notified, which Localities are creating the problem. If all Localities would be ok, all of them would be updated and logged correctly, after which, the Time Unit edit would be also logged and committed.
Similar logic for Time Bounds is under construction
@@ -2,11 +2,12 @@
Both editing and creating new entries go via same route in the app. Following things are needed for an entry (examples are from species) to work:
- Backend:
- A `put`-route in corresponding [routes file](../backend/src/routes/species.ts) that calls requireOneOf to check user rights, a validation function to check the data and the writer function to insert the data to the database and return the id of the entry if all checks pass.
- A validator and a write functions in [services](../backend/src/services/species.ts) for the route to call.
- Backend:
- Frontend:
- [Spesific validators](../frontend/src/validators/species.ts) are defined here.
- An api-endpoint for edit needs to be defined in [reducers](../frontend/src/redux/speciesReducer.ts) (this serves for creating a new entry as well)
- An onWrite-function needs to be defined with the help of the api-endpoint and call navigate-function to end up in the [details-view](../frontend/src/components/Species/SpeciesDetails.tsx) of the edited/created entry. (At the moment there's a 15ms time-out before the navigation for species as sometimes it tried to navigate before knowing where to (no id yet). This could be investigated if this is a more general bug or something weird happening only in species)
- A `put`-route in corresponding [routes file](../../backend/src/routes/species.ts) that calls requireOneOf to check user rights, a validation function to check the data and the writer function to insert the data to the database and return the id of the entry if all checks pass.
- A validator and a write functions in [services](../../backend/src/services/species.ts) for the route to call.
- Frontend:
- [Spesific validators](../../frontend/src/validators/species.ts) are defined here.
- An api-endpoint for edit needs to be defined in [reducers](../../frontend/src/redux/speciesReducer.ts) (this serves for creating a new entry as well)
- An onWrite-function needs to be defined with the help of the api-endpoint and call navigate-function to end up in the [details-view](../../frontend/src/components/Species/SpeciesDetails.tsx) of the edited/created entry. (At the moment there's a 15ms time-out before the navigation for species as sometimes it tried to navigate before knowing where to (no id yet). This could be investigated if this is a more general bug or something weird happening only in species)
+10 -9
View File
@@ -2,14 +2,15 @@
Following things are needed to create a new page with a table (examples are all from Cross-Search):
- Backend:
- A [service](../backend/src/services/crossSearch.ts) to get the data from the database. There might be a necessities to clean the result, filter the result by project ids (pid) and user's permissions, and create a temp type for typescript.
- A [route](../backend/src/routes/crossSearch.ts) function calling the service.
- The [App](../backend/src/app.ts) should use the route.
- Backend:
- A [service](../../backend/src/services/crossSearch.ts) to get the data from the database. There might be a necessities to clean the result, filter the result by project ids (pid) and user's permissions, and create a temp type for typescript.
- A [route](../../backend/src/routes/crossSearch.ts) function calling the service.
- The [App](../../backend/src/app.ts) should use the route.
- Frontend:
- Create necessary types in [backendtypes](../frontend/src/backendTypes.d.ts)
- Create a [component](../frontend/src/components/CrossSearch/CrossSearchTable.tsx) (note that cross search borrows the detail parts from localities). The tableview is defined via variables columns and visibleColumns. Columns in the most simple form only require an accesrKey and a header. If an accessorFn is used, a separate id is required as well. The accessorkey functions as a key to fetch data as well as an id to reference the column as well. The variable visibleColumns takes in accessorKeys or ids for columns, set to false, to be hidden by default.
- Create a button for the component in [navigation bar](../frontend/src/components/NavBar.tsx)
- Create a relevant page-object for [Pages](../frontend/src/components/pages.tsx)
- Add a route for the component to [App](../frontend/src/App.tsx)
- Create necessary types in [backendtypes](../../frontend/src/backendTypes.d.ts)
- Create a [component](../../frontend/src/components/CrossSearch/CrossSearchTable.tsx) (note that cross search borrows the detail parts from localities). The tableview is defined via variables columns and visibleColumns. Columns in the most simple form only require an accesrKey and a header. If an accessorFn is used, a separate id is required as well. The accessorkey functions as a key to fetch data as well as an id to reference the column as well. The variable visibleColumns takes in accessorKeys or ids for columns, set to false, to be hidden by default.
- Create a button for the component in [navigation bar](../../frontend/src/components/NavBar.tsx)
- Create a relevant page-object for [Pages](../../frontend/src/components/pages.tsx)
- Add a route for the component to [App](../../frontend/src/App.tsx)
+1 -1
View File
@@ -6,7 +6,7 @@
2. Place the database dump files `now_test.sql`, `now_log_test.sql` and `now_view.sql` inside it.
3. Notice, that the container will have to be able to read these files and execute `data/restore_and_create_user.sql`, so you may have to adjust the necessary rights depending on your operating system.
See [technical explanations](technical_explanations.md) for how this works.
See [technical explanations](./guides/technical_explanations.md) for how this works.
**Init the project** :rocket: