196 lines
5.8 KiB
Markdown
196 lines
5.8 KiB
Markdown
|
# Page internal routing
|
||
|
|
||
|
* The SPA is loaded from the BACKOFFICE_URL
|
||
|
|
||
|
* The view to be rendered is decided by the URL fragment
|
||
|
|
||
|
* Query parameters that may affect routing
|
||
|
|
||
|
- instance: use from the default instance to mimic another instance management
|
||
|
|
||
|
* The user must provide BACKEND_URL or BACKOFFICE_URL will use as default
|
||
|
|
||
|
* Token for querying the backend will be saved in localStorage under
|
||
|
backend-token-${name}
|
||
|
|
||
|
# HTTP queries to the backend
|
||
|
|
||
|
HTTP queries will have 4 states:
|
||
|
|
||
|
* loading: request did not end yet. data and error are undefined
|
||
|
|
||
|
* ok: data has information, http response status == 200
|
||
|
|
||
|
* clientError: http response status is between 400 and 499
|
||
|
|
||
|
- notfound: http status 404
|
||
|
|
||
|
- unauthorized: http status 401
|
||
|
|
||
|
* serverError: http response status is grater than 500
|
||
|
|
||
|
There are categories of queries:
|
||
|
|
||
|
* sync: getting information for the page rendering
|
||
|
|
||
|
* async: performing an CRUD operation
|
||
|
|
||
|
## Loading the page information (sync)
|
||
|
|
||
|
In this scenario, a failed request will make the app flow to break.
|
||
|
|
||
|
When receiving an not found error a generic not found page will be shown. If the
|
||
|
BACKEND_URL points to a default instance it should send the user to create the
|
||
|
instance.
|
||
|
|
||
|
When receiving an unauthorized error, the user should be prompted with a login form.
|
||
|
|
||
|
When receiving an another error (400 < http status < 600), the login form should
|
||
|
be shown with an error message using the hint from the backend.
|
||
|
|
||
|
On other unexpected error (like network error), the login form should be shown
|
||
|
with an error message.
|
||
|
|
||
|
## CRUD operation (async)
|
||
|
|
||
|
In this scenario, a failed request does not break the flow but a message will be
|
||
|
prompted.
|
||
|
|
||
|
# Forms
|
||
|
|
||
|
All the input components should be placed in the folder `src/components/from`.
|
||
|
|
||
|
The core concepts are:
|
||
|
|
||
|
* <FormProvider<T> /> places instead of <form /> it should be mapped to an
|
||
|
object of type T
|
||
|
|
||
|
* <Input /> an others: defines UI, create <input /> DOM controls and access the
|
||
|
form with useField()
|
||
|
|
||
|
To use it you will need a state somewhere with the object holding all the form
|
||
|
information.
|
||
|
|
||
|
```
|
||
|
const [state, setState] = useState({ name: '', age: 11 })
|
||
|
```
|
||
|
|
||
|
Optionally an error object an be built with the error messages
|
||
|
|
||
|
```
|
||
|
const errors = {
|
||
|
field1: undefined,
|
||
|
field2: 'should be greater than 18',
|
||
|
}
|
||
|
```
|
||
|
|
||
|
These 3 elements are used to setup the FormProvider
|
||
|
|
||
|
```
|
||
|
<FormProvider errors={errors} object={state} valueHandler={setState}>
|
||
|
...inputs
|
||
|
</FormProvider>
|
||
|
```
|
||
|
|
||
|
Inputs should handle UI rendering and use `useField(name)` to get:
|
||
|
|
||
|
* error: the field has been modified and the value is not correct
|
||
|
* required: the field need to be corrected
|
||
|
* value: the current value of the object
|
||
|
* initial: original value before change
|
||
|
* onChange: function to update the current field
|
||
|
|
||
|
Also, every input must be ready to receive these properties
|
||
|
|
||
|
* name: property of the form object being manipulated
|
||
|
* label: how the name of the property will be shown in the UI
|
||
|
* placeholder: optional, inplace text when there is no value yet
|
||
|
* readonly: default to false, will prevent change the value
|
||
|
* help: optional, example text below the input text to help the user
|
||
|
* tooltip: optional, will add a (i) with a popup to describe the field
|
||
|
|
||
|
|
||
|
# Custom Hooks
|
||
|
|
||
|
All the general purpose hooks should be placed in folder `src/hooks` and tests
|
||
|
under `tests/hooks`. Starts with the `use` word.
|
||
|
|
||
|
# Contexts
|
||
|
|
||
|
All the contexts should be placed in the folder `src/context` as a function.
|
||
|
Should expose provider as a component `<XxxContextProvider />` and consumer as a
|
||
|
hook function `useXxxContext()` (where XXX is the name)
|
||
|
|
||
|
# Components
|
||
|
|
||
|
Type of components:
|
||
|
|
||
|
* main entry point: src/index.tsx, mostly initialization
|
||
|
|
||
|
* routing: in the `src` folder, deciding who is going to take the work. That's
|
||
|
when the page is loading but also create navigation handlers
|
||
|
|
||
|
* pages: in the `paths` folder, setup page information (like querying the
|
||
|
backend for the list of things), handlers for CRUD events, delegated routing
|
||
|
to parent and UI to children.
|
||
|
|
||
|
Some other guidelines:
|
||
|
|
||
|
* Hooks over classes are preferred
|
||
|
|
||
|
* Components that are ready to be reused on any place should be in
|
||
|
`src/components` folder
|
||
|
|
||
|
* Since one of the build targets is a single bundle with all the pages, we are
|
||
|
avoiding route based code splitting
|
||
|
https://github.com/preactjs/preact-cli#route-based-code-splitting
|
||
|
|
||
|
|
||
|
# Testing
|
||
|
|
||
|
Every components should have examples using storybook (xxx.stories.tsx). There
|
||
|
is an automated test that check that every example can be rendered so we make
|
||
|
sure that we do not add a regression.
|
||
|
|
||
|
Every hook should have examples under `tests/hooks` with common usage trying to
|
||
|
follow this structure:
|
||
|
|
||
|
* (Given) set some context of the initial condition
|
||
|
|
||
|
* (When) some action to be tested. May be the initialization of a hook or an
|
||
|
action associated with it
|
||
|
|
||
|
* (Then) a particular set of observable consequences should be expected
|
||
|
|
||
|
# Accessibility
|
||
|
|
||
|
Pages and components should be built with accessibility in mind.
|
||
|
|
||
|
https://github.com/nickcolley/jest-axe
|
||
|
https://orkhanhuseyn.medium.com/accessibility-testing-in-react-with-jest-axe-e08c2a3f3289
|
||
|
http://accesibilidadweb.dlsi.ua.es/?menu=jaws
|
||
|
https://webaim.org/projects/screenreadersurvey8/#intro
|
||
|
https://www.gov.uk/service-manual/technology/testing-with-assistive-technologies#how-to-test
|
||
|
https://es.reactjs.org/docs/accessibility.html
|
||
|
|
||
|
# Internationalization
|
||
|
|
||
|
Every non translated message should be written in English and wrapped into:
|
||
|
|
||
|
* i18n function from useTranslator() hook
|
||
|
* <Translate /> component
|
||
|
|
||
|
Makefile has a i18n that will parse source files and update the po template.
|
||
|
When *.po are updated, running the i18n target will create the strings.ts that
|
||
|
the application will use in runtime.
|
||
|
|
||
|
# Documentation Conventions
|
||
|
|
||
|
* labels
|
||
|
* begin w/ a capital letter
|
||
|
* acronyms (e.g., "URL") are upper case
|
||
|
* tooltips
|
||
|
* begin w/ a lower case letter
|
||
|
* do not end w/ punctuation (period)
|
||
|
* avoid leading article ("a", "an", "the")
|