@worksafevictoria/wcl7.5 1.17.1 → 1.18.0-beta.1

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 (64) hide show
  1. package/package.json +1 -1
  2. package/src/assets/icons/chevron-right-icon.svg +14 -0
  3. package/src/assets/icons/notepad.svg +93 -0
  4. package/src/assets/icons/question.svg +7 -0
  5. package/src/components/Common/CardGrid/index.vue +83 -74
  6. package/src/components/Common/CardGridItem/index.vue +31 -4
  7. package/src/components/Containers/Carousel/index.vue +24 -4
  8. package/src/components/Containers/SectionGroup/index.stories.js +28 -27
  9. package/src/components/Containers/SectionGroup/index.vue +5 -0
  10. package/src/components/Global/AppHeaderNew/index.vue +9 -1
  11. package/src/components/Global/AppHeaderNew/mobile.scss +4 -2
  12. package/src/components/Global/HeroHeader/index.vue +74 -57
  13. package/src/components/Paragraphs/Calculator/CardContainer/index.vue +68 -60
  14. package/src/components/Paragraphs/Calculator/index.vue +24 -22
  15. package/src/components/Paragraphs/RTWPlanner/CardContainer/index.vue +133 -0
  16. package/src/components/Paragraphs/RTWPlanner/Constants.js +433 -0
  17. package/src/components/Paragraphs/RTWPlanner/Footer/index.vue +32 -0
  18. package/src/components/Paragraphs/RTWPlanner/{NavBar → Header}/index.vue +20 -9
  19. package/src/components/Paragraphs/RTWPlanner/HomePage/index.vue +72 -0
  20. package/src/components/Paragraphs/RTWPlanner/Injuries/index.vue +226 -0
  21. package/src/components/Paragraphs/RTWPlanner/Planners/PlanTasks.vue +9 -8
  22. package/src/components/Paragraphs/RTWPlanner/Planners/PlannerNameModal.vue +97 -0
  23. package/src/components/Paragraphs/RTWPlanner/Planners/index.vue +367 -175
  24. package/src/components/Paragraphs/RTWPlanner/index.stories.js +82 -15
  25. package/src/components/Paragraphs/RTWPlanner/index.vue +109 -31
  26. package/src/components/Paragraphs/SelectableCards/Control/index.stories.js +31 -11
  27. package/src/components/Paragraphs/SelectableCards/cardbody.vue +9 -10
  28. package/src/components/Paragraphs/SelectableCards/cardtop.vue +23 -16
  29. package/src/components/Paragraphs/SelectableCards/index.stories.js +29 -8
  30. package/src/components/Paragraphs/SelectableCards/index.vue +124 -50
  31. package/src/components/Paragraphs/TextMedia/index.vue +4 -0
  32. package/src/components/SubComponents/Breadcrumb/index.vue +40 -7
  33. package/src/components/SubComponents/FormInstance/models/base-form-element.js +4 -3
  34. package/src/components/SubComponents/FormInstance/services/form-render-parser.js +51 -16
  35. package/src/components/SubComponents/FormInstance/services/logic-parser.js +122 -18
  36. package/src/components/SubComponents/FormInstance/services/registry-factory.js +52 -50
  37. package/src/components/SubComponents/FormInstance/stories/mocks/checkboxesother.json +1 -10
  38. package/src/components/SubComponents/FormInstance/stories/mocks/emailconfirm.json +1 -10
  39. package/src/components/SubComponents/FormInstance/stories/mocks/jahd.json +1 -5
  40. package/src/components/SubComponents/FormInstance/stories/mocks/quad.json +1 -5
  41. package/src/components/SubComponents/FormInstance/stories/mocks/radiosother.json +1 -9
  42. package/src/components/SubComponents/FormInstance/stories/mocks/sameas.json +1 -5
  43. package/src/components/SubComponents/FormInstance/stories/mocks/selectother.json +1 -10
  44. package/src/components/SubComponents/FormInstance/stories/mocks/styles.json +1 -5
  45. package/src/components/SubComponents/FormInstance/stories/mocks/table-select.json +1 -15
  46. package/src/components/SubComponents/FormInstance/stories/mocks/token.json +1 -5
  47. package/src/components/SubComponents/FormInstance/stories/mocks/twig.json +1 -13
  48. package/src/components/SubComponents/FormInstance/stories/mocks/wizard.json +1 -13
  49. package/src/components/SubComponents/FormInstance/tests/form-test-utils.js +3 -0
  50. package/src/components/SubComponents/FormInstance/tests/form.test.js +2 -1
  51. package/src/components/SubComponents/FormInstance/tests/radiosother.test.js +12 -11
  52. package/src/components/SubComponents/FormInstance/tests/rule-disabled.test.js +13 -45
  53. package/src/components/SubComponents/FormInstance/tests/rule-enabled-value.test.js +8 -24
  54. package/src/components/SubComponents/FormInstance/tests/rule-hidden.test.js +13 -45
  55. package/src/components/SubComponents/FormInstance/tests/rule-required-value.test.js +15 -55
  56. package/src/components/SubComponents/FormInstance/tests/rule-visible.test.js +0 -413
  57. package/src/components/SubComponents/FormInstance/tests/sameas.test.js +9 -25
  58. package/src/components/SubComponents/FormInstance/tests/twig.test.js +7 -5
  59. package/src/components/SubComponents/ResourceGroup/index.vue +1 -1
  60. package/src/includes/scss/vars/src/colors.module.scss +3 -0
  61. package/src/includes/scss/vars/src/colors.scss +3 -0
  62. package/src/index.js +6 -0
  63. package/src/mock/control-selectable-cards.js +68 -25
  64. package/src/components/Paragraphs/RTWPlanner/Home/index.vue +0 -83
@@ -7,55 +7,99 @@
7
7
  :sub-heading="subHeading"
8
8
  :columns-per-row="columns || 4"
9
9
  class="selectable-cards"
10
- :is-selectable="true"
10
+ :is-selectable="isSelectable"
11
11
  :card-id-prefix="'selectable-cc-card'"
12
12
  @selected="selectedCardChangeFocus"
13
+ @selected-button-role="selectedButtonRole"
13
14
  >
14
- <template v-slot:cardItem="{ card }">
15
- <!-----CONTROL SELECTED CARD START-------->
15
+ <template v-slot:cardItem="slotProps">
16
16
  <card-grid-item
17
17
  v-if="cardType === 'control-selectcards'"
18
18
  class="control_selectcard"
19
- :button-role="'radio'"
20
- :glyph-src="card.icon"
19
+ :button-role="buttonRole"
20
+ :glyph-src="normalize(slotProps).icon"
21
21
  :icon-size="'medium'"
22
22
  :show-divider="false"
23
23
  :header-size="'medium'"
24
- :card-header-title="card.name"
25
- :card-padding="'small'"
24
+ :card-header-title="normalize(slotProps).name"
25
+ :card-padding="cardPadding"
26
26
  :border-type="'dark'"
27
27
  :border-on-select-background="'blue'"
28
28
  :icon-is-bordered="iconIsBordered"
29
29
  :card-text-align="cardTextAlign"
30
+ :is-selectable="isSelectable"
30
31
  >
31
- <template v-slot:cardTop>
32
- <card-top :card="card" :button-role="'radio'" :is-selectable="true" />
32
+ <template #cardTop>
33
+ <card-top
34
+ v-if="buttonRole !== 'none'"
35
+ :card="normalize(slotProps)"
36
+ :button-role="buttonRole"
37
+ :is-selectable="true"
38
+ @button-role-clicked="selectedButtonRole"
39
+ />
33
40
  </template>
34
- <template v-slot:cardDescription>
35
- <card-body :card="card" />
41
+
42
+ <template #cardDescription>
43
+ <card-body :card="normalize(slotProps)" />
36
44
  </template>
37
45
  </card-grid-item>
38
- <!-----CONTROL SELECTED CARD END-------->
46
+
39
47
  <card-grid-item
40
48
  v-else
41
49
  class="selectcard"
42
- :glyph-src="card.icon"
50
+ :glyph-src="normalize(slotProps).icon"
43
51
  :show-divider="false"
44
52
  :header-size="'large'"
45
- :card-header-title="card.name"
53
+ :card-header-title="normalize(slotProps).name"
46
54
  :card-padding="'small'"
47
55
  :invert-on-select-background="'black'"
48
56
  :border-type="'shadow'"
49
- :description="card.description"
57
+ :description="normalize(slotProps).description"
50
58
  />
51
59
  </template>
52
- <template v-slot:cardGridFooter>
53
- <slot name="cardGridFooter"></slot>
60
+
61
+ <template #cardGridFooter>
62
+ <slot name="cardGridFooter" />
54
63
  </template>
55
64
  </card-grid>
65
+
66
+ <!-- Reusable modal lives here now -->
67
+ <BModal
68
+ v-if="showModalOnButtonRole"
69
+ v-model="showModal"
70
+ :size="modalSize"
71
+ :no-footer="noModalFooter"
72
+ :scrollable="true"
73
+ :class="modalClass"
74
+ @show="$emit('modal-open', modalCard)"
75
+ @hide="$emit('modal-close')"
76
+ >
77
+ <div v-if="modalCard" class="selectable-cards__modal">
78
+ <!-- Parent can fully control; -->
79
+ <slot name="modalTitle" :card="modalCard">
80
+ <h2 class="card-title">{{ modalCard.name }}</h2>
81
+ </slot>
82
+
83
+ <slot name="modalBody" :card="modalCard">
84
+ <div class="card-body">
85
+ <p v-if="modalCard.value" class="card-value">{{ modalCard.value }}</p>
86
+ <div
87
+ v-if="modalCard.description"
88
+ class="card-description"
89
+ v-html="modalCard.description"
90
+ />
91
+ </div>
92
+ </slot>
93
+
94
+ <slot name="modalFooter" :card="modalCard">
95
+ <div />
96
+ </slot>
97
+ </div>
98
+ </BModal>
56
99
  </template>
57
100
 
58
101
  <script>
102
+ import { BModal } from 'bootstrap-vue-next'
59
103
  import CardGrid from './../../Common/CardGrid/index.vue'
60
104
  import CardGridItem from './../../Common/CardGridItem/index.vue'
61
105
  import CardBody from './cardbody.vue'
@@ -63,56 +107,86 @@ import CardTop from './cardtop.vue'
63
107
 
64
108
  export default {
65
109
  name: 'SelectableCards',
66
- components: {
67
- CardGrid,
68
- CardGridItem,
69
- CardBody,
70
- CardTop,
71
- },
110
+ components: { CardGrid, CardGridItem, CardBody, CardTop, BModal },
72
111
  emits: {
73
112
  selected: false,
113
+ 'selected-button-role': false,
114
+ 'modal-open': false,
115
+ 'modal-close': false,
74
116
  },
75
117
  props: {
76
- cards: {
77
- type: Array,
78
- required: true,
79
- default: () => [],
80
- },
81
- title: {
82
- type: String,
83
- default: null,
84
- },
85
- headingTag: {
86
- type: String,
87
- default: 'h2',
88
- },
89
- subHeading: {
118
+ cards: { type: Array, required: true, default: () => [] },
119
+ title: { type: String, default: null },
120
+ headingTag: { type: String, default: 'h2' },
121
+ subHeading: { type: String, default: null },
122
+ iconIsBordered: { type: Boolean, default: true },
123
+ isSelectable: { type: Boolean, default: true },
124
+ columns: { type: Number, default: 4 },
125
+ cardType: { type: String, default: null },
126
+ cardTextAlign: { type: String, default: null },
127
+ buttonRole: { type: String, default: 'radio' },
128
+ cardPadding: {
90
129
  type: String,
91
- default: null,
130
+ default: 'small',
131
+ validator: (value) =>
132
+ ['none', 'xsmall', 'small', 'medium', 'large'].indexOf(value) >= 0,
92
133
  },
93
- iconIsBordered: {
134
+
135
+ /* New modal controls for reusability */
136
+ showModalOnButtonRole: {
94
137
  type: Boolean,
95
138
  default: true,
96
139
  },
97
- columns: {
98
- type: Number,
99
- default: 4,
100
- },
101
- cardType: {
140
+ modalSize: {
102
141
  type: String,
103
- default: null,
142
+ default: 'xl',
104
143
  },
105
- cardTextAlign: {
144
+ modalClass: {
106
145
  type: String,
107
- default: null,
146
+ default: 'wcl-card-modal__modal',
108
147
  },
148
+ noModalFooter: {
149
+ type: Boolean,
150
+ default: true,
151
+ },
152
+ },
153
+ data() {
154
+ return {
155
+ showModal: false,
156
+ modalCard: null,
157
+ }
109
158
  },
110
159
  methods: {
111
- selectedCardChangeFocus(card) {
112
- this.$emit('selected', card.selectedCard)
160
+ // Accept various slot shapes and return the raw card
161
+ normalize(slotProps) {
162
+ const c =
163
+ slotProps?.card?.selectedCard ??
164
+ slotProps?.card ??
165
+ slotProps?.item ??
166
+ slotProps?.selectedCard ??
167
+ slotProps
168
+ return c || {}
113
169
  },
170
+
171
+ selectedCardChangeFocus(payload) {
172
+ // If CardGrid emits { selectedCard }, keep behavior consistent
173
+ const card = payload?.selectedCard ?? payload
174
+ this.$emit('selected', card)
175
+ },
176
+
177
+ selectedButtonRole(card) {
178
+ // Receive raw card from <card-top> and bubble it up as raw object
179
+ this.$emit('selected-button-role', card)
180
+
181
+ // open the reusable modal here
182
+ if (this.showModalOnButtonRole) {
183
+ this.modalCard = card
184
+ this.showModal = true
185
+ }
186
+ },
187
+
114
188
  clearCard() {
115
- this.$refs.cardgrid.clearCards()
189
+ this.$refs.cardgrid?.clearCards?.()
116
190
  },
117
191
  },
118
192
  }
@@ -177,6 +177,10 @@ export default {
177
177
  }
178
178
  }
179
179
 
180
+ &__title {
181
+ margin-bottom: 24px;
182
+ }
183
+
180
184
  /* variants */
181
185
  &.rtl {
182
186
  text-align: left;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  class="wcl-breadcrumb"
4
- :class="{ ['wcl-breadcrumb--rtl']: rtl, ['wcl-breadcrumb--ltr']: !rtl }"
4
+ :class="{ 'wcl-breadcrumb--rtl': rtl, 'wcl-breadcrumb--ltr': !rtl }"
5
5
  >
6
6
  <ol class="breadcrumb">
7
7
  <li
@@ -9,27 +9,60 @@
9
9
  :key="index"
10
10
  class="breadcrumb-item"
11
11
  >
12
- <a v-if="item.href" :href="item.href" target="_self">{{ item.text }}</a>
13
- <nuxt-link v-else-if="item.to" :to="item.to">{{ item.text }}</nuxt-link>
12
+ <!-- Plain anchor -->
13
+ <a
14
+ v-if="item.href"
15
+ :href="item.href"
16
+ target="_self"
17
+ @click="onClick(item, index, $event)"
18
+ >{{ item.text }}</a
19
+ >
20
+
21
+ <!-- NuxtLink (Nuxt 3 / Vue 3) -->
22
+ <nuxt-link
23
+ v-else-if="item.to"
24
+ :to="item.to"
25
+ @click="onClick(item, index, $event)"
26
+ >{{ item.text }}</nuxt-link
27
+ >
28
+
29
+ <!-- Fallback (no link) -->
30
+ <span v-else @click="onClick(item, index, $event)">{{
31
+ item.text
32
+ }}</span>
14
33
  </li>
15
34
  </ol>
16
35
  </div>
17
36
  </template>
37
+
18
38
  <script>
19
39
  export default {
20
40
  name: 'Breadcrumb',
21
41
  props: {
22
42
  breadcrumbItems: {
23
43
  type: Array,
24
- required: true
44
+ required: true,
25
45
  },
26
46
  rtl: {
27
47
  type: Boolean,
28
- required: false
29
- }
30
- }
48
+ required: false,
49
+ },
50
+ // If true, prevent default navigation and let the parent handle it.
51
+ interceptClicks: {
52
+ type: Boolean,
53
+ default: false,
54
+ },
55
+ },
56
+ emits: ['item-click'],
57
+ methods: {
58
+ onClick(item, index, event) {
59
+ this.$emit('item-click', { item, index, event })
60
+ if (this.interceptClicks) event.preventDefault()
61
+ },
62
+ },
31
63
  }
32
64
  </script>
65
+
33
66
  <style lang="scss" scoped>
34
67
  @import '../../../includes/scss/vars/src/colors.scss';
35
68
 
@@ -54,7 +54,7 @@ export class BaseFormElement {
54
54
  getLabelPosition() {
55
55
  return this.getPosition(
56
56
  this.webformElement['#_title_display'] ??
57
- this.webformElement['#title_display']
57
+ this.webformElement['#title_display'],
58
58
  )
59
59
  }
60
60
 
@@ -160,7 +160,7 @@ export class BaseFormElement {
160
160
  label: getStyle(this.webformElement['#label_attributes']),
161
161
  wrapper: getStyle(this.webformElement['#wrapper_attributes']),
162
162
  element: getStyle(this.webformElement['#attributes']),
163
- additional: this.getAdditionalStyles(this.getCustomClass())
163
+ additional: this.getAdditionalStyles(this.getCustomClass()),
164
164
  }
165
165
  const hasOverride = Object.values(style).some((attr) => !this.isEmpty(attr))
166
166
  return hasOverride ? style : undefined
@@ -251,7 +251,8 @@ export class BaseFormElement {
251
251
  if (this.isEmpty(this.webformElement['#states'])) {
252
252
  return undefined
253
253
  } else {
254
- return parseElementStates(this.webformElement)
254
+ // NEW: Pass the current instance (`this`) to the parser.
255
+ return parseElementStates(this.webformElement, this)
255
256
  }
256
257
  }
257
258
 
@@ -3,6 +3,39 @@ import { converFormIOElementToJSON } from './convert-form-element'
3
3
  import { updateConditionWithFormReferences } from './logic-linker'
4
4
  import { RegisterFactory } from './registry-factory'
5
5
 
6
+ function extractWebformKeys(webformObject) {
7
+ const result = []
8
+
9
+ const isElement = (obj) => obj && typeof obj === 'object' && obj['#type']
10
+
11
+ const extract = (obj) => {
12
+ if (!obj || typeof obj !== 'object') return null
13
+
14
+ const type = obj['#type']
15
+
16
+ const key = obj['#webform_key'] || null
17
+ const node = key ? { key, children: [], type } : null
18
+
19
+ for (const [childKey, val] of Object.entries(obj)) {
20
+ if (childKey.startsWith('#')) continue
21
+ if (isElement(val)) {
22
+ const childNode = extract(val)
23
+ if (childNode && node) node.children.push(childNode)
24
+ else if (childNode) return childNode
25
+ }
26
+ }
27
+
28
+ return node
29
+ }
30
+
31
+ for (const [topKey, val] of Object.entries(webformObject)) {
32
+ if (typeof val === 'object' && val['#type']) {
33
+ const topNode = extract(val)
34
+ if (topNode) result.push(topNode)
35
+ }
36
+ }
37
+ return result
38
+ }
6
39
  export class FormRenderParser {
7
40
  initialise(contentApiUrl, isPreview) {
8
41
  this.formSettingsMeta = { contentApiUrl }
@@ -17,14 +50,16 @@ export class FormRenderParser {
17
50
  const formlogicList = []
18
51
  try {
19
52
  webform = this.preProcessWebForm(webform)
53
+ const pathTree = extractWebformKeys(webform)
20
54
  const formiodefinition = {
21
55
  id: webform['#id'],
22
56
  components: this.parseFormContainerComponents(
23
57
  webform,
24
58
  webFormElementTree,
25
59
  null,
26
- formlogicList
27
- )
60
+ formlogicList,
61
+ pathTree,
62
+ ),
28
63
  }
29
64
 
30
65
  options.breadcrumbSettings = { clickable: false }
@@ -35,14 +70,12 @@ export class FormRenderParser {
35
70
 
36
71
  formiodefinition.display = this.getDisplay(webFormElementTree)
37
72
  if (this.isPreview) {
38
- console.log('drupal webform', webform)
39
- console.log('formio definition', formiodefinition)
40
73
  }
41
74
  return {
42
75
  definition: formiodefinition,
43
76
  tree: webFormElementTree,
44
77
  options,
45
- css
78
+ css,
46
79
  }
47
80
  } catch (e) {
48
81
  console.error(e)
@@ -54,7 +87,8 @@ export class FormRenderParser {
54
87
  container,
55
88
  webFormElementTree,
56
89
  parent,
57
- formlogicList
90
+ formlogicList,
91
+ tree,
58
92
  ) => {
59
93
  return Object.keys(container)
60
94
  .filter((i) => !i.startsWith('#'))
@@ -63,11 +97,12 @@ export class FormRenderParser {
63
97
  return RegisterFactory.getFormIOElement(
64
98
  webFormElement,
65
99
  this.formSettingsMeta,
66
- parent
100
+ parent,
101
+ tree,
67
102
  )
68
103
  })
69
104
  .filter(
70
- (formIOElement) => !!formIOElement && formIOElement.isRenderable()
105
+ (formIOElement) => !!formIOElement && formIOElement.isRenderable(),
71
106
  )
72
107
  .map((formIOElement) => {
73
108
  // nested form logic
@@ -78,11 +113,11 @@ export class FormRenderParser {
78
113
  subContainer,
79
114
  webFormElementTree,
80
115
  formIOElement,
81
- formlogicList
82
- )
116
+ formlogicList,
117
+ tree,
118
+ ),
83
119
  )
84
120
  }
85
-
86
121
  return formIOElement
87
122
  })
88
123
  .map((formIOElement) => {
@@ -90,7 +125,7 @@ export class FormRenderParser {
90
125
  webFormElementTree[formIOElement.getKey()] = formIOElement
91
126
  const formioElementJSON = converFormIOElementToJSON(
92
127
  formIOElement,
93
- formlogicList
128
+ formlogicList,
94
129
  ).json
95
130
  formIOElement.setFormIOdefinitionForElement(formioElementJSON)
96
131
  return formioElementJSON
@@ -103,7 +138,7 @@ export class FormRenderParser {
103
138
  webform.actions = {
104
139
  '#type': 'webform_actions',
105
140
  '#webform_id': 'form--actions',
106
- '#webform_key': 'actions'
141
+ '#webform_key': 'actions',
107
142
  }
108
143
  }
109
144
  return webform
@@ -115,8 +150,8 @@ export class FormRenderParser {
115
150
  updateConditionWithFormReferences(
116
151
  logic,
117
152
  sourceElement,
118
- webFormElementTree
119
- )
153
+ webFormElementTree,
154
+ ),
120
155
  )
121
156
  })
122
157
  }
@@ -146,7 +181,7 @@ export class FormRenderParser {
146
181
 
147
182
  getDisplay(webFormElementTree) {
148
183
  const pageKeys = Object.keys(webFormElementTree).filter((elementKey) =>
149
- webFormElementTree[elementKey].isPageComponent()
184
+ webFormElementTree[elementKey].isPageComponent(),
150
185
  )
151
186
  if (pageKeys.length > 0) {
152
187
  return 'wizard'