mod-build 4.0.100 → 4.0.101-beta.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.0.101
4
+
5
+ - Refactored `get-default-trade-questions.js` to reduce duplicated logic in `lead-form` and enable reuse across instances.
6
+
3
7
  ## 4.0.100
4
8
 
5
9
  - Added field-level `move` support in `get-default-trade-questions` (e.g. `move: [{ from: 3, to: 0 }]`) to reorder default options while merging
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mod-build",
3
- "version": "4.0.100",
3
+ "version": "4.0.101-beta.2",
4
4
  "description": "Share components for S3 sites.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -2,7 +2,7 @@
2
2
  import merge from 'lodash.merge';
3
3
  import { defaultSettings } from '../src/data/config.js';
4
4
 
5
- function resolveRefs(value, root, refChain = new Set()) {
5
+ export function resolveRefs(value, root = value, refChain = new Set()) {
6
6
  if (value === null || typeof value !== 'object') {
7
7
  return value;
8
8
  }
@@ -33,186 +33,150 @@ function resolveRefs(value, root, refChain = new Set()) {
33
33
  return resolved;
34
34
  }
35
35
 
36
- function mergeDefaultTradeQuestionsConfigOnSteps(config, steps) {
37
- if (!config || typeof config !== 'object') return;
38
-
39
- if (config.steps && typeof config.steps === 'object' && config.steps.items) {
40
- // cloning steps to keep multiple steps: {} objects independent
41
- const clonedSteps = JSON.parse(JSON.stringify(steps));
42
- mergeDefaultTradeQuestionsConfig(config, clonedSteps);
43
- }
44
-
45
- for (const key in config) {
46
- if (config.hasOwnProperty(key)) {
47
- const value = config[key];
48
- if (typeof value === 'object' && value !== null) {
49
- mergeDefaultTradeQuestionsConfigOnSteps(value, steps);
50
- }
36
+ function mergeQuestionFields(fields, defaultFields, {
37
+ legendAttributes,
38
+ optionFieldTypes,
39
+ errorMessages
40
+ }) {
41
+ let defaultOptions = [];
42
+ let defaultLegend = {};
43
+ defaultFields.forEach((defaultField) => {
44
+ defaultOptions = defaultField.options;
45
+ defaultLegend = defaultField.legend;
46
+
47
+ if (defaultLegend && legendAttributes) {
48
+ defaultLegend.attributes = {
49
+ ...defaultLegend.attributes,
50
+ ...legendAttributes
51
+ };
51
52
  }
52
- }
53
- }
53
+ });
54
54
 
55
- function mergeDefaultTradeQuestionsConfig(config, steps) {
56
- if (!config || !config.steps || !config.steps.items || !Array.isArray(config.steps.items)) {
57
- return;
58
- }
55
+ fields.forEach((field) => {
56
+ if (field.legend) {
57
+ field.legend = merge(defaultLegend, field.legend);
58
+ } else {
59
+ field.legend = defaultLegend;
60
+ }
59
61
 
60
- config.steps.items.forEach((item) => {
61
- const stepName = item.attributes.data['step-name'];
62
- if (steps[stepName]) {
63
- if (!item.stepContent) {
64
- item.stepContent = {};
62
+ if (field.remove) {
63
+ if (field.remove.includes('all')) {
64
+ defaultOptions = [];
65
+ } else {
66
+ defaultOptions = defaultOptions.filter((_, index) => !field.remove.includes(index));
65
67
  }
68
+ }
66
69
 
67
- if (!item.stepContent.fields) {
68
- item.stepContent.fields = [];
69
- }
70
- const defaultStepFields = steps[stepName].fields;
71
- let defaultStepOptions = [];
72
- let defaultQuestionLegend = {};
73
- defaultStepFields.forEach((defaultField) => {
74
- defaultStepOptions = defaultField.options;
75
- defaultQuestionLegend = defaultField.legend;
70
+ if (field.move) {
71
+ // field.move will look like [{from: 0, to: 1}]
72
+ // remove the option at from and insert it at to, in listed order
73
+ field.move.forEach((move) => {
74
+ const fromIndex = move.from;
75
+ const toIndex = move.to;
76
+ const fromOption = defaultOptions.splice(fromIndex, 1);
77
+ defaultOptions.splice(toIndex, 0, fromOption[0]);
76
78
  });
79
+ }
77
80
 
78
- item.stepContent.fields.forEach((field) => {
79
- if (field.legend) {
80
- field.legend = merge(defaultQuestionLegend, field.legend);
81
- } else {
82
- field.legend = defaultQuestionLegend;
83
- }
84
-
85
- if (field && field.remove) {
86
- if (field.remove.includes('all')) {
87
- defaultStepOptions = [];
88
- } else {
89
- defaultStepOptions = defaultStepOptions.filter((_, i) => !field.remove.includes(i));
90
- }
91
- }
92
-
93
- if (field && field.move) {
94
- // field.move will look like [{from: 0, to: 1}]
95
- // we need to remove option at from index and insert at to index
96
- field.move.forEach((move) => {
97
- const fromIndex = move.from;
98
- const toIndex = move.to;
99
- const fromOption = defaultStepOptions.splice(fromIndex, 1);
100
- defaultStepOptions.splice(toIndex, 0, fromOption[0]);
101
- });
102
- }
81
+ if (!optionFieldTypes.includes(field.fieldType)) {
82
+ return;
83
+ }
103
84
 
104
- if (field.fieldType === 'radio' || field.fieldType === 'checkbox-group') {
105
- if (!field.errorMessage) {
106
- field.errorMessage = field.fieldType === 'checkbox-group' ? 'Please select at least one option.' : 'Professionals need this info to generate a quote.';
107
- }
85
+ if (!field.errorMessage && errorMessages[field.fieldType]) {
86
+ field.errorMessage = errorMessages[field.fieldType];
87
+ }
108
88
 
109
- if (field.options && Array.isArray(field.options) && field.options.length) {
110
- field.options.forEach((option) => {
111
- if (typeof option.update === 'number') {
112
- if (option.attributes && option.attributes.checked === false) {
113
- delete option.attributes.checked;
114
- delete defaultStepOptions[option.update].attributes.checked;
115
- }
116
- merge(defaultStepOptions[option.update], option);
117
- } else {
118
- if (typeof option.insertAt === 'number') {
119
- defaultStepOptions.splice(option.insertAt, 0, option);
120
- } else {
121
- defaultStepOptions.push(option);
122
- }
123
- }
124
- });
89
+ if (Array.isArray(field.options)) {
90
+ field.options.forEach((option) => {
91
+ if (typeof option.update === 'number') {
92
+ if (option.attributes?.checked === false) {
93
+ delete option.attributes.checked;
94
+ delete defaultOptions[option.update]?.attributes?.checked;
125
95
  }
126
- field.options = defaultStepOptions;
96
+ merge(defaultOptions[option.update], option);
97
+ } else if (typeof option.insertAt === 'number') {
98
+ defaultOptions.splice(option.insertAt, 0, option);
127
99
  } else {
128
- if (field && Array.isArray(field) && field.length) {
129
- if (typeof field.update === 'number') {
130
- merge(defaultStepFields[field.update], field);
131
- } else {
132
- if (typeof field.insertAt === 'number') {
133
- defaultStepFields.splice(field.insertAt, 0, field);
134
- } else {
135
- defaultStepFields.push(field);
136
- }
137
- }
138
- }
139
-
140
- field = defaultStepFields;
100
+ defaultOptions.push(option);
141
101
  }
142
102
  });
103
+ }
104
+ field.options = defaultOptions;
105
+ });
106
+ }
143
107
 
144
- // extra question block
145
- if (item.stepContent.extraQuestionBlock) {
146
- if (!item.stepContent.extraQuestionBlock.fields) {
147
- item.stepContent.extraQuestionBlock.fields = [];
148
- }
108
+ export function mergeDefaultTradeQuestionsIntoItems(items, steps, options = {}) {
109
+ if (!Array.isArray(items) || !steps || typeof steps !== 'object') {
110
+ return;
111
+ }
149
112
 
150
- const defaultExtraQuestionFields = steps[stepName].extraQuestionBlock.fields;
151
- let defaultExtraQuestionOptions = [];
152
- let defaultExtraQuestionLegend = {};
153
- defaultExtraQuestionFields.forEach((defaultExtraQuestionField) => {
154
- defaultExtraQuestionOptions = defaultExtraQuestionField.options;
155
- defaultExtraQuestionLegend = defaultExtraQuestionField.legend;
156
- });
157
- item.stepContent.extraQuestionBlock.fields.forEach((field) => {
113
+ const mergeOptions = {
114
+ legendAttributes: options.legendAttributes,
115
+ optionFieldTypes: options.optionFieldTypes || ['radio', 'checkbox-group'],
116
+ errorMessages: {
117
+ radio: 'Professionals need this info to generate a quote.',
118
+ 'checkbox-group': 'Please select at least one option.',
119
+ ...options.errorMessages
120
+ }
121
+ };
158
122
 
159
- if (field.legend) {
160
- field.legend = merge(defaultExtraQuestionLegend, field.legend);
161
- } else {
162
- field.legend = defaultExtraQuestionLegend;
163
- }
123
+ items.forEach((item) => {
124
+ const stepName = item.attributes?.data?.['step-name'];
125
+ const defaultStep = steps[stepName];
126
+ if (!defaultStep) {
127
+ return;
128
+ }
164
129
 
165
- if (field && field.remove) {
166
- if (field.remove.includes('all')) {
167
- defaultExtraQuestionOptions = [];
168
- } else {
169
- defaultExtraQuestionOptions = defaultExtraQuestionOptions.filter((_, i) => !field.remove.includes(i));
170
- }
171
- }
130
+ if (!item.stepContent) {
131
+ item.stepContent = {};
132
+ }
133
+ if (!item.stepContent.fields) {
134
+ item.stepContent.fields = [];
135
+ }
172
136
 
173
- if (field.fieldType === 'radio') {
174
- if (!field.errorMessage) {
175
- field.errorMessage = 'Professionals need this info to generate a quote.';
176
- }
137
+ mergeQuestionFields(item.stepContent.fields, defaultStep.fields, mergeOptions);
177
138
 
178
- if (field.options && Array.isArray(field.options) && field.options.length) {
179
- field.options.forEach((option, optionIndex) => {
180
- if (typeof option.update === 'number') {
181
- if (option.attributes && option.attributes.checked === false) {
182
- delete option.attributes.checked;
183
- delete defaultStepOptions[option.update].attributes.checked;
184
- }
185
- merge(defaultExtraQuestionOptions[option.update], option);
186
- } else {
187
- if (typeof option.insertAt === 'number') {
188
- defaultExtraQuestionOptions.splice(option.insertAt, 0, option);
189
- } else {
190
- defaultExtraQuestionOptions.push(option);
191
- }
192
- }
193
- });
194
- }
195
- field.options = defaultExtraQuestionOptions;
196
- } else {
197
- if (field && Array.isArray(field) && field.length) {
198
- if (typeof field.update === 'number') {
199
- merge(defaultExtraQuestionFields[field.update], field);
200
- } else {
201
- if (typeof field.insertAt === 'number') {
202
- defaultExtraQuestionFields.splice(field.insertAt, 0, field);
203
- } else {
204
- defaultExtraQuestionFields.push(field);
205
- }
206
- }
207
- }
208
- field = defaultExtraQuestionFields;
209
- }
210
- });
139
+ const extraQuestionBlock = item.stepContent.extraQuestionBlock;
140
+ if (extraQuestionBlock) {
141
+ if (!extraQuestionBlock.fields) {
142
+ extraQuestionBlock.fields = [];
211
143
  }
144
+ mergeQuestionFields(
145
+ extraQuestionBlock.fields,
146
+ defaultStep.extraQuestionBlock.fields,
147
+ mergeOptions
148
+ );
212
149
  }
213
150
  });
214
151
  }
215
152
 
153
+ export function mergeDefaultTradeQuestionsConfigOnSteps(config, steps, options) {
154
+ if (!config || typeof config !== 'object') return;
155
+
156
+ if (config.steps && typeof config.steps === 'object' && config.steps.items) {
157
+ // cloning steps to keep multiple steps: {} objects independent
158
+ const clonedSteps = JSON.parse(JSON.stringify(steps));
159
+ mergeDefaultTradeQuestionsConfig(config, clonedSteps, options);
160
+ }
161
+
162
+ for (const key in config) {
163
+ if (config.hasOwnProperty(key)) {
164
+ const value = config[key];
165
+ if (typeof value === 'object' && value !== null) {
166
+ mergeDefaultTradeQuestionsConfigOnSteps(value, steps, options);
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ export function mergeDefaultTradeQuestionsConfig(config, steps, options) {
173
+ if (!config || !config.steps || !config.steps.items || !Array.isArray(config.steps.items)) {
174
+ return;
175
+ }
176
+
177
+ mergeDefaultTradeQuestionsIntoItems(config.steps.items, steps, options);
178
+ }
179
+
216
180
  export default async function(config) {
217
181
  if (config.useStepsConfig) {
218
182
  console.log('Starting get-default-trade-questions...');