Skip to content

Commit 9b50e32

Browse files
authored
Support mailto links in other_links (#942)
Signed-off-by: Sergio Castaño Arteaga <tegioz@icloud.com>
1 parent 2d4cee6 commit 9b50e32

5 files changed

Lines changed: 44 additions & 9 deletions

File tree

crates/core/src/data/legacy.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,16 @@ mod tests {
301301
..Default::default()
302302
}]),
303303
blog_url: Some("https://blog.url".to_string()),
304-
other_links: Some(vec![ItemLink {
305-
name: "link".to_string(),
306-
url: "https://link.url".to_string(),
307-
}]),
304+
other_links: Some(vec![
305+
ItemLink {
306+
name: "email".to_string(),
307+
url: "mailto:team@example.com".to_string(),
308+
},
309+
ItemLink {
310+
name: "link".to_string(),
311+
url: "https://link.url".to_string(),
312+
},
313+
]),
308314
..Default::default()
309315
}),
310316
..Default::default()

crates/core/src/util.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ pub(crate) fn validate_url(kind: &str, url: Option<&String>) -> Result<()> {
5858
};
5959

6060
// Check scheme
61+
if kind == "other_link" && url.scheme() == "mailto" {
62+
return Ok(());
63+
}
6164
if url.scheme() != "http" && url.scheme() != "https" {
6265
return invalid_url("invalid scheme");
6366
}
@@ -147,6 +150,11 @@ mod tests {
147150
}
148151
}
149152

153+
#[test]
154+
fn validate_url_mailto_other_link_succeeds() {
155+
validate_url("other_link", Some("mailto:team@example.com".to_string()).as_ref()).unwrap();
156+
}
157+
150158
#[test]
151159
#[should_panic(expected = "relative URL without a base")]
152160
fn validate_url_error_parsing() {
@@ -188,4 +196,15 @@ mod tests {
188196
assert!(error.starts_with(expected_error.as_str()));
189197
}
190198
}
199+
200+
#[test]
201+
fn validate_url_mailto_other_field_fails() {
202+
let error = validate_url(
203+
"mailing_list",
204+
Some("mailto:team@example.com".to_string()).as_ref(),
205+
)
206+
.unwrap_err()
207+
.to_string();
208+
assert_eq!(error, "invalid mailing_list url: invalid scheme");
209+
}
191210
}

docs/config/data.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ categories:
218218
# - # Name of the link (required).
219219
# name: Link name
220220
# # URL of the link (required).
221-
# url: https://link.url
221+
# # Supports `https://...` and `mailto:...`.
222+
# url: mailto:team@example.com
222223
other_links: []
223224

224225
# Package manager URL (optional). Link to the package manager where the item is

docs/config/schema/data.schema.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,8 @@
451451
"type": "string",
452452
"format": "uri",
453453
"examples": [
454-
"https://link.url"
454+
"https://link.url",
455+
"mailto:team@example.com"
455456
]
456457
}
457458
},

ui/common/src/components/ExternalLink.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ const LinkClass = css`
2525
`;
2626

2727
export const ExternalLink = (props: Props) => {
28+
const isMailtoLink = () => /^mailto:/i.test(props.href);
29+
const linkRel = () => (isMailtoLink() ? undefined : 'noopener noreferrer');
30+
const linkTarget = () => (isMailtoLink() ? undefined : props.target || '_blank');
31+
2832
const getData = () => {
2933
return (
3034
<>
@@ -54,7 +58,11 @@ export const ExternalLink = (props: Props) => {
5458
if (props.onClick) props.onClick();
5559

5660
if (isUndefined(props.disabled) || !props.disabled) {
57-
window.open(props.href, props.target || '_blank');
61+
if (isMailtoLink()) {
62+
window.location.href = props.href;
63+
} else {
64+
window.open(props.href, linkTarget());
65+
}
5866
}
5967
}}
6068
aria-label={props.label || 'Open external link'}
@@ -68,8 +76,8 @@ export const ExternalLink = (props: Props) => {
6876
title={props.title}
6977
class={`link ${LinkClass} ${props.class}`}
7078
href={props.href}
71-
target={props.target || '_blank'}
72-
rel="noopener noreferrer"
79+
target={linkTarget()}
80+
rel={linkRel()}
7381
onClick={(e) => {
7482
e.stopPropagation();
7583

0 commit comments

Comments
 (0)