TL;DR: Components rendered in a Confluence Macro’s configuration pane will re-render themselves on state change but none of the rendering changes will appear in the UI.
I have developed a Confluence macro that needs configuration, however certain elements of the configuration are dependent on other elements of the configuration.
Taking a hypothetical example, imagine a macro that wants to follow information about some set of city council members for a given city. The configuration for this macro might consist of two properties, a string representing the city and a list of council members that the user has selected from a Select dropdown configured for multi-select.
In this scenario, the Select component containing a list of city council members can not be populated until the city is known. The workflow on the configuration pane is to present a text box where the user enters a city name, and then the application fetches the list of council members
A minimal sample follows building from the UI Kit Hello World example, and adding the Config.
import React, { useEffect, useState } from 'react';
import ForgeReconciler, { Text, Textfield, Select } from '@forge/react';
import { invoke, view } from '@forge/bridge';
const Tag = "macro-config-example: ";
function log(message) {
console.log(Tag + message);
}
const mockCouncilMembers = [
{ label: "John Perry", value: "1" },
{ label: "Harry Wilson", value: "2" },
{ label: "Jane Sagan", value: "3" },
];
function getCouncilMembers(city) {
/*
* In a real application this would make an api call to fetch council members for
* a given city.
*/
if (city == "") {
log("No city no council");
return [];
} else {
log("City council for: " + city);
return mockCouncilMembers;
}
}
function CouncilMembers({city}) {
const [councilMembers, setCouncilMembers] = useState([]);
useEffect(() => {
var cm = getCouncilMembers(city);
setCouncilMembers(cm);
}, [city]);
if (city != "") {
log(city + " council members: " + JSON.stringify(councilMembers));
}
return (
<>
{ <Select
isMulti
inputId="selected-councilpeople"
placeholder="Select desired council members..."
name="council"
label="Council Members"
options={councilMembers}/> }
</>
)
}
const Config = () => {
const [city, setCity] = useState("");
useEffect(() => {
view.getContext().then((ctx) => {
if (ctx?.extension.config?.city != undefined) {
setCity(ctx.extension.config.city);
}
})
}, []);
log("City: " + city);
return (
<>
<Textfield name="city" label="City" />
<CouncilMembers city={city}/>
</>
);
};
const App = () => {
const [data, setData] = useState(null);
useEffect(() => {
invoke('getText', { example: 'my-invoke-variable' }).then(setData);
}, []);
return (
<>
<Text>Hello world!</Text>
<Text>{data ? data : 'Loading...'}</Text>
</>
);
};
ForgeReconciler.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
ForgeReconciler.addConfig(<Config />);
Upon adding the macro to a confluence page, we see in the log that Config and CouncilMembers were rendered and there is no ‘city’ property as the macro has not been configured yet.
From console logs:
16:12:24.890 macro-config-example: City:
16:12:24.891 macro-config-example: No city no council
After entering ‘San Francisco’ into the text field and pressing tab we can see that the components are rendered two more times.
16:12:24.890 macro-config-example: City:
16:12:24.891 macro-config-example: No city no council
16:25:22.410 macro-config-example: City:
16:25:22.412 macro-config-example: No city no council
16:25:22.578 macro-config-example: City: San Francisco
16:25:22.578 macro-config-example: San Francisco council members: []
16:25:22.580 macro-config-example: City council for: San Francisco
16:25:22.580 macro-config-example: San Francisco council members: [{"label":"John Perry","value":"1"},{"label":"Harry Wilson","value":"2"},{"label":"Jane Sagan","value":"3"}]
Once when ‘city’ changed from the empty string to ‘San Francisco’, and again, when the view.getContext() promise resolved and the config was updated in the context.
Despite the component being re-rendered, the options do not appear in the dropdown.
If we close the configuration pane, and re-open the pane the options will appear. Note that even though the options now appear on reopening, there are no additional log messages indicating that the components were re-rendered.
This is likely to cause great confusion among users of a macro that needs this ability as they enter a city and don’t see the Council Members dropdown populated. But then may come back later and see it populated and not know why or how.