@ndla/ui 36.0.1 → 37.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 (188) 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/BlogPost/BlogPost.js +4 -4
  7. package/es/CampaignBlock/CampaignBlock.js +77 -0
  8. package/es/CampaignBlock/index.js +9 -0
  9. package/es/ContactBlock/ContactBlock.js +63 -39
  10. package/es/Embed/AudioEmbed.js +44 -188
  11. package/es/Embed/BrightcoveEmbed.js +27 -123
  12. package/es/Embed/ConceptEmbed.js +53 -75
  13. package/es/Embed/EmbedErrorPlaceholder.js +41 -0
  14. package/es/Embed/ExternalEmbed.js +5 -12
  15. package/es/Embed/H5pEmbed.js +4 -14
  16. package/es/Embed/IframeEmbed.js +4 -4
  17. package/es/Embed/ImageEmbed.js +41 -153
  18. package/es/Embed/conceptComponents.js +62 -228
  19. package/es/Embed/types.js +1 -0
  20. package/es/KeyFigure/KeyFigure.js +57 -0
  21. package/es/{KeyPerformanceIndicator → KeyFigure}/index.js +1 -1
  22. package/es/LicenseByline/EmbedByline.js +115 -0
  23. package/es/LicenseByline/LicenseDescription.js +39 -0
  24. package/es/LicenseByline/LicenseLink.js +36 -0
  25. package/es/LicenseByline/index.js +1 -0
  26. package/es/List/OrderedList.js +48 -0
  27. package/es/List/UnOrderedList.js +36 -0
  28. package/es/List/index.js +10 -0
  29. package/es/Navigation/NavigationBox.js +41 -48
  30. package/es/Navigation/NavigationHeading.js +18 -29
  31. package/es/Notion/Notion.js +5 -5
  32. package/es/Resource/resourceComponents.js +12 -11
  33. package/es/Typography/Heading.js +38 -0
  34. package/es/Typography/index.js +9 -0
  35. package/es/all.css +1 -1
  36. package/es/index.js +4 -2
  37. package/es/locale/messages-en.js +13 -2
  38. package/es/locale/messages-nb.js +13 -2
  39. package/es/locale/messages-nn.js +13 -2
  40. package/es/locale/messages-se.js +13 -2
  41. package/es/locale/messages-sma.js +13 -2
  42. package/es/model/ContentType.js +7 -1
  43. package/lib/Article/Article.d.ts +1 -3
  44. package/lib/Article/Article.js +7 -13
  45. package/lib/Article/ArticleByline.d.ts +3 -5
  46. package/lib/Article/ArticleByline.js +83 -126
  47. package/lib/Article/ArticleFootNotes.js +16 -11
  48. package/lib/AudioPlayer/AudioPlayer.d.ts +1 -2
  49. package/lib/AudioPlayer/AudioPlayer.js +33 -36
  50. package/lib/AudioPlayer/initAudioPlayers.d.ts +1 -0
  51. package/lib/AudioPlayer/initAudioPlayers.js +9 -3
  52. package/lib/BlogPost/BlogPost.js +4 -4
  53. package/lib/CampaignBlock/CampaignBlock.d.ts +31 -0
  54. package/lib/CampaignBlock/CampaignBlock.js +82 -0
  55. package/lib/CampaignBlock/index.d.ts +8 -0
  56. package/lib/CampaignBlock/index.js +13 -0
  57. package/lib/ContactBlock/ContactBlock.js +63 -39
  58. package/lib/Embed/AudioEmbed.d.ts +3 -2
  59. package/lib/Embed/AudioEmbed.js +53 -192
  60. package/lib/Embed/BrightcoveEmbed.d.ts +3 -1
  61. package/lib/Embed/BrightcoveEmbed.js +27 -122
  62. package/lib/Embed/ConceptEmbed.d.ts +7 -2
  63. package/lib/Embed/ConceptEmbed.js +51 -73
  64. package/lib/Embed/EmbedErrorPlaceholder.d.ts +17 -0
  65. package/lib/Embed/EmbedErrorPlaceholder.js +48 -0
  66. package/lib/Embed/ExternalEmbed.js +5 -11
  67. package/lib/Embed/H5pEmbed.js +4 -13
  68. package/lib/Embed/IframeEmbed.d.ts +2 -2
  69. package/lib/Embed/IframeEmbed.js +4 -4
  70. package/lib/Embed/ImageEmbed.d.ts +3 -10
  71. package/lib/Embed/ImageEmbed.js +48 -161
  72. package/lib/Embed/conceptComponents.d.ts +4 -2
  73. package/lib/Embed/conceptComponents.js +67 -231
  74. package/lib/Embed/index.d.ts +1 -0
  75. package/lib/Embed/types.d.ts +14 -0
  76. package/lib/Embed/types.js +5 -0
  77. package/lib/KeyFigure/KeyFigure.d.ts +10 -0
  78. package/lib/KeyFigure/KeyFigure.js +62 -0
  79. package/lib/KeyFigure/index.d.ts +1 -0
  80. package/lib/KeyFigure/index.js +13 -0
  81. package/lib/LicenseByline/EmbedByline.d.ts +51 -0
  82. package/lib/LicenseByline/EmbedByline.js +120 -0
  83. package/lib/LicenseByline/LicenseDescription.d.ts +14 -0
  84. package/lib/LicenseByline/LicenseDescription.js +44 -0
  85. package/lib/LicenseByline/LicenseLink.d.ts +14 -0
  86. package/lib/LicenseByline/LicenseLink.js +44 -0
  87. package/lib/LicenseByline/index.d.ts +1 -0
  88. package/lib/LicenseByline/index.js +13 -0
  89. package/lib/List/OrderedList.d.ts +15 -0
  90. package/lib/List/OrderedList.js +56 -0
  91. package/lib/List/UnOrderedList.d.ts +10 -0
  92. package/lib/List/UnOrderedList.js +43 -0
  93. package/lib/List/index.d.ts +9 -0
  94. package/lib/List/index.js +20 -0
  95. package/lib/Navigation/NavigationBox.js +40 -47
  96. package/lib/Navigation/NavigationHeading.js +17 -28
  97. package/lib/Notion/Notion.js +5 -5
  98. package/lib/Resource/resourceComponents.js +12 -11
  99. package/lib/Typography/Heading.d.ts +26 -0
  100. package/lib/Typography/Heading.js +45 -0
  101. package/lib/Typography/index.d.ts +8 -0
  102. package/lib/Typography/index.js +13 -0
  103. package/lib/all.css +1 -1
  104. package/lib/index.d.ts +4 -1
  105. package/lib/index.js +23 -3
  106. package/lib/locale/messages-en.d.ts +11 -0
  107. package/lib/locale/messages-en.js +13 -2
  108. package/lib/locale/messages-nb.d.ts +11 -0
  109. package/lib/locale/messages-nb.js +13 -2
  110. package/lib/locale/messages-nn.d.ts +11 -0
  111. package/lib/locale/messages-nn.js +13 -2
  112. package/lib/locale/messages-se.d.ts +11 -0
  113. package/lib/locale/messages-se.js +13 -2
  114. package/lib/locale/messages-sma.d.ts +11 -0
  115. package/lib/locale/messages-sma.js +13 -2
  116. package/lib/model/ContentType.d.ts +1 -0
  117. package/lib/model/ContentType.js +9 -2
  118. package/package.json +15 -15
  119. package/src/Article/Article.tsx +1 -8
  120. package/src/Article/ArticleByline.tsx +78 -127
  121. package/src/Article/ArticleFootNotes.tsx +33 -10
  122. package/src/Article/component.article.scss +1 -52
  123. package/src/Article/component.footnotes.scss +2 -2
  124. package/src/Aside/component.aside.scss +3 -3
  125. package/src/AudioPlayer/AudioPlayer.tsx +11 -24
  126. package/src/AudioPlayer/initAudioPlayers.tsx +7 -2
  127. package/src/BlogPost/BlogPost.tsx +0 -4
  128. package/src/CampaignBlock/CampaignBlock.stories.tsx +63 -0
  129. package/src/CampaignBlock/CampaignBlock.tsx +99 -0
  130. package/src/CampaignBlock/index.ts +9 -0
  131. package/src/ContactBlock/ContactBlock.tsx +27 -19
  132. package/src/ContactBlock/Contactblock.stories.tsx +0 -1
  133. package/src/Dialog/component.dialog.scss +4 -5
  134. package/src/Embed/AudioEmbed.stories.tsx +5 -3
  135. package/src/Embed/AudioEmbed.tsx +45 -192
  136. package/src/Embed/BrightcoveEmbed.stories.tsx +5 -1
  137. package/src/Embed/BrightcoveEmbed.tsx +20 -95
  138. package/src/Embed/ConceptEmbed.stories.tsx +5 -0
  139. package/src/Embed/ConceptEmbed.tsx +43 -54
  140. package/src/Embed/EmbedErrorPlaceholder.tsx +59 -0
  141. package/src/Embed/ExternalEmbed.stories.tsx +86 -0
  142. package/src/Embed/ExternalEmbed.tsx +3 -8
  143. package/src/Embed/H5pEmbed.stories.tsx +92 -0
  144. package/src/Embed/H5pEmbed.tsx +2 -10
  145. package/src/Embed/IframeEmbed.stories.tsx +130 -0
  146. package/src/Embed/IframeEmbed.tsx +3 -3
  147. package/src/Embed/ImageEmbed.stories.tsx +3 -1
  148. package/src/Embed/ImageEmbed.tsx +21 -116
  149. package/src/Embed/conceptComponents.tsx +67 -257
  150. package/src/Embed/index.ts +1 -0
  151. package/src/Embed/types.ts +12 -0
  152. package/src/FactBox/component.factbox.scss +3 -3
  153. package/src/Figure/component.figure-license.scss +4 -4
  154. package/src/Figure/component.figure.scss +1 -1
  155. package/src/KeyFigure/KeyFigure.stories.tsx +36 -0
  156. package/src/{KeyPerformanceIndicator/KeyPerformanceIndicator.tsx → KeyFigure/KeyFigure.tsx} +9 -7
  157. package/src/{KeyPerformanceIndicator → KeyFigure}/index.ts +1 -1
  158. package/src/LicenseByline/EmbedByline.stories.tsx +83 -0
  159. package/src/LicenseByline/EmbedByline.tsx +165 -0
  160. package/src/LicenseByline/LicenseDescription.tsx +43 -0
  161. package/src/LicenseByline/LicenseLink.tsx +42 -0
  162. package/src/LicenseByline/index.tsx +1 -0
  163. package/src/List/OrderedList.tsx +115 -0
  164. package/src/List/UnOrderedList.tsx +49 -0
  165. package/src/List/index.ts +10 -0
  166. package/src/MediaList/component.medialist.scss +2 -2
  167. package/src/Navigation/NavigationBox.tsx +10 -14
  168. package/src/Navigation/NavigationHeading.tsx +15 -24
  169. package/src/Notion/Notion.tsx +1 -1
  170. package/src/RelatedArticleList/component.related-articles.scss +3 -13
  171. package/src/Resource/resourceComponents.tsx +4 -2
  172. package/src/Table/component.tables.scss +0 -46
  173. package/src/Translation/component.translation.scss +3 -5
  174. package/src/Typography/Heading.tsx +96 -0
  175. package/src/Typography/index.ts +9 -0
  176. package/src/index.ts +5 -1
  177. package/src/locale/messages-en.ts +11 -0
  178. package/src/locale/messages-nb.ts +11 -0
  179. package/src/locale/messages-nn.ts +11 -0
  180. package/src/locale/messages-se.ts +11 -0
  181. package/src/locale/messages-sma.ts +11 -0
  182. package/src/model/ContentType.ts +7 -0
  183. package/es/KeyPerformanceIndicator/KeyPerformanceIndicator.js +0 -57
  184. package/lib/KeyPerformanceIndicator/KeyPerformanceIndicator.d.ts +0 -8
  185. package/lib/KeyPerformanceIndicator/KeyPerformanceIndicator.js +0 -62
  186. package/lib/KeyPerformanceIndicator/index.d.ts +0 -1
  187. package/lib/KeyPerformanceIndicator/index.js +0 -13
  188. package/src/KeyPerformanceIndicator/KeyPerformanceIndicator.stories.tsx +0 -79
@@ -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,15 +23,8 @@ 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`;
@@ -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;
@@ -12,6 +12,7 @@ import { ImageEmbedData } from '@ndla/types-embed';
12
12
  import { IImageMetaInformationV2 } from '@ndla/types-backend/build/image-api';
13
13
  import ImageEmbed from './ImageEmbed';
14
14
  import { defaultParameters } from '../../../../stories/defaults';
15
+ import StoryFavoriteButton from '../../../../stories/StoryFavoriteButton';
15
16
 
16
17
  const embedData: ImageEmbedData = {
17
18
  resource: 'image',
@@ -59,7 +60,6 @@ const meta: Meta<typeof ImageEmbed> = {
59
60
  component: ImageEmbed,
60
61
  tags: ['autodocs'],
61
62
  args: {
62
- articlePath: undefined,
63
63
  previewAlt: true,
64
64
  },
65
65
  decorators: [
@@ -82,6 +82,7 @@ export default meta;
82
82
 
83
83
  export const ImageEmbedStory: StoryObj<typeof ImageEmbed> = {
84
84
  args: {
85
+ heartButton: StoryFavoriteButton,
85
86
  embed: {
86
87
  resource: 'image',
87
88
  status: 'success',
@@ -94,6 +95,7 @@ export const ImageEmbedStory: StoryObj<typeof ImageEmbed> = {
94
95
 
95
96
  export const Failed: StoryObj<typeof ImageEmbed> = {
96
97
  args: {
98
+ heartButton: StoryFavoriteButton,
97
99
  embed: {
98
100
  resource: 'image',
99
101
  status: 'error',
@@ -7,25 +7,21 @@
7
7
  */
8
8
 
9
9
  import isNumber from 'lodash/isNumber';
10
- import styled from '@emotion/styled';
11
- import { figureApa7CopyString, getGroupedContributorDescriptionList, getLicenseByAbbreviation } from '@ndla/licenses';
12
10
  import { ImageEmbedData, ImageMetaData } from '@ndla/types-embed';
13
11
  import { useTranslation } from 'react-i18next';
14
- import { ModalV2 } from '@ndla/modal';
15
- import { SafeLinkButton } from '@ndla/safelink';
16
12
  import { MouseEventHandler, useState } from 'react';
17
- import { ButtonV2, CopyButton } from '@ndla/button';
18
13
  import { ExpandTwoArrows } from '@ndla/icons/action';
19
14
  import { ArrowCollapse, ChevronDown, ChevronUp } from '@ndla/icons/common';
20
- import { Figure, FigureCaption, FigureType } from '../Figure';
15
+ import { Figure, FigureType } from '../Figure';
21
16
  import Image, { ImageLink } from '../Image';
22
- import { FigureLicenseDialogContent } from '../Figure/FigureLicenseDialogContent';
23
- import { Copyright } from '../types';
17
+ import { EmbedByline } from '../LicenseByline';
18
+ import EmbedErrorPlaceholder from './EmbedErrorPlaceholder';
19
+ import { HeartButtonType } from './types';
24
20
 
25
21
  interface Props {
26
22
  embed: ImageMetaData;
27
- articlePath?: string;
28
23
  previewAlt?: boolean;
24
+ heartButton?: HeartButtonType;
29
25
  }
30
26
 
31
27
  export interface Author {
@@ -100,38 +96,20 @@ const getCrop = (data: ImageEmbedData) => {
100
96
  return undefined;
101
97
  };
102
98
 
103
- const StyledSpan = styled.span`
104
- font-style: italic;
105
- color: grey;
106
- `;
107
-
108
99
  const expandedSizes = '(min-width: 1024px) 1024px, 100vw';
109
100
 
110
- const ImageEmbed = ({ embed, articlePath, previewAlt }: Props) => {
111
- const [isOpen, setIsOpen] = useState(false);
101
+ const ImageEmbed = ({ embed, previewAlt, heartButton: HeartButton }: Props) => {
112
102
  const [isBylineHidden, setIsBylineHidden] = useState(hideByline(embed.embedData.size));
113
103
  const [imageSizes, setImageSizes] = useState<string | undefined>(undefined);
114
- const { t, i18n } = useTranslation();
115
104
  if (embed.status === 'error') {
116
105
  const { align, size } = embed.embedData;
117
106
  const figureType = getFigureType(size, align);
118
- return (
119
- <Figure type={figureType}>
120
- <div className="c-figure__img">
121
- <img alt={t('image.error.url')} src={errorSvgSrc} />
122
- </div>
123
- <figcaption>{t('image.error.caption')}</figcaption>
124
- </Figure>
125
- );
107
+ return <EmbedErrorPlaceholder type={'image'} figureType={figureType} />;
126
108
  }
127
109
 
128
110
  const { data, embedData, seq } = embed;
129
111
 
130
- const authors = getLicenseCredits(data.copyright);
131
-
132
112
  const altText = embedData.alt || '';
133
- const caption = embedData.caption || '';
134
- const license = getLicenseByAbbreviation(data.copyright.license.license, i18n.language);
135
113
 
136
114
  const figureType = getFigureType(embedData.size, embedData.align);
137
115
  const sizes = getSizes(embedData.size, embedData.align);
@@ -139,29 +117,22 @@ const ImageEmbed = ({ embed, articlePath, previewAlt }: Props) => {
139
117
  const focalPoint = getFocalPoint(embedData);
140
118
  const crop = getCrop(embedData);
141
119
 
142
- const contributors = getGroupedContributorDescriptionList(data.copyright, i18n.language).map((item) => ({
143
- name: item.description,
144
- type: item.label,
145
- }));
146
-
147
120
  const figureId = `figure-${seq}-${data.id}`;
148
121
 
149
- const { creators, rightsholders, processors } = authors;
150
- const captionAuthors = creators.length || rightsholders.length ? [...creators, ...rightsholders] : processors;
151
122
  return (
152
123
  <Figure
153
124
  id={figureId}
154
125
  type={imageSizes ? undefined : figureType}
155
126
  className={imageSizes ? 'c-figure--right expanded' : ''}
156
127
  >
157
- <ImageWrapper src={data.imageUrl} crop={crop} size={embedData.size}>
128
+ <ImageWrapper src={data.image.imageUrl} crop={crop} size={embedData.size}>
158
129
  <Image
159
130
  focalPoint={focalPoint}
160
- contentType={data.contentType}
131
+ contentType={data.image.contentType}
161
132
  crop={crop}
162
133
  sizes={imageSizes ?? sizes}
163
134
  alt={altText}
164
- src={data.imageUrl}
135
+ src={data.image.imageUrl}
165
136
  expandButton={
166
137
  <ExpandButton
167
138
  size={embedData.size}
@@ -173,43 +144,17 @@ const ImageEmbed = ({ embed, articlePath, previewAlt }: Props) => {
173
144
  }
174
145
  />
175
146
  </ImageWrapper>
176
- {previewAlt ? <StyledSpan>{`Alt: ${embedData.alt}`}</StyledSpan> : null}
177
- <FigureCaption
178
- hideFigcaption={isSmall(embedData.size) || isBylineHidden}
179
- figureId={figureId}
180
- id={figureId}
181
- caption={caption}
182
- reuseLabel={t('image.reuse')}
183
- modalButton={
184
- <ButtonV2 shape="pill" variant="outline" size="small" onClick={() => setIsOpen(true)}>
185
- {t('image.reuse')}
186
- </ButtonV2>
187
- }
188
- licenseRights={license.rights}
189
- authors={captionAuthors}
190
- locale={i18n.language}
191
- >
192
- <ModalV2 controlled isOpen={isOpen} onClose={() => setIsOpen(false)} labelledBy="license-dialog-rules-heading">
193
- {(close) => (
194
- <FigureLicenseDialogContent
195
- title={data.title.title}
196
- license={license}
197
- onClose={close}
198
- authors={contributors}
199
- origin={data.copyright.origin}
200
- locale={i18n.language}
201
- type="image"
202
- >
203
- <ImageLicenseButtons
204
- articlePath={articlePath}
205
- title={data.title.title}
206
- imageUrl={data.imageUrl}
207
- copyright={data.copyright}
208
- />
209
- </FigureLicenseDialogContent>
210
- )}
211
- </ModalV2>
212
- </FigureCaption>
147
+ {isBylineHidden || (isSmall(embedData.size) && !imageSizes) ? null : (
148
+ <EmbedByline
149
+ type="image"
150
+ copyright={data.copyright}
151
+ description={embedData.caption ?? data.caption.caption}
152
+ bottomRounded
153
+ visibleAlt={previewAlt ? embed.embedData.alt : ''}
154
+ >
155
+ {HeartButton && <HeartButton embed={embed} />}
156
+ </EmbedByline>
157
+ )}
213
158
  </Figure>
214
159
  );
215
160
  };
@@ -229,46 +174,6 @@ const hideByline = (size?: string): boolean => {
229
174
  return !!size && size.endsWith('-hide-byline');
230
175
  };
231
176
 
232
- interface ImageLicenseButtonsProps {
233
- imageUrl: string;
234
- title?: string;
235
- articlePath?: string;
236
- copyright?: Partial<Copyright>;
237
- }
238
-
239
- export const ImageLicenseButtons = ({ imageUrl, title, articlePath, copyright }: ImageLicenseButtonsProps) => {
240
- const { t, i18n } = useTranslation();
241
- if (!copyright?.license?.license || copyright?.license?.license === 'COPYRIGHTED') return null;
242
-
243
- const copyString = figureApa7CopyString(
244
- title,
245
- undefined,
246
- imageUrl,
247
- articlePath,
248
- copyright,
249
- copyright?.license?.license,
250
- '',
251
- t,
252
- i18n.language,
253
- );
254
-
255
- return (
256
- <>
257
- <CopyButton
258
- variant="outline"
259
- onClick={() => navigator.clipboard.writeText(copyString)}
260
- copyNode={t('license.hasCopiedTitle')}
261
- aria-live="assertive"
262
- >
263
- {t('license.copyTitle')}
264
- </CopyButton>
265
- <SafeLinkButton to={`${imageUrl}?download=true`} download variant="outline">
266
- {t('image.download')}
267
- </SafeLinkButton>
268
- </>
269
- );
270
- };
271
-
272
177
  const ImageWrapper = ({ src, crop, size, children }: ImageWrapperProps) => {
273
178
  const { t } = useTranslation();
274
179
  if (isSmall(size) || hideByline(size)) {