From 22a4fe578c463b85d8abf8d6c13f21963ef63186 Mon Sep 17 00:00:00 2001 From: Gjore Milevski Date: Mon, 16 Dec 2024 14:55:15 +0100 Subject: [PATCH 1/7] Introduce the USWDS Site Alert as a info banner, use the USWDS Banner as intended --- .../components/common/banner/banner.scss | 3 + .../components/common/banner/index.tsx | 181 ++++++++++++------ .../components/common/layout-root/index.tsx | 13 +- .../components/common/site-alert/index.tsx | 82 ++++++++ .../components/common/uswds/banner.tsx | 10 - .../components/common/uswds/banner/index.tsx | 42 ++++ .../components/common/uswds/site-alert.tsx | 6 + app/scripts/components/home/index.tsx | 18 +- app/scripts/styles/styles.scss | 8 +- mock/veda.config.js | 37 +++- package.json | 2 + parcel-resolver-veda/index.d.ts | 40 ++-- parcel-resolver-veda/index.js | 51 +++-- yarn.lock | 22 ++- 14 files changed, 396 insertions(+), 119 deletions(-) create mode 100644 app/scripts/components/common/banner/banner.scss create mode 100644 app/scripts/components/common/site-alert/index.tsx delete mode 100644 app/scripts/components/common/uswds/banner.tsx create mode 100644 app/scripts/components/common/uswds/banner/index.tsx create mode 100644 app/scripts/components/common/uswds/site-alert.tsx diff --git a/app/scripts/components/common/banner/banner.scss b/app/scripts/components/common/banner/banner.scss new file mode 100644 index 000000000..4cd04cc1d --- /dev/null +++ b/app/scripts/components/common/banner/banner.scss @@ -0,0 +1,3 @@ +.usa-banner__button:after { + top: 3px; +} \ No newline at end of file diff --git a/app/scripts/components/common/banner/index.tsx b/app/scripts/components/common/banner/index.tsx index 6272bb57f..3dbaebd50 100644 --- a/app/scripts/components/common/banner/index.tsx +++ b/app/scripts/components/common/banner/index.tsx @@ -1,78 +1,141 @@ import React, { useState } from 'react'; -import { Icon } from '@trussworks/react-uswds'; +import { decode } from 'he'; import { USWDSBanner, - USWDSBannerContent + USWDSBannerContent, + USWDSBannerButton, + USWDSBannerFlag, + USWDSBannerHeader, + USWDSBannerIcon, + USWDSBannerGuidance, + USWDSMediaBlockBody } from '$components/common/uswds/banner'; -const BANNER_KEY = 'dismissedBannerUrl'; - -function hasExpired(expiryDatetime) { - const expiryDate = new Date(expiryDatetime); - const currentDate = new Date(); - return !!(currentDate > expiryDate); -} - -enum BannerType { - info = 'info', - warning = 'warning' +interface GuidanceContent { + icon: string; + iconAlt?: string; + title: string; + text: string; } -const infoTypeFlag = BannerType.info; interface BannerProps { - appTitle: string; - expires: Date; - url: string; - text: string; - type?: BannerType; + headerText?: string; + headerActionText?: string; + ariaLabel?: string; + flagImgAlt?: string; + leftGuidance?: GuidanceContent; + rightGuidance?: GuidanceContent; + className?: string; + defaultIsOpen?: boolean; + contentId?: string; } -export default function Banner({ - appTitle, - expires, - url, - text, - type = infoTypeFlag +const defaultGuidance = { + left: { + title: 'Official websites use .gov', + text: 'A .gov website belongs to an official government organization in the United States.', + iconAlt: 'Dot gov icon', + icon: '/img/icon-dot-gov.svg' + }, + right: { + title: 'Secure .gov websites use HTTPS', + text: `<> + A lock or https:// means you've safely + connected to the .gov website. Share sensitive information only on + official, secure websites. + `, + iconAlt: 'HTTPS icon', + icon: '/img/icon-https.svg' + } +}; + +export default function GovBanner({ + headerText, + headerActionText = "Here's how you know", + ariaLabel, + flagImgAlt = '', + leftGuidance, + rightGuidance, + className = '', + defaultIsOpen = false, + contentId = 'gov-banner-content' }: BannerProps) { + const [isOpen, setIsOpen] = useState(defaultIsOpen); - const showBanner = localStorage.getItem(BANNER_KEY) !== url; - const [isOpen, setIsOpen] = useState(showBanner && !hasExpired(expires)); + const defaultHeaderText = + 'An official website of the United States government'; - function onClose() { - localStorage.setItem(BANNER_KEY, url); - setIsOpen(false); - } + const leftContent = { + ...defaultGuidance.left, + ...leftGuidance + }; + + const rightContent = { + ...defaultGuidance.right, + ...rightGuidance + }; return ( -
- {isOpen && ( -
- - - -
+ + + } + headerText={headerText ?? defaultHeaderText} + headerActionText={headerActionText} + > + setIsOpen((prev) => !prev)} + aria-controls={contentId} + > + {headerActionText} + + + + +
+ + + +

+ {leftContent.title} +
+ +

+
+
- -
- -
- -
+ + + +

+ {rightContent.title} +
+ +

+
+
- )} -
+ +
); } diff --git a/app/scripts/components/common/layout-root/index.tsx b/app/scripts/components/common/layout-root/index.tsx index 9f67192bb..f69bbd47b 100644 --- a/app/scripts/components/common/layout-root/index.tsx +++ b/app/scripts/components/common/layout-root/index.tsx @@ -10,10 +10,15 @@ import { useDeepCompareEffect } from 'use-deep-compare'; import styled from 'styled-components'; import { Outlet } from 'react-router'; import { reveal } from '@devseed-ui/animation'; -import { getBannerFromVedaConfig, getCookieConsentFromVedaConfig } from 'veda'; +import { + getBannerFromVedaConfig, + getCookieConsentFromVedaConfig, + getSiteAlertFromVedaConfig +} from 'veda'; import MetaTags from '../meta-tags'; import PageFooter from '../page-footer'; const Banner = React.lazy(() => import('../banner')); +const SiteAlert = React.lazy(() => import('../site-alert')); const CookieConsent = React.lazy(() => import('../cookie-consent')); import { LayoutRootContext } from './context'; @@ -50,6 +55,7 @@ const PageBody = styled.div` function LayoutRoot(props: { children?: ReactNode }) { const cookieConsentContent = getCookieConsentFromVedaConfig(); const bannerContent = getBannerFromVedaConfig(); + const siteAlertContent = getSiteAlertFromVedaConfig(); const { children } = props; const [displayCookieConsentForm, setDisplayCookieConsentForm] = useState(true); @@ -74,8 +80,9 @@ function LayoutRoot(props: { children?: ReactNode }) { description={description || appDescription} thumbnail={thumbnail} /> - {bannerContent && ( - + {bannerContent && } + {siteAlertContent && ( + )} expiryDate); +} + +enum SiteAlertType { + info = 'info', + emergency = 'emergency' +} + +const infoTypeFlag = SiteAlertType.info; + +interface SiteAlertProps { + appTitle: string; + expires?: Date; + content: string; + type?: SiteAlertType; + heading?: string; + showIcon?: boolean; + slim?: boolean; + className?: string; +} + +export default function SiteAlertMessage({ + appTitle, + expires, + content, + type = infoTypeFlag, + heading, + showIcon = true, + slim = false, + className = '' +}: SiteAlertProps) { + const alertId = content; + const showAlert = localStorage.getItem(ALERT_KEY) !== alertId; + const [isOpen, setIsOpen] = useState(showAlert && !hasExpired(expires)); + + function onClose() { + localStorage.setItem(ALERT_KEY, alertId); + setIsOpen(false); + } + + return ( +
+ {isOpen && ( +
+ +
+ +
+ +
+
+ )} +
+ ); +} diff --git a/app/scripts/components/common/uswds/banner.tsx b/app/scripts/components/common/uswds/banner.tsx deleted file mode 100644 index ed6cb535e..000000000 --- a/app/scripts/components/common/uswds/banner.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; -import { Banner, BannerContent } from "@trussworks/react-uswds"; - -export function USWDSBanner (props) { - return ; -} - -export function USWDSBannerContent (props) { - return ; -} \ No newline at end of file diff --git a/app/scripts/components/common/uswds/banner/index.tsx b/app/scripts/components/common/uswds/banner/index.tsx new file mode 100644 index 000000000..375470421 --- /dev/null +++ b/app/scripts/components/common/uswds/banner/index.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { + Banner, + BannerContent, + BannerButton, + BannerFlag, + BannerHeader, + BannerIcon, + BannerGuidance, MediaBlockBody +} from '@trussworks/react-uswds'; + +export function USWDSBanner(props) { + return ; +} + +export function USWDSBannerContent(props) { + return ; +} + +export function USWDSBannerButton(props) { + return ; +} + +export function USWDSBannerFlag(props) { + return ; +} + +export function USWDSBannerHeader(props) { + return ; +} + +export function USWDSBannerIcon(props) { + return ; +} + +export function USWDSBannerGuidance(props) { + return ; +} + +export function USWDSMediaBlockBody(props) { + return ; +} diff --git a/app/scripts/components/common/uswds/site-alert.tsx b/app/scripts/components/common/uswds/site-alert.tsx new file mode 100644 index 000000000..926b30e66 --- /dev/null +++ b/app/scripts/components/common/uswds/site-alert.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { SiteAlert } from '@trussworks/react-uswds'; + +export function USWDSSiteAlert(props) { + return ; +} diff --git a/app/scripts/components/home/index.tsx b/app/scripts/components/home/index.tsx index a79767758..d32beaf59 100644 --- a/app/scripts/components/home/index.tsx +++ b/app/scripts/components/home/index.tsx @@ -5,7 +5,7 @@ import { Button } from '@devseed-ui/button'; import { glsp, listReset, media, themeVal } from '@devseed-ui/theme-provider'; import { Heading } from '@devseed-ui/typography'; import { CollecticonChevronRightSmall } from '@devseed-ui/collecticons'; -import { getOverride, getBannerFromVedaConfig } from 'veda'; +import { getOverride, getSiteAlertFromVedaConfig } from 'veda'; import rootCoverImage from '../../../graphics/layout/root-welcome--cover.jpg'; @@ -25,7 +25,6 @@ import { ContentOverride } from '$components/common/page-overrides'; - const homeContent = getOverride('homeContent'); const Connections = styled(Hug)` @@ -115,10 +114,10 @@ const getCoverProps = () => { return author ? { - ...coverProps, - attributionAuthor: author.name, - attributionUrl: author.url - } + ...coverProps, + attributionAuthor: author.name, + attributionUrl: author.url + } : coverProps; } else { return { @@ -134,14 +133,14 @@ const getCoverProps = () => { function RootHome() { const { show: showFeedbackModal } = useFeedbackModal(); - const banner = getBannerFromVedaConfig(); - const renderBanner = !!banner && banner.text && banner.url && banner.expires; + const siteAlert = getSiteAlertFromVedaConfig(); + const renderSiteAlert = !!siteAlert && siteAlert.content && siteAlert.expires; return ( - diff --git a/app/scripts/styles/styles.scss b/app/scripts/styles/styles.scss index 9e426c960..9d0b7f448 100644 --- a/app/scripts/styles/styles.scss +++ b/app/scripts/styles/styles.scss @@ -2,17 +2,19 @@ @use 'uswds-utilities'; @use 'usa-layout-grid'; -@use 'usa-banner'; @use 'usa-button'; +@use 'usa-icon'; @use 'usa-card'; -@use 'usa-alert'; @use 'usa-button-group'; -@use 'usa-icon'; @use 'usa-modal'; @use 'usa-header'; +@use 'usa-alert'; +@use 'usa-site-alert'; +@use 'usa-banner'; // Custom VEDA UI styles should be added here, so that they can be // picked up by Parcel and included in the final CSS bundle for the veda-ui library. @use "../components/common/page-header/page-header.scss"; +@use "../components/common/banner/banner.scss"; @use "../components/common/page-header/logo-container/logo-container.scss"; @use "../components/common/datepicker/datepicker.scss"; diff --git a/mock/veda.config.js b/mock/veda.config.js index 5aadadc95..4c847ae41 100644 --- a/mock/veda.config.js +++ b/mock/veda.config.js @@ -71,6 +71,21 @@ let subNavItems = [ } ]; +const defaultGuidance = { + left: { + title: 'Official websites use .gov', + text: 'A .gov website belongs to an official government organization in the United States.', + iconAlt: 'Dot gov icon', + icon: '/img/icon-dot-gov.svg' + }, + right: { + title: 'Secure .gov websites use HTTPS', + text: `A lock icon or https:// means you've safely connected to the .gov website. Share sensitive information only on official, secure websites.`, + iconAlt: 'HTTPS icon', + icon: '/img/icon-https.svg' + } +}; + if (config.GOOGLE_FORM) { subNavItems = [ ...subNavItems, @@ -103,10 +118,24 @@ module.exports = { } }, banner: { - text: 'Read the new data insight on using EMIT and AVIRIS-3 for monitoring large methane emission events.', - url: 'stories/emit-and-aviris-3', - expires: '2024-08-03T12:00:00-04:00', - type: 'info' + headerText: 'An official website of the United States government', + headerActionText: "Here's how you know", + ariaLabel: 'Banner for official government website', + flagImgSrc: '/img/us_flag_small.png', + flagImgAlt: 'US flag', + leftGuidance: defaultGuidance.left, + rightGuidance: defaultGuidance.right, + className: '', + defaultIsOpen: false, + contentId: 'gov-banner-content' + }, + siteAlert: { + content: + '

Read the new data insight on using EMIT and AVIRIS-3 for monitoring large methane emission events.

', + expires: '2026-08-03T12:00:00-04:00', + type: 'info', + slim: true, + showIcon: true }, navItems: { mainNavItems, diff --git a/package.json b/package.json index 831ffd913..7b19318b0 100644 --- a/package.json +++ b/package.json @@ -160,6 +160,7 @@ "@turf/simplify": "^6.5.0", "@turf/union": "^6.5.0", "@types/geojson": "^7946.0.10", + "@types/he": "^1.2.3", "@types/mdx": "^2.0.1", "@types/react": "18.0.32", "@types/react-dom": "18.0.11", @@ -181,6 +182,7 @@ "google-polyline": "^1.0.3", "gulp-postcss": "^10.0.0", "gulp-sass": "^6.0.0", + "he": "^1.2.0", "history": "^5.1.0", "intersection-observer": "^0.12.0", "jest-environment-jsdom": "^28.1.3", diff --git a/parcel-resolver-veda/index.d.ts b/parcel-resolver-veda/index.d.ts index a47e84c72..156df2c12 100644 --- a/parcel-resolver-veda/index.d.ts +++ b/parcel-resolver-veda/index.d.ts @@ -260,18 +260,37 @@ declare module 'veda' { * Since we are moving forward to ditching VEDA faux module */ - enum BannerType { + enum SiteAlertType { info = 'info', - warning = 'warning' + emergency = 'emergency' } - const infoTypeFlag = BannerType.info; - interface BannerData { + const infoTypeFlag = SiteAlertType.info; + interface SiteAlertData { expires: Date; title: string; - url: string; + content: string; + type?: SiteAlertType; + } + + interface BannerData { + headerText?: string; + headerActionText?: string; + ariaLabel?: string; + flagImgSrc: string; + flagImgAlt?: string; + leftGuidance?: GuidanceContent; + rightGuidance?: GuidanceContent; + className?: string; + defaultIsOpen?: boolean; + contentId?: string; + } + + interface GuidanceContent { + icon: string; + iconAlt?: string; + title: string; text: string; - type?: BannerType; } interface CookieConsentData { @@ -342,6 +361,7 @@ declare module 'veda' { export const getBoolean: (variable: string) => boolean; + export const getSiteAlertFromVedaConfig: () => SiteAlertData | undefined; export const getBannerFromVedaConfig: () => BannerData | undefined; export const getCookieConsentFromVedaConfig: () => | CookieConsentData @@ -349,12 +369,8 @@ declare module 'veda' { export const getNavItemsFromVedaConfig: () => | { - mainNavItems: - | (NavLinkItem | DropdownNavLink)[] - | undefined; - subNavItems: - | (NavLinkItem | DropdownNavLink)[] - | undefined; + mainNavItems: (NavLinkItem | DropdownNavLink)[] | undefined; + subNavItems: (NavLinkItem | DropdownNavLink)[] | undefined; } | undefined; diff --git a/parcel-resolver-veda/index.js b/parcel-resolver-veda/index.js index f00ca4028..90b2a64ae 100644 --- a/parcel-resolver-veda/index.js +++ b/parcel-resolver-veda/index.js @@ -86,7 +86,6 @@ function generateMdxDataObject(data) { function getCookieConsentForm(result) { if (!result.cookieConsentForm) return undefined; else { - const parsedCopy = md.render(result.cookieConsentForm.copy); const trimmedCopy = parsedCopy.replace(/(\r\n|\n|\r)/gm, ''); return JSON.stringify({ @@ -97,19 +96,45 @@ function getCookieConsentForm(result) { } } +function getSiteAlertContent(result) { + if (!result.siteAlert) return undefined; + + const { title, content, expires, type, slim, showIcon, className } = + result.siteAlert; + + const parsedText = content ? md.render(content) : ''; + const trimmedText = parsedText.replace(/(\r\n|\n|\r)/gm, ''); + return JSON.stringify({ + title, + content: trimmedText, + expires, + type, + slim, + showIcon, + className + }); +} + function getBannerContent(result) { if (!result.banner) return undefined; - else { - const parsedCopy = md.render(result.banner.text); - const trimmedCopy = parsedCopy.replace(/(\r\n|\n|\r)/gm, ''); - return JSON.stringify({ - title: result.banner.title, - text: trimmedCopy, - url: result.banner.url, - expires: result.banner.expires, - type: result.banner.type - }); - } + + const { title, text, leftGuidance, rightGuidance } = result.banner; + + const parsedText = text ? md.render(text) : ''; + const trimmedText = parsedText.replace(/(\r\n|\n|\r)/gm, ''); + + return JSON.stringify({ + headerText: title, + headerActionText: "Here's how you know", + ariaLabel: trimmedText || title, + flagImgSrc: '/img/us_flag_small.png', + flagImgAlt: '', + leftGuidance, + rightGuidance, + className: '', + defaultIsOpen: false, + contentId: 'gov-banner-content' + }); } // Using all the "key: path" combinations under config.pageOverrides, load the @@ -228,6 +253,7 @@ module.exports = new Resolver({ strings: ${JSON.stringify(withDefaultStrings(result.strings))}, booleans: ${JSON.stringify(withDefaultStrings(result.booleans))}, banner: ${getBannerContent(result)}, + siteAlert: ${getSiteAlertContent(result)}, navItems: ${JSON.stringify(result.navItems)}, cookieConsentForm: ${getCookieConsentForm(result)} }; @@ -248,6 +274,7 @@ module.exports = new Resolver({ export const getConfig = () => config; export const getBannerFromVedaConfig = () => config.banner; + export const getSiteAlertFromVedaConfig = () => config.siteAlert; export const getNavItemsFromVedaConfig = () => config.navItems; export const getCookieConsentFromVedaConfig = () => config.cookieConsentForm; diff --git a/yarn.lock b/yarn.lock index 480a48781..180b7d2b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4641,6 +4641,11 @@ dependencies: "@types/unist" "*" +"@types/he@^1.2.3": + version "1.2.3" + resolved "http://verdaccio.ds.io:4873/@types%2fhe/-/he-1.2.3.tgz#c33ca3096f30cbd5d68d78211572de3f9adff75a" + integrity sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA== + "@types/hoist-non-react-statics@*": version "3.3.1" resolved "http://verdaccio.ds.io:4873/@types%2fhoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" @@ -9417,6 +9422,11 @@ hat@0.0.3: resolved "http://verdaccio.ds.io:4873/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a" integrity sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo= +he@^1.2.0: + version "1.2.0" + resolved "http://verdaccio.ds.io:4873/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + history@^5.1.0, history@^5.2.0: version "5.3.0" resolved "http://verdaccio.ds.io:4873/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" @@ -11378,7 +11388,7 @@ macos-release@^3.1.0: resolved "http://verdaccio.ds.io:4873/macos-release/-/macos-release-3.3.0.tgz#92cb67bc66d67c3fde4a9e14f5f909afa418b072" integrity sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ== -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@~3.1.0: version "3.1.0" resolved "http://verdaccio.ds.io:4873/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -13400,10 +13410,10 @@ postcss-selector-parser@^6.0.6: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-selector-parser@^6.1.2: - version "6.1.2" - resolved "http://verdaccio.ds.io:4873/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" - integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== +postcss-selector-parser@^7.0.0: + version "7.0.0" + resolved "http://verdaccio.ds.io:4873/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz#41bd8b56f177c093ca49435f65731befe25d6b9c" + integrity sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -16895,7 +16905,7 @@ yaml@^2.4.2: resolved "http://verdaccio.ds.io:4873/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== -yargs-parser@>=5.0.0-security.0: +yargs-parser@21.1.1, yargs-parser@>=5.0.0-security.0: version "21.1.1" resolved "http://verdaccio.ds.io:4873/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== From bb4682ab6db4b42820dcf7097c4f17790b8aeba7 Mon Sep 17 00:00:00 2001 From: Gjore Milevski Date: Mon, 16 Dec 2024 15:08:12 +0100 Subject: [PATCH 2/7] Renaming --- app/scripts/components/common/banner/index.tsx | 2 +- app/scripts/components/common/site-alert/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/components/common/banner/index.tsx b/app/scripts/components/common/banner/index.tsx index 3dbaebd50..8346eb43a 100644 --- a/app/scripts/components/common/banner/index.tsx +++ b/app/scripts/components/common/banner/index.tsx @@ -49,7 +49,7 @@ const defaultGuidance = { } }; -export default function GovBanner({ +export default function Banner({ headerText, headerActionText = "Here's how you know", ariaLabel, diff --git a/app/scripts/components/common/site-alert/index.tsx b/app/scripts/components/common/site-alert/index.tsx index ac6bc58ae..af186c2d5 100644 --- a/app/scripts/components/common/site-alert/index.tsx +++ b/app/scripts/components/common/site-alert/index.tsx @@ -30,7 +30,7 @@ interface SiteAlertProps { className?: string; } -export default function SiteAlertMessage({ +export default function SiteAlert({ appTitle, expires, content, From 79e1683fb072ce201f0dfe71858152abbcab147d Mon Sep 17 00:00:00 2001 From: Sandra Hoang Date: Tue, 17 Dec 2024 15:54:14 -0500 Subject: [PATCH 3/7] nav callback for exploration for layer-info-modal --- .../components/datasets/dataset-list-item.tsx | 4 +++- .../components/datasets/dataset-list.tsx | 4 +++- .../components/layer-info-modal.tsx | 24 +++++++++---------- .../components/timeline/timeline.tsx | 6 +++-- .../components/exploration/container.tsx | 17 +++++++++---- app/scripts/components/exploration/index.tsx | 4 +++- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/app/scripts/components/exploration/components/datasets/dataset-list-item.tsx b/app/scripts/components/exploration/components/datasets/dataset-list-item.tsx index 34fab5edd..64390cda0 100644 --- a/app/scripts/components/exploration/components/datasets/dataset-list-item.tsx +++ b/app/scripts/components/exploration/components/datasets/dataset-list-item.tsx @@ -94,10 +94,11 @@ interface DatasetListItemProps { xScaled?: ScaleTime; onDragStart?: () => void; onDragEnd?: () => void; + onNavigation?: (path: string) => void; } export function DatasetListItem(props: DatasetListItemProps) { - const { datasetId, width, xScaled, onDragStart, onDragEnd } = props; + const { datasetId, width, xScaled, onDragStart, onDragEnd, onNavigation } = props; const datasetAtom = useTimelineDatasetAtom(datasetId); const dataset = useAtomValue(datasetAtom); @@ -235,6 +236,7 @@ export function DatasetListItem(props: DatasetListItemProps) { revealed={!!modalLayerInfo} close={() => setModalLayerInfo(undefined)} layerData={modalLayerInfo} + onNavigation={onNavigation} /> )} diff --git a/app/scripts/components/exploration/components/datasets/dataset-list.tsx b/app/scripts/components/exploration/components/datasets/dataset-list.tsx index b0c7b538d..dc1178757 100644 --- a/app/scripts/components/exploration/components/datasets/dataset-list.tsx +++ b/app/scripts/components/exploration/components/datasets/dataset-list.tsx @@ -16,10 +16,11 @@ const DatasetListSelf = styled.ul` interface DatasetListProps { width: number; xScaled?: ScaleTime; + onNavigation?: (path: string) => void; } export function DatasetList(props: DatasetListProps) { - const { width, xScaled } = props; + const { width, xScaled, onNavigation } = props; const [isDragging, setIsDragging] = useState(false); const [datasets, setDatasets] = useAtom(timelineDatasetsAtom); @@ -40,6 +41,7 @@ export function DatasetList(props: DatasetListProps) { xScaled={xScaled} onDragStart={() => setIsDragging(true)} onDragEnd={() => setIsDragging(false)} + onNavigation={onNavigation} /> ))} diff --git a/app/scripts/components/exploration/components/layer-info-modal.tsx b/app/scripts/components/exploration/components/layer-info-modal.tsx index 977bbd254..8a4b8e10c 100644 --- a/app/scripts/components/exploration/components/layer-info-modal.tsx +++ b/app/scripts/components/exploration/components/layer-info-modal.tsx @@ -9,9 +9,8 @@ import { ModalHeadline } from '@devseed-ui/modal'; import { glsp, themeVal } from '@devseed-ui/theme-provider'; -import { createButtonStyles } from '@devseed-ui/button'; +import { Button } from '@devseed-ui/button'; import { LayerInfo } from 'veda'; - import { getDatasetPath } from '$utils/routes'; import { CollecticonDatasetLayers } from '$components/common/icons/dataset-layers'; import { ParentDatasetTitle } from '$components/common/catalog/catalog-content'; @@ -53,13 +52,6 @@ const ParentDatasetHeading = styled.h2` padding: ${glsp(0.5)} 0; `; -// override with 'as' as LinkComponent -const ButtonStyleLink = styled.a` - &&& { - ${({ variation, size }) => createButtonStyles({ variation, size })} - } -`; - export interface LayerInfoModalData { name: string; info?: LayerInfo; @@ -75,6 +67,7 @@ interface LayerInfoModalProps { revealed: boolean; close: () => void; layerData: LayerInfoModalData + onNavigation?: (path: string) => void; } export function LayerInfoLiner(props: { info: LayerInfo }) { @@ -100,10 +93,17 @@ const LayerInfoLinerModal = styled.div` `; export default function LayerInfoModal(props: LayerInfoModalProps) { - const { revealed, close, layerData } = props; + const { revealed, close, layerData, onNavigation } = props; const { parentData } = layerData; const dataCatalogPage = getDatasetPath(parentData.id); + + const handleButtonClick = () => { + close(); + if (onNavigation) { + onNavigation(dataCatalogPage) + } + }; return ( } footerContent={ - + } /> ); diff --git a/app/scripts/components/exploration/components/timeline/timeline.tsx b/app/scripts/components/exploration/components/timeline/timeline.tsx index 8655f34bb..4881a6915 100644 --- a/app/scripts/components/exploration/components/timeline/timeline.tsx +++ b/app/scripts/components/exploration/components/timeline/timeline.tsx @@ -170,6 +170,7 @@ interface TimelineProps { setSelectedCompareDay: (d: Date | null) => void; onDatasetAddClick?: () => void; panelHeight: number; + onNavigation?: (path: string) => void; } const getIntervalFromDate = (selectedDay: Date, dataDomain: [Date, Date]) => { @@ -200,7 +201,8 @@ export default function Timeline(props: TimelineProps) { selectedCompareDay, setSelectedCompareDay, onDatasetAddClick, - panelHeight + panelHeight, + onNavigation, } = props; // Refs for non react based interactions. @@ -798,7 +800,7 @@ export default function Timeline(props: TimelineProps) { ref={datasetsContainerRef} panelHeight={panelHeight} > - + diff --git a/app/scripts/components/exploration/container.tsx b/app/scripts/components/exploration/container.tsx index b8439c738..16020a585 100644 --- a/app/scripts/components/exploration/container.tsx +++ b/app/scripts/components/exploration/container.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { TourProvider } from '@reactour/tour'; +import { useNavigate } from "react-router-dom"; import { DevTools } from 'jotai-devtools'; import { useAtom, useSetAtom } from 'jotai'; import { PopoverTourComponent, TourManager } from './tour-manager'; @@ -39,6 +40,7 @@ export default function ExplorationAndAnalysisContainer() { const [datasetModalRevealed, setDatasetModalRevealed] = useState( !timelineDatasets.length ); + const navigate = useNavigate(); // @NOTE: When Exploration page is preloaded (ex. Linked with react-router) // atomWithLocation gets initialized outside of Exploration page and returns the previous page's value @@ -49,6 +51,15 @@ export default function ExplorationAndAnalysisContainer() { const openModal = () => setDatasetModalRevealed(true); const closeModal = () => setDatasetModalRevealed(false); + const handleNavigation = (path: string) => { + navigate(path); + }; + + const linkProps = { + LinkElement: SmartLink, + pathAttributeKeyName: 'to' + }; + return (
diff --git a/app/scripts/components/exploration/index.tsx b/app/scripts/components/exploration/index.tsx index 5e4242cba..a809507f3 100644 --- a/app/scripts/components/exploration/index.tsx +++ b/app/scripts/components/exploration/index.tsx @@ -59,10 +59,11 @@ interface ExplorationAndAnalysisProps { datasets: TimelineDataset[]; setDatasets: (datasets: TimelineDataset[]) => void; openDatasetsSelectionModal?: () => void; + onNavigation?: (path: string) => void; } function ExplorationAndAnalysis(props: ExplorationAndAnalysisProps) { - const { datasets, setDatasets, openDatasetsSelectionModal } = props; + const { datasets, setDatasets, openDatasetsSelectionModal, onNavigation } = props; const [selectedDay, setSelectedDay] = useAtom(selectedDateAtom); @@ -111,6 +112,7 @@ function ExplorationAndAnalysis(props: ExplorationAndAnalysisProps) { setSelectedCompareDay={setSelectedCompareDay} onDatasetAddClick={openDatasetsSelectionModal} panelHeight={panelHeight} + onNavigation={onNavigation} /> From ea766a188495cad69ff89464442ca825dc19fa10 Mon Sep 17 00:00:00 2001 From: Sandra Hoang Date: Tue, 17 Dec 2024 16:13:54 -0500 Subject: [PATCH 4/7] fix lint --- .../components/exploration/components/layer-info-modal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/scripts/components/exploration/components/layer-info-modal.tsx b/app/scripts/components/exploration/components/layer-info-modal.tsx index 8a4b8e10c..75a6f49a8 100644 --- a/app/scripts/components/exploration/components/layer-info-modal.tsx +++ b/app/scripts/components/exploration/components/layer-info-modal.tsx @@ -101,9 +101,10 @@ export default function LayerInfoModal(props: LayerInfoModalProps) { const handleButtonClick = () => { close(); if (onNavigation) { - onNavigation(dataCatalogPage) + onNavigation(dataCatalogPage); } }; + return ( Date: Wed, 18 Dec 2024 09:35:54 +0100 Subject: [PATCH 5/7] Address comments --- .../components/common/banner/index.tsx | 101 +++++++++--------- .../components/common/site-alert/index.tsx | 5 +- mock/veda.config.js | 6 +- 3 files changed, 54 insertions(+), 58 deletions(-) diff --git a/app/scripts/components/common/banner/index.tsx b/app/scripts/components/common/banner/index.tsx index 8346eb43a..127d6b1fd 100644 --- a/app/scripts/components/common/banner/index.tsx +++ b/app/scripts/components/common/banner/index.tsx @@ -11,11 +11,16 @@ import { USWDSMediaBlockBody } from '$components/common/uswds/banner'; +interface Guidance { + left?: GuidanceContent; + right?: GuidanceContent; +} + interface GuidanceContent { - icon: string; + icon?: string; iconAlt?: string; - title: string; - text: string; + title?: string; + text?: string; } interface BannerProps { @@ -30,7 +35,12 @@ interface BannerProps { contentId?: string; } -const defaultGuidance = { +const DEFAULT_HEADER_TEXT = + 'An official website of the United States government'; + +const DEFAULT_HEADER_ACTION_TEXT = "Here's how you know"; + +const DEFAULT_GUIDANCE: Guidance = { left: { title: 'Official websites use .gov', text: 'A .gov website belongs to an official government organization in the United States.', @@ -39,19 +49,40 @@ const defaultGuidance = { }, right: { title: 'Secure .gov websites use HTTPS', - text: `<> + text: ` A lock or https:// means you've safely connected to the .gov website. Share sensitive information only on official, secure websites. - `, + `, iconAlt: 'HTTPS icon', icon: '/img/icon-https.svg' } }; +const GuidanceBlock = ({ + content, + className +}: { + content: GuidanceContent; + className?: string; +}) => ( + + + +

+ {content.title} +
+ +

+
+
+); + export default function Banner({ headerText, - headerActionText = "Here's how you know", + headerActionText, ariaLabel, flagImgAlt = '', leftGuidance, @@ -62,22 +93,19 @@ export default function Banner({ }: BannerProps) { const [isOpen, setIsOpen] = useState(defaultIsOpen); - const defaultHeaderText = - 'An official website of the United States government'; - const leftContent = { - ...defaultGuidance.left, + ...DEFAULT_GUIDANCE.left, ...leftGuidance - }; + } as GuidanceContent; const rightContent = { - ...defaultGuidance.right, + ...DEFAULT_GUIDANCE.right, ...rightGuidance - }; + } as GuidanceContent; return ( } - headerText={headerText ?? defaultHeaderText} - headerActionText={headerActionText} + headerText={headerText ?? DEFAULT_HEADER_TEXT} + headerActionText={headerActionText ?? DEFAULT_HEADER_ACTION_TEXT} > setIsOpen((prev) => !prev)} aria-controls={contentId} > - {headerActionText} + {headerActionText ?? DEFAULT_HEADER_ACTION_TEXT}
- - - -

- {leftContent.title} -
- -

-
-
- - - - -

- {rightContent.title} -
- -

-
-
+ +
diff --git a/app/scripts/components/common/site-alert/index.tsx b/app/scripts/components/common/site-alert/index.tsx index af186c2d5..07b1ec63d 100644 --- a/app/scripts/components/common/site-alert/index.tsx +++ b/app/scripts/components/common/site-alert/index.tsx @@ -40,12 +40,11 @@ export default function SiteAlert({ slim = false, className = '' }: SiteAlertProps) { - const alertId = content; - const showAlert = localStorage.getItem(ALERT_KEY) !== alertId; + const showAlert = localStorage.getItem(ALERT_KEY) !== content; const [isOpen, setIsOpen] = useState(showAlert && !hasExpired(expires)); function onClose() { - localStorage.setItem(ALERT_KEY, alertId); + localStorage.setItem(ALERT_KEY, content); setIsOpen(false); } diff --git a/mock/veda.config.js b/mock/veda.config.js index 4c847ae41..33c8d8a9c 100644 --- a/mock/veda.config.js +++ b/mock/veda.config.js @@ -130,8 +130,10 @@ module.exports = { contentId: 'gov-banner-content' }, siteAlert: { - content: - '

Read the new data insight on using EMIT and AVIRIS-3 for monitoring large methane emission events.

', + content: `

+ + Discover insights on how the COVID-19 pandemic + impacted air quality worldwide, observed through NASA's satellite data.

`, expires: '2026-08-03T12:00:00-04:00', type: 'info', slim: true, From a007d43fce7be0a53667bce7ccf049a2d32e71e4 Mon Sep 17 00:00:00 2001 From: Hanbyul Jo Date: Wed, 18 Dec 2024 08:36:44 -0500 Subject: [PATCH 6/7] fix: specify repos for actions, add yarn.lock (#1334) Our `yarn.lock` doesn't seem to be up to date. (Release is failing because working dir is not clean : https://github.com/NASA-IMPACT/veda-ui/actions/runs/12380947841/job/34558416499) Specify repos for action so it doesn't try to access all the repos under nasa-impact --- .github/workflows/release.yml | 3 +++ yarn.lock | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 01f46d77c..d74622d30 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,6 +21,9 @@ jobs: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PEM }} owner: ${{ github.repository_owner }} + repositories: | + veda-ui + veda-config - uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/yarn.lock b/yarn.lock index 480a48781..1e8e6c43d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11378,7 +11378,7 @@ macos-release@^3.1.0: resolved "http://verdaccio.ds.io:4873/macos-release/-/macos-release-3.3.0.tgz#92cb67bc66d67c3fde4a9e14f5f909afa418b072" integrity sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ== -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@~3.1.0: version "3.1.0" resolved "http://verdaccio.ds.io:4873/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -13400,10 +13400,10 @@ postcss-selector-parser@^6.0.6: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-selector-parser@^6.1.2: - version "6.1.2" - resolved "http://verdaccio.ds.io:4873/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" - integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== +postcss-selector-parser@^7.0.0: + version "7.0.0" + resolved "http://verdaccio.ds.io:4873/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz#41bd8b56f177c093ca49435f65731befe25d6b9c" + integrity sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -16895,7 +16895,7 @@ yaml@^2.4.2: resolved "http://verdaccio.ds.io:4873/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== -yargs-parser@>=5.0.0-security.0: +yargs-parser@21.1.1, yargs-parser@>=5.0.0-security.0: version "21.1.1" resolved "http://verdaccio.ds.io:4873/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== From e586f5009f2c30ed5ff163a3715f6701a71ff221 Mon Sep 17 00:00:00 2001 From: hanbyul-here Date: Wed, 18 Dec 2024 14:05:44 +0000 Subject: [PATCH 7/7] chore(release): update to version v5.11.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 831ffd913..08374a7cf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@developmentseed/veda-ui", "description": "Dashboard", - "version": "5.11.3", + "version": "5.11.4", "author": { "name": "Development Seed", "url": "https://developmentseed.org/"