project-booster-vue 9.53.0 → 9.55.0
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/package.json +1 -1
- package/src/components/rework/question/city-search/MPbCitySearch.vue +1 -1
- package/src/components/rework/question/name-input/MPbNameInput.stories.mdx +308 -0
- package/src/components/rework/question/name-input/MPbNameInput.vue +349 -0
- package/src/components/rework/question/name-input/NameInput.ts +22 -0
- package/src/components/rework/question/name-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-name-input-/360/237/246/240-101-sandbox-1-snap.png +0 -0
- package/src/components/rework/question/name-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-name-input-/360/237/246/240-showcase-feature-customize-1-snap.png +0 -0
- package/src/components/rework/question/name-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-name-input-/360/237/246/240-showcase-feature-optin-1-snap.png +0 -0
- package/src/components/rework/question/name-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-name-input-/360/237/246/240-showcase-feature-previous-value-from-answer-1-snap.png +0 -0
- package/src/components/rework/question/name-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-name-input-/360/237/246/240-showcase-feature-previous-value-from-answer-with-optin-1-snap.png +0 -0
- package/src/components/rework/question/name-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-name-input-/360/237/246/240-showcase-feature-previous-value-from-default-1-snap.png +0 -0
- package/src/components/rework/question/name-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-name-input-/360/237/246/240-showcase-feature-previous-value-from-default-with-optin-1-snap.png +0 -0
- package/src/components/rework/question/name-input/default-payload.json +13 -0
- package/src/components/rework/question/space-input/MPbSpaceInput.stories.mdx +297 -0
- package/src/components/rework/question/space-input/MPbSpaceInput.vue +511 -0
- package/src/components/rework/question/space-input/SpaceInput.ts +29 -0
- package/src/components/rework/question/space-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-space-input-/360/237/246/240-101-sandbox-1-snap.png +0 -0
- package/src/components/rework/question/space-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-space-input-/360/237/246/240-showcase-feature-custom-footer-1-snap.png +0 -0
- package/src/components/rework/question/space-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-space-input-/360/237/246/240-showcase-feature-customize-1-snap.png +0 -0
- package/src/components/rework/question/space-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-space-input-/360/237/246/240-showcase-feature-previous-value-from-answer-1-snap.png +0 -0
- package/src/components/rework/question/space-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-space-input-/360/237/246/240-showcase-feature-previous-value-from-default-1-snap.png +0 -0
- package/src/components/rework/question/space-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-space-input-/360/237/246/240-showcase-feature-with-modal-1-snap.png +0 -0
- package/src/components/rework/question/space-input/default-payload.json +15 -0
- package/src/components/rework/ui/progress/MPbProgress.vue +4 -1
- package/src/components/scenario/PbScenario.vue +5 -0
- package/src/components/scenario/scenarii/floor-insulation.json +31 -13
- package/src/components/trezor/PbTrezor.vue +4 -2
- package/src/components/trezor/default-payload.json +1 -1
package/package.json
CHANGED
|
@@ -115,7 +115,7 @@ const BACK_ICON =
|
|
|
115
115
|
'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
|
|
116
116
|
|
|
117
117
|
export default defineComponent({
|
|
118
|
-
name: '
|
|
118
|
+
name: 'PbCitySearch',
|
|
119
119
|
|
|
120
120
|
components: {
|
|
121
121
|
MFlex,
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { nestedAppDecorator } from '../../../../../.storybook/nested-app-decorator';
|
|
2
|
+
import { Story, Preview, Meta, Props, ArgsTable, Source, Canvas } from '@storybook/addon-docs';
|
|
3
|
+
import MPbNameInput from './MPbNameInput';
|
|
4
|
+
import DEFAULT_PAYLOAD from './default-payload.json';
|
|
5
|
+
import dedent from 'ts-dedent';
|
|
6
|
+
|
|
7
|
+
<Meta
|
|
8
|
+
title="Project Booster/Rework/Questions/MPbNameInput 🦠"
|
|
9
|
+
component={MPbNameInput}
|
|
10
|
+
argTypes={{
|
|
11
|
+
'payload': {
|
|
12
|
+
table: {
|
|
13
|
+
defaultValue: {
|
|
14
|
+
summary: 'object',
|
|
15
|
+
detail: JSON.stringify(DEFAULT_PAYLOAD, null, ' '),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
control: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
'dynamic event name according to completedEventName prop': {
|
|
23
|
+
table: {
|
|
24
|
+
defaultValue: {
|
|
25
|
+
summary: 'Event payload',
|
|
26
|
+
detail: JSON.stringify({ answers: [{ projectName: 'My project name', optin: false }] }, null, ' '),
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
'onStepCompleted': {
|
|
31
|
+
action: 'Step completed',
|
|
32
|
+
table: { disable: true },
|
|
33
|
+
},
|
|
34
|
+
}}
|
|
35
|
+
decorators={[
|
|
36
|
+
nestedAppDecorator(
|
|
37
|
+
{
|
|
38
|
+
actions: {
|
|
39
|
+
sendEventToBus({}, payload) {
|
|
40
|
+
console.log('Event sent to bus', payload);
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
[],
|
|
45
|
+
),
|
|
46
|
+
]}
|
|
47
|
+
parameters={{ layout: 'fullscreen' }}
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
# 🦠 `MPbNameInput` - Components
|
|
51
|
+
|
|
52
|
+
[](https://github.com/adeo/project-booster-vue/tree/master/src/components/question)
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
The `MPbNameInput` Vue component allows to ask for a name. It
|
|
57
|
+
is customizable through the payload property. It has the same
|
|
58
|
+
behaviour as the `PbQuestion` component and is designed to work in scenarii.
|
|
59
|
+
|
|
60
|
+
It also provides the debrayable optin collect feature.
|
|
61
|
+
|
|
62
|
+
It has a default provided payload, used in the Sandbox below.
|
|
63
|
+
|
|
64
|
+
The input field is automatically focused.
|
|
65
|
+
|
|
66
|
+
The back button will make `vue-router` trigger the history back feature from the browser. It can be hidden with the
|
|
67
|
+
`showBackButton` flag.
|
|
68
|
+
|
|
69
|
+
# `MPbNameInput` - Component props
|
|
70
|
+
|
|
71
|
+
export const TemplateSandbox = (args, { argTypes }) => ({
|
|
72
|
+
props: Object.keys(argTypes),
|
|
73
|
+
components: { MPbNameInput },
|
|
74
|
+
setup() {
|
|
75
|
+
return { args };
|
|
76
|
+
},
|
|
77
|
+
template: `<m-pb-name-input :payload="args.payload" :show-back-button="args.showBackButton" :answers="args.answers" :runtime-options="args.runtimeOptions" :completed-event-name="args.completedEventName" @step-completed="args.onStepCompleted" />`,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
<Canvas>
|
|
81
|
+
<Story name="101 Sandbox" args={{ payload: DEFAULT_PAYLOAD }}>
|
|
82
|
+
{TemplateSandbox.bind({})}
|
|
83
|
+
</Story>
|
|
84
|
+
</Canvas>
|
|
85
|
+
|
|
86
|
+
<ArgsTable story="101 Sandbox" />
|
|
87
|
+
|
|
88
|
+
# `MPbNameInput` - Features
|
|
89
|
+
|
|
90
|
+
## Customization
|
|
91
|
+
|
|
92
|
+
It is possible to customize the component default behaviour by **overriding** a property in the provided `payload` property.
|
|
93
|
+
|
|
94
|
+
Here is the default payload:
|
|
95
|
+
|
|
96
|
+
<Source language="json" code={JSON.stringify(DEFAULT_PAYLOAD, null, ' ')} />
|
|
97
|
+
|
|
98
|
+
Here is the payload with overriden values:
|
|
99
|
+
|
|
100
|
+
export const overridenLabelsPayload = {
|
|
101
|
+
viewModel: {
|
|
102
|
+
backLabel: 'Retour',
|
|
103
|
+
label: 'Quelle est le montant de votre bien ?',
|
|
104
|
+
placeholder: 'Montant de votre bien',
|
|
105
|
+
actionLabel: 'Enregistrer',
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
<Source language="json" code={JSON.stringify(overridenLabelsPayload, null, ' ')} />
|
|
110
|
+
|
|
111
|
+
<Canvas>
|
|
112
|
+
<Story
|
|
113
|
+
name="Showcase - Feature customize"
|
|
114
|
+
parameters={{ controls: { disable: true } }}
|
|
115
|
+
args={{ payload: overridenLabelsPayload }}
|
|
116
|
+
>
|
|
117
|
+
{TemplateSandbox.bind({})}
|
|
118
|
+
</Story>
|
|
119
|
+
</Canvas>
|
|
120
|
+
|
|
121
|
+
## Optin management with runtime options
|
|
122
|
+
|
|
123
|
+
The optin toggle display is managed with the `runtimeOptions` property :
|
|
124
|
+
|
|
125
|
+
export const runtimeOptions = {
|
|
126
|
+
displayOptin: true,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
<Source language="json" code={JSON.stringify(runtimeOptions, null, ' ')} />
|
|
130
|
+
|
|
131
|
+
<Canvas>
|
|
132
|
+
<Story
|
|
133
|
+
name="Showcase - Feature Optin"
|
|
134
|
+
parameters={{ controls: { disable: true } }}
|
|
135
|
+
args={{ runtimeOptions: runtimeOptions }}
|
|
136
|
+
>
|
|
137
|
+
{TemplateSandbox.bind({})}
|
|
138
|
+
</Story>
|
|
139
|
+
</Canvas>
|
|
140
|
+
|
|
141
|
+
## Value injection from a previous answer with default value fallback
|
|
142
|
+
|
|
143
|
+
It is possible to inject a value for the component from a previous answer. To do so, add a `value` to the component
|
|
144
|
+
`payload`. The value should have the same structure as the component anwser when completed (See event payload in the
|
|
145
|
+
[Sandbox story](#pbnameinput---component-props)) :
|
|
146
|
+
|
|
147
|
+
export const valuePayload = {
|
|
148
|
+
value: {
|
|
149
|
+
projectName: "${getAnswerValue('LMFR_PREVIOUS_QUESTION', 'pathForProjectName')}",
|
|
150
|
+
optin: "${getAnswerValue('LMFR_PREVIOUS_QUESTION', 'pathForOptin')}",
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
<Source language="json" code={JSON.stringify(valuePayload, null, ' ')} />
|
|
155
|
+
|
|
156
|
+
The `getAnswerValue` function allows to retrieve a value from one provided answer. To retrieve a value, the first
|
|
157
|
+
parameter is the answer Id, the second is an optionnal path to extract a spcific value.
|
|
158
|
+
|
|
159
|
+
A `defaultDecoratorValue` value can be added as a fallback when no previous answer is found. When no default value is provided and no answer
|
|
160
|
+
is found, the component value will be empty :
|
|
161
|
+
|
|
162
|
+
export const valueWithFallbackPayload = {
|
|
163
|
+
defaultDecoratorValue: {
|
|
164
|
+
projectName: 'Rénover la salle de bain',
|
|
165
|
+
optin: true,
|
|
166
|
+
},
|
|
167
|
+
value: {
|
|
168
|
+
projectName: "${getAnswerValue('LMFR_PREVIOUS_QUESTION', 'pathForProjectName') || defaultValue.projectName}",
|
|
169
|
+
optin: "${getAnswerValue('LMFR_PREVIOUS_QUESTION', 'pathForOptin') || defaultValue.optin}",
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
<Source language="json" code={JSON.stringify(valueWithFallbackPayload, null, ' ')} />
|
|
174
|
+
|
|
175
|
+
It will try to find a value in a previous answer for a question with id `LMFR_PREVIOUS_QUESTION`,
|
|
176
|
+
provided thanks to the `answers` prop. If no value is found, it will use the `defaultDecoratorValue` as a fallback.
|
|
177
|
+
|
|
178
|
+
export const previousAnswers = new Map(
|
|
179
|
+
Object.entries({
|
|
180
|
+
LMFR_PREVIOUS_QUESTION: [
|
|
181
|
+
{
|
|
182
|
+
pathForProjectName: 'Rénover la cuisine',
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
}),
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
<Source language="json" code={JSON.stringify(previousAnswers, null, ' ')} />
|
|
189
|
+
|
|
190
|
+
<Canvas>
|
|
191
|
+
<Story
|
|
192
|
+
name="Showcase - Feature previous value from answer with optin"
|
|
193
|
+
parameters={{ controls: { disable: true } }}
|
|
194
|
+
args={{
|
|
195
|
+
payload: valuePayload,
|
|
196
|
+
answers: previousAnswers,
|
|
197
|
+
}}
|
|
198
|
+
>
|
|
199
|
+
{TemplateSandbox.bind({})}
|
|
200
|
+
</Story>
|
|
201
|
+
</Canvas>
|
|
202
|
+
|
|
203
|
+
When no answer is found, the default value fallback is used:
|
|
204
|
+
|
|
205
|
+
<Canvas>
|
|
206
|
+
<Story
|
|
207
|
+
name="Showcase - Feature previous value from default with optin"
|
|
208
|
+
parameters={{ controls: { disable: true } }}
|
|
209
|
+
args={{ payload: valueWithFallbackPayload }}
|
|
210
|
+
>
|
|
211
|
+
{TemplateSandbox.bind({})}
|
|
212
|
+
</Story>
|
|
213
|
+
</Canvas>
|
|
214
|
+
|
|
215
|
+
With optin value :
|
|
216
|
+
|
|
217
|
+
`runtimeOptions` prop to add option :
|
|
218
|
+
|
|
219
|
+
<Source language="json" code={JSON.stringify(runtimeOptions, null, ' ')} />
|
|
220
|
+
|
|
221
|
+
`answers` prop to add a previous answer :
|
|
222
|
+
|
|
223
|
+
export const previousAnswersWithOptin = {
|
|
224
|
+
LMFR_PREVIOUS_QUESTION: [
|
|
225
|
+
{
|
|
226
|
+
pathForProjectName: 'Rénover la cuisine',
|
|
227
|
+
pathForOptin: true,
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
<Source language="json" code={JSON.stringify(previousAnswersWithOptin, null, ' ')} />
|
|
233
|
+
|
|
234
|
+
<Canvas>
|
|
235
|
+
<Story
|
|
236
|
+
name="Showcase - Feature previous value from answer"
|
|
237
|
+
parameters={{ controls: { disable: true } }}
|
|
238
|
+
args={{
|
|
239
|
+
payload: valuePayload,
|
|
240
|
+
answers: new Map(Object.entries(previousAnswersWithOptin)),
|
|
241
|
+
runtimeOptions: runtimeOptions,
|
|
242
|
+
}}
|
|
243
|
+
>
|
|
244
|
+
{TemplateSandbox.bind({})}
|
|
245
|
+
</Story>
|
|
246
|
+
</Canvas>
|
|
247
|
+
|
|
248
|
+
When no answer is found, the default value fallback is used:
|
|
249
|
+
|
|
250
|
+
export const valueWithFallbackPayloadAndOptin = {
|
|
251
|
+
defaultDecoratorValue: {
|
|
252
|
+
projectName: 'Rénover la salle de bain',
|
|
253
|
+
optin: true,
|
|
254
|
+
},
|
|
255
|
+
value: {
|
|
256
|
+
projectName: "${getAnswerValue('LMFR_PREVIOUS_QUESTION', 'pathForProjectName') || defaultValue.projectName}",
|
|
257
|
+
optin: "${getAnswerValue('LMFR_PREVIOUS_QUESTION', 'pathForOptin') || defaultValue.optin}",
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
<Source language="json" code={JSON.stringify(valueWithFallbackPayloadAndOptin, null, ' ')} />
|
|
262
|
+
|
|
263
|
+
<Canvas>
|
|
264
|
+
<Story
|
|
265
|
+
name="Showcase - Feature previous value from default"
|
|
266
|
+
parameters={{ controls: { disable: true } }}
|
|
267
|
+
args={{
|
|
268
|
+
payload: valueWithFallbackPayloadAndOptin,
|
|
269
|
+
runtimeOptions: runtimeOptions,
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
{TemplateSandbox.bind({})}
|
|
273
|
+
</Story>
|
|
274
|
+
</Canvas>
|
|
275
|
+
|
|
276
|
+
## Custom validation
|
|
277
|
+
|
|
278
|
+
It is possible to customize the input field validation. To do so, provide a `validation` property to the `viewModel`.
|
|
279
|
+
|
|
280
|
+
The default `validation` object is:
|
|
281
|
+
|
|
282
|
+
<Source language="json" code={JSON.stringify(DEFAULT_PAYLOAD.viewModel.validation, null, ' ')} />
|
|
283
|
+
|
|
284
|
+
It is possible to customize those values:
|
|
285
|
+
|
|
286
|
+
export const customValidation = {
|
|
287
|
+
viewModel: {
|
|
288
|
+
validation: {
|
|
289
|
+
minLength: 5,
|
|
290
|
+
minErrorMessage: 'Plus de 5 caractères',
|
|
291
|
+
maxLength: 10,
|
|
292
|
+
maxErrorMessage: 'Moins de 10 caractères',
|
|
293
|
+
requiredErrorMessage: 'Champ obligatoire',
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
<Source language="json" code={JSON.stringify(customValidation, null, ' ')} />
|
|
299
|
+
|
|
300
|
+
<Canvas>
|
|
301
|
+
<Story
|
|
302
|
+
name="Showcase - Feature custom validation"
|
|
303
|
+
parameters={{ controls: { disable: true }, storyshots: { disable: true } }}
|
|
304
|
+
args={{ payload: customValidation }}
|
|
305
|
+
>
|
|
306
|
+
{TemplateSandbox.bind({})}
|
|
307
|
+
</Story>
|
|
308
|
+
</Canvas>
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<m-flex class="pb-name-input" direction="column" align-items="center">
|
|
3
|
+
<m-flex class="pb-name-input__back-button-container" align-items="stretch">
|
|
4
|
+
<m-link
|
|
5
|
+
:label="computedPayload.viewModel.backLabel"
|
|
6
|
+
:left-icon="BACK_ICON"
|
|
7
|
+
:class="{
|
|
8
|
+
'pb-name-input__back-button': true,
|
|
9
|
+
'pb-name-input__back-button--hidden':
|
|
10
|
+
!showBackButton && !decorate(answers, runtimeOptions, payload.viewModel.forceBackButton),
|
|
11
|
+
}"
|
|
12
|
+
@click.once="$emit('go-back')"
|
|
13
|
+
/>
|
|
14
|
+
</m-flex>
|
|
15
|
+
<form class="pb-name-input__form-container" ref="pbNameInputFormObserver" @submit.prevent="handleFormSubmit">
|
|
16
|
+
<div class="pb-name-input__title">
|
|
17
|
+
{{ computedPayload.viewModel.label }}
|
|
18
|
+
</div>
|
|
19
|
+
<m-flex class="pb-name-input__form" direction="column" align-items="stretch">
|
|
20
|
+
<m-text-input
|
|
21
|
+
:name="`text-input-${id}`"
|
|
22
|
+
class="pb-name-input__text-input"
|
|
23
|
+
ref="pbNameInputTextInput"
|
|
24
|
+
:placeholder="computedPayload.viewModel.placeholder"
|
|
25
|
+
v-model="name"
|
|
26
|
+
/>
|
|
27
|
+
<m-flex v-if="runtimeOptions.displayOptin" class="pb-name-input__optin">
|
|
28
|
+
<m-toggle class="pb-name-input__optin-toggle" size="s" v-model="optin" />
|
|
29
|
+
<div class="pb-name-input__optin-label">
|
|
30
|
+
J'accepte de recevoir par email les offres commerciales de Leroy Merlin
|
|
31
|
+
</div>
|
|
32
|
+
</m-flex>
|
|
33
|
+
<m-flex class="pb-name-input__buttons-container" direction="column">
|
|
34
|
+
<m-button
|
|
35
|
+
class="pb-name-input__button"
|
|
36
|
+
:label="computedPayload.viewModel.actionLabel"
|
|
37
|
+
width="full"
|
|
38
|
+
size="l"
|
|
39
|
+
@click="handleFormSubmit"
|
|
40
|
+
/>
|
|
41
|
+
</m-flex>
|
|
42
|
+
</m-flex>
|
|
43
|
+
</form>
|
|
44
|
+
</m-flex>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script lang="ts">
|
|
48
|
+
import { defineComponent, computed, ref, onMounted, ComputedRef, PropType, Ref } from 'vue';
|
|
49
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
50
|
+
import { useStore } from 'vuex';
|
|
51
|
+
import { useForm } from 'vee-validate';
|
|
52
|
+
import * as yup from 'yup';
|
|
53
|
+
import cloneDeep from 'lodash.clonedeep';
|
|
54
|
+
import merge from 'lodash.merge';
|
|
55
|
+
import MFlex from '../../../mozaic/flex/MFlex.vue';
|
|
56
|
+
import MButton from '../../../mozaic/buttons/MButton.vue';
|
|
57
|
+
import MLink from '../../../mozaic/link/MLink.vue';
|
|
58
|
+
import MTextInput from '../../../mozaic/text-input/MTextInput.vue';
|
|
59
|
+
import MToggle from '../../../mozaic/toggle/MToggle.vue';
|
|
60
|
+
import DEFAULT_PAYLOAD from './default-payload.json';
|
|
61
|
+
import { NameInputPayload } from '@/components/question/name-input/NameInput';
|
|
62
|
+
import { ScenarioStepAnswer } from '@/types/pb/Scenario';
|
|
63
|
+
import { decorate } from '@/services/decorate';
|
|
64
|
+
|
|
65
|
+
const BACK_ICON =
|
|
66
|
+
'https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Arrow_Arrow--Left_16px.svg';
|
|
67
|
+
|
|
68
|
+
const initValidation = (componentId: string, computedPayload: ComputedRef<NameInputPayload>) => {
|
|
69
|
+
const validation = computedPayload.value.viewModel.validation;
|
|
70
|
+
const validationSchema = computed(() => {
|
|
71
|
+
return yup.object({
|
|
72
|
+
[`text-input-${componentId}`]: yup
|
|
73
|
+
.string()
|
|
74
|
+
.typeError(validation.notValidTypeMessage ?? 'Saisir un nom de projet')
|
|
75
|
+
.required(validation.requiredErrorMessage ?? 'Le nom du projet est obligatoire')
|
|
76
|
+
.min(
|
|
77
|
+
validation.minLength ?? 0,
|
|
78
|
+
validation.minErrorMessage ?? 'Le nom du projet doit avoir plus de ${min} caractères',
|
|
79
|
+
)
|
|
80
|
+
.max(
|
|
81
|
+
validation.maxLength ?? 60,
|
|
82
|
+
validation.maxErrorMessage ?? 'Le nom du projet doit avoir moins de ${max} caractères',
|
|
83
|
+
),
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
return validationSchema;
|
|
87
|
+
};
|
|
88
|
+
const computeDefaultValue = (
|
|
89
|
+
runtimeOptions: any,
|
|
90
|
+
answers: Map<string, ScenarioStepAnswer[]>,
|
|
91
|
+
computedPayload: ComputedRef<NameInputPayload>,
|
|
92
|
+
) => {
|
|
93
|
+
let name: Ref<string | undefined> = ref(undefined);
|
|
94
|
+
let optin: Ref<string | undefined> = ref(undefined);
|
|
95
|
+
|
|
96
|
+
if (computedPayload?.value?.value) {
|
|
97
|
+
name.value = decorate(
|
|
98
|
+
answers,
|
|
99
|
+
runtimeOptions,
|
|
100
|
+
computedPayload?.value?.value?.projectName,
|
|
101
|
+
computedPayload?.value?.defaultDecoratorValue ?? '',
|
|
102
|
+
);
|
|
103
|
+
optin.value = decorate(
|
|
104
|
+
answers,
|
|
105
|
+
runtimeOptions,
|
|
106
|
+
computedPayload?.value?.value?.optin,
|
|
107
|
+
computedPayload?.value?.defaultDecoratorValue ?? '',
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { name, optin };
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default defineComponent({
|
|
115
|
+
name: 'PbNameInput',
|
|
116
|
+
|
|
117
|
+
components: {
|
|
118
|
+
MFlex,
|
|
119
|
+
MButton,
|
|
120
|
+
MLink,
|
|
121
|
+
MTextInput,
|
|
122
|
+
MToggle,
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
props: {
|
|
126
|
+
/**
|
|
127
|
+
* The component view model and business data as an object. The provided prop
|
|
128
|
+
* is merged with the default payload value so only overriden values will change
|
|
129
|
+
* from the default ones.
|
|
130
|
+
*/
|
|
131
|
+
payload: {
|
|
132
|
+
type: Object as PropType<NameInputPayload>,
|
|
133
|
+
default: () => ({}),
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* The options provided at runtime to customize component behaviour
|
|
137
|
+
*/
|
|
138
|
+
runtimeOptions: {
|
|
139
|
+
type: Object,
|
|
140
|
+
default: () => ({}),
|
|
141
|
+
},
|
|
142
|
+
/**
|
|
143
|
+
* Indicates whether the back button should be displayed
|
|
144
|
+
*/
|
|
145
|
+
showBackButton: {
|
|
146
|
+
type: Boolean,
|
|
147
|
+
default: true,
|
|
148
|
+
},
|
|
149
|
+
/**
|
|
150
|
+
* Name for the event to send when the step is questio is answered
|
|
151
|
+
*/
|
|
152
|
+
completedEventName: {
|
|
153
|
+
type: String,
|
|
154
|
+
default: 'step-completed',
|
|
155
|
+
},
|
|
156
|
+
/**
|
|
157
|
+
* The previous answers to inject
|
|
158
|
+
*/
|
|
159
|
+
answers: {
|
|
160
|
+
type: Object as PropType<Map<string, ScenarioStepAnswer[]>>,
|
|
161
|
+
default: () => new Map<string, ScenarioStepAnswer[]>(),
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
setup(props, { emit }) {
|
|
166
|
+
const componentId = uuidv4();
|
|
167
|
+
|
|
168
|
+
const pbNameInputTextInput = ref<HTMLElement>();
|
|
169
|
+
|
|
170
|
+
const store = useStore();
|
|
171
|
+
|
|
172
|
+
const computedPayload = computed(() => {
|
|
173
|
+
const tempPayload = cloneDeep(DEFAULT_PAYLOAD);
|
|
174
|
+
return merge(tempPayload, props.payload);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const { name, optin } = computeDefaultValue(props.runtimeOptions, props.answers!, computedPayload);
|
|
178
|
+
|
|
179
|
+
const validationSchema = initValidation(componentId, computedPayload);
|
|
180
|
+
const { handleSubmit } = useForm({ validationSchema: validationSchema.value });
|
|
181
|
+
|
|
182
|
+
const handleFormSubmit = handleSubmit(() => {
|
|
183
|
+
store.dispatch('sendEventToBus', {
|
|
184
|
+
code: 'INPUT-NAME-VALIDATION-CLICKED',
|
|
185
|
+
payload: {
|
|
186
|
+
...props.payload,
|
|
187
|
+
context: {
|
|
188
|
+
answers: props.answers,
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Emitted when step is completed
|
|
195
|
+
* @event dynamic event name according to completedEventName prop
|
|
196
|
+
*/
|
|
197
|
+
emit(props.completedEventName, {
|
|
198
|
+
answers: [
|
|
199
|
+
{
|
|
200
|
+
projectName: name.value,
|
|
201
|
+
optin: optin.value,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
onMounted(() => {
|
|
208
|
+
if (props.runtimeOptions?.displayOptin) {
|
|
209
|
+
store.dispatch('sendEventToBus', {
|
|
210
|
+
code: 'OPTIN-MAIL-DISPLAYED',
|
|
211
|
+
payload: {
|
|
212
|
+
...props.payload,
|
|
213
|
+
context: {
|
|
214
|
+
answers: props.answers,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
setTimeout(() => {
|
|
221
|
+
pbNameInputTextInput.value?.focus();
|
|
222
|
+
}, 150);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
BACK_ICON,
|
|
227
|
+
pbNameInputTextInput,
|
|
228
|
+
id: componentId,
|
|
229
|
+
name,
|
|
230
|
+
optin,
|
|
231
|
+
computedPayload,
|
|
232
|
+
handleFormSubmit,
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
</script>
|
|
237
|
+
|
|
238
|
+
<style lang="scss" scoped>
|
|
239
|
+
@import 'pb-variables';
|
|
240
|
+
|
|
241
|
+
$responsive-breakpoint: 'm';
|
|
242
|
+
|
|
243
|
+
.pb-name-input {
|
|
244
|
+
@include set-font-face('regular');
|
|
245
|
+
|
|
246
|
+
flex-grow: 1;
|
|
247
|
+
height: 100vh;
|
|
248
|
+
margin: 0 auto;
|
|
249
|
+
width: calc(100% - #{$mu050} - #{$mu050});
|
|
250
|
+
|
|
251
|
+
@include set-from-screen($responsive-breakpoint) {
|
|
252
|
+
height: 100%;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
&__back-button-container {
|
|
256
|
+
position: absolute;
|
|
257
|
+
left: 0;
|
|
258
|
+
top: 0;
|
|
259
|
+
align-self: flex-start;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
&__back-button {
|
|
263
|
+
opacity: 1;
|
|
264
|
+
padding: $mu100 0 $mu100 $mu025;
|
|
265
|
+
transition: opacity 0.15s 0.5s;
|
|
266
|
+
|
|
267
|
+
&--hidden {
|
|
268
|
+
opacity: 0;
|
|
269
|
+
pointer-events: none;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
&__form-container {
|
|
274
|
+
align-items: center;
|
|
275
|
+
display: flex;
|
|
276
|
+
flex-direction: column;
|
|
277
|
+
flex-grow: 1;
|
|
278
|
+
justify-content: center;
|
|
279
|
+
width: 100%;
|
|
280
|
+
|
|
281
|
+
@include set-from-screen($responsive-breakpoint) {
|
|
282
|
+
width: calc(100% - #{$mu050} - #{$mu050});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
&__form {
|
|
287
|
+
flex-grow: 1;
|
|
288
|
+
max-width: 564px;
|
|
289
|
+
width: calc(100% - 1.5rem);
|
|
290
|
+
|
|
291
|
+
@include set-from-screen($responsive-breakpoint) {
|
|
292
|
+
width: 564px;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
&__title {
|
|
297
|
+
@include set-font-face('semi-bold');
|
|
298
|
+
@include set-font-scale('06', 's');
|
|
299
|
+
|
|
300
|
+
color: $color-grey-700;
|
|
301
|
+
max-width: 100%;
|
|
302
|
+
padding: 0 0 $mu100 0;
|
|
303
|
+
text-align: left;
|
|
304
|
+
width: calc(100% - 1.5rem);
|
|
305
|
+
margin-top: $mu100;
|
|
306
|
+
|
|
307
|
+
@include set-from-screen($responsive-breakpoint) {
|
|
308
|
+
@include set-font-scale('06', 'm');
|
|
309
|
+
|
|
310
|
+
width: 564px;
|
|
311
|
+
padding: $mu250 $mu100 0 $mu100;
|
|
312
|
+
text-align: left;
|
|
313
|
+
margin-top: 0;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
&__text-input {
|
|
318
|
+
margin-bottom: $mu100;
|
|
319
|
+
width: 100%;
|
|
320
|
+
|
|
321
|
+
@include set-from-screen($responsive-breakpoint) {
|
|
322
|
+
width: 50%;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
&__optin {
|
|
327
|
+
margin-bottom: $mu250;
|
|
328
|
+
|
|
329
|
+
&-toggle {
|
|
330
|
+
margin-top: 0.1rem;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
&-label {
|
|
334
|
+
color: $color-grey-800;
|
|
335
|
+
margin-left: $mu050;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
&__buttons-container {
|
|
340
|
+
justify-content: flex-end !important;
|
|
341
|
+
margin-bottom: $mu150;
|
|
342
|
+
width: 100%;
|
|
343
|
+
|
|
344
|
+
@include set-from-screen($responsive-breakpoint) {
|
|
345
|
+
width: 60%;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface NameInputValidation {
|
|
2
|
+
minLength: number;
|
|
3
|
+
minErrorMessage: string;
|
|
4
|
+
maxLength: number;
|
|
5
|
+
maxErrorMessage: string;
|
|
6
|
+
requiredErrorMessage: string;
|
|
7
|
+
notValidTypeMessage: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface NameInputViewModel {
|
|
11
|
+
backLabel: string;
|
|
12
|
+
label: string;
|
|
13
|
+
placeholder: string;
|
|
14
|
+
actionLabel: string;
|
|
15
|
+
validation: NameInputValidation;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface NameInputPayload {
|
|
19
|
+
viewModel: NameInputViewModel;
|
|
20
|
+
value: { projectName: string; optin: string };
|
|
21
|
+
defaultDecoratorValue: string;
|
|
22
|
+
}
|