mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-25 09:24:54 +10:00
fix(#2978): implement inlineLink functionality
This commit is contained in:
@@ -121,8 +121,7 @@ export function RichInput({ value, onChange, style, className, editorClassName,
|
|||||||
"data-editor": "true",
|
"data-editor": "true",
|
||||||
"data-fullscreen": isFullscreen ? "true" : "false",
|
"data-fullscreen": isFullscreen ? "true" : "false",
|
||||||
class: cn(
|
class: cn(
|
||||||
"group/editor overflow-y-auto p-3 pb-4",
|
"wysiwyg group/editor overflow-y-auto p-3 pb-4",
|
||||||
"prose prose-sm prose-zinc dark:prose-invert max-w-none",
|
|
||||||
"rounded-md rounded-t-none border outline-none focus-visible:border-ring",
|
"rounded-md rounded-t-none border outline-none focus-visible:border-ring",
|
||||||
"[td:has(.selectedCell)]:bg-primary",
|
"[td:has(.selectedCell)]:bg-primary",
|
||||||
"data-[fullscreen=false]:max-h-[400px] data-[fullscreen=false]:min-h-[100px]",
|
"data-[fullscreen=false]:max-h-[400px] data-[fullscreen=false]:min-h-[100px]",
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { getInlineItemWebsiteUrl, shouldRenderSeparateItemWebsite } from "./section-links";
|
||||||
|
|
||||||
|
describe("section item website links", () => {
|
||||||
|
test("uses inline website URL and suppresses the separate website link when inlineLink is true", () => {
|
||||||
|
const website = {
|
||||||
|
url: "https://example.com/company",
|
||||||
|
label: "example.com/company",
|
||||||
|
inlineLink: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getInlineItemWebsiteUrl(website)).toBe("https://example.com/company");
|
||||||
|
expect(shouldRenderSeparateItemWebsite(website)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders the website separately when inlineLink is false", () => {
|
||||||
|
const website = {
|
||||||
|
url: "https://example.com/company",
|
||||||
|
label: "example.com/company",
|
||||||
|
inlineLink: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getInlineItemWebsiteUrl(website)).toBeUndefined();
|
||||||
|
expect(shouldRenderSeparateItemWebsite(website)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not create an inline or separate link without a URL", () => {
|
||||||
|
const website = {
|
||||||
|
url: "",
|
||||||
|
label: "",
|
||||||
|
inlineLink: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getInlineItemWebsiteUrl(website)).toBeUndefined();
|
||||||
|
expect(shouldRenderSeparateItemWebsite(website)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import type { Website } from "@reactive-resume/schema/resume/data";
|
||||||
|
|
||||||
|
type ItemWebsite = Website & {
|
||||||
|
inlineLink?: boolean | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getInlineItemWebsiteUrl = (website: ItemWebsite): string | undefined => {
|
||||||
|
if (!website.url || !website.inlineLink) return undefined;
|
||||||
|
|
||||||
|
return website.url;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const shouldRenderSeparateItemWebsite = (website: ItemWebsite): boolean => {
|
||||||
|
return Boolean(website.url && !website.inlineLink);
|
||||||
|
};
|
||||||
@@ -38,6 +38,7 @@ import { MetaLine } from "./meta-line";
|
|||||||
import { getTemplateMetrics } from "./metrics";
|
import { getTemplateMetrics } from "./metrics";
|
||||||
import { Bold, Div, Heading, Icon, Link, Small, Text } from "./primitives";
|
import { Bold, Div, Heading, Icon, Link, Small, Text } from "./primitives";
|
||||||
import { RichText } from "./rich-text";
|
import { RichText } from "./rich-text";
|
||||||
|
import { getInlineItemWebsiteUrl, shouldRenderSeparateItemWebsite } from "./section-links";
|
||||||
import { composeStyles } from "./styles";
|
import { composeStyles } from "./styles";
|
||||||
|
|
||||||
type SectionItemsContextValue = {
|
type SectionItemsContextValue = {
|
||||||
@@ -206,6 +207,27 @@ const SectionItemHeader = ({ children }: { children: ReactNode }) => {
|
|||||||
return <View style={composeStyles(sectionItemHeaderStyle)}>{children}</View>;
|
return <View style={composeStyles(sectionItemHeaderStyle)}>{children}</View>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ItemWebsite = {
|
||||||
|
url: string;
|
||||||
|
label: string;
|
||||||
|
inlineLink?: boolean | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ItemTitle = ({ children, website }: { children: ReactNode; website: ItemWebsite }) => {
|
||||||
|
const inlineWebsiteUrl = getInlineItemWebsiteUrl(website);
|
||||||
|
const title = <Bold>{children}</Bold>;
|
||||||
|
|
||||||
|
if (!inlineWebsiteUrl) return title;
|
||||||
|
|
||||||
|
return <Link src={inlineWebsiteUrl}>{title}</Link>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ItemWebsiteLink = ({ website }: { website: ItemWebsite }) => {
|
||||||
|
if (!shouldRenderSeparateItemWebsite(website)) return null;
|
||||||
|
|
||||||
|
return <Link src={website.url}>{website.label || website.url}</Link>;
|
||||||
|
};
|
||||||
|
|
||||||
const SummarySection = ({ showHeading = true }: { showHeading?: boolean } = {}) => {
|
const SummarySection = ({ showHeading = true }: { showHeading?: boolean } = {}) => {
|
||||||
const data = useRender();
|
const data = useRender();
|
||||||
const { summary } = data;
|
const { summary } = data;
|
||||||
@@ -272,9 +294,7 @@ const ProfileSection = ({
|
|||||||
<Bold>{item.network}</Bold>
|
<Bold>{item.network}</Bold>
|
||||||
</View>
|
</View>
|
||||||
</SectionItemHeader>
|
</SectionItemHeader>
|
||||||
<Link src={item.website.url}>
|
<Link src={item.website.url}>{item.username}</Link>
|
||||||
<Text>{item.username}</Text>
|
|
||||||
</Link>
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
))}
|
))}
|
||||||
</SectionItems>
|
</SectionItems>
|
||||||
@@ -316,7 +336,7 @@ const ExperienceSection = ({
|
|||||||
</Text>
|
</Text>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
middle={<Bold>{item.company}</Bold>}
|
middle={<ItemTitle website={item.website}>{item.company}</ItemTitle>}
|
||||||
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
|
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -324,7 +344,7 @@ const ExperienceSection = ({
|
|||||||
const renderSplitHeader = () => (
|
const renderSplitHeader = () => (
|
||||||
<>
|
<>
|
||||||
<View style={composeStyles(splitRowStyle)}>
|
<View style={composeStyles(splitRowStyle)}>
|
||||||
<Bold>{item.company}</Bold>
|
<ItemTitle website={item.website}>{item.company}</ItemTitle>
|
||||||
<Text style={composeStyles(alignRightStyle)}>{item.location}</Text>
|
<Text style={composeStyles(alignRightStyle)}>{item.location}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -355,11 +375,7 @@ const ExperienceSection = ({
|
|||||||
|
|
||||||
{item.roles.length === 0 && <RichText>{item.description}</RichText>}
|
{item.roles.length === 0 && <RichText>{item.description}</RichText>}
|
||||||
|
|
||||||
{item.website.url && (
|
<ItemWebsiteLink website={item.website} />
|
||||||
<Link src={item.website.url}>
|
|
||||||
<Small>{item.website.label || item.website.url}</Small>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -406,7 +422,7 @@ const EducationSection = ({
|
|||||||
</Text>
|
</Text>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
middle={<Bold>{item.school}</Bold>}
|
middle={<ItemTitle website={item.website}>{item.school}</ItemTitle>}
|
||||||
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
|
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
|
||||||
/>
|
/>
|
||||||
{gradeAndLocation && <Text>{gradeAndLocation}</Text>}
|
{gradeAndLocation && <Text>{gradeAndLocation}</Text>}
|
||||||
@@ -416,7 +432,7 @@ const EducationSection = ({
|
|||||||
const renderSplitHeader = () => (
|
const renderSplitHeader = () => (
|
||||||
<>
|
<>
|
||||||
<View style={composeStyles(splitRowStyle)}>
|
<View style={composeStyles(splitRowStyle)}>
|
||||||
<Bold>{item.school}</Bold>
|
<ItemTitle website={item.website}>{item.school}</ItemTitle>
|
||||||
<Text style={composeStyles(alignRightStyle)}>{degreeAndGrade}</Text>
|
<Text style={composeStyles(alignRightStyle)}>{degreeAndGrade}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -433,11 +449,7 @@ const EducationSection = ({
|
|||||||
|
|
||||||
<RichText>{item.description}</RichText>
|
<RichText>{item.description}</RichText>
|
||||||
|
|
||||||
{item.website.url && (
|
<ItemWebsiteLink website={item.website} />
|
||||||
<Link src={item.website.url}>
|
|
||||||
<Small>{item.website.label || item.website.url}</Small>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -468,18 +480,14 @@ const ProjectsSection = ({
|
|||||||
<SectionItem key={item.id}>
|
<SectionItem key={item.id}>
|
||||||
<SectionItemHeader>
|
<SectionItemHeader>
|
||||||
<View style={composeStyles(splitRowStyle)}>
|
<View style={composeStyles(splitRowStyle)}>
|
||||||
<Bold>{item.name}</Bold>
|
<ItemTitle website={item.website}>{item.name}</ItemTitle>
|
||||||
<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>
|
<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>
|
||||||
</View>
|
</View>
|
||||||
</SectionItemHeader>
|
</SectionItemHeader>
|
||||||
|
|
||||||
<RichText>{item.description}</RichText>
|
<RichText>{item.description}</RichText>
|
||||||
|
|
||||||
{item.website.url && (
|
<ItemWebsiteLink website={item.website} />
|
||||||
<Link src={item.website.url}>
|
|
||||||
<Small>{item.website.label || item.website.url}</Small>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
))}
|
))}
|
||||||
</SectionItems>
|
</SectionItems>
|
||||||
@@ -610,17 +618,13 @@ const AwardsSection = ({
|
|||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<SectionItem key={item.id}>
|
<SectionItem key={item.id}>
|
||||||
<SectionItemHeader>
|
<SectionItemHeader>
|
||||||
<Bold>{item.title}</Bold>
|
<ItemTitle website={item.website}>{item.title}</ItemTitle>
|
||||||
<Text>{item.awarder}</Text>
|
<Text>{item.awarder}</Text>
|
||||||
<Small>{item.date}</Small>
|
<Small>{item.date}</Small>
|
||||||
</SectionItemHeader>
|
</SectionItemHeader>
|
||||||
<RichText>{item.description}</RichText>
|
<RichText>{item.description}</RichText>
|
||||||
|
|
||||||
{item.website.url && (
|
<ItemWebsiteLink website={item.website} />
|
||||||
<Link src={item.website.url}>
|
|
||||||
<Small>{item.website.label || item.website.url}</Small>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
))}
|
))}
|
||||||
</SectionItems>
|
</SectionItems>
|
||||||
@@ -647,17 +651,13 @@ const CertificationsSection = ({
|
|||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<SectionItem key={item.id}>
|
<SectionItem key={item.id}>
|
||||||
<SectionItemHeader>
|
<SectionItemHeader>
|
||||||
<Bold>{item.title}</Bold>
|
<ItemTitle website={item.website}>{item.title}</ItemTitle>
|
||||||
<Text>{item.issuer}</Text>
|
<Text>{item.issuer}</Text>
|
||||||
<Small>{item.date}</Small>
|
<Small>{item.date}</Small>
|
||||||
</SectionItemHeader>
|
</SectionItemHeader>
|
||||||
<RichText>{item.description}</RichText>
|
<RichText>{item.description}</RichText>
|
||||||
|
|
||||||
{item.website.url && (
|
<ItemWebsiteLink website={item.website} />
|
||||||
<Link src={item.website.url}>
|
|
||||||
<Small>{item.website.label || item.website.url}</Small>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
))}
|
))}
|
||||||
</SectionItems>
|
</SectionItems>
|
||||||
@@ -684,17 +684,13 @@ const PublicationsSection = ({
|
|||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<SectionItem key={item.id}>
|
<SectionItem key={item.id}>
|
||||||
<SectionItemHeader>
|
<SectionItemHeader>
|
||||||
<Bold>{item.title}</Bold>
|
<ItemTitle website={item.website}>{item.title}</ItemTitle>
|
||||||
<Text>{item.publisher}</Text>
|
<Text>{item.publisher}</Text>
|
||||||
<Small>{item.date}</Small>
|
<Small>{item.date}</Small>
|
||||||
</SectionItemHeader>
|
</SectionItemHeader>
|
||||||
<RichText>{item.description}</RichText>
|
<RichText>{item.description}</RichText>
|
||||||
|
|
||||||
{item.website.url && (
|
<ItemWebsiteLink website={item.website} />
|
||||||
<Link src={item.website.url}>
|
|
||||||
<Small>{item.website.label || item.website.url}</Small>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
))}
|
))}
|
||||||
</SectionItems>
|
</SectionItems>
|
||||||
@@ -727,13 +723,13 @@ const VolunteerSection = ({
|
|||||||
{inlineItemHeader ? (
|
{inlineItemHeader ? (
|
||||||
<InlineItemHeader
|
<InlineItemHeader
|
||||||
leading={<Text>{item.location}</Text>}
|
leading={<Text>{item.location}</Text>}
|
||||||
middle={<Bold>{item.organization}</Bold>}
|
middle={<ItemTitle website={item.website}>{item.organization}</ItemTitle>}
|
||||||
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
|
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<View style={composeStyles(splitRowStyle)}>
|
<View style={composeStyles(splitRowStyle)}>
|
||||||
<Bold>{item.organization}</Bold>
|
<ItemTitle website={item.website}>{item.organization}</ItemTitle>
|
||||||
<Text style={composeStyles(alignRightStyle)}>{item.location}</Text>
|
<Text style={composeStyles(alignRightStyle)}>{item.location}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -743,11 +739,7 @@ const VolunteerSection = ({
|
|||||||
</SectionItemHeader>
|
</SectionItemHeader>
|
||||||
<RichText>{item.description}</RichText>
|
<RichText>{item.description}</RichText>
|
||||||
|
|
||||||
{item.website.url && (
|
<ItemWebsiteLink website={item.website} />
|
||||||
<Link src={item.website.url}>
|
|
||||||
<Small>{item.website.label || item.website.url}</Small>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
))}
|
))}
|
||||||
</SectionItems>
|
</SectionItems>
|
||||||
@@ -774,17 +766,13 @@ const ReferencesSection = ({
|
|||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<SectionItem key={item.id}>
|
<SectionItem key={item.id}>
|
||||||
<SectionItemHeader>
|
<SectionItemHeader>
|
||||||
<Bold>{item.name}</Bold>
|
<ItemTitle website={item.website}>{item.name}</ItemTitle>
|
||||||
<Text>{item.position}</Text>
|
<Text>{item.position}</Text>
|
||||||
<MetaLine>{[item.phone]}</MetaLine>
|
<MetaLine>{[item.phone]}</MetaLine>
|
||||||
</SectionItemHeader>
|
</SectionItemHeader>
|
||||||
<RichText>{item.description}</RichText>
|
<RichText>{item.description}</RichText>
|
||||||
|
|
||||||
{item.website.url && (
|
<ItemWebsiteLink website={item.website} />
|
||||||
<Link src={item.website.url}>
|
|
||||||
<Small>{item.website.label || item.website.url}</Small>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</SectionItem>
|
</SectionItem>
|
||||||
))}
|
))}
|
||||||
</SectionItems>
|
</SectionItems>
|
||||||
|
|||||||
@@ -152,6 +152,10 @@
|
|||||||
[role="button"]:not(:disabled) {
|
[role="button"]:not(:disabled) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wysiwyg {
|
||||||
|
@apply prose prose-sm prose-zinc dark:prose-invert max-w-none prose-p:my-0! prose-li:my-0! prose-ul:my-0! prose-ol:my-0!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
|||||||
Reference in New Issue
Block a user