@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.
- package/es/Article/Article.js +3 -17
- package/es/Embed/AudioEmbed.js +6 -14
- package/es/Embed/BrightcoveEmbed.js +13 -5
- package/es/Embed/ConceptEmbed.js +16 -14
- package/es/Embed/ImageEmbed.js +11 -4
- package/es/Embed/conceptComponents.js +11 -12
- package/es/Footer/FooterLinks.js +6 -6
- package/es/LicenseByline/EmbedByline.js +7 -15
- package/es/Messages/MessageBanner.js +4 -13
- package/es/Messages/MessageBox.js +10 -20
- package/es/Navigation/NavigationTopicAbout.js +15 -17
- package/es/Notion/Notion.js +8 -7
- package/es/RadioButtonGroup/RadioButtonGroup.js +99 -80
- package/es/SearchTypeResult/SearchFieldHeader.js +4 -4
- package/es/Topic/Topic.js +18 -20
- package/es/index.js +0 -1
- package/lib/Article/Article.d.ts +2 -4
- package/lib/Article/Article.js +3 -17
- package/lib/Embed/AudioEmbed.js +6 -15
- package/lib/Embed/BrightcoveEmbed.js +12 -4
- package/lib/Embed/ConceptEmbed.js +15 -13
- package/lib/Embed/ImageEmbed.js +10 -3
- package/lib/Embed/conceptComponents.d.ts +1 -1
- package/lib/Embed/conceptComponents.js +11 -12
- package/lib/Footer/FooterLinks.js +6 -6
- package/lib/LicenseByline/EmbedByline.d.ts +2 -2
- package/lib/LicenseByline/EmbedByline.js +8 -16
- package/lib/Messages/MessageBanner.d.ts +2 -1
- package/lib/Messages/MessageBanner.js +5 -13
- package/lib/Messages/MessageBox.d.ts +2 -1
- package/lib/Messages/MessageBox.js +11 -19
- package/lib/Navigation/NavigationTopicAbout.d.ts +2 -3
- package/lib/Navigation/NavigationTopicAbout.js +15 -17
- package/lib/Notion/Notion.js +7 -6
- package/lib/RadioButtonGroup/RadioButtonGroup.d.ts +16 -8
- package/lib/RadioButtonGroup/RadioButtonGroup.js +108 -84
- package/lib/SearchTypeResult/SearchFieldHeader.js +4 -4
- package/lib/TagSelector/ariaMessages.d.ts +1 -1
- package/lib/Topic/Topic.d.ts +2 -3
- package/lib/Topic/Topic.js +18 -20
- package/lib/index.d.ts +0 -2
- package/lib/index.js +0 -7
- package/lib/types.d.ts +1 -1
- package/package.json +20 -20
- package/src/Article/Article.tsx +2 -21
- package/src/Embed/AudioEmbed.tsx +5 -11
- package/src/Embed/BrightcoveEmbed.tsx +10 -7
- package/src/Embed/ConceptEmbed.stories.tsx +1 -1
- package/src/Embed/ConceptEmbed.tsx +9 -4
- package/src/Embed/ImageEmbed.tsx +12 -2
- package/src/Embed/conceptComponents.tsx +2 -3
- package/src/Footer/FooterLinks.tsx +1 -1
- package/src/LicenseByline/EmbedByline.tsx +4 -11
- package/src/Messages/MessageBanner.tsx +3 -8
- package/src/Messages/MessageBox.tsx +3 -8
- package/src/Navigation/NavigationTopicAbout.tsx +2 -5
- package/src/Notion/Notion.tsx +2 -2
- package/src/RadioButtonGroup/RadioButtonGroup.stories.tsx +126 -0
- package/src/RadioButtonGroup/RadioButtonGroup.tsx +137 -104
- package/src/SearchTypeResult/SearchFieldHeader.tsx +4 -4
- package/src/TagSelector/ariaMessages.ts +1 -1
- package/src/Topic/Topic.tsx +2 -5
- package/src/index.ts +0 -10
- package/src/types.ts +1 -1
- package/es/User/UserInfo.js +0 -114
- package/es/User/apiTypes.js +0 -1
- package/es/User/index.js +0 -10
- package/es/User/parseUserObject.js +0 -101
- package/lib/User/UserInfo.d.ts +0 -12
- package/lib/User/UserInfo.js +0 -119
- package/lib/User/apiTypes.d.ts +0 -70
- package/lib/User/apiTypes.js +0 -5
- package/lib/User/index.d.ts +0 -11
- package/lib/User/index.js +0 -12
- package/lib/User/parseUserObject.d.ts +0 -33
- package/lib/User/parseUserObject.js +0 -107
- package/src/User/UserInfo.tsx +0 -101
- package/src/User/__tests__/parseUserObject-test.ts +0 -316
- package/src/User/apiTypes.ts +0 -84
- package/src/User/index.ts +0 -21
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "
|
|
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.
|
|
35
|
-
"@ndla/button": "^
|
|
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.
|
|
39
|
-
"@ndla/forms": "^5.0.
|
|
40
|
-
"@ndla/hooks": "^2.1.
|
|
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": "^
|
|
44
|
-
"@ndla/notion": "^6.0.
|
|
45
|
-
"@ndla/safelink": "^4.1.
|
|
46
|
-
"@ndla/select": "^
|
|
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": "^
|
|
49
|
+
"@ndla/tooltip": "^5.0.0",
|
|
50
50
|
"@ndla/typography": "^0.2.1",
|
|
51
|
-
"@ndla/util": "^
|
|
52
|
-
"@radix-ui/react-popover": "^1.0.
|
|
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": "^
|
|
56
|
-
"i18next-browser-languagedetector": "^
|
|
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": "^
|
|
66
|
+
"i18next": "^23.5.1",
|
|
67
67
|
"lodash": "^4.17.20",
|
|
68
68
|
"react": ">= 16.8.0",
|
|
69
|
-
"react-i18next": "^
|
|
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": "
|
|
83
|
+
"gitHead": "7b9ab5e858be896e97c8360f92b2385adceabf7a"
|
|
84
84
|
}
|
package/src/Article/Article.tsx
CHANGED
|
@@ -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
|
|
221
|
-
{introduction}
|
|
222
|
-
</ArticleIntroduction>
|
|
203
|
+
<ArticleIntroduction lang={lang}>{introduction}</ArticleIntroduction>
|
|
223
204
|
</ArticleHeaderWrapper>
|
|
224
205
|
</LayoutItem>
|
|
225
206
|
<LayoutItem layout="center">
|
package/src/Embed/AudioEmbed.tsx
CHANGED
|
@@ -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={
|
|
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
|
|
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={
|
|
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={
|
|
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={
|
|
146
|
+
content={parsedContent}
|
|
142
147
|
metaImage={concept.metaImage}
|
|
143
148
|
copyright={concept.copyright}
|
|
144
149
|
source={concept.source}
|
package/src/Embed/ImageEmbed.tsx
CHANGED
|
@@ -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={
|
|
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?:
|
|
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>{
|
|
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
|
|
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
|
|
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?:
|
|
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
|
|
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
|
-
{
|
|
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?:
|
|
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
|
|
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?:
|
|
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
|
|
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:
|
|
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
|
-
{
|
|
143
|
+
{introduction}
|
|
147
144
|
<StyledButtonWrapper invertedStyle={invertedStyle}>
|
|
148
145
|
<ButtonV2
|
|
149
146
|
aria-expanded={!!showContent}
|
package/src/Notion/Notion.tsx
CHANGED
|
@@ -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
|
-
{
|
|
85
|
+
<b>{title.trim()}</b>
|
|
86
|
+
{text}
|
|
87
87
|
{!!labels.length && (
|
|
88
88
|
<LabelsContainer>
|
|
89
89
|
{t('searchPage.resultType.notionLabels')}
|