@ndla/ui 36.0.2 → 37.0.1

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 (174) hide show
  1. package/es/Article/Article.js +7 -13
  2. package/es/Article/ArticleByline.js +79 -123
  3. package/es/Article/ArticleFootNotes.js +16 -11
  4. package/es/AudioPlayer/AudioPlayer.js +33 -35
  5. package/es/AudioPlayer/initAudioPlayers.js +6 -1
  6. package/es/ContentTypeBadge/ContentTypeBadge.js +27 -6
  7. package/es/Embed/AudioEmbed.js +44 -188
  8. package/es/Embed/BrightcoveEmbed.js +32 -127
  9. package/es/Embed/ConceptEmbed.js +53 -75
  10. package/es/Embed/EmbedErrorPlaceholder.js +41 -0
  11. package/es/Embed/ExternalEmbed.js +5 -12
  12. package/es/Embed/H5pEmbed.js +5 -15
  13. package/es/Embed/IframeEmbed.js +4 -4
  14. package/es/Embed/ImageEmbed.js +41 -153
  15. package/es/Embed/RelatedContentEmbed.js +3 -3
  16. package/es/Embed/conceptComponents.js +62 -228
  17. package/es/Embed/types.js +1 -0
  18. package/es/KeyFigure/KeyFigure.js +57 -0
  19. package/es/{KeyPerformanceIndicator → KeyFigure}/index.js +1 -1
  20. package/es/LicenseByline/EmbedByline.js +33 -8
  21. package/es/LicenseByline/LicenseDescription.js +16 -14
  22. package/es/List/OrderedList.js +48 -0
  23. package/es/List/UnOrderedList.js +36 -0
  24. package/es/List/index.js +10 -0
  25. package/es/Navigation/NavigationBox.js +41 -48
  26. package/es/Navigation/NavigationHeading.js +18 -29
  27. package/es/Notion/Notion.js +5 -5
  28. package/es/Resource/ListResource.js +9 -9
  29. package/es/Resource/resourceComponents.js +12 -11
  30. package/es/Typography/Heading.js +38 -0
  31. package/es/Typography/index.js +9 -0
  32. package/es/all.css +1 -1
  33. package/es/index.js +4 -2
  34. package/es/locale/messages-en.js +6 -2
  35. package/es/locale/messages-nb.js +6 -2
  36. package/es/locale/messages-nn.js +6 -2
  37. package/es/locale/messages-se.js +6 -2
  38. package/es/locale/messages-sma.js +6 -2
  39. package/es/model/ContentType.js +7 -1
  40. package/lib/Article/Article.d.ts +1 -3
  41. package/lib/Article/Article.js +7 -13
  42. package/lib/Article/ArticleByline.d.ts +3 -5
  43. package/lib/Article/ArticleByline.js +83 -126
  44. package/lib/Article/ArticleFootNotes.js +16 -11
  45. package/lib/AudioPlayer/AudioPlayer.d.ts +1 -2
  46. package/lib/AudioPlayer/AudioPlayer.js +33 -36
  47. package/lib/AudioPlayer/initAudioPlayers.d.ts +1 -0
  48. package/lib/AudioPlayer/initAudioPlayers.js +9 -3
  49. package/lib/ContentTypeBadge/ContentTypeBadge.js +27 -6
  50. package/lib/Embed/AudioEmbed.d.ts +3 -2
  51. package/lib/Embed/AudioEmbed.js +53 -192
  52. package/lib/Embed/BrightcoveEmbed.d.ts +3 -1
  53. package/lib/Embed/BrightcoveEmbed.js +32 -126
  54. package/lib/Embed/ConceptEmbed.d.ts +7 -2
  55. package/lib/Embed/ConceptEmbed.js +51 -73
  56. package/lib/Embed/EmbedErrorPlaceholder.d.ts +17 -0
  57. package/lib/Embed/EmbedErrorPlaceholder.js +48 -0
  58. package/lib/Embed/ExternalEmbed.js +5 -11
  59. package/lib/Embed/H5pEmbed.js +5 -14
  60. package/lib/Embed/IframeEmbed.d.ts +2 -2
  61. package/lib/Embed/IframeEmbed.js +4 -4
  62. package/lib/Embed/ImageEmbed.d.ts +3 -10
  63. package/lib/Embed/ImageEmbed.js +48 -161
  64. package/lib/Embed/RelatedContentEmbed.js +3 -3
  65. package/lib/Embed/conceptComponents.d.ts +4 -2
  66. package/lib/Embed/conceptComponents.js +67 -231
  67. package/lib/Embed/index.d.ts +1 -0
  68. package/lib/Embed/types.d.ts +14 -0
  69. package/lib/Embed/types.js +5 -0
  70. package/lib/KeyFigure/KeyFigure.d.ts +10 -0
  71. package/lib/KeyFigure/KeyFigure.js +62 -0
  72. package/lib/KeyFigure/index.d.ts +1 -0
  73. package/lib/KeyFigure/index.js +13 -0
  74. package/lib/LicenseByline/EmbedByline.d.ts +10 -2
  75. package/lib/LicenseByline/EmbedByline.js +32 -7
  76. package/lib/LicenseByline/LicenseDescription.d.ts +3 -1
  77. package/lib/LicenseByline/LicenseDescription.js +14 -13
  78. package/lib/List/OrderedList.d.ts +15 -0
  79. package/lib/List/OrderedList.js +56 -0
  80. package/lib/List/UnOrderedList.d.ts +10 -0
  81. package/lib/List/UnOrderedList.js +43 -0
  82. package/lib/List/index.d.ts +9 -0
  83. package/lib/List/index.js +20 -0
  84. package/lib/Navigation/NavigationBox.js +40 -47
  85. package/lib/Navigation/NavigationHeading.js +17 -28
  86. package/lib/Notion/Notion.js +5 -5
  87. package/lib/Resource/ListResource.js +8 -8
  88. package/lib/Resource/resourceComponents.js +12 -11
  89. package/lib/Typography/Heading.d.ts +26 -0
  90. package/lib/Typography/Heading.js +45 -0
  91. package/lib/Typography/index.d.ts +8 -0
  92. package/lib/Typography/index.js +13 -0
  93. package/lib/all.css +1 -1
  94. package/lib/index.d.ts +4 -1
  95. package/lib/index.js +23 -3
  96. package/lib/locale/messages-en.d.ts +4 -0
  97. package/lib/locale/messages-en.js +6 -2
  98. package/lib/locale/messages-nb.d.ts +4 -0
  99. package/lib/locale/messages-nb.js +6 -2
  100. package/lib/locale/messages-nn.d.ts +4 -0
  101. package/lib/locale/messages-nn.js +6 -2
  102. package/lib/locale/messages-se.d.ts +4 -0
  103. package/lib/locale/messages-se.js +6 -2
  104. package/lib/locale/messages-sma.d.ts +4 -0
  105. package/lib/locale/messages-sma.js +6 -2
  106. package/lib/model/ContentType.d.ts +1 -0
  107. package/lib/model/ContentType.js +9 -2
  108. package/package.json +15 -15
  109. package/src/Article/Article.tsx +1 -8
  110. package/src/Article/ArticleByline.tsx +78 -127
  111. package/src/Article/ArticleFootNotes.tsx +33 -10
  112. package/src/Article/component.article.scss +1 -52
  113. package/src/Article/component.footnotes.scss +2 -2
  114. package/src/Aside/component.aside.scss +3 -3
  115. package/src/AudioPlayer/AudioPlayer.tsx +11 -24
  116. package/src/AudioPlayer/initAudioPlayers.tsx +7 -2
  117. package/src/ContentTypeBadge/ContentTypeBadge.tsx +29 -6
  118. package/src/ContentTypeBadge/component.content-type-badge.scss +9 -3
  119. package/src/Dialog/component.dialog.scss +4 -5
  120. package/src/Embed/AudioEmbed.stories.tsx +5 -3
  121. package/src/Embed/AudioEmbed.tsx +45 -192
  122. package/src/Embed/BrightcoveEmbed.stories.tsx +5 -1
  123. package/src/Embed/BrightcoveEmbed.tsx +24 -98
  124. package/src/Embed/ConceptEmbed.stories.tsx +5 -0
  125. package/src/Embed/ConceptEmbed.tsx +43 -54
  126. package/src/Embed/EmbedErrorPlaceholder.tsx +59 -0
  127. package/src/Embed/ExternalEmbed.stories.tsx +86 -0
  128. package/src/Embed/ExternalEmbed.tsx +3 -8
  129. package/src/Embed/H5pEmbed.stories.tsx +92 -0
  130. package/src/Embed/H5pEmbed.tsx +3 -11
  131. package/src/Embed/IframeEmbed.stories.tsx +130 -0
  132. package/src/Embed/IframeEmbed.tsx +3 -3
  133. package/src/Embed/ImageEmbed.stories.tsx +3 -1
  134. package/src/Embed/ImageEmbed.tsx +21 -116
  135. package/src/Embed/RelatedContentEmbed.tsx +3 -1
  136. package/src/Embed/conceptComponents.tsx +67 -257
  137. package/src/Embed/index.ts +1 -0
  138. package/src/Embed/types.ts +12 -0
  139. package/src/FactBox/component.factbox.scss +3 -3
  140. package/src/Figure/component.figure-license.scss +4 -4
  141. package/src/Figure/component.figure.scss +1 -1
  142. package/src/KeyFigure/KeyFigure.stories.tsx +36 -0
  143. package/src/{KeyPerformanceIndicator/KeyPerformanceIndicator.tsx → KeyFigure/KeyFigure.tsx} +9 -7
  144. package/src/{KeyPerformanceIndicator → KeyFigure}/index.ts +1 -1
  145. package/src/LicenseByline/EmbedByline.stories.tsx +1 -0
  146. package/src/LicenseByline/EmbedByline.tsx +57 -9
  147. package/src/LicenseByline/LicenseDescription.tsx +9 -3
  148. package/src/List/OrderedList.tsx +115 -0
  149. package/src/List/UnOrderedList.tsx +49 -0
  150. package/src/List/index.ts +10 -0
  151. package/src/MediaList/component.medialist.scss +2 -2
  152. package/src/Navigation/NavigationBox.tsx +10 -14
  153. package/src/Navigation/NavigationHeading.tsx +15 -24
  154. package/src/Notion/Notion.tsx +1 -1
  155. package/src/RelatedArticleList/component.related-articles.scss +3 -13
  156. package/src/Resource/ListResource.tsx +6 -2
  157. package/src/Resource/resourceComponents.tsx +4 -2
  158. package/src/Table/component.tables.scss +0 -46
  159. package/src/Translation/component.translation.scss +3 -5
  160. package/src/Typography/Heading.tsx +96 -0
  161. package/src/Typography/index.ts +9 -0
  162. package/src/index.ts +5 -1
  163. package/src/locale/messages-en.ts +4 -0
  164. package/src/locale/messages-nb.ts +4 -0
  165. package/src/locale/messages-nn.ts +4 -0
  166. package/src/locale/messages-se.ts +4 -0
  167. package/src/locale/messages-sma.ts +4 -0
  168. package/src/model/ContentType.ts +7 -0
  169. package/es/KeyPerformanceIndicator/KeyPerformanceIndicator.js +0 -57
  170. package/lib/KeyPerformanceIndicator/KeyPerformanceIndicator.d.ts +0 -8
  171. package/lib/KeyPerformanceIndicator/KeyPerformanceIndicator.js +0 -62
  172. package/lib/KeyPerformanceIndicator/index.d.ts +0 -1
  173. package/lib/KeyPerformanceIndicator/index.js +0 -13
  174. package/src/KeyPerformanceIndicator/KeyPerformanceIndicator.stories.tsx +0 -79
@@ -6,23 +6,23 @@
6
6
  *
7
7
  */
8
8
 
9
- import { useCallback, useRef, useState } from 'react';
9
+ import { ReactElement, ReactNode, useCallback, useRef, useState } from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import styled from '@emotion/styled';
12
12
  import { isMobile } from 'react-device-detect';
13
13
  import { Root, Trigger, Content, Anchor, Close, Portal } from '@radix-ui/react-popover';
14
- import { ButtonV2, IconButtonV2 } from '@ndla/button';
14
+ import { IconButtonV2 } from '@ndla/button';
15
15
  import { Cross } from '@ndla/icons/action';
16
16
  import { breakpoints, colors, mq, spacing } from '@ndla/core';
17
- import { getGroupedContributorDescriptionList, getLicenseByAbbreviation, getLicenseCredits } from '@ndla/licenses';
18
- import { ModalV2 } from '@ndla/modal';
19
17
  import { ConceptMetaData } from '@ndla/types-embed';
20
18
  import Tooltip from '@ndla/tooltip';
21
19
  import { Notion as UINotion } from '../Notion';
22
- import { Figure, FigureCaption } from '../Figure';
23
- import { FigureLicenseDialogContent } from '../Figure/FigureLicenseDialogContent';
20
+ import { Figure } from '../Figure';
24
21
  import { NotionImage } from '../Notion/NotionImage';
25
22
  import { ConceptNotionV2, ConceptNotionData } from './conceptComponents';
23
+ import { EmbedByline } from '../LicenseByline';
24
+ import EmbedErrorPlaceholder from './EmbedErrorPlaceholder';
25
+ import { HeartButtonType } from './types';
26
26
 
27
27
  const BottomBorder = styled.div`
28
28
  margin-top: ${spacing.normal};
@@ -72,6 +72,7 @@ const ImageWrapper = styled.div`
72
72
  interface Props {
73
73
  embed: ConceptMetaData;
74
74
  fullWidth?: boolean;
75
+ heartButton?: HeartButtonType;
75
76
  }
76
77
 
77
78
  const StyledButton = styled.button`
@@ -93,9 +94,11 @@ const StyledButton = styled.button`
93
94
  }
94
95
  `;
95
96
 
96
- export const ConceptEmbed = ({ embed, fullWidth }: Props) => {
97
- if (embed.status === 'error') {
97
+ export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Props) => {
98
+ if (embed.status === 'error' && embed.embedData.type === 'inline') {
98
99
  return <span>{embed.embedData.linkText}</span>;
100
+ } else if (embed.status === 'error') {
101
+ return <EmbedErrorPlaceholder type="concept" />;
99
102
  }
100
103
 
101
104
  const {
@@ -112,6 +115,8 @@ export const ConceptEmbed = ({ embed, fullWidth }: Props) => {
112
115
  copyright={concept.copyright}
113
116
  source={concept.source}
114
117
  visualElement={visualElement}
118
+ heartButton={HeartButton}
119
+ conceptHeartButton={HeartButton && <HeartButton embed={embed} />}
115
120
  />
116
121
  );
117
122
  } else if (embed.embedData.type === 'inline') {
@@ -124,6 +129,8 @@ export const ConceptEmbed = ({ embed, fullWidth }: Props) => {
124
129
  source={concept.source}
125
130
  visualElement={visualElement}
126
131
  linkText={embed.embedData.linkText}
132
+ heartButton={HeartButton}
133
+ conceptHeartButton={HeartButton && <HeartButton embed={embed} />}
127
134
  />
128
135
  );
129
136
  } else {
@@ -135,6 +142,8 @@ export const ConceptEmbed = ({ embed, fullWidth }: Props) => {
135
142
  copyright={concept.copyright}
136
143
  source={concept.source}
137
144
  visualElement={visualElement}
145
+ heartButton={HeartButton}
146
+ conceptHeartButton={HeartButton && <HeartButton embed={embed} />}
138
147
  />
139
148
  );
140
149
  }
@@ -142,6 +151,8 @@ export const ConceptEmbed = ({ embed, fullWidth }: Props) => {
142
151
 
143
152
  interface InlineConceptProps extends ConceptNotionData {
144
153
  linkText: string;
154
+ heartButton?: HeartButtonType;
155
+ conceptHeartButton?: ReactNode;
145
156
  }
146
157
 
147
158
  const BaselineIcon = styled.span`
@@ -202,7 +213,16 @@ const getModalPosition = (anchor: HTMLElement) => {
202
213
  return anchorPos.top - (articlePos?.top || -window.scrollY);
203
214
  };
204
215
 
205
- const InlineConcept = ({ title, content, copyright, source, visualElement, linkText }: InlineConceptProps) => {
216
+ const InlineConcept = ({
217
+ title,
218
+ content,
219
+ copyright,
220
+ source,
221
+ visualElement,
222
+ linkText,
223
+ heartButton,
224
+ conceptHeartButton,
225
+ }: InlineConceptProps) => {
206
226
  const { t } = useTranslation();
207
227
  const anchorRef = useRef<HTMLDivElement>(null);
208
228
  const [modalPos, setModalPos] = useState(-9999);
@@ -240,6 +260,8 @@ const InlineConcept = ({ title, content, copyright, source, visualElement, linkT
240
260
  source={source}
241
261
  visualElement={visualElement}
242
262
  inPopover
263
+ heartButton={heartButton}
264
+ conceptHeartButton={conceptHeartButton}
243
265
  closeButton={
244
266
  <Close asChild>
245
267
  <IconButtonV2 aria-label={t('close')} variant="ghost">
@@ -257,6 +279,8 @@ const InlineConcept = ({ title, content, copyright, source, visualElement, linkT
257
279
 
258
280
  interface ConceptProps extends ConceptNotionData {
259
281
  fullWidth?: boolean;
282
+ heartButton?: HeartButtonType;
283
+ conceptHeartButton?: ReactElement;
260
284
  }
261
285
 
262
286
  export const BlockConcept = ({
@@ -267,24 +291,16 @@ export const BlockConcept = ({
267
291
  source,
268
292
  visualElement,
269
293
  fullWidth,
294
+ heartButton,
295
+ conceptHeartButton,
270
296
  }: ConceptProps) => {
271
- const { t, i18n } = useTranslation();
297
+ const { t } = useTranslation();
272
298
  const anchorRef = useRef<HTMLDivElement>(null);
273
299
  const [modalPos, setModalPos] = useState(-9999);
274
300
 
275
- const [isOpen, setIsOpen] = useState(false);
276
- const licenseCredits = getLicenseCredits(copyright);
277
- const { creators, rightsholders, processors } = licenseCredits;
278
- const authors = creators.length || rightsholders.length ? creators.concat(rightsholders) : processors;
279
301
  const visualElementType =
280
302
  visualElement?.embedData.resource === 'brightcove' ? 'video' : visualElement?.embedData.resource;
281
303
 
282
- const groupedAuthors = getGroupedContributorDescriptionList(licenseCredits, i18n.language).map((item) => ({
283
- name: item.description,
284
- type: item.label,
285
- }));
286
- const license = copyright?.license && getLicenseByAbbreviation(copyright?.license?.license, i18n.language);
287
-
288
304
  const onOpenChange = useCallback((open: boolean) => {
289
305
  if (open) {
290
306
  const anchor = anchorRef.current;
@@ -316,7 +332,7 @@ export const BlockConcept = ({
316
332
  <NotionImage
317
333
  type={visualElementType}
318
334
  id={''}
319
- src={visualElement.data.imageUrl}
335
+ src={visualElement.data.image.imageUrl}
320
336
  alt={visualElement.data.alttext.alttext}
321
337
  />
322
338
  ) : metaImage ? (
@@ -346,6 +362,8 @@ export const BlockConcept = ({
346
362
  copyright={copyright}
347
363
  source={source}
348
364
  visualElement={visualElement}
365
+ heartButton={heartButton}
366
+ conceptHeartButton={conceptHeartButton}
349
367
  inPopover
350
368
  closeButton={
351
369
  <Close asChild>
@@ -362,39 +380,10 @@ export const BlockConcept = ({
362
380
  )
363
381
  }
364
382
  />
365
- {copyright?.license && license ? (
366
- <FigureCaption
367
- figureId=""
368
- id=""
369
- authors={authors}
370
- licenseRights={license.rights}
371
- locale={i18n.language}
372
- hideIconsAndAuthors
373
- modalButton={
374
- <ButtonV2 variant="outline" size="small" shape="pill" onClick={() => setIsOpen(true)}>
375
- {t('concept.reuse')}
376
- </ButtonV2>
377
- }
378
- >
379
- <ModalV2
380
- controlled
381
- isOpen={isOpen}
382
- onClose={() => setIsOpen(false)}
383
- labelledBy="license-dialog-rules-heading"
384
- >
385
- {(close) => (
386
- <FigureLicenseDialogContent
387
- authors={groupedAuthors}
388
- locale={i18n.language}
389
- title={title}
390
- origin={copyright.origin}
391
- license={license}
392
- onClose={close}
393
- type="concept"
394
- />
395
- )}
396
- </ModalV2>
397
- </FigureCaption>
383
+ {copyright ? (
384
+ <EmbedByline copyright={copyright} bottomRounded topRounded type="concept">
385
+ {conceptHeartButton}
386
+ </EmbedByline>
398
387
  ) : (
399
388
  <BottomBorder />
400
389
  )}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Copyright (c) 2023-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import { ReactNode } from 'react';
10
+ import styled from '@emotion/styled';
11
+ import { WarningOutline } from '@ndla/icons/common';
12
+ import { colors, spacing } from '@ndla/core';
13
+ import { Figure, FigureType } from '../Figure';
14
+ import { EmbedByline } from '../LicenseByline';
15
+ import { EmbedBylineErrorProps } from '../LicenseByline/EmbedByline';
16
+
17
+ interface Props {
18
+ type: EmbedBylineErrorProps['type'];
19
+ figureType?: FigureType;
20
+ children?: ReactNode;
21
+ }
22
+
23
+ const ErrorPlaceholder = styled.div`
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ background-color: ${colors.brand.greyLighter};
28
+ height: 330px;
29
+
30
+ svg {
31
+ fill: ${colors.text.light};
32
+ height: 90%;
33
+ width: 90%;
34
+ }
35
+ &[data-embed-type='audio'] {
36
+ height: 150px;
37
+ }
38
+ `;
39
+
40
+ const StyledFigure = styled(Figure)`
41
+ display: flex;
42
+ flex-direction: column;
43
+ gap: ${spacing.xsmall};
44
+ `;
45
+
46
+ const EmbedErrorPlaceholder = ({ type, children, figureType }: Props) => {
47
+ return (
48
+ <StyledFigure type={figureType}>
49
+ {children ?? (
50
+ <ErrorPlaceholder data-embed-type={type}>
51
+ <WarningOutline />
52
+ </ErrorPlaceholder>
53
+ )}
54
+ <EmbedByline error type={type} topRounded bottomRounded />
55
+ </StyledFigure>
56
+ );
57
+ };
58
+
59
+ export default EmbedErrorPlaceholder;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Copyright (c) 2023-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import React from 'react';
10
+ import { Meta, StoryObj } from '@storybook/react';
11
+ import { H5pEmbedData, H5pData, OembedEmbedData, OembedData } from '@ndla/types-embed';
12
+ import ExternalEmbed from './ExternalEmbed';
13
+ import { defaultParameters } from '../../../../stories/defaults';
14
+
15
+ const embedData: OembedEmbedData = {
16
+ resource: 'external',
17
+ url: 'https://embed.ted.com/talks/zahra_biabani_the_eco_creators_helping_the_climate_through_social_media',
18
+ type: 'iframe',
19
+ };
20
+
21
+ const metaData: OembedData = {
22
+ oembed: {
23
+ type: 'video',
24
+ version: '1.0',
25
+ title: 'Zahra Biabani: The eco-creators helping the climate through social media',
26
+ description:
27
+ '"Climate doom-ism," or a pessimistic outlook on the future of the planet, rivals climate denialism in holding up the fight against climate change, says activist Zahra Biabani. Illuminating how hope combats inaction, she takes us inside the world of eco-friendly content on TikTok -- and shows that we all have what it takes to make real change.',
28
+ authorName: 'Zahra Biabani',
29
+ authorUrl: 'https://www.ted.com/speakers/zahra_biabani',
30
+ providerName: 'TED',
31
+ providerUrl: 'https://www.ted.com',
32
+ cacheAge: 300,
33
+ thumbnailUrl:
34
+ 'https://pi.tedcdn.com/r/talkstar-photos.s3.amazonaws.com/uploads/803ab5d5-2cff-4764-b5b6-545217159538/ZahraBiabani_2022T-embed.jpg?h=315&w=560',
35
+ thumbnailWidth: 560,
36
+ thumbnailHeight: 315,
37
+ width: 560,
38
+ height: 315,
39
+ html: '<iframe src="https://embed.ted.com/talks/zahra_biabani_the_eco_creators_helping_the_climate_through_social_media" width="560" height="315" frameborder="0" scrolling="no" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>',
40
+ },
41
+ };
42
+
43
+ const meta: Meta<typeof ExternalEmbed> = {
44
+ title: 'Enkle komponenter/Embeds/ExternalEmbed',
45
+ component: ExternalEmbed,
46
+ tags: ['autodocs'],
47
+ decorators: [
48
+ (Story) => (
49
+ <div className="o-wrapper">
50
+ <article className="c-article c-article--clean">
51
+ <section className="u-4/6@desktop u-push-1/6@desktop u-10/12@tablet u-push-1/12@tablet">
52
+ <section>
53
+ <Story />
54
+ </section>
55
+ </section>
56
+ </article>
57
+ </div>
58
+ ),
59
+ ],
60
+ parameters: defaultParameters,
61
+ };
62
+
63
+ export default meta;
64
+
65
+ export const Regular: StoryObj<typeof ExternalEmbed> = {
66
+ args: {
67
+ embed: {
68
+ resource: 'external',
69
+ status: 'success',
70
+ seq: 8,
71
+ embedData: embedData,
72
+ data: metaData,
73
+ },
74
+ },
75
+ };
76
+
77
+ export const Failed: StoryObj<typeof ExternalEmbed> = {
78
+ args: {
79
+ embed: {
80
+ resource: 'external',
81
+ status: 'error',
82
+ seq: 3,
83
+ embedData: embedData,
84
+ },
85
+ },
86
+ };
@@ -12,7 +12,7 @@ import { useEffect, useRef } from 'react';
12
12
  import { useTranslation } from 'react-i18next';
13
13
  import { Figure } from '../Figure';
14
14
  import { ResourceBox } from '../ResourceBox';
15
- import { errorSvgSrc } from './ImageEmbed';
15
+ import EmbedErrorPlaceholder from './EmbedErrorPlaceholder';
16
16
 
17
17
  interface Props {
18
18
  embed: OembedMetaData;
@@ -39,18 +39,13 @@ const ExternalEmbed = ({ embed, isConcept }: Props) => {
39
39
  }
40
40
  }, []);
41
41
  if (embed.status === 'error') {
42
- return (
43
- <figure className={isConcept ? '' : 'c-figure'}>
44
- <img alt={t('external.error')} src={errorSvgSrc} />
45
- <figcaption>{t('external.error')}</figcaption>
46
- </figure>
47
- );
42
+ return <EmbedErrorPlaceholder type="external" />;
48
43
  }
49
44
 
50
45
  const { embedData, data } = embed;
51
46
 
52
47
  if (embedData.type === 'fullscreen') {
53
- const image = { src: data.iframeImage?.imageUrl ?? '', alt: data.iframeImage?.alttext?.alttext ?? '' };
48
+ const image = { src: data.iframeImage?.image.imageUrl ?? '', alt: data.iframeImage?.alttext?.alttext ?? '' };
54
49
  return (
55
50
  <Figure type="full">
56
51
  <ResourceBox
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Copyright (c) 2023-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import React from 'react';
10
+ import { Meta, StoryObj } from '@storybook/react';
11
+ import { H5pEmbedData, H5pData } from '@ndla/types-embed';
12
+ import H5pEmbed from './H5pEmbed';
13
+ import { defaultParameters } from '../../../../stories/defaults';
14
+
15
+ const embedData: H5pEmbedData = {
16
+ resource: 'h5p',
17
+ path: '/resource/c56368d0-0432-4ec3-97bd-f4ba4badf55e',
18
+ title: 'Sorter avfall',
19
+ url: 'https://h5p-test.ndla.no/resource/c56368d0-0432-4ec3-97bd-f4ba4badf55e?locale=nb-no&cssUrl=https://test.ndla.no/static/h5p-custom-css.css',
20
+ };
21
+
22
+ const metaData: H5pData = {
23
+ h5pLicenseInformation: {
24
+ h5p: {
25
+ title: 'Sorter avfall',
26
+ source: null,
27
+ license: 'CC BY-SA',
28
+ licenseVersion: '4.0',
29
+ licenseExtras: null,
30
+ thumbnail: null,
31
+ authors: [
32
+ { name: 'Amendor AS', role: 'Author' },
33
+ { name: 'A B', role: 'Author' },
34
+ ],
35
+ },
36
+ },
37
+ h5pUrl:
38
+ 'https://h5p-test.ndla.no/resource/c56368d0-0432-4ec3-97bd-f4ba4badf55e?locale=nb-no&cssUrl=https://test.ndla.no/static/h5p-custom-css.css',
39
+ oembed: {
40
+ type: 'proxy',
41
+ version: '1.0',
42
+ title: 'Sorter avfall',
43
+ width: 800,
44
+ height: 600,
45
+ html: '<div><iframe width="800" height="600" allowfullscreen="allowfullscreen" src="https://h5p-test.ndla.no/resource/c56368d0-0432-4ec3-97bd-f4ba4badf55e?locale=nb-no&amp;cssUrl=https%3A%2F%2Ftest.ndla.no%2Fstatic%2Fh5p-custom-css.css" title="Sorter avfall"></iframe><script src="https://ca.h5p.ndla.no/h5p-php-library/js/h5p-resizer.js"></script></div>',
46
+ },
47
+ };
48
+
49
+ const meta: Meta<typeof H5pEmbed> = {
50
+ title: 'Enkle komponenter/Embeds/H5pEmbed',
51
+ component: H5pEmbed,
52
+ tags: ['autodocs'],
53
+ decorators: [
54
+ (Story) => (
55
+ <div className="o-wrapper">
56
+ <article className="c-article c-article--clean">
57
+ <section className="u-4/6@desktop u-push-1/6@desktop u-10/12@tablet u-push-1/12@tablet">
58
+ <section>
59
+ <Story />
60
+ </section>
61
+ </section>
62
+ </article>
63
+ </div>
64
+ ),
65
+ ],
66
+ parameters: defaultParameters,
67
+ };
68
+
69
+ export default meta;
70
+
71
+ export const Regular: StoryObj<typeof H5pEmbed> = {
72
+ args: {
73
+ embed: {
74
+ resource: 'h5p',
75
+ status: 'success',
76
+ seq: 5,
77
+ embedData: embedData,
78
+ data: metaData,
79
+ },
80
+ },
81
+ };
82
+
83
+ export const Failed: StoryObj<typeof H5pEmbed> = {
84
+ args: {
85
+ embed: {
86
+ resource: 'h5p',
87
+ status: 'error',
88
+ seq: 3,
89
+ embedData: embedData,
90
+ },
91
+ },
92
+ };
@@ -9,8 +9,7 @@
9
9
  import styled from '@emotion/styled';
10
10
  import { H5pMetaData } from '@ndla/types-embed';
11
11
  import React from 'react';
12
- import { useTranslation } from 'react-i18next';
13
- import { errorSvgSrc } from './ImageEmbed';
12
+ import EmbedErrorPlaceholder from './EmbedErrorPlaceholder';
14
13
 
15
14
  interface Props {
16
15
  embed: H5pMetaData;
@@ -24,21 +23,14 @@ const StyledFigure = styled.figure`
24
23
  `;
25
24
 
26
25
  const H5pEmbed = ({ embed, isConcept }: Props) => {
27
- const { t } = useTranslation();
28
-
29
26
  if (embed.status === 'error') {
30
- return (
31
- <figure className={isConcept ? '' : 'c-figure'}>
32
- <img alt={t('h5p.error')} src={errorSvgSrc} />
33
- <figcaption>{t('h5p.error')}</figcaption>
34
- </figure>
35
- );
27
+ return <EmbedErrorPlaceholder type="h5p" />;
36
28
  }
37
29
  const fullColumnClass = isConcept ? 'c-figure--full-column' : '';
38
30
  const classes = `c-figure ${fullColumnClass} c-figure--resize`;
39
31
 
40
32
  if (embed.data.oembed) {
41
- return <StyledFigure className={classes} dangerouslySetInnerHTML={{ __html: embed.data.oembed.html ?? '' }} />;
33
+ return <figure className={classes} dangerouslySetInnerHTML={{ __html: embed.data.oembed.html ?? '' }} />;
42
34
  }
43
35
 
44
36
  return (
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Copyright (c) 2023-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import React from 'react';
10
+ import { Meta, StoryObj } from '@storybook/react';
11
+ import { IframeData, IframeEmbedData } from '@ndla/types-embed';
12
+ import IframeEmbed from './IframeEmbed';
13
+ import { defaultParameters } from '../../../../stories/defaults';
14
+
15
+ const embedData: IframeEmbedData = {
16
+ width: '708px',
17
+ title: 'Tittel på iframen!',
18
+ height: '278px',
19
+ type: 'iframe',
20
+ resource: 'iframe',
21
+ url: 'https://embed.kahoot.it/2a51c481-d362-475b-862b-e4b47b96b3c9',
22
+ };
23
+
24
+ const meta: Meta<typeof IframeEmbed> = {
25
+ title: 'Enkle komponenter/Embeds/IframeEmbed',
26
+ component: IframeEmbed,
27
+ tags: ['autodocs'],
28
+ decorators: [
29
+ (Story) => (
30
+ <div className="o-wrapper">
31
+ <article className="c-article c-article--clean">
32
+ <section className="u-4/6@desktop u-push-1/6@desktop u-10/12@tablet u-push-1/12@tablet">
33
+ <section>
34
+ <Story />
35
+ </section>
36
+ </section>
37
+ </article>
38
+ </div>
39
+ ),
40
+ ],
41
+ parameters: defaultParameters,
42
+ };
43
+
44
+ export default meta;
45
+
46
+ export const Regular: StoryObj<typeof IframeEmbed> = {
47
+ args: {
48
+ embed: {
49
+ resource: 'iframe',
50
+ status: 'success',
51
+ seq: 3,
52
+ embedData: embedData,
53
+ data: {},
54
+ },
55
+ },
56
+ };
57
+
58
+ export const Failed: StoryObj<typeof IframeEmbed> = {
59
+ args: {
60
+ embed: {
61
+ resource: 'iframe',
62
+ status: 'error',
63
+ seq: 3,
64
+ embedData: embedData,
65
+ },
66
+ },
67
+ };
68
+
69
+ const opensInNewEmbedData: IframeEmbedData = {
70
+ title: 'Kahoot!',
71
+ caption: 'Sjekk ut denne!',
72
+ imageid: '65086',
73
+ type: 'fullscreen',
74
+ resource: 'iframe',
75
+ url: 'https://embed.kahoot.it/2a51c481-d362-475b-862b-e4b47b96b3c9',
76
+ };
77
+
78
+ const opensInnewMetaData: IframeData = {
79
+ iframeImage: {
80
+ id: '65086',
81
+ metaUrl: 'https://api.test.ndla.no/image-api/v2/images/65086',
82
+ title: { title: '\nSamtale ', language: 'nb' },
83
+ alttext: { alttext: ' To ungdommer sitter og snakker. Foto. ', language: 'nb' },
84
+ imageUrl: 'https://api.test.ndla.no/image-api/raw/IgOjO6og.jpg',
85
+ size: 176667,
86
+ contentType: 'image/jpeg',
87
+ copyright: {
88
+ license: { license: 'COPYRIGHTED', description: 'Copyrighted' },
89
+ origin: '',
90
+ creators: [],
91
+ processors: [],
92
+ rightsholders: [
93
+ {
94
+ type: 'rightsholder',
95
+ name: 'Folkehelseprosjektet Helsefremmende miljø på sosial medier, Bergen kommune 2019-2022',
96
+ },
97
+ ],
98
+ },
99
+ tags: { tags: ['samtale', 'Dialog', 'gutter'], language: 'nb' },
100
+ caption: { caption: 'Dette bildet skal bare brukes i casen "Livet på sosiale medier". ', language: 'nb' },
101
+ supportedLanguages: ['nb'],
102
+ created: '2022-12-02T14:24:19Z',
103
+ createdBy: 'oltQx44eGQp0DwkiR1NRo5qE',
104
+ modelRelease: 'yes',
105
+ imageDimensions: { width: 1920, height: 804 },
106
+ },
107
+ };
108
+
109
+ export const OpensInNewWindow: StoryObj<typeof IframeEmbed> = {
110
+ args: {
111
+ embed: {
112
+ resource: 'iframe',
113
+ status: 'success',
114
+ seq: 4,
115
+ embedData: opensInNewEmbedData,
116
+ data: opensInnewMetaData,
117
+ },
118
+ },
119
+ };
120
+
121
+ export const OpensInNewWindowFailed: StoryObj<typeof IframeEmbed> = {
122
+ args: {
123
+ embed: {
124
+ resource: 'iframe',
125
+ status: 'error',
126
+ seq: 4,
127
+ embedData: opensInNewEmbedData,
128
+ },
129
+ },
130
+ };
@@ -18,7 +18,7 @@ interface Props {
18
18
  isConcept?: boolean;
19
19
  }
20
20
 
21
- const ExternalEmbed = ({ embed, isConcept }: Props) => {
21
+ const IframeEmbed = ({ embed, isConcept }: Props) => {
22
22
  const { t } = useTranslation();
23
23
  const iframeRef = useRef<HTMLIFrameElement>(null);
24
24
 
@@ -36,7 +36,7 @@ const ExternalEmbed = ({ embed, isConcept }: Props) => {
36
36
 
37
37
  if (embedData.type === 'fullscreen') {
38
38
  const iframeImage = embed.status === 'success' ? embed.data.iframeImage : undefined;
39
- const image = { src: iframeImage?.imageUrl ?? '', alt: iframeImage?.alttext?.alttext ?? '' };
39
+ const image = { src: iframeImage?.image.imageUrl ?? '', alt: iframeImage?.alttext?.alttext ?? '' };
40
40
  return (
41
41
  <Figure type="full">
42
42
  <ResourceBox
@@ -83,4 +83,4 @@ const ExternalEmbed = ({ embed, isConcept }: Props) => {
83
83
  );
84
84
  };
85
85
 
86
- export default ExternalEmbed;
86
+ export default IframeEmbed;