terminal-layout 0.1.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.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/.travis.yml +14 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +60 -0
- data/README.md +7 -0
- data/block-flow.rb +361 -0
- data/lib/ansi_string.rb +315 -0
- data/lib/terminal_layout.rb +527 -0
- data/spec/ansi_string_spec.rb +499 -0
- data/spec/spec_helper.rb +102 -0
- data/spec/terminal_layout_spec.rb +745 -0
- data/terminal-layout.gemspec +28 -0
- data/test-1.rb +90 -0
- metadata +158 -0
@@ -0,0 +1,745 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
def print_tree(render_tree, indent=0)
|
5
|
+
render_tree.children.each do |box|
|
6
|
+
print " " * indent
|
7
|
+
puts box
|
8
|
+
print_tree box, indent + 2
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_first_in_tree(tree, comparator_proc)
|
13
|
+
tree.children.each do |node|
|
14
|
+
return node if comparator_proc.call(node)
|
15
|
+
find_first_in_tree node, comparator_proc
|
16
|
+
end
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_all_in_tree(tree, comparator_proc)
|
21
|
+
return nil if tree.children.empty?
|
22
|
+
tree.children.inject([]) do |results, node|
|
23
|
+
if comparator_proc.call(node)
|
24
|
+
results.push node
|
25
|
+
else
|
26
|
+
results.push find_first_in_tree(node, comparator_proc)
|
27
|
+
end
|
28
|
+
results.compact
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
module TerminalLayout
|
34
|
+
|
35
|
+
describe "Laying things out" do
|
36
|
+
subject(:render_tree){ RenderTree.new(view, parent:nil, style:style).tap{ |rt| rt.layout } }
|
37
|
+
let(:view){ Box.new(style: style, children: children) }
|
38
|
+
let(:style){ raise(NotImplementedError, "Must provide :children") }
|
39
|
+
let(:children){ raise(NotImplementedError, "Must provide :children") }
|
40
|
+
|
41
|
+
def first_rendered(box, parent: render_tree)
|
42
|
+
find_first_in_tree(parent, ->(node){ node.box == box })
|
43
|
+
end
|
44
|
+
|
45
|
+
def all_rendered(box, parent: render_tree)
|
46
|
+
find_all_in_tree(parent, ->(node){ node.box == box })
|
47
|
+
end
|
48
|
+
|
49
|
+
after(:each) do |example|
|
50
|
+
if example.exception
|
51
|
+
puts
|
52
|
+
puts " #{example.location} #{example.description}"
|
53
|
+
print_tree render_tree
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "normal block flow - vertical stacking" do
|
58
|
+
context "with one block child" do
|
59
|
+
let(:style){ {width:10, height: 10} }
|
60
|
+
let(:children){ [block_a] }
|
61
|
+
let(:block_a){ Box.new(style: {display: :block, height: 1}) }
|
62
|
+
let(:rendered_element){ first_rendered(block_a) }
|
63
|
+
|
64
|
+
it "positions the box at (0,0) with the same width as its containing box" do
|
65
|
+
expect(rendered_element.position).to eq(Position.new(0, 0))
|
66
|
+
expect(rendered_element.size).to eq(Dimension.new(10, 1))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "block with content" do
|
71
|
+
let(:style){ {width:10, height: 10} }
|
72
|
+
let(:children){ [block_a] }
|
73
|
+
let(:block_a){ Box.new(content: "Foobar", style: {display: :block}) }
|
74
|
+
let(:rendered_element){ first_rendered(block_a) }
|
75
|
+
|
76
|
+
it "sets the height" do
|
77
|
+
expect(rendered_element.position).to eq(Position.new(0, 0))
|
78
|
+
expect(rendered_element.size).to eq(Dimension.new(10, 1))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "block with height set" do
|
83
|
+
let(:style){ {width:10, height: 10} }
|
84
|
+
let(:children){ [block_a] }
|
85
|
+
let(:block_a){ Box.new(style: {display: :block, height:4}, children:[
|
86
|
+
Box.new(content: "Foobar", style: {display: :inline})
|
87
|
+
])}
|
88
|
+
let(:rendered_element){ first_rendered(block_a) }
|
89
|
+
|
90
|
+
it "doesn't shrink the height when the content needs less" do
|
91
|
+
expect(block_a.height).to eq 4
|
92
|
+
end
|
93
|
+
|
94
|
+
it "doesn't grow the height when the content needs more" do
|
95
|
+
block_a.content = "Foobar" * 100
|
96
|
+
expect(block_a.height).to eq 4
|
97
|
+
end
|
98
|
+
|
99
|
+
it "doesn't affect the height of the render tree" do
|
100
|
+
expect(render_tree.height).to eq 10
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "with multiple block children" do
|
105
|
+
let(:style){ {width:10, height: 10} }
|
106
|
+
let(:children){ [block_a, block_b] }
|
107
|
+
let(:block_a){ Box.new(style: {display: :block, height: 1}) }
|
108
|
+
let(:block_b){ Box.new(style: {display: :block, height: 1}) }
|
109
|
+
|
110
|
+
let(:rendered_element_a){ first_rendered(block_a) }
|
111
|
+
let(:rendered_element_b){ first_rendered(block_b) }
|
112
|
+
|
113
|
+
it "stacks the boxes vertically with the widths matching the containing box" do
|
114
|
+
expect(render_tree.children.length).to eq(2)
|
115
|
+
|
116
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
117
|
+
expect(rendered_element_a.size).to eq(Dimension.new(10, 1))
|
118
|
+
|
119
|
+
expect(rendered_element_b.position).to eq(Position.new(0, 1))
|
120
|
+
expect(rendered_element_b.size).to eq(Dimension.new(10, 1))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "block content following inline content" do
|
125
|
+
let(:style){ {width:10, height: 10} }
|
126
|
+
let(:children){ [inline_a, block_b] }
|
127
|
+
let(:inline_a){ Box.new(content: "ABCDE", style: {display: :inline}) }
|
128
|
+
let(:block_b){ Box.new(style: {display: :block, height: 1, width: 5}) }
|
129
|
+
|
130
|
+
let(:rendered_element_a){ first_rendered(inline_a) }
|
131
|
+
let(:rendered_element_b){ first_rendered(block_b) }
|
132
|
+
|
133
|
+
it "puts the block on the next line even though they would both fit on a single line" do
|
134
|
+
expect(render_tree.children.length).to eq(2)
|
135
|
+
|
136
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
137
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
138
|
+
|
139
|
+
expect(rendered_element_b.position).to eq(Position.new(0, 1))
|
140
|
+
expect(rendered_element_b.size).to eq(Dimension.new(5, 1))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "with a block child that has 0 height" do
|
145
|
+
let(:style){ {width:10, height: 10} }
|
146
|
+
let(:children){ [block_0_height] }
|
147
|
+
let(:block_0_height){ Box.new(style: {display: :block, height: 0}) }
|
148
|
+
|
149
|
+
it "doesn't include it in the layout render_tree" do
|
150
|
+
expect(render_tree.children.length).to eq(0)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "with a block child that has 0 width" do
|
155
|
+
let(:style){ {width:10, height: 10} }
|
156
|
+
let(:children){ [block_0_width] }
|
157
|
+
let(:block_0_width){ Box.new(style: {display: :block, width: 0}) }
|
158
|
+
|
159
|
+
it "doesn't include it in the layout render_tree" do
|
160
|
+
expect(render_tree.children.length).to eq(0)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "normal flow - inline elements" do
|
166
|
+
context "with multiple inline children that fit within the width of the parent" do
|
167
|
+
let(:style){ {width:10, height: 10} }
|
168
|
+
let(:children){ [inline_a, inline_b] }
|
169
|
+
let(:inline_a){ Box.new(content:"ABC", style: {display: :inline}) }
|
170
|
+
let(:inline_b){ Box.new(content:"DEFGHIJ", style: {display: :inline}) }
|
171
|
+
|
172
|
+
let(:rendered_element_a){ first_rendered(inline_a) }
|
173
|
+
let(:rendered_element_b){ first_rendered(inline_b) }
|
174
|
+
|
175
|
+
it "horizontally lays out the two boxes with the width matching their respective content's length" do
|
176
|
+
expect(render_tree.children.length).to eq(2)
|
177
|
+
|
178
|
+
expect(rendered_element_a.content).to eq("ABC")
|
179
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
180
|
+
expect(rendered_element_a.size).to eq(Dimension.new(3, 1))
|
181
|
+
|
182
|
+
expect(rendered_element_b.content).to eq("DEFGHIJ")
|
183
|
+
expect(rendered_element_b.position).to eq(Position.new(3, 0))
|
184
|
+
expect(rendered_element_b.size).to eq(Dimension.new(7, 1))
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context "with multiple inline children that do not fit within the width of the parent" do
|
189
|
+
let(:style){ {width:10, height: 10} }
|
190
|
+
let(:children){ [inline_a, inline_b] }
|
191
|
+
let(:inline_a){ Box.new(content:"ABCDEFG", style: {display: :inline}) }
|
192
|
+
let(:inline_b){ Box.new(content:"HIJKLMNO", style: {display: :inline}) }
|
193
|
+
|
194
|
+
let(:rendered_element_a){ first_rendered(inline_a) }
|
195
|
+
let(:rendered_elements_b){ all_rendered(inline_b) }
|
196
|
+
|
197
|
+
it "horizontally lays out the two boxes wrapping lines as needed" do
|
198
|
+
expect(render_tree.children.length).to eq(3)
|
199
|
+
|
200
|
+
# row 1
|
201
|
+
expect(rendered_element_a.content).to eq("ABCDEFG")
|
202
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
203
|
+
expect(rendered_element_a.size).to eq(Dimension.new(7, 1))
|
204
|
+
|
205
|
+
expect(rendered_elements_b[0].content).to eq("HIJ")
|
206
|
+
expect(rendered_elements_b[0].position).to eq(Position.new(7, 0))
|
207
|
+
expect(rendered_elements_b[0].size).to eq(Dimension.new(3, 1))
|
208
|
+
|
209
|
+
# row 2
|
210
|
+
expect(rendered_elements_b[1].content).to eq("KLMNO")
|
211
|
+
expect(rendered_elements_b[1].position).to eq(Position.new(0, 1))
|
212
|
+
expect(rendered_elements_b[1].size).to eq(Dimension.new(5, 1))
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "normal flow - float left" do
|
218
|
+
context "with a single element float left without a width" do
|
219
|
+
let(:style){ {width:10, height: 10} }
|
220
|
+
let(:children){ [float_a] }
|
221
|
+
let(:float_a){ Box.new(style: {display: :float, float: :left}) }
|
222
|
+
|
223
|
+
it "doesn't include the element in the render render_tree" do
|
224
|
+
expect(render_tree.children.length).to eq(0)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context "with a single element float left without a height" do
|
229
|
+
let(:style){ {width:10, height: 10} }
|
230
|
+
let(:children){ [float_a] }
|
231
|
+
let(:float_a){ Box.new(style: {display: :float, float: :left, width: 1}) }
|
232
|
+
|
233
|
+
it "doesn't include the element in the render render_tree" do
|
234
|
+
expect(render_tree.children.length).to eq(0)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "a float left element without a height that has a block child" do
|
239
|
+
let(:style){ {width:10, height: 10} }
|
240
|
+
let(:children){ [float_a] }
|
241
|
+
let(:float_a){ Box.new(style: {display: :float, float: :left, width: 1}, children: [block_b]) }
|
242
|
+
let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
|
243
|
+
|
244
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
245
|
+
|
246
|
+
it "gets its height from its children" do
|
247
|
+
expect(rendered_element_a.height).to eq(1)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context "a float left element without a height that has multiple block children" do
|
252
|
+
let(:style){ {width:10, height: 10} }
|
253
|
+
let(:children){ [float_a] }
|
254
|
+
let(:float_a){ Box.new(style: {display: :float, float: :left, width: 10}, children: [block_b, block_c]) }
|
255
|
+
let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
|
256
|
+
let(:block_c){ Box.new(style: {width: 5, height: 4, display: :block}) }
|
257
|
+
|
258
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
259
|
+
|
260
|
+
it "gets its height from its children" do
|
261
|
+
expect(rendered_element_a.height).to eq(5)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
context "a float left element without a height that has inline children" do
|
266
|
+
let(:style){ {width:10, height: 10} }
|
267
|
+
let(:children){ [float_a] }
|
268
|
+
let(:float_a){ Box.new(style: {display: :float, float: :left, width: 5}, children: [inline_b]) }
|
269
|
+
let(:inline_b){ Box.new(content: "ABCDEFGHIJK", style: {display: :inline}) }
|
270
|
+
|
271
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
272
|
+
|
273
|
+
it "gets its height from its children" do
|
274
|
+
# Line 1: ABCDE, Line 2: FGHIJ, Line 3: K
|
275
|
+
expect(rendered_element_a.height).to eq(3)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
context "with a single element float left with a width" do
|
280
|
+
let(:style){ {width:10, height: 10} }
|
281
|
+
let(:children){ [float_a] }
|
282
|
+
let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :left}) }
|
283
|
+
|
284
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
285
|
+
|
286
|
+
it "includes the element at the left-most position for the current row" do
|
287
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
288
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context "with a float left followed by a block element" do
|
293
|
+
let(:style){ {width:10, height: 10} }
|
294
|
+
let(:children){ [float_a, block_b] }
|
295
|
+
let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :left}) }
|
296
|
+
let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
|
297
|
+
|
298
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
299
|
+
let(:rendered_element_b){ first_rendered(block_b) }
|
300
|
+
|
301
|
+
context "and they both fit on the same line" do
|
302
|
+
it "puts the float left element before the block element" do
|
303
|
+
# float
|
304
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
305
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
306
|
+
|
307
|
+
# block
|
308
|
+
expect(rendered_element_b.position).to eq(Position.new(5, 0))
|
309
|
+
expect(rendered_element_b.size).to eq(Dimension.new(5, 1))
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
context "and they do not fit on the same line" do
|
314
|
+
before do
|
315
|
+
block_b.width = 6
|
316
|
+
end
|
317
|
+
|
318
|
+
it "puts the block element on the next line when they both dont fit" do
|
319
|
+
# float
|
320
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
321
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
322
|
+
|
323
|
+
# block
|
324
|
+
expect(rendered_element_b.position).to eq(Position.new(0, 1))
|
325
|
+
expect(rendered_element_b.size).to eq(Dimension.new(6, 1))
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
context "with a float left followed by an block" do
|
331
|
+
let(:style){ {width:10, height: 10} }
|
332
|
+
let(:children){ [float_a, inline_b] }
|
333
|
+
let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :left}) }
|
334
|
+
let(:inline_b){ Box.new(content: "ABCD", style: {height: 1, display: :inline}) }
|
335
|
+
|
336
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
337
|
+
let(:rendered_element_b){ first_rendered(inline_b) }
|
338
|
+
|
339
|
+
context "and they both fit on the same line" do
|
340
|
+
it "puts it before inline elements on the same line" do
|
341
|
+
# float
|
342
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
343
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
344
|
+
|
345
|
+
# inline
|
346
|
+
expect(rendered_element_b.position).to eq(Position.new(5, 0))
|
347
|
+
expect(rendered_element_b.size).to eq(Dimension.new(4, 1))
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context "and they do not fit on the same line" do
|
352
|
+
before do
|
353
|
+
inline_b.content = "ABCDEFGHIJ"
|
354
|
+
end
|
355
|
+
|
356
|
+
let(:rendered_elements_b){ all_rendered(inline_b) }
|
357
|
+
|
358
|
+
it "wraps the text onto the next line" do
|
359
|
+
# float
|
360
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
361
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
362
|
+
|
363
|
+
# inline
|
364
|
+
expect(rendered_elements_b[0].position).to eq(Position.new(5, 0))
|
365
|
+
expect(rendered_elements_b[0].size).to eq(Dimension.new(5, 1))
|
366
|
+
|
367
|
+
expect(rendered_elements_b[1].position).to eq(Position.new(0, 1))
|
368
|
+
expect(rendered_elements_b[1].size).to eq(Dimension.new(5, 1))
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
context "with multiple elements floated left" do
|
374
|
+
let(:style){ {width:10, height: 10} }
|
375
|
+
let(:children){ [float_a, float_b, float_c, float_d] }
|
376
|
+
let(:float_a){ Box.new(style: {width: 3, height: 1, display: :float, float: :left}) }
|
377
|
+
let(:float_b){ Box.new(style: {width: 3, height: 1, display: :float, float: :left}) }
|
378
|
+
let(:float_c){ Box.new(style: {width: 3, height: 1, display: :float, float: :left}) }
|
379
|
+
let(:float_d){ Box.new(style: {width: 3, height: 1, display: :float, float: :left}) }
|
380
|
+
|
381
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
382
|
+
let(:rendered_element_b){ first_rendered(float_b) }
|
383
|
+
let(:rendered_element_c){ first_rendered(float_c) }
|
384
|
+
let(:rendered_element_d){ first_rendered(float_d) }
|
385
|
+
|
386
|
+
it "places them horizontally next to each other" do
|
387
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
388
|
+
expect(rendered_element_b.position).to eq(Position.new(3, 0))
|
389
|
+
expect(rendered_element_c.position).to eq(Position.new(6, 0))
|
390
|
+
end
|
391
|
+
|
392
|
+
it "wraps floats that don't fit on the current line to the next line" do
|
393
|
+
expect(rendered_element_d.position).to eq(Position.new(0, 1))
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
context "with a float left element that is taller than a single line" do
|
398
|
+
let(:style){ {width:10, height: 10} }
|
399
|
+
let(:children){ [float_a, block_b, block_c] }
|
400
|
+
let(:float_a){ Box.new(style: {width: 3, height: 5, display: :float, float: :left}) }
|
401
|
+
let(:block_b){ Box.new(style: {width: 3, height: 2, display: :block}) }
|
402
|
+
let(:block_c){ Box.new(style: {width: 3, height: 2, display: :block}) }
|
403
|
+
|
404
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
405
|
+
let(:rendered_element_b){ first_rendered(block_b) }
|
406
|
+
let(:rendered_element_c){ first_rendered(block_c) }
|
407
|
+
|
408
|
+
it "spans multiple lines" do
|
409
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
410
|
+
expect(rendered_element_a.size).to eq(Dimension.new(3, 5))
|
411
|
+
end
|
412
|
+
|
413
|
+
it "positions block elements next to the float on all lines" do
|
414
|
+
expect(rendered_element_b.position).to eq(Position.new(3, 0))
|
415
|
+
expect(rendered_element_b.size).to eq(Dimension.new(3, 2))
|
416
|
+
|
417
|
+
expect(rendered_element_c.position).to eq(Position.new(3, 2))
|
418
|
+
expect(rendered_element_c.size).to eq(Dimension.new(3, 2))
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context "nested floats" do
|
423
|
+
let(:style){ {width:10, height: 10} }
|
424
|
+
let(:children){ [float_a] }
|
425
|
+
let(:float_a){ Box.new(style: {width: 3, height: 5, display: :float, float: :left}, children: [
|
426
|
+
float_b
|
427
|
+
])}
|
428
|
+
let(:float_b){ Box.new(style: {width: 3, height: 5, display: :float, float: :left}, children: [
|
429
|
+
float_c
|
430
|
+
])}
|
431
|
+
let(:float_c){ Box.new(style: {width: 3, height: 5, display: :float, float: :left}, children: [
|
432
|
+
float_d,
|
433
|
+
block_e
|
434
|
+
])}
|
435
|
+
let(:float_d){ Box.new(style: {width: 2, height: 5, display: :float, float: :left}) }
|
436
|
+
let(:block_e){ Box.new(style: {width: 1, height: 5, display: :block}) }
|
437
|
+
|
438
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
439
|
+
let(:rendered_element_b){ first_rendered(float_b, parent: rendered_element_a) }
|
440
|
+
let(:rendered_element_c){ first_rendered(float_c, parent: rendered_element_b) }
|
441
|
+
let(:rendered_element_d){ first_rendered(float_d, parent: rendered_element_c) }
|
442
|
+
let(:rendered_element_e){ first_rendered(block_e, parent: rendered_element_c) }
|
443
|
+
|
444
|
+
it "nests properly by aligning left elements to the left-most position" do
|
445
|
+
expect(rendered_element_a.position).to eq(Position.new(0, 0))
|
446
|
+
expect(rendered_element_a.size).to eq(Dimension.new(3, 5))
|
447
|
+
|
448
|
+
expect(rendered_element_b.position).to eq(Position.new(0, 0))
|
449
|
+
expect(rendered_element_b.size).to eq(Dimension.new(3, 5))
|
450
|
+
|
451
|
+
expect(rendered_element_c.position).to eq(Position.new(0, 0))
|
452
|
+
expect(rendered_element_c.size).to eq(Dimension.new(3, 5))
|
453
|
+
end
|
454
|
+
|
455
|
+
it "still floats and positions elements correctly when nested" do
|
456
|
+
# float
|
457
|
+
expect(rendered_element_d.position).to eq(Position.new(0, 0))
|
458
|
+
expect(rendered_element_d.size).to eq(Dimension.new(2, 5))
|
459
|
+
|
460
|
+
# block gets placed beside the float
|
461
|
+
expect(rendered_element_e.position).to eq(Position.new(2, 0))
|
462
|
+
expect(rendered_element_e.size).to eq(Dimension.new(1, 5))
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
describe "normal flow - float right" do
|
468
|
+
context "with a single element float right without a width" do
|
469
|
+
let(:style){ {width:10, height: 10} }
|
470
|
+
let(:children){ [float_a] }
|
471
|
+
let(:float_a){ Box.new(style: {display: :float, float: :right}) }
|
472
|
+
|
473
|
+
it "doesn't include the element in the render render_tree" do
|
474
|
+
expect(render_tree.children.length).to eq(0)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
context "with a single element float right without a height" do
|
479
|
+
let(:style){ {width:10, height: 10} }
|
480
|
+
let(:children){ [float_a] }
|
481
|
+
let(:float_a){ Box.new(style: {display: :float, float: :right, width: 1}) }
|
482
|
+
|
483
|
+
it "doesn't include the element in the render render_tree" do
|
484
|
+
expect(render_tree.children.length).to eq(0)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
context "with a single element float right with a width" do
|
489
|
+
let(:style){ {width:10, height: 10} }
|
490
|
+
let(:children){ [float_a] }
|
491
|
+
let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :right}) }
|
492
|
+
|
493
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
494
|
+
|
495
|
+
it "includes the element at the right-most position minus its width for the current row" do
|
496
|
+
expect(rendered_element_a.position).to eq(Position.new(5, 0))
|
497
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
context "a float right element without a height that has a block child" do
|
502
|
+
let(:style){ {width:10, height: 10} }
|
503
|
+
let(:children){ [float_a] }
|
504
|
+
let(:float_a){ Box.new(style: {display: :float, float: :right, width: 1}, children: [block_b]) }
|
505
|
+
let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
|
506
|
+
|
507
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
508
|
+
|
509
|
+
it "gets its height from its children" do
|
510
|
+
expect(rendered_element_a.height).to eq(1)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
context "a float right element without a height that has multiple block children" do
|
515
|
+
let(:style){ {width:10, height: 10} }
|
516
|
+
let(:children){ [float_a] }
|
517
|
+
let(:float_a){ Box.new(style: {display: :float, float: :right, width: 10}, children: [block_b, block_c]) }
|
518
|
+
let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
|
519
|
+
let(:block_c){ Box.new(style: {width: 5, height: 4, display: :block}) }
|
520
|
+
|
521
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
522
|
+
|
523
|
+
it "gets its height from its children" do
|
524
|
+
expect(rendered_element_a.height).to eq(5)
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
context "a float right element without a height that has inline children" do
|
529
|
+
let(:style){ {width:10, height: 10} }
|
530
|
+
let(:children){ [float_a] }
|
531
|
+
let(:float_a){ Box.new(style: {display: :float, float: :right, width: 5}, children: [inline_b]) }
|
532
|
+
let(:inline_b){ Box.new(content: "ABCDEFGHIJK", style: {display: :inline}) }
|
533
|
+
|
534
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
535
|
+
|
536
|
+
it "gets its height from its children" do
|
537
|
+
# Line 1: ABCDE, Line 2: FGHIJ, Line 3: K
|
538
|
+
expect(rendered_element_a.height).to eq(3)
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
context "with a float right followed by a block element" do
|
543
|
+
let(:style){ {width:10, height: 10} }
|
544
|
+
let(:children){ [float_a, block_b] }
|
545
|
+
let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :right}) }
|
546
|
+
let(:block_b){ Box.new(style: {width: 5, height: 1, display: :block}) }
|
547
|
+
|
548
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
549
|
+
let(:rendered_element_b){ first_rendered(block_b) }
|
550
|
+
|
551
|
+
context "and they both fit on the same line" do
|
552
|
+
it "puts the block element before the float right element" do
|
553
|
+
# float
|
554
|
+
expect(rendered_element_a.position).to eq(Position.new(5, 0))
|
555
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
556
|
+
|
557
|
+
# block
|
558
|
+
expect(rendered_element_b.position).to eq(Position.new(0, 0))
|
559
|
+
expect(rendered_element_b.size).to eq(Dimension.new(5, 1))
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
context "and they do not fit on the same line" do
|
564
|
+
before do
|
565
|
+
block_b.width = 6
|
566
|
+
end
|
567
|
+
|
568
|
+
it "puts the block element on the next line" do
|
569
|
+
# float
|
570
|
+
expect(rendered_element_a.position).to eq(Position.new(5, 0))
|
571
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
572
|
+
|
573
|
+
# block
|
574
|
+
expect(rendered_element_b.position).to eq(Position.new(0, 1))
|
575
|
+
expect(rendered_element_b.size).to eq(Dimension.new(6, 1))
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
context "with a float right followed by an inline element" do
|
581
|
+
let(:style){ {width:10, height: 10} }
|
582
|
+
let(:children){ [float_a, inline_b] }
|
583
|
+
let(:float_a){ Box.new(style: {width: 5, height: 1, display: :float, float: :right}) }
|
584
|
+
let(:inline_b){ Box.new(content: "ABCD", style: {height: 1, display: :inline}) }
|
585
|
+
|
586
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
587
|
+
let(:rendered_element_b){ first_rendered(inline_b) }
|
588
|
+
|
589
|
+
context "and they both fit on the same line" do
|
590
|
+
it "puts it after the inline elements on the same line" do
|
591
|
+
# float
|
592
|
+
expect(rendered_element_a.position).to eq(Position.new(5, 0))
|
593
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
594
|
+
|
595
|
+
# inline
|
596
|
+
expect(rendered_element_b.position).to eq(Position.new(0, 0))
|
597
|
+
expect(rendered_element_b.size).to eq(Dimension.new(4, 1))
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
context "and they do not fit on the same line" do
|
602
|
+
before do
|
603
|
+
inline_b.content = "ABCDEFGHIJ"
|
604
|
+
end
|
605
|
+
|
606
|
+
let(:rendered_elements_b){ all_rendered(inline_b) }
|
607
|
+
|
608
|
+
it "puts wraps the text onto the next line" do
|
609
|
+
# float
|
610
|
+
expect(rendered_element_a.position).to eq(Position.new(5, 0))
|
611
|
+
expect(rendered_element_a.size).to eq(Dimension.new(5, 1))
|
612
|
+
|
613
|
+
# inline
|
614
|
+
expect(rendered_elements_b[0].position).to eq(Position.new(0, 0))
|
615
|
+
expect(rendered_elements_b[0].size).to eq(Dimension.new(5, 1))
|
616
|
+
|
617
|
+
expect(rendered_elements_b[1].position).to eq(Position.new(0, 1))
|
618
|
+
expect(rendered_elements_b[1].size).to eq(Dimension.new(5, 1))
|
619
|
+
end
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
context "with multiple elements floated left" do
|
624
|
+
let(:style){ {width:10, height: 10} }
|
625
|
+
let(:children){ [float_a, float_b, float_c, float_d] }
|
626
|
+
let(:float_a){ Box.new(style: {width: 3, height: 1, display: :float, float: :right}) }
|
627
|
+
let(:float_b){ Box.new(style: {width: 3, height: 1, display: :float, float: :right}) }
|
628
|
+
let(:float_c){ Box.new(style: {width: 3, height: 1, display: :float, float: :right}) }
|
629
|
+
let(:float_d){ Box.new(style: {width: 3, height: 1, display: :float, float: :right}) }
|
630
|
+
|
631
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
632
|
+
let(:rendered_element_b){ first_rendered(float_b) }
|
633
|
+
let(:rendered_element_c){ first_rendered(float_c) }
|
634
|
+
let(:rendered_element_d){ first_rendered(float_d) }
|
635
|
+
|
636
|
+
it "places them horizontally next to each other" do
|
637
|
+
expect(rendered_element_a.position).to eq(Position.new(7, 0))
|
638
|
+
expect(rendered_element_b.position).to eq(Position.new(4, 0))
|
639
|
+
expect(rendered_element_c.position).to eq(Position.new(1, 0))
|
640
|
+
end
|
641
|
+
|
642
|
+
it "wraps floats that don't fit on the current line to the next line" do
|
643
|
+
expect(rendered_element_d.position).to eq(Position.new(7, 1))
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
context "with a float right element that is taller than a single line" do
|
648
|
+
let(:style){ {width:10, height: 10} }
|
649
|
+
let(:children){ [float_a, block_b, block_c] }
|
650
|
+
let(:float_a){ Box.new(style: {width: 3, height: 5, display: :float, float: :right}) }
|
651
|
+
let(:block_b){ Box.new(style: {width: 3, height: 2, display: :block}) }
|
652
|
+
let(:block_c){ Box.new(style: {width: 3, height: 2, display: :block}) }
|
653
|
+
|
654
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
655
|
+
let(:rendered_element_b){ first_rendered(block_b) }
|
656
|
+
let(:rendered_element_c){ first_rendered(block_c) }
|
657
|
+
|
658
|
+
it "spans multiple lines" do
|
659
|
+
expect(rendered_element_a.position).to eq(Position.new(7, 0))
|
660
|
+
expect(rendered_element_a.size).to eq(Dimension.new(3, 5))
|
661
|
+
end
|
662
|
+
|
663
|
+
it "positions block elements next to the float on all lines" do
|
664
|
+
expect(rendered_element_b.position).to eq(Position.new(0, 0))
|
665
|
+
expect(rendered_element_b.size).to eq(Dimension.new(3, 2))
|
666
|
+
|
667
|
+
expect(rendered_element_c.position).to eq(Position.new(0, 2))
|
668
|
+
expect(rendered_element_c.size).to eq(Dimension.new(3, 2))
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
context "nested floats" do
|
673
|
+
let(:style){ {width:10, height: 10} }
|
674
|
+
let(:children){ [float_a] }
|
675
|
+
let(:float_a){ Box.new(style: {width: 7, height: 5, display: :float, float: :right}, children: [
|
676
|
+
float_b
|
677
|
+
])}
|
678
|
+
let(:float_b){ Box.new(style: {width: 6, height: 5, display: :float, float: :right}, children: [
|
679
|
+
float_c
|
680
|
+
])}
|
681
|
+
let(:float_c){ Box.new(style: {width: 5, height: 5, display: :float, float: :right}, children: [
|
682
|
+
float_d,
|
683
|
+
block_e
|
684
|
+
])}
|
685
|
+
let(:float_d){ Box.new(style: {width: 2, height: 5, display: :float, float: :right}) }
|
686
|
+
let(:block_e){ Box.new(style: {width: 1, height: 5, display: :block}) }
|
687
|
+
|
688
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
689
|
+
let(:rendered_element_b){ first_rendered(float_b, parent: rendered_element_a) }
|
690
|
+
let(:rendered_element_c){ first_rendered(float_c, parent: rendered_element_b) }
|
691
|
+
let(:rendered_element_d){ first_rendered(float_d, parent: rendered_element_c) }
|
692
|
+
let(:rendered_element_e){ first_rendered(block_e, parent: rendered_element_c) }
|
693
|
+
|
694
|
+
it "nests properly by aligning right elements to the right-most position" do
|
695
|
+
expect(rendered_element_a.position).to eq(Position.new(3, 0))
|
696
|
+
expect(rendered_element_a.size).to eq(Dimension.new(7, 5))
|
697
|
+
|
698
|
+
expect(rendered_element_b.position).to eq(Position.new(1, 0))
|
699
|
+
expect(rendered_element_b.size).to eq(Dimension.new(6, 5))
|
700
|
+
|
701
|
+
expect(rendered_element_c.position).to eq(Position.new(1, 0))
|
702
|
+
expect(rendered_element_c.size).to eq(Dimension.new(5, 5))
|
703
|
+
end
|
704
|
+
|
705
|
+
it "still floats and positions elements correctly when nested" do
|
706
|
+
# float
|
707
|
+
expect(rendered_element_d.position).to eq(Position.new(3, 0))
|
708
|
+
expect(rendered_element_d.size).to eq(Dimension.new(2, 5))
|
709
|
+
|
710
|
+
# block gets placed beside the float
|
711
|
+
expect(rendered_element_e.position).to eq(Position.new(0, 0))
|
712
|
+
expect(rendered_element_e.size).to eq(Dimension.new(1, 5))
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
describe "offsets" do
|
718
|
+
let(:style){ {width:10, height: 10} }
|
719
|
+
let(:children){ [float_a, float_b, block_c, inline_d, inline_e, block_f] }
|
720
|
+
let(:float_a){ Box.new(content: "AAA", style: {width: 3, height: 1, display: :float, float: :left}) }
|
721
|
+
let(:float_b){ Box.new(content: "BBB", style: {width: 3, height: 1, display: :float, float: :right}) }
|
722
|
+
let(:block_c){ Box.new(content: "CCCCCCCCCCCC", style: {width: 6, height: 2, display: :block}) }
|
723
|
+
let(:inline_d){ Box.new(content: "ABCD", style: {display: :inline}) }
|
724
|
+
let(:inline_e){ Box.new(content: "DEFG", style: {display: :inline}) }
|
725
|
+
let(:block_f){ Box.new(content: "JJJJJJJJJJJJJJJJJJ", style: {width: 6, height: 3, display: :block}) }
|
726
|
+
|
727
|
+
let(:rendered_element_a){ first_rendered(float_a) }
|
728
|
+
let(:rendered_element_b){ first_rendered(float_b) }
|
729
|
+
let(:rendered_element_c){ first_rendered(block_c) }
|
730
|
+
let(:rendered_element_d){ first_rendered(inline_d) }
|
731
|
+
let(:rendered_element_e){ first_rendered(inline_e) }
|
732
|
+
let(:rendered_element_f){ first_rendered(block_f) }
|
733
|
+
|
734
|
+
it "knows its offset" do
|
735
|
+
expect(rendered_element_a.offset).to eq(Position.new(0, 0))
|
736
|
+
expect(rendered_element_b.offset).to eq(Position.new(7, 0))
|
737
|
+
expect(rendered_element_c.offset).to eq(Position.new(0, 1))
|
738
|
+
expect(rendered_element_d.offset).to eq(Position.new(0, 3))
|
739
|
+
expect(rendered_element_e.offset).to eq(Position.new(4, 3))
|
740
|
+
expect(rendered_element_f.offset).to eq(Position.new(0, 4))
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
end
|
745
|
+
end
|