activeadmin_sortable_table 1.1.2 → 1.1.3

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
  SHA1:
3
- metadata.gz: 7eee1f5a8aea16d3c1e840ab58b4d5e357503ae0
4
- data.tar.gz: 20b55c4342234b9afeb1e3808c93d3a96768bc76
3
+ metadata.gz: 66207fb91f6c2abf4f7a29107fabb9ace050c52f
4
+ data.tar.gz: 679a10bc3b27667d32fe43a9a86dd14855bed9d7
5
5
  SHA512:
6
- metadata.gz: 861ada336d936e7d31d4733d9563cdf0954fd0cbb2ab31273f74aafb6579ecda25a13e780cef0a2311be0542d1e4a4a51499b7581aace52fd5587a0f6ab47369
7
- data.tar.gz: 087823ddf085e64d6f1dc4fa35c21e2062ad9e59c369f9a2705454f8fe631c31e9564232735e505005525fbc25039ad064643176ad7660dc608a0b4742be8f38
6
+ metadata.gz: f5ce91b8a8994c8322f90e941a4c5ba01cf7bcd0de42e5080eac43f8067969f0ab18871f945311f5b3a87ed276dd07e73b8045ec0f0a5c9e08754ef08ba86f48
7
+ data.tar.gz: 3686d18158dc53c1adaf5a581e72cced145057f33a8239e22448ed092b020e045b4302b3106eddecfbf9675eb78785b7bfd3324e3ddf91d5f333b3c24479193d
@@ -1,3 +1,7 @@
1
+ 1.1.3
2
+
3
+ * **Fix**. Reordering was broken.
4
+
1
5
  1.1.2
2
6
 
3
7
  * **Fix**. Reload page after drug-and-dropping rows, so it recalculates positions properly.
@@ -19,7 +19,7 @@ GIT
19
19
  PATH
20
20
  remote: .
21
21
  specs:
22
- activeadmin_sortable_table (1.1.1)
22
+ activeadmin_sortable_table (1.1.3)
23
23
  activeadmin (>= 1.0.0.pre1)
24
24
  uber (= 0.0.15)
25
25
 
@@ -9,16 +9,30 @@
9
9
  update: function(event, ui) {
10
10
  var item = ui.item.find('[data-sort-url]');
11
11
  var url = item.data('sort-url');
12
- var actionOnSuccess = item.data('sort-success-action');
13
12
  var customParams = {};
14
13
  if (typeof item.data('sort-custom-params') === 'object') {
15
14
  customParams = item.data('sort-custom-params');
16
15
  }
17
16
 
17
+ var nextElement = ui.item[0].nextElementSibling;
18
+
19
+ var nextPosition = $(nextElement).find('[data-position]').data('position');
20
+ var currentPosition = ui.item.find('[data-position]').data('position');
21
+
22
+ if(nextPosition === undefined || nextPosition > currentPosition) {
23
+ // moved down
24
+ var previousElement = ui.item[0].previousElementSibling;
25
+ var previousPosition = $(previousElement).find('[data-position]').data('position');
26
+ var newPosition = previousPosition;
27
+ } else {
28
+ // moved up
29
+ var newPosition = nextPosition;
30
+ }
31
+
18
32
  $.ajax({
19
33
  url: url,
20
34
  type: 'post',
21
- data: $.extend(customParams, { position: ui.item.find('[data-position]').data('position') - 1 }),
35
+ data: $.extend(customParams, { position: newPosition }),
22
36
  error: function () { console.error('Saving sortable error'); },
23
37
  success: function () {
24
38
  location.href = location.href;
@@ -1,6 +1,6 @@
1
1
  module ActiveAdmin
2
2
  #
3
3
  module SortableTable
4
- VERSION = '1.1.2'
4
+ VERSION = '1.1.3'
5
5
  end
6
6
  end
@@ -1,3 +1,3 @@
1
1
  #= require active_admin/base
2
- #= require jquery.simulate
2
+ #= require jquery.simulate.drag-sortable
3
3
  #= require activeadmin_sortable_table
@@ -0,0 +1,235 @@
1
+ (function($) {
2
+ /*
3
+ * Simulate drag of a JQuery UI sortable list
4
+ * Repository: https://github.com/mattheworiordan/jquery.simulate.drag-sortable.js
5
+ * Author: http://mattheworiordan.com
6
+ *
7
+ * options are:
8
+ * - move: move item up (positive) or down (negative) by Integer amount
9
+ * - dropOn: move item to a new linked list, move option now represents position in the new list (zero indexed)
10
+ * - handle: selector for the draggable handle element (optional)
11
+ * - listItem: selector to limit which sibling items can be used for reordering
12
+ * - placeHolder: if a placeholder is used during dragging, we need to consider it's height
13
+ * - tolerance: (optional) number of pixels to overlap by instead of the default 50% of the element height
14
+ *
15
+ */
16
+ $.fn.simulateDragSortable = function(options) {
17
+ // build main options before element iteration
18
+ var opts = $.extend({}, $.fn.simulateDragSortable.defaults, options);
19
+
20
+ applyDrag = function(options) {
21
+ // allow for a drag handle if item is not draggable
22
+ var that = this,
23
+ options = options || opts, // default to plugin opts unless options explicitly provided
24
+ handle = options.handle ? $(this).find(options.handle)[0] : $(this)[0],
25
+ listItem = options.listItem,
26
+ placeHolder = options.placeHolder,
27
+ sibling = $(this),
28
+ moveCounter = Math.floor(options.move),
29
+ direction = moveCounter > 0 ? 'down' : 'up',
30
+ moveVerticalAmount = 0,
31
+ initialVerticalPosition = 0,
32
+ extraDrag = !isNaN(parseInt(options.tolerance, 10)) ? function() { return Number(options.tolerance); } : function(obj) { return ($(obj).outerHeight() / 2) + 5; },
33
+ dragPastBy = 0, // represents the additional amount one drags past an element and bounce back
34
+ dropOn = options.dropOn ? $(options.dropOn) : false,
35
+ center = findCenter(handle),
36
+ x = Math.floor(center.x),
37
+ y = Math.floor(center.y),
38
+ mouseUpAfter = (opts.debug ? 2500 : 10);
39
+
40
+ if (dropOn) {
41
+ if (dropOn.length === 0) {
42
+ if (console && console.log) { console.log('simulate.drag-sortable.js ERROR: Drop on target could not be found'); console.log(options.dropOn); }
43
+ return;
44
+ }
45
+ sibling = dropOn.find('>*:last');
46
+ moveCounter = -(dropOn.find('>*').length + 1) + (moveCounter + 1); // calculate length of list after this move, use moveCounter as a positive index position in list to reverse back up
47
+ if (dropOn.offset().top - $(this).offset().top < 0) {
48
+ // moving to a list above this list, so move to just above top of last item (tried moving to top but JQuery UI wouldn't bite)
49
+ initialVerticalPosition = sibling.offset().top - $(this).offset().top - extraDrag(this);
50
+ } else {
51
+ // moving to a list below this list, so move to bottom and work up (JQuery UI does not trigger new list below unless you move past top item first)
52
+ initialVerticalPosition = sibling.offset().top - $(this).offset().top - $(this).height();
53
+ }
54
+ } else if (moveCounter === 0) {
55
+ if (console && console.log) { console.log('simulate.drag-sortable.js WARNING: Drag with move set to zero has no effect'); }
56
+ return;
57
+ } else {
58
+ while (moveCounter !== 0) {
59
+ if (direction === 'down') {
60
+ if (sibling.next(listItem).length) {
61
+ sibling = sibling.next(listItem);
62
+ moveVerticalAmount += sibling.outerHeight();
63
+ }
64
+ moveCounter -= 1;
65
+ } else {
66
+ if (sibling.prev(listItem).length) {
67
+ sibling = sibling.prev(listItem);
68
+ moveVerticalAmount -= sibling.outerHeight();
69
+ }
70
+ moveCounter += 1;
71
+ }
72
+ }
73
+ }
74
+
75
+ dispatchEvent(handle, 'mousedown', createEvent('mousedown', handle, { clientX: x, clientY: y }));
76
+ // simulate drag start
77
+ dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x+1, clientY: y+1 }));
78
+
79
+ if (dropOn) {
80
+ // jump to top or bottom of new list but do it in increments so that JQuery UI registers the drag events
81
+ slideUpTo(x, y, initialVerticalPosition);
82
+
83
+ // reset y position to top or bottom of list and move from there
84
+ y += initialVerticalPosition;
85
+
86
+ // now call regular shift/down in a list
87
+ options = jQuery.extend(options, { move: moveCounter });
88
+ delete options.dropOn;
89
+
90
+ // add some delays to allow JQuery UI to catch up
91
+ setTimeout(function() {
92
+ dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y }));
93
+ }, 5);
94
+ setTimeout(function() {
95
+ dispatchEvent(handle, 'mouseup', createEvent('mouseup', handle, { clientX: x, clientY: y }));
96
+ setTimeout(function() {
97
+ if (options.move) {
98
+ applyDrag.call(that, options);
99
+ }
100
+ }, 5);
101
+ }, mouseUpAfter);
102
+
103
+ // stop execution as applyDrag has been called again
104
+ return;
105
+ }
106
+
107
+ // Sortable is using a fixed height placeholder meaning items jump up and down as you drag variable height items into fixed height placeholder
108
+ placeHolder = placeHolder && $(this).parent().find(placeHolder);
109
+
110
+ if (!placeHolder && (direction === 'down')) {
111
+ // need to move at least as far as this item and or the last sibling
112
+ if ($(this).outerHeight() > $(sibling).outerHeight()) {
113
+ moveVerticalAmount += $(this).outerHeight() - $(sibling).outerHeight();
114
+ }
115
+ moveVerticalAmount += extraDrag(sibling);
116
+ dragPastBy += extraDrag(sibling);
117
+ } else if (direction === 'up') {
118
+ // move a little extra to ensure item clips into next position
119
+ moveVerticalAmount -= Math.max(extraDrag(this), 5);
120
+ } else if (direction === 'down') {
121
+ // moving down with a place holder
122
+ if (placeHolder.height() < $(this).height()) {
123
+ moveVerticalAmount += Math.max(placeHolder.height(), 5);
124
+ } else {
125
+ moveVerticalAmount += extraDrag(sibling);
126
+ }
127
+ }
128
+
129
+ if (sibling[0] !== $(this)[0]) {
130
+ // step through so that the UI controller can determine when to show the placeHolder
131
+ slideUpTo(x, y, moveVerticalAmount, dragPastBy);
132
+ } else {
133
+ if (window.console) {
134
+ console.log('simulate.drag-sortable.js WARNING: Could not move as at top or bottom already');
135
+ }
136
+ }
137
+
138
+ setTimeout(function() {
139
+ dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + moveVerticalAmount }));
140
+ }, 5);
141
+ setTimeout(function() {
142
+ dispatchEvent(handle, 'mouseup', createEvent('mouseup', handle, { clientX: x, clientY: y + moveVerticalAmount }));
143
+ }, mouseUpAfter);
144
+ };
145
+
146
+ // iterate and move each matched element
147
+ return this.each(applyDrag);
148
+ };
149
+
150
+ // fire mouse events, go half way, then the next half, so small mouse movements near target and big at the start
151
+ function slideUpTo(x, y, targetOffset, goPastBy) {
152
+ var moveBy, offset;
153
+
154
+ if (!goPastBy) { goPastBy = 0; }
155
+ if ((targetOffset < 0) && (goPastBy > 0)) { goPastBy = -goPastBy; } // ensure go past is in the direction as often passed in from object height so always positive
156
+
157
+ // go forwards including goPastBy
158
+ for (offset = 0; Math.abs(offset) + 1 < Math.abs(targetOffset + goPastBy); offset += ((targetOffset + goPastBy - offset)/2) ) {
159
+ dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + Math.ceil(offset) }));
160
+ }
161
+ offset = targetOffset + goPastBy;
162
+ dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + offset }));
163
+
164
+ // now bounce back
165
+ for (; Math.abs(offset) - 1 >= Math.abs(targetOffset); offset += ((targetOffset - offset)/2) ) {
166
+ dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + Math.ceil(offset) }));
167
+ }
168
+ dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + targetOffset }));
169
+ }
170
+
171
+ function createEvent(type, target, options) {
172
+ var evt;
173
+ var e = $.extend({
174
+ target: target,
175
+ preventDefault: function() { },
176
+ stopImmediatePropagation: function() { },
177
+ stopPropagation: function() { },
178
+ isPropagationStopped: function() { return true; },
179
+ isImmediatePropagationStopped: function() { return true; },
180
+ isDefaultPrevented: function() { return true; },
181
+ bubbles: true,
182
+ cancelable: (type != "mousemove"),
183
+ view: window,
184
+ detail: 0,
185
+ screenX: 0,
186
+ screenY: 0,
187
+ clientX: 0,
188
+ clientY: 0,
189
+ ctrlKey: false,
190
+ altKey: false,
191
+ shiftKey: false,
192
+ metaKey: false,
193
+ button: 0,
194
+ relatedTarget: undefined
195
+ }, options || {});
196
+
197
+ if ($.isFunction(document.createEvent)) {
198
+ evt = document.createEvent("MouseEvents");
199
+ evt.initMouseEvent(type, e.bubbles, e.cancelable, e.view, e.detail,
200
+ e.screenX, e.screenY, e.clientX, e.clientY,
201
+ e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
202
+ e.button, e.relatedTarget || document.body.parentNode);
203
+ } else if (document.createEventObject) {
204
+ evt = document.createEventObject();
205
+ $.extend(evt, e);
206
+ evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button;
207
+ }
208
+ return evt;
209
+ }
210
+
211
+ function dispatchEvent(el, type, evt) {
212
+ if (el.dispatchEvent) {
213
+ el.dispatchEvent(evt);
214
+ } else if (el.fireEvent) {
215
+ el.fireEvent('on' + type, evt);
216
+ }
217
+ return evt;
218
+ }
219
+
220
+ function findCenter(el) {
221
+ var elm = $(el),
222
+ o = elm.offset();
223
+ return {
224
+ x: o.left + elm.outerWidth() / 2,
225
+ y: o.top + elm.outerHeight() / 2
226
+ };
227
+ }
228
+
229
+ //
230
+ // plugin defaults
231
+ //
232
+ $.fn.simulateDragSortable.defaults = {
233
+ move: 0
234
+ };
235
+ })(jQuery);
@@ -1,43 +1,66 @@
1
- RSpec.describe ActiveAdmin::SortableTable, 'Drag-and-Drop', type: :feature do
1
+ RSpec.describe ActiveAdmin::SortableTable, 'Drag-and-Drop', type: :feature, js: true do
2
2
  before do
3
3
  Category.create!
4
4
  Category.create!
5
5
  Category.create!
6
6
  end
7
7
 
8
- def ordered_elements
8
+ def ordered_ids
9
9
  Category.order(:position).pluck(:id)
10
10
  end
11
11
 
12
12
  context 'first page' do
13
- it 'reorders elements by dragging vertically by handle', js: true do
14
- expect(ordered_elements).to eq([1, 2, 3])
13
+ before do
14
+ expect(ordered_ids).to eq([1, 2, 3])
15
15
 
16
16
  visit admin_categories_path
17
17
 
18
- expect(visible_elements).to eq([1, 2, 3])
18
+ expect(visible_ids).to eq([1, 2, 3])
19
19
  expect(visible_positions).to eq([1, 2, 3])
20
+ end
20
21
 
21
- move_higher(2, by_handle: true)
22
+ context 'move element up' do
23
+ it 'to the top position' do
24
+ move_up(3, by: 2)
22
25
 
23
- expect(visible_elements).to eq([2, 1, 3])
24
- expect(visible_positions).to eq([1, 2, 3])
25
- expect(ordered_elements).to eq([2, 1, 3])
26
+ expect(visible_ids).to eq([3, 1, 2])
27
+ expect(visible_positions).to eq([1, 2, 3])
28
+ expect(ordered_ids).to eq([3, 1, 2])
29
+ end
30
+
31
+ it 'to the middle of the table' do
32
+ move_up(3, by: 1)
33
+
34
+ expect(visible_ids).to eq([1, 3, 2])
35
+ expect(visible_positions).to eq([1, 2, 3])
36
+ expect(ordered_ids).to eq([1, 3, 2])
37
+ end
26
38
  end
27
39
 
28
- it 'does not reorder elements by dragging vertically by row', js: true do
29
- expect(ordered_elements).to eq([1, 2, 3])
40
+ context 'move element down' do
41
+ it 'to the bottom of the table' do
42
+ move_down(1, by: 2)
30
43
 
31
- visit admin_categories_path
44
+ expect(visible_ids).to eq([2, 3, 1])
45
+ expect(visible_positions).to eq([1, 2, 3])
46
+ expect(ordered_ids).to eq([2, 3, 1])
47
+ end
32
48
 
33
- expect(visible_elements).to eq([1, 2, 3])
34
- expect(visible_positions).to eq([1, 2, 3])
49
+ it 'to the middle of the table' do
50
+ move_down(2, by: 1)
51
+
52
+ expect(visible_ids).to eq([1, 3, 2])
53
+ expect(visible_positions).to eq([1, 2, 3])
54
+ expect(ordered_ids).to eq([1, 3, 2])
55
+ end
56
+ end
35
57
 
36
- move_higher(2, by_handle: false)
58
+ it 'can not drug not by handle' do
59
+ move_up(3, by: 2, use_handle: false)
37
60
 
38
- expect(visible_elements).to eq([1, 2, 3])
61
+ expect(visible_ids).to eq([1, 2, 3])
39
62
  expect(visible_positions).to eq([1, 2, 3])
40
- expect(ordered_elements).to eq([1, 2, 3])
63
+ expect(ordered_ids).to eq([1, 2, 3])
41
64
  end
42
65
  end
43
66
 
@@ -48,40 +71,63 @@ RSpec.describe ActiveAdmin::SortableTable, 'Drag-and-Drop', type: :feature do
48
71
  Category.create!
49
72
  end
50
73
 
51
- it 'reorders elements by dragging vertically by handle', js: true do
52
- expect(ordered_elements).to eq([1, 2, 3, 4, 5, 6])
74
+ before do
75
+ expect(ordered_ids).to eq([1, 2, 3, 4, 5, 6])
53
76
 
54
77
  visit admin_categories_path(page: 2)
55
78
 
56
- expect(visible_elements).to eq([4, 5, 6])
79
+ expect(visible_ids).to eq([4, 5, 6])
57
80
  expect(visible_positions).to eq([4, 5, 6])
81
+ end
58
82
 
59
- move_higher(5, by_handle: true)
83
+ context 'move element up' do
84
+ it 'to the top position' do
85
+ move_up(6, by: 2)
60
86
 
61
- expect(visible_elements).to eq([5, 4, 6])
62
- expect(visible_positions).to eq([4, 5, 6])
63
- expect(ordered_elements).to eq([1, 2, 3, 5, 4, 6])
87
+ expect(visible_ids).to eq([6, 4, 5])
88
+ expect(visible_positions).to eq([4, 5, 6])
89
+ expect(ordered_ids).to eq([1, 2, 3, 6, 4, 5])
90
+ end
91
+
92
+ it 'to the middle of the table' do
93
+ move_up(6, by: 1)
94
+
95
+ expect(visible_ids).to eq([4, 6, 5])
96
+ expect(visible_positions).to eq([4, 5, 6])
97
+ expect(ordered_ids).to eq([1, 2, 3, 4, 6, 5])
98
+ end
64
99
  end
65
100
 
66
- it 'does not reorder elements by dragging vertically by row', js: true do
67
- expect(ordered_elements).to eq([1, 2, 3, 4, 5, 6])
101
+ context 'move element down' do
102
+ it 'to the bottom of the table' do
103
+ move_down(4, by: 2)
68
104
 
69
- visit admin_categories_path(page: 2)
105
+ expect(visible_ids).to eq([5, 6, 4])
106
+ expect(visible_positions).to eq([4, 5, 6])
107
+ expect(ordered_ids).to eq([1, 2, 3, 5, 6, 4])
108
+ end
70
109
 
71
- expect(visible_elements).to eq([4, 5, 6])
72
- expect(visible_positions).to eq([4, 5, 6])
110
+ it 'to the middle of the table' do
111
+ move_down(4, by: 1)
73
112
 
74
- move_higher(5, by_handle: false)
113
+ expect(visible_ids).to eq([5, 4, 6])
114
+ expect(visible_positions).to eq([4, 5, 6])
115
+ expect(ordered_ids).to eq([1, 2, 3, 5, 4, 6])
116
+ end
117
+ end
118
+
119
+ it 'can not drug not by handle' do
120
+ move_down(6, by: 2, use_handle: false)
75
121
 
76
- expect(visible_elements).to eq([4, 5, 6])
122
+ expect(visible_ids).to eq([4, 5, 6])
77
123
  expect(visible_positions).to eq([4, 5, 6])
78
- expect(ordered_elements).to eq([1, 2, 3, 4, 5, 6])
124
+ expect(ordered_ids).to eq([1, 2, 3, 4, 5, 6])
79
125
  end
80
126
  end
81
127
 
82
128
  private
83
129
 
84
- def visible_elements
130
+ def visible_ids
85
131
  all('.ui-sortable .col-id').map(&:text).map(&:to_i)
86
132
  end
87
133
 
@@ -89,14 +135,24 @@ RSpec.describe ActiveAdmin::SortableTable, 'Drag-and-Drop', type: :feature do
89
135
  all('.ui-sortable .col-position').map(&:text).map(&:to_i)
90
136
  end
91
137
 
92
- def move_higher(element_id, by_handle:)
93
- drag_element(element_id, by_handle, dy: -200)
138
+ def move_up(element_id, by: 1, use_handle: true)
139
+ drag_element(element_id, by: -by, use_handle: use_handle)
94
140
  end
95
141
 
96
- def drag_element(element_id, by_handle, options)
97
- options.reverse_merge! moves: 20
98
- selector = by_handle ? "#category_#{element_id} .handle" : "#category_#{element_id}"
99
- page.execute_script(%($("#{selector}").simulate("drag", #{options.to_json})))
100
- sleep 0.1
142
+ def move_down(element_id, by: 1, use_handle: true)
143
+ drag_element(element_id, by: by, use_handle: use_handle)
144
+ end
145
+
146
+ def drag_element(element_id, by:, use_handle:)
147
+ if use_handle
148
+ page.execute_script(<<-JS)
149
+ $("#category_#{element_id}").simulateDragSortable({ move: #{by}, handle: ".handle" })
150
+ JS
151
+ else
152
+ page.execute_script(<<-JS)
153
+ $("#category_#{element_id}").simulateDragSortable({ move: #{by} })
154
+ JS
155
+ end
156
+ sleep 0.5
101
157
  end
102
158
  end
@@ -1,4 +1,4 @@
1
- RSpec.describe ActiveAdmin::SortableTable, 'Move to top', type: :feature do
1
+ RSpec.describe ActiveAdmin::SortableTable, 'Move to top', type: :feature, js: true do
2
2
  before do
3
3
  Category.create!
4
4
  Category.create!
@@ -6,39 +6,47 @@ RSpec.describe ActiveAdmin::SortableTable, 'Move to top', type: :feature do
6
6
  Category.create!
7
7
  end
8
8
 
9
- def ordered_elements
9
+ def ordered_ids
10
10
  Category.order(:position).pluck(:id)
11
11
  end
12
12
 
13
- it 'push element to top by clicking "move to top"', js: true do
14
- expect(ordered_elements).to eq([1, 2, 3, 4])
13
+ before do
14
+ expect(ordered_ids).to eq([1, 2, 3, 4])
15
15
 
16
16
  # Initially only one element on the second page
17
17
  visit admin_categories_path(page: 2)
18
18
 
19
- expect(visible_elements).to contain_exactly(4)
19
+ expect(visible_ids).to contain_exactly(4)
20
+ expect(visible_positions).to contain_exactly(4)
21
+ end
20
22
 
23
+ it 'push element to top by clicking "move to top"' do
21
24
  # When I push "move to top" button
22
25
  move_to_top(4)
23
26
 
24
27
  # The last element from the previous page should be shown
25
28
  # save_and_open_page
26
- expect(visible_elements).to contain_exactly(3)
29
+ expect(visible_ids).to contain_exactly(3)
27
30
 
28
31
  # And when I visit previous page
29
32
  visit admin_categories_path(page: 1)
30
33
 
31
34
  # I should see pushed elenent on the top
32
- expect(visible_elements).to eq([4, 1, 2])
33
- expect(ordered_elements).to eq([4, 1, 2, 3])
35
+ expect(visible_ids).to eq([4, 1, 2])
36
+ expect(visible_positions).to eq([1, 2, 3])
37
+ expect(ordered_ids).to eq([4, 1, 2, 3])
34
38
  end
35
39
 
36
40
  private
37
41
 
38
- def visible_elements
42
+ def visible_ids
39
43
  all('.ui-sortable .col-id').map(&:text).map(&:to_i)
40
44
  end
41
45
 
46
+ def visible_positions
47
+ all('.ui-sortable .col-position').map(&:text).map(&:to_i)
48
+ end
49
+
42
50
  def move_to_top(element_id)
43
51
  within "#category_#{element_id}" do
44
52
  first('.move_to_top').click
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeadmin_sortable_table
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam McCrea
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-10-19 00:00:00.000000000 Z
13
+ date: 2015-10-23 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activeadmin
@@ -172,7 +172,7 @@ files:
172
172
  - spec/dummy/app/assets/images/.keep
173
173
  - spec/dummy/app/assets/javascripts/active_admin.js.coffee
174
174
  - spec/dummy/app/assets/javascripts/application.js
175
- - spec/dummy/app/assets/javascripts/jquery.simulate.js
175
+ - spec/dummy/app/assets/javascripts/jquery.simulate.drag-sortable.js
176
176
  - spec/dummy/app/assets/stylesheets/active_admin.scss
177
177
  - spec/dummy/app/assets/stylesheets/application.css
178
178
  - spec/dummy/app/controllers/application_controller.rb
@@ -242,7 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
242
242
  version: '0'
243
243
  requirements: []
244
244
  rubyforge_project:
245
- rubygems_version: 2.4.5
245
+ rubygems_version: 2.4.8
246
246
  signing_key:
247
247
  specification_version: 4
248
248
  summary: Drag and drop reordering interface for ActiveAdmin tables
@@ -254,7 +254,7 @@ test_files:
254
254
  - spec/dummy/app/assets/images/.keep
255
255
  - spec/dummy/app/assets/javascripts/active_admin.js.coffee
256
256
  - spec/dummy/app/assets/javascripts/application.js
257
- - spec/dummy/app/assets/javascripts/jquery.simulate.js
257
+ - spec/dummy/app/assets/javascripts/jquery.simulate.drag-sortable.js
258
258
  - spec/dummy/app/assets/stylesheets/active_admin.scss
259
259
  - spec/dummy/app/assets/stylesheets/application.css
260
260
  - spec/dummy/app/controllers/application_controller.rb
@@ -1,314 +0,0 @@
1
- /*!
2
- * jQuery Simulate v@VERSION - simulate browser mouse and keyboard events
3
- * https://github.com/jquery/jquery-simulate
4
- *
5
- * Copyright 2012 jQuery Foundation and other contributors
6
- * Released under the MIT license.
7
- * http://jquery.org/license
8
- *
9
- * Date: @DATE
10
- */
11
-
12
- ;(function( $, undefined ) {
13
-
14
- var rkeyEvent = /^key/,
15
- rmouseEvent = /^(?:mouse|contextmenu)|click/;
16
-
17
- $.fn.simulate = function( type, options ) {
18
- return this.each(function() {
19
- new $.simulate( this, type, options );
20
- });
21
- };
22
-
23
- $.simulate = function( elem, type, options ) {
24
- var method = $.camelCase( "simulate-" + type );
25
-
26
- this.target = elem;
27
- this.options = options;
28
-
29
- if ( this[ method ] ) {
30
- this[ method ]();
31
- } else {
32
- this.simulateEvent( elem, type, options );
33
- }
34
- };
35
-
36
- $.extend( $.simulate, {
37
-
38
- keyCode: {
39
- BACKSPACE: 8,
40
- COMMA: 188,
41
- DELETE: 46,
42
- DOWN: 40,
43
- END: 35,
44
- ENTER: 13,
45
- ESCAPE: 27,
46
- HOME: 36,
47
- LEFT: 37,
48
- NUMPAD_ADD: 107,
49
- NUMPAD_DECIMAL: 110,
50
- NUMPAD_DIVIDE: 111,
51
- NUMPAD_ENTER: 108,
52
- NUMPAD_MULTIPLY: 106,
53
- NUMPAD_SUBTRACT: 109,
54
- PAGE_DOWN: 34,
55
- PAGE_UP: 33,
56
- PERIOD: 190,
57
- RIGHT: 39,
58
- SPACE: 32,
59
- TAB: 9,
60
- UP: 38
61
- },
62
-
63
- buttonCode: {
64
- LEFT: 0,
65
- MIDDLE: 1,
66
- RIGHT: 2
67
- }
68
- });
69
-
70
- $.extend( $.simulate.prototype, {
71
-
72
- simulateEvent: function( elem, type, options ) {
73
- var event = this.createEvent( type, options );
74
- this.dispatchEvent( elem, type, event, options );
75
- },
76
-
77
- createEvent: function( type, options ) {
78
- if ( rkeyEvent.test( type ) ) {
79
- return this.keyEvent( type, options );
80
- }
81
-
82
- if ( rmouseEvent.test( type ) ) {
83
- return this.mouseEvent( type, options );
84
- }
85
- },
86
-
87
- mouseEvent: function( type, options ) {
88
- var event, eventDoc, doc, body;
89
- options = $.extend({
90
- bubbles: true,
91
- cancelable: (type !== "mousemove"),
92
- view: window,
93
- detail: 0,
94
- screenX: 0,
95
- screenY: 0,
96
- clientX: 1,
97
- clientY: 1,
98
- ctrlKey: false,
99
- altKey: false,
100
- shiftKey: false,
101
- metaKey: false,
102
- button: 0,
103
- relatedTarget: undefined
104
- }, options );
105
-
106
- if ( document.createEvent ) {
107
- event = document.createEvent( "MouseEvents" );
108
- event.initMouseEvent( type, options.bubbles, options.cancelable,
109
- options.view, options.detail,
110
- options.screenX, options.screenY, options.clientX, options.clientY,
111
- options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
112
- options.button, options.relatedTarget || document.body.parentNode );
113
-
114
- // IE 9+ creates events with pageX and pageY set to 0.
115
- // Trying to modify the properties throws an error,
116
- // so we define getters to return the correct values.
117
- if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) {
118
- eventDoc = event.relatedTarget.ownerDocument || document;
119
- doc = eventDoc.documentElement;
120
- body = eventDoc.body;
121
-
122
- Object.defineProperty( event, "pageX", {
123
- get: function() {
124
- return options.clientX +
125
- ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
126
- ( doc && doc.clientLeft || body && body.clientLeft || 0 );
127
- }
128
- });
129
- Object.defineProperty( event, "pageY", {
130
- get: function() {
131
- return options.clientY +
132
- ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
133
- ( doc && doc.clientTop || body && body.clientTop || 0 );
134
- }
135
- });
136
- }
137
- } else if ( document.createEventObject ) {
138
- event = document.createEventObject();
139
- $.extend( event, options );
140
- // standards event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ff974877(v=vs.85).aspx
141
- // old IE event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ms533544(v=vs.85).aspx
142
- // so we actually need to map the standard back to oldIE
143
- event.button = {
144
- 0: 1,
145
- 1: 4,
146
- 2: 2
147
- }[ event.button ] || event.button;
148
- }
149
-
150
- return event;
151
- },
152
-
153
- keyEvent: function( type, options ) {
154
- var event;
155
- options = $.extend({
156
- bubbles: true,
157
- cancelable: true,
158
- view: window,
159
- ctrlKey: false,
160
- altKey: false,
161
- shiftKey: false,
162
- metaKey: false,
163
- keyCode: 0,
164
- charCode: undefined
165
- }, options );
166
-
167
- if ( document.createEvent ) {
168
- try {
169
- event = document.createEvent( "KeyEvents" );
170
- event.initKeyEvent( type, options.bubbles, options.cancelable, options.view,
171
- options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
172
- options.keyCode, options.charCode );
173
- // initKeyEvent throws an exception in WebKit
174
- // see: http://stackoverflow.com/questions/6406784/initkeyevent-keypress-only-works-in-firefox-need-a-cross-browser-solution
175
- // and also https://bugs.webkit.org/show_bug.cgi?id=13368
176
- // fall back to a generic event until we decide to implement initKeyboardEvent
177
- } catch( err ) {
178
- event = document.createEvent( "Events" );
179
- event.initEvent( type, options.bubbles, options.cancelable );
180
- $.extend( event, {
181
- view: options.view,
182
- ctrlKey: options.ctrlKey,
183
- altKey: options.altKey,
184
- shiftKey: options.shiftKey,
185
- metaKey: options.metaKey,
186
- keyCode: options.keyCode,
187
- charCode: options.charCode
188
- });
189
- }
190
- } else if ( document.createEventObject ) {
191
- event = document.createEventObject();
192
- $.extend( event, options );
193
- }
194
-
195
- if ( !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ) || (({}).toString.call( window.opera ) === "[object Opera]") ) {
196
- event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode;
197
- event.charCode = undefined;
198
- }
199
-
200
- return event;
201
- },
202
-
203
- dispatchEvent: function( elem, type, event ) {
204
- if ( elem[ type ] ) {
205
- elem[ type ]();
206
- } else if ( elem.dispatchEvent ) {
207
- elem.dispatchEvent( event );
208
- } else if ( elem.fireEvent ) {
209
- elem.fireEvent( "on" + type, event );
210
- }
211
- },
212
-
213
- simulateFocus: function() {
214
- var focusinEvent,
215
- triggered = false,
216
- element = $( this.target );
217
-
218
- function trigger() {
219
- triggered = true;
220
- }
221
-
222
- element.bind( "focus", trigger );
223
- element[ 0 ].focus();
224
-
225
- if ( !triggered ) {
226
- focusinEvent = $.Event( "focusin" );
227
- focusinEvent.preventDefault();
228
- element.trigger( focusinEvent );
229
- element.triggerHandler( "focus" );
230
- }
231
- element.unbind( "focus", trigger );
232
- },
233
-
234
- simulateBlur: function() {
235
- var focusoutEvent,
236
- triggered = false,
237
- element = $( this.target );
238
-
239
- function trigger() {
240
- triggered = true;
241
- }
242
-
243
- element.bind( "blur", trigger );
244
- element[ 0 ].blur();
245
-
246
- // blur events are async in IE
247
- setTimeout(function() {
248
- // IE won't let the blur occur if the window is inactive
249
- if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) {
250
- element[ 0 ].ownerDocument.body.focus();
251
- }
252
-
253
- // Firefox won't trigger events if the window is inactive
254
- // IE doesn't trigger events if we had to manually focus the body
255
- if ( !triggered ) {
256
- focusoutEvent = $.Event( "focusout" );
257
- focusoutEvent.preventDefault();
258
- element.trigger( focusoutEvent );
259
- element.triggerHandler( "blur" );
260
- }
261
- element.unbind( "blur", trigger );
262
- }, 1 );
263
- }
264
- });
265
-
266
-
267
-
268
- /** complex events **/
269
-
270
- function findCenter( elem ) {
271
- var offset,
272
- document = $( elem.ownerDocument );
273
- elem = $( elem );
274
- offset = elem.offset();
275
-
276
- return {
277
- x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(),
278
- y: offset.top + elem.outerHeight() / 2 - document.scrollTop()
279
- };
280
- }
281
-
282
- $.extend( $.simulate.prototype, {
283
- simulateDrag: function() {
284
- var i = 0,
285
- target = this.target,
286
- options = this.options,
287
- center = findCenter( target ),
288
- x = Math.floor( center.x ),
289
- y = Math.floor( center.y ),
290
- dx = options.dx || 0,
291
- dy = options.dy || 0,
292
- moves = options.moves || 3,
293
- coord = { clientX: x, clientY: y };
294
-
295
- this.simulateEvent( target, "mousedown", coord );
296
-
297
- for ( ; i < moves ; i++ ) {
298
- x += dx / moves;
299
- y += dy / moves;
300
-
301
- coord = {
302
- clientX: Math.round( x ),
303
- clientY: Math.round( y )
304
- };
305
-
306
- this.simulateEvent( document, "mousemove", coord );
307
- }
308
-
309
- this.simulateEvent( target, "mouseup", coord );
310
- this.simulateEvent( target, "click", coord );
311
- }
312
- });
313
-
314
- })( jQuery );