Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
ANCHOR_TARGET,
REL_ATTRIBUTE,
} from "@databiosphere/findable-ui/lib/components/Links/common/entities";
import { Link } from "@databiosphere/findable-ui/lib/components/Links/components/Link/link";
import { SVG_ICON_PROPS } from "@databiosphere/findable-ui/lib/styles/common/mui/svgIcon";
import { TYPOGRAPHY_PROPS } from "@databiosphere/findable-ui/lib/styles/common/mui/typography";
import type { JSX } from "react";
import { StyledNoWrap, StyledOpenInNewIcon } from "./citation.styles";
import { DOI_BASE_URL, EXTERNAL_LINK_TITLE } from "./constants";
import type { Props } from "./types";
import { splitTrailingWord } from "./utils";

/**
* The source study citation. Links to the DOI when there is one, otherwise
* renders as plain text - an external-link affordance on a link to nowhere
* would be worse than none. Takes a required `publicationString`, so the
* caller owns the "is there a citation at all?" question and the split only
* runs on the branch that consumes it.
* @param props - Component props.
* @param props.doi - Source study DOI, if any.
* @param props.publicationString - Source study citation.
* @returns citation, linked to the DOI when one exists.
*/
export const Citation = ({ doi, publicationString }: Props): JSX.Element => {
if (!doi) return <>{publicationString}</>;
const { head, tail } = splitTrailingWord(publicationString);
return (
<Link
// Text and icon are one label, so they are a single link target.
label={
<>
{head}
<StyledNoWrap>
{tail}
<StyledOpenInNewIcon
fontSize={SVG_ICON_PROPS.FONT_SIZE.XXSMALL}
titleAccess={EXTERNAL_LINK_TITLE}
/>
</StyledNoWrap>
</>
}
rel={REL_ATTRIBUTE.NO_OPENER_NO_REFERRER}
target={ANCHOR_TARGET.BLANK}
// MUI `Link` defaults to `color="primary"` and findable-ui's theme does
// not override it, so the wrapper's `ink.light` is ignored unless the
// colour is set on the link itself.
TypographyProps={{ color: TYPOGRAPHY_PROPS.COLOR.INK_LIGHT }}
url={`${DOI_BASE_URL}${doi}`}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Props {
doi: string | null;
publicationString: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ export function splitTrailingWord(text: string): {
head: string;
tail: string;
} {
const index = text.trimEnd().lastIndexOf(" ");
if (index === -1) return { head: "", tail: text };
return { head: text.slice(0, index + 1), tail: text.slice(index + 1) };
// Slice the trimmed value, not the original: computing the index from
// `trimEnd()` and then slicing `text` leaves any trailing whitespace on the
// tail, which renders as a gap between the final word and the icon after it.
const trimmed = text.trimEnd();
const index = trimmed.lastIndexOf(" ");
if (index === -1) return { head: "", tail: trimmed };
return { head: trimmed.slice(0, index + 1), tail: trimmed.slice(index + 1) };
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import {
ANCHOR_TARGET,
REL_ATTRIBUTE,
} from "@databiosphere/findable-ui/lib/components/Links/common/entities";
import { Link } from "@databiosphere/findable-ui/lib/components/Links/components/Link/link";
import { SVG_ICON_PROPS } from "@databiosphere/findable-ui/lib/styles/common/mui/svgIcon";
import { TYPOGRAPHY_PROPS } from "@databiosphere/findable-ui/lib/styles/common/mui/typography";
import { Stack, Typography } from "@mui/material";
import type { JSX } from "react";
import { buildVersionedFileNameValue } from "../../accessor";
import { DOI_BASE_URL, EXTERNAL_LINK_TITLE } from "./constants";
import { StyledNoWrap, StyledOpenInNewIcon } from "./fileNameCell.styles";
import { Citation } from "./components/Citation/citation";
import type { Props } from "./types";
import { splitTrailingWord } from "./utils";

/**
* Pinned cell stacking the published file name over the source study citation.
Expand All @@ -26,50 +18,18 @@ export const FileNameCell = ({ row }: Props): JSX.Element => {
// Shared with the column's `accessorFn`, so the pinned column always sorts on
// the value it displays.
const fileName = buildVersionedFileNameValue(row);
// Split once - the final word is held on one line with the icon that follows.
const citation = publicationString
? splitTrailingWord(publicationString)
: null;
return (
<Stack spacing={2} useFlexGap>
<Typography variant={TYPOGRAPHY_PROPS.VARIANT.BODY_400}>
{fileName}
</Typography>
{citation && (
{publicationString && (
<Typography
color={TYPOGRAPHY_PROPS.COLOR.INK_LIGHT}
component="div"
variant={TYPOGRAPHY_PROPS.VARIANT.BODY_SMALL_400}
>
{doi ? (
// Text and icon are one label, so they are a single link target.
<Link
label={
<>
{citation.head}
<StyledNoWrap>
{citation.tail}
<StyledOpenInNewIcon
fontSize={SVG_ICON_PROPS.FONT_SIZE.XXSMALL}
titleAccess={EXTERNAL_LINK_TITLE}
/>
</StyledNoWrap>
</>
}
rel={REL_ATTRIBUTE.NO_OPENER_NO_REFERRER}
target={ANCHOR_TARGET.BLANK}
// MUI `Link` defaults to `color="primary"` and findable-ui's
// theme does not override it, so the wrapper's `ink.light` is
// ignored unless the colour is set on the link itself.
TypographyProps={{
color: TYPOGRAPHY_PROPS.COLOR.INK_LIGHT,
}}
url={`${DOI_BASE_URL}${doi}`}
/>
) : (
// No DOI, so no link and no external-link affordance.
publicationString
)}
<Citation doi={doi} publicationString={publicationString} />
</Typography>
)}
</Stack>
Expand Down
Loading