@magicfeedback/native 2.2.0-alpha.5 → 2.2.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +315 -475
- package/dist/magicfeedback-sdk.browser.js +1 -1
- package/dist/magicfeedback-sdk.node.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,560 +1,400 @@
|
|
|
1
|
-
#
|
|
1
|
+
# MagicFeedback SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
of [MagicFeedback.io](https://magicfeedback.io/) into your web applications. With minimal code, you can capture valuable
|
|
5
|
-
user feedback and insights, driving continuous improvement and enhancing user experience.
|
|
3
|
+
Browser SDK for rendering MagicFeedback surveys/forms, resuming sessions, previewing question definitions, and sending feedback directly from your app.
|
|
6
4
|
|
|
7
|
-
##
|
|
5
|
+
## What this SDK covers
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
- Render a hosted MagicFeedback form with `appId` + `publicKey`
|
|
8
|
+
- Resume an existing survey flow with `sessionId`
|
|
9
|
+
- Submit feedback directly when you already own the UI
|
|
10
|
+
- Preview one or more question objects locally
|
|
11
|
+
- Use the bundled default theme or override CSS variables
|
|
12
|
+
|
|
13
|
+
## Important before you start
|
|
14
|
+
|
|
15
|
+
- This is a browser-oriented SDK. It relies on `window`, `document`, `navigator`, and `localStorage`.
|
|
16
|
+
- Use it on the client side only. Server-side rendering and Node-only execution are not supported.
|
|
17
|
+
- Call `magicfeedback.init()` before `form()`, `session()`, or `send()`. `init()` sets the API base URL.
|
|
18
|
+
- `form.generate()` expects a DOM element id such as `"survey-root"`, not a CSS selector such as `"#survey-root"`.
|
|
14
19
|
|
|
15
20
|
## Install
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
project using NPM with a front-end packager such as [Browserify](http://browserify.org/)
|
|
19
|
-
or [Webpack](https://webpack.github.io/):
|
|
22
|
+
Install from [npm](https://www.npmjs.com/package/@magicfeedback/native):
|
|
20
23
|
|
|
21
24
|
```sh
|
|
22
|
-
npm
|
|
25
|
+
npm install @magicfeedback/native
|
|
23
26
|
```
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```js
|
|
28
|
-
var magicfeedback = require("@magicfeedback/native");
|
|
29
|
-
|
|
30
|
-
// or
|
|
28
|
+
## Quick start
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
### Plain HTML
|
|
33
31
|
|
|
32
|
+
```html
|
|
33
|
+
<link
|
|
34
|
+
rel="stylesheet"
|
|
35
|
+
href="./node_modules/@magicfeedback/native/dist/styles/magicfeedback-default.css"
|
|
36
|
+
/>
|
|
37
|
+
|
|
38
|
+
<div id="survey-root"></div>
|
|
39
|
+
|
|
40
|
+
<script src="./node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js"></script>
|
|
41
|
+
<script>
|
|
42
|
+
window.magicfeedback.init({
|
|
43
|
+
env: "prod",
|
|
44
|
+
debug: false
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const form = window.magicfeedback.form("APP_ID", "PUBLIC_KEY");
|
|
48
|
+
|
|
49
|
+
form.generate("survey-root", {
|
|
50
|
+
addButton: true,
|
|
51
|
+
addSuccessScreen: true
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
34
54
|
```
|
|
35
55
|
|
|
36
|
-
|
|
56
|
+
### Vite / Webpack / SPA
|
|
37
57
|
|
|
38
|
-
|
|
58
|
+
```ts
|
|
59
|
+
import magicfeedback from "@magicfeedback/native";
|
|
60
|
+
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
|
|
39
61
|
|
|
40
|
-
```js
|
|
41
62
|
magicfeedback.init({
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
dryRun: true | false // Default false. If true, survey answers are not sent to API.
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
`dryRun` is useful to preview surveys and test conditional logic without creating real feedback in MagicFeedback.
|
|
50
|
-
|
|
51
|
-
## How to use
|
|
52
|
-
|
|
53
|
-
This guide provides instructions for utilizing various features and functionalities of the application. Each section
|
|
54
|
-
below highlights a specific use case and provides a code snippet to demonstrate its implementation.
|
|
55
|
-
|
|
56
|
-
## Answer format
|
|
57
|
-
|
|
58
|
-
See `docs/answer-format.md` for the per-type payload format produced by `Form.answer()`.
|
|
59
|
-
|
|
60
|
-
### A. Generate feedback forms
|
|
63
|
+
env: "prod"
|
|
64
|
+
});
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
section provides an overview of how to use this feature and the necessary code snippets.
|
|
66
|
+
const form = magicfeedback.form("APP_ID", "PUBLIC_KEY");
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
await form.generate("survey-root", {
|
|
69
|
+
addButton: true,
|
|
70
|
+
addSuccessScreen: true
|
|
71
|
+
});
|
|
72
|
+
```
|
|
66
73
|
|
|
67
74
|
```html
|
|
68
|
-
|
|
69
|
-
<div id="demo_form_div"></div>
|
|
75
|
+
<div id="survey-root"></div>
|
|
70
76
|
```
|
|
71
77
|
|
|
72
|
-
|
|
78
|
+
## Initialization
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
```js
|
|
77
|
-
let form = window.magicfeedback.form(
|
|
78
|
-
"$_APP_ID",
|
|
79
|
-
"$_PUBLIC_KEY"
|
|
80
|
-
);
|
|
81
|
-
// or
|
|
82
|
-
let form = window.magicfeedback.session(
|
|
83
|
-
"$_SESSION_ID",
|
|
84
|
-
);
|
|
80
|
+
`init()` should be called once before any networked usage.
|
|
85
81
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
nextButtonText: string, // Default "Next", option to change the text of the next button
|
|
93
|
-
addSuccessScreen: boolean, // Default flase, option to add a success screen after send the form
|
|
94
|
-
successMessage: string, // Default "Thank you for your feedback!", option to change the success message
|
|
95
|
-
questionFormat: "standard" | "slim", // Default "standard", option to change the format of the questions.
|
|
96
|
-
getMetaData: boolean, // Default true, option to get the metadata of the form
|
|
97
|
-
beforeSubmitEvent: ({
|
|
98
|
-
loading: boolean,
|
|
99
|
-
progress: number,
|
|
100
|
-
total: number
|
|
101
|
-
}) => {
|
|
102
|
-
}, //Function to execute before send the form
|
|
103
|
-
afterSubmitEvent: ({
|
|
104
|
-
loading: boolean,
|
|
105
|
-
progress: number,
|
|
106
|
-
total: number,
|
|
107
|
-
response: string, // Response of the server if everything is ok
|
|
108
|
-
error: string, // Error of the server if something is wrong
|
|
109
|
-
}) => {
|
|
110
|
-
}, //Function to execute after send the form with the response
|
|
111
|
-
onLoadedEvent: ({
|
|
112
|
-
loading: boolean,
|
|
113
|
-
progress: number,
|
|
114
|
-
total: number,
|
|
115
|
-
formData: FormData
|
|
116
|
-
}) => {
|
|
117
|
-
}, //Function to execute after load the form
|
|
118
|
-
onBackEvent: ({
|
|
119
|
-
loading: boolean,
|
|
120
|
-
progress: number,
|
|
121
|
-
followup: boolean,
|
|
122
|
-
error: string, // Error of the server if something is wrong
|
|
123
|
-
}) => {
|
|
124
|
-
} //Function to execute after back the form
|
|
125
|
-
/*
|
|
126
|
-
class FormData {
|
|
127
|
-
id: string;
|
|
128
|
-
name: string;
|
|
129
|
-
description: string;
|
|
130
|
-
type: string;
|
|
131
|
-
identity: string;
|
|
132
|
-
status: string;
|
|
133
|
-
createdAt: Date;
|
|
134
|
-
updatedAt: Date;
|
|
135
|
-
externalId?: string | null;
|
|
136
|
-
companyId: string;
|
|
137
|
-
productId: string;
|
|
138
|
-
userId: string;
|
|
139
|
-
setting: Record<string, any>;
|
|
140
|
-
conf: Record<string, any>;
|
|
141
|
-
*/
|
|
142
|
-
}
|
|
143
|
-
)
|
|
82
|
+
```ts
|
|
83
|
+
magicfeedback.init({
|
|
84
|
+
env: "prod",
|
|
85
|
+
debug: false,
|
|
86
|
+
dryRun: false
|
|
87
|
+
});
|
|
144
88
|
```
|
|
145
89
|
|
|
146
|
-
|
|
147
|
-
|
|
90
|
+
| Option | Type | Default | Description |
|
|
91
|
+
| --- | --- | --- | --- |
|
|
92
|
+
| `env` | `"prod" \| "dev"` | `"prod"` | Selects the production or development API host. |
|
|
93
|
+
| `debug` | `boolean` | `false` | Enables console logging. |
|
|
94
|
+
| `dryRun` | `boolean` | `false` | Loads and navigates forms without sending feedback or requesting follow-up questions. |
|
|
148
95
|
|
|
149
|
-
|
|
150
|
-
this example). You can customize the form generation by including the optional parameters:
|
|
96
|
+
`dryRun` is the safest way to QA a survey before giving it to a client.
|
|
151
97
|
|
|
152
|
-
|
|
153
|
-
themselves. By default, this value is set to false, indicating that the button will not be displayed.
|
|
154
|
-
* **beforeSubmitEvent**: An optional function that you can define to execute some actions or validations before the form
|
|
155
|
-
is submitted.
|
|
156
|
-
* **afterSubmitEvent**: An optional function that you can define to execute actions after the form is submitted. This
|
|
157
|
-
function receives the server response as a parameter.
|
|
158
|
-
* **onLoadedEvent**: An optional function that you can define to execute actions after the form is loaded.
|
|
98
|
+
## Render a form
|
|
159
99
|
|
|
160
|
-
|
|
161
|
-
following functions to manage the form.
|
|
100
|
+
Create a form instance with an app id and public key:
|
|
162
101
|
|
|
163
|
-
```
|
|
164
|
-
form
|
|
165
|
-
|
|
166
|
-
form.back() // Go to the previous question.
|
|
102
|
+
```ts
|
|
103
|
+
const form = magicfeedback.form("APP_ID", "PUBLIC_KEY");
|
|
167
104
|
```
|
|
168
105
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
106
|
+
Then render it into a container:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
await form.generate("survey-root", {
|
|
110
|
+
addButton: true,
|
|
111
|
+
sendButtonText: "Send",
|
|
112
|
+
backButtonText: "Back",
|
|
113
|
+
nextButtonText: "Next",
|
|
114
|
+
startButtonText: "Start",
|
|
115
|
+
addSuccessScreen: true,
|
|
116
|
+
successMessage: "Thank you for your feedback!",
|
|
117
|
+
questionFormat: "standard",
|
|
118
|
+
getMetaData: true,
|
|
119
|
+
customMetaData: [
|
|
120
|
+
{ key: "customer-id", value: ["acme-42"] },
|
|
121
|
+
{ key: "plan", value: ["enterprise"] }
|
|
122
|
+
],
|
|
123
|
+
onLoadedEvent: ({ formData, progress, total, error }) => {
|
|
124
|
+
console.log("loaded", { formData, progress, total, error });
|
|
177
125
|
},
|
|
178
|
-
{
|
|
179
|
-
"
|
|
180
|
-
"value": "value_2"
|
|
126
|
+
beforeSubmitEvent: ({ progress, total }) => {
|
|
127
|
+
console.log("before submit", { progress, total });
|
|
181
128
|
},
|
|
182
|
-
|
|
183
|
-
|
|
129
|
+
afterSubmitEvent: ({ response, progress, total, completed, followup, error }) => {
|
|
130
|
+
console.log("after submit", {
|
|
131
|
+
response,
|
|
132
|
+
progress,
|
|
133
|
+
total,
|
|
134
|
+
completed,
|
|
135
|
+
followup,
|
|
136
|
+
error
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
onBackEvent: ({ progress, followup, error }) => {
|
|
140
|
+
console.log("back", { progress, followup, error });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
184
143
|
```
|
|
185
144
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
145
|
+
### `generate()` options used by the current runtime
|
|
146
|
+
|
|
147
|
+
| Option | Default | Description |
|
|
148
|
+
| --- | --- | --- |
|
|
149
|
+
| `addButton` | `true` | Renders the built-in action buttons. |
|
|
150
|
+
| `sendButtonText` | `"Send"` | Label for the final submit button. |
|
|
151
|
+
| `backButtonText` | `"Back"` | Label for the back button. |
|
|
152
|
+
| `nextButtonText` | `"Next"` | Label for the next button in multi-step flows. |
|
|
153
|
+
| `startButtonText` | `"Go!"` | Label for the start button when the form has a backend start message. |
|
|
154
|
+
| `addSuccessScreen` | `true` | Shows the built-in success view when the flow finishes. |
|
|
155
|
+
| `successMessage` | `"Thank you for your feedback!"` | Custom success text. |
|
|
156
|
+
| `questionFormat` | `"standard"` | `"standard"` or `"slim"`. |
|
|
157
|
+
| `getMetaData` | `true` | Appends browser and page metadata automatically. |
|
|
158
|
+
| `customMetaData` | `[]` | Extra metadata merged into `feedback.metadata` when `getMetaData` is enabled. |
|
|
159
|
+
| `onLoadedEvent` | `undefined` | Called after the form or start screen is ready. |
|
|
160
|
+
| `beforeSubmitEvent` | `undefined` | Called before a page is submitted. |
|
|
161
|
+
| `afterSubmitEvent` | `undefined` | Called after a page submit, follow-up render, or final completion. |
|
|
162
|
+
| `onBackEvent` | `undefined` | Called after navigating back. |
|
|
163
|
+
|
|
164
|
+
When `getMetaData` is enabled, the SDK includes the current URL, origin, pathname, query string, user agent, browser language, platform, app metadata, screen size, and the session id when rendering from `session()`.
|
|
165
|
+
|
|
166
|
+
## Resume an existing session
|
|
167
|
+
|
|
168
|
+
If you already have a MagicFeedback session id, render it directly:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
magicfeedback.init({ env: "prod" });
|
|
172
|
+
|
|
173
|
+
const form = magicfeedback.session("SESSION_ID");
|
|
174
|
+
await form.generate("survey-root", {
|
|
175
|
+
addButton: true
|
|
176
|
+
});
|
|
194
177
|
```
|
|
195
178
|
|
|
196
|
-
|
|
179
|
+
## Manual navigation
|
|
197
180
|
|
|
198
|
-
|
|
181
|
+
If you want to control your own buttons, disable the built-in actions and call `send()` / `back()` yourself.
|
|
199
182
|
|
|
200
|
-
|
|
201
|
-
|
|
183
|
+
```ts
|
|
184
|
+
const form = magicfeedback.form("APP_ID", "PUBLIC_KEY");
|
|
202
185
|
|
|
203
|
-
|
|
186
|
+
await form.generate("survey-root", {
|
|
187
|
+
addButton: false
|
|
188
|
+
});
|
|
204
189
|
|
|
205
|
-
|
|
206
|
-
|
|
190
|
+
document.getElementById("next-btn")?.addEventListener("click", () => {
|
|
191
|
+
form.send(
|
|
192
|
+
[{ key: "source", value: ["pricing-page"] }],
|
|
193
|
+
[{ key: "account-score", value: ["92"] }],
|
|
194
|
+
[{ key: "customer-email", value: ["user@example.com"] }]
|
|
195
|
+
);
|
|
196
|
+
});
|
|
207
197
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
window.magicfeedback.send(
|
|
212
|
-
"$_APP_ID",
|
|
213
|
-
"$_PUBLIC_KEY",
|
|
214
|
-
feedbackData,
|
|
215
|
-
completed, // Default true
|
|
216
|
-
"$_ID", // Optional
|
|
217
|
-
"$_PRIVATE_KEY", // Optional
|
|
218
|
-
)
|
|
198
|
+
document.getElementById("back-btn")?.addEventListener("click", () => {
|
|
199
|
+
form.back();
|
|
200
|
+
});
|
|
219
201
|
```
|
|
220
202
|
|
|
221
|
-
|
|
222
|
-
|
|
203
|
+
`form.send()` accepts arguments in this order:
|
|
204
|
+
|
|
205
|
+
1. `metadata`
|
|
206
|
+
2. `metrics`
|
|
207
|
+
3. `profile`
|
|
208
|
+
|
|
209
|
+
Each item should follow the same shape:
|
|
223
210
|
|
|
224
|
-
|
|
211
|
+
```ts
|
|
212
|
+
{ key: "some-key", value: ["some-value"] }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Send feedback directly
|
|
225
216
|
|
|
226
|
-
|
|
217
|
+
Use `magicfeedback.send()` when you do not want the SDK to render any UI.
|
|
227
218
|
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
|
|
219
|
+
```ts
|
|
220
|
+
await magicfeedback.send(
|
|
221
|
+
"APP_ID",
|
|
222
|
+
"PUBLIC_KEY",
|
|
223
|
+
{
|
|
224
|
+
text: "",
|
|
231
225
|
answers: [
|
|
232
|
-
{
|
|
233
|
-
|
|
234
|
-
value: ["string"]
|
|
235
|
-
},
|
|
226
|
+
{ key: "nps", value: ["9"] },
|
|
227
|
+
{ key: "favorite-feature", value: ["Conditional logic"] }
|
|
236
228
|
],
|
|
237
229
|
metadata: [
|
|
238
|
-
{
|
|
239
|
-
key: 'string',
|
|
240
|
-
value: "string"
|
|
241
|
-
},
|
|
230
|
+
{ key: "source", value: ["pricing-page"] }
|
|
242
231
|
],
|
|
243
232
|
metrics: [
|
|
244
|
-
{
|
|
245
|
-
key: 'string',
|
|
246
|
-
value: "string"
|
|
247
|
-
},
|
|
233
|
+
{ key: "plan", value: ["pro"] }
|
|
248
234
|
],
|
|
249
235
|
profile: [
|
|
250
|
-
{
|
|
251
|
-
key: 'string',
|
|
252
|
-
value: "string"
|
|
253
|
-
},
|
|
236
|
+
{ key: "email", value: ["user@example.com"] }
|
|
254
237
|
]
|
|
255
|
-
}
|
|
238
|
+
},
|
|
239
|
+
true
|
|
240
|
+
);
|
|
256
241
|
```
|
|
257
242
|
|
|
258
|
-
|
|
259
|
-
* **value**: This setting determines the value of the feedback data.
|
|
260
|
-
|
|
261
|
-
Not all the fields are required. You can send only the fields that you need. But you need to send one of that minimal.
|
|
262
|
-
|
|
263
|
-
Finally, to send the feedback, you can use the magicfeedback.send() function.
|
|
243
|
+
Signature:
|
|
264
244
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
245
|
+
```ts
|
|
246
|
+
magicfeedback.send(
|
|
247
|
+
appId,
|
|
248
|
+
publicKey,
|
|
249
|
+
feedback,
|
|
250
|
+
completed = true,
|
|
251
|
+
id?,
|
|
252
|
+
privateKey?
|
|
253
|
+
);
|
|
271
254
|
```
|
|
272
255
|
|
|
273
|
-
|
|
256
|
+
## Preview a question locally
|
|
257
|
+
|
|
258
|
+
`previewQuestion()` renders one question or an array of questions without changing the internal flow state.
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
const previewForm = magicfeedback.form("demo", "demo");
|
|
262
|
+
|
|
263
|
+
previewForm.previewQuestion("preview-root", {
|
|
264
|
+
id: "q_text",
|
|
265
|
+
title: "What is your name?",
|
|
266
|
+
type: "TEXT",
|
|
267
|
+
questionType: { conf: [] },
|
|
268
|
+
ref: "name",
|
|
269
|
+
require: true,
|
|
270
|
+
external_id: "",
|
|
271
|
+
value: [],
|
|
272
|
+
defaultValue: "",
|
|
273
|
+
followup: false,
|
|
274
|
+
position: 1,
|
|
275
|
+
assets: {
|
|
276
|
+
placeholder: "Type your name",
|
|
277
|
+
subtitle: "Used only for preview"
|
|
278
|
+
},
|
|
279
|
+
refMetric: "",
|
|
280
|
+
integrationId: "demo",
|
|
281
|
+
integrationPageId: "demo"
|
|
282
|
+
}, {
|
|
283
|
+
format: "standard",
|
|
284
|
+
language: "en",
|
|
285
|
+
product: { customIcons: false },
|
|
286
|
+
clearContainer: true,
|
|
287
|
+
wrap: true
|
|
288
|
+
});
|
|
289
|
+
```
|
|
274
290
|
|
|
275
|
-
|
|
276
|
-
|
|
291
|
+
This is useful for QA, local demos, and visual regression checks.
|
|
292
|
+
|
|
293
|
+
## Supported rendered question types
|
|
294
|
+
|
|
295
|
+
The renderer currently supports these question types:
|
|
296
|
+
|
|
297
|
+
- `TEXT`
|
|
298
|
+
- `LONGTEXT`
|
|
299
|
+
- `NUMBER`
|
|
300
|
+
- `RADIO`
|
|
301
|
+
- `MULTIPLECHOICE`
|
|
302
|
+
- `SELECT`
|
|
303
|
+
- `DATE`
|
|
304
|
+
- `EMAIL`
|
|
305
|
+
- `PASSWORD`
|
|
306
|
+
- `BOOLEAN`
|
|
307
|
+
- `CONSENT`
|
|
308
|
+
- `RATING_STAR`
|
|
309
|
+
- `RATING_EMOJI`
|
|
310
|
+
- `RATING_NUMBER`
|
|
311
|
+
- `MULTIPLECHOISE_IMAGE`
|
|
312
|
+
- `MULTI_QUESTION_MATRIX`
|
|
313
|
+
- `POINT_SYSTEM`
|
|
314
|
+
- `PRIORITY_LIST`
|
|
315
|
+
- `INFO_PAGE`
|
|
316
|
+
- `UPLOAD_FILE`
|
|
317
|
+
- `UPLOAD_IMAGE`
|
|
318
|
+
|
|
319
|
+
For the output payload generated by `Form.answer()`, see [docs/answer-format.md](docs/answer-format.md). That document describes payload serialization, while the list above reflects the question types currently rendered by the browser UI.
|
|
320
|
+
|
|
321
|
+
Important payload notes:
|
|
322
|
+
|
|
323
|
+
- `EMAIL` answers are also copied into `feedback.profile` as `email`.
|
|
324
|
+
- `POINT_SYSTEM` answers are serialized as values such as `"Quality:60%"`.
|
|
325
|
+
- `MULTI_QUESTION_MATRIX` answers are grouped into a single JSON string entry.
|
|
326
|
+
- Required `MULTI_QUESTION_MATRIX` questions must have an answer in every row before the SDK allows submission.
|
|
327
|
+
- `INFO_PAGE`, `UPLOAD_FILE`, and `UPLOAD_IMAGE` render in the UI but do not currently create answer entries.
|
|
328
|
+
|
|
329
|
+
## Styling
|
|
330
|
+
|
|
331
|
+
Import the bundled stylesheet:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
|
|
277
335
|
```
|
|
278
336
|
|
|
279
|
-
|
|
337
|
+
Or with plain HTML:
|
|
280
338
|
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
--mf-primary-light: #dbeafe;
|
|
287
|
-
|
|
288
|
-
--mf-text-primary: #0f172a;
|
|
289
|
-
--mf-text-secondary: #64748b;
|
|
290
|
-
--mf-text-muted: #94a3b8;
|
|
291
|
-
|
|
292
|
-
--mf-bg-primary: #ffffff;
|
|
293
|
-
--mf-bg-secondary: #f8fafc;
|
|
294
|
-
--mf-bg-hover: #f1f5f9;
|
|
295
|
-
|
|
296
|
-
--mf-border: #e2e8f0;
|
|
297
|
-
--mf-border-focus: #2563eb;
|
|
298
|
-
|
|
299
|
-
--mf-success: #10b981;
|
|
300
|
-
--mf-error: #ef4444;
|
|
301
|
-
--mf-warning: #f59e0b;
|
|
302
|
-
|
|
303
|
-
/* Spacing */
|
|
304
|
-
--mf-space-xs: 0.25rem;
|
|
305
|
-
--mf-space-sm: 0.5rem;
|
|
306
|
-
--mf-space-md: 0.75rem;
|
|
307
|
-
--mf-space-lg: 1rem;
|
|
308
|
-
--mf-space-xl: 1.5rem;
|
|
309
|
-
|
|
310
|
-
/* Border Radius */
|
|
311
|
-
--mf-radius-sm: 0.375rem;
|
|
312
|
-
--mf-radius-md: 0.5rem;
|
|
313
|
-
--mf-radius-lg: 0.75rem;
|
|
314
|
-
--mf-radius-full: 9999px;
|
|
315
|
-
|
|
316
|
-
/* Shadows */
|
|
317
|
-
--mf-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
318
|
-
--mf-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
319
|
-
--mf-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
320
|
-
--mf-shadow-focus: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
321
|
-
|
|
322
|
-
/* Typography */
|
|
323
|
-
--mf-font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica", "Arial", sans-serif;
|
|
324
|
-
--mf-font-size-sm: 0.875rem;
|
|
325
|
-
--mf-font-size-base: 1rem;
|
|
326
|
-
--mf-font-size-lg: 1.125rem;
|
|
327
|
-
--mf-font-size-xl: 1.25rem;
|
|
328
|
-
--mf-line-height: 1.6;
|
|
329
|
-
|
|
330
|
-
/* Transitions */
|
|
331
|
-
--mf-transition: all 0.2s ease;
|
|
332
|
-
--mf-transition-fast: all 0.15s ease;
|
|
333
|
-
}
|
|
339
|
+
```html
|
|
340
|
+
<link
|
|
341
|
+
rel="stylesheet"
|
|
342
|
+
href="./node_modules/@magicfeedback/native/dist/styles/magicfeedback-default.css"
|
|
343
|
+
/>
|
|
334
344
|
```
|
|
335
345
|
|
|
336
|
-
|
|
346
|
+
You can override the main CSS variables without modifying the distributed file:
|
|
337
347
|
|
|
338
348
|
```css
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
/* Main form element */
|
|
345
|
-
.magicfeedback-form {
|
|
346
|
-
/* ... add your form styles here ... */
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/* Section for questions */
|
|
350
|
-
.magicfeedback-questions {
|
|
351
|
-
/* ... add your questions section styles here ... */
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/* Section for div */
|
|
355
|
-
.magicfeedback-div {
|
|
356
|
-
/* ... add your generic div styles here ... */
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/* Generic styles for various input elements */
|
|
360
|
-
.magicfeedback-label,
|
|
361
|
-
.magicfeedback-input,
|
|
362
|
-
.magicfeedback-contact,
|
|
363
|
-
.magicfeedback-password,
|
|
364
|
-
.magicfeedback-email,
|
|
365
|
-
.magicfeedback-boolean,
|
|
366
|
-
.magicfeedback-consent,
|
|
367
|
-
.magicfeedback-date,
|
|
368
|
-
.magicfeedback-select,
|
|
369
|
-
.magicfeedback-radio,
|
|
370
|
-
.magicfeedback-checkbox,
|
|
371
|
-
.magicfeedback-rating,
|
|
372
|
-
.magicfeedback-rating-container,
|
|
373
|
-
.magicfeedback-rating-option,
|
|
374
|
-
.magicfeedback-rating-option-label-container,
|
|
375
|
-
.magicfeedback-number,
|
|
376
|
-
.magicfeedback-longtext,
|
|
377
|
-
.magicfeedback-text,
|
|
378
|
-
.magicfeedback-priority-list{
|
|
379
|
-
/* ... add your generic input styles here ... */
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
.magicfeedback-skip-container {
|
|
383
|
-
/* ... add your skip container styles here ... */
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
.magicfeedback-skip{
|
|
387
|
-
/* ... add your skip button styles here ... */
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
.magicfeedback-image {
|
|
391
|
-
/* ... add your image styles here ... */
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/* Specific styles for individual input types */
|
|
395
|
-
.magicfeedback-radio-container,
|
|
396
|
-
.magicfeedback-boolean-container,
|
|
397
|
-
.magicfeedback-consent-container,
|
|
398
|
-
.magicfeedback-checkbox-container,
|
|
399
|
-
.magicfeedback-longtext-container,
|
|
400
|
-
.magicfeedback-priority-list-container{
|
|
401
|
-
/* ... add styles for radio/checkbox containers ... */
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
.magicfeedback-rating-placeholder {
|
|
405
|
-
/* ... add your rating placeholder styles here ... */
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
.magicfeedback-rating-placeholder-min {
|
|
409
|
-
/* ... add your rating placeholder min styles here ... */
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
.magicfeedback-rating-placeholder-max {
|
|
413
|
-
/* ... add your rating placeholder max styles here ... */
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
.magicfeedback-rating-image1,
|
|
417
|
-
.magicfeedback-rating-image2,
|
|
418
|
-
.magicfeedback-rating-image3,
|
|
419
|
-
.magicfeedback-rating-image4,
|
|
420
|
-
.magicfeedback-rating-image5,
|
|
421
|
-
.magicfeedback-rating-image6,
|
|
422
|
-
.magicfeedback-rating-image7,
|
|
423
|
-
.magicfeedback-rating-image8,
|
|
424
|
-
.magicfeedback-rating-image9,
|
|
425
|
-
.magicfeedback-rating-image10,
|
|
426
|
-
.magicfeedback-rating-image-extra {
|
|
427
|
-
/* ... add styles for rating images ... */
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/* Section for number rating */
|
|
431
|
-
.magicfeedback-rating-number-container {
|
|
432
|
-
/* ... add your number rating container styles here ... */
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
.magicfeedback-rating-number-placeholder {
|
|
436
|
-
/* ... add your number rating placeholder styles here ... */
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
.magicfeedback-rating-number-placeholder-min {
|
|
440
|
-
/* ... add your number rating placeholder min styles here ... */
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
.magicfeedback-rating-number-placeholder-max {
|
|
444
|
-
/* ... add your number rating placeholder max styles here ... */
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
.magicfeedback-rating-number-option {
|
|
448
|
-
/* ... add your number rating option styles here ... */
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
.magicfeedback-rating-number-option-label-container {
|
|
452
|
-
/* ... add your number rating option label container styles here ... */
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
/* Section for star rating */
|
|
457
|
-
.magicfeedback-rating-star {
|
|
458
|
-
/* ... add your star rating container styles here ... */
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
.magicfeedback-rating-star-container {
|
|
462
|
-
/* ... add your star rating styles here ... */
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
.magicfeedback-rating-star-option {
|
|
466
|
-
/* ... add your star rating option styles here ... */
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
.magicfeedback-rating-star-selected {
|
|
470
|
-
/* ... add your star rating selected styles here ... */
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/* Section for multiple choice image */
|
|
474
|
-
.magicfeedback-multiple-choice-image {
|
|
475
|
-
/* ... add your multiple choice image container styles here ... */
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
.magicfeedback-multiple-choice-image-container {
|
|
479
|
-
/* ... add your multiple choice image styles here ... */
|
|
480
|
-
}
|
|
349
|
+
:root {
|
|
350
|
+
--mf-primary: #0f766e;
|
|
351
|
+
--mf-primary-hover: #115e59;
|
|
352
|
+
--mf-primary-light: #ccfbf1;
|
|
481
353
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
354
|
+
--mf-text-primary: #0f172a;
|
|
355
|
+
--mf-text-secondary: #475569;
|
|
485
356
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
357
|
+
--mf-bg-primary: #ffffff;
|
|
358
|
+
--mf-bg-secondary: #f8fafc;
|
|
489
359
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
360
|
+
--mf-border: #cbd5e1;
|
|
361
|
+
--mf-border-focus: #0f766e;
|
|
493
362
|
|
|
494
|
-
|
|
495
|
-
|
|
363
|
+
--mf-radius-md: 0.5rem;
|
|
364
|
+
--mf-shadow-md: 0 10px 20px rgba(15, 23, 42, 0.08);
|
|
496
365
|
}
|
|
366
|
+
```
|
|
497
367
|
|
|
498
|
-
|
|
499
|
-
/* ... add your multiple choice image image styles here ... */
|
|
500
|
-
}
|
|
368
|
+
For deeper customization, inspect `dist/styles/magicfeedback-default.css` and override the generated classes from your own stylesheet.
|
|
501
369
|
|
|
502
|
-
|
|
503
|
-
.magicfeedback-priority-list-container {
|
|
504
|
-
/* ... add your priority list container styles here ... */
|
|
505
|
-
}
|
|
506
|
-
.magicfeedback-priority-list-list {
|
|
507
|
-
/* ... add your priority list list styles here ... */
|
|
508
|
-
}
|
|
509
|
-
.magicfeedback-priority-list-item{
|
|
510
|
-
/* ... add your priority list item styles here ... */
|
|
511
|
-
}
|
|
512
|
-
.magicfeedback-priority-list-item-label{
|
|
513
|
-
/* ... add your priority list item label styles here ... */
|
|
514
|
-
}
|
|
515
|
-
.magicfeedback-priority-list-arrow-up,
|
|
516
|
-
.magicfeedback-priority-list-arrow-down{
|
|
517
|
-
/* ... add your priority list arrow up styles here ... */
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
/* Action buttons container */
|
|
521
|
-
.magicfeedback-action-container {
|
|
522
|
-
/* ... add your action button container styles here ... */
|
|
523
|
-
}
|
|
370
|
+
## QA and staging
|
|
524
371
|
|
|
525
|
-
|
|
526
|
-
.magicfeedback-submit {
|
|
527
|
-
/* ... add your submit button styles here ... */
|
|
528
|
-
}
|
|
372
|
+
Recommended setup for client review:
|
|
529
373
|
|
|
530
|
-
|
|
531
|
-
.
|
|
532
|
-
|
|
533
|
-
|
|
374
|
+
```ts
|
|
375
|
+
magicfeedback.init({
|
|
376
|
+
env: "dev",
|
|
377
|
+
debug: true,
|
|
378
|
+
dryRun: true
|
|
379
|
+
});
|
|
380
|
+
```
|
|
534
381
|
|
|
535
|
-
|
|
536
|
-
.magicfeedback-start-message-container {
|
|
537
|
-
/* ... add your start message container styles here ... */
|
|
538
|
-
}
|
|
382
|
+
This combination lets you load the survey, navigate questions, and inspect callbacks without creating real feedback records.
|
|
539
383
|
|
|
540
|
-
|
|
541
|
-
/* ... add your start message styles here ... */
|
|
542
|
-
}
|
|
384
|
+
## Examples in this repository
|
|
543
385
|
|
|
544
|
-
.
|
|
545
|
-
|
|
546
|
-
|
|
386
|
+
- `examples/frontend/browser.html` - minimal browser integration
|
|
387
|
+
- `examples/frontend/browser_static.html` - live form plus local previews for all supported question types
|
|
388
|
+
- `examples/frontend/form.html` - embed the SDK inside an existing page layout
|
|
389
|
+
- `examples/frontend/embedded_in_form.html` - combine static inputs with SDK-managed questions
|
|
390
|
+
- `docs/answer-format.md` - exact payload format produced by `Form.answer()`
|
|
547
391
|
|
|
548
|
-
|
|
549
|
-
/* ... add your info page styles here ... */
|
|
550
|
-
}
|
|
392
|
+
## Troubleshooting
|
|
551
393
|
|
|
552
|
-
|
|
553
|
-
/* ... add your info message styles here ... */
|
|
554
|
-
}
|
|
394
|
+
If the form does not load:
|
|
555
395
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
396
|
+
- Confirm that `magicfeedback.init()` has been called.
|
|
397
|
+
- Confirm that the container id passed to `generate()` exists in the DOM.
|
|
398
|
+
- Check that you are using the correct `APP_ID`, `PUBLIC_KEY`, or `SESSION_ID`.
|
|
399
|
+
- Turn on `debug: true` to inspect SDK logs.
|
|
400
|
+
- Use `dryRun: true` when validating the flow before production rollout.
|