Skip to content

Commit

Permalink
Merge branch 'main' into task/WP-209-Footer-Main-Menu
Browse files Browse the repository at this point in the history
  • Loading branch information
sophia-massie authored Nov 22, 2024
2 parents 61913b7 + a0b9578 commit cd8fe9d
Show file tree
Hide file tree
Showing 20 changed files with 134 additions and 140 deletions.
11 changes: 11 additions & 0 deletions react/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@
pathname.startsWith('/dev-react')
) {
document.getElementById('base').href = '/dev-react/';
} else if (
/^hazmapper.tacc.utexas.edu/.test(hostname) &&
pathname.startsWith('/exp-react')
) {
document.getElementById('base').href = '/exp-react/';
} else if (/^hazmapper.tacc.utexas.edu/.test(hostname)) {
document.getElementById('base').href = '/hazmapper-react/';
} else if (
/* NOTE: non `-react` statements below are not being used */
/^hazmapper.tacc.utexas.edu/.test(hostname) &&
pathname.startsWith('/staging')
) {
Expand All @@ -33,6 +39,11 @@
pathname.startsWith('/dev')
) {
document.getElementById('base').href = '/dev/';
} else if (
/^hazmapper.tacc.utexas.edu/.test(hostname) &&
pathname.startsWith('/exp')
) {
document.getElementById('base').href = '/exp/';
} else if (/^hazmapper.tacc.utexas.edu/.test(hostname)) {
document.getElementById('base').href = '/hazmapper/';
} else {
Expand Down
5 changes: 3 additions & 2 deletions react/jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ export function shouldIgnoreError(args: any[]): boolean {

beforeAll(() => {
// Establish mocking of APIs before all tests
server.listen({ onUnhandledRequest: 'error' });

server.listen({
onUnhandledRequest: 'error',
});
const originalError = console.error;

jest.spyOn(console, 'error').mockImplementation((...args: any[]) => {
Expand Down
5 changes: 4 additions & 1 deletion react/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ function AppRouter() {
</ProtectedRoute>
}
/>
<Route path={ROUTES.PUBLIC_PROJECT} element={<MapProject isPublic />} />
<Route
path={ROUTES.PUBLIC_PROJECT}
element={<MapProject isPublicView />}
/>
<Route path={ROUTES.CALLBACK} element={<Callback />} />
<Route
path={ROUTES.STREETVIEW_CALLBACK}
Expand Down
5 changes: 3 additions & 2 deletions react/src/components/AssetsPanel/AssetsPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { act } from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import AssetsPanel from './AssetsPanel';
import { featureCollection } from '@hazmapper/__fixtures__/featuresFixture';
import { projectMock } from '@hazmapper/__fixtures__/projectFixtures';
import { useFeatures } from '@hazmapper/hooks';

jest.mock('@hazmapper/hooks', () => ({
Expand All @@ -18,8 +19,8 @@ jest.mock('@hazmapper/components/FeatureFileTree', () => {
describe('AssetsPanel', () => {
const defaultProps = {
featureCollection,
projectId: 1,
isPublic: false,
project: projectMock,
isPublicView: false,
};

beforeEach(() => {
Expand Down
38 changes: 21 additions & 17 deletions react/src/components/AssetsPanel/AssetsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import React from 'react';
import styles from './AssetsPanel.module.css';
import FeatureFileTree from '@hazmapper/components/FeatureFileTree';
import { FeatureCollection } from '@hazmapper/types';
import { FeatureCollection, Project } from '@hazmapper/types';
import { Button } from '@tacc/core-components';
import { useFeatures } from '@hazmapper/hooks';

const getFilename = (projectName: string) => {
// Convert to lowercase filename based on projectName
const sanitizedString = projectName.toLowerCase().replace(/[^a-z0-9]/g, '_');
return `${sanitizedString}.json`;
};

interface DownloadFeaturesButtonProps {
projectId: number;
isPublic: boolean;
project: Project;
}

const DownloadFeaturesButton: React.FC<DownloadFeaturesButtonProps> = ({
projectId,
isPublic,
project,
}) => {
const { isLoading: isDownloading, refetch: triggerDownload } = useFeatures({
projectId,
isPublic,
projectId: project.id,
isPublicView: project.public,
assetTypes: [], // Empty array to get all features
options: {
enabled: false, // Only fetch when triggered by user clicking button
Expand All @@ -31,7 +35,7 @@ const DownloadFeaturesButton: React.FC<DownloadFeaturesButtonProps> = ({
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `hazmapper.json`;
link.download = getFilename(project.name);

document.body.appendChild(link);
link.click();
Expand All @@ -56,23 +60,23 @@ interface Props {
featureCollection: FeatureCollection;

/**
* Whether or not the map project is public.
* Whether or not the map project is a public view.
*/
isPublic: boolean;
isPublicView: boolean;

/**
* active project id
* active project
*/
projectId: number;
project: Project;
}

/**
* A panel component that displays info on feature assets
*/
const AssetsPanel: React.FC<Props> = ({
isPublic,
isPublicView,
featureCollection,
projectId,
project,
}) => {
return (
<div className={styles.root}>
Expand All @@ -81,13 +85,13 @@ const AssetsPanel: React.FC<Props> = ({
</div>
<div className={styles.middleSection}>
<FeatureFileTree
projectId={projectId}
isPublic={isPublic}
projectId={project.id}
isPublicView={isPublicView}
featureCollection={featureCollection}
/>
</div>
<div className={styles.bottomSection}>
<DownloadFeaturesButton projectId={projectId} isPublic={isPublic} />
<DownloadFeaturesButton project={project} />
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions react/src/components/DeleteMapModal/DeleteMapModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ const DeleteMapModal = ({
isLoading: isDeletingProject,
isError,
isSuccess,
} = useDeleteProject(project.id);
} = useDeleteProject();
const handleClose = () => {
parentToggle();
};

const handleDeleteProject = () => {
deleteProject(undefined, {});
deleteProject({ projectId: project.id });
};

return (
Expand Down
71 changes: 31 additions & 40 deletions react/src/components/FeatureFileTree/FeatureFileTree.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { fireEvent, waitFor } from '@testing-library/react';
import { http, HttpResponse } from 'msw';
import FeatureFileTree from './FeatureFileTree';
import { server, renderInTest } from '@hazmapper/test/testUtil';
import { featureCollection } from '@hazmapper/__fixtures__/featuresFixture';
import { useDeleteFeature } from '@hazmapper/hooks';

// Mock the hooks
jest.mock('@hazmapper/hooks', () => ({
useDeleteFeature: jest.fn(),
}));
import { testDevConfiguration } from '@hazmapper/__fixtures__/appConfigurationFixture';

jest.mock('react-resize-detector', () => ({
useResizeDetector: () => ({
Expand All @@ -17,64 +13,59 @@ jest.mock('react-resize-detector', () => ({
}),
}));

const renderWithRouter = (ui: React.ReactElement, { route = '/' } = {}) => {
return {
...render(<MemoryRouter initialEntries={[route]}>{ui}</MemoryRouter>),
};
};

describe('FeatureFileTree', () => {
const defaultProps = {
const defaultTreeProps = {
featureCollection: featureCollection,
isPublic: false,
isPublicView: false,
projectId: 1,
};

beforeEach(() => {
jest.clearAllMocks();

(useDeleteFeature as jest.Mock).mockImplementation(() => ({
mutate: jest.fn(),
isLoading: false,
}));
});

it('renders feature list correctly', () => {
const { getByText } = renderWithRouter(
<FeatureFileTree {...defaultProps} />
const { getByText } = renderInTest(
<FeatureFileTree {...defaultTreeProps} />
);

expect(getByText('foo')).toBeDefined();
expect(getByText('image1.JPG')).toBeDefined();
expect(getByText('image2.JPG')).toBeDefined();
});

it('handles feature deletion for non-public projects', () => {
const deleteFeatureMock = jest.fn();
(useDeleteFeature as jest.Mock).mockImplementation(() => ({
mutate: deleteFeatureMock,
isLoading: false,
}));
it('handles feature deletion for non-public projects', async () => {
const featureId = 1;
let wasDeleted = false;

server.use(
http.delete(
`${testDevConfiguration.geoapiUrl}/projects/${defaultTreeProps.projectId}/features/${featureId}/`,
() => {
wasDeleted = true;
return HttpResponse.json({}, { status: 200 });
}
)
);

const { getByTestId } = renderWithRouter(
<FeatureFileTree {...defaultProps} />,
{ route: '/?selectedFeature=1' }
const { getByTestId } = renderInTest(
<FeatureFileTree {...defaultTreeProps} />,
`/?selectedFeature=${featureId}`
);

// Find and click delete button (as featured is selected)
const deleteButton = getByTestId('delete-feature-button');
fireEvent.click(deleteButton);

expect(deleteFeatureMock).toHaveBeenCalledWith({
projectId: 1,
featureId: 1,
await waitFor(() => {
expect(wasDeleted).toBeTruthy();
});
});

it('does not show delete button for public projects', () => {
const { queryByTestId } = renderWithRouter(
<FeatureFileTree {...defaultProps} isPublic={true} />,
{ route: '/?selectedFeature=1' }
const { queryByTestId } = renderInTest(
<FeatureFileTree {...defaultTreeProps} isPublicView={true} />,
'/?selectedFeature=1'
);

// Verify delete button is not present
Expand All @@ -83,8 +74,8 @@ describe('FeatureFileTree', () => {
});

it('does not show delete button when no feature is selected', () => {
const { queryByTestId } = renderWithRouter(
<FeatureFileTree {...defaultProps} isPublic={true} />
const { queryByTestId } = renderInTest(
<FeatureFileTree {...defaultTreeProps} isPublicView={false} />
);

// Verify delete button is not present
Expand Down
6 changes: 3 additions & 3 deletions react/src/components/FeatureFileTree/FeatureFileTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ interface FeatureFileTreeProps {
/**
* Whether or not the map project is public.
*/
isPublic: boolean;
isPublicView: boolean;

/**
* active project id
Expand All @@ -44,7 +44,7 @@ interface FeatureFileTreeProps {
*/
const FeatureFileTree: React.FC<FeatureFileTreeProps> = ({
featureCollection,
isPublic,
isPublicView,
projectId,
}) => {
const { mutate: deleteFeature, isLoading } = useDeleteFeature();
Expand Down Expand Up @@ -169,7 +169,7 @@ const FeatureFileTree: React.FC<FeatureFileTreeProps> = ({
<FeatureIcon featureType={featureNode.featureType} />
)}
<span className={styles.fileName}>{featureNode.name}</span>
{!isPublic && isSelected && (
{!isPublicView && isSelected && (
<Button
size="small"
type="primary"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import styles from './ManageMapProjectModal.module.css';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';

interface ManageMapProjectModalProps {
isPublic: boolean;
isPublicView: boolean;
}

const ManageMapProjectModal: React.FC<ManageMapProjectModalProps> = ({
isPublic,
isPublicView,
}) => {
const navigate = useNavigate();
const location = useLocation();
Expand All @@ -24,7 +24,7 @@ const ManageMapProjectModal: React.FC<ManageMapProjectModalProps> = ({
<ModalHeader toggle={closeModal}>TODO</ModalHeader>
<ModalBody>
<div className={styles.root}>
Manage Map Project TODO, isPublic: {isPublic}
Manage Map Project TODO, isPublicView: {isPublicView}
</div>
</ModalBody>
</Modal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('MapProjectNavBar', () => {
it('renders the public nav items for public maps', () => {
const { getByText, queryByText } = render(
<BrowserRouter>
<MapProjectNavBar isPublic={true} />
<MapProjectNavBar isPublicView={true} />
</BrowserRouter>
);
expect(getByText('Assets')).toBeDefined();
Expand All @@ -22,7 +22,7 @@ describe('MapProjectNavBar', () => {
it('renders all nav items for non-public maps', () => {
const { getByText } = render(
<BrowserRouter>
<MapProjectNavBar isPublic={false} />
<MapProjectNavBar isPublicView={false} />
</BrowserRouter>
);

Expand Down
8 changes: 5 additions & 3 deletions react/src/components/MapProjectNavBar/MapProjectNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,20 @@ const navItems: NavItem[] = [
];

interface NavBarPanelProps {
isPublic?: boolean;
isPublicView?: boolean;
}

const MapProjectNavBar: React.FC<NavBarPanelProps> = ({ isPublic = false }) => {
const MapProjectNavBar: React.FC<NavBarPanelProps> = ({
isPublicView = false,
}) => {
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const activePanel = queryParams.get(queryPanelKey);

return (
<div className={styles.root}>
{navItems
.filter((item) => (isPublic ? item.showWhenPublic : true))
.filter((item) => (isPublicView ? item.showWhenPublic : true))
.map((item) => {
const updatedQueryParams = new URLSearchParams(location.search);

Expand Down
Loading

0 comments on commit cd8fe9d

Please sign in to comment.