From 6ce5f2163d743ae13172d8263ddb0620be06355e Mon Sep 17 00:00:00 2001 From: Sandra Hoang Date: Mon, 26 Aug 2024 15:36:35 -0700 Subject: [PATCH] update slidingStickyHeaderProps and pass in element for ElementInteractive --- app/scripts/components/common/card/index.tsx | 5 ++- .../common/catalog/catalog-card.tsx | 4 +- .../common/catalog/catalog-content.tsx | 12 ++++-- .../controls/hooks/use-filters-with-query.ts | 23 +++++++++--- .../common/catalog/filters-control.tsx | 17 +++++---- .../components/common/catalog/index.tsx | 25 ++++++------- .../components/common/element-interactive.js | 37 +++++++------------ .../components/common/layout-root/context.tsx | 4 +- .../components/data-catalog/container.tsx | 22 ++++++++++- app/scripts/components/stories/hub/index.tsx | 3 +- app/scripts/index.ts | 6 ++- .../utils/use-sliding-sticky-header.ts | 27 ++++++-------- 12 files changed, 109 insertions(+), 76 deletions(-) diff --git a/app/scripts/components/common/card/index.tsx b/app/scripts/components/common/card/index.tsx index e0fde1bb6..9d038af5b 100644 --- a/app/scripts/components/common/card/index.tsx +++ b/app/scripts/components/common/card/index.tsx @@ -237,6 +237,7 @@ export interface CardComponentProps { footerContent?: JSX.Element; onCardClickCapture?: MouseEventHandler; onLinkClick?: MouseEventHandler; + CardInteractiveElement?: any; } function CardComponent(props: CardComponentProps) { @@ -255,7 +256,8 @@ function CardComponent(props: CardComponentProps) { parentTo, footerContent, onCardClickCapture, - onLinkClick + onLinkClick, + CardInteractiveElement, } = props; const isExternalLink = /^https?:\/\//.test(linkTo); @@ -270,6 +272,7 @@ function CardComponent(props: CardComponentProps) { linkLabel={linkLabel ?? 'View more'} linkProps={linkProps} onClickCapture={onCardClickCapture} + CardInteractiveElement={CardInteractiveElement} > { cardType !== 'horizontal-info' && ( diff --git a/app/scripts/components/common/catalog/catalog-card.tsx b/app/scripts/components/common/catalog/catalog-card.tsx index 3e45378b4..540a8acf8 100644 --- a/app/scripts/components/common/catalog/catalog-card.tsx +++ b/app/scripts/components/common/catalog/catalog-card.tsx @@ -20,6 +20,7 @@ interface CatalogCardProps { selectable?: boolean; selected?: boolean; onDatasetClick?: () => void; + CardInteractiveElement?: any; } const CardSelectable = styled(Card)<{ checked?: boolean, selectable?: boolean }>` @@ -90,7 +91,7 @@ const CardSelectable = styled(Card)<{ checked?: boolean, selectable?: boolean }> `; export const CatalogCard = (props: CatalogCardProps) => { - const { dataset, layer, searchTerm, selectable, selected, onDatasetClick } = props; + const { dataset, layer, searchTerm, selectable, selected, onDatasetClick, CardInteractiveElement } = props; const topics = getTaxonomy(dataset, TAXONOMY_TOPICS)?.values; const sources = getTaxonomy(dataset, TAXONOMY_SOURCE)?.values; @@ -153,6 +154,7 @@ export const CatalogCard = (props: CatalogCardProps) => { ) : null} } + CardInteractiveElement={CardInteractiveElement} /> ); }; diff --git a/app/scripts/components/common/catalog/catalog-content.tsx b/app/scripts/components/common/catalog/catalog-content.tsx index 99d7ccb87..d8598f8fc 100644 --- a/app/scripts/components/common/catalog/catalog-content.tsx +++ b/app/scripts/components/common/catalog/catalog-content.tsx @@ -34,6 +34,8 @@ export interface CatalogContentProps { search: string; taxonomies: Record; onAction: (action: FilterActions, value?: any) => void; + location?: any; + CardInteractiveElement?: any; } const DEFAULT_SORT_OPTION = 'asc'; @@ -70,10 +72,12 @@ function CatalogContent({ search, taxonomies, onAction, + location, + CardInteractiveElement, }: CatalogContentProps) { const [exclusiveSourceSelected, setExclusiveSourceSelected] = useState(null); const isSelectable = selectedIds !== undefined; - + const datasetTaxonomies = generateTaxonomies(datasets); const urlTaxonomyItems = taxonomies ? Object.entries(taxonomies).map(([key, val]) => getTaxonomyByIds(key, val, datasetTaxonomies)).flat() : []; @@ -188,7 +192,7 @@ function CatalogContent({ const getSelectedLayerCount = (dataset) => { return dataset.layers.filter((layer) => selectedIds?.includes(layer.id)).length; }; - console.log(`LOG_FOR_TEST_DATASETS: `, datasets) + return ( onCardSelect(datasetLayer.id, currentDataset)} + onDatasetClick={() => onCardSelect(datasetLayer.id, currentDataset)} + CardInteractiveElement={CardInteractiveElement} /> ))} diff --git a/app/scripts/components/common/catalog/controls/hooks/use-filters-with-query.ts b/app/scripts/components/common/catalog/controls/hooks/use-filters-with-query.ts index 393adf58e..77cac6fae 100644 --- a/app/scripts/components/common/catalog/controls/hooks/use-filters-with-query.ts +++ b/app/scripts/components/common/catalog/controls/hooks/use-filters-with-query.ts @@ -1,6 +1,5 @@ import { useAtom } from 'jotai'; import { useCallback } from 'react'; -import { useNavigate } from 'react-router'; import useQsStateCreator from 'qs-state-hook'; import { taxonomyAtom } from '../atoms/taxonomy-atom'; import { searchAtom } from '../atoms/search-atom'; @@ -33,11 +32,23 @@ export function useFiltersWithURLAtom(): UseFiltersWithQueryResult { }; } -export function useFiltersWithQS(): UseFiltersWithQueryResult { - const navigate = useNavigate(); - const useQsState = useQsStateCreator({ - commit: navigate - }); + export function useFiltersWithQS({ + navigate, + push=false + }: { + navigate: any, + push?: boolean, + }): UseFiltersWithQueryResult { + + let navCommit = navigate; + + if (push) { + navCommit = ({ search }) => navigate.push(`?${search}`); + } + + const useQsState = useQsStateCreator({ + commit: navCommit + }); const [search, setSearch] = useQsState.memo( { diff --git a/app/scripts/components/common/catalog/filters-control.tsx b/app/scripts/components/common/catalog/filters-control.tsx index 7c27cde66..b9ff06d2e 100644 --- a/app/scripts/components/common/catalog/filters-control.tsx +++ b/app/scripts/components/common/catalog/filters-control.tsx @@ -1,10 +1,11 @@ import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'; +import { Location } from 'react-router'; import styled from 'styled-components'; import { FilterActions } from './utils'; import { Taxonomy } from '$types/veda'; import SearchField from '$components/common/search-field'; import CheckableFilters, { OptionItem } from '$components/common/form/checkable-filter'; -// import { useSlidingStickyHeader, HEADER_TRANSITION_DURATION } from '$utils/use-sliding-sticky-header'; +import { useSlidingStickyHeader, HEADER_TRANSITION_DURATION } from '$utils/use-sliding-sticky-header'; const ControlsWrapper = styled.div<{ widthValue?: string; heightValue?: string; topValue: string }>` min-width: 20rem; @@ -12,8 +13,7 @@ const ControlsWrapper = styled.div<{ widthValue?: string; heightValue?: string; position: sticky; top: calc(${props => props.topValue} + 1rem); height: ${props => props.heightValue}; - // transition: top ${HEADER_TRANSITION_DURATION}ms ease-out; - transition: top 1000ms ease-out; + transition: top ${HEADER_TRANSITION_DURATION}ms ease-out; `; interface FiltersMenuProps { @@ -28,6 +28,7 @@ interface FiltersMenuProps { exclusiveSourceSelected?: string | null; customTopOffset?: number; openByDefault?: boolean; + location?: Location } export default function FiltersControl(props: FiltersMenuProps) { @@ -47,12 +48,13 @@ export default function FiltersControl(props: FiltersMenuProps) { // has a different header reference as opposed to what the useSlidingStickyHeader hook // uses as a reference (the main page header). To avoid changing the reference IDs in the // main logic of the sliding sticky header hook, we provide this custom top offset for more control. - customTopOffset = 0 + customTopOffset = 0, + location, } = props; const controlsRef = useRef(null); const [controlsHeight, setControlsHeight] = useState(0); - // const { isHeaderHidden, wrapperHeight } = useSlidingStickyHeader(); + const { isHeaderHidden, wrapperHeight } = useSlidingStickyHeader(location); const handleChanges = useCallback((item: OptionItem, action: 'add' | 'remove') => { const isSelected = allSelected.some(selected => selected.id === item.id && selected.taxonomy === item.taxonomy); @@ -96,10 +98,9 @@ export default function FiltersControl(props: FiltersMenuProps) { } // eslint-disable-next-line react-hooks/exhaustive-deps }, [exclusiveSourceSelected]); - + return ( - // - +
| Record, onAction: () => void, } | any; + location?: any; + CardInteractiveElement?: any; } function CatalogView({ datasets, - onFilterChanges + onFilterChanges, + location, + CardInteractiveElement, }: CatalogViewProps) { - // const { headerHeight } = useSlidingStickyHeaderProps(); - // const { isHeaderHidden, wrapperHeight } = useSlidingStickyHeader(); + const { headerHeight } = useSlidingStickyHeaderProps(); - // const { search, taxonomies , onAction } = useFiltersWithQS(); const { search, taxonomies , onAction } = onFilterChanges(); - console.log(`taxonomies: `, taxonomies) - const headerHeight = 60; return ( @@ -75,9 +73,8 @@ function CatalogView({ search={search} taxonomies={taxonomies} onAction={onAction} - search={''} - taxonomies={{}} - onAction={() => true} + location={location} + CardInteractiveElement={CardInteractiveElement} /> ); diff --git a/app/scripts/components/common/element-interactive.js b/app/scripts/components/common/element-interactive.js index d52b74879..0be7326f6 100644 --- a/app/scripts/components/common/element-interactive.js +++ b/app/scripts/components/common/element-interactive.js @@ -2,15 +2,6 @@ import React, { useCallback, useState } from 'react'; import T from 'prop-types'; import styled from 'styled-components'; -// const InteractiveLink = styled.a` -// position: absolute; -// inset: 0; -// z-index: -1; -// pointer-events: auto; -// font-size: 0; -// margin: 0; -// `; - export const Wrapper = styled.div` position: relative; z-index: 1; @@ -73,11 +64,19 @@ export const Wrapper = styled.div` */ export const ElementInteractive = React.forwardRef( function ElementInteractiveFwd(props, ref) { - const { linkProps = {}, linkLabel = 'View', children, ...rest } = props; + const { + linkProps = {}, + linkLabel = 'View', + children, + CardInteractiveElement, + ...rest + } = props; const [isStateOver, setStateOver] = useState(false); const [isStateActive, setStateActive] = useState(false); const [isStateFocus, setStateFocus] = useState(false); - console.log(`linkLabel: `, linkLabel) + + const CardInteractive = CardInteractiveElement ?? 'a'; + return ( {children} - {/* setStateActive(true), [])} - onMouseUp={useCallback(() => setStateActive(false), [])} - onFocus={useCallback(() => setStateFocus(true), [])} - onBlur={useCallback(() => setStateFocus(false), [])} - > - {linkLabel} - */} - setStateActive(true), [])} onMouseUp={useCallback(() => setStateActive(false), [])} @@ -111,7 +101,7 @@ export const ElementInteractive = React.forwardRef( onBlur={useCallback(() => setStateFocus(false), [])} > {linkLabel} - + ); } @@ -120,7 +110,8 @@ export const ElementInteractive = React.forwardRef( ElementInteractive.propTypes = { children: T.node.isRequired, linkLabel: T.string.isRequired, - linkProps: T.object + linkProps: T.object, + CardInteractiveElement: T.any }; /** diff --git a/app/scripts/components/common/layout-root/context.tsx b/app/scripts/components/common/layout-root/context.tsx index 1c95587a6..15f6df70a 100644 --- a/app/scripts/components/common/layout-root/context.tsx +++ b/app/scripts/components/common/layout-root/context.tsx @@ -5,6 +5,7 @@ import React, { SetStateAction, ReactNode, } from 'react'; +import { useLocation } from 'react-router'; import { useSlidingStickyHeader } from '$utils/use-sliding-sticky-header'; interface LayoutRootContextProps extends Record { @@ -29,8 +30,9 @@ export function LayoutRootContextProvider({ // Put the header size and visibility status in the context so that children // elements can access them for positioning purposes. + const location = useLocation(); const { isHeaderHidden, headerHeight, wrapperHeight } = - useSlidingStickyHeader(); + useSlidingStickyHeader(location); const ctx = { ...layoutProps, diff --git a/app/scripts/components/data-catalog/container.tsx b/app/scripts/components/data-catalog/container.tsx index 7f1a80fa1..387e62cc5 100644 --- a/app/scripts/components/data-catalog/container.tsx +++ b/app/scripts/components/data-catalog/container.tsx @@ -1,5 +1,7 @@ import React from 'react'; +import { useNavigate, useLocation } from 'react-router'; import { getString } from 'veda'; +import styled from 'styled-components'; import { getAllDatasetsProps } from '$utils/veda-data'; import CatalogView from '$components/common/catalog'; import { PageMainContent } from '$styles/page'; @@ -16,16 +18,32 @@ import { useFiltersWithQS } from '$components/common/catalog/controls/hooks/use- * veda2 instances can just use the direct component, 'DataCatalog', and manage data directly in their page views */ +const InteractiveLink = styled.a` + position: absolute; + inset: 0; + z-index: -1; + pointer-events: auto; + font-size: 0; + margin: 0; +`; + export default function DataCatalogContainer() { const allDatasets = getAllDatasetsProps(veda_faux_module_datasets); - // const { search, taxonomies , onAction } = useFiltersWithQS(); + const pathName = useLocation(); + const navigate = useNavigate(); + const controlVars = useFiltersWithQS({navigate: navigate}); return ( - + controlVars} + location={pathName} + CardInteractiveElement={InteractiveLink} + /> ); } \ No newline at end of file diff --git a/app/scripts/components/stories/hub/index.tsx b/app/scripts/components/stories/hub/index.tsx index 08b49b8c6..720a0416d 100644 --- a/app/scripts/components/stories/hub/index.tsx +++ b/app/scripts/components/stories/hub/index.tsx @@ -1,4 +1,5 @@ import React, { useMemo, useRef } from 'react'; +import { useNavigate } from 'react-router'; import { Link } from 'react-router-dom'; import styled from 'styled-components'; import { stories, storyTaxonomies, getString } from 'veda'; @@ -65,7 +66,7 @@ const FoldWithTopMargin = styled(Fold)` `; function StoriesHub() { - const controlVars = useFiltersWithQS(); + const controlVars = useFiltersWithQS({navigate: useNavigate()}); const { search, taxonomies, onAction } = controlVars; diff --git a/app/scripts/index.ts b/app/scripts/index.ts index da145f482..da9959aec 100644 --- a/app/scripts/index.ts +++ b/app/scripts/index.ts @@ -11,11 +11,13 @@ import CompareImage from './components/common/blocks/images/compare'; import ReactQueryProvider from './context/react-query'; import Embed from './components/common/blocks/embed'; import DevseedUiThemeProvider from './theme-provider'; +import CatalogView from './components/common/catalog'; import { PageMainContent } from '$styles/page'; import PageHero from '$components/common/page-hero'; -import CatalogView from './components/common/catalog'; +import { useFiltersWithQS } from '$components/common/catalog/controls/hooks/use-filters-with-query'; export { + // COMPONENTS Block, Figure, Prose, @@ -33,4 +35,6 @@ export { PageMainContent, PageHero, ReactQueryProvider, + // HOOKS + useFiltersWithQS, }; diff --git a/app/scripts/utils/use-sliding-sticky-header.ts b/app/scripts/utils/use-sliding-sticky-header.ts index be24d3a17..7e3f23c39 100644 --- a/app/scripts/utils/use-sliding-sticky-header.ts +++ b/app/scripts/utils/use-sliding-sticky-header.ts @@ -1,33 +1,30 @@ import { useEffect, useState } from 'react'; -// import { useLocation } from 'react-router'; import { useEffectPrevious } from './use-effect-previous'; export const HEADER_ID = 'page-header'; export const HEADER_WRAPPER_ID = 'header-wrapper'; export const HEADER_TRANSITION_DURATION = 320; -export function useSlidingStickyHeader({ pathname }: { pathname: string }) { +export function useSlidingStickyHeader(pathname) { const [isHidden, setHidden] = useState(false); const [headerHeight, setHeaderHeight] = useState(0); const [wrapperHeight, setWrapperHeight] = useState(0); - // const { pathname } = useLocation(); - const navWrapperElement = document.querySelector( `#${HEADER_WRAPPER_ID}` ); - // useEffectPrevious( - // ([pathnamePrev]) => { - // const pageChanged = pathnamePrev !== pathname; - // if (pageChanged) { - // setHidden(false); - // setHeaderHeight(0); - // setWrapperHeight(0); - // } - // }, - // [pathname] - // ); + useEffectPrevious( + ([pathnamePrev]) => { + const pageChanged = pathnamePrev !== pathname; + if (pageChanged) { + setHidden(false); + setHeaderHeight(0); + setWrapperHeight(0); + } + }, + [pathname] + ); useEffect(() => { let ticking = false;