Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions spec/init_once_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
require 'spec_helper'

describe 'initialising the same element twice', type: :feature, js: true do

let(:today) { Time.now.utc.to_date }

before { add_post_resource }
before { visit '/admin/posts' }

def filter
page.find('.filter_date_range')
end

def init_again(options = '')
page.execute_script(
"$('form.filter_form div.filter_date_range').date_range_ext_preset(#{options});"
)
end

# first, not find: with the bug there are two links, and an ambiguous-match
# error would mask whatever the example is actually asserting.
def pick_today
page.first('.filter_date_range a.btn_timerange').click
page.find('.block_timerange .btn_today').click
end

def chosen_range
inputs = filter.all('input[type="text"]')
[inputs.first.value, inputs.last.value]
end

# The auto-init was dead from jQuery 3 until it was restored, so the
# workaround -- calling the plugin by hand in active_admin.js -- is what
# host apps are carrying. Both run after an upgrade.
context 'with no options' do
before { init_again }

it 'leaves one "Set range" link, not one per call' do
expect(filter).to have_css('a.btn_timerange', count: 1)
end

it 'leaves one click handler, not one per call' do
count = page.evaluate_script(
"$._data($('form.filter_form div.filter_date_range')[0], 'events').click.length"
)

expect(count).to eq(1)
end

it 'still fills the inputs' do
pick_today

expect(chosen_range).to eq([today.strftime('%Y-%m-%d'),
(today + 1).strftime('%Y-%m-%d')])
end
end

# Sprockets puts the plugin's own ready callback ahead of anything the host
# appends to active_admin.js, so a guard that kept the first wiring would
# drop the host's configuration without a word.
context 'when the second call passes options' do
before { init_again('{ date_to_human_readable: true }') }

it 'honours them instead of discarding them' do
pick_today

expect(chosen_range).to eq([today.strftime('%Y-%m-%d'),
today.strftime('%Y-%m-%d')])
end
end
end
17 changes: 16 additions & 1 deletion vendor/assets/javascripts/active_admin_date_range_preset.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,21 @@ $(function () {
return this.each(function(i, el) {
let $this = $(el);

// Re-initialising an element replaces its wiring instead of adding to
// it. The auto-init below was dead from jQuery 3 onwards, so hosts
// worked around it by calling the plugin themselves; now that it works
// again both run, and without this the label grows a second "Set range"
// whose handler tears down the first one's popup.
//
// Replacing rather than skipping matters: the auto-init registers its
// ready callback before anything a host adds to active_admin.js, so
// bailing out on the second call would silently drop the host's
// options. Reading the link itself rather than a data flag keeps the
// guard and the thing it guards from drifting apart across clone() and
// Turbo cache restores, which copy the node but not jQuery's data.
$this.find('a.btn_timerange').remove();
$this.off('click.dateRangeExtPreset');

// Per element, because data-show-time is read off the element itself.
// Writing it back into the shared opts let one pair's setting reach
// every other pair in the same call, and formatDate reads the flag at
Expand Down Expand Up @@ -93,7 +108,7 @@ $(function () {
lteq_input.val(formatDate(end, el_opts));
}

$this.on('click', '.btn_timerange', function(e) {
$this.on('click.dateRangeExtPreset', '.btn_timerange', function(e) {
unbindClickEventBlockTimerange();
e.stopPropagation();
e.preventDefault();
Expand Down
Loading