diff --git a/instance_selector/static/instance_selector/instance_selector.css b/instance_selector/static/instance_selector/instance_selector.css index b1def01..755fef0 100644 --- a/instance_selector/static/instance_selector/instance_selector.css +++ b/instance_selector/static/instance_selector/instance_selector.css @@ -6,11 +6,6 @@ display: none; } -/* Hide the add button */ -.is-instance-selector-embed .w-header .right { - display: none; -} - /* Override wagtail styles [smaller screens] to make sure the title appears in line with the rows */ @media (max-width: 50em) { .is-instance-selector-embed .w-header.w-header--with-padding, diff --git a/instance_selector/static/instance_selector/instance_selector_embed.js b/instance_selector/static/instance_selector/instance_selector_embed.js index b10d064..9936d47 100644 --- a/instance_selector/static/instance_selector/instance_selector_embed.js +++ b/instance_selector/static/instance_selector/instance_selector_embed.js @@ -4,10 +4,20 @@ ? window.parent.location.pathname.indexOf('/instance-selector/') !== -1 : false; - const SESSION_STORAGE_EMBED_KEY = 'INSTANCE_SELECTOR_EMBED_ID'; + const SESSION_STORAGE_EMBED_KEY_PREFIX = 'INSTANCE_SELECTOR_EMBED_ID'; if (IS_INSTANCE_SELECTOR_EMBED) { const HASH_EMBED_ID = window.location.hash.split('#instance_selector_embed_id:')[1]; + + if (HASH_EMBED_ID && !window.name) { + // Give this specific frame a stable, unique identity so its + // sessionStorage key can't collide with any other (nested or + // sibling) instance-selector frame in the same tab. + window.name = SESSION_STORAGE_EMBED_KEY_PREFIX + ':' + HASH_EMBED_ID + ':' + Math.random().toString(36).slice(2); + } + + const SESSION_STORAGE_EMBED_KEY = SESSION_STORAGE_EMBED_KEY_PREFIX + ':' + (window.name || 'default'); + // Persist embed id across page loads (allows clicking on filters, searching, etc) const SESSION_EMBED_ID = sessionStorage.getItem(SESSION_STORAGE_EMBED_KEY); if (HASH_EMBED_ID) { @@ -56,7 +66,7 @@ const success_messages = $('.messages .success'); success_messages.each(function() { const success_message = $(this); - + const buttons = success_message.find('.buttons a'); buttons.each(function() { const button = $(this); @@ -66,7 +76,7 @@ const data = get_data_from_url(url); if (data) { const select_button = $(` - Select diff --git a/instance_selector/static/instance_selector/instance_selector_widget.js b/instance_selector/static/instance_selector/instance_selector_widget.js index a3f856e..ed4878a 100644 --- a/instance_selector/static/instance_selector/instance_selector_widget.js +++ b/instance_selector/static/instance_selector/instance_selector_widget.js @@ -33,6 +33,21 @@ function create_instance_selector_widget(opts) { opts.embed_url = opts.embed_url.replace(/__prefix__/g, index); } const widget_root = $('#' + opts.widget_id); + + // Inside StreamField blocks this function is called twice for the same + // widget: once directly by the telepath widget definition + // (instance_selector_telepath.js, which needs the widget API back + // synchronously) and again by the Stimulus `instance-selector` + // controller's connect(), which fires because the widget's root element + // also carries `data-controller="instance-selector"`. Without a guard, + // both calls bind their own click handler on the trigger button, so a + // single click opens two stacked modal dialogs. Cache the widget API on + // the root element so the second call is a no-op. + const existing_widget_api = widget_root.data('instanceSelectorWidgetApi'); + if (existing_widget_api) { + return existing_widget_api; + } + const field_input = $('#' + opts.input_id); const display_edit_link = widget_root.find('.js-instance-selector-widget-display-edit-link'); const display_markup_wrap = widget_root.find('.js-instance-selector-widget-display-wrap'); @@ -68,7 +83,7 @@ function create_instance_selector_widget(opts) { Close @@ -173,5 +188,6 @@ function create_instance_selector_widget(opts) { trigger_button.focus(); }, } + widget_root.data('instanceSelectorWidgetApi', widget_api); return widget_api; }