5.8 KiB
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 /> places instead of
it should be mapped to an object of type T -
an others: defines UI, create 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
- 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")