Skip to content

Commit

Permalink
refactor(catalog): introduce usePathname hook, remove React Router de…
Browse files Browse the repository at this point in the history
…pendency from the Catalog (#1276)

**Related Ticket:** Contributes to
#1108

### Description of Changes

- Remove `react-router` dependency from the Catalog components
- Introduce custom `usePathname` hook
- Remove prop drilling of pathname from the Catalog

### Notes & Questions About Changes

The `pathname` prop is passed down for two things:

1. Generating a link to the dataset at
https://github.com/NASA-IMPACT/veda-ui/blob/820730fd7c1e25e68997b200d31bc4ee27525f7e/app/scripts/components/common/catalog/catalog-card.tsx#L126
2. Controlling scroll in the filters control at
https://github.com/NASA-IMPACT/veda-ui/blob/820730fd7c1e25e68997b200d31bc4ee27525f7e/app/scripts/components/common/catalog/filters-control.tsx#L57

I suspect that the usage on item 1 was intented to cover the use case of
an instance running on a subpath (e.g. `https://myinstance.org/veda`),
why is hard to replicate locally but this change shouldn't affect that.

### Validation / Testing

Using `veda-config`:

1. Visit the data catalog page
1. Clicking on cards should open the proper URL (same as before)
1. Visit Explorer page, clicking on cards selected them, instead of
visiting an URL (same as before)

Using `next-veda-ui` instance:

1. Link the source of this PR to the Next.js instance 
1. Visit the catalog page
1. Clicking on cards should open the proper URL  (same as before)
1. Visit Explorer page, clicking on cards selected them, instead of
visiting an URL (same as before)

This is ready for review.
  • Loading branch information
vgeorge authored Nov 28, 2024
2 parents 33594d0 + 2807db6 commit 7233df8
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 17 deletions.
7 changes: 4 additions & 3 deletions app/scripts/components/common/catalog/catalog-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { DatasetData, DatasetLayer } from "$types/veda";
import { getDatasetPath } from "$utils/routes";
import { TAXONOMY_SOURCE, TAXONOMY_TOPICS, getAllTaxonomyValues, getTaxonomy } from "$utils/veda-data/taxonomies";
import { Pill } from "$styles/pill";
import { usePathname } from "$utils/use-pathname";

interface CatalogCardProps {
dataset: DatasetData;
Expand All @@ -21,7 +22,6 @@ interface CatalogCardProps {
selectable?: boolean;
selected?: boolean;
onDatasetClick?: () => void;
pathname?: string;
linkProperties?: LinkProperties;
}

Expand Down Expand Up @@ -102,10 +102,11 @@ export const CatalogCard = (props: CatalogCardProps) => {
selectable,
selected,
onDatasetClick,
linkProperties,
pathname
linkProperties
} = props;

const pathname = usePathname();

const topics = getTaxonomy(dataset, TAXONOMY_TOPICS)?.values;
const sources = getTaxonomy(dataset, TAXONOMY_SOURCE)?.values;
const allTaxonomyValues = getAllTaxonomyValues(dataset).map((v) => v.name);
Expand Down
5 changes: 0 additions & 5 deletions app/scripts/components/common/catalog/catalog-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export interface CatalogContentProps {
taxonomies: Record<string, string[]>;
onAction: (action: FilterActions, value?: any) => void;
linkProperties: LinkProperties;
pathname?: string;
}

const DEFAULT_SORT_OPTION = 'asc';
Expand Down Expand Up @@ -72,7 +71,6 @@ function CatalogContent({
search,
taxonomies,
onAction,
pathname,
linkProperties
}: CatalogContentProps) {
const [exclusiveSourceSelected, setExclusiveSourceSelected] = useState<string | null>(null);
Expand Down Expand Up @@ -215,7 +213,6 @@ function CatalogContent({
exclusiveSourceSelected={exclusiveSourceSelected}
customTopOffset={isSelectable ? 50 : 0}
openByDefault={false}
pathname={pathname}
/>
<Catalog>
<CatalogTagsContainer
Expand Down Expand Up @@ -264,7 +261,6 @@ function CatalogContent({
selectable={true}
selected={selectedIds.includes(datasetLayer.id)}
onDatasetClick={() => onCardSelect(datasetLayer.id, currentDataset)}
pathname={pathname}
/>
</li>
))}
Expand All @@ -280,7 +276,6 @@ function CatalogContent({
dataset={d}
searchTerm={search}
linkProperties={linkProperties}
pathname={pathname}
/>
</li>
))}
Expand Down
5 changes: 3 additions & 2 deletions app/scripts/components/common/catalog/filters-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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 { usePathname } from '$utils/use-pathname';

const ControlsWrapper = styled.div<{ widthValue?: string; heightValue?: string; topValue: string }>`
min-width: 20rem;
Expand All @@ -27,7 +28,6 @@ interface FiltersMenuProps {
exclusiveSourceSelected?: string | null;
customTopOffset?: number;
openByDefault?: boolean;
pathname?: string;
}

export default function FiltersControl(props: FiltersMenuProps) {
Expand All @@ -48,9 +48,10 @@ export default function FiltersControl(props: FiltersMenuProps) {
// 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,
pathname,
} = props;

const pathname = usePathname();

const controlsRef = useRef<HTMLDivElement>(null);
const [controlsHeight, setControlsHeight] = useState<number>(0);
const { isHeaderHidden, wrapperHeight } = useSlidingStickyHeader(pathname);
Expand Down
3 changes: 0 additions & 3 deletions app/scripts/components/common/catalog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,11 @@ export interface CatalogViewProps {
onAction: () => void,
} | any;
linkProperties: LinkProperties;
pathname: string;
}

function CatalogView({
datasets,
onFilterChanges,
pathname,
linkProperties,
}: CatalogViewProps) {

Expand All @@ -72,7 +70,6 @@ function CatalogView({
search={search}
taxonomies={taxonomies}
onAction={onAction}
pathname={pathname}
linkProperties={linkProperties}
/>
</CatalogWrapper>
Expand Down
3 changes: 0 additions & 3 deletions app/scripts/components/data-catalog/container.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import { useLocation } from 'react-router';
import { getString } from 'veda';
import { getAllDatasetsProps } from '$utils/veda-data';
import CatalogView from '$components/common/catalog';
Expand All @@ -20,7 +19,6 @@ import SmartLink from '$components/common/smart-link';

export default function DataCatalogContainer() {
const allDatasets = getAllDatasetsProps(veda_faux_module_datasets);
const pathname = useLocation().pathname;
const controlVars = useFiltersWithQS();

return (
Expand All @@ -37,7 +35,6 @@ export default function DataCatalogContainer() {
<CatalogView
datasets={allDatasets}
onFilterChanges={() => controlVars}
pathname={pathname}
linkProperties={{
LinkElement: SmartLink,
pathAttributeKeyName: 'to'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ export function DatasetSelectorModal(props: DatasetSelectorModalProps) {
onAction={onAction}
filterLayers={true}
linkProperties={linkProperties}
pathname={datasetPathName}
emptyStateContent={
<>
<p>There are no datasets to show with the selected filters.</p>
Expand Down
59 changes: 59 additions & 0 deletions app/scripts/utils/use-pathname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useEffect, useState } from 'react';

/**
* usePathname
* *
* This hook is implemented to work in both client-side rendering
* and server-side rendering environments. During SSR, it initializes the
* `pathname` as an empty string, ensuring the application remains stable in
* non-browser environments.
*
* @returns {string} The current `pathname`. Returns an empty string during SSR
* or if the `window` object is unavailable.
*/
export const usePathname = () => {
const [pathname, setPathname] = useState(
typeof window !== 'undefined' ? window.location.pathname : ''
);

useEffect(() => {
if (typeof window === 'undefined') return;

const updatePathname = () => {
setPathname(window.location.pathname);
};

// Listen to popstate events (back/forward navigation)
window.addEventListener('popstate', updatePathname);

// Detect programmatic navigation by dispatching a custom event
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;

const customEvent = new Event('pathnamechange');
const dispatchPathnameChange = () => {
window.dispatchEvent(customEvent);
};

history.pushState = function (...args) {
originalPushState.apply(this, args);
dispatchPathnameChange();
};

history.replaceState = function (...args) {
originalReplaceState.apply(this, args);
dispatchPathnameChange();
};

window.addEventListener('pathnamechange', updatePathname);

return () => {
window.removeEventListener('popstate', updatePathname);
window.removeEventListener('pathnamechange', updatePathname);
history.pushState = originalPushState;
history.replaceState = originalReplaceState;
};
}, []);

return pathname;
};

0 comments on commit 7233df8

Please sign in to comment.