metadata_presenter 2.4.2 → 2.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3e99fe731812a98c2a9d2f6cefcade21ccd233f24118cd23440aa4d726b49c1
4
- data.tar.gz: 98496d517a2cdbf8da121d9d15469c9f017065921ca81b8133d895cec9bc9cff
3
+ metadata.gz: 1f8ec576a9274943e0bc1bd36901b909e408073718c64e6310ac31035227aa69
4
+ data.tar.gz: 42be1d66e898e46e79f9292034da70e12d3e2abdd62d98787dc12c9391b34205
5
5
  SHA512:
6
- metadata.gz: f3735753a806e4b199b6420ed46469a06a856fa2140a3c893f91e187a8ae2a8068f5d54118ddefddabfd72db15ca04b9d532e9eb63019aa5d5611497ce322aee
7
- data.tar.gz: 5322246015561fc511b8fb1dc2072fec440df0fce1ca34555a3893b6a43b281b59d896a0941e3d69ea0f0114ea90408fc702b2bcd7a8080ade9934dc09bdd3ad
6
+ metadata.gz: 54081e0c1702f9c69fd136714bf24c455a089b1b37675721aecd3ebe7c5c548ca5973c91a1d6ef8c5d8a7987ef5fa22d448cf61f4ed72fe8cd3f7b78cacf8241
7
+ data.tar.gz: 5baa5506512d0d40ead58378c7ed288400f3a210304ecc89943d26e72b046dc4d1181913d7029936f41b14942a2e95f46fa49538d250b7f8d3977958278bafa2
@@ -0,0 +1,203 @@
1
+ module MetadataPresenter
2
+ class Spacer < OpenStruct
3
+ end
4
+
5
+ class Grid
6
+ def initialize(service)
7
+ @service = service
8
+ @ordered = []
9
+ @routes = []
10
+ @traversed = []
11
+ @coordinates = setup_coordinates
12
+ end
13
+
14
+ ROW_ZERO = 0
15
+
16
+ def build
17
+ return @ordered unless @ordered.empty?
18
+
19
+ @ordered = make_grid
20
+ add_columns
21
+ add_rows
22
+ add_by_coordinates
23
+ insert_expression_spacers
24
+ trim_spacers
25
+
26
+ @ordered
27
+ end
28
+
29
+ def ordered_flow
30
+ @ordered_flow ||=
31
+ build.flatten.reject { |obj| obj.is_a?(MetadataPresenter::Spacer) }
32
+ end
33
+
34
+ def ordered_pages
35
+ ordered_flow.reject(&:branch?)
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :service
41
+ attr_accessor :ordered, :traversed, :routes, :coordinates
42
+
43
+ def setup_coordinates
44
+ service.flow.keys.index_with { |_uuid| { row: nil, column: nil } }
45
+ end
46
+
47
+ def route_from_start
48
+ @route_from_start ||=
49
+ MetadataPresenter::Route.new(
50
+ service: service,
51
+ traverse_from: service.start_page.uuid
52
+ )
53
+ end
54
+
55
+ def make_grid
56
+ traverse_all_routes
57
+
58
+ rows = @routes.map(&:row).max
59
+ columns = @routes.map { |r| r.column + r.flow_uuids.count }.max
60
+ columns.times.map { rows.times.map { MetadataPresenter::Spacer.new } }
61
+ end
62
+
63
+ def traverse_all_routes
64
+ # Always traverse the route that begins from the start page first and get
65
+ # the potential routes from any branching points that exist.
66
+ route_from_start.traverse
67
+ @routes.append(route_from_start)
68
+ traversed_routes = route_from_start.routes
69
+
70
+ index = 0
71
+ until traversed_routes.empty?
72
+ if index > total_potential_routes
73
+ ActiveSupport::Notifications.instrument(
74
+ 'exceeded_total_potential_routes',
75
+ message: 'Exceeded total number of potential routes'
76
+ )
77
+ break
78
+ end
79
+
80
+ route = traversed_routes.shift
81
+ @routes.append(route)
82
+
83
+ # Every route exiting a branching point needs to be traversed and any
84
+ # additional routes from other branching points collected and then also
85
+ # traversed.
86
+ route.traverse
87
+ traversed_routes |= route.routes
88
+
89
+ index += 1
90
+ end
91
+ end
92
+
93
+ def add_columns
94
+ @routes.each do |route|
95
+ route.flow_uuids.each.with_index(route.column) do |uuid, column|
96
+ column_number = @coordinates[uuid][:column]
97
+ if column_number.nil? || column > column_number
98
+ @coordinates[uuid][:column] = column
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ def add_rows
105
+ @routes.each do |route|
106
+ route.flow_uuids.each do |uuid|
107
+ next if @traversed.include?(uuid)
108
+
109
+ @coordinates[uuid][:row] = route.row if @coordinates[uuid][:row].nil?
110
+
111
+ update_route_rows(route, uuid)
112
+ @traversed.push(uuid)
113
+ end
114
+ end
115
+ end
116
+
117
+ # Each Route object has a starting row. Each Route object has no knowledge
118
+ # of other potential routes and pages/branches that may or may not exist in
119
+ # them. The starting row may need to change dependent upon what has been
120
+ # traversed in other routes.
121
+ def update_route_rows(route, uuid)
122
+ flow_object = service.flow_object(uuid)
123
+ if flow_object.branch? && route.row > ROW_ZERO
124
+ destinations = routes_exiting_branch(flow_object)
125
+ destinations.each.with_index(route.row) do |destination_uuid, row|
126
+ @routes.each do |r|
127
+ r.row = row if r.traverse_from == destination_uuid
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ def add_by_coordinates
134
+ @coordinates.each do |uuid, position|
135
+ # If row and column are nil then the object is detached
136
+ next if position[:row].nil? || position[:column].nil?
137
+
138
+ flow_object = service.flow_object(uuid)
139
+ @ordered[position[:column]][position[:row]] = flow_object
140
+ end
141
+ end
142
+
143
+ # Find the very last MetadataPresenter::Flow object in every column and
144
+ # remove any Spacer objects after that.
145
+ def trim_spacers
146
+ @ordered.each_with_index do |column, index|
147
+ last_index_of = column.rindex { |item| item.is_a?(MetadataPresenter::Flow) }
148
+ @ordered[index] = @ordered[index][0..last_index_of]
149
+ end
150
+ end
151
+
152
+ # Each branch has a certain number of exits that require their own line
153
+ # and arrow. Insert any spacers into the necessary row in the column after
154
+ # the one the branch is located in.
155
+ def insert_expression_spacers
156
+ service.branches.each do |branch|
157
+ position = @coordinates[branch.uuid]
158
+ next if position[:row].nil? || position[:column].nil? # detached branch
159
+
160
+ next_column = position[:column] + 1
161
+ uuids = []
162
+ exiting_destinations_from_branch(branch).each.with_index(position[:row]) do |uuid, row|
163
+ if uuids.include?(uuid)
164
+ @ordered[next_column].insert(row, MetadataPresenter::Spacer.new)
165
+ end
166
+
167
+ uuids.push(uuid) unless uuids.include?(uuid)
168
+ end
169
+ end
170
+ end
171
+
172
+ # The frontend requires that expressions of type 'or' get there own line and
173
+ # arrow. 'and' expression types continue to be grouped together.
174
+ # Return the UUIDs of the destinations exiting a branch and allow duplicates
175
+ # if the expression type is an 'or'.
176
+ def exiting_destinations_from_branch(branch)
177
+ destination_uuids = branch.conditionals.map do |conditional|
178
+ if conditional.type == 'or'
179
+ conditional.expressions.map(&:next)
180
+ else
181
+ conditional.next
182
+ end
183
+ end
184
+ destination_uuids.flatten
185
+ end
186
+
187
+ # Any destinations exiting the branch that have not already been traversed.
188
+ def routes_exiting_branch(branch)
189
+ branch.all_destination_uuids.reject { |uuid| @traversed.include?(uuid) }
190
+ end
191
+
192
+ # Deliberately not including the default next for each branch as when row
193
+ # zero is created it takes the first available conditional for each branch.
194
+ # The remaining are then used to create route objects. Therefore the total
195
+ # number of remaining routes will be the same as the total of all the branch
196
+ # conditionals.
197
+ # Add 1 additional route as that represents the route_from_start.
198
+ def total_potential_routes
199
+ @total_potential_routes ||=
200
+ service.branches.sum { |branch| branch.conditionals.size } + 1
201
+ end
202
+ end
203
+ end
@@ -18,6 +18,7 @@ module MetadataPresenter
18
18
  page.checkanswers
19
19
  page.confirmation
20
20
  page.multiplequestions
21
+ page.exit
21
22
  ].freeze
22
23
 
23
24
  def editable_attributes
@@ -0,0 +1,76 @@
1
+ module MetadataPresenter
2
+ class Route
3
+ attr_reader :traverse_from
4
+ attr_accessor :flow_uuids, :routes, :row, :column
5
+
6
+ def initialize(service:, traverse_from:, row: 0, column: 0)
7
+ @service = service
8
+ @traverse_from = traverse_from
9
+ @row = row
10
+ @column = column
11
+ @routes = []
12
+ @flow_uuids = []
13
+ end
14
+
15
+ def traverse
16
+ @flow_uuid = traverse_from
17
+
18
+ index = column
19
+ until @flow_uuid.blank?
20
+ if index > service.flow.size
21
+ ActiveSupport::Notifications.instrument(
22
+ 'exceeded_total_flow_objects',
23
+ message: 'Exceeded total number of flow objects'
24
+ )
25
+ break
26
+ end
27
+
28
+ @flow_uuids.push(@flow_uuid) unless @flow_uuids.include?(@flow_uuid)
29
+ flow_object = service.flow_object(@flow_uuid)
30
+
31
+ if flow_object.branch?
32
+ destinations = destination_uuids(flow_object)
33
+ # Take the first conditional destination and follow that until the end
34
+ # of the route.
35
+ @flow_uuid = destinations.shift
36
+
37
+ # The remaining conditional destinations and the branch's default next
38
+ # (otherwise) will be the starting point for a new Route object to be
39
+ # traversed.
40
+ # The default behaviour is that the next destination will be on the row
41
+ # below this current route's row. This can be changed under certain
42
+ # conditions in the Grid model.
43
+ # Each of the destinations need to be placed in the column after the
44
+ # current column.
45
+ row_number = row + 1
46
+ column_number = index + 1
47
+ destinations.each do |uuid|
48
+ @routes.push(
49
+ MetadataPresenter::Route.new(
50
+ service: service,
51
+ traverse_from: uuid,
52
+ row: row_number,
53
+ column: column_number
54
+ )
55
+ )
56
+ row_number += 1
57
+ end
58
+ else
59
+ @flow_uuid = flow_object.default_next
60
+ end
61
+
62
+ index += 1
63
+ end
64
+
65
+ @flow_uuids
66
+ end
67
+
68
+ private
69
+
70
+ attr_reader :service
71
+
72
+ def destination_uuids(flow_object)
73
+ flow_object.conditionals.map(&:next).push(flow_object.default_next)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ <div class="fb-main-grid-wrapper" data-fb-pagetype="<%= @page.type %>">
2
+ <div class="govuk-grid-row">
3
+ <div class="govuk-grid-column-two-thirds">
4
+
5
+ <%= render 'metadata_presenter/attribute/section_heading' %>
6
+
7
+ <h1 class="fb-editable govuk-heading-xl"
8
+ data-fb-content-id="page[heading]"
9
+ data-fb-content-type="element">
10
+ <%= @page.heading %>
11
+ </h1>
12
+
13
+ <%= render 'metadata_presenter/attribute/lede' %>
14
+
15
+ <%= form_for @page_answers, as: :answers, url: @page.url, method: :post do |f| %>
16
+
17
+ <%= render partial: 'metadata_presenter/component/components',
18
+ locals: {
19
+ f: f,
20
+ components: @page.components,
21
+ tag: nil,
22
+ classes: nil,
23
+ input_components: @page.supported_input_components,
24
+ content_components: @page.supported_content_components
25
+ } %>
26
+ <% end %>
27
+ </div>
28
+ </div>
29
+ </div>
@@ -12,6 +12,10 @@ Rails.application.config.supported_components =
12
12
  input: %w(),
13
13
  content: %w(content)
14
14
  },
15
+ exit: {
16
+ input: %w(),
17
+ content: %w(content)
18
+ },
15
19
  multiplequestions: {
16
20
  input: %w(text textarea number date radios checkboxes),
17
21
  content: %w(content)
@@ -0,0 +1,9 @@
1
+ {
2
+ "_id": "page.exit",
3
+ "_type": "page.exit",
4
+ "section_heading": "",
5
+ "heading": "Title",
6
+ "lede": "",
7
+ "components": [],
8
+ "url": ""
9
+ }
@@ -148,7 +148,7 @@
148
148
  "body": "Start page body",
149
149
  "_type": "page.start",
150
150
  "_uuid": "cf6dc32f-502c-4215-8c27-1151a45735bb",
151
- "heading": "Branching Fixture 4",
151
+ "heading": "Branching Fixture 7",
152
152
  "before_you_start": "Start page before you start"
153
153
  },
154
154
  {
@@ -329,28 +329,20 @@
329
329
  {
330
330
  "_id": "page.g",
331
331
  "url": "page-g",
332
- "body": "Page G body",
333
- "lede": "",
334
- "_type": "page.singlequestion",
332
+ "lede": "lede",
333
+ "_type": "page.exit",
335
334
  "_uuid": "3a584d15-6805-4a21-bc05-b61c3be47857",
336
335
  "heading": "Page G",
337
336
  "components": [
338
337
  {
339
- "_id": "page-g_text_1",
340
- "hint": "",
341
- "name": "page-g_text_1",
342
- "_type": "text",
343
- "_uuid": "fa91e9cd-d935-4c15-bd92-5b6fafd81a91",
344
- "label": "Page G",
345
- "errors": {},
346
- "collection": "components",
347
- "validation": {
348
- "required": true,
349
- "max_length": 20,
350
- "min_length": 2
351
- }
338
+ "_id": "exit_content_1",
339
+ "name": "exit_content_1",
340
+ "_type": "content",
341
+ "_uuid": "9774d986-135a-4b7f-924d-7527b790e1a0",
342
+ "content": "This is an exit page"
352
343
  }
353
- ]
344
+ ],
345
+ "section_heading": "Section heading"
354
346
  },
355
347
  {
356
348
  "_id": "page.h",
@@ -559,7 +551,7 @@
559
551
  "created_by": "099d5bf5-5f7b-444c-86ee-9e189cc1a369",
560
552
  "service_id": "ea3bfb09-fc6d-4464-b025-9d6aadafc593",
561
553
  "version_id": "429a8039-75b3-42b8-84dc-230fb371c71d",
562
- "service_name": "Branching Fixture 4",
554
+ "service_name": "Branching Fixture 7",
563
555
  "configuration": {
564
556
  "meta": {
565
557
  "_id": "config.meta",
@@ -142,7 +142,7 @@
142
142
  "body": "Start page body",
143
143
  "_type": "page.start",
144
144
  "_uuid": "cf6dc32f-502c-4215-8c27-1151a45735bb",
145
- "heading": "Branching Fixture 4",
145
+ "heading": "Branching Fixture 8",
146
146
  "before_you_start": "Start page before you start"
147
147
  },
148
148
  {
@@ -497,7 +497,7 @@
497
497
  "created_by": "099d5bf5-5f7b-444c-86ee-9e189cc1a369",
498
498
  "service_id": "ea3bfb09-fc6d-4464-b025-9d6aadafc593",
499
499
  "version_id": "429a8039-75b3-42b8-84dc-230fb371c71d",
500
- "service_name": "Branching Fixture 4",
500
+ "service_name": "Branching Fixture 8",
501
501
  "configuration": {
502
502
  "meta": {
503
503
  "_id": "config.meta",
@@ -1,3 +1,3 @@
1
1
  module MetadataPresenter
2
- VERSION = '2.4.2'.freeze
2
+ VERSION = '2.6.1'.freeze
3
3
  end
@@ -2,3 +2,4 @@ require 'metadata_presenter/engine'
2
2
  require 'govuk_design_system_formbuilder'
3
3
  require 'json-schema'
4
4
  require 'kramdown'
5
+ require 'active_support/notifications'
@@ -0,0 +1,25 @@
1
+ {
2
+ "$id": "http://gov.uk/schema/v1.0.0/page/exit",
3
+ "_name": "page.exit",
4
+ "title": "Exit",
5
+ "description": "Display an exit page to users when they cannot proceed",
6
+ "type": "object",
7
+ "properties": {
8
+ "_type": {
9
+ "const": "page.exit"
10
+ },
11
+ "components": {
12
+ "title": "Content",
13
+ "description": "The form or content elements used on the page",
14
+ "type": "array",
15
+ "items": {
16
+ "$ref": "component.content"
17
+ }
18
+ }
19
+ },
20
+ "allOf": [
21
+ {
22
+ "$ref": "definition.page.content"
23
+ }
24
+ ]
25
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metadata_presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.2
4
+ version: 2.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - MoJ Forms
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-27 00:00:00.000000000 Z
11
+ date: 2021-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: govuk_design_system_formbuilder
@@ -283,6 +283,7 @@ files:
283
283
  - app/models/metadata_presenter/expression.rb
284
284
  - app/models/metadata_presenter/file_uploader.rb
285
285
  - app/models/metadata_presenter/flow.rb
286
+ - app/models/metadata_presenter/grid.rb
286
287
  - app/models/metadata_presenter/item.rb
287
288
  - app/models/metadata_presenter/meta.rb
288
289
  - app/models/metadata_presenter/meta_item.rb
@@ -292,6 +293,7 @@ files:
292
293
  - app/models/metadata_presenter/page.rb
293
294
  - app/models/metadata_presenter/page_answers.rb
294
295
  - app/models/metadata_presenter/previous_page.rb
296
+ - app/models/metadata_presenter/route.rb
295
297
  - app/models/metadata_presenter/service.rb
296
298
  - app/models/metadata_presenter/traversed_pages.rb
297
299
  - app/models/metadata_presenter/uploaded_file.rb
@@ -335,6 +337,7 @@ files:
335
337
  - app/views/metadata_presenter/page/checkanswers.html.erb
336
338
  - app/views/metadata_presenter/page/confirmation.html.erb
337
339
  - app/views/metadata_presenter/page/content.html.erb
340
+ - app/views/metadata_presenter/page/exit.html.erb
338
341
  - app/views/metadata_presenter/page/form.html.erb
339
342
  - app/views/metadata_presenter/page/multiplequestions.html.erb
340
343
  - app/views/metadata_presenter/page/singlequestion.html.erb
@@ -365,6 +368,7 @@ files:
365
368
  - default_metadata/page/checkanswers.json
366
369
  - default_metadata/page/confirmation.json
367
370
  - default_metadata/page/content.json
371
+ - default_metadata/page/exit.json
368
372
  - default_metadata/page/multiplequestions.json
369
373
  - default_metadata/page/singlequestion.json
370
374
  - default_metadata/page/standalone.json
@@ -458,6 +462,7 @@ files:
458
462
  - schemas/page/checkanswers.json
459
463
  - schemas/page/confirmation.json
460
464
  - schemas/page/content.json
465
+ - schemas/page/exit.json
461
466
  - schemas/page/multiplequestions.json
462
467
  - schemas/page/singlequestion.json
463
468
  - schemas/page/standalone.json