Compare commits

...

6 Commits

Author SHA1 Message Date
Garrick Aden-Buie
42b6b8bdde chore: yarn build 2025-01-22 16:41:47 -05:00
Garrick Aden-Buie
4a01dde46b fix: Only call renderContentAsync on element nodes 2025-01-22 16:41:04 -05:00
Garrick Aden-Buie
6c281f377c chore: yarn build 2025-01-22 13:44:37 -05:00
Garrick Aden-Buie
33d6686223 refactor: Don't check binding validity if scope isn't an element 2025-01-22 13:44:26 -05:00
gadenbuie
20b2669e76 yarn build (GitHub Actions) 2025-01-14 18:34:51 +00:00
Garrick Aden-Buie
db123a1508 fix: Fix checking if scope is a jquery element
Fixes rstudio/bslib#1159
2025-01-14 13:30:39 -05:00
6 changed files with 61 additions and 27 deletions

View File

@@ -20604,6 +20604,9 @@
if (Array.isArray(arr))
return arr;
}
function isJQuery(value) {
return Object.prototype.hasOwnProperty.call(value, "jquery") && typeof value.jquery === "string";
}
function valueChangeCallback(inputs, binding, el, allowDeferred) {
var id = binding.getId(el);
if (id) {
@@ -20622,6 +20625,9 @@
var bindingsRegistry = function() {
var bindings = /* @__PURE__ */ new Map();
function checkValidity(scope) {
if (!isJQuery(scope) && !(scope instanceof HTMLElement)) {
return;
}
var duplicateIds = /* @__PURE__ */ new Map();
var problems = /* @__PURE__ */ new Set();
bindings.forEach(function(idTypes, id) {
@@ -20672,7 +20678,7 @@
headline: headline,
message: message
});
var scopeElement = scope instanceof HTMLElement ? scope : scope.get(0);
var scopeElement = isJQuery(scope) ? scope.get(0) : scope;
(scopeElement || window).dispatchEvent(event);
}
function addBinding(id, bindingType) {
@@ -24642,36 +24648,40 @@
_iterator3.s();
case 27:
if ((_step3 = _iterator3.n()).done) {
_context15.next = 34;
_context15.next = 35;
break;
}
el = _step3.value;
$tabContent[0].appendChild(el);
_context15.next = 32;
if (!(el.nodeType === Node.ELEMENT_NODE)) {
_context15.next = 33;
break;
}
_context15.next = 33;
return renderContentAsync(el, el.innerHTML || el.textContent);
case 32:
case 33:
_context15.next = 27;
break;
case 34:
_context15.next = 39;
case 35:
_context15.next = 40;
break;
case 36:
_context15.prev = 36;
case 37:
_context15.prev = 37;
_context15.t0 = _context15["catch"](25);
_iterator3.e(_context15.t0);
case 39:
_context15.prev = 39;
case 40:
_context15.prev = 40;
_iterator3.f();
return _context15.finish(39);
case 42:
return _context15.finish(40);
case 43:
if (message.select) {
$liTag.find("a").tab("show");
}
case 43:
case 44:
case "end":
return _context15.stop();
}
}, _callee15, null, [[25, 36, 39, 42]]);
}, _callee15, null, [[25, 37, 40, 43]]);
}));
return function(_x17) {
return _ref10.apply(this, arguments);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,6 +13,18 @@ import { sendImageSizeFns } from "./sendImageSize";
type BindScope = HTMLElement | JQuery<HTMLElement>;
/**
* Type guard to check if a value is a jQuery object containing HTMLElements
* @param value The value to check
* @returns A type predicate indicating if the value is a jQuery<HTMLElement>
*/
function isJQuery<T = HTMLElement>(value: unknown): value is JQuery<T> {
return (
Object.prototype.hasOwnProperty.call(value, "jquery") &&
typeof (value as any).jquery === "string"
);
}
// todo make sure allowDeferred can NOT be supplied and still work
function valueChangeCallback(
inputs: InputValidateDecorator,
@@ -79,6 +91,10 @@ const bindingsRegistry = (() => {
* otherwise returns an ok status.
*/
function checkValidity(scope: BindScope): void {
if (!isJQuery(scope) && !(scope instanceof HTMLElement)) {
return;
}
type BindingCounts = { [T in BindingTypes]: number };
const duplicateIds = new Map<string, BindingCounts>();
const problems: Set<string> = new Set();
@@ -146,7 +162,7 @@ const bindingsRegistry = (() => {
}:\n${duplicateIdMsg}`;
const event = new ShinyClientMessageEvent({ headline, message });
const scopeElement = scope instanceof HTMLElement ? scope : scope.get(0);
const scopeElement = isJQuery(scope) ? scope.get(0) : scope;
(scopeElement || window).dispatchEvent(event);
}

View File

@@ -1053,6 +1053,12 @@ class ShinyApp {
const $tabContent = getTabContent($tabset);
let tabsetId = $parentTabset.attr("data-tabsetid");
// TODO: Only render tab content HTML once
// The following lines turn the content/nav control HTML into DOM nodes,
// but we don't insert these directly, instead we take the HTML from
// these nodes and pass it through `renderContentAsync()`. This means
// the inserted HTML may not perfectly match the message HTML, esp. if
// the content uses web components that alter their HTML when loaded.
const $divTag = $(message.divTag.html);
const $liTag = $(message.liTag.html);
const $aTag = $liTag.find("> a");
@@ -1166,12 +1172,14 @@ class ShinyApp {
// Must not use jQuery for appending el to the doc, we don't want any
// scripts to run (since they will run when renderContent takes a crack).
$tabContent[0].appendChild(el);
// If `el` itself is a script tag, this approach won't work (the script
// won't be run), since we're only sending innerHTML through renderContent
// and not the whole tag. That's fine in this case because we control the
// R code that generates this HTML, and we know that the element is not
// a script tag.
await renderContentAsync(el, el.innerHTML || el.textContent);
if (el.nodeType === Node.ELEMENT_NODE) {
// If `el` itself is a script tag, this approach won't work (the script
// won't be run), since we're only sending innerHTML through renderContent
// and not the whole tag. That's fine in this case because we control the
// R code that generates this HTML, and we know that the element is not
// a script tag.
await renderContentAsync(el, el.innerHTML || el.textContent);
}
}
if (message.select) {