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.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/src/components/rework/question/city-search/MPbCitySearch.vue +1 -1
  3. package/src/components/rework/question/name-input/MPbNameInput.stories.mdx +308 -0
  4. package/src/components/rework/question/name-input/MPbNameInput.vue +349 -0
  5. package/src/components/rework/question/name-input/NameInput.ts +22 -0
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. package/src/components/rework/question/name-input/default-payload.json +13 -0
  14. package/src/components/rework/question/space-input/MPbSpaceInput.stories.mdx +297 -0
  15. package/src/components/rework/question/space-input/MPbSpaceInput.vue +511 -0
  16. package/src/components/rework/question/space-input/SpaceInput.ts +29 -0
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. package/src/components/rework/question/space-input/default-payload.json +15 -0
  24. package/src/components/rework/ui/progress/MPbProgress.vue +4 -1
  25. package/src/components/scenario/PbScenario.vue +5 -0
  26. package/src/components/scenario/scenarii/floor-insulation.json +31 -13
  27. package/src/components/trezor/PbTrezor.vue +4 -2
  28. package/src/components/trezor/default-payload.json +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-booster-vue",
3
- "version": "9.53.0",
3
+ "version": "9.55.0",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -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: 'MPbCitySearch',
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
+ [![alt text](https://storage.googleapis.com/project-booster-media/project-booster-vue/project-booster-sources.svg 'Project booster component source code')](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
+ }