How to style the popup component in v4.28?
In the previous version 4.27, there are a couple of classes to change the styling
.esri-popup__footer
.esri-popup__header
.esri-popup
But these classes are gone? It's not possible to overwrite the popup's styling?
Is there a way to overwrite these? I would like to set display to none
.esri-popup__footer { display: none }
.esri-popup__header { display: none }
Thanks
Solved! Go to Solution.
The short answer is no, you can't override those with page-level CSS like you could previously. The good news is that it's still possible to hide these elements, using the approach mentioned here. The rest (which I apologize for) just adds some depth to the overall topic.
Changes to the Popup in 4.28 were fairly radical due to the Calcite integration that took place in that release. As a result, many of the elements are now in a shadow DOM where they can't be touched by page-level CSS selectors. This was definitely the biggest problem I wrestled with in the upgrade to 4.28. To illustrate, popups in our framework (which supports over 100 unique applications) look something like this in 4.27:
Opinions may vary as to whether that's aesthetically pleasing or not, but it has looked generally the same since the early days of 3.x over 10 years ago, and it matches the theme of the rest of the UI. Like most windows in a GUI, it has a title bar at the top, and this layout favors the content, making it the most important feature of the popup. Tools - which are of lesser importance than the content - are at the bottom, which makes perfect sense.
Enter 4.28, and that all changes to something like this:
So not only did it no longer fit the look-and-feel of our framework, it just isn't laid out well either. Should we then overhaul the entire framework's UI just to match this new-and-improved look and layout? How many hours/weeks/months would that take? Even if we didn't, what about the numerous hours we'd be spending just updating screenshots within our documentation?
Fortunately, I was able to reproduce enough of the pre-4.28 appearance to be acceptable, but not without a lot of hacking. For example, this post mentioned previous shows how to programmatically adjust the styling of elements in a shadow DOM, but it has to be done every time the popup opens. Nonetheless, it works, but I had to add over 100 lines of some of the ugliest code I've written:
var contentFeatureElement = popup.container.querySelector(".esri-features__content-feature");
var headerElement = contentFeatureElement?.shadowRoot.querySelector("calcite-panel")?.shadowRoot?.querySelector("div.header-container");
if (headerElement) {
//Title bar
headerElement.style.backgroundImage = "linear-gradient(#19628C, #114562)";
headerElement.style.backgroundColor = "#19628C";
//The 'x' (close) button and dock buttons at the right-hand side of the title bar
var element, elements = headerElement.querySelectorAll(".header-actions calcite-action");
for (var x = 0; x < elements.length; x++) {
element = elements[x].shadowRoot?.querySelector("button");
if (element) {
element.style.backgroundImage = "linear-gradient(#19628C, #114562)";
element.style.backgroundColor = "#19628C";
element.style.color = "#FFFFFF";
}
}
//The action bar (immediately beneath the title bar)
var calciteActionBar = contentFeatureElement.querySelector("calcite-action-bar");
if (calciteActionBar) {
calciteActionBar.style.backgroundColor = "#EEF7FD";
calciteActionBar.style.borderTop = "1px solid #19628C";
calciteActionBar.style.borderBottom = "1px solid #19628C";
//The individual buttons in the action bar
elements = calciteActionBar.querySelectorAll("calcite-action");
for (var x = 0; x < elements.length; x++) {
element = elements[x].shadowRoot?.querySelector("button");
if (element) {
element.style.backgroundColor = "#EEF7FD";
element.style.outline = "none !important";
}
}
//The button for the "More" menu to the right of the other action buttons (if it exists)
element = calciteActionBar.querySelector("calcite-action-group")?.shadowRoot?.querySelector("calcite-action")?.shadowRoot?.querySelector("button");
if (element)
element.style.backgroundColor = "#EEF7FD";
}
//The section below is for the pagination bar, which only appears when the popup has multiple features; can appear at the top (header) or the bottom (footer)
calciteActionBar = popup.container.querySelector(".esri-features__pagination-action-bar");
if (calciteActionBar) {
calciteActionBar.style.backgroundColor = "#EEF7FD";
//The < (Previous) and > (Next) buttons at the left-hand side of the bar
elements = calciteActionBar.querySelectorAll("calcite-action");
for (var x = 0; x < elements.length; x++) {
element = elements[x].shadowRoot?.querySelector("button");
if (element)
element.style.backgroundColor = "#EEF7FD";
}
//The "Select feature" button at the right-hand side of the bar
element = calciteActionBar.nextElementSibling.shadowRoot?.querySelector("button");
if (element)
element.style.backgroundColor = "#EEF7FD";
}
//Check if the popup is in feature selection mode (i.e. when there are multiple features)
var listItemGroup = popup.container.querySelector("calcite-list-item-group");
if (listItemGroup) {
//The table in which the features are listed
element = listItemGroup.parentNode.shadowRoot?.querySelector("table");
if (element)
element.style.border = "1px solid #BECEDE";
//The title bar of the table
element = listItemGroup.shadowRoot?.querySelector("tr");
if (element) {
element.style.backgroundColor = "#BECEDE";
element.style.color = "#000066";
}
//Individual rows in the table
elements = listItemGroup.querySelectorAll("calcite-list-item");
for (var x = 0; x < elements.length; x++) {
element = elements[x].shadowRoot?.querySelector("tr");
if (element)
element.style.backgroundColor = ((x % 2 === 0) ? "#DFECF7" : "#EEF7FD");
}
var calcitePanel = popup.container.querySelectorAll("calcite-flow-item")[1]?.shadowRoot?.querySelector("calcite-panel");
if (calcitePanel) {
//The title bar
element = calcitePanel.shadowRoot?.querySelector("div.header-container");
if (element) {
element.style.backgroundImage = "linear-gradient(#19628C, #114562)";
element.style.backgroundColor = "#19628C";
element.style.color = "#FFFFFF";
//The title sub-text
element = element.querySelector(".header-content .description");
if (element)
element.style.color = "inherit";
}
//The back button in the title bar
element = calcitePanel.querySelector("calcite-action")?.shadowRoot?.querySelector("button");
if (element) {
element.style.backgroundImage = "linear-gradient(#19628C, #114562)";
element.style.backgroundColor = "#19628C";
element.style.color = "#FFFFFF";
}
}
}
}
In addition, slight modifications were made to our locally-hosted copy of the API to (1) force the action bar to appear below the content, and (2) force the pagination bar to always appear at the bottom whenever it's visible. So we now have something like this instead:
As I mentioned earlier, the pagination controls take up way more room than they need, but it is what it is...
The short answer is no, you can't override those with page-level CSS like you could previously. The good news is that it's still possible to hide these elements, using the approach mentioned here. The rest (which I apologize for) just adds some depth to the overall topic.
Changes to the Popup in 4.28 were fairly radical due to the Calcite integration that took place in that release. As a result, many of the elements are now in a shadow DOM where they can't be touched by page-level CSS selectors. This was definitely the biggest problem I wrestled with in the upgrade to 4.28. To illustrate, popups in our framework (which supports over 100 unique applications) look something like this in 4.27:
Opinions may vary as to whether that's aesthetically pleasing or not, but it has looked generally the same since the early days of 3.x over 10 years ago, and it matches the theme of the rest of the UI. Like most windows in a GUI, it has a title bar at the top, and this layout favors the content, making it the most important feature of the popup. Tools - which are of lesser importance than the content - are at the bottom, which makes perfect sense.
Enter 4.28, and that all changes to something like this:
So not only did it no longer fit the look-and-feel of our framework, it just isn't laid out well either. Should we then overhaul the entire framework's UI just to match this new-and-improved look and layout? How many hours/weeks/months would that take? Even if we didn't, what about the numerous hours we'd be spending just updating screenshots within our documentation?
Fortunately, I was able to reproduce enough of the pre-4.28 appearance to be acceptable, but not without a lot of hacking. For example, this post mentioned previous shows how to programmatically adjust the styling of elements in a shadow DOM, but it has to be done every time the popup opens. Nonetheless, it works, but I had to add over 100 lines of some of the ugliest code I've written:
var contentFeatureElement = popup.container.querySelector(".esri-features__content-feature");
var headerElement = contentFeatureElement?.shadowRoot.querySelector("calcite-panel")?.shadowRoot?.querySelector("div.header-container");
if (headerElement) {
//Title bar
headerElement.style.backgroundImage = "linear-gradient(#19628C, #114562)";
headerElement.style.backgroundColor = "#19628C";
//The 'x' (close) button and dock buttons at the right-hand side of the title bar
var element, elements = headerElement.querySelectorAll(".header-actions calcite-action");
for (var x = 0; x < elements.length; x++) {
element = elements[x].shadowRoot?.querySelector("button");
if (element) {
element.style.backgroundImage = "linear-gradient(#19628C, #114562)";
element.style.backgroundColor = "#19628C";
element.style.color = "#FFFFFF";
}
}
//The action bar (immediately beneath the title bar)
var calciteActionBar = contentFeatureElement.querySelector("calcite-action-bar");
if (calciteActionBar) {
calciteActionBar.style.backgroundColor = "#EEF7FD";
calciteActionBar.style.borderTop = "1px solid #19628C";
calciteActionBar.style.borderBottom = "1px solid #19628C";
//The individual buttons in the action bar
elements = calciteActionBar.querySelectorAll("calcite-action");
for (var x = 0; x < elements.length; x++) {
element = elements[x].shadowRoot?.querySelector("button");
if (element) {
element.style.backgroundColor = "#EEF7FD";
element.style.outline = "none !important";
}
}
//The button for the "More" menu to the right of the other action buttons (if it exists)
element = calciteActionBar.querySelector("calcite-action-group")?.shadowRoot?.querySelector("calcite-action")?.shadowRoot?.querySelector("button");
if (element)
element.style.backgroundColor = "#EEF7FD";
}
//The section below is for the pagination bar, which only appears when the popup has multiple features; can appear at the top (header) or the bottom (footer)
calciteActionBar = popup.container.querySelector(".esri-features__pagination-action-bar");
if (calciteActionBar) {
calciteActionBar.style.backgroundColor = "#EEF7FD";
//The < (Previous) and > (Next) buttons at the left-hand side of the bar
elements = calciteActionBar.querySelectorAll("calcite-action");
for (var x = 0; x < elements.length; x++) {
element = elements[x].shadowRoot?.querySelector("button");
if (element)
element.style.backgroundColor = "#EEF7FD";
}
//The "Select feature" button at the right-hand side of the bar
element = calciteActionBar.nextElementSibling.shadowRoot?.querySelector("button");
if (element)
element.style.backgroundColor = "#EEF7FD";
}
//Check if the popup is in feature selection mode (i.e. when there are multiple features)
var listItemGroup = popup.container.querySelector("calcite-list-item-group");
if (listItemGroup) {
//The table in which the features are listed
element = listItemGroup.parentNode.shadowRoot?.querySelector("table");
if (element)
element.style.border = "1px solid #BECEDE";
//The title bar of the table
element = listItemGroup.shadowRoot?.querySelector("tr");
if (element) {
element.style.backgroundColor = "#BECEDE";
element.style.color = "#000066";
}
//Individual rows in the table
elements = listItemGroup.querySelectorAll("calcite-list-item");
for (var x = 0; x < elements.length; x++) {
element = elements[x].shadowRoot?.querySelector("tr");
if (element)
element.style.backgroundColor = ((x % 2 === 0) ? "#DFECF7" : "#EEF7FD");
}
var calcitePanel = popup.container.querySelectorAll("calcite-flow-item")[1]?.shadowRoot?.querySelector("calcite-panel");
if (calcitePanel) {
//The title bar
element = calcitePanel.shadowRoot?.querySelector("div.header-container");
if (element) {
element.style.backgroundImage = "linear-gradient(#19628C, #114562)";
element.style.backgroundColor = "#19628C";
element.style.color = "#FFFFFF";
//The title sub-text
element = element.querySelector(".header-content .description");
if (element)
element.style.color = "inherit";
}
//The back button in the title bar
element = calcitePanel.querySelector("calcite-action")?.shadowRoot?.querySelector("button");
if (element) {
element.style.backgroundImage = "linear-gradient(#19628C, #114562)";
element.style.backgroundColor = "#19628C";
element.style.color = "#FFFFFF";
}
}
}
}
In addition, slight modifications were made to our locally-hosted copy of the API to (1) force the action bar to appear below the content, and (2) force the pagination bar to always appear at the bottom whenever it's visible. So we now have something like this instead:
As I mentioned earlier, the pagination controls take up way more room than they need, but it is what it is...
Thanks for all the details and code! Any chance you could share the modifications to the API that forced pagination bar to always show up at the bottom?
This behavior can be altered in the file esri/widgets/Popup.js, and fortunately it's very simple. Here are my notes for 4.28: