From b804faf35da95d12e33f8a4919a3cfde77ef458d Mon Sep 17 00:00:00 2001 From: yvonnesjy Date: Thu, 6 Jun 2024 16:55:25 -0700 Subject: [PATCH 01/10] Portal Theme: Remove semantic color variables 1. Replace current blue- and grey- color variables with primary- and neutral- 2. Remove all semantic color variables I created in the light theme update. Replace them with the primary- and neutral- variables that they reference. This PR is a part of the plan for new color system. design doc in progress: https://docs.google.com/document/d/1Eb0uixayVeEgO0AcIEjDO6J5555A_CA1BK0nZp1s-uk/edit?usp=sharing We're doing this refactoring first because it will make the legend work a lot simpler. --- src/css/map-view.css | 342 +++++++++++---------------- src/css/portal-layouts/panels.css | 12 +- src/css/portal-themes/light.css | 103 +++----- src/js/views/maps/FeatureInfoView.js | 2 +- 4 files changed, 172 insertions(+), 287 deletions(-) diff --git a/src/css/map-view.css b/src/css/map-view.css index 0a096ba76..ff6011c56 100644 --- a/src/css/map-view.css +++ b/src/css/map-view.css @@ -27,75 +27,6 @@ :root { /* COLOURS */ - --map-col-content-bkg: var(--portal-col-content-bkg); - --map-col-content-bkg-highlight: var(--portal-col-content-bkg-highlight); - --map-col-content-bkg-muted: var(--portal-col-content-bkg-muted); - --map-col-content-bkg-active-highlight: var( - --portal-col-content-bkg-active-highlight - ); - --map-col-content-bkg-hover-highlight: var( - --portal-col-content-bkg-hover-highlight - ); - --map-col-content-bkg-transparent: var(--portal-col-content-bkg-transparent); - - --map-col-buttons-bkg: var(--portal-col-buttons-bkg); - --map-col-buttons-bkg-active: var(--portal-col-content-bkg-highlight); - --map-col-buttons-bkg-hover: var(--portal-col-buttons-bkg-hover); - --map-col-buttons-bkg-highlight: var(--portal-col-buttons-bkg-highlight); - --map-col-buttons-bkg-muted: var(--portal-col-buttons-bkg-muted); - --map-col-buttons-bkg-white-content-contrast: var( - --portal-col-buttons-bkg-white-content-contrast - ); - --map-col-buttons-text: var(--portal-col-buttons-text); - --map-col-buttons-text-highlight: var(--portal-col-buttons-text-highlight); - --map-col-buttons-icon: var(--portal-col-buttons-icon); - --map-col-buttons-icon-highlight: var(--portal-col-buttons-icon-highlight); - --map-col-buttons-icon-muted: var(--portal-col-buttons-icon-muted); - --map-col-utility-buttons: var(--portal-col-utility-buttons); - --map-col-buttons-icon-active-highlight: var( - --portal-col-buttons-icon-active-highlight - ); - --map-col-buttons-icon-hover-highlight: var( - --portal-col-buttons-icon-hover-highlight - ); - - --map-col-border: var(--portal-col-border); - --map-col-border-highlight: var(--portal-col-border-highlight); - --map-col-border-muted: var(--portal-col-border-muted); - --map-col-border-highlight-search-box: var( - --portal-col-border-highlight-search-box - ); - --map-col-border-panel: var(--portal-col-border-panel); - - --map-col-section-divider: var(--portal-col-section-divider); - --map-col-item-divider: var(--portal-col-item-divider); - - --map-col-text-body: var(--portal-col-text-body); - --map-col-text-highlight: var(--portal-col-text-highlight); - --map-col-text-title: var(--portal-col-text-title); - --map-col-text-label: var(--portal-col-text-label); - --map-col-text-detail: var(--portal-col-text-detail); - --map-col-text-muted: var(--portal-col-text-muted); - --map-col-text-active-highlight: var(--portal-col-text-active-highlight); - --map-col-text-hover-highlight: var(--portal-col-text-hover-highlight); - - --map-col-opacity-slider-active: var(--portal-col-opacity-slider-active); - --map-col-opacity-slider-inactive: var(--portal-col-opacity-slider-inactive); - - --map-col-input-error-bkg: var( - --portal-col-input-error-bkg, - rgba(30, 127, 196, 0.5) - ); - --map-col-input-error-border: var( - --portal-col-input-error-border, - rgba(82, 168, 236, 0.8) - ); - --map-col-input-error-text: var(--portal-col-input-error-text, #ca6c0b); - --map-col-search-match-highlight: var( - --portal-col-search-match-highlight, - rgba(30, 127, 196, 0.5) - ); - --map-no-brightness-or-opacity-tweaks: var( --portal-no-brightness-or-opacity-tweaks ); @@ -107,9 +38,6 @@ --map-col-yellow: #fda; --map-col-text-on-yellow: #1b2538; - --map-col-tooltip-background: var(--portal-col-tooltip-background); - --map-col-tooltip-text: var(--portal-col-tooltip-text); - --map-col-bkg__deprecate: #111827; --map-col-bkg-lighter__deprecate: #1b2538; --map-col-bkg-lightest__deprecate: #242e42; @@ -164,7 +92,7 @@ /* ---- TOOLTIP ---- */ .map-tooltip { - color: var(--map-col-tooltip-text, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-00, var(--map-col-text__deprecate)); border-radius: var(--map-border-radius-big); opacity: var(--map-no-brightness-or-opacity-tweaks, 1) !important; font-size: 0.8rem; @@ -173,28 +101,28 @@ .map-tooltip.top .tooltip-arrow { border-top-color: var( - --map-col-tooltip-background, + --portal-col-neutral-7, var(--map-col-buttons__deprecate) ); } .map-tooltip.right .tooltip-arrow { border-right-color: var( - --map-col-tooltip-background, + --portal-col-neutral-7, var(--map-col-buttons__deprecate) ); } .map-tooltip.left .tooltip-arrow { border-left-color: var( - --map-col-tooltip-background, + --portal-col-neutral-7, var(--map-col-buttons__deprecate) ); } .map-tooltip.bottom .tooltip-arrow { border-bottom-color: var( - --map-col-tooltip-background, + --portal-col-neutral-7, var(--map-col-buttons__deprecate) ); } @@ -202,10 +130,10 @@ .map-tooltip .tooltip-inner { padding: 0.3rem; background-color: var( - --map-col-tooltip-background, + --portal-col-neutral-7, var(--map-col-buttons__deprecate) ); - color: var(--map-col-tooltip-text); + color: var(--portal-col-neutral-00); } /* ---- BUTTON ---- */ @@ -221,8 +149,8 @@ -webkit-appearance: button; border: 0; border-radius: var(--map-border-radius-big); - background: var(--map-col-buttons-bkg, var(--map-col-buttons__deprecate)); - color: var(--map-col-buttons-text, var(--map-col-text__deprecate)); + background: var(--portal-col-neutral-00, var(--map-col-buttons__deprecate)); + color: var(--portal-col-primary-6, var(--map-col-text__deprecate)); white-space: nowrap; text-decoration: none; padding: 0.25rem 0.5rem; @@ -235,20 +163,20 @@ .map-view__button--emphasis { background-color: var( - --map-col-buttons-bkg, + --portal-col-neutral-00, var(--map-col-buttons-emphasis__deprecate) ); padding: 0.5rem 0.55rem; font-size: 1rem; - border: 1px solid var(--map-col-border); + border: 1px solid var(--portal-col-primary-2); } .map-view__button--active { background-color: var( - --map-col-buttons-bkg-highlight, + --portal-col-primary-6, var(--map-col-highlight__deprecate) ); - color: var(--map-col-buttons-text-highlight); + color: var(--portal-col-neutral-00); } /* ---- BADGE ---- */ @@ -258,10 +186,10 @@ margin: 0 0.25rem; font-size: 0.66rem; background-color: var( - --map-col-content-bkg-muted, + --portal-col-neutral-1, var(--map-col-bkg-lighter__deprecate) ); - color: var(--map-col-text-muted, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-5, var(--map-col-text__deprecate)); border-radius: var(--map-border-radius-small); line-height: 1; font-weight: 500; @@ -285,8 +213,8 @@ } .map-view__badge--contrast { - background-color: var(--map-col-text-label, var(--map-col-text__deprecate)); - color: var(--map-col-content-bkg, var(--map-col-bkg__deprecate)); + background-color: var(--portal-col-neutral-7, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-00, var(--map-col-bkg__deprecate)); opacity: var(--map-no-brightness-or-opacity-tweaks, 0.8); } @@ -408,7 +336,7 @@ .feature-info__zoom-button, .feature-info__layer-details-button { - border: 1px solid var(--map-col-border); + border: 1px solid var(--portal-col-primary-2); border-radius: var(--map-border-radius-small); padding: 0.5rem 1rem; text-wrap: wrap; @@ -461,9 +389,9 @@ min-width: 12rem; min-height: 2rem; box-shadow: var(--map-shadow-md); - background: var(--map-col-content-bkg-muted, var(--map-col-bkg__deprecate)); + background: var(--portal-col-neutral-1, var(--map-col-bkg__deprecate)); border-radius: var(--map-border-radius-small); - color: var(--map-col-text-muted, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-5, var(--map-col-text__deprecate)); font-size: 0.8rem; padding: 0.18rem 0.8rem; display: grid; @@ -506,7 +434,7 @@ represents 1 unit of the given distance measurement. */ height: 0.25rem; transition: width 0.3s ease-in-out; background-color: var( - --map-col-buttons-icon-muted, + --portal-col-neutral-4, var(--map-col-text__deprecate) ); } @@ -527,7 +455,7 @@ represents 1 unit of the given distance measurement. */ .toolbar { display: grid; grid-auto-flow: column; - color: var(--map-col-content-bkg, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-00, var(--map-col-text__deprecate)); } .toolbar__links { @@ -535,7 +463,7 @@ represents 1 unit of the given distance measurement. */ flex-direction: column; height: min-content; background-color: var( - --map-col-content-bkg-highlight, + --portal-col-primary-1, var(--map-col-bkg__deprecate) ); border-radius: var(--map-border-radius-big); @@ -543,12 +471,12 @@ represents 1 unit of the given distance measurement. */ padding: 0.5rem; row-gap: 0.5rem; pointer-events: all; - color: var(--map-col-buttons-bkg-highlight); + color: var(--portal-col-primary-6); box-shadow: var(--map-shadow-md); } .toolbar__all-content { - background-color: var(--map-col-content-bkg, var(--map-col-bkg__deprecate)); + background-color: var(--portal-col-neutral-00, var(--map-col-bkg__deprecate)); width: var(--map-width-toolbar); box-shadow: var(--map-shadow-md); flex-direction: column; @@ -580,7 +508,7 @@ represents 1 unit of the given distance measurement. */ overflow: auto; .toolbar__content-header { - color: var(--map-col-text-title); + color: var(--portal-col-primary-7); font-size: 1.5rem; font-weight: 700; line-height: 2rem; @@ -597,30 +525,30 @@ represents 1 unit of the given distance measurement. */ cursor: pointer; height: var(--map-size-toolbar-link); width: var(--map-size-toolbar-link); - background-color: var(--map-col-content-bkg, var(--map-col-bkg__deprecate)); + background-color: var(--portal-col-neutral-00, var(--map-col-bkg__deprecate)); padding: 0.5rem; border-radius: var(--map-border-radius-small); - border: 1px solid var(--map-col-border); + border: 1px solid var(--portal-col-primary-2); } .toolbar__link:hover { background-color: var( - --map-col-buttons-bkg-hover, + --portal-col-primary-2, var(--map-col-bkg-lighter__deprecate) ); } .toolbar__link--active { background-color: var( - --map-col-buttons-bkg-highlight, + --portal-col-primary-6, var(--map-col-highlight__deprecate) ); - color: var(--map-col-buttons-icon-highlight); + color: var(--portal-col-primary-3); } .toolbar__link--active:hover { background-color: var( - --map-col-buttons-bkg-highlight, + --portal-col-primary-6, var(--map-col-highlight__deprecate) ); } @@ -629,7 +557,7 @@ represents 1 unit of the given distance measurement. */ position: absolute; left: calc(100% + 0.5rem); top: 0.25rem; - color: var(--map-col-tooltip-text, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-00, var(--map-col-text__deprecate)); font-size: 0.75rem; font-weight: 700; /* don't show the link title until the link is hovered */ @@ -645,7 +573,7 @@ represents 1 unit of the given distance measurement. */ top: 50%; transform: translate(50%, -50%) rotate(-45deg); background-color: var( - --map-col-tooltip-background, + --portal-col-neutral-7, var(--map-col-buttons__deprecate) ); } @@ -655,7 +583,7 @@ represents 1 unit of the given distance measurement. */ padding: 0.25rem 0.5rem; border-radius: var(--map-border-radius-small); background-color: var( - --map-col-tooltip-background, + --portal-col-neutral-7, var(--map-col-buttons__deprecate) ); } @@ -678,7 +606,7 @@ represents 1 unit of the given distance measurement. */ .search-input { padding-bottom: 1rem; - background-color: var(--map-col-content-bkg, var(--map-col-bkg)); + background-color: var(--portal-col-neutral-00, var(--map-col-bkg)); .search-input__field { position: relative; @@ -688,7 +616,7 @@ represents 1 unit of the given distance measurement. */ height: 2.5rem; margin-bottom: 0.5rem; border: 1px solid; - border-color: var(--map-col-border-muted); + border-color: var(--portal-col-neutral-4); border-radius: var(--map-border-radius-small); .search-input__input { @@ -700,27 +628,30 @@ represents 1 unit of the given distance measurement. */ margin: unset; padding-left: 1rem; background: none; - color: var(--map-col-text-body, var(--map-col-text)); + color: var(--portal-col-neutral-8, var(--map-col-text)); font-family: var(--map-body-font); border: none; box-shadow: unset; ::placeholder { - color: var(--map-col-text-muted); + color: var(--portal-col-neutral-5); } } } &:focus-within { border-color: var( - --map-col-border-highlight-search-box, + --portal-col-primary-3, var(--map-col-blue) ); } &.search-input__error-input { - border-color: var(--map-col-input-error-border); - background-color: var(--map-col-input-error-bkg); + border-color: var( + --portal-col-highlight-2, + rgba(82, 168, 236, 0.8) + ); + background-color: var(--portal-col-highlight-1, rgba(30, 127, 196, 0.5)); } .search-input__cancel-button-container { @@ -750,23 +681,23 @@ represents 1 unit of the given distance measurement. */ .search-input__vertical-divider { width: 0; height: 1.5rem; - border-right: solid 1px var(--map-col-border); + border-right: solid 1px var(--portal-col-primary-2); } .search-input__search-button { - color: var(--map-col-buttons-icon-muted); + color: var(--portal-col-neutral-4); border-radius: var(--map-border-radius-small); &.search-input__search-button--active { - color: var(--map-col-buttons-icon, currentColor); + color: var(--portal-col-primary-6, currentColor); background: var( - --map-col-buttons-bkg-active, + --portal-col-primary-1, var(--map-col-bkg-lighter__deprecate) ); } } .search-input__cancel-button { - color: var(--map-col-utility-buttons); + color: var(--portal-col-neutral-6); margin-left: 0; width: 1.5rem; } @@ -774,7 +705,7 @@ represents 1 unit of the given distance measurement. */ .search-input__error-text { line-height: 1rem; - color: var(--map-col-input-error-text); + color: var(--portal-col-highlight-3, #ca6c0b); } } @@ -798,11 +729,11 @@ represents 1 unit of the given distance measurement. */ .layers-panel__layers { border: solid - var(--map-col-section-divider, var(--map-col-bkg-lightest__deprecate)); + var(--portal-col-neutral-3, var(--map-col-bkg-lightest__deprecate)); border-width: 1px 0; overflow: auto; border-top: 1px solid - var(--map-col-item-divider, var(--map-col-bkg-lightest__deprecate)); + var(--portal-col-neutral-2, var(--map-col-bkg-lightest__deprecate)); } } @@ -842,13 +773,13 @@ represents 1 unit of the given distance measurement. */ display: flex; align-items: center; justify-content: center; - color: var(--map-col-buttons-icon-muted, currentColor); + color: var(--portal-col-neutral-4, currentColor); background-color: transparent; .layer-item__icon { - fill: var(--map-col-buttons-icon-muted, currentColor); + fill: var(--portal-col-neutral-4, currentColor); svg > path { - fill: var(--map-col-buttons-icon-muted, currentColor); + fill: var(--portal-col-neutral-4, currentColor); } height: 1rem; @@ -858,23 +789,23 @@ represents 1 unit of the given distance measurement. */ } &:hover { - background-color: var(--map-col-content-bkg-muted, #323745); + background-color: var(--portal-col-neutral-1, #323745); transition: background-color 0.3s ease-in-out; &.layer-item--shown { - background-color: var(--map-col-content-bkg-highlight, #15324e); + background-color: var(--portal-col-primary-1, #15324e); } .layer-item__legend-and-settings { /* Show button when layer is hovered. */ display: flex; - border: 1px solid var(--map-col-border-muted); + border: 1px solid var(--portal-col-neutral-4); /* Use the same style on button hover and when selected. */ &.layer-item--selected, &:hover { background-color: var( - --map-col-buttons-bkg-hover, + --portal-col-primary-2, var(--map-col-bkg-lighter__deprecate) ); } @@ -884,18 +815,18 @@ represents 1 unit of the given distance measurement. */ &.layer-item--shown { .layer-item__visibility-toggle { color: var( - --map-col-buttons-bkg-highlight, + --portal-col-primary-6, var(--map-col-highlight__deprecate) ); .layer-item__icon { fill: var( - --map-col-buttons-bkg-highlight, + --portal-col-primary-6, var(--map-col-highlight__deprecate) ); svg > path { fill: var( - --map-col-buttons-bkg-highlight, + --portal-col-primary-6, var(--map-col-highlight__deprecate) ); } @@ -905,7 +836,7 @@ represents 1 unit of the given distance measurement. */ .layer-item__legend-and-settings { /* Show button when layer is shown. */ display: flex; - border: 1px solid var(--map-col-border); + border: 1px solid var(--portal-col-primary-2); /* Only show legend when layer is shown. */ .layer-item__legend-container { @@ -928,7 +859,7 @@ represents 1 unit of the given distance measurement. */ align-items: center; justify-content: center; margin: 0.5rem; - color: var(--map-col-utility-buttons, currentColor); + color: var(--portal-col-neutral-6, currentColor); } .layer-item__legend-container { @@ -941,11 +872,11 @@ represents 1 unit of the given distance measurement. */ &.layer-item--selected, &:hover { display: flex; - background-color: var(--map-col-content-bkg-highlight, #15324e); - border: 1px solid var(--map-col-content-bkg-highlight, #15324e); + background-color: var(--portal-col-primary-1, #15324e); + border: 1px solid var(--portal-col-primary-1, #15324e); .layer-item__settings-toggle { - color: var(--map-col-buttons-icon); + color: var(--portal-col-primary-6); } } } @@ -955,7 +886,7 @@ represents 1 unit of the given distance measurement. */ font-size: 0.825rem; font-weight: 400; margin: 0 0.5rem; - color: var(--map-col-text-label); + color: var(--portal-col-neutral-7); min-width: 0; overflow: hidden; text-overflow: ellipsis; @@ -963,7 +894,10 @@ represents 1 unit of the given distance measurement. */ } .layer-item__highlighted-text { - background-color: var(--map-col-search-match-highlight); + background-color: var( + --portal-col-highlight-2, + rgba(30, 127, 196, 0.5) + ); } .layer-item__icon > svg { @@ -988,10 +922,10 @@ represents 1 unit of the given distance measurement. */ border-top-right-radius: var(--map-border-radius-big); border-top-left-radius: var(--map-border-radius-big); background-color: var( - --map-col-content-bkg-highlight, + --portal-col-primary-1, var(--map-col-bkg-lighter__deprecate) ); - color: var(--map-col-text-body, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-8, var(--map-col-text__deprecate)); box-shadow: var(--map-shadow-md); grid-template-columns: auto min-content; grid-template-rows: min-content auto; @@ -999,7 +933,7 @@ represents 1 unit of the given distance measurement. */ align-items: center; /* Don't show the details panel unless it also has the layer-details--open class */ display: none; - border: 1px solid var(--map-col-border); + border: 1px solid var(--portal-col-primary-2); animation: fadeIn 0.3s ease-in-out; } @@ -1016,7 +950,7 @@ represents 1 unit of the given distance measurement. */ .layer-details__label { /* the important is needed to overwrite more specific styles set on portal title tags */ color: var( - --map-col-text-highlight, + --portal-col-primary-6, var(--map-col-text__deprecate) ) !important; margin: 0; @@ -1028,7 +962,7 @@ represents 1 unit of the given distance measurement. */ } .layer-details__toggle { - color: var(--map-col-utility-buttons); + color: var(--portal-col-neutral-6); font-size: 1rem; height: 2rem; margin-right: 1rem; @@ -1040,11 +974,11 @@ represents 1 unit of the given distance measurement. */ margin: 0.5rem 0 1rem; padding: 0.75rem; background-color: var( - --map-col-content-bkg, + --portal-col-neutral-00, var(--map-col-buttons__deprecate) ); border-radius: var(--map-border-radius-small); - border: 1px solid var(--map-col-border); + border: 1px solid var(--portal-col-primary-2); } .layer-details__notification--blue { @@ -1066,14 +1000,14 @@ represents 1 unit of the given distance measurement. */ } .layer-details__notification--contrast { - background-color: var(--map-col-text-body, var(--map-col-text__deprecate)); - color: var(--map-col-content-bkg, var(--map-col-bkg__deprecate)); + background-color: var(--portal-col-neutral-8, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-00, var(--map-col-bkg__deprecate)); opacity: var(--map-no-brightness-or-opacity-tweaks, 0.95); border-color: none; } .layer-details__sections { - background: var(--map-col-content-bkg, var(--map-col-bkg__deprecate)); + background: var(--portal-col-neutral-00, var(--map-col-bkg__deprecate)); display: grid; grid-template-rows: auto; grid-template-columns: 100%; @@ -1091,7 +1025,7 @@ represents 1 unit of the given distance measurement. */ .layer-detail__label { /* the important is needed to overwrite more specific styles set on portal title tags */ - color: var(--map-col-text-body, var(--map-col-text__deprecate)) !important; + color: var(--portal-col-neutral-8, var(--map-col-text__deprecate)) !important; margin: 0; font-weight: 500; font-size: 1rem; @@ -1155,11 +1089,11 @@ represents 1 unit of the given distance measurement. */ .layer-info__attribution { font-size: 0.75rem; line-height: 1.5; - color: var(--map-col-text-muted); + color: var(--portal-col-neutral-5); } .layer-info__link { - border: 1px solid var(--map-col-border); + border: 1px solid var(--portal-col-primary-2); border-radius: var(--map-border-radius-small); font-size: 1rem; margin-right: 0.5rem; @@ -1179,7 +1113,7 @@ represents 1 unit of the given distance measurement. */ background: var(--c-neutral-3); position: relative; background-color: var( - --map-col-opacity-slider-inactive, + --portal-col-neutral-3, var(--map-col-buttons__deprecate) ); } @@ -1190,7 +1124,7 @@ represents 1 unit of the given distance measurement. */ other class: .ui-slider-range */ .layer-opacity__range { border-radius: var(--map-border-radius-big); - background-color: var(--map-col-opacity-slider-active, #1f254f); + background-color: var(--portal-col-primary-5, #1f254f); } .layer-navigation { @@ -1214,7 +1148,7 @@ other class: .ui-slider-range */ align-items: center; justify-content: center; border: none; - background-color: var(--map-col-buttons-bkg-highlight, #efefef); + background-color: var(--portal-col-primary-6, #efefef); } /* The element that displays the current opacity as a percentage */ @@ -1223,7 +1157,7 @@ other class: .ui-slider-range */ font-size: 0.8em; /* Show the opacity value just below the slider handle */ margin-top: 2.5em; - color: var(--map-col-text-muted, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-5, var(--map-col-text__deprecate)); } /***************************************************************************************** @@ -1244,10 +1178,10 @@ other class: .ui-slider-range */ row-gap: 0.8rem; border-radius: var(--map-border-radius-big); background-color: var( - --map-col-content-bkg-highlight, + --portal-col-primary-1, var(--map-col-bkg-lighter__deprecate) ); - color: var(--map-col-text-body, var(--map-col-text__deprecate)); + color: var(--portal-col-neutral-8, var(--map-col-text__deprecate)); box-shadow: var(--map-shadow-md); /* Don't show the details panel unless it also has the feature-info--open class */ display: none; @@ -1266,8 +1200,8 @@ other class: .ui-slider-range */ .feature-info__label { border-bottom: 1px solid - var(--map-col-border, var(--map-col-bkg-lightest__deprecate)); - color: var(--map-col-text-title, var(--map-col-text__deprecate)) !important; + var(--portal-col-primary-2, var(--map-col-bkg-lightest__deprecate)); + color: var(--portal-col-primary-7, var(--map-col-text__deprecate)) !important; font-weight: 600; font-size: 1.33rem; line-height: 1.5rem; @@ -1284,7 +1218,7 @@ other class: .ui-slider-range */ } .feature-info__toggle { - color: var(--map-col-utility-buttons); + color: var(--portal-col-neutral-6); font-size: 1rem; height: 2rem; position: absolute; @@ -1303,7 +1237,7 @@ other class: .ui-slider-range */ .feature-info__table { background-color: var( - --map-col-content-bkg-muted, + --portal-col-neutral-1, var(--map-col-bkg-lightest__deprecate) ); border-radius: var(--map-border-radius-big); @@ -1322,7 +1256,7 @@ other class: .ui-slider-range */ .feature-info__table-row:nth-child(even) { background-color: var( - --map-col-content-bkg, + --portal-col-neutral-00, var(--map-col-bkg-lighter__deprecate) ); } @@ -1353,7 +1287,7 @@ other class: .ui-slider-range */ display: block; margin: 0 0 0.5rem; padding: 0; - color: var(--map-col-text-muted); + color: var(--portal-col-neutral-5); } .feature-info__description { @@ -1362,11 +1296,11 @@ other class: .ui-slider-range */ } .feature-info__link { - background: var(--map-col-buttons-bkg, var(--map-col-buttons__deprecate)); - border: 1px solid var(--map-col-border); + background: var(--portal-col-neutral-00, var(--map-col-buttons__deprecate)); + border: 1px solid var(--portal-col-primary-2); border-radius: var(--map-border-radius-small); text-decoration: none; - color: var(--map-col-buttons-text, var(--portal-col-highlight__deprecate)); + color: var(--portal-col-primary-6, var(--portal-col-highlight__deprecate)); display: inline-block; padding: 0.5rem 1rem; @@ -1436,29 +1370,29 @@ other class: .ui-slider-range */ } .nav-help { - color: var(--map-col-text-label, white); + color: var(--portal-col-neutral-7, white); } .nav-help .map-view__button { background-color: var( - --map-col-content-bkg-highlight, + --portal-col-primary-1, var(--map-col-bkg-lighter__deprecate) ); border-radius: var(--map-border-radius-small); - color: var(--map-col-text-label); + color: var(--portal-col-neutral-7); margin-right: 0.5rem; padding: 0.25rem 1rem; } .nav-help .map-view__button--active { - /* TODO: https://github.com/NCEAS/metacatui/issues/2347 - Update to --map-col-buttons-bkg-highlight */ - background-color: var(--map-col-buttons-bkg-white-content-contrast, #505561); + /* TODO: https://github.com/NCEAS/metacatui/issues/2347 - Update to --portal-col-primary-6 */ + background-color: var(--portal-col-neutral-5, #505561); color: white; } .nav-help__img { /* TODO: https://github.com/NCEAS/metacatui/issues/2347 - Update to transparent */ - background: var(--map-col-buttons-bkg-hover, #505561); + background: var(--portal-col-primary-2, #505561); border-radius: var(--map-border-radius-big); height: 3rem; padding: 0.5rem; @@ -1467,7 +1401,7 @@ other class: .ui-slider-range */ .nav-help__instructions { border-top: 1px solid - var(--map-col-section-divider, var(--map-col-bkg-lightest__deprecate)); + var(--portal-col-neutral-3, var(--map-col-bkg-lightest__deprecate)); display: grid; margin: 1rem 0; padding: 0; @@ -1482,13 +1416,13 @@ other class: .ui-slider-range */ .cesium-navigation-help-zoom, .cesium-navigation-help-rotate, .cesium-navigation-help-tilt { - color: var(--map-col-text-body, #66ccff); + color: var(--portal-col-neutral-8, #66ccff); font-weight: 500; } .nav-help__instruction { border-bottom: 1px solid - var(--map-col-item-divider, var(--map-col-bkg-lightest__deprecate)); + var(--portal-col-neutral-2, var(--map-col-bkg-lightest__deprecate)); display: grid; grid-template-columns: 3rem auto; gap: 1rem; @@ -1508,7 +1442,7 @@ other class: .ui-slider-range */ .viewfinder__search { border-bottom: 1px solid - var(--map-col-item-divider, var(--map-col-bkg-lightest__deprecate)); + var(--portal-col-neutral-2, var(--map-col-bkg-lightest__deprecate)); } .viewfinder__zoom-presets { @@ -1528,11 +1462,11 @@ other class: .ui-slider-range */ .viewfinder-prediction__content { align-items: center; - background: var(--map-col-content-bkg, var(--map-col-bkg__deprecate)); - border: 1px solid var(--map-col-border-muted, white); + background: var(--portal-col-neutral-00, var(--map-col-bkg__deprecate)); + border: 1px solid var(--portal-col-neutral-4, white); border-radius: var(--map-border-radius-small); box-sizing: border-box; - color: var(--map-col-text-highlight, white); + color: var(--portal-col-primary-6, white); cursor: pointer; display: flex; gap: 0.5rem; @@ -1557,9 +1491,9 @@ other class: .ui-slider-range */ &:hover, &.viewfinder-prediction__focused { - border-color: var(--map-col-border-highlight); + border-color: unset; background-color: var( - --map-col-content-bkg-highlight, + --portal-col-primary-1, var(--map-col-bkg-lighter__deprecate) ); } @@ -1575,7 +1509,7 @@ other class: .ui-slider-range */ .viewfinder-zoom-preset__preset { border-radius: var(--map-border-radius-big); border: 1px solid - var(--map-col-border-panel, var(--map-col-bkg-lightest__deprecate)); + var(--portal-col-neutral-3, var(--map-col-bkg-lightest__deprecate)); cursor: pointer; display: flex; flex-direction: column; @@ -1584,33 +1518,33 @@ other class: .ui-slider-range */ &:hover { background-color: var( - --map-col-content-bkg-highlight, + --portal-col-primary-1, var(--map-col-bkg-lighter__deprecate) ); .viewfinder-zoom-preset__layer { background-color: var( - --map-col-content-bkg-hover-highlight, + rgb(from var(--portal-col-primary-2) r g b / 50%), var(--map-col-bkg__deprecate) ); .viewfinder-zoom-preset__layer-content { - color: var(--map-col-text-hover-highlight); + color: var(--portal-col-primary-6); i { - color: var(--map-col-buttons-icon-hover-highlight); + color: var(--portal-col-primary-6); } } } } .viewfinder-zoom-preset__title { - color: var(--map-col-text-highlight, white); + color: var(--portal-col-primary-6, white); font-weight: 600; } .viewfinder-zoom-preset__description { - color: var(--map-col-text-label, white); + color: var(--portal-col-neutral-7, white); font-size: 0.85rem; margin-bottom: 0.5rem; } @@ -1625,7 +1559,7 @@ other class: .ui-slider-range */ .viewfinder-zoom-preset__layer { align-items: center; background: var( - --map-col-content-bkg-transparent, + rgb(from var(--portal-col-neutral-2) r g b / 50%), var(--map-col-bkg-lighter__deprecate) ); border-radius: var(--map-border-radius-big); @@ -1638,13 +1572,13 @@ other class: .ui-slider-range */ overflow: hidden; text-overflow: ellipsis; text-wrap: nowrap; - color: var(--map-col-text-muted, white); + color: var(--portal-col-neutral-5, white); font-size: 0.8rem; max-width: 10rem; i { padding: 0 0.25rem; - color: var(--map-col-buttons-icon, white); + color: var(--portal-col-primary-6, white); } } } @@ -1652,24 +1586,24 @@ other class: .ui-slider-range */ .viewfinder-zoom-preset.viewfinder-zoom-preset--active .viewfinder-zoom-preset__preset { - background-color: var(--map-col-buttons-bkg-highlight, #15324e); + background-color: var(--portal-col-primary-6, #15324e); .viewfinder-zoom-preset__title, .viewfinder-zoom-preset__description { - color: var(--map-col-buttons-text-highlight, white); + color: var(--portal-col-neutral-00, white); } .viewfinder-zoom-preset__layer { background: var( - --map-col-content-bkg-active-highlight, + rgb(from var(--portal-col-primary-5) r g b / 50%), var(--map-col-bkg__deprecate) ); .viewfinder-zoom-preset__layer-content { - color: var(--map-col-text-active-highlight, white); + color: var(--portal-col-primary-2, white); i { - color: var(--map-col-buttons-icon-active-highlight, white); + color: var(--portal-col-primary-4, white); } } } @@ -1679,9 +1613,9 @@ other class: .ui-slider-range */ /** START - Expansion panel View. */ .expansion-panel { box-shadow: 0 1px - var(--map-col-item-divider, var(--map-col-bkg-lightest__deprecate)) inset; + var(--portal-col-neutral-2, var(--map-col-bkg-lightest__deprecate)) inset; border-bottom: 1px solid - var(--map-col-item-divider, var(--map-col-bkg-lightest__deprecate)); + var(--portal-col-neutral-2, var(--map-col-bkg-lightest__deprecate)); font-size: 1rem; .expansion-panel__toggle { @@ -1694,7 +1628,7 @@ other class: .ui-slider-range */ border: none; &:hover { - background-color: var(--map-col-content-bkg-highlight, #15324e); + background-color: var(--portal-col-primary-1, #15324e); transition: background-color 0.3s ease-in-out; } } @@ -1723,12 +1657,12 @@ other class: .ui-slider-range */ .expansion-panel__title { font-weight: 500; - color: var(--map-col-text-highlight, var(--map-col-highlight__deprecate)); + color: var(--portal-col-primary-6, var(--map-col-highlight__deprecate)); } .expansion-panel__icon { - fill: var(--map-col-text-highlight, var(--map-col-highlight__deprecate)); - color: var(--map-col-text-highlight, var(--map-col-highlight__deprecate)); + fill: var(--portal-col-primary-6, var(--map-col-highlight__deprecate)); + color: var(--portal-col-primary-6, var(--map-col-highlight__deprecate)); height: 2rem; width: 2rem; margin: 0.25rem; @@ -1746,7 +1680,7 @@ other class: .ui-slider-range */ width: 2rem; display: flex; justify-content: center; - color: var(--map-col-utility-buttons); + color: var(--portal-col-neutral-6); } .expansion-panel__content { diff --git a/src/css/portal-layouts/panels.css b/src/css/portal-layouts/panels.css index ec7fa8fed..22b89e002 100644 --- a/src/css/portal-layouts/panels.css +++ b/src/css/portal-layouts/panels.css @@ -70,7 +70,7 @@ } .portal-view .portal-title { - color: var(--portal-col-text-nav-highlight); + color: var(--portal-col-primary-2); font-size: 1rem; font-weight: 400; } @@ -97,7 +97,7 @@ display: flex; font-size: 1rem; cursor: help; - color: var(--portal-col-text-nav); + color: var(--portal-col-primary-3); } .portal-view .portal-description:hover::before { @@ -113,7 +113,7 @@ left: 1.5rem; width: max-content; background-color: var( - --portal-col-tooltip-background, + --portal-col-neutral-7, var(--portal-col-bkg-active__deprecate, white) ); z-index: 10; @@ -180,14 +180,14 @@ .portal-view #portal-section-tabs .section-link-container .portal-section-link { background: transparent; - color: var(--portal-col-text-nav); + color: var(--portal-col-primary-3); } .portal-view #portal-section-tabs .section-link-container.active .portal-section-link { - color: var(--portal-col-text-nav-highlight, var(--portal-secondary-color)); + color: var(--portal-col-primary-2, var(--portal-secondary-color)); background: transparent; } @@ -202,7 +202,7 @@ .portal-section-link:hover { background: transparent; color: var( - --portal-col-text-nav-hover, + --portal-col-neutral-00, var(--portal-secondary-color) ) !important; } diff --git a/src/css/portal-themes/light.css b/src/css/portal-themes/light.css index 907899843..66818b7c8 100644 --- a/src/css/portal-themes/light.css +++ b/src/css/portal-themes/light.css @@ -2,80 +2,31 @@ :root { /* COLOURS */ - --portal-blue-1: #ecf8fc; - --portal-blue-2: #ccf0fc; - --portal-blue-2-50: #ccf0fc80; - --portal-blue-3: #99e1fa; - --portal-blue-4: #33c3f5; - --portal-blue-5: #00a9e4; - --portal-blue-5-50: #00a9e480; - --portal-blue-6: #0087b5; - --portal-blue-7: #136682; - - --portal-grey-1: #f8f9fa; - --portal-grey-2: #f1f3f4; - --portal-grey-2-50: #f1f3f480; - --portal-grey-3: #e8eaed; - --portal-grey-4: #bdc1c6; - --portal-grey-5: #80868b; - --portal-grey-6: #5f6368; - --portal-grey-7: #3c4043; - --portal-grey-8: #202124; - - --portal-col-content-bkg: white; - --portal-col-content-bkg-highlight: var(--portal-blue-1); - --portal-col-content-bkg-muted: var(--portal-grey-1); - --portal-col-content-bkg-transparent: var(--portal-grey-2-50); - --portal-col-content-bkg-active-highlight: var(--portal-blue-5-50); - --portal-col-content-bkg-hover-highlight: var(--portal-blue-2-50); - - --portal-col-buttons-bkg: white; - --portal-col-buttons-bkg-hover: var(--portal-blue-2); - --portal-col-buttons-bkg-highlight: var(--portal-blue-6); - --portal-col-buttons-bkg-muted: var(--portal-grey-1); - --portal-col-buttons-bkg-white-content-contrast: var(--portal-grey-5); - --portal-col-buttons-text: var(--portal-blue-6); - --portal-col-buttons-text-highlight: white; - --portal-col-buttons-icon: var(--portal-blue-6); - --portal-col-buttons-icon-active-highlight: var(--portal-blue-4); - --portal-col-buttons-icon-hover-highlight: var(--portal-blue-6); - --portal-col-buttons-icon-highlight: var(--portal-blue-3); - --portal-col-buttons-icon-muted: var(--portal-grey-4); - --portal-col-utility-buttons: var(--portal-grey-6); - - --portal-col-border: var(--portal-blue-2); - --portal-col-border-highlight: unset; - --portal-col-border-panel: var(--portal-grey-3); - --portal-col-border-muted: var(--portal-grey-4); - --portal-col-border-highlight-search-box: var(--portal-blue-3); - - --portal-col-section-divider: var(--portal-grey-3); - --portal-col-item-divider: var(--portal-grey-2); - - --portal-col-text-body: var(--portal-grey-8); - --portal-col-text-highlight: var(--portal-blue-6); - --portal-col-text-title: var(--portal-blue-7); - --portal-col-text-label: var(--portal-grey-7); - --portal-col-text-detail: var(--portal-grey-6); - --portal-col-text-muted: var(--portal-grey-5); - --portal-col-text-active-highlight: var(--portal-blue-2); - --portal-col-text-hover-highlight: var(--portal-blue-6); - - --portal-col-page-bkg: var(--portal-blue-7); - --portal-col-text-nav: var(--portal-blue-3); - --portal-col-text-nav-highlight: var(--portal-blue-2); - --portal-col-text-nav-hover: white; - - --portal-col-opacity-slider-active: var(--portal-blue-5); - --portal-col-opacity-slider-inactive: var(--portal-grey-3); - - --portal-col-input-error-bkg: #fffcf4; - --portal-col-input-error-border: #ffddaa; - --portal-col-input-error-text: #ca6c0b; - --portal-col-search-match-highlight: #fef4d6; + --portal-col-primary-1: #ecf8fc; + --portal-col-primary-2: #ccf0fc; + --portal-col-primary-3: #99e1fa; + --portal-col-primary-4: #33c3f5; + --portal-col-primary-5: #00a9e4; + --portal-col-primary-6: #0087b5; + --portal-col-primary-7: #136682; + + --portal-col-neutral-1: #f8f9fa; + --portal-col-neutral-2: #f1f3f4; + --portal-col-neutral-3: #e8eaed; + --portal-col-neutral-4: #bdc1c6; + --portal-col-neutral-5: #80868b; + --portal-col-neutral-6: #5f6368; + --portal-col-neutral-7: #3c4043; + --portal-col-neutral-8: #202124; + + --portal-col-neutral-00: white; + --portal-col-neutral-10: black; + + --portal-col-highlight-1: #fffcf4; + --portal-col-highlight-2: #ffddaa; + --portal-col-highlight-3: #ca6c0b; - --portal-col-tooltip-background: var(--portal-grey-7); - --portal-col-tooltip-text: white; + --portal-col-search-match-highlight: #fef4d6; --portal-col-bkg__deprecate: #111827; --portal-col-bkg-lighter__deprecate: #1f2937; @@ -111,12 +62,12 @@ body { .portal-view, .Portal #Content, .PortalView #Content { - background-color: var(--portal-col-page-bkg); + background-color: var(--portal-col-primary-7); } body, #portal-sections { - /* TODO: Update to var(--portal-col-content-bkg) when all portal tabs support the color palette from a theme. */ + /* TODO: Update to var(--portal-col-neutral-00) when all portal tabs support the color palette from a theme. */ background-color: #111827; } @@ -480,7 +431,7 @@ textarea { } .map-view a { - color: var(--portal-col-buttons-text); + color: var(--portal-col-primary-6); filter: unset; } diff --git a/src/js/views/maps/FeatureInfoView.js b/src/js/views/maps/FeatureInfoView.js index 55de68e85..f980eed51 100644 --- a/src/js/views/maps/FeatureInfoView.js +++ b/src/js/views/maps/FeatureInfoView.js @@ -204,7 +204,7 @@ define([ - `); - iFrameDoc.close(); - - // Identify the elements from the template that will be updated when the - // Feature model changes - view.elements = { - title: view.el.querySelector(`.${classes.title}`), - iFrame, - iFrameContentContainer: iFrameDoc.getElementById("content"), - layerDetailsButton: view.el.querySelector( - `.${classes.layerDetailsButton}`, - ), - zoomButton: view.el.querySelector(`.${classes.zoomButton}`), - }; - - view.update(); - - // Ensure the view's main element has the given class name - view.el.classList.add(view.className); - - // When the model changes, update the view - view.stopListening(view.model, "change"); - view.listenTo(view.model, "change", view.update); - } catch (error) { - console.log( - `There was an error rendering a FeatureInfoView` + - `. Error details: ${error}`, - ); + // Show the feature info box as open if the view is set to have it open + // already + if (view.isOpen) { + view.el.classList.add(view.classes.open); } - return this; + + // Insert the principal template into the view + view.$el.html( + view.template({ + classes, + }), + ); + + const iFrame = view.el.querySelector(`.${classes.contentContainer}`); + + // Select the iFrame + const iFrameDoc = iFrame.contentWindow.document; + + // Add a script that gets all of the CSS stylesheets from the parent and + // applies them within the iFrame. Create a div within the iFrame to hold the + // feature info template content. + iFrameDoc.open(); + iFrameDoc.write(` +
+ + + `); + iFrameDoc.close(); + + // Identify the elements from the template that will be updated when the + // Feature model changes + view.elements = { + title: view.el.querySelector(`.${classes.title}`), + iFrame, + iFrameContentContainer: iFrameDoc.getElementById("content"), + layerDetailsButton: view.el.querySelector( + `.${classes.layerDetailsButton}`, + ), + zoomButton: view.el.querySelector(`.${classes.zoomButton}`), + }; + + view.update(); + + // Ensure the view's main element has the given class name + view.el.classList.add(view.className); + + // When the model changes, update the view + view.stopListening(view.model, "change"); + view.listenTo(view.model, "change", view.update); + return view; }, /** * Updates the view with information from the current Feature model */ updateContent() { - try { - const view = this; - - // Elements to update - const title = this.getFeatureTitle(); - const { iFrame } = this.elements; - const iFrameDiv = this.elements.iFrameContentContainer; - const { layerDetailsButton } = this.elements; - const { zoomButton } = this.elements; - const mapAsset = this.model.get("mapAsset"); - const mapAssetLabel = mapAsset ? mapAsset.get("label") : null; - const layerButtonDisplay = mapAsset ? null : "none"; - const layerButtonText = `See ${mapAssetLabel} layer details`; - // The Cesium Map Widget can't zoom to Cesium3DTileFeatures, so for now, hide - // the 'zoom to feature' button - const zoomButtonDisplay = - !mapAsset || mapAsset.get("type") === "Cesium3DTileset" - ? "none" - : null; - - // Insert the title into the title element - this.elements.title.innerHTML = title; - - // Update the iFrame content - this.getContent().then((html) => { - iFrameDiv.innerHTML = html; - iFrame.style.height = 0; - iFrame.style.opacity = 0; - // Not the ideal solution, but check the height of the iFrame - // again after some time to allow external content to load. This - // is necessary for content that loads asynchronously, like - // images. Difficult to set listeners for this, since the content - // may be from a different domain. - setTimeout(() => { - view.updateIFrameHeight(); - }, 500); - }); + const view = this; - // Show or hide the layer details button, update the text - layerDetailsButton.style.display = layerButtonDisplay; - layerDetailsButton.innerText = layerButtonText; - - // Show or hide the zoom to feature button - zoomButton.style.display = zoomButtonDisplay; - } catch (error) { - console.log( - `There was an error rendering the content of a FeatureInfoView` + - `. Error details: ${error}`, - ); - } + // Elements to update + const title = this.getFeatureTitle(); + const { iFrame } = this.elements; + const iFrameDiv = this.elements.iFrameContentContainer; + const { layerDetailsButton } = this.elements; + const { zoomButton } = this.elements; + const mapAsset = this.model.get("mapAsset"); + const mapAssetLabel = mapAsset ? mapAsset.get("label") : null; + const layerButtonDisplay = mapAsset ? null : "none"; + const layerButtonText = `See ${mapAssetLabel} layer details`; + // The Cesium Map Widget can't zoom to Cesium3DTileFeatures, so for now, hide + // the 'zoom to feature' button + const zoomButtonDisplay = + !mapAsset || mapAsset.get("type") === "Cesium3DTileset" + ? "none" + : null; + + // Insert the title into the title element + this.elements.title.innerHTML = title; + + // Update the iFrame content + this.getContent().then((html) => { + iFrameDiv.innerHTML = html; + iFrame.style.height = 0; + iFrame.style.opacity = 0; + // Not the ideal solution, but check the height of the iFrame + // again after some time to allow external content to load. This + // is necessary for content that loads asynchronously, like + // images. Difficult to set listeners for this, since the content + // may be from a different domain. + setTimeout(() => { + view.updateIFrameHeight(); + }, 500); + }); + + // Show or hide the layer details button, update the text + layerDetailsButton.style.display = layerButtonDisplay; + layerDetailsButton.innerText = layerButtonText; + + // Show or hide the zoom to feature button + zoomButton.style.display = zoomButtonDisplay; }, /** @@ -324,65 +299,57 @@ define([ * based on the feature and if there is a template set on the parent Map Asset * model. * @since 2.19.0 - * @returns {Promise|null} Returns a promise that resolves to the content HTML + * @returns {Promise} Returns a promise that resolves to the content HTML * when ready, otherwise null */ getContent() { - try { - let content = null; - let templateOptions = this.model.toJSON(); - const mapAsset = this.model.get("mapAsset"); - const featureProperties = this.model.get("properties"); - const templateConfig = mapAsset - ? mapAsset.get("featureTemplate") - : null; - const propertyMap = templateConfig ? templateConfig.options : {}; - const templateName = templateConfig ? templateConfig.template : null; - const { contentTemplates } = this; - - // Given the name of a template configured in the MapAsset model, find the - // matching template from the contentTemplates set on this view - let contentTemplate = contentTemplates.find( - (template) => template.name === templateName, - ); - if (!contentTemplate) { - contentTemplate = contentTemplates[contentTemplates.length - 1]; - } - - // To get variables to pass to the template, there must be properties set on - // the feature and the selected content template must accept options - if ( - contentTemplate && - contentTemplate.options && - templateConfig && - templateConfig.options - ) { - templateOptions = {}; - contentTemplate.options.forEach((prop) => { - const key = propertyMap[prop]; - templateOptions[prop] = featureProperties[key] || ""; - }); - } + let content = null; + let templateOptions = this.model.toJSON(); + const mapAsset = this.model.get("mapAsset"); + const featureProperties = this.model.get("properties"); + const templateConfig = mapAsset + ? mapAsset.get("featureTemplate") + : null; + const propertyMap = templateConfig ? templateConfig.options : {}; + const templateName = templateConfig ? templateConfig.template : null; + const { contentTemplates } = this; + + // Given the name of a template configured in the MapAsset model, find the + // matching template from the contentTemplates set on this view + let contentTemplate = contentTemplates.find( + (template) => template.name === templateName, + ); + if (!contentTemplate) { + contentTemplate = contentTemplates[contentTemplates.length - 1]; + } - // Return a promise that resolves to the content HTML - return new Promise((resolve) => { - if (contentTemplate) { - // eslint-disable-next-line import/no-dynamic-require - require([contentTemplate.template], (template) => { - content = _.template(template)(templateOptions); - resolve(content); - }); - } else { - resolve(null); - } + // To get variables to pass to the template, there must be properties set on + // the feature and the selected content template must accept options + if ( + contentTemplate && + contentTemplate.options && + templateConfig && + templateConfig.options + ) { + templateOptions = {}; + contentTemplate.options.forEach((prop) => { + const key = propertyMap[prop]; + templateOptions[prop] = featureProperties[key] || ""; }); - } catch (error) { - console.log( - `There was an error getting the content of a FeatureInfoView` + - `. Error details: ${error}`, - ); - return null; } + + // Return a promise that resolves to the content HTML + return new Promise((resolve) => { + if (contentTemplate) { + // eslint-disable-next-line import/no-dynamic-require + require([contentTemplate.template], (template) => { + content = _.template(template)(templateOptions); + resolve(content); + }); + } else { + resolve(null); + } + }); }, /** @@ -391,74 +358,66 @@ define([ * @returns {string} The title for the feature info box */ getFeatureTitle() { - try { - let title = ""; - let suffix = ""; - - if (this.model) { - // Get the layer/mapAsset model - const mapAsset = this.model.get("mapAsset"); - - const featureTemplate = mapAsset - ? mapAsset.get("featureTemplate") - : null; - const properties = this.model.get("properties") ?? {}; - const assetName = mapAsset ? mapAsset.get("label") : null; - let name = featureTemplate - ? properties[featureTemplate.label] - : this.model.get("label"); - - // Build a title if the feature has no label. Check if the feature has a name, - // title, ID, or identifier property. Search for these properties independent - // of case. If none of these properties exist, use the feature ID provided by - // the model. - if (!name) { - title = "Feature"; - - let searchKeys = ["name", "title", "id", "identifier"]; - searchKeys = searchKeys.map((key) => key.toLowerCase()); - const propKeys = Object.keys(properties); - const propKeysLower = propKeys.map((key) => key.toLowerCase()); - - // Search by search key, since search keys are in order of preference. Find - // the first matching key. - const nameKeyLower = searchKeys.find((searchKey) => - propKeysLower.includes(searchKey), - ); - - // Then figure out which of the original property keys matches (we need it - // in the original case). - const nameKey = propKeys[propKeysLower.indexOf(nameKeyLower)]; - - name = properties[nameKey] ?? this.model.get("featureID"); - - if (assetName) { - suffix = ` from ${assetName} Layer`; - } - } - if (name) { - title = `${title} ${name}`; - } - if (suffix) { - title += suffix; + let title = ""; + let suffix = ""; + + if (this.model) { + // Get the layer/mapAsset model + const mapAsset = this.model.get("mapAsset"); + + const featureTemplate = mapAsset + ? mapAsset.get("featureTemplate") + : null; + const properties = this.model.get("properties") ?? {}; + const assetName = mapAsset ? mapAsset.get("label") : null; + let name = featureTemplate + ? properties[featureTemplate.label] + : this.model.get("label"); + + // Build a title if the feature has no label. Check if the feature has a name, + // title, ID, or identifier property. Search for these properties independent + // of case. If none of these properties exist, use the feature ID provided by + // the model. + if (!name) { + title = "Feature"; + + let searchKeys = ["name", "title", "id", "identifier"]; + searchKeys = searchKeys.map((key) => key.toLowerCase()); + const propKeys = Object.keys(properties); + const propKeysLower = propKeys.map((key) => key.toLowerCase()); + + // Search by search key, since search keys are in order of preference. Find + // the first matching key. + const nameKeyLower = searchKeys.find((searchKey) => + propKeysLower.includes(searchKey), + ); + + // Then figure out which of the original property keys matches (we need it + // in the original case). + const nameKey = propKeys[propKeysLower.indexOf(nameKeyLower)]; + + name = properties[nameKey] ?? this.model.get("featureID"); + + if (assetName) { + suffix = ` from ${assetName} Layer`; } } - - // Do some basic sanitization of the title - title = title.replace(/&/g, "&"); - title = title.replace(//g, ">"); - title = title.replace(/"/g, """); - title = title.replace(/'/g, "'"); - - return title; - } catch (error) { - console.log( - `There was an error making a title for the FeatureInfoView` + - `. Error details: ${error}`, - ); - return "Feature"; + if (name) { + title = `${title} ${name}`; + } + if (suffix) { + title += suffix; + } } + + // Do some basic sanitization of the title + title = title.replace(/&/g, "&"); + title = title.replace(//g, ">"); + title = title.replace(/"/g, """); + title = title.replace(/'/g, "'"); + + return title; }, /** @@ -468,15 +427,8 @@ define([ * attribute is changed. */ showLayerDetails() { - try { - if (this.model && this.model.get("mapAsset")) { - this.model.get("mapAsset").set("selected", true); - } - } catch (error) { - console.log( - `There was an error showing the layer details panel from a FeatureInfoView` + - `. Error details: ${error}`, - ); + if (this.model && this.model.get("mapAsset")) { + this.model.get("mapAsset").set("selected", true); } }, @@ -486,17 +438,10 @@ define([ * Asset layer is visible in the map. */ zoomToFeature() { - try { - const { model } = this; - const mapAsset = model ? model.get("mapAsset") : false; - if (mapAsset) { - mapAsset.zoomTo(model); - } - } catch (error) { - console.log( - `There was an error zooming to a feature from a FeatureInfoView` + - `. Error details: ${error}`, - ); + const { model } = this; + const mapAsset = model ? model.get("mapAsset") : false; + if (mapAsset) { + mapAsset.zoomTo(model); } }, @@ -504,35 +449,21 @@ define([ * Shows the feature info box */ open() { - try { - this.el.classList.add(this.classes.open); - this.isOpen = true; - } catch (error) { - console.log( - `There was an error showing the FeatureInfoView` + - `. Error details: ${error}`, - ); - } + this.el.classList.add(this.classes.open); + this.isOpen = true; }, /** * Hide the feature info box from view */ close() { - try { - this.el.classList.remove(this.classes.open); - this.isOpen = false; - // When the feature info panel is closed, remove the Feature model from the - // Features collection. This will trigger the map widget to remove - // highlighting from the feature. - if (this.model && this.model.collection) { - this.model.collection.remove(this.model); - } - } catch (error) { - console.log( - `There was an error hiding the FeatureInfoView` + - `. Error details: ${error}`, - ); + this.el.classList.remove(this.classes.open); + this.isOpen = false; + // When the feature info panel is closed, remove the Feature model from the + // Features collection. This will trigger the map widget to remove + // highlighting from the feature. + if (this.model && this.model.collection) { + this.model.collection.remove(this.model); } }, @@ -542,20 +473,13 @@ define([ * or close it if there is no model or the model has only default values. */ update() { - try { - if (!this.model || this.model.isDefault()) { - if (this.isOpen) { - this.close(); - } - } else { - this.open(); - this.updateContent(); + if (!this.model || this.model.isDefault()) { + if (this.isOpen) { + this.close(); } - } catch (error) { - console.log( - `There was an error updating the content of a FeatureInfoView` + - `. Error details: ${error}`, - ); + } else { + this.open(); + this.updateContent(); } }, From 38cdce39a17b28b7e13b8a0d5fb95de3dba8e53d Mon Sep 17 00:00:00 2001 From: yvonnesjy Date: Wed, 5 Jun 2024 14:40:55 -0700 Subject: [PATCH 05/10] Legend: Create Cesium widget container This container is intended to be a parent for the map and all overlays (ex. legend, scale bar, etc). --- .../views/maps/CesiumWidgetContainerView.js | 51 +++++++++++++++++++ src/js/views/maps/MapView.js | 30 ++++------- test/config/tests.json | 3 +- .../maps/CesiumWidgetContainerView.spec.js | 44 ++++++++++++++++ 4 files changed, 108 insertions(+), 20 deletions(-) create mode 100644 src/js/views/maps/CesiumWidgetContainerView.js create mode 100644 test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js diff --git a/src/js/views/maps/CesiumWidgetContainerView.js b/src/js/views/maps/CesiumWidgetContainerView.js new file mode 100644 index 000000000..1e60df1ea --- /dev/null +++ b/src/js/views/maps/CesiumWidgetContainerView.js @@ -0,0 +1,51 @@ +"use strict"; + +define([ + "backbone", + "models/maps/Map", + "views/maps/CesiumWidgetView", +], (Backbone, Map, CesiumWidgetView) => { + /** + * @class CesiumWidgetContainerView + * @classdesc A container for CesiumWidgetView and other map overlays, e.g. lat/lng, legends, etc. + * @classcategory Views/Maps + * @name CesiumWidgetContainerView + * @augments Backbone.View + * @since x.x.x + * @constructs + */ + const CesiumWidgetContainerView = Backbone.View.extend( + /** @lends CesiumWidgetContainerView.prototype */ { + /** + * The model that this view uses + * @type {Map} + */ + model: null, + + /** @inheritdoc */ + el: null, + + /** @inheritdoc */ + initialize(options) { + this.el = options.el; + this.model = options.model; + }, + + /** @inheritdoc */ + render() { + this.renderMapWidget(this.el, this.model); + }, + + /** Renders Cesium map. */ + renderMapWidget() { + const mapWidget = new CesiumWidgetView({ + el: this.el, + model: this.model, + }); + mapWidget.render(); + }, + }, + ); + + return CesiumWidgetContainerView; +}); diff --git a/src/js/views/maps/MapView.js b/src/js/views/maps/MapView.js index 15a4987cb..c2e901b80 100644 --- a/src/js/views/maps/MapView.js +++ b/src/js/views/maps/MapView.js @@ -7,7 +7,7 @@ define([ "models/maps/Map", "text!templates/maps/map.html", // SubViews - "views/maps/CesiumWidgetView", + "views/maps/CesiumWidgetContainerView", "views/maps/ToolbarView", "views/maps/ScaleBarView", "views/maps/FeatureInfoView", @@ -21,7 +21,7 @@ define([ Map, Template, // SubViews - CesiumWidgetView, + CesiumWidgetContainerView, ToolbarView, ScaleBarView, FeatureInfoView, @@ -159,25 +159,17 @@ define([ /** * Renders the view that shows the map/globe and all of the geo-spatial data. - * Currently, this uses the CesiumWidgetView, but this function could be modified + * Currently, this uses the CesiumWidgetContainerView, but this function could be modified * to use an alternative map widget in the future. - * @returns {CesiumWidgetView} Returns the rendered view + * @returns {CesiumWidgetContainerView} Returns the rendered view */ renderMapWidget: function () { - try { - this.mapWidget = new CesiumWidgetView({ - el: this.subElements.mapWidgetContainer, - model: this.model, - }); - this.mapWidget.render(); - return this.mapWidget; - } catch (error) { - console.log( - "There was an error rendering the map widget in a MapView" + - ". Error details: " + - error, - ); - } + this.mapWidgetContainer = new CesiumWidgetContainerView({ + el: this.subElements.mapWidgetContainer, + model: this.model, + }); + this.mapWidgetContainer.render(); + return this.mapWidgetContainer; }, /** @@ -325,7 +317,7 @@ define([ */ getSubViews: function () { return [ - this.mapWidget, + this.mapWidgetContainer, this.toolbar, this.featureInfo, this.layerDetails, diff --git a/test/config/tests.json b/test/config/tests.json index c133e5cd7..e2c8239fe 100644 --- a/test/config/tests.json +++ b/test/config/tests.json @@ -67,7 +67,8 @@ "./js/specs/unit/models/Search.spec.js", "./js/specs/unit/models/SolrResult.spec.js", "./js/specs/unit/models/DataONEObject.spec.js", - "./js/specs/unit/views/maps/MapView.spec.js" + "./js/specs/unit/views/maps/MapView.spec.js", + "./js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js" ], "integration": [ "./js/specs/integration/collections/SolrResults.spec.js", diff --git a/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js b/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js new file mode 100644 index 000000000..51dd925e0 --- /dev/null +++ b/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js @@ -0,0 +1,44 @@ +define([ + "views/maps/CesiumWidgetContainerView", + "models/maps/Map", + "collections/maps/AssetCategories", + "views/maps/LayerCategoryListView", + "views/maps/LayerListView", + "/test/js/specs/shared/clean-state.js", +], ( + CesiumWidgetContainerView, + Map, + AssetCategories, + LayerCategoryListView, + LayerListView, + cleanState, +) => { + const expect = chai.expect; + + describe("CesiumWidgetContainerView Test Suite", () => { + const state = cleanState(() => { + const view = new CesiumWidgetContainerView({ + el: document.createElement("div"), + model: new Map(), + }); + + return { view }; + }, beforeEach); + + describe("Initialization", () => { + it("creates an CesiumWidgetContainerView instance", () => { + expect(state.view).to.be.instanceof(CesiumWidgetContainerView); + }); + }); + + describe("render", () => { + it("adds a Cesium widget to the DOM tree", () => { + state.view.render(); + + expect( + state.view.el.getElementsByClassName("cesium-widget"), + ).to.have.lengthOf(1); + }); + }); + }); +}); From 5b90a849249674db5d7270a6b272b8cf8203ea4e Mon Sep 17 00:00:00 2001 From: yvonnesjy Date: Fri, 7 Jun 2024 03:36:39 -0700 Subject: [PATCH 06/10] Fixup: Fix format and lint errors --- src/js/views/maps/MapView.js | 258 ++++++++---------- test/js/specs/unit/views/maps/MapView.spec.js | 4 + 2 files changed, 113 insertions(+), 149 deletions(-) diff --git a/src/js/views/maps/MapView.js b/src/js/views/maps/MapView.js index c2e901b80..63ef185fa 100644 --- a/src/js/views/maps/MapView.js +++ b/src/js/views/maps/MapView.js @@ -13,8 +13,8 @@ define([ "views/maps/FeatureInfoView", "views/maps/LayerDetailsView", // CSS - "text!" + MetacatUI.root + "/css/map-view.css", -], function ( + `text!${MetacatUI.root}/css/map-view.css`, +], ( $, _, Backbone, @@ -28,7 +28,7 @@ define([ LayerDetailsView, // CSS MapCSS, -) { +) => { const CLASS_NAMES = { mapWidgetContainer: "map-view__map-widget-container", scaleBarContainer: "map-view__scale-bar-container", @@ -44,12 +44,12 @@ define([ * data. * @classcategory Views/Maps * @name MapView - * @extends Backbone.View + * @augments Backbone.View * @screenshot views/maps/MapView.png * @since 2.18.0 * @constructs */ - var MapView = Backbone.View.extend( + const MapView = Backbone.View.extend( /** @lends MapView.prototype */ { /** * The type of View this is @@ -77,24 +77,21 @@ define([ /** * The events this view will listen to and the associated function to call. - * @type {Object} + * @type {object} */ events: { // 'event selector': 'function', }, /** - * @typedef {Object} ViewfinderViewOptions + * @typedef {object} ViewfinderViewOptions * @property {Map} model The map model that contains the configs for this map view. * @property {boolean} isPortalMap Indicates whether the map view is a part of a * portal, which is styled differently. */ - /** - * Executed when a new MapView is created - * @param {ViewfinderViewOptions} options - */ - initialize: function (options) { + /** @inheritdoc */ + initialize(options) { // Add the CSS required for this view and its sub-views. MetacatUI.appModel.addCSS(MapCSS, "mapView"); @@ -104,57 +101,47 @@ define([ /** * Renders this view - * @return {MapView} Returns the rendered view element + * @returns {MapView} Returns the rendered view element */ - render: function () { - try { - // Save a reference to this view - var view = this; + render() { + // Save a reference to this view + const view = this; - // TODO: Add a nice loading animation? + // TODO: Add a nice loading animation? - // Insert the template into the view - this.$el.html(this.template()); + // Insert the template into the view + this.$el.html(this.template()); - // Ensure the view's main element has the given class name - this.el.classList.add(this.className); - if (this.isPortalMap) { - this.el.classList.add(CLASS_NAMES.portalIndicator); - } - - // Select the elements that will be updatable - this.subElements = {}; - for (const [element, className] of Object.entries(CLASS_NAMES)) { - view.subElements[element] = document.querySelector("." + className); - } + // Ensure the view's main element has the given class name + this.el.classList.add(this.className); + if (this.isPortalMap) { + this.el.classList.add(CLASS_NAMES.portalIndicator); + } - // Render the (Cesium) map - this.renderMapWidget(); + // Select the elements that will be updatable + this.subElements = {}; + Object.entries(CLASS_NAMES).forEach(([element, className]) => { + view.subElements[element] = this.el.querySelector(`.${className}`); + }); - // Optionally add the toolbar, layer details, scale bar, and feature info box. - if (this.model.get("showToolbar")) { - this.renderToolbar(); - this.renderLayerDetails(); - } - if (this.model.get("showScaleBar")) { - this.renderScaleBar(); - } - if ( - this.model.get("showFeatureInfo") & - (this.model.get("clickFeatureAction") === "showDetails") - ) { - this.renderFeatureInfo(); - } + // Render the (Cesium) map + this.renderMapWidget(); - // Return this MapView - return this; - } catch (error) { - console.log( - "There was an error rendering a MapView" + - ". Error details: " + - error, - ); + // Optionally add the toolbar, layer details, scale bar, and feature info box. + if (this.model.get("showToolbar")) { + this.renderToolbar(); + this.renderLayerDetails(); + } + if (this.model.get("showScaleBar")) { + this.renderScaleBar(); } + if ( + this.model.get("showFeatureInfo") && + this.model.get("clickFeatureAction") === "showDetails" + ) { + this.renderFeatureInfo(); + } + return this; }, /** @@ -163,7 +150,7 @@ define([ * to use an alternative map widget in the future. * @returns {CesiumWidgetContainerView} Returns the rendered view */ - renderMapWidget: function () { + renderMapWidget() { this.mapWidgetContainer = new CesiumWidgetContainerView({ el: this.subElements.mapWidgetContainer, model: this.model, @@ -177,21 +164,13 @@ define([ * layer list. * @returns {ToolbarView} Returns the rendered view */ - renderToolbar: function () { - try { - this.toolbar = new ToolbarView({ - el: this.subElements.toolbarContainer, - model: this.model, - }); - this.toolbar.render(); - return this.toolbar; - } catch (error) { - console.log( - "There was an error rendering a toolbarView in a MapView" + - ". Error details: " + - error, - ); - } + renderToolbar() { + this.toolbar = new ToolbarView({ + el: this.subElements.toolbarContainer, + model: this.model, + }); + this.toolbar.render(); + return this.toolbar; }, /** @@ -200,33 +179,29 @@ define([ * the first one only. * @returns {FeatureInfoView} Returns the rendered view */ - renderFeatureInfo: function () { - try { - const view = this; - const interactions = view.model.get("interactions"); - const features = view.model.getSelectedFeatures(); - - view.featureInfo = new FeatureInfoView({ - el: view.subElements.featureInfoContainer, - model: features.at(0), - }).render(); - - // When the selectedFeatures collection changes, update the feature - // info view - view.stopListening(features, "update"); - view.listenTo(features, "update", function () { - view.featureInfo.changeModel(features.at(-1)); - }); + renderFeatureInfo() { + const view = this; + const interactions = view.model.get("interactions"); + const features = view.model.getSelectedFeatures(); + + view.featureInfo = new FeatureInfoView({ + el: view.subElements.featureInfoContainer, + model: features.at(0), + }).render(); + + // When the selectedFeatures collection changes, update the feature + // info view + view.stopListening(features, "update"); + view.listenTo(features, "update", () => { + view.featureInfo.changeModel(features.at(-1)); + }); - // If the Feature model is ever completely replaced for any reason, - // make the the Feature Info view gets updated. - const event = "change:selectedFeatures"; - view.stopListening(interactions, event); - view.listenTo(interactions, event, view.renderFeatureInfo); - return view.featureInfo; - } catch (e) { - console.log("Error rendering a FeatureInfoView in a MapView", e); - } + // If the Feature model is ever completely replaced for any reason, + // make the the Feature Info view gets updated. + const event = "change:selectedFeatures"; + view.stopListening(interactions, event); + view.listenTo(interactions, event, view.renderFeatureInfo); + return view.featureInfo; }, /** @@ -234,7 +209,7 @@ define([ * in the toolbar. * @returns {LayerDetailsView} Returns the rendered view */ - renderLayerDetails: function () { + renderLayerDetails() { this.layerDetails = new LayerDetailsView({ el: this.subElements.layerDetailsContainer, }); @@ -243,22 +218,18 @@ define([ // When a layer is selected, show the layer details panel. When a layer is // de-selected, close it. The Layer model's 'selected' attribute gets updated // from the Layer Item View, and also from the Layers collection. - for (const layers of this.model.getLayerGroups()) { + this.model.getLayerGroups().forEach((layers) => { this.stopListening(layers); - this.listenTo( - layers, - "change:selected", - function (layerModel, selected) { - if (selected === false) { - this.layerDetails.updateModel(null); - this.layerDetails.close(); - } else { - this.layerDetails.updateModel(layerModel); - this.layerDetails.open(); - } - }, - ); - } + this.listenTo(layers, "change:selected", (layerModel, selected) => { + if (selected === false) { + this.layerDetails.updateModel(null); + this.layerDetails.close(); + } else { + this.layerDetails.updateModel(layerModel); + this.layerDetails.open(); + } + }); + }); return this.layerDetails; }, @@ -266,47 +237,36 @@ define([ /** * Renders the scale bar view that shows the current position of the mouse on the * map. - * @returns {ScaleBarView} Returns the rendered view */ - renderScaleBar: function () { - try { - const interactions = this.model.get("interactions"); - if (!interactions) { - this.listenToOnce( - this.model, - "change:interactions", - this.renderScaleBar, - ); - return; - } - this.scaleBar = new ScaleBarView({ - el: this.subElements.scaleBarContainer, - scaleModel: interactions.get("scale"), - pointModel: interactions.get("mousePosition"), - }); - this.scaleBar.render(); - - // If the interaction model or relevant sub-models are ever completely - // replaced for any reason, re-render the scale bar. - this.listenToOnce( - interactions, - "change:scale change:mousePosition", - this.renderScaleBar, - ); + renderScaleBar() { + const interactions = this.model.get("interactions"); + if (!interactions) { this.listenToOnce( this.model, "change:interactions", this.renderScaleBar, ); - - return this.scaleBar; - } catch (error) { - console.log( - "There was an error rendering a ScaleBarView in a MapView" + - ". Error details: " + - error, - ); + return; } + this.scaleBar = new ScaleBarView({ + el: this.subElements.scaleBarContainer, + scaleModel: interactions.get("scale"), + pointModel: interactions.get("mousePosition"), + }); + this.scaleBar.render(); + + // If the interaction model or relevant sub-models are ever completely + // replaced for any reason, re-render the scale bar. + this.listenToOnce( + interactions, + "change:scale change:mousePosition", + this.renderScaleBar, + ); + this.listenToOnce( + this.model, + "change:interactions", + this.renderScaleBar, + ); }, /** @@ -315,7 +275,7 @@ define([ * Some may be undefined if they have not been rendered yet. * @since 2.27.0 */ - getSubViews: function () { + getSubViews() { return [ this.mapWidgetContainer, this.toolbar, @@ -329,7 +289,7 @@ define([ * Executed when the view is closed. This will close all of the sub-views. * @since 2.27.0 */ - onClose: function () { + onClose() { const subViews = this.getSubViews(); subViews.forEach((subView) => { if (subView && typeof subView.onClose === "function") { diff --git a/test/js/specs/unit/views/maps/MapView.spec.js b/test/js/specs/unit/views/maps/MapView.spec.js index da586a87b..db9d60e83 100644 --- a/test/js/specs/unit/views/maps/MapView.spec.js +++ b/test/js/specs/unit/views/maps/MapView.spec.js @@ -12,10 +12,14 @@ define(["views/maps/MapView", "models/maps/Map"], (MapView, MapAsset) => { describe("Portal map", () => { it("has an additional portal indicator class", () => { const nonPortalMap = new MapView(); + // Required for iFrame to not break in FeatureInfoView. + document.body.appendChild(nonPortalMap.el); nonPortalMap.render(); expect(nonPortalMap.$el.hasClass("map-view__portal")).to.be.false; const portalMap = new MapView({ isPortalMap: true }); + // Required for iFrame to not break in FeatureInfoView. + document.body.appendChild(portalMap.el); portalMap.render(); expect(portalMap.$el.hasClass("map-view__portal")).to.be.true; }); From 3e5ff2060ea2a05d80f90226598c5b6b28f279cd Mon Sep 17 00:00:00 2001 From: yvonnesjy Date: Fri, 7 Jun 2024 04:14:31 -0700 Subject: [PATCH 07/10] Fixup: Remove unused imports from test --- .../specs/unit/views/maps/CesiumWidgetContainerView.spec.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js b/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js index 51dd925e0..da484d533 100644 --- a/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js +++ b/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js @@ -1,16 +1,10 @@ define([ "views/maps/CesiumWidgetContainerView", "models/maps/Map", - "collections/maps/AssetCategories", - "views/maps/LayerCategoryListView", - "views/maps/LayerListView", "/test/js/specs/shared/clean-state.js", ], ( CesiumWidgetContainerView, Map, - AssetCategories, - LayerCategoryListView, - LayerListView, cleanState, ) => { const expect = chai.expect; From 4bb5238201f82ebdf8535b451470ce227266bc8d Mon Sep 17 00:00:00 2001 From: yvonnesjy Date: Fri, 7 Jun 2024 12:05:59 -0700 Subject: [PATCH 08/10] Fixup: Replace @since x.x.x with 0.0.0 To make lint happy --- src/js/views/maps/CesiumWidgetContainerView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/views/maps/CesiumWidgetContainerView.js b/src/js/views/maps/CesiumWidgetContainerView.js index 1e60df1ea..ba5948286 100644 --- a/src/js/views/maps/CesiumWidgetContainerView.js +++ b/src/js/views/maps/CesiumWidgetContainerView.js @@ -11,7 +11,7 @@ define([ * @classcategory Views/Maps * @name CesiumWidgetContainerView * @augments Backbone.View - * @since x.x.x + * @since 0.0.0 * @constructs */ const CesiumWidgetContainerView = Backbone.View.extend( From f2b7544d31d1c637237ee677e38ce1e5ef682841 Mon Sep 17 00:00:00 2001 From: yvonnesjy Date: Fri, 7 Jun 2024 14:11:09 -0700 Subject: [PATCH 09/10] Fixup: Rename CesiumWidgetContainerView ...to MapWidgetContainerView. This is so that this container can be reusable in the future if we swap out the underlying map framework. --- src/js/views/maps/MapView.js | 10 ++++----- ...ainerView.js => MapWidgetContainerView.js} | 22 +++++++++---------- test/config/tests.json | 2 +- ...spec.js => MapWidgetContainerView.spec.js} | 16 +++++--------- 4 files changed, 22 insertions(+), 28 deletions(-) rename src/js/views/maps/{CesiumWidgetContainerView.js => MapWidgetContainerView.js} (61%) rename test/js/specs/unit/views/maps/{CesiumWidgetContainerView.spec.js => MapWidgetContainerView.spec.js} (62%) diff --git a/src/js/views/maps/MapView.js b/src/js/views/maps/MapView.js index 63ef185fa..5cce070f2 100644 --- a/src/js/views/maps/MapView.js +++ b/src/js/views/maps/MapView.js @@ -7,7 +7,7 @@ define([ "models/maps/Map", "text!templates/maps/map.html", // SubViews - "views/maps/CesiumWidgetContainerView", + "views/maps/MapWidgetContainerView", "views/maps/ToolbarView", "views/maps/ScaleBarView", "views/maps/FeatureInfoView", @@ -21,7 +21,7 @@ define([ Map, Template, // SubViews - CesiumWidgetContainerView, + MapWidgetContainerView, ToolbarView, ScaleBarView, FeatureInfoView, @@ -146,12 +146,10 @@ define([ /** * Renders the view that shows the map/globe and all of the geo-spatial data. - * Currently, this uses the CesiumWidgetContainerView, but this function could be modified - * to use an alternative map widget in the future. - * @returns {CesiumWidgetContainerView} Returns the rendered view + * @returns {MapWidgetContainerView} Returns the rendered view */ renderMapWidget() { - this.mapWidgetContainer = new CesiumWidgetContainerView({ + this.mapWidgetContainer = new MapWidgetContainerView({ el: this.subElements.mapWidgetContainer, model: this.model, }); diff --git a/src/js/views/maps/CesiumWidgetContainerView.js b/src/js/views/maps/MapWidgetContainerView.js similarity index 61% rename from src/js/views/maps/CesiumWidgetContainerView.js rename to src/js/views/maps/MapWidgetContainerView.js index ba5948286..18f6bbc90 100644 --- a/src/js/views/maps/CesiumWidgetContainerView.js +++ b/src/js/views/maps/MapWidgetContainerView.js @@ -1,21 +1,21 @@ "use strict"; -define([ - "backbone", - "models/maps/Map", - "views/maps/CesiumWidgetView", -], (Backbone, Map, CesiumWidgetView) => { +define(["backbone", "models/maps/Map", "views/maps/CesiumWidgetView"], ( + Backbone, + Map, + CesiumWidgetView, +) => { /** - * @class CesiumWidgetContainerView + * @class MapWidgetContainerView * @classdesc A container for CesiumWidgetView and other map overlays, e.g. lat/lng, legends, etc. * @classcategory Views/Maps - * @name CesiumWidgetContainerView + * @name MapWidgetContainerView * @augments Backbone.View * @since 0.0.0 * @constructs */ - const CesiumWidgetContainerView = Backbone.View.extend( - /** @lends CesiumWidgetContainerView.prototype */ { + const MapWidgetContainerView = Backbone.View.extend( + /** @lends MapWidgetContainerView.prototype */ { /** * The model that this view uses * @type {Map} @@ -36,7 +36,7 @@ define([ this.renderMapWidget(this.el, this.model); }, - /** Renders Cesium map. */ + /** Renders Cesium map. Currently, this uses the MapWidgetContainerView, but this function could be modified to use an alternative map widget in the future. */ renderMapWidget() { const mapWidget = new CesiumWidgetView({ el: this.el, @@ -47,5 +47,5 @@ define([ }, ); - return CesiumWidgetContainerView; + return MapWidgetContainerView; }); diff --git a/test/config/tests.json b/test/config/tests.json index e2c8239fe..4cc33fab9 100644 --- a/test/config/tests.json +++ b/test/config/tests.json @@ -68,7 +68,7 @@ "./js/specs/unit/models/SolrResult.spec.js", "./js/specs/unit/models/DataONEObject.spec.js", "./js/specs/unit/views/maps/MapView.spec.js", - "./js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js" + "./js/specs/unit/views/maps/MapWidgetContainerView.spec.js" ], "integration": [ "./js/specs/integration/collections/SolrResults.spec.js", diff --git a/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js b/test/js/specs/unit/views/maps/MapWidgetContainerView.spec.js similarity index 62% rename from test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js rename to test/js/specs/unit/views/maps/MapWidgetContainerView.spec.js index da484d533..87fd2610e 100644 --- a/test/js/specs/unit/views/maps/CesiumWidgetContainerView.spec.js +++ b/test/js/specs/unit/views/maps/MapWidgetContainerView.spec.js @@ -1,17 +1,13 @@ define([ - "views/maps/CesiumWidgetContainerView", + "views/maps/MapWidgetContainerView", "models/maps/Map", "/test/js/specs/shared/clean-state.js", -], ( - CesiumWidgetContainerView, - Map, - cleanState, -) => { +], (MapWidgetContainerView, Map, cleanState) => { const expect = chai.expect; - describe("CesiumWidgetContainerView Test Suite", () => { + describe("MapWidgetContainerView Test Suite", () => { const state = cleanState(() => { - const view = new CesiumWidgetContainerView({ + const view = new MapWidgetContainerView({ el: document.createElement("div"), model: new Map(), }); @@ -20,8 +16,8 @@ define([ }, beforeEach); describe("Initialization", () => { - it("creates an CesiumWidgetContainerView instance", () => { - expect(state.view).to.be.instanceof(CesiumWidgetContainerView); + it("creates an MapWidgetContainerView instance", () => { + expect(state.view).to.be.instanceof(MapWidgetContainerView); }); }); From 06e19207f2cd12836db9466e8b72cd8deefd6479 Mon Sep 17 00:00:00 2001 From: yvonnesjy Date: Fri, 7 Jun 2024 15:15:15 -0700 Subject: [PATCH 10/10] Fixup: Hide map from the test page DOM --- test/js/specs/unit/views/maps/MapView.spec.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/js/specs/unit/views/maps/MapView.spec.js b/test/js/specs/unit/views/maps/MapView.spec.js index db9d60e83..6ae2565d3 100644 --- a/test/js/specs/unit/views/maps/MapView.spec.js +++ b/test/js/specs/unit/views/maps/MapView.spec.js @@ -13,13 +13,17 @@ define(["views/maps/MapView", "models/maps/Map"], (MapView, MapAsset) => { it("has an additional portal indicator class", () => { const nonPortalMap = new MapView(); // Required for iFrame to not break in FeatureInfoView. + nonPortalMap.$el.hide(); document.body.appendChild(nonPortalMap.el); + nonPortalMap.render(); expect(nonPortalMap.$el.hasClass("map-view__portal")).to.be.false; const portalMap = new MapView({ isPortalMap: true }); // Required for iFrame to not break in FeatureInfoView. + portalMap.$el.hide(); document.body.appendChild(portalMap.el); + portalMap.render(); expect(portalMap.$el.hasClass("map-view__portal")).to.be.true; });