@ndla/ui 46.0.2 → 47.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/es/Article/Article.js +3 -17
  2. package/es/Embed/AudioEmbed.js +6 -14
  3. package/es/Embed/BrightcoveEmbed.js +13 -5
  4. package/es/Embed/ConceptEmbed.js +16 -14
  5. package/es/Embed/ImageEmbed.js +11 -4
  6. package/es/Embed/conceptComponents.js +11 -12
  7. package/es/Footer/FooterLinks.js +6 -6
  8. package/es/LicenseByline/EmbedByline.js +7 -15
  9. package/es/Messages/MessageBanner.js +4 -13
  10. package/es/Messages/MessageBox.js +10 -20
  11. package/es/Navigation/NavigationTopicAbout.js +15 -17
  12. package/es/Notion/Notion.js +8 -7
  13. package/es/RadioButtonGroup/RadioButtonGroup.js +99 -80
  14. package/es/SearchTypeResult/SearchFieldHeader.js +4 -4
  15. package/es/Topic/Topic.js +18 -20
  16. package/es/index.js +0 -1
  17. package/lib/Article/Article.d.ts +2 -4
  18. package/lib/Article/Article.js +3 -17
  19. package/lib/Embed/AudioEmbed.js +6 -15
  20. package/lib/Embed/BrightcoveEmbed.js +12 -4
  21. package/lib/Embed/ConceptEmbed.js +15 -13
  22. package/lib/Embed/ImageEmbed.js +10 -3
  23. package/lib/Embed/conceptComponents.d.ts +1 -1
  24. package/lib/Embed/conceptComponents.js +11 -12
  25. package/lib/Footer/FooterLinks.js +6 -6
  26. package/lib/LicenseByline/EmbedByline.d.ts +2 -2
  27. package/lib/LicenseByline/EmbedByline.js +8 -16
  28. package/lib/Messages/MessageBanner.d.ts +2 -1
  29. package/lib/Messages/MessageBanner.js +5 -13
  30. package/lib/Messages/MessageBox.d.ts +2 -1
  31. package/lib/Messages/MessageBox.js +11 -19
  32. package/lib/Navigation/NavigationTopicAbout.d.ts +2 -3
  33. package/lib/Navigation/NavigationTopicAbout.js +15 -17
  34. package/lib/Notion/Notion.js +7 -6
  35. package/lib/RadioButtonGroup/RadioButtonGroup.d.ts +16 -8
  36. package/lib/RadioButtonGroup/RadioButtonGroup.js +108 -84
  37. package/lib/SearchTypeResult/SearchFieldHeader.js +4 -4
  38. package/lib/TagSelector/ariaMessages.d.ts +1 -1
  39. package/lib/Topic/Topic.d.ts +2 -3
  40. package/lib/Topic/Topic.js +18 -20
  41. package/lib/index.d.ts +0 -2
  42. package/lib/index.js +0 -7
  43. package/lib/types.d.ts +1 -1
  44. package/package.json +20 -20
  45. package/src/Article/Article.tsx +2 -21
  46. package/src/Embed/AudioEmbed.tsx +5 -11
  47. package/src/Embed/BrightcoveEmbed.tsx +10 -7
  48. package/src/Embed/ConceptEmbed.stories.tsx +1 -1
  49. package/src/Embed/ConceptEmbed.tsx +9 -4
  50. package/src/Embed/ImageEmbed.tsx +12 -2
  51. package/src/Embed/conceptComponents.tsx +2 -3
  52. package/src/Footer/FooterLinks.tsx +1 -1
  53. package/src/LicenseByline/EmbedByline.tsx +4 -11
  54. package/src/Messages/MessageBanner.tsx +3 -8
  55. package/src/Messages/MessageBox.tsx +3 -8
  56. package/src/Navigation/NavigationTopicAbout.tsx +2 -5
  57. package/src/Notion/Notion.tsx +2 -2
  58. package/src/RadioButtonGroup/RadioButtonGroup.stories.tsx +126 -0
  59. package/src/RadioButtonGroup/RadioButtonGroup.tsx +137 -104
  60. package/src/SearchTypeResult/SearchFieldHeader.tsx +4 -4
  61. package/src/TagSelector/ariaMessages.ts +1 -1
  62. package/src/Topic/Topic.tsx +2 -5
  63. package/src/index.ts +0 -10
  64. package/src/types.ts +1 -1
  65. package/es/User/UserInfo.js +0 -114
  66. package/es/User/apiTypes.js +0 -1
  67. package/es/User/index.js +0 -10
  68. package/es/User/parseUserObject.js +0 -101
  69. package/lib/User/UserInfo.d.ts +0 -12
  70. package/lib/User/UserInfo.js +0 -119
  71. package/lib/User/apiTypes.d.ts +0 -70
  72. package/lib/User/apiTypes.js +0 -5
  73. package/lib/User/index.d.ts +0 -11
  74. package/lib/User/index.js +0 -12
  75. package/lib/User/parseUserObject.d.ts +0 -33
  76. package/lib/User/parseUserObject.js +0 -107
  77. package/src/User/UserInfo.tsx +0 -101
  78. package/src/User/__tests__/parseUserObject-test.ts +0 -316
  79. package/src/User/apiTypes.ts +0 -84
  80. package/src/User/index.ts +0 -21
  81. package/src/User/parseUserObject.ts +0 -89
package/lib/index.d.ts CHANGED
@@ -37,8 +37,6 @@ export { default as SearchResultSleeve } from './Search/SearchResultSleeve';
37
37
  export { default as ContentTypeResult } from './Search/ContentTypeResult';
38
38
  export { SearchFieldForm } from './Search/SearchFieldForm';
39
39
  export { default as MastheadSearchModal } from './Masthead/MastheadSearchModal';
40
- export { UserInfo } from './User';
41
- export type { AffiliationType, FeideGoGroup, FeideGroup, FeideOrg, FeideUserApiType, FeideMembershipType, FeideUser, } from './User';
42
40
  export { default as resourceTypeColor } from './utils/resourceTypeColor';
43
41
  export { default as CreatedBy } from './CreatedBy';
44
42
  export { MessageBox, MessageBoxTag, MessageBanner } from './Messages';
package/lib/index.js CHANGED
@@ -1162,12 +1162,6 @@ Object.defineProperty(exports, "UnknownEmbed", {
1162
1162
  return _Embed.UnknownEmbed;
1163
1163
  }
1164
1164
  });
1165
- Object.defineProperty(exports, "UserInfo", {
1166
- enumerable: true,
1167
- get: function get() {
1168
- return _User.UserInfo;
1169
- }
1170
- });
1171
1165
  Object.defineProperty(exports, "WIDE_FRONTPAGE_ARTICLE_MAX_WIDTH", {
1172
1166
  enumerable: true,
1173
1167
  get: function get() {
@@ -1281,7 +1275,6 @@ var _SearchResultSleeve = _interopRequireDefault(require("./Search/SearchResultS
1281
1275
  var _ContentTypeResult = _interopRequireDefault(require("./Search/ContentTypeResult"));
1282
1276
  var _SearchFieldForm = require("./Search/SearchFieldForm");
1283
1277
  var _MastheadSearchModal = _interopRequireDefault(require("./Masthead/MastheadSearchModal"));
1284
- var _User = require("./User");
1285
1278
  var _resourceTypeColor = _interopRequireDefault(require("./utils/resourceTypeColor"));
1286
1279
  var _CreatedBy = _interopRequireDefault(require("./CreatedBy"));
1287
1280
  var _Messages = require("./Messages");
package/lib/types.d.ts CHANGED
@@ -57,7 +57,7 @@ export interface FootNote {
57
57
  }
58
58
  export interface Article {
59
59
  title: string;
60
- introduction: string;
60
+ introduction: ReactNode;
61
61
  content: ReactNode;
62
62
  footNotes: Array<FootNote>;
63
63
  copyright?: Copyright;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndla/ui",
3
- "version": "46.0.2",
3
+ "version": "47.0.0",
4
4
  "description": "UI component library for NDLA.",
5
5
  "license": "GPL-3.0",
6
6
  "main": "lib/index.js",
@@ -31,42 +31,42 @@
31
31
  "types"
32
32
  ],
33
33
  "dependencies": {
34
- "@ndla/accordion": "^2.2.25",
35
- "@ndla/button": "^11.0.10",
34
+ "@ndla/accordion": "^2.2.26",
35
+ "@ndla/button": "^12.0.0",
36
36
  "@ndla/carousel": "^4.0.3",
37
37
  "@ndla/core": "^4.1.9",
38
- "@ndla/dropdown-menu": "^1.0.7",
39
- "@ndla/forms": "^5.0.0",
40
- "@ndla/hooks": "^2.1.0",
38
+ "@ndla/dropdown-menu": "^1.0.8",
39
+ "@ndla/forms": "^5.0.1",
40
+ "@ndla/hooks": "^2.1.1",
41
41
  "@ndla/icons": "^4.0.9",
42
42
  "@ndla/licenses": "^7.1.4",
43
- "@ndla/modal": "^4.0.10",
44
- "@ndla/notion": "^6.0.0",
45
- "@ndla/safelink": "^4.1.24",
46
- "@ndla/select": "^2.4.16",
43
+ "@ndla/modal": "^5.0.0",
44
+ "@ndla/notion": "^6.0.1",
45
+ "@ndla/safelink": "^4.1.25",
46
+ "@ndla/select": "^3.0.0",
47
47
  "@ndla/switch": "^1.1.14",
48
48
  "@ndla/tabs": "^3.0.10",
49
- "@ndla/tooltip": "^4.1.21",
49
+ "@ndla/tooltip": "^5.0.0",
50
50
  "@ndla/typography": "^0.2.1",
51
- "@ndla/util": "^3.2.0",
52
- "@radix-ui/react-popover": "^1.0.6",
51
+ "@ndla/util": "^4.0.0",
52
+ "@radix-ui/react-popover": "^1.0.7",
53
+ "@radix-ui/react-radio-group": "^1.1.3",
53
54
  "@radix-ui/react-slider": "^1.1.2",
54
55
  "date-fns": "^2.30.0",
55
- "html-react-parser": "^3.0.8",
56
- "i18next-browser-languagedetector": "^6.1.1",
56
+ "html-react-parser": "^4.2.2",
57
+ "i18next-browser-languagedetector": "^7.1.0",
57
58
  "react-bem-helper": "1.4.1",
58
59
  "react-device-detect": "^2.2.3",
59
60
  "react-select": "^5.7.5",
60
- "react-swipeable": "^7.0.0",
61
- "remarkable": "^2.0.1"
61
+ "react-swipeable": "^7.0.0"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "@emotion/react": "^11.10.4",
65
65
  "@emotion/styled": "^11.10.4",
66
- "i18next": "^21.9.2",
66
+ "i18next": "^23.5.1",
67
67
  "lodash": "^4.17.20",
68
68
  "react": ">= 16.8.0",
69
- "react-i18next": "^11.18.6"
69
+ "react-i18next": "^13.3.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@ndla/types-backend": "^0.2.21",
@@ -80,5 +80,5 @@
80
80
  "publishConfig": {
81
81
  "access": "public"
82
82
  },
83
- "gitHead": "6beec50182999798fe82aa9aad1499d33a175b97"
83
+ "gitHead": "7b9ab5e858be896e97c8360f92b2385adceabf7a"
84
84
  }
@@ -8,7 +8,6 @@
8
8
 
9
9
  import { ReactNode, useEffect, useRef, useState, forwardRef } from 'react';
10
10
  import BEMHelper from 'react-bem-helper';
11
- import parse from 'html-react-parser';
12
11
  import styled from '@emotion/styled';
13
12
 
14
13
  import { useIntersectionObserver } from '@ndla/hooks';
@@ -72,24 +71,10 @@ export const ArticleTitle = ({ children, icon, label, id, lang }: ArticleTitlePr
72
71
 
73
72
  type ArticleIntroductionProps = {
74
73
  children: ReactNode;
75
- renderMarkdown: (text: string) => string;
76
74
  lang?: string;
77
75
  };
78
76
 
79
- export const ArticleIntroduction = ({
80
- children,
81
- lang,
82
- renderMarkdown = (text) => {
83
- return text;
84
- },
85
- }: ArticleIntroductionProps) => {
86
- if (typeof children === 'string') {
87
- return (
88
- <div className="article_introduction" lang={lang}>
89
- {parse(renderMarkdown(children))}
90
- </div>
91
- );
92
- }
77
+ export const ArticleIntroduction = ({ children, lang }: ArticleIntroductionProps) => {
93
78
  if (children) {
94
79
  return (
95
80
  <div className="article_introduction" lang={lang}>
@@ -135,7 +120,6 @@ type Props = {
135
120
  messageBoxLinks?: [];
136
121
  competenceGoals?: ReactNode;
137
122
  id: string;
138
- renderMarkdown: (text: string) => string;
139
123
  notions?: ReactNode;
140
124
  accessMessage?: string;
141
125
  lang?: string;
@@ -164,7 +148,6 @@ export const Article = ({
164
148
  competenceGoals,
165
149
  id,
166
150
  notions,
167
- renderMarkdown,
168
151
  accessMessage,
169
152
  heartButton,
170
153
  contentTransformed,
@@ -217,9 +200,7 @@ export const Article = ({
217
200
  <ArticleTitle id={id} icon={icon} label={messages.label} lang={lang}>
218
201
  {title}
219
202
  </ArticleTitle>
220
- <ArticleIntroduction renderMarkdown={renderMarkdown} lang={lang}>
221
- {introduction}
222
- </ArticleIntroduction>
203
+ <ArticleIntroduction lang={lang}>{introduction}</ArticleIntroduction>
223
204
  </ArticleHeaderWrapper>
224
205
  </LayoutItem>
225
206
  <LayoutItem layout="center">
@@ -8,8 +8,6 @@
8
8
 
9
9
  import { AudioMetaData, ImageMetaData } from '@ndla/types-embed';
10
10
  import { COPYRIGHTED } from '@ndla/licenses';
11
- //@ts-ignore
12
- import { Remarkable } from 'remarkable';
13
11
  import AudioPlayer from '../AudioPlayer';
14
12
  import { Figure } from '../Figure';
15
13
  import { Author } from './ImageEmbed';
@@ -29,12 +27,6 @@ export const getFirstNonEmptyLicenseCredits = (authors: {
29
27
  processors: Author[];
30
28
  }) => Object.values(authors).find((i) => i.length > 0) ?? [];
31
29
 
32
- const renderMarkdown = (text: string) => {
33
- const md = new Remarkable();
34
- const rendered = md.render(text);
35
- return <span dangerouslySetInnerHTML={{ __html: rendered }} />;
36
- };
37
-
38
30
  const imageMetaToMockEmbed = (
39
31
  imageMeta: Extract<AudioMetaData, { status: 'success' }>,
40
32
  ): Extract<ImageMetaData, { status: 'success' }> => ({
@@ -62,8 +54,6 @@ const AudioEmbed = ({ embed, heartButton: HeartButton, lang }: Props) => {
62
54
 
63
55
  const subtitle = data.series ? { title: data.series.title.title, url: `/podkast/${data.series.id}` } : undefined;
64
56
 
65
- const textVersion = data.manuscript?.manuscript.length ? renderMarkdown(data.manuscript.manuscript) : undefined;
66
-
67
57
  const coverPhoto = data.podcastMeta?.coverPhoto;
68
58
 
69
59
  const img = coverPhoto && { url: coverPhoto.url, alt: coverPhoto.altText };
@@ -74,7 +64,11 @@ const AudioEmbed = ({ embed, heartButton: HeartButton, lang }: Props) => {
74
64
  description={data.podcastMeta?.introduction ?? ''}
75
65
  img={img}
76
66
  src={data.audioFile.url}
77
- textVersion={textVersion}
67
+ textVersion={
68
+ data.manuscript?.manuscript.length ? (
69
+ <span dangerouslySetInnerHTML={{ __html: data.manuscript.manuscript }} />
70
+ ) : undefined
71
+ }
78
72
  title={data.title.title}
79
73
  subtitle={subtitle}
80
74
  />
@@ -10,7 +10,8 @@ import sortBy from 'lodash/sortBy';
10
10
  import styled from '@emotion/styled';
11
11
  import { spacing } from '@ndla/core';
12
12
  import { COPYRIGHTED } from '@ndla/licenses';
13
- import { useEffect, useRef, useState } from 'react';
13
+ import { useEffect, useMemo, useRef, useState } from 'react';
14
+ import parse from 'html-react-parser';
14
15
  import { BrightcoveEmbedData, BrightcoveMetaData, BrightcoveVideoSource } from '@ndla/types-embed';
15
16
  import { useTranslation } from 'react-i18next';
16
17
  import { ButtonV2 } from '@ndla/button';
@@ -62,6 +63,13 @@ const BrightcoveEmbed = ({ embed, isConcept, heartButton: HeartButton }: Props)
62
63
  const iframeRef = useRef<HTMLIFrameElement>(null);
63
64
  const { embedData } = embed;
64
65
  const fallbackTitle = `${t('embed.type.video')}: ${embedData.videoid}`;
66
+ const parsedDescription = useMemo(() => {
67
+ if (embed.embedData.caption) {
68
+ return parse(embed.embedData.caption);
69
+ } else if (embed.status === 'success' && embed.data.description) {
70
+ return parse(embed.data.description);
71
+ }
72
+ }, [embed]);
65
73
 
66
74
  useEffect(() => {
67
75
  const iframe = iframeRef.current;
@@ -108,12 +116,7 @@ const BrightcoveEmbed = ({ embed, isConcept, heartButton: HeartButton }: Props)
108
116
  allowFullScreen
109
117
  />
110
118
  </div>
111
- <EmbedByline
112
- type="video"
113
- copyright={data.copyright!}
114
- description={embedData.caption ?? data.description ?? ''}
115
- bottomRounded
116
- >
119
+ <EmbedByline type="video" copyright={data.copyright!} description={parsedDescription} bottomRounded>
117
120
  {!!linkedVideoId && (
118
121
  <LinkedVideoButton
119
122
  variant="outline"
@@ -46,7 +46,7 @@ const conceptMetaData: ConceptData['concept'] = {
46
46
  title: { title: 'skin – formasjonsskade', language: 'nb' },
47
47
  content: {
48
48
  content:
49
- 'Ordet «skin» er engelsk og brukes om formasjonsskade som oppstår i boreprosessen i området som grenser inn til brønnen. Skaden er størst i området nærmest hullet, men den kan bre seg utover et stykke fra brønnen. Skin forteller om bergartens permeabilitet i reservoarsonen. \n\nHullveggen skades både av borekronen, små partikler og væsken som brukes i brønnen.\n\nSkaden i bergarten gir dårligere forhold for oljen som skal strømme til brønnen. Gangene i bergarten plugges, og det oppstår et trykkfall som reduserer produksjonstrykket i brønnen.\n\nDet er viktig å redusere omfanget av skaden ved å velge væsker som passer godt til bergartsegenskapene, og å bore med en borekrone som skader minst mulig.\n\nSkader som er dannet av borevæske, kan repareres ved å syrebehandle hullets overflate.\n',
49
+ ' <p>Ordet «skin» er engelsk og brukes om formasjonsskade som oppstår i boreprosessen i området som grenser inn til brønnen. Skaden er størst i området nærmest hullet, men den kan bre seg utover et stykke fra brønnen. Skin forteller om bergartens permeabilitet i reservoarsonen.</p> <p>Hullveggen skades både av borekronen, små partikler og væsken som brukes i brønnen.</p> <p>Skaden i bergarten gir dårligere forhold for oljen som skal strømme til brønnen. Gangene i bergarten plugges, og det oppstår et trykkfall som reduserer produksjonstrykket i brønnen.</p> <p>Det er viktig å redusere omfanget av skaden ved å velge væsker som passer godt til bergartsegenskapene, og å bore med en borekrone som skader minst mulig.</p> <p>Skader som er dannet av borevæske, kan repareres ved å syrebehandle hullets overflate.</p>',
50
50
  language: 'nb',
51
51
  },
52
52
  copyright: {
@@ -6,7 +6,8 @@
6
6
  *
7
7
  */
8
8
 
9
- import { ReactElement, ReactNode, useCallback, useRef, useState } from 'react';
9
+ import { ReactElement, ReactNode, useCallback, useMemo, useRef, useState } from 'react';
10
+ import parse from 'html-react-parser';
10
11
  import { useTranslation } from 'react-i18next';
11
12
  import styled from '@emotion/styled';
12
13
  import { isMobile } from 'react-device-detect';
@@ -92,6 +93,10 @@ const StyledButton = styled.button`
92
93
  `;
93
94
 
94
95
  export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Props) => {
96
+ const parsedContent = useMemo(() => {
97
+ if (embed.status === 'error' || !embed.data.concept.content) return undefined;
98
+ return parse(embed.data.concept.content.content);
99
+ }, [embed]);
95
100
  if (embed.status === 'error' && embed.embedData.type === 'inline') {
96
101
  return <span>{embed.embedData.linkText}</span>;
97
102
  } else if (embed.status === 'error') {
@@ -107,7 +112,7 @@ export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Pro
107
112
  <BlockConcept
108
113
  fullWidth={fullWidth}
109
114
  title={concept.title}
110
- content={concept.content?.content}
115
+ content={parsedContent}
111
116
  metaImage={concept.metaImage}
112
117
  copyright={concept.copyright}
113
118
  source={concept.source}
@@ -122,7 +127,7 @@ export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Pro
122
127
  return (
123
128
  <InlineConcept
124
129
  title={concept.title}
125
- content={concept.content?.content}
130
+ content={parsedContent}
126
131
  metaImage={concept.metaImage}
127
132
  copyright={concept.copyright}
128
133
  source={concept.source}
@@ -138,7 +143,7 @@ export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Pro
138
143
  return (
139
144
  <ConceptNotionV2
140
145
  title={concept.title}
141
- content={concept.content?.content}
146
+ content={parsedContent}
142
147
  metaImage={concept.metaImage}
143
148
  copyright={concept.copyright}
144
149
  source={concept.source}
@@ -8,7 +8,8 @@
8
8
 
9
9
  import { ImageEmbedData, ImageMetaData } from '@ndla/types-embed';
10
10
  import { useTranslation } from 'react-i18next';
11
- import { MouseEventHandler, useState } from 'react';
11
+ import { MouseEventHandler, useMemo, useState } from 'react';
12
+ import parse from 'html-react-parser';
12
13
  import { ExpandTwoArrows } from '@ndla/icons/action';
13
14
  import { COPYRIGHTED } from '@ndla/licenses';
14
15
  import { ArrowCollapse, ChevronDown, ChevronUp } from '@ndla/icons/common';
@@ -106,6 +107,15 @@ const expandedSizes = '(min-width: 1024px) 1024px, 100vw';
106
107
  const ImageEmbed = ({ embed, previewAlt, heartButton: HeartButton, inGrid, path }: Props) => {
107
108
  const [isBylineHidden, setIsBylineHidden] = useState(hideByline(embed.embedData.size));
108
109
  const [imageSizes, setImageSizes] = useState<string | undefined>(undefined);
110
+
111
+ const parsedDescription = useMemo(() => {
112
+ if (embed.embedData.caption) {
113
+ return parse(embed.embedData.caption);
114
+ } else if (embed.status === 'success' && embed.data.caption.caption) {
115
+ return parse(embed.data.caption.caption);
116
+ }
117
+ }, [embed]);
118
+
109
119
  if (embed.status === 'error') {
110
120
  const { align, size } = embed.embedData;
111
121
  const figureType = getFigureType(size, align);
@@ -158,7 +168,7 @@ const ImageEmbed = ({ embed, previewAlt, heartButton: HeartButton, inGrid, path
158
168
  <EmbedByline
159
169
  type="image"
160
170
  copyright={data.copyright}
161
- description={embedData.caption ?? data.caption.caption}
171
+ description={parsedDescription}
162
172
  bottomRounded
163
173
  visibleAlt={previewAlt ? embed.embedData.alt : ''}
164
174
  inGrid={inGrid}
@@ -11,7 +11,6 @@ import { ConceptData, ConceptVisualElementMeta } from '@ndla/types-embed';
11
11
  import { useTranslation } from 'react-i18next';
12
12
  import { css } from '@emotion/react';
13
13
  import { breakpoints, colors, fonts, misc, mq, spacing } from '@ndla/core';
14
- import { parseMarkdown } from '@ndla/util';
15
14
  import styled from '@emotion/styled';
16
15
  import { COPYRIGHTED } from '@ndla/licenses';
17
16
  import { Copyright } from '../types';
@@ -26,7 +25,7 @@ export type ConceptType = 'concept' | 'gloss';
26
25
 
27
26
  export interface ConceptNotionData {
28
27
  title: ConceptData['concept']['title'];
29
- content?: string;
28
+ content?: ReactNode;
30
29
  metaImage?: {
31
30
  url?: string;
32
31
  alt?: string;
@@ -210,7 +209,7 @@ export const ConceptNotionV2 = forwardRef<HTMLDivElement, ConceptNotionProps>(
210
209
  ) : visualElement?.resource === 'external' ? (
211
210
  <ExternalEmbed embed={visualElement} />
212
211
  ) : null}
213
- <NotionDialogText>{parseMarkdown(content ?? '', 'body')}</NotionDialogText>
212
+ {content && <NotionDialogText>{content}</NotionDialogText>}
214
213
  </StyledNotionDialogContent>
215
214
  {tags && (
216
215
  <ListWrapper>
@@ -106,7 +106,7 @@ const FooterLinks = ({ links }: FooterLinksProps) => {
106
106
  {commonLinks.map((link) => (
107
107
  <div key={link.url}>
108
108
  <StyledSafeLink
109
- key={t<string>(`footer.ndlaLinks.${link.key}`)}
109
+ key={t(`footer.ndlaLinks.${link.key}`)}
110
110
  aria-label={t(`footer.ndlaLinks.${link.key}`)}
111
111
  to={link.url}
112
112
  target="_blank"
@@ -6,7 +6,7 @@
6
6
  *
7
7
  */
8
8
 
9
- import { ReactNode, useMemo } from 'react';
9
+ import { ReactNode } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import styled from '@emotion/styled';
12
12
  import { css } from '@emotion/react';
@@ -17,14 +17,13 @@ import { ICopyright as AudioCopyright } from '@ndla/types-backend/audio-api';
17
17
  import { IDraftCopyright as ConceptCopyright } from '@ndla/types-backend/concept-api';
18
18
  import { BrightcoveCopyright } from '@ndla/types-embed';
19
19
  import { WarningOutline } from '@ndla/icons/common';
20
- import { parseMarkdown } from '@ndla/util';
21
20
  import LicenseLink from './LicenseLink';
22
21
  import LicenseDescription from './LicenseDescription';
23
22
 
24
23
  interface BaseProps {
25
24
  topRounded?: boolean;
26
25
  bottomRounded?: boolean;
27
- description?: string;
26
+ description?: ReactNode;
28
27
  children?: ReactNode;
29
28
  visibleAlt?: string;
30
29
  error?: true | false;
@@ -132,7 +131,7 @@ const EmbedByline = ({
132
131
  type,
133
132
  topRounded,
134
133
  bottomRounded,
135
- description: descriptionProp,
134
+ description,
136
135
  children,
137
136
  visibleAlt,
138
137
  first = true,
@@ -140,12 +139,6 @@ const EmbedByline = ({
140
139
  ...props
141
140
  }: Props) => {
142
141
  const { t, i18n } = useTranslation();
143
- const strippedDescription = descriptionProp?.trim();
144
-
145
- const description = useMemo(() => {
146
- const stripped = strippedDescription?.trim() ?? '';
147
- return parseMarkdown(stripped, 'caption');
148
- }, [strippedDescription]);
149
142
 
150
143
  if (props.error) {
151
144
  const typeString = type === 'h5p' ? 'H5P' : t(`embed.type.${type}`).toLowerCase();
@@ -164,7 +157,7 @@ const EmbedByline = ({
164
157
 
165
158
  return (
166
159
  <BylineWrapper data-top-rounded={topRounded} data-bottom-rounded={bottomRounded} data-first={first}>
167
- {!!strippedDescription?.length && description && <LicenseDescription description={description} />}
160
+ {description && <LicenseDescription description={description} />}
168
161
  {visibleAlt ? <StyledSpan>{`Alt: ${visibleAlt}`}</StyledSpan> : null}
169
162
  <RightsWrapper data-grid={inGrid}>
170
163
  {license ? <LicenseLink license={license} asLink={!!license.url.length} /> : null}
@@ -8,9 +8,8 @@
8
8
 
9
9
  import styled from '@emotion/styled';
10
10
  import { colors, spacing } from '@ndla/core';
11
- // @ts-ignore
12
- import { Remarkable } from 'remarkable';
13
11
  import { CloseButton } from '@ndla/button';
12
+ import { ReactNode } from 'react';
14
13
 
15
14
  interface WrapperProps {
16
15
  small?: boolean;
@@ -44,19 +43,15 @@ const StyledClosebutton = styled(CloseButton)`
44
43
 
45
44
  interface Props {
46
45
  small?: boolean;
47
- children?: string;
46
+ children?: ReactNode;
48
47
  showCloseButton?: boolean;
49
48
  onClose?: () => void;
50
49
  }
51
50
 
52
- const markdown = new Remarkable({ breaks: true });
53
- markdown.inline.ruler.enable(['sub', 'sup']);
54
- markdown.block.ruler.disable(['list', 'table']);
55
-
56
51
  const MessageBanner = ({ children, onClose, showCloseButton, small }: Props) => {
57
52
  return (
58
53
  <MessageBannerWrapper small={small}>
59
- <TextWrapper dangerouslySetInnerHTML={{ __html: markdown.render(children) }} />
54
+ <TextWrapper>{children}</TextWrapper>
60
55
  {showCloseButton && <StyledClosebutton onClick={onClose} />}
61
56
  </MessageBannerWrapper>
62
57
  );
@@ -10,14 +10,9 @@ import styled from '@emotion/styled';
10
10
  import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
11
11
  import { InformationOutline, HumanMaleBoard, Forward, WarningOutline } from '@ndla/icons/common';
12
12
 
13
- // @ts-ignore
14
- import { Remarkable } from 'remarkable';
15
13
  import { CloseButton } from '@ndla/button';
16
14
  import { css } from '@emotion/react';
17
-
18
- const markdown = new Remarkable({ breaks: true });
19
- markdown.inline.ruler.enable(['sub', 'sup']);
20
- markdown.block.ruler.disable(['list', 'table']);
15
+ import { ReactNode } from 'react';
21
16
 
22
17
  type MessageBoxType = 'ghost' | 'danger';
23
18
 
@@ -109,7 +104,7 @@ interface LinkProps {
109
104
 
110
105
  interface Props {
111
106
  type?: MessageBoxType;
112
- children?: string;
107
+ children?: ReactNode;
113
108
  links?: LinkProps[];
114
109
  showCloseButton?: boolean;
115
110
  onClose?: () => void;
@@ -133,7 +128,7 @@ export const MessageBox = ({ type, children = '', links, showCloseButton, onClos
133
128
  <Icon type={type} />
134
129
  </IconWrapper>
135
130
  <div>
136
- <TextWrapper dangerouslySetInnerHTML={{ __html: markdown.render(children) }} />
131
+ <TextWrapper>{children}</TextWrapper>
137
132
  {links && (
138
133
  <LinkWrapper>
139
134
  {links.map((x) => (
@@ -1,5 +1,4 @@
1
1
  import { ReactNode } from 'react';
2
- import parse from 'html-react-parser';
3
2
  import styled from '@emotion/styled';
4
3
  import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
5
4
  import { css } from '@emotion/react';
@@ -105,13 +104,12 @@ const StyledContentWrapper = styled.div<InvertItProps>`
105
104
 
106
105
  type Props = {
107
106
  heading: string;
108
- introduction: string;
107
+ introduction: ReactNode;
109
108
  onToggleShowContent: () => void;
110
109
  showContent: boolean;
111
110
  isLoading: boolean;
112
111
  isAdditionalTopic?: boolean;
113
112
  invertedStyle?: boolean;
114
- renderMarkdown: (text: string) => string;
115
113
  children: ReactNode;
116
114
  };
117
115
 
@@ -123,7 +121,6 @@ export const NavigationTopicAbout = ({
123
121
  isLoading,
124
122
  isAdditionalTopic,
125
123
  invertedStyle,
126
- renderMarkdown,
127
124
  children,
128
125
  }: Props) => {
129
126
  const { t } = useTranslation();
@@ -143,7 +140,7 @@ export const NavigationTopicAbout = ({
143
140
  ) : (
144
141
  <>
145
142
  <StyledIngress invertedStyle={invertedStyle}>
146
- {parse(renderMarkdown(introduction))}
143
+ {introduction}
147
144
  <StyledButtonWrapper invertedStyle={invertedStyle}>
148
145
  <ButtonV2
149
146
  aria-expanded={!!showContent}
@@ -8,7 +8,6 @@
8
8
  import { Fragment, ReactNode } from 'react';
9
9
  import styled from '@emotion/styled';
10
10
  import { useTranslation } from 'react-i18next';
11
- import { parseMarkdown } from '@ndla/util';
12
11
  import { breakpoints, fonts, mq, spacing } from '@ndla/core';
13
12
 
14
13
  const ContentWrapper = styled.div`
@@ -83,7 +82,8 @@ const Notion = ({ id, labels = [], text, title, visualElement, imageElement, chi
83
82
  {imageElement}
84
83
  {visualElement}
85
84
  <TextWrapper hasVisualElement={!!(imageElement || visualElement)}>
86
- {parseMarkdown(`**${title.trim()}** \u2013 ${text}`, 'body')}
85
+ <b>{title.trim()}</b>
86
+ {text}
87
87
  {!!labels.length && (
88
88
  <LabelsContainer>
89
89
  {t('searchPage.resultType.notionLabels')}