summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/content-src/asrouter/components/RichText/RichText.jsx
blob: d430fa5c3defb9619397f59ad0a6b32b2184dcf6 (plain)
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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

import { Localized } from "@fluent/react";
import React from "react";
import { RICH_TEXT_KEYS } from "../../rich-text-strings";
import { safeURI } from "../../template-utils";

// Elements allowed in snippet content
const ALLOWED_TAGS = {
  b: <b />,
  i: <i />,
  u: <u />,
  strong: <strong />,
  em: <em />,
  br: <br />,
};

/**
 * Transform an object (tag name: {url}) into (tag name: anchor) where the url
 * is used as href, in order to render links inside a Fluent.Localized component.
 */
export function convertLinks(
  links,
  sendClick,
  doNotAutoBlock,
  openNewWindow = false
) {
  if (links) {
    return Object.keys(links).reduce((acc, linkTag) => {
      const { action } = links[linkTag];
      // Setting the value to false will not include the attribute in the anchor
      const url = action ? false : safeURI(links[linkTag].url);

      acc[linkTag] = (
        // eslint was getting a false positive caused by the dynamic injection
        // of content.
        // eslint-disable-next-line jsx-a11y/anchor-has-content
        <a
          href={url}
          target={openNewWindow ? "_blank" : ""}
          data-metric={links[linkTag].metric}
          data-action={action}
          data-args={links[linkTag].args}
          data-do_not_autoblock={doNotAutoBlock}
          data-entrypoint_name={links[linkTag].entrypoint_name}
          data-entrypoint_value={links[linkTag].entrypoint_value}
          rel="noreferrer"
          onClick={sendClick}
        />
      );
      return acc;
    }, {});
  }

  return null;
}

/**
 * Message wrapper used to sanitize markup and render HTML.
 */
export function RichText(props) {
  if (!RICH_TEXT_KEYS.includes(props.localization_id)) {
    throw new Error(
      `ASRouter: ${props.localization_id} is not a valid rich text property. If you want it to be processed, you need to add it to asrouter/rich-text-strings.js`
    );
  }
  return (
    <Localized
      id={props.localization_id}
      elems={{
        ...ALLOWED_TAGS,
        ...props.customElements,
        ...convertLinks(
          props.links,
          props.sendClick,
          props.doNotAutoBlock,
          props.openNewWindow
        ),
      }}
    >
      <span>{props.text}</span>
    </Localized>
  );
}