unpoly-rails 0.52.0 → 0.53.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/CHANGELOG.md +52 -2
- data/dist/unpoly.js +204 -55
- data/dist/unpoly.min.js +4 -4
- data/lib/assets/javascripts/unpoly/classes/extract_cascade.coffee +20 -2
- data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +3 -9
- data/lib/assets/javascripts/unpoly/dom.coffee +14 -5
- data/lib/assets/javascripts/unpoly/form.coffee +23 -5
- data/lib/assets/javascripts/unpoly/layout.coffee +8 -2
- data/lib/assets/javascripts/unpoly/link.coffee +19 -5
- data/lib/assets/javascripts/unpoly/protocol.coffee +12 -6
- data/lib/assets/javascripts/unpoly/radio.coffee +63 -0
- data/lib/assets/javascripts/unpoly/util.coffee +15 -4
- data/lib/assets/javascripts/unpoly.coffee +1 -0
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/app/controllers/hash_test_controller.rb +5 -0
- data/spec_app/app/views/hash_test/vanilla.erb +13 -0
- data/spec_app/app/views/pages/start.erb +1 -0
- data/spec_app/config/routes.rb +1 -0
- data/spec_app/spec/javascripts/up/dom_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +111 -1
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +122 -4
- data/spec_app/spec/javascripts/up/radio_spec.js.coffee +75 -0
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +30 -5
- metadata +6 -2
@@ -5,7 +5,7 @@ class up.ExtractPlan
|
|
5
5
|
constructor: (selector, options) ->
|
6
6
|
@origin = options.origin
|
7
7
|
@selector = up.dom.resolveSelector(selector, options.origin)
|
8
|
-
@transition = options.transition
|
8
|
+
@transition = options.transition
|
9
9
|
@response = options.response
|
10
10
|
@oldLayer = options.layer
|
11
11
|
@steps = @parseSteps()
|
@@ -41,16 +41,11 @@ class up.ExtractPlan
|
|
41
41
|
]
|
42
42
|
###
|
43
43
|
parseSteps: =>
|
44
|
-
if u.isString(@transition)
|
45
|
-
transitions = @transition.split(comma)
|
46
|
-
else
|
47
|
-
transitions = [@transition]
|
48
|
-
|
49
44
|
comma = /\ *,\ */
|
50
45
|
|
51
46
|
disjunction = @selector.split(comma)
|
52
47
|
|
53
|
-
u.map disjunction, (literal, i)
|
48
|
+
u.map disjunction, (literal, i) =>
|
54
49
|
literalParts = literal.match(/^(.+?)(?:\:(before|after))?$/)
|
55
50
|
literalParts or up.fail('Could not parse selector literal "%s"', literal)
|
56
51
|
selector = literalParts[1]
|
@@ -60,8 +55,7 @@ class up.ExtractPlan
|
|
60
55
|
selector = 'body'
|
61
56
|
|
62
57
|
pseudoClass = literalParts[2]
|
63
|
-
transition = transitions[i] || u.last(transitions)
|
64
58
|
|
65
59
|
selector: selector
|
66
60
|
pseudoClass: pseudoClass
|
67
|
-
transition: transition
|
61
|
+
transition: @transition
|
@@ -28,13 +28,15 @@ up.dom = (($) ->
|
|
28
28
|
It is recommend to always keep `'body'` as the last selector in the last in the case
|
29
29
|
your server or load balancer renders an error message that does not contain your
|
30
30
|
application layout.
|
31
|
-
@param {string} [options.fallbackTransition=
|
32
|
-
The transition to use when using a fallback target.
|
31
|
+
@param {string} [options.fallbackTransition=null]
|
32
|
+
The transition to use when using a [fallback target](/#options.fallbacks).
|
33
|
+
|
34
|
+
By default this is not set and the original replacement's transition is used.
|
33
35
|
@stable
|
34
36
|
###
|
35
37
|
config = u.config
|
36
38
|
fallbacks: ['body']
|
37
|
-
fallbackTransition:
|
39
|
+
fallbackTransition: null
|
38
40
|
|
39
41
|
reset = ->
|
40
42
|
config.reset()
|
@@ -187,8 +189,11 @@ up.dom = (($) ->
|
|
187
189
|
If set to `false`, the history will remain unchanged.
|
188
190
|
@param {boolean|string} [options.source=true]
|
189
191
|
@param {boolean|string} [options.reveal=false]
|
190
|
-
Whether to [reveal](/up.reveal) the
|
191
|
-
|
192
|
+
Whether to [reveal](/up.reveal) the new fragment.
|
193
|
+
|
194
|
+
You can also pass a CSS selector for the element to reveal.
|
195
|
+
@param {boolean|string} [options.failReveal=false]
|
196
|
+
Whether to [reveal](/up.reveal) the new fragment when the server responds with an error.
|
192
197
|
|
193
198
|
You can also pass a CSS selector for the element to reveal.
|
194
199
|
@param {boolean} [options.restoreScroll=false]
|
@@ -239,8 +244,12 @@ up.dom = (($) ->
|
|
239
244
|
failureOptions = u.merge options,
|
240
245
|
humanizedTarget: 'failure target'
|
241
246
|
provideTarget: undefined # don't provide a target if we're targeting the failTarget
|
247
|
+
restoreScroll: false
|
248
|
+
hungry: false
|
249
|
+
|
242
250
|
u.renameKey(failureOptions, 'failTransition', 'transition')
|
243
251
|
u.renameKey(failureOptions, 'failLayer', 'layer')
|
252
|
+
u.renameKey(failureOptions, 'failReveal', 'reveal')
|
244
253
|
|
245
254
|
try
|
246
255
|
improvedTarget = bestPreflightSelector(selectorOrElement, successOptions)
|
@@ -102,8 +102,14 @@ up.form = (($) ->
|
|
102
102
|
The delay before the transition starts. See [`up.morph()`](/up.morph).
|
103
103
|
@param {string} [options.easing]
|
104
104
|
The timing function that controls the transition's acceleration. [`up.morph()`](/up.morph).
|
105
|
-
@param {Element|
|
106
|
-
Whether to reveal the target
|
105
|
+
@param {Element|string} [options.reveal=true]
|
106
|
+
Whether to reveal the target fragment after it was replaced.
|
107
|
+
|
108
|
+
You can also pass a CSS selector for the element to reveal.
|
109
|
+
@param {boolean|string} [options.failReveal=true]
|
110
|
+
Whether to [reveal](/up.reveal) the target fragment when the server responds with an error.
|
111
|
+
|
112
|
+
You can also pass a CSS selector for the element to reveal.
|
107
113
|
@param {boolean} [options.restoreScroll]
|
108
114
|
If set to `true`, this will attempt to [`restore scroll positions`](/up.restoreScroll)
|
109
115
|
previously seen on the destination URL.
|
@@ -135,13 +141,14 @@ up.form = (($) ->
|
|
135
141
|
target = u.option(options.target, $form.attr('up-target'), 'body')
|
136
142
|
url = u.option(options.url, $form.attr('action'), up.browser.url())
|
137
143
|
options.failTarget = u.option(options.failTarget, $form.attr('up-fail-target')) || u.selectorForElement($form)
|
144
|
+
options.reveal = u.option(options.reveal, u.castedAttr($form, 'up-reveal'), true)
|
145
|
+
options.failReveal = u.option(options.failReveal, u.castedAttr($form, 'up-fail-reveal'), true)
|
138
146
|
options.fallback = u.option(options.fallback, $form.attr('up-fallback'))
|
139
147
|
options.history = u.option(options.history, u.castedAttr($form, 'up-history'), true)
|
140
148
|
options.transition = u.option(options.transition, u.castedAttr($form, 'up-transition'), 'none')
|
141
149
|
options.failTransition = u.option(options.failTransition, u.castedAttr($form, 'up-fail-transition'), 'none')
|
142
150
|
options.method = u.option(options.method, $form.attr('up-method'), $form.attr('data-method'), $form.attr('method'), 'post').toUpperCase()
|
143
151
|
options.headers = u.option(options.headers, {})
|
144
|
-
options.reveal = u.option(options.reveal, u.castedAttr($form, 'up-reveal'), true)
|
145
152
|
options.cache = u.option(options.cache, u.castedAttr($form, 'up-cache'))
|
146
153
|
options.restoreScroll = u.option(options.restoreScroll, u.castedAttr($form, 'up-restore-scroll'))
|
147
154
|
options.origin = u.option(options.origin, $form)
|
@@ -520,7 +527,7 @@ up.form = (($) ->
|
|
520
527
|
If omitted, Unpoly will replace the `<form>` tag itself, assuming that the
|
521
528
|
server has echoed the form with validation errors.
|
522
529
|
@param [up-fallback]
|
523
|
-
The selector to replace if the server responds with
|
530
|
+
The selector to replace if the server responds with an error.
|
524
531
|
@param {string} [up-transition]
|
525
532
|
The animation to use when the form is replaced after a successful submission.
|
526
533
|
@param {string} [up-fail-transition]
|
@@ -549,7 +556,18 @@ up.form = (($) ->
|
|
549
556
|
The name of the layer that ought to be updated if the server sends a
|
550
557
|
non-200 status code.
|
551
558
|
@param {string} [up-reveal='true']
|
552
|
-
Whether to reveal the target element
|
559
|
+
Whether to reveal the target element after it was replaced.
|
560
|
+
|
561
|
+
You can also pass a CSS selector for the element to reveal.
|
562
|
+
@param {string} [up-fail-reveal='true']
|
563
|
+
Whether to reveal the target element when the server responds with an error.
|
564
|
+
|
565
|
+
You can also pass a CSS selector for the element to reveal. You may use this, for example,
|
566
|
+
to reveal the first validation error message:
|
567
|
+
|
568
|
+
<form up-target=".content" up-fail-reveal=".error">
|
569
|
+
...
|
570
|
+
</form>
|
553
571
|
@param {string} [up-restore-scroll='false']
|
554
572
|
Whether to restore previously known scroll position of all viewports
|
555
573
|
within the target selector.
|
@@ -237,7 +237,7 @@ up.layout = (($) ->
|
|
237
237
|
@stable
|
238
238
|
###
|
239
239
|
reveal = (elementOrSelector, options) ->
|
240
|
-
$element = $(elementOrSelector)
|
240
|
+
$element = $(elementOrSelector).first() # we can only reveal one element
|
241
241
|
up.puts 'Revealing fragment %o', $element.get(0)
|
242
242
|
options = u.options(options)
|
243
243
|
|
@@ -473,7 +473,13 @@ up.layout = (($) ->
|
|
473
473
|
selector = revealSelector(options.reveal)
|
474
474
|
$element = up.first(selector) || $element
|
475
475
|
revealOptions.top = true
|
476
|
-
|
476
|
+
|
477
|
+
# If selectorOrElement was a CSS selector, don't blow up by calling reveal()
|
478
|
+
# with an empty jQuery collection. This might happen if a failed form submission
|
479
|
+
# reveals the first validation error message, but the error is shown in an
|
480
|
+
# unexpected element.
|
481
|
+
if $element.length
|
482
|
+
return reveal($element, revealOptions)
|
477
483
|
|
478
484
|
# If we didn't need to scroll above, just return a resolved promise
|
479
485
|
# to fulfill this function's signature.
|
@@ -124,7 +124,14 @@ up.link = (($) ->
|
|
124
124
|
The selector to replace.
|
125
125
|
Defaults to the `[up-target]`, `[up-modal]` or `[up-popup]` attribute on `link`.
|
126
126
|
If no target is given, the `<body>` element will be replaced.
|
127
|
+
@param {boolean|string} [options.reveal=true]
|
128
|
+
Whether to [reveal](/up.reveal) the target fragment after it was replaced.
|
127
129
|
|
130
|
+
You can also pass a CSS selector for the element to reveal.
|
131
|
+
@param {boolean|string} [options.failReveal=true]
|
132
|
+
Whether to [reveal](/up.reveal) the target fragment when the server responds with an error.
|
133
|
+
|
134
|
+
You can also pass a CSS selector for the element to reveal.
|
128
135
|
@return {Promise}
|
129
136
|
A promise that will be fulfilled when the link destination
|
130
137
|
has been loaded and rendered.
|
@@ -163,6 +170,7 @@ up.link = (($) ->
|
|
163
170
|
options.failTransition = u.option(options.failTransition, u.castedAttr($link, 'up-fail-transition'), 'none')
|
164
171
|
options.history = u.option(options.history, u.castedAttr($link, 'up-history'))
|
165
172
|
options.reveal = u.option(options.reveal, u.castedAttr($link, 'up-reveal'), true)
|
173
|
+
options.failReveal = u.option(options.failReveal, u.castedAttr($link, 'up-fail-reveal'), true)
|
166
174
|
options.cache = u.option(options.cache, u.castedAttr($link, 'up-cache'))
|
167
175
|
options.restoreScroll = u.option(options.restoreScroll, u.castedAttr($link, 'up-restore-scroll'))
|
168
176
|
options.method = followMethod($link, options)
|
@@ -347,10 +355,10 @@ up.link = (($) ->
|
|
347
355
|
@param {string} [up-transition='none']
|
348
356
|
The [transition](/up.motion) to use for morphing between the old and new elements.
|
349
357
|
@param [up-fail-target='body']
|
350
|
-
The selector to replace if the server responds with
|
358
|
+
The selector to replace if the server responds with an error.
|
351
359
|
@param {string} [up-fail-transition='none']
|
352
360
|
The [transition](/up.motion) to use for morphing between the old and new elements
|
353
|
-
when the server responds with
|
361
|
+
when the server responds with an error.
|
354
362
|
@param {string} [up-fallback]
|
355
363
|
The selector to update when the original target was not found in the page.
|
356
364
|
@param {string} [up-href]
|
@@ -360,7 +368,13 @@ up.link = (($) ->
|
|
360
368
|
A message that will be displayed in a cancelable confirmation dialog
|
361
369
|
before the link is followed.
|
362
370
|
@param {string} [up-reveal='true']
|
363
|
-
Whether to reveal the target element
|
371
|
+
Whether to reveal the target element after it was replaced.
|
372
|
+
|
373
|
+
You can also pass a CSS selector for the element to reveal.
|
374
|
+
@param {string} [up-fail-reveal='true']
|
375
|
+
Whether to reveal the target element when the server responds with an error.
|
376
|
+
|
377
|
+
You can also pass a CSS selector for the element to reveal.
|
364
378
|
@param {string} [up-restore-scroll='false']
|
365
379
|
Whether to restore previously known scroll position of all viewports
|
366
380
|
within the target selector.
|
@@ -416,14 +430,14 @@ up.link = (($) ->
|
|
416
430
|
@param {string} [up-method='get']
|
417
431
|
The HTTP method to use for the request.
|
418
432
|
@param [up-fail-target='body']
|
419
|
-
The selector to replace if the server responds with
|
433
|
+
The selector to replace if the server responds with an error.
|
420
434
|
@param {string} [up-fallback]
|
421
435
|
The selector to update when the original target was not found in the page.
|
422
436
|
@param {string} [up-transition='none']
|
423
437
|
The [transition](/up.motion) to use for morphing between the old and new elements.
|
424
438
|
@param {string} [up-fail-transition='none']
|
425
439
|
The [transition](/up.motion) to use for morphing between the old and new elements
|
426
|
-
when the server responds with
|
440
|
+
when the server responds with an error.
|
427
441
|
@param [up-href]
|
428
442
|
The destination URL to follow.
|
429
443
|
If omitted, the the link's `href` attribute will be used.
|
@@ -11,16 +11,22 @@ That said, there is an **optional** protocol your server can use to
|
|
11
11
|
exchange additional information when Unpoly is [updating fragments](/up.link).
|
12
12
|
|
13
13
|
While the protocol can help you optimize performance and handle some
|
14
|
-
edge cases, implementing it is entirely optional
|
14
|
+
edge cases, implementing it is **entirely optional**. For instance,
|
15
15
|
`unpoly.com` itself is a static site that uses Unpoly on the frontend
|
16
16
|
and doesn't even have a server component.
|
17
17
|
|
18
|
-
|
19
|
-
is already implemented and you will get some
|
20
|
-
[Ruby bindings](https://github.com/unpoly/unpoly/blob/master/README_RAILS.md)
|
21
|
-
in your controllers and views. If your server-side app uses another language
|
22
|
-
or framework, you should be able to implement the protocol in a very short time.
|
18
|
+
## Existing implementations
|
23
19
|
|
20
|
+
You should be able to implement the protocol in a very short time.
|
21
|
+
There are existing implementations for various web frameworks:
|
22
|
+
|
23
|
+
- [Ruby on Rails](/install/rails)
|
24
|
+
- [Roda](https://github.com/adam12/roda-unpoly)
|
25
|
+
- [Rack](https://github.com/adam12/rack-unpoly) (Sinatra, Padrino, Hanami, Cuba, ...)
|
26
|
+
- [Phoenix](https://elixirforum.com/t/unpoly-a-framework-like-turbolinks/3614/15) (Elixir)
|
27
|
+
|
28
|
+
|
29
|
+
## Protocol details
|
24
30
|
|
25
31
|
\#\#\# Redirect detection for IE11
|
26
32
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
###*
|
2
|
+
Passive updates
|
3
|
+
===============
|
4
|
+
|
5
|
+
This work-in-progress package will contain functionality to
|
6
|
+
passively receive updates from the server.
|
7
|
+
|
8
|
+
@class up.radio
|
9
|
+
###
|
10
|
+
up.radio = (($) ->
|
11
|
+
|
12
|
+
u = up.util
|
13
|
+
|
14
|
+
###*
|
15
|
+
Configures defaults for passive updates.
|
16
|
+
|
17
|
+
@property up.radio.config
|
18
|
+
@param {Array<string>} [options.hungry]
|
19
|
+
An array of CSS selectors that is replaced whenever a matching element is found in a response.
|
20
|
+
These elements are replaced even when they were not targeted directly.
|
21
|
+
|
22
|
+
By default this contains the [`[up-hungry]`](/up-hungry) attribute as well as
|
23
|
+
`<meta name="csrf-param">` and `<meta name="csrf-token">` tags.
|
24
|
+
@param {string} [options.hungryTransition=null]
|
25
|
+
The transition to use when a [hungry element](/up-hungry) is replacing itself
|
26
|
+
while another target is replaced.
|
27
|
+
|
28
|
+
By default this is not set and the original replacement's transition is used.
|
29
|
+
@stable
|
30
|
+
###
|
31
|
+
config = u.config
|
32
|
+
hungry: ['[up-hungry]', 'meta[name="csrf-param"]', 'meta[name="csrf-token"]']
|
33
|
+
hungryTransition: null
|
34
|
+
|
35
|
+
reset = ->
|
36
|
+
config.reset()
|
37
|
+
|
38
|
+
###*
|
39
|
+
@function up.radio.hungrySelector
|
40
|
+
@internal
|
41
|
+
###
|
42
|
+
hungrySelector = ->
|
43
|
+
u.multiSelector(config.hungry)
|
44
|
+
|
45
|
+
###*
|
46
|
+
Elements with this attribute are [updated](/up.replace) whenever there is a
|
47
|
+
matching element found in a successful response. The element is replaced even
|
48
|
+
when it isn't [targeted](/a-up-target) directly.
|
49
|
+
|
50
|
+
Use cases for this are unread message counters or notification flashes.
|
51
|
+
Such elements often live in the layout, outside of the content area that is
|
52
|
+
being replaced.
|
53
|
+
|
54
|
+
@selector [up-hungry]
|
55
|
+
@stable
|
56
|
+
###
|
57
|
+
|
58
|
+
up.on 'up:framework:reset', reset
|
59
|
+
|
60
|
+
config: config
|
61
|
+
hungrySelector: hungrySelector
|
62
|
+
|
63
|
+
)(jQuery)
|
@@ -187,20 +187,31 @@ up.util = (($) ->
|
|
187
187
|
$element = $(element)
|
188
188
|
selector = undefined
|
189
189
|
|
190
|
+
tagName = $element.prop('tagName').toLowerCase()
|
191
|
+
|
190
192
|
if upId = presence($element.attr("up-id"))
|
191
|
-
selector =
|
193
|
+
selector = attributeSelector('up-id', upId)
|
192
194
|
else if id = presence($element.attr("id"))
|
193
|
-
|
195
|
+
if id.match(/^[a-z0-9\-_]+$/i)
|
196
|
+
selector = "##{id}"
|
197
|
+
else
|
198
|
+
selector = attributeSelector('id', id)
|
194
199
|
else if name = presence($element.attr("name"))
|
195
|
-
selector =
|
200
|
+
selector = tagName + attributeSelector('name', name)
|
196
201
|
else if classes = presence(nonUpClasses($element))
|
197
202
|
selector = ''
|
198
203
|
for klass in classes
|
199
204
|
selector += ".#{klass}"
|
205
|
+
else if ariaLabel = presence($element.attr("aria-label"))
|
206
|
+
selector = attributeSelector('aria-label', ariaLabel)
|
200
207
|
else
|
201
|
-
selector =
|
208
|
+
selector = tagName
|
202
209
|
selector
|
203
210
|
|
211
|
+
attributeSelector = (attribute, value) ->
|
212
|
+
value = value.replace(/"/g, '\\"')
|
213
|
+
"[#{attribute}=\"#{value}\"]"
|
214
|
+
|
204
215
|
nonUpClasses = ($element) ->
|
205
216
|
classString = $element.attr('class') || ''
|
206
217
|
classes = classString.split(' ')
|
data/lib/unpoly/rails/version.rb
CHANGED
data/package.json
CHANGED
data/spec_app/Gemfile.lock
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
<h1>Hash links (without Unpoly)</h1>
|
2
|
+
|
3
|
+
<div class="example" id="one" style="height: 1000px">
|
4
|
+
<h2>This is #one</h2>
|
5
|
+
|
6
|
+
<a href="#two">Scroll down to #two</a>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="example" id="two" style="height: 1000px">
|
10
|
+
<h2>This is #two</h2>
|
11
|
+
|
12
|
+
<a href="#one">Scroll up to #one</a>
|
13
|
+
</div>
|
@@ -60,6 +60,7 @@
|
|
60
60
|
<li><%= link_to 'Error', '/error_test/trigger' %></li>
|
61
61
|
<li><%= link_to 'Booting with non-GET method', '/method_test/page1' %></li>
|
62
62
|
<li><%= link_to 'Fragment update', '/replace_test/page1' %></li>
|
63
|
+
<li><%= link_to 'Hash links (without Unpoly)', '/hash_test/vanilla' %></li>
|
63
64
|
</ul>
|
64
65
|
|
65
66
|
</div>
|
data/spec_app/config/routes.rb
CHANGED
@@ -11,6 +11,7 @@ Rails.application.routes.draw do
|
|
11
11
|
get 'error_test/:action', controller: 'error_test'
|
12
12
|
post 'error_test/:action', controller: 'error_test'
|
13
13
|
get 'replace_test/:action', controller: 'replace_test'
|
14
|
+
get 'hash_test/:action', controller: 'hash_test'
|
14
15
|
|
15
16
|
namespace :form_test do
|
16
17
|
resource :basic, only: [:new, :create]
|
@@ -133,7 +133,7 @@ describe 'up.dom', ->
|
|
133
133
|
up.replace('.middle', '/path', method: 'put')
|
134
134
|
next => expect(@lastRequest()).toHaveRequestMethod('PUT')
|
135
135
|
|
136
|
-
describe 'when the server responds with
|
136
|
+
describe 'when the server responds with an error', ->
|
137
137
|
|
138
138
|
it 'replaces the first fallback instead of the given selector', asyncSpec (next) ->
|
139
139
|
up.dom.config.fallbacks = ['.fallback']
|
@@ -392,6 +392,116 @@ describe 'up.form', ->
|
|
392
392
|
next => @respondWith('<div class="response">new-text</div>')
|
393
393
|
next =>expect(up.browser.url()).toEqual(@hrefBeforeExample)
|
394
394
|
|
395
|
+
describe 'revealing', ->
|
396
|
+
|
397
|
+
it 'reaveals the target fragment if the submission succeeds', asyncSpec (next) ->
|
398
|
+
$form = affix('form[action="/action"][up-target=".target"]')
|
399
|
+
$target = affix('.target')
|
400
|
+
|
401
|
+
revealStub = up.layout.knife.mock('reveal')
|
402
|
+
|
403
|
+
up.submit($form)
|
404
|
+
|
405
|
+
next =>
|
406
|
+
@respondWith('<div class="target">new text</div>')
|
407
|
+
|
408
|
+
next =>
|
409
|
+
expect(revealStub).toHaveBeenCalled()
|
410
|
+
expect(revealStub.calls.mostRecent().args[0]).toBeMatchedBy('.target')
|
411
|
+
|
412
|
+
it 'reveals the form if the submission fails', asyncSpec (next) ->
|
413
|
+
$form = affix('form#foo-form[action="/action"][up-target=".target"]')
|
414
|
+
$target = affix('.target')
|
415
|
+
|
416
|
+
revealStub = up.layout.knife.mock('reveal')
|
417
|
+
|
418
|
+
up.submit($form)
|
419
|
+
|
420
|
+
next =>
|
421
|
+
@respondWith
|
422
|
+
status: 500,
|
423
|
+
responseText: """
|
424
|
+
<form id="foo-form">
|
425
|
+
Errors here
|
426
|
+
</form>
|
427
|
+
"""
|
428
|
+
|
429
|
+
next =>
|
430
|
+
expect(revealStub).toHaveBeenCalled()
|
431
|
+
expect(revealStub.calls.mostRecent().args[0]).toBeMatchedBy('#foo-form')
|
432
|
+
|
433
|
+
|
434
|
+
describe 'with { reveal } option', ->
|
435
|
+
|
436
|
+
it 'allows to reveal a different selector', asyncSpec (next) ->
|
437
|
+
$form = affix('form[action="/action"][up-target=".target"]')
|
438
|
+
$target = affix('.target')
|
439
|
+
$other = affix('.other')
|
440
|
+
|
441
|
+
revealStub = up.layout.knife.mock('reveal')
|
442
|
+
|
443
|
+
up.submit($form, reveal: '.other')
|
444
|
+
|
445
|
+
next =>
|
446
|
+
@respondWith """
|
447
|
+
<div class="target">
|
448
|
+
new text
|
449
|
+
</div>
|
450
|
+
<div class="other">
|
451
|
+
new other
|
452
|
+
</div>
|
453
|
+
"""
|
454
|
+
|
455
|
+
next =>
|
456
|
+
expect(revealStub).toHaveBeenCalled()
|
457
|
+
expect(revealStub.calls.mostRecent().args[0]).toBeMatchedBy('.other')
|
458
|
+
|
459
|
+
it 'still reveals the form for a failed submission', asyncSpec (next) ->
|
460
|
+
$form = affix('form#foo-form[action="/action"][up-target=".target"]')
|
461
|
+
$target = affix('.target')
|
462
|
+
$other = affix('.other')
|
463
|
+
|
464
|
+
revealStub = up.layout.knife.mock('reveal')
|
465
|
+
|
466
|
+
up.submit($form, reveal: '.other')
|
467
|
+
|
468
|
+
next =>
|
469
|
+
@respondWith
|
470
|
+
status: 500,
|
471
|
+
responseText: """
|
472
|
+
<form id="foo-form">
|
473
|
+
Errors here
|
474
|
+
</form>
|
475
|
+
"""
|
476
|
+
|
477
|
+
next =>
|
478
|
+
expect(revealStub).toHaveBeenCalled()
|
479
|
+
expect(revealStub.calls.mostRecent().args[0]).toBeMatchedBy('#foo-form')
|
480
|
+
|
481
|
+
describe 'with { failReveal } option', ->
|
482
|
+
|
483
|
+
it 'reveals the given selector for a failed submission', asyncSpec (next) ->
|
484
|
+
$form = affix('form#foo-form[action="/action"][up-target=".target"]')
|
485
|
+
$target = affix('.target')
|
486
|
+
$other = affix('.other')
|
487
|
+
|
488
|
+
revealStub = up.layout.knife.mock('reveal')
|
489
|
+
|
490
|
+
up.submit($form, reveal: '.other', failReveal: '.error')
|
491
|
+
|
492
|
+
next =>
|
493
|
+
@respondWith
|
494
|
+
status: 500,
|
495
|
+
responseText: """
|
496
|
+
<form id="foo-form">
|
497
|
+
<div class="error">Errors here</div>
|
498
|
+
</form>
|
499
|
+
"""
|
500
|
+
|
501
|
+
next =>
|
502
|
+
expect(revealStub).toHaveBeenCalled()
|
503
|
+
expect(revealStub.calls.mostRecent().args[0]).toBeMatchedBy('.error')
|
504
|
+
|
395
505
|
describe 'in a form with file inputs', ->
|
396
506
|
|
397
507
|
beforeEach ->
|
@@ -599,7 +709,7 @@ describe 'up.form', ->
|
|
599
709
|
next =>
|
600
710
|
request = @lastRequest()
|
601
711
|
expect(request.requestHeaders['X-Up-Validate']).toEqual('user')
|
602
|
-
expect(request.requestHeaders['X-Up-Target']).toEqual(
|
712
|
+
expect(request.requestHeaders['X-Up-Target']).toEqual('.field-group:has(input[name="user"])')
|
603
713
|
|
604
714
|
@respondWith """
|
605
715
|
<div class="field-group has-error">
|