unpoly-rails 0.60.3 → 1.0.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.
Potentially problematic release.
This version of unpoly-rails might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.ruby-version +1 -2
- data/CHANGELOG.md +84 -0
- data/README.md +1 -1
- data/Rakefile +11 -1
- data/dist/unpoly.js +226 -91
- data/dist/unpoly.min.js +4 -4
- data/lib/assets/javascripts/unpoly/browser.coffee.erb +10 -5
- data/lib/assets/javascripts/unpoly/classes/body_shifter.coffee +21 -12
- data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +2 -2
- data/lib/assets/javascripts/unpoly/classes/event_listener.coffee +1 -1
- data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +11 -3
- data/lib/assets/javascripts/unpoly/classes/params.coffee.erb +1 -0
- data/lib/assets/javascripts/unpoly/element.coffee.erb +9 -6
- data/lib/assets/javascripts/unpoly/event.coffee.erb +12 -4
- data/lib/assets/javascripts/unpoly/form.coffee.erb +85 -14
- data/lib/assets/javascripts/unpoly/fragment.coffee.erb +7 -3
- data/lib/assets/javascripts/unpoly/framework.coffee +7 -9
- data/lib/assets/javascripts/unpoly/link.coffee.erb +1 -1
- data/lib/assets/javascripts/unpoly/log.coffee +6 -2
- data/lib/assets/javascripts/unpoly/modal.coffee.erb +4 -2
- data/lib/assets/javascripts/unpoly/motion.coffee.erb +1 -1
- data/lib/assets/javascripts/unpoly/protocol.coffee +1 -1
- data/lib/assets/javascripts/unpoly/syntax.coffee.erb +3 -4
- data/lib/assets/javascripts/unpoly/util.coffee.erb +4 -2
- data/lib/assets/javascripts/unpoly/viewport.coffee.erb +26 -2
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -1
- data/spec_app/Gemfile +2 -4
- data/spec_app/Gemfile.lock +23 -27
- data/spec_app/app/views/compiler_test/timestamp.erb +1 -0
- data/spec_app/app/views/css_test/modal.erb +1 -1
- data/spec_app/app/views/css_test/popup.erb +1 -1
- data/spec_app/app/views/pages/start.erb +2 -1
- data/spec_app/app/views/replace_test/table.erb +16 -0
- data/spec_app/spec/javascripts/up/event_spec.js.coffee +34 -0
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +128 -0
- data/spec_app/spec/javascripts/up/fragment_spec.js.coffee +36 -1
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +7 -2
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +23 -1
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +2 -1
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +28 -0
- metadata +4 -4
@@ -6,6 +6,47 @@ describe 'up.form', ->
|
|
6
6
|
|
7
7
|
describe 'JavaScript functions', ->
|
8
8
|
|
9
|
+
describe 'up.form.fields', ->
|
10
|
+
|
11
|
+
it 'returns a list of form fields within the given element', ->
|
12
|
+
form = fixture('form')
|
13
|
+
textField = e.affix(form, 'input[type=text]')
|
14
|
+
select = e.affix(form, 'select')
|
15
|
+
results = up.form.fields(form)
|
16
|
+
expect(results).toMatchList([textField, select])
|
17
|
+
|
18
|
+
it 'returns an empty list if the given element contains no form fields', ->
|
19
|
+
form = fixture('form')
|
20
|
+
results = up.form.fields(form)
|
21
|
+
expect(results).toMatchList([])
|
22
|
+
|
23
|
+
it 'returns a list of the given element if the element is itself a form field', ->
|
24
|
+
textArea = fixture('textarea')
|
25
|
+
results = up.form.fields(textArea)
|
26
|
+
expect(results).toMatchList([textArea])
|
27
|
+
|
28
|
+
it 'ignores fields outside the given form', ->
|
29
|
+
form1 = fixture('form')
|
30
|
+
form1Field = e.affix(form1, 'input[type=text]')
|
31
|
+
form2 = fixture('form')
|
32
|
+
form2Field = e.affix(form2, 'input[type=text]')
|
33
|
+
results = up.form.fields(form1)
|
34
|
+
expect(results).toMatchList([form1Field])
|
35
|
+
|
36
|
+
it "includes fields outside the form with a [form] attribute matching the given form's ID", ->
|
37
|
+
form = fixture('form#form-id')
|
38
|
+
insideField = e.affix(form, 'input[type=text]')
|
39
|
+
outsideField = fixture('input[type=text][form=form-id]')
|
40
|
+
results = up.form.fields(form)
|
41
|
+
expect(results).toMatchList([insideField, outsideField])
|
42
|
+
|
43
|
+
it "does not return duplicate fields if a field with a matching [form] attribute is also a child of the form", ->
|
44
|
+
form = fixture('form#form-id')
|
45
|
+
field = e.affix(form, 'input[type=text][form=form-id]')
|
46
|
+
results = up.form.fields(form)
|
47
|
+
expect(results).toMatchList([field])
|
48
|
+
|
49
|
+
|
9
50
|
describe 'up.observe', ->
|
10
51
|
|
11
52
|
beforeEach ->
|
@@ -858,6 +899,23 @@ describe 'up.form', ->
|
|
858
899
|
Trigger.change($field)
|
859
900
|
next => expect(submitSpy).toHaveBeenCalled()
|
860
901
|
|
902
|
+
it 'submits the form when a change is observed on a container for a radio button group', asyncSpec (next) ->
|
903
|
+
form = fixture('form')
|
904
|
+
group = e.affix(form, '.group[up-autosubmit][up-delay=0]')
|
905
|
+
radio1 = e.affix(group, 'input[type=radio][name=foo][value=1]')
|
906
|
+
radio2 = e.affix(group, 'input[type=radio][name=foo][value=2]')
|
907
|
+
up.hello(form)
|
908
|
+
submitSpy = up.form.knife.mock('submit').and.returnValue(Promise.reject())
|
909
|
+
Trigger.clickSequence(radio1)
|
910
|
+
next =>
|
911
|
+
expect(submitSpy.calls.count()).toBe(1)
|
912
|
+
Trigger.clickSequence(radio2)
|
913
|
+
next =>
|
914
|
+
expect(submitSpy.calls.count()).toBe(2)
|
915
|
+
Trigger.clickSequence(radio1)
|
916
|
+
next =>
|
917
|
+
expect(submitSpy.calls.count()).toBe(3)
|
918
|
+
|
861
919
|
describe 'form[up-autosubmit]', ->
|
862
920
|
|
863
921
|
it 'submits the form when a change is observed in any of its fields', asyncSpec (next) ->
|
@@ -1023,6 +1081,7 @@ describe 'up.form', ->
|
|
1023
1081
|
|
1024
1082
|
</form>
|
1025
1083
|
"""
|
1084
|
+
up.hello(container)
|
1026
1085
|
|
1027
1086
|
Trigger.change $('#registration input[name=password]')
|
1028
1087
|
|
@@ -1048,6 +1107,75 @@ describe 'up.form', ->
|
|
1048
1107
|
expect($labels[0]).not.toHaveText('Validation message')
|
1049
1108
|
expect($labels[1]).toHaveText('Validation message')
|
1050
1109
|
|
1110
|
+
describe 'form[up-validate]', ->
|
1111
|
+
|
1112
|
+
# it 'prints an error saying that this form is not yet supported', ->
|
1113
|
+
|
1114
|
+
it 'performs server-side validation for all fieldsets contained within the form', asyncSpec (next) ->
|
1115
|
+
container = fixture('.container')
|
1116
|
+
container.innerHTML = """
|
1117
|
+
<form action="/users" id="registration" up-validate>
|
1118
|
+
|
1119
|
+
<div up-fieldset>
|
1120
|
+
<input type="text" name="email">
|
1121
|
+
</div>
|
1122
|
+
|
1123
|
+
<div up-fieldset>
|
1124
|
+
<input type="password" name="password">
|
1125
|
+
</div>
|
1126
|
+
|
1127
|
+
</form>
|
1128
|
+
"""
|
1129
|
+
up.hello(container)
|
1130
|
+
|
1131
|
+
Trigger.change $('#registration input[name=password]')
|
1132
|
+
|
1133
|
+
next =>
|
1134
|
+
expect(jasmine.Ajax.requests.count()).toEqual(1)
|
1135
|
+
expect(@lastRequest().requestHeaders['X-Up-Validate']).toEqual('password')
|
1136
|
+
expect(@lastRequest().requestHeaders['X-Up-Target']).toEqual('[up-fieldset]:has(input[name="password"])')
|
1137
|
+
|
1138
|
+
|
1139
|
+
@respondWith """
|
1140
|
+
<form action="/users" id="registration" up-validate>
|
1141
|
+
|
1142
|
+
<div up-fieldset>
|
1143
|
+
Validation message
|
1144
|
+
<input type="text" name="email">
|
1145
|
+
</div>
|
1146
|
+
|
1147
|
+
<div up-fieldset>
|
1148
|
+
Validation message
|
1149
|
+
<input type="password" name="password">
|
1150
|
+
</div>
|
1151
|
+
|
1152
|
+
</form>
|
1153
|
+
"""
|
1154
|
+
|
1155
|
+
next =>
|
1156
|
+
$labels = $('#registration [up-fieldset]')
|
1157
|
+
expect($labels[0]).not.toHaveText('Validation message')
|
1158
|
+
expect($labels[1]).toHaveText('Validation message')
|
1159
|
+
|
1160
|
+
it 'only sends a single request when a radio button group changes', asyncSpec (next) ->
|
1161
|
+
container = fixture('.container')
|
1162
|
+
container.innerHTML = """
|
1163
|
+
<form action="/users" id="registration" up-validate>
|
1164
|
+
|
1165
|
+
<div up-fieldset>
|
1166
|
+
<input type="radio" name="foo" value="1" checked>
|
1167
|
+
<input type="radio" name="foo" value="2">
|
1168
|
+
</div>
|
1169
|
+
|
1170
|
+
</form>
|
1171
|
+
"""
|
1172
|
+
up.hello(container)
|
1173
|
+
|
1174
|
+
Trigger.change $('#registration input[value="2"]')
|
1175
|
+
|
1176
|
+
next =>
|
1177
|
+
expect(jasmine.Ajax.requests.count()).toEqual(1)
|
1178
|
+
|
1051
1179
|
describe '[up-switch]', ->
|
1052
1180
|
|
1053
1181
|
describe 'on a select', ->
|
@@ -2432,13 +2432,26 @@ describe 'up.fragment', ->
|
|
2432
2432
|
expect($element).toBeDetached()
|
2433
2433
|
|
2434
2434
|
it 'calls destructors for custom elements', (done) ->
|
2435
|
-
up.$compiler('.element', ($element) -> destructor)
|
2436
2435
|
destructor = jasmine.createSpy('destructor')
|
2436
|
+
up.$compiler('.element', ($element) -> destructor)
|
2437
2437
|
up.hello(fixture('.element'))
|
2438
2438
|
up.destroy('.element').then ->
|
2439
2439
|
expect(destructor).toHaveBeenCalled()
|
2440
2440
|
done()
|
2441
2441
|
|
2442
|
+
it 'does not call destructors twice if up.destroy() is called twice on the same fragment', asyncSpec (next) ->
|
2443
|
+
destructor = jasmine.createSpy('destructor')
|
2444
|
+
up.compiler('.element', (element) -> destructor)
|
2445
|
+
|
2446
|
+
element = fixture('.element')
|
2447
|
+
up.hello(element)
|
2448
|
+
|
2449
|
+
up.destroy(element, animation: 'fade-out', duration: 10)
|
2450
|
+
up.destroy(element, animation: 'fade-out', duration: 10)
|
2451
|
+
|
2452
|
+
next.after 60, ->
|
2453
|
+
expect(destructor.calls.count()).toBe(1)
|
2454
|
+
|
2442
2455
|
it 'marks the old element as .up-destroying before destructors', (done) ->
|
2443
2456
|
destructor = jasmine.createSpy('destructor')
|
2444
2457
|
up.$compiler '.container', ($element) ->
|
@@ -2452,6 +2465,19 @@ describe 'up.fragment', ->
|
|
2452
2465
|
expect(destructor).toHaveBeenCalledWith('old text', true)
|
2453
2466
|
done()
|
2454
2467
|
|
2468
|
+
it 'marks the old element as [aria-hidden=true] before destructors', (done) ->
|
2469
|
+
destructor = jasmine.createSpy('destructor')
|
2470
|
+
up.$compiler '.container', ($element) ->
|
2471
|
+
-> destructor($element.text(), $element.is('[aria-hidden=true]'))
|
2472
|
+
$container = $fixture('.container').text('old text')
|
2473
|
+
up.hello($container)
|
2474
|
+
|
2475
|
+
destroyDone = up.destroy('.container')
|
2476
|
+
|
2477
|
+
destroyDone.then ->
|
2478
|
+
expect(destructor).toHaveBeenCalledWith('old text', true)
|
2479
|
+
done()
|
2480
|
+
|
2455
2481
|
it 'marks the old element as .up-destroying before destructors after an { animation }', (done) ->
|
2456
2482
|
destructor = jasmine.createSpy('destructor')
|
2457
2483
|
up.$compiler '.container', ($element) ->
|
@@ -2533,6 +2559,15 @@ describe 'up.fragment', ->
|
|
2533
2559
|
)
|
2534
2560
|
expect($element).toBeDetached()
|
2535
2561
|
|
2562
|
+
it 'removes element-related data from the global jQuery cache (bugfix)', asyncSpec (next) ->
|
2563
|
+
$element = $fixture('.element')
|
2564
|
+
$element.data('foo', { foo: '1' })
|
2565
|
+
expect($element.data('foo')).toEqual({ foo: '1'})
|
2566
|
+
up.destroy($element)
|
2567
|
+
|
2568
|
+
next ->
|
2569
|
+
expect($element.data('foo')).toBeMissing()
|
2570
|
+
|
2536
2571
|
describe 'up.reload', ->
|
2537
2572
|
|
2538
2573
|
describeCapability 'canPushState', ->
|
@@ -928,9 +928,14 @@ describe 'up.link', ->
|
|
928
928
|
Trigger.mouseup(@$link)
|
929
929
|
next => expect(@followSpy).not.toHaveBeenCalled()
|
930
930
|
|
931
|
-
it 'does nothing on click', asyncSpec (next)->
|
931
|
+
it 'does nothing on click if there was an earlier mousedown event', asyncSpec (next)->
|
932
|
+
Trigger.mousedown(@$link)
|
932
933
|
Trigger.click(@$link)
|
933
|
-
next => expect(@followSpy
|
934
|
+
next => expect(@followSpy.calls.count()).toBe(1)
|
935
|
+
|
936
|
+
it 'does follow a link on click if there was never a mousedown event (e.g. if the user pressed enter)', asyncSpec (next) ->
|
937
|
+
Trigger.click(@$link)
|
938
|
+
next => expect(@followSpy.calls.mostRecent().args[0]).toEqual(@$link[0])
|
934
939
|
|
935
940
|
# IE does not call JavaScript and always performs the default action on right clicks
|
936
941
|
unless AgentDetector.isIE() || AgentDetector.isEdge()
|
@@ -532,8 +532,9 @@ describe 'up.modal', ->
|
|
532
532
|
next => expect(@followSpy).not.toHaveBeenCalled()
|
533
533
|
|
534
534
|
it 'does nothing on click', asyncSpec (next) ->
|
535
|
+
Trigger.mousedown(@$link)
|
535
536
|
Trigger.click(@$link)
|
536
|
-
next => expect(@followSpy
|
537
|
+
next => expect(@followSpy.calls.count()).toBe(1)
|
537
538
|
|
538
539
|
# IE does not call JavaScript and always performs the default action on right clicks
|
539
540
|
unless AgentDetector.isIE() || AgentDetector.isEdge()
|
@@ -562,6 +563,27 @@ describe 'up.modal', ->
|
|
562
563
|
next =>
|
563
564
|
expect(@lastRequest().method).toEqual 'POST'
|
564
565
|
|
566
|
+
describe 'with [up-cache] modifier', ->
|
567
|
+
it 'honours the given setting', asyncSpec (next) ->
|
568
|
+
$link = $fixture('a[href="/path"][up-modal=".target"][up-cache="false"]')
|
569
|
+
Trigger.clickSequence($link)
|
570
|
+
|
571
|
+
next =>
|
572
|
+
@respondWith('<div class="target">modal content 1</div>')
|
573
|
+
|
574
|
+
next =>
|
575
|
+
expect('.up-modal .target').toHaveText('modal content 1')
|
576
|
+
history.back()
|
577
|
+
|
578
|
+
next =>
|
579
|
+
Trigger.clickSequence($link)
|
580
|
+
|
581
|
+
next =>
|
582
|
+
@respondWith('<div class="target">modal content 2</div>')
|
583
|
+
|
584
|
+
next =>
|
585
|
+
expect('.up-modal .target').toHaveText('modal content 2')
|
586
|
+
|
565
587
|
it 'adds a history entry and allows the user to use the back button', asyncSpec (next) ->
|
566
588
|
up.motion.config.enabled = false
|
567
589
|
up.history.config.enabled = true
|
@@ -232,8 +232,9 @@ describe 'up.popup', ->
|
|
232
232
|
next => expect(@attachSpy).not.toHaveBeenCalled()
|
233
233
|
|
234
234
|
it 'does nothing on click', asyncSpec (next) ->
|
235
|
+
Trigger.mousedown(@$link)
|
235
236
|
Trigger.click(@$link)
|
236
|
-
next => expect(@attachSpy
|
237
|
+
next => expect(@attachSpy.calls.count()).toBe(1)
|
237
238
|
|
238
239
|
# IE does not call JavaScript and always performs the default action on right clicks
|
239
240
|
unless AgentDetector.isIE() || AgentDetector.isEdge()
|
@@ -1404,3 +1404,31 @@ describe 'up.util', ->
|
|
1404
1404
|
it 'returns false for NaN', ->
|
1405
1405
|
value = NaN
|
1406
1406
|
expect(up.util.isList(value)).toBe(false)
|
1407
|
+
|
1408
|
+
describe 'up.util.isJQuery', ->
|
1409
|
+
|
1410
|
+
it 'returns true for a jQuery collection', ->
|
1411
|
+
value = $('body')
|
1412
|
+
expect(up.util.isJQuery(value)).toBe(true)
|
1413
|
+
|
1414
|
+
it 'returns false for a native element', ->
|
1415
|
+
value = document.body
|
1416
|
+
expect(up.util.isJQuery(value)).toBe(false)
|
1417
|
+
|
1418
|
+
it 'returns false (and does not crash) for undefined', ->
|
1419
|
+
value = undefined
|
1420
|
+
expect(up.util.isJQuery(value)).toBe(false)
|
1421
|
+
|
1422
|
+
describe 'up.util.escapeHtml', ->
|
1423
|
+
|
1424
|
+
it 'escapes double quotes', ->
|
1425
|
+
result = up.util.escapeHtml('before"after')
|
1426
|
+
expect(result).toEqual('before"after')
|
1427
|
+
|
1428
|
+
it 'escapes single quotes', ->
|
1429
|
+
result = up.util.escapeHtml("before'after")
|
1430
|
+
expect(result).toEqual('before'after')
|
1431
|
+
|
1432
|
+
it 'escapes angle brackets', ->
|
1433
|
+
result = up.util.escapeHtml('before<script>after')
|
1434
|
+
expect(result).toEqual('before<script>after')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unpoly-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henning Koch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -232,6 +232,7 @@ files:
|
|
232
232
|
- spec_app/app/views/replace_test/_nav.erb
|
233
233
|
- spec_app/app/views/replace_test/page1.erb
|
234
234
|
- spec_app/app/views/replace_test/page2.erb
|
235
|
+
- spec_app/app/views/replace_test/table.erb
|
235
236
|
- spec_app/app/views/reveal_test/long1.erb
|
236
237
|
- spec_app/app/views/reveal_test/long2.erb
|
237
238
|
- spec_app/app/views/reveal_test/within_document_viewport.erb
|
@@ -399,8 +400,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
399
400
|
- !ruby/object:Gem::Version
|
400
401
|
version: '0'
|
401
402
|
requirements: []
|
402
|
-
|
403
|
-
rubygems_version: 2.7.6
|
403
|
+
rubygems_version: 3.2.16
|
404
404
|
signing_key:
|
405
405
|
specification_version: 4
|
406
406
|
summary: Rails bindings for Unpoly, the unobtrusive JavaScript framework
|