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
1 change: 0 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
## Checklist

- [ ] PR title follows [Conventional Commits](https://www.conventionalcommits.org/) format
- [ ] A changeset has been added (`pnpm changeset`) for any change to a published package
- [ ] `pnpm build` passes locally
- [ ] No hardcoded credentials, internal URLs, client names, or PII introduced
- [ ] Any new dependency has a GPL-2.0-or-later-compatible license (MIT, BSD, Apache-2.0, ISC are all compatible)
119 changes: 91 additions & 28 deletions packages/commons/src/Blocks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,29 @@ import { getTranslatedOptions } from './APIutils';
import { isSupersetAPI } from "./APIutils";


// Global iframe load throttle — prevents browser ERR_INSUFFICIENT_RESOURCES when many blocks
// are on the same page and all try to load the Vite dev server simultaneously.
const _iframeQueue = [];
let _iframeLoadingCount = 0;
const MAX_CONCURRENT_IFRAMES = 3;

function _requestIframeSlot(callback) {
if (_iframeLoadingCount < MAX_CONCURRENT_IFRAMES) {
_iframeLoadingCount++;
callback();
} else {
_iframeQueue.push(callback);
}
}

function _releaseIframeSlot() {
_iframeLoadingCount = Math.max(0, _iframeLoadingCount - 1);
if (_iframeQueue.length > 0) {
_iframeLoadingCount++;
_iframeQueue.shift()();
}
}

export const SizeConfig = ({ height, setAttributes, panelStatus, initialOpen }) => {
return (<PanelBody initialOpen={panelStatus ? panelStatus["SIZE"] : initialOpen}
onToggle={e => togglePanel("SIZE", panelStatus, setAttributes)}
Expand All @@ -42,15 +65,20 @@ export class ComponentWithSettings extends Component {
react_ui_url: ''
};

window.addEventListener("message", (event) => {
this.iframe = createRef();
this._messageHandler = (event) => {
if (event.data.type === 'componentReady' && event.data.value === true) {
if (this.iframe.current) {
console.log("-----------Sending message -----------");
// Only respond to our own iframe to avoid O(N²) postMessage storms
if (this.iframe.current && event.source === this.iframe.current.contentWindow) {
if (this._hasIframeSlot) {
_releaseIframeSlot();
this._hasIframeSlot = false;
}
this.iframe.current.contentWindow.postMessage(({ messageType: 'component-attributes', ...this.props.attributes }), "*");
}
}
}, false);
this.iframe = createRef();
};
window.addEventListener("message", this._messageHandler, false);
this.unsubscribe = wp.data.subscribe(() => {
const newPreviewMode = wp.data.select("core/editor").getDeviceType();
if (newPreviewMode !== this.state.previewMode) {
Expand All @@ -67,25 +95,43 @@ export class ComponentWithSettings extends Component {

componentDidMount() {
apiFetch({ path: '/dg/v1/settings' }).then((data) => {
this.setState({
react_ui_url: data["react_ui_url"] + '/' + window._page_locale,
const stateWithoutUrl = {
react_api_url: data["react_api_url"],
apache_superset_url: data["apache_superset_url"],
site_language: data["site_language"],
current_language: new URLSearchParams(document.location.search).get("edit_lang"),
landing_page_url: data["landing_page_url"] || ''
}, () => {
if (this.onSettingsLoaded) {
this.onSettingsLoaded();
};
const url = data["react_ui_url"] + '/' + window._page_locale;
// Set non-iframe state immediately, defer the URL that triggers iframe rendering
this.setState(stateWithoutUrl);
this._hasIframeSlot = true;
_requestIframeSlot(() => {
if (!this._unmounted) {
this.setState({ react_ui_url: url }, () => {
if (this.onSettingsLoaded) {
this.onSettingsLoaded();
}
});
} else {
_releaseIframeSlot();
this._hasIframeSlot = false;
}
});
});
}

componentWillUnmount() {
this._unmounted = true;
if (this.unsubscribe) {
this.unsubscribe();
}
window.removeEventListener('message', this._messageHandler);
// Release any held slot so the queue doesn't stall
if (this._hasIframeSlot) {
_releaseIframeSlot();
this._hasIframeSlot = false;
}
}

renderWordpressSource() {
Expand Down Expand Up @@ -564,39 +610,56 @@ export class BlockEditWithAPIMetadata extends ComponentWithSettings {
label: 'CSV', value: 'csv'
}];

this.setState({
react_ui_url: settingsData["react_ui_url"] + '/' + window._page_locale,
const url = settingsData["react_ui_url"] + '/' + window._page_locale;
const baseState = {
react_api_url: settingsData["react_api_url"],
apache_superset_url: settingsData["apache_superset_url"],
site_language: settingsData["site_language"],
current_language: new URLSearchParams(document.location.search).get("edit_lang"),
apps
}, () => {

const { app, dvzProxyDatasetId } = this.props.attributes;

if (isSupersetAPI(app, this.state.apps)) {
this.loadDatasets(app);
}

if (app && app != 'none') {
this.loadMetadata(app, dvzProxyDatasetId);
};
this.setState(baseState);
this._hasIframeSlot = true;
_requestIframeSlot(() => {
if (!this._unmounted) {
this.setState({ react_ui_url: url }, () => {
const { app, dvzProxyDatasetId } = this.props.attributes;
if (isSupersetAPI(app, this.state.apps)) {
this.loadDatasets(app);
}
if (app && app != 'none') {
this.loadMetadata(app, dvzProxyDatasetId);
}
});
} else {
_releaseIframeSlot();
this._hasIframeSlot = false;
}
});
})
.catch((error) => {
console.error("Error when loading apps, falling back to CSV", error);
this.setState({
react_ui_url: settingsData["react_ui_url"] + '/' + window._page_locale,
const url = settingsData["react_ui_url"] + '/' + window._page_locale;
const baseState = {
react_api_url: settingsData["react_api_url"],
apache_superset_url: settingsData["apache_superset_url"],
site_language: settingsData["site_language"],
current_language: new URLSearchParams(document.location.search).get("edit_lang"),
apps: [{ label: 'CSV', value: 'csv' }]
}, () => {
const { app, dvzProxyDatasetId } = this.props.attributes;
if (app && app != 'none') {
this.loadMetadata(app, dvzProxyDatasetId);
};
this.setState(baseState);
this._hasIframeSlot = true;
_requestIframeSlot(() => {
if (!this._unmounted) {
this.setState({ react_ui_url: url }, () => {
const { app, dvzProxyDatasetId } = this.props.attributes;
if (app && app != 'none') {
this.loadMetadata(app, dvzProxyDatasetId);
}
});
} else {
_releaseIframeSlot();
this._hasIframeSlot = false;
}
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ class BlockEdit extends BlockEditWithAPIMetadata {
super.componentDidMount()
}

componentDidUpdate(prevProps, prevState, snapshot) {
super.componentDidUpdate(prevProps, prevState, snapshot);
// The base class sends raw WP attributes, but measures/format/filters/percentChangeFormat
// are Array/Object types that the embeddable expects as JSON strings. Send a correction.
if (this.iframe && this.iframe.current) {
const { measures, format, filters, percentChangeFormat } = this.props.attributes;
this.iframe.current.contentWindow.postMessage({
messageType: 'component-attributes',
measures: JSON.stringify(measures),
format: JSON.stringify(format),
filters: JSON.stringify(filters),
percentChangeFormat: JSON.stringify(percentChangeFormat),
}, '*');
}
}

render() {
const {
className, isSelected,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class BlockEdit extends BlockEditWithAPIMetadata {
} = this.props;


const queryString = `data-group=${group}&data-app=${app}&data-reset-label=${resetLabel}`
const iframeStyles = {height: '30px'}

return ([isSelected && (<InspectorControls>
Expand Down
33 changes: 13 additions & 20 deletions plugins/wp-react-blocks-plugin/blocks/featured-tabs/BlockEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,10 @@ class BlockEdit extends BlockEditWithFilters {
}

onChangeColor(i, value) {
const {
setAttributes,
attributes: {
colors
},
} = this.props;

const newColors = Object.assign({}, colors);
newColors["color_" + i] = value;

setAttributes({ "colors": newColors });
const { setAttributes, attributes: { color } } = this.props;
const parts = (color || '').split(',');
parts[i] = value || '#FFFF';
setAttributes({ color: parts.join(',') });
}

componentWillUnmount() {
Expand All @@ -30,6 +23,7 @@ class BlockEdit extends BlockEditWithFilters {
}

componentDidUpdate(prevProps, prevState, snapshot) {
super.componentDidUpdate(prevProps, prevState, snapshot);
const newPreviewMode = this.state?.previewMode;
if (newPreviewMode !== prevState.previewMode) {
this.props.setAttributes({ previewMode: newPreviewMode });
Expand All @@ -42,21 +36,19 @@ class BlockEdit extends BlockEditWithFilters {
toggleSelection,
setAttributes,
attributes: {
count,
items,
type,
taxonomy,
categories,
height,
colors,
color,
useScrolls,
readMoreLabel,
closeLabel,
previewMode
},
} = this.props;

const colorsParams = Object.keys(colors).map(k => colors[k]).join(",");
const queryString = `editing=true&data-type=${type}&data-taxonomy=${taxonomy}&data-categories=${categories}&data-items=${count}&data-height=${height}&data-color=${encodeURIComponent(colorsParams)}&data-read-more-label=${readMoreLabel}&data-close-label=${encodeURIComponent(closeLabel || 'Close')}&data-use-scrolls=${useScrolls}&data-preview-mode=${previewMode}`;
const divStyles = { height: `${height}px`, width: "100%" };

return (
Expand All @@ -67,8 +59,8 @@ class BlockEdit extends BlockEditWithFilters {
<PanelRow>
<RangeControl
label={__("Items", "dg")}
value={count}
onChange={(count) => setAttributes({ count })}
value={items}
onChange={(items) => setAttributes({ items })}
min={2}
max={10}
/>
Expand Down Expand Up @@ -118,13 +110,13 @@ class BlockEdit extends BlockEditWithFilters {
{this.renderFilters()}

<PanelBody title={__("Colors")}>
{new Array(count).fill(1).map((v, i) =>
{new Array(items).fill(1).map((v, i) =>
<PanelRow key={i}>
<PanelColorSettings
title={__(`Color Settings ${i + 1}`)}
colorSettings={[
{
value: colors[`color_${i}`],
value: (color || '').split(',')[i],
onChange: (colorValue) => this.onChangeColor(i, colorValue),
label: __('Background Color'),
}
Expand Down Expand Up @@ -164,7 +156,8 @@ class BlockEdit extends BlockEditWithFilters {
>
<div style={divStyles}>
{this.state.react_ui_url && <iframe style={divStyles} scrolling={"no"}
src={this.state.react_ui_url + "/embeddable/featuredtabs?" + queryString}/>}
ref={this.iframe}
src={this.state.react_ui_url + "/embeddable/featuredtabs"}/>}

</div>
</ResizableBox>
Expand Down
16 changes: 6 additions & 10 deletions plugins/wp-react-blocks-plugin/blocks/featured-tabs/BlockSave.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
const SaveComponent = (props) => {
const {
attributes: {
count,
items,
type,
taxonomy,
categories,
height,
colors,
color,
useScrolls,
readMoreLabel,
closeLabel,
previewMode
},
} = props;

const divClass = {};
const divStyles = {
height: `${height}px`, // Set the height style
height: `${height}px`,
};

const colorsParams = Object.keys(colors).map(k => colors[k]).join(",");

return (
<div className={divClass} style={divStyles}>
<div style={divStyles}>
<div
data-items={count}
data-items={items}
data-height={height}
data-color={colorsParams}
data-color={color}
data-type={type}
data-taxonomy={taxonomy}
data-categories={categories.toString()}
Expand All @@ -42,4 +39,3 @@ const SaveComponent = (props) => {
}

export default SaveComponent;

Loading
Loading