-
Notifications
You must be signed in to change notification settings - Fork 54
Expand file tree
/
Copy pathindividual-resource.tsx
More file actions
130 lines (112 loc) · 4.26 KB
/
individual-resource.tsx
File metadata and controls
130 lines (112 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"use client";
import React, { useContext, useEffect, useRef, useState } from "react";
import { InternalError } from "../error-boundary";
import { IndividualResourceLink } from "./group-and-resource-links";
import IconContainer from "./icon-container";
import { SetModalDataContext } from "./modal";
import TooltipWrapper from "./tooltip-wrapper";
import { Resource } from "./types";
import { displayResourceType } from ".";
import styles from "./individual-resource.module.css";
/**
* React Client Component to display an individual resource as part of
* a resource listing.
*/
export function IndividualResource({
gapWidth,
isMobile,
resource,
}: {
/** the value for the CSS `gap` property in the `<div>` containing the resource */
gapWidth: number;
/** boolean for whether the display is a mobile one */
isMobile: boolean;
/** the resource to display */
resource: Resource;
}): React.ReactElement | null {
const setModalData = useContext(SetModalDataContext);
if (!setModalData) {
throw new InternalError("Context not provided!");
}
const [topOfColumn, setTopOfColumn] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (
ref.current === null ||
ref.current.parentNode === null ||
ref.current.parentNode.nodeName !== "DIV"
) {
throw new InternalError(
"ref must be defined and the parent must be a div (i.e., IndividualResourceContainer).",
);
}
// The type of ref.current.parentNode is ParentNode which does not have an
// offsetTop property. I don't think there is a way to appease the
// TypeScript compiler other than a type assertion. It is loosely coupled
// to the check above for parentNode.nodeName.
// Note: this doesn't strictly have to be a div, but that's what it is in
// current usage of the component at the time of writing.
const parentNode = ref.current.parentNode as HTMLDivElement; // eslint-disable-line @typescript-eslint/consistent-type-assertions
// The column CSS is great but doesn't allow us to know if an
// element is at the top of its column, so we resort to JS
if (ref.current.offsetTop === parentNode.offsetTop) {
setTopOfColumn(true);
}
}, []);
// don't show anything if display name is unavailable
if (!resource.displayName) {
return null;
}
const restricted = resource.maybeRestricted ? (
<TooltipWrapper description={`Warning! This ${displayResourceType(resource.resourceType, 1)} may contain restricted data. Please refer to Restricted Data Terms of Use linked above.`}>
<IconContainer iconName="restricted" text={''}/>
</TooltipWrapper>
) : null;
// If an out of date warning exists then show it. Otherwise show cadence information if it's available
let history: React.JSX.Element | null = null;
if (resource.maybeOutOfDate) {
history = (
<TooltipWrapper description={`Warning! This ${displayResourceType(resource.resourceType, 1)} may be over a year old. Last known update on ${resource.lastUpdated}`}>
<IconContainer iconName="out-of-date" text={''}/>
</TooltipWrapper>
);
} else if (
!isMobile && // don't show cadence information on mobile
resource.updateCadence &&
resource.nVersions &&
resource.lastUpdated
) {
history = (
<TooltipWrapper
description={
resource.updateCadence.description +
`<br/>Last known update on ${resource.lastUpdated}` +
`<br/>${resource.nVersions} snapshots of this dataset available (click to see them)`
}
>
<IconContainer
handleClick={() => setModalData(resource)}
iconName="history"
text={`${resource.updateCadence.summary} (n=${resource.nVersions})`}
/>
</TooltipWrapper>
);
}
const description = resource.lastUpdated
? `Last known update on ${resource.lastUpdated}`
: "";
return (
<div className={styles.individualResourceContainer} ref={ref}>
<div className={styles.flexRow} style={{ gap: `${gapWidth}px` }}>
<TooltipWrapper description={description}>
<IndividualResourceLink
resource={resource}
topOfColumn={topOfColumn}
/>
</TooltipWrapper>
{restricted}
{history}
</div>
</div>
);
}