@defra/forms-model 3.0.502 → 3.0.504
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/dist/module/__stubs__/pages.js +14 -1
- package/dist/module/__stubs__/pages.js.map +1 -1
- package/dist/module/form/form-editor/preview/controller/guidance-page-controller.js +58 -0
- package/dist/module/form/form-editor/preview/controller/guidance-page-controller.js.map +1 -0
- package/dist/module/form/form-editor/preview/controller/page-controller-base.js +363 -0
- package/dist/module/form/form-editor/preview/controller/page-controller-base.js.map +1 -0
- package/dist/module/form/form-editor/preview/controller/page-controller.js +26 -265
- package/dist/module/form/form-editor/preview/controller/page-controller.js.map +1 -1
- package/dist/module/form/form-editor/preview/index.js +2 -0
- package/dist/module/form/form-editor/preview/index.js.map +1 -1
- package/dist/module/form/form-editor/preview/types.js.map +1 -1
- package/dist/module/pages/helpers.js +2 -1
- package/dist/module/pages/helpers.js.map +1 -1
- package/dist/module/utils/markdown.js +23 -3
- package/dist/module/utils/markdown.js.map +1 -1
- package/dist/types/__stubs__/pages.d.ts +14 -0
- package/dist/types/__stubs__/pages.d.ts.map +1 -1
- package/dist/types/form/form-editor/preview/controller/guidance-page-controller.d.ts +10 -0
- package/dist/types/form/form-editor/preview/controller/guidance-page-controller.d.ts.map +1 -0
- package/dist/types/form/form-editor/preview/controller/page-controller-base.d.ts +198 -0
- package/dist/types/form/form-editor/preview/controller/page-controller-base.d.ts.map +1 -0
- package/dist/types/form/form-editor/preview/controller/page-controller.d.ts +3 -150
- package/dist/types/form/form-editor/preview/controller/page-controller.d.ts.map +1 -1
- package/dist/types/form/form-editor/preview/index.d.ts +2 -0
- package/dist/types/form/form-editor/preview/types.d.ts +3 -1
- package/dist/types/form/form-editor/preview/types.d.ts.map +1 -1
- package/dist/types/pages/helpers.d.ts.map +1 -1
- package/dist/types/utils/markdown.d.ts +1 -1
- package/dist/types/utils/markdown.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__stubs__/pages.ts +20 -1
- package/src/form/form-editor/preview/controller/guidance-page-controller.js +64 -0
- package/src/form/form-editor/preview/controller/page-controller-base.js +388 -0
- package/src/form/form-editor/preview/controller/page-controller.js +28 -288
- package/src/form/form-editor/preview/index.js +2 -0
- package/src/form/form-editor/preview/types.ts +4 -1
- package/src/pages/helpers.ts +2 -1
- package/src/utils/markdown.ts +29 -3
@@ -1,10 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
import { HIGHLIGHT_CLASS } from '~/src/form/form-editor/preview/constants.js'
|
3
|
-
import { ContentElements } from '~/src/form/form-editor/preview/content.js'
|
1
|
+
import { PreviewPageControllerBase } from '~/src/form/form-editor/preview/controller/page-controller-base.js'
|
4
2
|
import { mapComponentToPreviewQuestion } from '~/src/form/form-editor/preview/helpers.js'
|
5
3
|
import { Markdown } from '~/src/form/form-editor/preview/markdown.js'
|
6
|
-
import { hasRepeater } from '~/src/index.js'
|
7
|
-
import { hasComponents } from '~/src/pages/helpers.js'
|
8
4
|
|
9
5
|
/**
|
10
6
|
* @type {QuestionRenderer}
|
@@ -19,125 +15,13 @@ const questionRenderer = {
|
|
19
15
|
}
|
20
16
|
}
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
*/
|
25
|
-
export class PagePreviewElements {
|
26
|
-
/**
|
27
|
-
* @type {Page}
|
28
|
-
* @private
|
29
|
-
*/
|
30
|
-
_page
|
31
|
-
|
32
|
-
/**
|
33
|
-
* @param {Page} page
|
34
|
-
*/
|
35
|
-
constructor(page) {
|
36
|
-
this._page = page
|
37
|
-
}
|
38
|
-
|
39
|
-
get heading() {
|
40
|
-
return this._page.title
|
41
|
-
}
|
42
|
-
|
43
|
-
get guidance() {
|
44
|
-
if (!hasComponents(this._page) || !this._page.components.length) {
|
45
|
-
return ''
|
46
|
-
}
|
47
|
-
|
48
|
-
const [possibleGuidanceComponent] = this._page.components
|
49
|
-
|
50
|
-
return possibleGuidanceComponent.type === ComponentType.Markdown
|
51
|
-
? possibleGuidanceComponent.content
|
52
|
-
: ''
|
53
|
-
}
|
54
|
-
|
55
|
-
get addHeading() {
|
56
|
-
return this._page.title.length > 0
|
57
|
-
}
|
58
|
-
|
59
|
-
get repeatQuestion() {
|
60
|
-
if (hasRepeater(this._page)) {
|
61
|
-
return this._page.repeat.options.title
|
62
|
-
}
|
63
|
-
return undefined
|
64
|
-
}
|
65
|
-
|
66
|
-
get hasRepeater() {
|
67
|
-
return hasRepeater(this._page)
|
68
|
-
}
|
69
|
-
}
|
70
|
-
|
71
|
-
/**
|
72
|
-
* Enum for Highlight classes
|
73
|
-
* @readonly
|
74
|
-
* @enum {string}
|
75
|
-
*/
|
76
|
-
const HighlightClass = {
|
77
|
-
TITLE: 'title',
|
78
|
-
GUIDANCE: 'guidance',
|
79
|
-
REPEATER: 'repeater'
|
80
|
-
}
|
81
|
-
|
82
|
-
/**
|
83
|
-
* @implements {PagePreviewPanelMacro}
|
84
|
-
*/
|
85
|
-
export class PreviewPageController {
|
86
|
-
static PATH = 'preview-controllers/'
|
87
|
-
/**
|
88
|
-
* @type {string}
|
89
|
-
* @protected
|
90
|
-
*/
|
91
|
-
_pageTemplate = PreviewPageController.PATH + 'page-controller.njk'
|
18
|
+
export class PreviewPageController extends PreviewPageControllerBase {
|
19
|
+
static PATH = PreviewPageControllerBase.PATH
|
92
20
|
/**
|
93
21
|
* @protected
|
94
22
|
* @type {Question[]}
|
95
23
|
*/
|
96
24
|
_components = []
|
97
|
-
/**
|
98
|
-
* @type {string}
|
99
|
-
*/
|
100
|
-
#title = ''
|
101
|
-
/**
|
102
|
-
*
|
103
|
-
* @type {PageRenderer}
|
104
|
-
*/
|
105
|
-
#pageRenderer
|
106
|
-
/**
|
107
|
-
* @type { undefined | HighlightClass }
|
108
|
-
* @protected
|
109
|
-
*/
|
110
|
-
_highlighted = undefined
|
111
|
-
/**
|
112
|
-
* @type {string}
|
113
|
-
* @private
|
114
|
-
*/
|
115
|
-
_guidanceText = ''
|
116
|
-
/**
|
117
|
-
* @type { string }
|
118
|
-
* @protected
|
119
|
-
*/
|
120
|
-
_sectionTitle = ''
|
121
|
-
/**
|
122
|
-
* @type {Markdown}
|
123
|
-
* @private
|
124
|
-
*/
|
125
|
-
_emptyGuidance = PreviewPageController.createGuidanceComponent()
|
126
|
-
/**
|
127
|
-
*
|
128
|
-
* @type {Markdown}
|
129
|
-
* @protected
|
130
|
-
*/
|
131
|
-
_guidanceComponent
|
132
|
-
/**
|
133
|
-
* @type {boolean}
|
134
|
-
* @private
|
135
|
-
*/
|
136
|
-
_showTitle = true
|
137
|
-
/**
|
138
|
-
* @type {boolean}
|
139
|
-
*/
|
140
|
-
#isRepeater = false
|
141
25
|
|
142
26
|
/**
|
143
27
|
* @param {ComponentDef[]} components
|
@@ -146,29 +30,26 @@ export class PreviewPageController {
|
|
146
30
|
* @param {PageRenderer} renderer
|
147
31
|
*/
|
148
32
|
constructor(components, elements, definition, renderer) {
|
33
|
+
super(elements, renderer)
|
149
34
|
const questions = components.map(
|
150
35
|
mapComponentToPreviewQuestion(questionRenderer, definition)
|
151
36
|
)
|
152
37
|
const firstQuestion = /** @type { Markdown | undefined | Question } */ (
|
153
38
|
questions.shift()
|
154
39
|
)
|
155
|
-
|
156
40
|
this._guidanceComponent =
|
157
41
|
PreviewPageController.getOrCreateGuidanceComponent(firstQuestion)
|
158
42
|
this._guidanceText = elements.guidance
|
159
43
|
this._components = this.#constructComponents(firstQuestion, questions)
|
160
44
|
this._showTitle = elements.addHeading
|
161
|
-
|
162
|
-
this.#pageRenderer = renderer
|
163
|
-
this.#title = elements.heading
|
164
45
|
this._sectionTitle = elements.repeatQuestion ?? ''
|
165
|
-
this
|
46
|
+
this._isRepeater = elements.hasRepeater
|
166
47
|
}
|
167
48
|
|
168
49
|
/**
|
169
|
-
* @type {typeof HighlightClass}
|
50
|
+
* @type {typeof PreviewPageControllerBase.HighlightClass}
|
170
51
|
*/
|
171
|
-
static HighlightClass = HighlightClass
|
52
|
+
static HighlightClass = PreviewPageControllerBase.HighlightClass
|
172
53
|
|
173
54
|
/**
|
174
55
|
* @param { Question | Markdown | undefined} firstQuestion
|
@@ -181,20 +62,6 @@ export class PreviewPageController {
|
|
181
62
|
: [firstQuestion, ...questions]
|
182
63
|
}
|
183
64
|
|
184
|
-
/**
|
185
|
-
* @returns {Markdown[]}
|
186
|
-
* @private
|
187
|
-
*/
|
188
|
-
get _guidanceComponents() {
|
189
|
-
if (this._guidanceText.length) {
|
190
|
-
return [this._guidanceComponent]
|
191
|
-
}
|
192
|
-
if (this._highlighted === 'guidance') {
|
193
|
-
return [this._emptyGuidance]
|
194
|
-
}
|
195
|
-
return []
|
196
|
-
}
|
197
|
-
|
198
65
|
/**
|
199
66
|
* @returns {PagePreviewComponent[]}
|
200
67
|
*/
|
@@ -223,7 +90,7 @@ export class PreviewPageController {
|
|
223
90
|
return false
|
224
91
|
}
|
225
92
|
// |_ one component and title not highlighted
|
226
|
-
if (this
|
93
|
+
if (this._title.trim() === this._components[0]?.question.trim()) {
|
227
94
|
return true
|
228
95
|
}
|
229
96
|
// titles not the same
|
@@ -267,60 +134,13 @@ export class PreviewPageController {
|
|
267
134
|
}
|
268
135
|
}
|
269
136
|
|
270
|
-
set guidanceText(text) {
|
271
|
-
this._guidanceText = text
|
272
|
-
this._guidanceComponent.content = text
|
273
|
-
this.render()
|
274
|
-
}
|
275
|
-
|
276
|
-
get guidanceText() {
|
277
|
-
if (!this._showTitle) {
|
278
|
-
return ''
|
279
|
-
}
|
280
|
-
return this._guidanceText
|
281
|
-
}
|
282
|
-
|
283
|
-
/**
|
284
|
-
*
|
285
|
-
* @param {boolean} showTitle
|
286
|
-
*/
|
287
|
-
set showTitle(showTitle) {
|
288
|
-
this._showTitle = showTitle
|
289
|
-
this.render()
|
290
|
-
}
|
291
|
-
|
292
|
-
get showTitle() {
|
293
|
-
return this._showTitle
|
294
|
-
}
|
295
|
-
|
296
|
-
get guidance() {
|
297
|
-
return {
|
298
|
-
text: this.guidanceText,
|
299
|
-
classes: this.#isHighlighted(HighlightClass.GUIDANCE)
|
300
|
-
}
|
301
|
-
}
|
302
|
-
|
303
|
-
/**
|
304
|
-
* @returns {{ text: string, classes: string }}
|
305
|
-
*/
|
306
|
-
get pageTitle() {
|
307
|
-
return {
|
308
|
-
text: this.title,
|
309
|
-
classes: this.#isHighlighted(HighlightClass.TITLE)
|
310
|
-
}
|
311
|
-
}
|
312
|
-
|
313
|
-
render() {
|
314
|
-
this.#pageRenderer.render(this._pageTemplate, this)
|
315
|
-
}
|
316
|
-
|
317
137
|
/**
|
318
138
|
* @returns {boolean}
|
319
139
|
*/
|
320
140
|
get titleAndFirstTitleSame() {
|
321
141
|
return (
|
322
142
|
this._components.length > 0 &&
|
323
|
-
this
|
143
|
+
this._title.trim() === this._components[0]?.question.trim() &&
|
324
144
|
this.components.length === 1 &&
|
325
145
|
this._highlighted !== 'title'
|
326
146
|
)
|
@@ -328,58 +148,28 @@ export class PreviewPageController {
|
|
328
148
|
|
329
149
|
/**
|
330
150
|
* @returns {string}
|
151
|
+
* @protected
|
331
152
|
*/
|
332
|
-
|
153
|
+
_getTitle() {
|
333
154
|
if (!this._showTitle || this.titleAndFirstTitleSame) {
|
334
155
|
return ''
|
335
156
|
}
|
336
|
-
|
337
|
-
return this.#title
|
338
|
-
}
|
339
|
-
return 'Page heading'
|
157
|
+
return super._getTitle()
|
340
158
|
}
|
341
159
|
|
342
160
|
/**
|
343
|
-
* @
|
344
|
-
|
345
|
-
set title(value) {
|
346
|
-
this.#title = value
|
347
|
-
this.render()
|
348
|
-
}
|
349
|
-
|
350
|
-
highlightTitle() {
|
351
|
-
this.setHighLighted(HighlightClass.TITLE)
|
352
|
-
}
|
353
|
-
|
354
|
-
setRepeater() {
|
355
|
-
this.#isRepeater = true
|
356
|
-
this.render()
|
357
|
-
}
|
358
|
-
|
359
|
-
unsetRepeater() {
|
360
|
-
this.#isRepeater = false
|
361
|
-
this.render()
|
362
|
-
}
|
363
|
-
|
364
|
-
get isRepeater() {
|
365
|
-
return this.#isRepeater
|
366
|
-
}
|
367
|
-
|
368
|
-
/**
|
369
|
-
* @returns {{classes: string, text: string} | undefined}
|
161
|
+
* @returns {string}
|
162
|
+
* @protected
|
370
163
|
*/
|
371
|
-
|
372
|
-
if (this.
|
373
|
-
return
|
374
|
-
}
|
375
|
-
return {
|
376
|
-
classes: this.#isHighlighted(HighlightClass.REPEATER),
|
377
|
-
text: this.sectionTitleText
|
164
|
+
_getGuidanceText() {
|
165
|
+
if (!this._showTitle) {
|
166
|
+
return ''
|
378
167
|
}
|
168
|
+
return super._getGuidanceText()
|
379
169
|
}
|
380
170
|
|
381
171
|
get repeaterText() {
|
382
|
-
if (!this
|
172
|
+
if (!this._isRepeater) {
|
383
173
|
return undefined
|
384
174
|
}
|
385
175
|
if (!this._sectionTitle.length) {
|
@@ -389,15 +179,11 @@ export class PreviewPageController {
|
|
389
179
|
}
|
390
180
|
|
391
181
|
/**
|
392
|
-
* @
|
182
|
+
* @returns {string|undefined}
|
183
|
+
* @protected
|
393
184
|
*/
|
394
|
-
|
395
|
-
this.
|
396
|
-
this.render()
|
397
|
-
}
|
398
|
-
|
399
|
-
get sectionTitleText() {
|
400
|
-
if (this.#isRepeater) {
|
185
|
+
_getSectionTitleText() {
|
186
|
+
if (this._isRepeater) {
|
401
187
|
return this.repeaterText
|
402
188
|
}
|
403
189
|
return undefined
|
@@ -408,13 +194,15 @@ export class PreviewPageController {
|
|
408
194
|
return undefined
|
409
195
|
}
|
410
196
|
return {
|
411
|
-
classes: this
|
197
|
+
classes: this._isHighlighted(
|
198
|
+
PreviewPageControllerBase.HighlightClass.REPEATER
|
199
|
+
),
|
412
200
|
text: this.repeaterButtonText
|
413
201
|
}
|
414
202
|
}
|
415
203
|
|
416
204
|
get repeaterButtonText() {
|
417
|
-
if (!this
|
205
|
+
if (!this._isRepeater) {
|
418
206
|
return undefined
|
419
207
|
}
|
420
208
|
|
@@ -428,26 +216,6 @@ export class PreviewPageController {
|
|
428
216
|
return firstToken.toLowerCase() + restOfStr
|
429
217
|
}
|
430
218
|
|
431
|
-
/**
|
432
|
-
* Creates a dummy component for when guidance is highlighted
|
433
|
-
* but no guidance text exists
|
434
|
-
* @returns {Markdown}
|
435
|
-
*/
|
436
|
-
static createGuidanceComponent() {
|
437
|
-
const guidanceElement = new ContentElements({
|
438
|
-
type: ComponentType.Markdown,
|
439
|
-
title: 'Guidance component',
|
440
|
-
name: 'guidanceComponent',
|
441
|
-
content: 'Guidance text',
|
442
|
-
options: {}
|
443
|
-
})
|
444
|
-
const guidanceComponent = new Markdown(guidanceElement, questionRenderer)
|
445
|
-
|
446
|
-
// the dummy component should always be highlighted
|
447
|
-
guidanceComponent.highlightContent()
|
448
|
-
return guidanceComponent
|
449
|
-
}
|
450
|
-
|
451
219
|
/**
|
452
220
|
* Helper method to return the guidance or a new one
|
453
221
|
* @param { Markdown | Question | undefined } guidanceComponent
|
@@ -458,35 +226,7 @@ export class PreviewPageController {
|
|
458
226
|
if (guidanceComponent instanceof Markdown) {
|
459
227
|
return guidanceComponent
|
460
228
|
}
|
461
|
-
return
|
462
|
-
}
|
463
|
-
|
464
|
-
highlightGuidance() {
|
465
|
-
this._guidanceComponent.highlightContent()
|
466
|
-
this.setHighLighted(HighlightClass.GUIDANCE)
|
467
|
-
}
|
468
|
-
|
469
|
-
/**
|
470
|
-
* @param {HighlightClass} highlightSection
|
471
|
-
*/
|
472
|
-
setHighLighted(highlightSection) {
|
473
|
-
this._highlighted = highlightSection
|
474
|
-
this.render()
|
475
|
-
}
|
476
|
-
|
477
|
-
clearHighlight() {
|
478
|
-
this._highlighted = undefined
|
479
|
-
|
480
|
-
this._guidanceComponent.unHighlightContent()
|
481
|
-
this.render()
|
482
|
-
}
|
483
|
-
|
484
|
-
/**
|
485
|
-
* @param {string} field
|
486
|
-
* @returns {string}
|
487
|
-
*/
|
488
|
-
#isHighlighted(field) {
|
489
|
-
return this._highlighted === field ? HIGHLIGHT_CLASS : ''
|
229
|
+
return PreviewPageControllerBase.createGuidanceComponent()
|
490
230
|
}
|
491
231
|
}
|
492
232
|
|
@@ -21,3 +21,5 @@ export * from '~/src/form/form-editor/preview/long-answer.js'
|
|
21
21
|
export * from '~/src/form/form-editor/preview/uk-address.js'
|
22
22
|
export * from '~/src/form/form-editor/preview/yes-no.js'
|
23
23
|
export * from '~/src/form/form-editor/preview/controller/page-controller.js'
|
24
|
+
export * from '~/src/form/form-editor/preview/controller/guidance-page-controller.js'
|
25
|
+
export * from '~/src/form/form-editor/preview/controller/page-controller-base.js'
|
@@ -88,9 +88,12 @@ export interface ListElements extends QuestionElements {
|
|
88
88
|
afterInputsHTML: string
|
89
89
|
}
|
90
90
|
|
91
|
-
export interface
|
91
|
+
export interface PagePreviewBaseElements {
|
92
92
|
heading: string
|
93
93
|
guidance: string
|
94
|
+
}
|
95
|
+
|
96
|
+
export interface PageOverviewElements extends PagePreviewBaseElements {
|
94
97
|
addHeading: boolean
|
95
98
|
repeatQuestion: string | undefined
|
96
99
|
hasRepeater: boolean
|
package/src/pages/helpers.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import { ComponentType } from '~/src/components/enums.js'
|
2
|
+
import { hasFormField } from '~/src/components/helpers.js'
|
1
3
|
import { type ComponentDef } from '~/src/components/types.js'
|
2
4
|
import {
|
3
5
|
type Link,
|
@@ -6,7 +8,6 @@ import {
|
|
6
8
|
type PageQuestion,
|
7
9
|
type PageRepeat
|
8
10
|
} from '~/src/form/form-definition/types.js'
|
9
|
-
import { ComponentType, hasFormField } from '~/src/index.js'
|
10
11
|
import {
|
11
12
|
ControllerNames,
|
12
13
|
ControllerTypes
|
package/src/utils/markdown.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Marked } from 'marked'
|
1
|
+
import { Marked, Renderer, type Tokens } from 'marked'
|
2
2
|
|
3
3
|
/**
|
4
4
|
* Marked instance (avoids global option/extension scope)
|
@@ -8,10 +8,31 @@ export const marked = new Marked({
|
|
8
8
|
gfm: true
|
9
9
|
})
|
10
10
|
|
11
|
+
function renderLink(href: string, text: string, baseUrl?: string) {
|
12
|
+
let isLocalLink = true
|
13
|
+
|
14
|
+
if (baseUrl) {
|
15
|
+
isLocalLink = href.startsWith(baseUrl) || href.startsWith('mailto:')
|
16
|
+
}
|
17
|
+
|
18
|
+
const attrs = [`class="govuk-link"`, `href="${href}"`]
|
19
|
+
|
20
|
+
if (!isLocalLink) {
|
21
|
+
attrs.push(`target="_blank" rel="noreferrer noopener"`)
|
22
|
+
}
|
23
|
+
|
24
|
+
const label = !isLocalLink ? `${text} (opens in new tab)` : text
|
25
|
+
|
26
|
+
return `<a ${attrs.join(' ')}>${label}</a>`
|
27
|
+
}
|
28
|
+
|
11
29
|
/**
|
12
30
|
* Convert markdown to HTML, escaping any HTML tags first
|
13
31
|
*/
|
14
|
-
export function markdownToHtml(
|
32
|
+
export function markdownToHtml(
|
33
|
+
markdown: string | null | undefined,
|
34
|
+
baseUrl?: string // optional in some contexts, e.g. from the designer where it might not make sense
|
35
|
+
) {
|
15
36
|
if (markdown === undefined || markdown === null) {
|
16
37
|
return ''
|
17
38
|
}
|
@@ -23,5 +44,10 @@ export function markdownToHtml(markdown?: string | null) {
|
|
23
44
|
.replace(/"/g, '"')
|
24
45
|
.replace(/'/g, ''')
|
25
46
|
|
26
|
-
|
47
|
+
const renderer = new Renderer()
|
48
|
+
renderer.link = ({ href, text }: Tokens.Link): string => {
|
49
|
+
return renderLink(href, text, baseUrl)
|
50
|
+
}
|
51
|
+
|
52
|
+
return marked.parse(escaped, { async: false, renderer })
|
27
53
|
}
|