@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
@@ -6,36 +6,36 @@
6
6
  *
7
7
  */
8
8
 
9
- import React, { ReactNode } from 'react';
9
+ import React, { ReactNode, useCallback, useEffect, useState } from 'react';
10
10
  import styled from '@emotion/styled';
11
- import Modal, { ModalHeader, ModalBody, ModalCloseButton } from '@ndla/modal';
12
- import { CopyButton, ButtonV2 } from '@ndla/button';
13
- import { colors, fonts, spacing } from '@ndla/core';
14
- import { copyTextToClipboard, printPage } from '@ndla/util';
11
+ import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
15
12
  import { useTranslation } from 'react-i18next';
16
13
  import { getLicenseByAbbreviation } from '@ndla/licenses';
17
- import { LicenseByline } from '@ndla/notion';
14
+ import { AccordionRoot, AccordionHeader, AccordionContent, AccordionItem } from '@ndla/accordion';
18
15
  import { TFunction } from 'i18next';
16
+ import { FootNote } from '../types';
17
+ import ArticleFootNotes from './ArticleFootNotes';
18
+ import LicenseLink from '../LicenseByline/LicenseLink';
19
19
 
20
20
  const Wrapper = styled.div`
21
21
  margin-top: ${spacing.normal};
22
22
  padding-top: ${spacing.normal};
23
23
  padding-bottom: ${spacing.xsmall};
24
24
  border-top: 1px solid ${colors.brand.greyLight};
25
- ${fonts.sizes('14px', '18px')};
25
+ ${fonts.sizes('16px', '24px')};
26
26
  font-family: ${fonts.sans};
27
27
  color: ${colors.brand.greyDark};
28
28
  `;
29
29
 
30
30
  const TextWrapper = styled.div`
31
- margin-top: 10px;
32
- `;
33
-
34
- const ButtonWrapper = styled.div`
35
- margin-top: 18px;
36
- button {
37
- margin-bottom: 10px;
38
- margin-right: 16px;
31
+ display: flex;
32
+ justify-content: space-between;
33
+ width: 100%;
34
+ padding-bottom: ${spacing.mediumlarge};
35
+ ${mq.range({ until: breakpoints.tabletWide })} {
36
+ flex-direction: column;
37
+ flex-direction: column-reverse;
38
+ gap: ${spacing.xsmall};
39
39
  }
40
40
  `;
41
41
 
@@ -57,11 +57,8 @@ type Props = {
57
57
  published: string;
58
58
  license: string;
59
59
  licenseBox?: ReactNode;
60
- copyPageUrlLink?: string;
61
- printUrl?: string;
62
60
  locale?: string;
63
- copyEmbedLink?: string;
64
- copySourceReference?: string;
61
+ footnotes?: FootNote[];
65
62
  };
66
63
 
67
64
  const renderContributors = (contributors: SupplierProps[] | AuthorProps[], t: TFunction) => {
@@ -85,133 +82,87 @@ const getSuppliersText = (suppliers: SupplierProps[], t: TFunction) => {
85
82
  : t('article.supplierLabel', { name: renderContributors(suppliers, t), interpolation: { escapeValue: false } });
86
83
  };
87
84
 
85
+ const LicenseWrapper = styled.div`
86
+ display: flex;
87
+ gap: ${spacing.small};
88
+ `;
89
+
90
+ const refRegexp = /note\d/;
91
+ const referencesAccordionId = 'references';
92
+
88
93
  const ArticleByline = ({
89
94
  authors = [],
90
95
  suppliers = [],
91
- license,
96
+ footnotes,
97
+ license: licenseString,
92
98
  licenseBox,
93
99
  published,
94
- copyPageUrlLink,
95
- printUrl,
96
100
  locale,
97
- copyEmbedLink,
98
- copySourceReference,
99
101
  }: Props) => {
100
102
  const { t } = useTranslation();
103
+ const [openAccordions, setOpenAccordions] = useState<string[]>([]);
104
+
105
+ const onHashChange = useCallback(
106
+ (e: HashChangeEvent) => {
107
+ const hash = e.newURL.split('#')[1];
108
+ if (hash.match(refRegexp) && !openAccordions.includes(referencesAccordionId)) {
109
+ setOpenAccordions([...openAccordions, referencesAccordionId]);
110
+ const el = document.getElementById(`#${hash}`);
111
+ el?.click();
112
+ el?.focus();
113
+ }
114
+ },
115
+ [openAccordions],
116
+ );
101
117
 
102
- const copyLinkHandler = () => {
103
- if (copyPageUrlLink) {
104
- copyTextToClipboard(copyPageUrlLink);
105
- }
106
- };
107
- const licenseRights = getLicenseByAbbreviation(license, locale).rights;
108
-
109
- const copyLicense = () => {
110
- if (copySourceReference) {
111
- copyTextToClipboard(copySourceReference);
112
- }
113
- };
114
- const copyEmbededLink = () => {
115
- if (copyEmbedLink) {
116
- copyTextToClipboard(copyEmbedLink);
117
- }
118
- };
118
+ useEffect(() => {
119
+ window.addEventListener('hashchange', onHashChange);
120
+ return () => window.removeEventListener('hashchange', onHashChange);
121
+ }, [onHashChange]);
122
+
123
+ const license = getLicenseByAbbreviation(licenseString, locale);
119
124
 
120
125
  const showPrimaryContributors = suppliers.length > 0 || authors.length > 0;
121
126
  const showSecondaryContributors = suppliers.length > 0 && authors.length > 0;
122
127
 
123
- const buttonId = 'popupUseContent';
124
-
125
128
  return (
126
129
  <Wrapper>
127
- <div>
128
- {t('article.lastUpdated')} {published}
129
- </div>
130
- {(showPrimaryContributors || licenseRights.length > 0) && (
131
- <TextWrapper>
132
- <LicenseByline licenseRights={licenseRights}>
133
- {showPrimaryContributors && (
134
- <PrimaryContributorsWrapper>
135
- {authors.length > 0
136
- ? t('article.authorsLabel', {
137
- names: renderContributors(authors, t),
138
- interpolation: { escapeValue: false },
139
- })
140
- : getSuppliersText(suppliers, t)}
141
- </PrimaryContributorsWrapper>
142
- )}
143
- </LicenseByline>
144
- </TextWrapper>
145
- )}
130
+ <TextWrapper>
131
+ <LicenseWrapper>
132
+ <LicenseLink license={license} />
133
+ {showPrimaryContributors && (
134
+ <PrimaryContributorsWrapper>
135
+ {authors.length > 0
136
+ ? t('article.authorsLabel', {
137
+ names: renderContributors(authors, t),
138
+ interpolation: { escapeValue: false },
139
+ })
140
+ : getSuppliersText(suppliers, t)}
141
+ </PrimaryContributorsWrapper>
142
+ )}
143
+ </LicenseWrapper>
144
+ <div>
145
+ {t('article.lastUpdated')} {published}
146
+ </div>
147
+ </TextWrapper>
146
148
  {showSecondaryContributors && <TextWrapper>{getSuppliersText(suppliers, t)}</TextWrapper>}
147
- <ButtonWrapper>
149
+ <AccordionRoot type="multiple" onValueChange={setOpenAccordions} value={openAccordions}>
148
150
  {licenseBox && (
149
- <Modal
150
- labelledBy={buttonId}
151
- activateButton={
152
- <ButtonV2 id={buttonId} size="small" shape="pill" variant="outline">
153
- {t('article.useContent')}
154
- </ButtonV2>
155
- }
156
- backgroundColor="white"
157
- position="top"
158
- size="medium"
159
- >
160
- {(onClose: () => void) => (
161
- <>
162
- <ModalHeader modifier="no-bottom-padding">
163
- <ModalCloseButton onClick={onClose} title="Lukk" />
164
- </ModalHeader>
165
- <ModalBody>{licenseBox}</ModalBody>
166
- </>
167
- )}
168
- </Modal>
169
- )}
170
- {copySourceReference && (
171
- <CopyButton
172
- size="small"
173
- shape="pill"
174
- variant="outline"
175
- aria-live="assertive"
176
- copyNode={t('license.hasCopiedTitle')}
177
- data-copy-string={copySourceReference}
178
- onClick={copyLicense}
179
- >
180
- {`${t('license.copy')} ${t('license.copyTitle').toLowerCase()}`}
181
- </CopyButton>
151
+ <AccordionItem value="rulesForUse">
152
+ <AccordionHeader>{t('article.useContent')}</AccordionHeader>
153
+ <AccordionContent>{licenseBox}</AccordionContent>
154
+ </AccordionItem>
182
155
  )}
183
- {copyPageUrlLink && (
184
- <CopyButton
185
- onClick={copyLinkHandler}
186
- size="small"
187
- shape="pill"
188
- variant="outline"
189
- aria-live="assertive"
190
- data-copy-string={copyPageUrlLink}
191
- copyNode={t('article.copyPageLinkCopied')}
192
- >
193
- {t('article.copyPageLink')}
194
- </CopyButton>
195
- )}
196
- {copyEmbedLink && (
197
- <CopyButton
198
- size="small"
199
- shape="pill"
200
- variant="outline"
201
- aria-live="assertive"
202
- copyNode={t('license.hasCopiedTitle')}
203
- data-copy-string={copyEmbedLink}
204
- onClick={copyEmbededLink}
205
- >
206
- {`${t('license.copy')} ${t('license.tabs.embedlink').toLowerCase()}`}
207
- </CopyButton>
208
- )}
209
- {printUrl && (
210
- <ButtonV2 size="small" shape="pill" variant="outline" onClick={() => printPage(printUrl)}>
211
- {t('article.printPage')}
212
- </ButtonV2>
156
+
157
+ {!!footnotes?.length && (
158
+ <AccordionItem value={referencesAccordionId}>
159
+ <AccordionHeader>Referanser</AccordionHeader>
160
+ <AccordionContent forceMount>
161
+ <ArticleFootNotes footNotes={footnotes} />
162
+ </AccordionContent>
163
+ </AccordionItem>
213
164
  )}
214
- </ButtonWrapper>
165
+ </AccordionRoot>
215
166
  </Wrapper>
216
167
  );
217
168
  };
@@ -7,6 +7,8 @@
7
7
  */
8
8
 
9
9
  import React from 'react';
10
+ import styled from '@emotion/styled';
11
+ import { colors, spacing } from '@ndla/core';
10
12
  import { FootNote as FootNoteType } from '../types';
11
13
 
12
14
  const citeDetailString = (description: string | undefined) => (description ? `${description}. ` : '');
@@ -15,14 +17,26 @@ type FootNoteProps = {
15
17
  footNote: FootNoteType;
16
18
  };
17
19
 
20
+ const Cite = styled.cite`
21
+ display: flex;
22
+ gap: ${spacing.small};
23
+ a {
24
+ box-shadow: none;
25
+ text-decoration: underline;
26
+ text-underline-offset: ${spacing.xxsmall};
27
+ &:hover,
28
+ &:focus-visible {
29
+ text-decoration: none;
30
+ }
31
+ }
32
+ `;
33
+
18
34
  const FootNote = ({ footNote }: FootNoteProps) => (
19
- <li className="c-footnotes__item">
20
- <cite className="c-footnotes__cite" id={`note${footNote.ref}`}>
21
- <sup>
22
- <a href={`#ref${footNote.ref}`} target="_self">
23
- {footNote.ref}
24
- </a>
25
- </sup>
35
+ <li>
36
+ <Cite id={`note${footNote.ref}`}>
37
+ <a href={`#ref${footNote.ref}`} target="_self">
38
+ {footNote.ref}
39
+ </a>
26
40
  {`«${footNote.title}». ${footNote.authors.join(' ')}. ${citeDetailString(footNote.edition)}${citeDetailString(
27
41
  footNote.publisher,
28
42
  )}${footNote.year}. `}
@@ -32,7 +46,7 @@ const FootNote = ({ footNote }: FootNoteProps) => (
32
46
  {'.'}
33
47
  </a>
34
48
  ) : null}
35
- </cite>
49
+ </Cite>
36
50
  </li>
37
51
  );
38
52
 
@@ -40,12 +54,21 @@ type ArticleFootNotesProps = {
40
54
  footNotes: Array<FootNoteType>;
41
55
  };
42
56
 
57
+ const FootnoteList = styled.ol`
58
+ margin: 0;
59
+ display: flex;
60
+ flex-direction: column;
61
+ list-style: none;
62
+ padding: ${spacing.small};
63
+ color: ${colors.text.light};
64
+ `;
65
+
43
66
  const ArticleFootNotes = ({ footNotes }: ArticleFootNotesProps) => (
44
- <ol className="c-footnotes">
67
+ <FootnoteList>
45
68
  {footNotes.map((footNote) => (
46
69
  <FootNote key={footNote.ref} footNote={footNote} />
47
70
  ))}
48
- </ol>
71
+ </FootnoteList>
49
72
  );
50
73
 
51
74
  export default ArticleFootNotes;
@@ -4,15 +4,6 @@
4
4
  ** Title has icon when article is a resource type
5
5
  **/
6
6
 
7
- @mixin contentList() {
8
- ul:not([class]),
9
- ul.o-list--two-columns,
10
- ul.o-list--bullets,
11
- ol {
12
- @content;
13
- }
14
- }
15
-
16
7
  .c-article {
17
8
  font-family: $font-serif;
18
9
  background: $white;
@@ -33,58 +24,16 @@
33
24
  margin-bottom: 29px;
34
25
  }
35
26
  }
36
- p {
37
- @include chinese() {
38
- @include font-size(20px, 35px);
39
- }
40
- }
41
-
42
- @include contentList() {
43
- @include chinese() {
44
- @include font-size(20px, 35px);
45
- }
46
- }
47
-
48
- @include chinese() {
49
- @include contentList() {
50
- @include font-size(22px, 35px);
51
- }
52
-
53
- p {
54
- @include font-size(22px, 35px);
55
- }
56
- }
57
27
 
58
28
  @include mq(tablet) {
59
29
  @include font-size(20px, 35px);
60
30
 
61
- p {
62
- @include chinese() {
63
- @include font-size(22px, 35px);
64
- }
65
- }
66
-
67
31
  > section > p {
68
32
  &:not([class]) {
69
33
  margin-bottom: 35px;
70
34
  }
71
35
  }
72
36
 
73
- @include contentList() {
74
- @include chinese() {
75
- @include font-size(22px, 35px);
76
- }
77
- }
78
-
79
- @include chinese() {
80
- @include contentList() {
81
- @include font-size(22px, 35px);
82
- }
83
- p {
84
- @include font-size(22px, 35px);
85
- }
86
- }
87
-
88
37
  padding: 0 $spacing $spacing;
89
38
  margin-bottom: $spacing--large;
90
39
  margin-top: -$spacing * 6;
@@ -178,7 +127,7 @@
178
127
  }
179
128
 
180
129
  p {
181
- @include inuit-font-size(16px, 20px);
130
+ @include font-size(16px, 20px);
182
131
  color: $text-light-color;
183
132
  text-transform: uppercase;
184
133
  margin-bottom: 0;
@@ -17,7 +17,7 @@ $highlight-color: $brand-grey--lighter;
17
17
  display: block;
18
18
  border-top: 2px solid $brand-grey--lighter;
19
19
  color: gray;
20
- @include inuit-font-size(15px);
20
+ @include font-size(15px, 1.6);
21
21
  }
22
22
  .c-footnotes__item {
23
23
  margin-bottom: $spacing--small;
@@ -37,7 +37,7 @@ $highlight-color: $brand-grey--lighter;
37
37
  padding: 10px 15px;
38
38
  box-shadow: none;
39
39
  text-decoration: underline;
40
- @include inuit-font-size(15px);
40
+ @include font-size(15px, 1.6);
41
41
 
42
42
  &:hover,
43
43
  &:active,
@@ -6,7 +6,7 @@
6
6
  .c-aside {
7
7
  position: relative;
8
8
  margin: $spacing--large 0;
9
- @include inuit-font-size(16px);
9
+ @include font-size(16px, 1.5);
10
10
  z-index: 1;
11
11
 
12
12
  @include mq(tablet) {
@@ -59,7 +59,7 @@
59
59
  .c-aside h1 {
60
60
  margin-top: 0;
61
61
  margin-bottom: $spacing;
62
- @include inuit-font-size(22px, 34px);
62
+ @include font-size(22px, 34px);
63
63
  font-weight: $font-weight-bold;
64
64
  position: relative;
65
65
  z-index: 2;
@@ -70,7 +70,7 @@
70
70
  .c-aside h4,
71
71
  .c-aside h5 {
72
72
  display: block;
73
- @include inuit-font-size(16px);
73
+ @include font-size(16px, 1.5);
74
74
  letter-spacing: 0.1em;
75
75
  margin-top: $spacing;
76
76
  margin-bottom: $spacing--small;
@@ -6,14 +6,13 @@
6
6
  *
7
7
  */
8
8
 
9
- import React, { ReactNode, useEffect, useRef, useState } from 'react';
9
+ import React, { ReactNode, useMemo, useState } from 'react';
10
10
  import styled from '@emotion/styled';
11
11
  import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
12
12
  import { ButtonV2 } from '@ndla/button';
13
13
  import { Cross as CrossIcon } from '@ndla/icons/action';
14
14
  import { useTranslation } from 'react-i18next';
15
15
  import SafeLink from '@ndla/safelink';
16
- import shave from 'shave';
17
16
  import Controls from './Controls';
18
17
  import SpeechControl from './SpeechControl';
19
18
 
@@ -163,11 +162,7 @@ const TextVersionText = styled.div`
163
162
  max-width: 670px;
164
163
  `;
165
164
 
166
- export const truncateDescription = (el: HTMLElement, readMoreLabel: string | null) => {
167
- shave(el, 90, {
168
- character: `... <a href="#" onclick="(function(e){e.preventDefault(); const parentNode = e.target.parentNode; parentNode.nextSibling.style.display = 'inline'; parentNode.remove();return false;})(arguments[0]);return false;">${readMoreLabel}</a>`,
169
- });
170
- };
165
+ const DESCRIPTION_MAX_LENGTH = 200;
171
166
 
172
167
  type Props = {
173
168
  src: string;
@@ -177,7 +172,7 @@ type Props = {
177
172
  url?: string;
178
173
  };
179
174
  speech?: boolean;
180
- description?: ReactNode;
175
+ description?: string;
181
176
  textVersion?: ReactNode;
182
177
  img?: {
183
178
  url: string;
@@ -189,15 +184,8 @@ type Props = {
189
184
  const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersion, staticRenderId }: Props) => {
190
185
  const { t } = useTranslation();
191
186
  const [showTextVersion, setShowTextVersion] = useState(false);
192
-
193
- const descriptionRef = useRef<HTMLDivElement>(null);
194
- const readMoreDescriptionLabel = t('audio.readMoreDescriptionLabel');
195
-
196
- useEffect(() => {
197
- if (descriptionRef?.current) {
198
- truncateDescription(descriptionRef.current, readMoreDescriptionLabel);
199
- }
200
- }, [readMoreDescriptionLabel]);
187
+ const [showFullDescription, setShowFullDescription] = useState(false);
188
+ const truncatedDescription = useMemo(() => description?.slice(0, DESCRIPTION_MAX_LENGTH), [description]);
201
189
 
202
190
  if (speech) {
203
191
  return (
@@ -244,13 +232,12 @@ const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersi
244
232
  </TitleWrapper>
245
233
  {description && (
246
234
  <StyledDescription>
247
- <div
248
- ref={descriptionRef}
249
- data-audio-player-description={1}
250
- data-read-more-text={t('audio.readMoreDescriptionLabel')}
251
- >
252
- {description}
253
- </div>
235
+ {showFullDescription || description.length < DESCRIPTION_MAX_LENGTH
236
+ ? description
237
+ : `${truncatedDescription}...`}
238
+ <ButtonV2 variant="link" onClick={() => setShowFullDescription((p) => !p)}>
239
+ {t(`audio.${showFullDescription ? 'readLessDescriptionLabel' : 'readMoreDescriptionLabel'}`)}
240
+ </ButtonV2>
254
241
  </StyledDescription>
255
242
  )}
256
243
  {textVersion && img && <TextVersionComponent />}
@@ -7,11 +7,16 @@
7
7
  */
8
8
  import React from 'react';
9
9
  import ReactDOM from 'react-dom';
10
-
10
+ import shave from 'shave';
11
11
  import Controls from './Controls';
12
12
  import SpeechControl from './SpeechControl';
13
13
  import { Locale } from '../types';
14
- import { truncateDescription } from './AudioPlayer';
14
+
15
+ export const truncateDescription = (el: HTMLElement, readMoreLabel: string | null) => {
16
+ shave(el, 90, {
17
+ character: `... <a href="#" onclick="(function(e){e.preventDefault(); const parentNode = e.target.parentNode; parentNode.nextSibling.style.display = 'inline'; parentNode.remove();return false;})(arguments[0]);return false;">${readMoreLabel}</a>`,
18
+ });
19
+ };
15
20
 
16
21
  const forEachElement = (selector: string, callback: Function) => {
17
22
  const nodeList = document.querySelectorAll(selector);
@@ -33,18 +33,14 @@ const Container = styled(SafeLink)`
33
33
  flex-direction: column;
34
34
  color: ${colors.text.primary};
35
35
  max-width: 350px;
36
- min-width: 350px;
37
36
  max-height: 415px;
38
- min-height: 415px;
39
37
  gap: ${spacing.nsmall};
40
38
  box-shadow: none;
41
39
  border: 1px solid ${colors.brand.lightest};
42
40
  border-radius: ${misc.borderRadius};
43
41
  padding: ${spacing.normal} ${spacing.medium};
44
42
  &[data-size='large'] {
45
- min-width: 532px;
46
43
  max-width: 532px;
47
- min-height: 550px;
48
44
  max-height: 550px;
49
45
  }
50
46
  &:hover,
@@ -0,0 +1,63 @@
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 CampaignBlock from './CampaignBlock';
12
+ import { defaultParameters } from '../../../../stories/defaults';
13
+
14
+ export default {
15
+ title: 'Enkle komponenter/Campaign Block',
16
+ component: CampaignBlock,
17
+ tags: ['autodocs'],
18
+ parameters: {
19
+ ...defaultParameters,
20
+ },
21
+ args: {},
22
+ } as Meta<typeof CampaignBlock>;
23
+
24
+ export const BothImages: StoryObj<typeof CampaignBlock> = {
25
+ args: {
26
+ title: { title: 'NDLA film', language: 'nb-no' },
27
+ description: {
28
+ text: 'NDLA film er en tjeneste i samarbeid med Norgesfilm. Denne tjenesten lar deg se en rekke spillefilmer, kortfilmer, dokumentarer og serier. Du kan også se undervisningsfilm og filmklipp. Velkommen inn i filmens verden!',
29
+ language: 'nb-no',
30
+ },
31
+ headingLevel: 'h2',
32
+ url: {
33
+ url: '#',
34
+ text: 'Gå til NDLA film',
35
+ },
36
+ imageBefore: {
37
+ alt: '',
38
+ src: 'https://api.test.ndla.no/image-api/raw/n2UYRxEG.png',
39
+ },
40
+ imageAfter: {
41
+ alt: '',
42
+ src: 'https://api.test.ndla.no/image-api/raw/8GOxOhjr.png',
43
+ },
44
+ },
45
+ };
46
+
47
+ export const SingleImage: StoryObj<typeof CampaignBlock> = {
48
+ args: {
49
+ title: { title: 'FN-dagen 24. oktober!', language: 'nb-no' },
50
+ description: {
51
+ text: ' Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation quis nostrud exercitation ',
52
+ language: 'nb-no',
53
+ },
54
+ url: {
55
+ url: '#',
56
+ text: 'Les mer om FN-dagen',
57
+ },
58
+ imageAfter: {
59
+ alt: 'FN-symbol',
60
+ src: 'https://api.test.ndla.no/image-api/raw/LkmDGtip.png',
61
+ },
62
+ },
63
+ };