summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/DESIGN.md
blob: d6252ccdc8a71a262aa11cde5771f9d1993b0206 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# 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")