-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Changes useCurrentFeature hook so that it picks
up the latest query accurately - Creates asset detail component with styles - Creates asset detail placeholders for questionnaire and pointcloud - Need to add meaningful test
- Loading branch information
1 parent
8c6c0da
commit a14fb68
Showing
6 changed files
with
276 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
.root { | ||
display: flex; | ||
flex-direction: column; | ||
width: 100%; | ||
height: 100%; | ||
overflow: hidden; | ||
} | ||
|
||
.topSection { | ||
flex: 0 0 auto; | ||
padding: 10px; | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
font-size: large; | ||
} | ||
|
||
.middleSection { | ||
flex: 1 1 auto; | ||
overflow: hidden; | ||
min-height: 0; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-evenly; | ||
align-items: center; | ||
} | ||
.middleSection > div { | ||
flex: 1; | ||
overflow: auto; | ||
} | ||
.bottomSection { | ||
display: block; | ||
flex: 0 0 auto; | ||
overflow-x: hidden; | ||
justify-items: flex-end; | ||
align-items: flex-end; | ||
width: 100%; | ||
} | ||
.metadataTable { | ||
flex-grow: 1; | ||
width: 100%; | ||
} | ||
.metadataTable table { | ||
width: 100%; | ||
table-layout: fixed; | ||
border-collapse: collapse; | ||
margin: 5px; | ||
padding: 5px; | ||
} | ||
.metadataTable thead, | ||
th { | ||
background: #d0d0d0; | ||
} | ||
.metadataTable tbody { | ||
display: block; | ||
max-height: 300px; | ||
overflow-y: auto; | ||
scroll-padding: 5px; | ||
} | ||
.metadataTable tr { | ||
display: table; | ||
width: 100%; | ||
table-layout: fixed; | ||
text-overflow: ellipsis; | ||
overflow: hidden; | ||
white-space: wrap; | ||
max-height: 30px; | ||
} | ||
.metadataTable td { | ||
word-wrap: break-word; | ||
word-break: break-word; | ||
white-space: normal; | ||
text-overflow: clip; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import React, { act } from 'react'; | ||
Check failure on line 1 in react/src/components/AssetDetail/AssetDetail.test.tsx GitHub Actions / React-Linting
|
||
import { render, screen, fireEvent } from '@testing-library/react'; | ||
Check failure on line 2 in react/src/components/AssetDetail/AssetDetail.test.tsx GitHub Actions / React-Linting
Check failure on line 2 in react/src/components/AssetDetail/AssetDetail.test.tsx GitHub Actions / React-Linting
|
||
import AssetDetail from './AssetDetail'; | ||
import { featureCollection } from '@hazmapper/__fixtures__/featuresFixture'; | ||
import { projectMock } from '@hazmapper/__fixtures__/projectFixtures'; | ||
import { useFeatures } from '@hazmapper/hooks'; | ||
|
||
jest.mock('@hazmapper/hooks', () => ({ | ||
useFeatures: jest.fn(), | ||
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import React, { Suspense, useState, useEffect } from 'react'; | ||
import _ from 'lodash'; | ||
import { useAppConfiguration, useFeatureSelection } from '@hazmapper/hooks'; | ||
import { useLocation, useNavigate } from 'react-router-dom'; | ||
import { Asset, FeatureTypeNullable } from '@hazmapper/types'; | ||
import { FeatureIcon } from '@hazmapper/components/FeatureIcon'; | ||
import { Button, LoadingSpinner, SectionMessage } from '@tacc/core-components'; | ||
import styles from './AssetDetail.module.css'; | ||
import { faCameraRetro, faPhotoFilm } from '@fortawesome/free-solid-svg-icons'; | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | ||
|
||
type AssetModalProps = { | ||
isOpen: boolean; | ||
projectId: number; | ||
isPublicView: boolean; | ||
}; | ||
|
||
const AssetDetail: React.FC<AssetModalProps> = ({ | ||
isOpen, | ||
projectId, | ||
isPublicView, | ||
}) => { | ||
const { selectedFeature, selectedFeatureId, setSelectedFeatureId } = | ||
useFeatureSelection(); | ||
|
||
const navigate = useNavigate(); | ||
const location = useLocation(); | ||
|
||
const closePanel = () => { | ||
const params = new URLSearchParams(location.search); | ||
params.delete('selectedFeature'); | ||
navigate(`${location.pathname}?${params.toString()}`); | ||
}; | ||
|
||
const config = useAppConfiguration(); | ||
const geoapiUrl = config.geoapiUrl; | ||
|
||
const [selectedFeatureAsset, setSelectedFeatureAsset] = useState< | ||
Asset | undefined | ||
>(selectedFeature?.assets[0]); | ||
|
||
useEffect(() => { | ||
const featureAsset = selectedFeature?.assets[0]; | ||
setSelectedFeatureAsset(featureAsset); | ||
}, [selectedFeature]); | ||
|
||
const featureSource: string | undefined = | ||
geoapiUrl + '/assets/' + selectedFeatureAsset?.path; | ||
|
||
const fileType: string | undefined = selectedFeatureAsset?.asset_type; | ||
|
||
const AssetRenderer = React.memo( | ||
({ | ||
type, | ||
source, | ||
}: { | ||
type: string | undefined; | ||
source: string | undefined; | ||
}) => { | ||
switch (type) { | ||
case 'image': | ||
return <img src={source} alt="Asset" loading="lazy" width="100%" />; | ||
case 'video': | ||
return ( | ||
<video src={source} controls preload="metadata" height="90%" /> | ||
); | ||
case 'point_cloud': | ||
/*TODO Add pointcloud */ | ||
return <div> source={source}</div>; | ||
case 'questionnaire': | ||
/*TODO Add questionnaire */ | ||
return <div> source={source}</div>; | ||
default: | ||
return null; | ||
} | ||
} | ||
); | ||
|
||
return ( | ||
<div className={styles.root}> | ||
<div className={styles.topSection}> | ||
<FeatureIcon featureType={fileType as FeatureTypeNullable} /> | ||
{selectedFeature?.id} | ||
<Button type="link" iconNameAfter="close" onClick={closePanel}></Button> | ||
</div> | ||
<div className={styles.middleSection}> | ||
{fileType ? ( | ||
<> | ||
<Suspense fallback={<LoadingSpinner />}> | ||
<AssetRenderer type={fileType} source={featureSource} /> | ||
</Suspense> | ||
<Button /*TODO Download Action */>Download</Button> | ||
</> | ||
) : ( | ||
<> | ||
<SectionMessage type="info">Feature has no asset.</SectionMessage> | ||
|
||
<Button type="primary" /* TODO Add asset to a feature */> | ||
Add asset from DesignSafe | ||
</Button> | ||
</> | ||
)} | ||
</div> | ||
<div className={styles.bottomSection}> | ||
<div className={styles.metadataTable}> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th colSpan={2}>Metadata</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{selectedFeature?.properties && | ||
Object.keys(selectedFeature.properties).length > 0 ? ( | ||
Object.entries(selectedFeature.properties) | ||
.filter(([key]) => !key.startsWith('_hazmapper')) | ||
.sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) // Alphabetizes metadata | ||
.map(([propKey, propValue]) => ( | ||
<tr key={propKey}> | ||
<td>{_.startCase(propKey)}</td> | ||
<td> | ||
{propKey.startsWith('description') ? ( | ||
<code>{propValue}</code> | ||
) : ( | ||
_.trim(JSON.stringify(propValue), '"') | ||
)} | ||
</td> | ||
</tr> | ||
)) | ||
) : ( | ||
<tr> | ||
<td colSpan={2}> | ||
There are no metadata properties for this feature. | ||
</td> | ||
</tr> | ||
)} | ||
</tbody> | ||
</table> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th colSpan={2}>Geometry</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{selectedFeature?.geometry && | ||
Object.entries(selectedFeature.geometry) | ||
.map( | ||
([propKey, propValue]) => ( | ||
propValue && propValue !== undefined && propValue.toString().trim() !== '' && propValue.toString() !== 'null' && ( | ||
<tr key={propKey}> | ||
<td>{_.trim(_.startCase(propKey.toString()), '"')}</td> | ||
<td> | ||
{' '} | ||
{Array.isArray(propValue) && propValue.length === 2 | ||
? `Latitude: ${propValue[0].toString()}, | ||
Longitude: ${propValue[1].toString()}` | ||
: _.trim(JSON.stringify(propValue), '"')} | ||
</td> | ||
</tr> | ||
) | ||
) | ||
)} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default AssetDetail; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './AssetDetail'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters