project-booster-vue 9.24.1 → 9.27.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 (21) hide show
  1. package/package.json +1 -1
  2. package/src/components/cards/PbCard.vue +14 -2
  3. package/src/components/mozaic/buttons/MButton.vue +26 -0
  4. package/src/components/mozaic/quantityselector/MQuantitySelector.stories.mdx +48 -0
  5. package/src/components/mozaic/quantityselector/MQuantitySelector.vue +188 -0
  6. package/src/components/mozaic/text-input/MTextInput.vue +59 -4
  7. package/src/components/question/PbQuestion-Features-Answers-Conditional.stories.mdx +2 -1
  8. package/src/components/question/PbQuestion-Features-Answers-Modal.stories.mdx +122 -0
  9. package/src/components/question/PbQuestion.vue +99 -1
  10. package/src/components/question/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-question-/360/237/246/240-features-answers-conditional-with-modal-1-snap.png +0 -0
  11. package/src/components/question/incremental-amount-input/IncrementalAmount.ts +24 -0
  12. package/src/components/question/incremental-amount-input/PbIncrementalAmountInput.stories.mdx +50 -0
  13. package/src/components/question/incremental-amount-input/PbIncrementalAmountInput.vue +332 -0
  14. package/src/components/question/incremental-amount-input/__snapshots__/storyshots-puppeteer-test-puppeteer-ts-image-storyshots-project-booster-scenario-questions-pb-incremental-amount-input-/360/237/246/240-101-sandbox-1-snap.png +0 -0
  15. package/src/components/question/incremental-amount-input/default-payload.json +12 -0
  16. package/src/components/question/space-input/PbSpaceInput.stories.mdx +48 -0
  17. package/src/components/question/space-input/PbSpaceInput.vue +88 -0
  18. package/src/components/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
  19. package/src/components/scenario/PbScenario.vue +4 -0
  20. package/src/components/warning-message/PbWarningMessage.vue +1 -1
  21. package/src/types/pb/Scenario.ts +7 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-booster-vue",
3
- "version": "9.24.1",
3
+ "version": "9.27.0",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -5,9 +5,9 @@
5
5
  disabled ? 'pb-card--disabled' : ''
6
6
  }${flattened ? 'pb-card--no-pointer-events' : ''} ${
7
7
  cardMinRatio && cardMinRatio !== 'auto' ? 'm-ratio-container--' + cardMinRatio : ''
8
- }`"
8
+ }${borderTop ? 'pb-card--border-top' : ''}`"
9
9
  :role="!buttonLabel ? 'button' : 'article'"
10
- :style="`min-height: ${cardMinHeight}`"
10
+ :style="`min-height: ${cardMinHeight}; border-color:${borderTop ? borderTop : null}`"
11
11
  @click="handleCardClick"
12
12
  >
13
13
  <m-flex
@@ -309,6 +309,13 @@ export default defineComponent({
309
309
  type: [Boolean, String],
310
310
  default: false,
311
311
  },
312
+ /**
313
+ * Definies if the card has a border top
314
+ */
315
+ borderTop: {
316
+ type: String,
317
+ default: null,
318
+ },
312
319
  },
313
320
 
314
321
  computed: {
@@ -361,6 +368,11 @@ $responsive-breakpoint: 's';
361
368
  overflow: hidden;
362
369
  position: relative;
363
370
 
371
+ &--border-top {
372
+ border-top-style: solid !important;
373
+ border-top-width: 4px !important;
374
+ }
375
+
364
376
  &__items-container:empty ~ &__image-container {
365
377
  height: 100%;
366
378
 
@@ -35,6 +35,7 @@
35
35
  class="mc-button"
36
36
  :class="[...themeClasses, ...sizeClasses, ...widthClasses, $attrs.class]"
37
37
  :aria-label="label"
38
+ :disabled="disabled"
38
39
  >
39
40
  <!-- Add wrapper to mitigate iOS 10 bug: https://github.com/philipwalton/flexbugs#9-some-html-elements-cant-be-flex-containers -->
40
41
  <m-flex class="mc-button__content" direction="row" align-items="center" justify-content="center">
@@ -70,6 +71,8 @@ export const M_BUTTON_VALIDATOR = {
70
71
  null,
71
72
  'solid',
72
73
  'bordered',
74
+ 'bordered-left',
75
+ 'bordered-right',
73
76
  'bordered-neutral',
74
77
  'solid-neutral',
75
78
  'solid-primary-02',
@@ -94,6 +97,13 @@ export default defineComponent({
94
97
  },
95
98
 
96
99
  props: {
100
+ /**
101
+ * Disabled button
102
+ */
103
+ disabled: {
104
+ type: Boolean,
105
+ default: false,
106
+ },
97
107
  /**
98
108
  * The button label
99
109
  */
@@ -531,6 +541,22 @@ $text-neutral: (
531
541
  .mc-button--text-neutral {
532
542
  @include set-button-theme($text-neutral);
533
543
  }
544
+
545
+ .mc-button--bordered-left {
546
+ background: transparent;
547
+ border-color: $color-success-500;
548
+ border-radius: 0.25rem 0 0 0.25rem;
549
+ color: $color-success-500;
550
+ padding: 0.375rem;
551
+ }
552
+
553
+ .mc-button--bordered-right {
554
+ background: transparent;
555
+ border-color: $color-success-500;
556
+ border-radius: 0 0.25rem 0.25rem 0;
557
+ color: $color-success-500;
558
+ padding: 0.375rem;
559
+ }
534
560
  </style>
535
561
 
536
562
  <style lang="scss">
@@ -0,0 +1,48 @@
1
+ import { Meta, Canvas, Story, ArgsTable } from '@storybook/addon-docs';
2
+ import MQuantitySelector, { M_NOTIFICATION_VALIDATOR } from './MQuantitySelector';
3
+
4
+ <Meta title="Mozaic/Components/MQuantitySelector 🧬" component={MQuantitySelector} argTypes={{}} />
5
+
6
+ # 🧬 `MQuantitySelector` - Component
7
+
8
+ [![alt text](https://storage.googleapis.com/project-booster-media/project-booster-vue/mozaic-documentation.svg 'Mozaic component documentation')](http://mozaic.adeo.cloud/Components/Notification/)
9
+ [![alt text](https://storage.googleapis.com/project-booster-media/project-booster-vue/project-booster-wireframes.svg 'Project booster component wireframes')](https://www.figma.com/file/XqqYm6Trew66wygIrVdH6r/%5BLIBRAIRIE%5D-Composants-Project-Booster?node-id=389%3A0)
10
+ [![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/mozaic/notifications)
11
+
12
+ ---
13
+
14
+ The `MQuantitySelector` Vue component is the implementation of the [mozaic design system **notification** component](http://mozaic.adeo.cloud/Components/Notification/).
15
+
16
+ # `MQuantitySelector` - Component props
17
+
18
+ export const TemplateSandbox = (args, { argTypes }) => ({
19
+ props: Object.keys(argTypes),
20
+ components: { MQuantitySelector },
21
+ setup() {
22
+ return { args };
23
+ },
24
+ template: `<m-quantity-selector
25
+ :value="4"
26
+ :valuemin="0"
27
+ :valuemax="50"
28
+ />`,
29
+ });
30
+
31
+ <Canvas>
32
+ <Story
33
+ name="101 Sandbox"
34
+ inline={false}
35
+ height="512px"
36
+ args={{
37
+ title: 'Notification title',
38
+ text: 'Notification text',
39
+ linkLabel: 'Notification link',
40
+ closable: true,
41
+ }}
42
+ parameters={{ storyshots: { disable: true } }}
43
+ >
44
+ {TemplateSandbox.bind({})}
45
+ </Story>
46
+ </Canvas>
47
+
48
+ <ArgsTable story="101 Sandbox" />
@@ -0,0 +1,188 @@
1
+ <template>
2
+ <div class="mc-quantity-selector">
3
+ <m-flex class="mc-quantity-selector__container" :class="cssFieldElementClass">
4
+ <m-button
5
+ class="mc-quantity-selector__button-left"
6
+ theme="bordered-left"
7
+ leftIcon="https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Control_Less_32px.svg"
8
+ icon-position="left"
9
+ :aria-label="decrementAriaLabel"
10
+ :aria-controls="id"
11
+ :disabled="currentValue <= valuemin"
12
+ :size="small ? 's' : null"
13
+ tabindex="-1"
14
+ type="button"
15
+ aria-hidden="true"
16
+ @click="decrement()"
17
+ />
18
+
19
+ <m-text-input
20
+ :id="id"
21
+ v-model="currentValue"
22
+ type="number"
23
+ class="mc-quantity-selector__input"
24
+ name="quantity-selector-input"
25
+ :aria-label="inputAriaLabel"
26
+ :aria-valuenow="currentValue"
27
+ :aria-valuemin="valuemin"
28
+ :aria-valuemax="valuemax"
29
+ :placeholder="placeholder"
30
+ :rounded="false"
31
+ :textCenter="true"
32
+ :blankField="true"
33
+ :size="small ? 's' : null"
34
+ role="spinbutton"
35
+ @input="handle($event.target.value)"
36
+ @keypress="integerOnly && formatValue($event)"
37
+ />
38
+
39
+ <m-button
40
+ class="mc-quantity-selector__button-right"
41
+ theme="bordered-right"
42
+ leftIcon="https://storage.googleapis.com/project-booster-media/mozaic-icons/svg/Navigation_Control_More_32px.svg"
43
+ icon-position="right"
44
+ :aria-label="incrementAriaLabel"
45
+ :aria-controls="id"
46
+ :disabled="currentValue === valuemax"
47
+ :size="small ? 's' : null"
48
+ tabindex="-1"
49
+ aria-hidden="true"
50
+ type="button"
51
+ @click="increment()"
52
+ />
53
+ </m-flex>
54
+ <div v-if="!(error || errorMessage) && !info" class="m-text-input__error-placeholder mc-field__error-message">
55
+ &nbsp;
56
+ </div>
57
+ <div v-else-if="error || errorMessage" class="m-text-input__error mc-field__error-message">
58
+ {{ error || errorMessage }}
59
+ </div>
60
+ <div v-else-if="info" class="m-text-input__info mc-field__error-message">
61
+ {{ info }}
62
+ </div>
63
+ </div>
64
+ </template>
65
+
66
+ <script>
67
+ import MButton from '../buttons/MButton.vue';
68
+ import MTextInput from '../text-input/MTextInput.vue';
69
+ import MFlex from '../flex/MFlex.vue';
70
+
71
+ export default {
72
+ name: 'MQuantitySelector',
73
+
74
+ components: {
75
+ MButton,
76
+ MTextInput,
77
+ MFlex,
78
+ },
79
+
80
+ inject: {
81
+ cssFieldElementClass: {
82
+ default: '',
83
+ },
84
+ },
85
+
86
+ props: {
87
+ id: {
88
+ type: String,
89
+ default: 'qty-selector',
90
+ },
91
+ value: {
92
+ type: Number,
93
+ default: 0,
94
+ },
95
+ inputAriaLabel: {
96
+ type: String,
97
+ default: 'Quantity Selector',
98
+ },
99
+ decrementAriaLabel: {
100
+ type: String,
101
+ default: 'Decrement',
102
+ },
103
+ incrementAriaLabel: {
104
+ type: String,
105
+ default: 'Increment',
106
+ },
107
+ valuemin: {
108
+ type: Number,
109
+ default: 1,
110
+ },
111
+ valuemax: {
112
+ type: Number,
113
+ default: 100,
114
+ },
115
+ placeholder: {
116
+ type: String,
117
+ default: null,
118
+ },
119
+ small: {
120
+ type: Boolean,
121
+ default: false,
122
+ },
123
+ integerOnly: {
124
+ type: Boolean,
125
+ default: false,
126
+ },
127
+ error: {
128
+ type: String,
129
+ default: null,
130
+ },
131
+ },
132
+
133
+ emits: ['input', 'increment', 'decrement'],
134
+
135
+ data() {
136
+ return {
137
+ currentValue: this.value || this.valuemin,
138
+ };
139
+ },
140
+
141
+ watch: {
142
+ currentValue(newValue, oldValue) {
143
+ this.handle(newValue);
144
+ },
145
+ },
146
+
147
+ methods: {
148
+ handle(value) {
149
+ this.currentValue = value;
150
+ if (this.currentValue > this.valuemax) {
151
+ this.currentValue = this.valuemax;
152
+ }
153
+ if (this.currentValue < this.valuemin) {
154
+ this.currentValue = this.valuemin;
155
+ }
156
+ this.$emit('input', this.currentValue);
157
+ },
158
+ increment() {
159
+ if (this.currentValue < this.valuemax) {
160
+ this.currentValue++;
161
+ this.$emit('increment', this.currentValue);
162
+ }
163
+ },
164
+ decrement() {
165
+ if (this.currentValue > this.valuemin) {
166
+ this.currentValue--;
167
+ this.$emit('decrement', this.currentValue);
168
+ }
169
+ },
170
+ formatValue(e) {
171
+ const INTEGER_ONLY_REGEX = /[0-9/]+/;
172
+ if (!INTEGER_ONLY_REGEX.test(e.key)) {
173
+ e.preventDefault();
174
+ }
175
+ },
176
+ },
177
+ };
178
+ </script>
179
+
180
+ <style lang="scss">
181
+ @import 'settings-tools/_all-settings';
182
+ @import 'components/_c.quantity-selector';
183
+
184
+ .mc-quantity-selector__button-left,
185
+ .mc-quantity-selector__button-right {
186
+ margin: 0.5rem 0;
187
+ }
188
+ </style>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="m-text-input mc-field">
2
+ <div class="m-text-input mc-field" :class="{ 'm-text-input__nomargin': blankField }">
3
3
  <label v-if="label" class="m-text-input__label mc-field__label" :for="`input-${id}`">
4
4
  {{ label }}
5
5
  <span v-if="required" class="mc-field__requirement" aria-hidden="true"> obligatoire </span>
@@ -31,6 +31,8 @@
31
31
  'is-valid': valid,
32
32
  'is-invalid': invalid || errorMessage,
33
33
  'mc-text-input--s': size === 's',
34
+ 'unrounded': !rounded,
35
+ 'text-center': textCenter,
34
36
  }"
35
37
  :name="name"
36
38
  :disabled="disabled"
@@ -45,13 +47,25 @@
45
47
  @blur="handleBlurEvent"
46
48
  />
47
49
  </div>
48
- <div v-if="!(error || errorMessage) && !info" class="m-text-input__error-placeholder mc-field__error-message">
50
+ <div
51
+ v-if="!(error || errorMessage) && !info"
52
+ class="m-text-input__error-placeholder mc-field__error-message"
53
+ :class="{ 'm-text-input__blank': blankField }"
54
+ >
49
55
  &nbsp;
50
56
  </div>
51
- <div v-else-if="error || errorMessage" class="m-text-input__error mc-field__error-message">
57
+ <div
58
+ v-else-if="error || errorMessage"
59
+ class="m-text-input__error mc-field__error-message"
60
+ :class="{ 'm-text-input__blank': blankField }"
61
+ >
52
62
  {{ error || errorMessage }}
53
63
  </div>
54
- <div v-else-if="info" class="m-text-input__info mc-field__error-message">
64
+ <div
65
+ v-else-if="info"
66
+ class="m-text-input__info mc-field__error-message"
67
+ :class="{ 'm-text-input__blank': blankField }"
68
+ >
55
69
  {{ info }}
56
70
  </div>
57
71
  </div>
@@ -225,6 +239,27 @@ export default defineComponent({
225
239
  type: Number,
226
240
  default: -1,
227
241
  },
242
+ /**
243
+ * Defines if this input has bordered
244
+ */
245
+ rounded: {
246
+ type: Boolean,
247
+ default: true,
248
+ },
249
+ /**
250
+ * Defines text alignment inside input
251
+ */
252
+ textCenter: {
253
+ type: Boolean,
254
+ default: false,
255
+ },
256
+ /**
257
+ * Definies if input has blank fields
258
+ */
259
+ blankField: {
260
+ type: Boolean,
261
+ default: false,
262
+ },
228
263
  },
229
264
 
230
265
  setup(props, { emit, expose }) {
@@ -354,4 +389,24 @@ export default defineComponent({
354
389
  @import 'components/c.text-input';
355
390
  @import 'components/c.left-icon-input';
356
391
  @import 'components/c.fields';
392
+
393
+ .unrounded {
394
+ border-left: 0;
395
+ border-radius: 0;
396
+ border-right: 0;
397
+ }
398
+
399
+ .text-center {
400
+ text-align: center;
401
+ }
402
+
403
+ .m-text-input__blank {
404
+ display: none !important;
405
+ }
406
+
407
+ .m-text-input__nomargin {
408
+ input {
409
+ margin-top: 0 !important;
410
+ }
411
+ }
357
412
  </style>
@@ -36,7 +36,8 @@ It is possible to condition the display of certain answers according to previous
36
36
 
37
37
  export const conditionalAnswersPayload = {
38
38
  viewModel: {
39
- label: 'The question title',
39
+ label: 'Quel est votre revenu fiscal de référence annuel de votre foyer fiscal ?',
40
+ subtitle: 'Le revenu fiscal de référence du foyer concerne la somme des revenus des conjoints.',
40
41
  answersComponent: 'PbCard',
41
42
  },
42
43
  answers: {
@@ -0,0 +1,122 @@
1
+ import { nestedAppDecorator } from '../../../.storybook/nested-app-decorator';
2
+ import { Story, Preview, Meta, Props, ArgsTable, Source, Canvas } from '@storybook/addon-docs';
3
+ import PbQuestion from './PbQuestion';
4
+ import { TemplateSandbox } from './PbQuestion.stories.mdx';
5
+
6
+ <Meta
7
+ title="Project Booster/Scenario/Questions/PbQuestion 🦠/Features/Answers/Conditional"
8
+ component={PbQuestion}
9
+ argTypes={{
10
+ onStepCompleted: {
11
+ action: 'Step completed',
12
+ table: { disable: true },
13
+ },
14
+ }}
15
+ decorators={[
16
+ nestedAppDecorator(
17
+ {
18
+ actions: {
19
+ sendEventToBus({}, payload) {
20
+ console.log('Event sent to bus', payload);
21
+ },
22
+ },
23
+ },
24
+ [],
25
+ ),
26
+ ]}
27
+ parameters={{
28
+ layout: 'fullscreen',
29
+ }}
30
+ />
31
+
32
+ # `PbQuestion` - With Modal
33
+
34
+ export const conditionalAnswersPayload = {
35
+ viewModel: {
36
+ label: 'Quel est votre revenu fiscal de référence annuel de votre foyer fiscal ?',
37
+ subtitle: 'Le revenu fiscal de référence du foyer concerne la somme des revenus des conjoints.',
38
+ answersComponent: 'PbCard',
39
+ },
40
+ answers: {
41
+ 'ANSWER-1': {
42
+ code: 'ANSWER-1',
43
+ conditions: [
44
+ "isAnswerMatching('QUESTION_1', 'QUESTION_1--ANSWER_1')",
45
+ "isAnswerMatching('QUESTION_2', 'QUESTION_2--ANSWER_1')",
46
+ ],
47
+ viewModel: {
48
+ title: '15000 - 20000 €',
49
+ borderColor: 'orange',
50
+ },
51
+ },
52
+ 'ANSWER-2': {
53
+ code: 'ANSWER-2',
54
+ conditions: ['runtimeOptions.isElligible'],
55
+ viewModel: {
56
+ title: '20000 - 30000 €',
57
+ borderColor: 'red',
58
+ },
59
+ },
60
+ 'ANSWER-3': {
61
+ code: 'ANSWER-3',
62
+ viewModel: {
63
+ title: '30000 - 40000 €',
64
+ borderColor: 'purple',
65
+ },
66
+ },
67
+ 'ANSWER-4': {
68
+ code: 'ANSWER-4',
69
+ viewModel: {
70
+ title: 'Plus de 50000 €',
71
+ borderColor: 'yellow',
72
+ },
73
+ },
74
+ },
75
+ helpArea: [
76
+ {
77
+ type: 'text',
78
+ label: 'Besoin d’aide pour trouver votre revenu fiscal de référence ?',
79
+ },
80
+ {
81
+ type: 'link',
82
+ label: 'Voir l’aide pour trouver votre revenu fiscal',
83
+ action: {
84
+ type: 'MODAL',
85
+ viewModelDialog: {
86
+ headerTitle: 'Ou trouver votre revenu fiscal de référence ?',
87
+ subTitle: 'Sur votre dernier avis d’imposition disponible dans votre espace impots-gouv.fr',
88
+ imgUrl:
89
+ 'https://www.lerevenu.com/sites/site/files/styles/img_lg/public/field/image/impots_7.jpg?itok=A2rlQzx3',
90
+ },
91
+ },
92
+ },
93
+ ],
94
+ };
95
+
96
+ <Source language="json" code={JSON.stringify(conditionalAnswersPayload, null, ' ')} />
97
+
98
+ export const conditionalAnswersAnswers = {
99
+ QUESTION_1: [{ code: 'QUESTION_1--ANSWER_1' }],
100
+ QUESTION_2: [{ code: 'QUESTION_2--ANSWER_1' }],
101
+ };
102
+
103
+ export const conditionalAnswersRuntimeOptions = {
104
+ isElligible: true,
105
+ };
106
+
107
+ <Canvas>
108
+ <Story
109
+ name="With Modal"
110
+ inline={false}
111
+ height="862px"
112
+ parameters={{ controls: { disable: true } }}
113
+ args={{
114
+ payload: conditionalAnswersPayload,
115
+ answers: new Map(Object.entries(conditionalAnswersAnswers)),
116
+ runtimeOptions: conditionalAnswersRuntimeOptions,
117
+ minHeight: 'auto',
118
+ }}
119
+ >
120
+ {TemplateSandbox.bind({})}
121
+ </Story>
122
+ </Canvas>