@ndla/ui 43.0.3 → 44.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 (50) hide show
  1. package/es/Article/Article.js +6 -14
  2. package/es/Article/ArticleNotions.js +24 -48
  3. package/es/Article/index.js +1 -2
  4. package/es/Embed/ImageEmbed.js +2 -2
  5. package/es/Grid/Grid.js +15 -2
  6. package/es/Grid/index.js +1 -1
  7. package/es/LinkBlock/LinkBlock.js +5 -5
  8. package/es/{Article/ArticleContent.js → LinkBlock/LinkBlockSection.js} +22 -30
  9. package/es/LinkBlock/index.js +2 -1
  10. package/es/Topic/Topic.js +60 -42
  11. package/es/index.js +4 -3
  12. package/lib/Article/Article.d.ts +3 -9
  13. package/lib/Article/Article.js +6 -14
  14. package/lib/Article/ArticleNotions.d.ts +3 -9
  15. package/lib/Article/ArticleNotions.js +30 -56
  16. package/lib/Article/index.d.ts +1 -2
  17. package/lib/Article/index.js +0 -7
  18. package/lib/Embed/ImageEmbed.d.ts +11 -1
  19. package/lib/Embed/ImageEmbed.js +3 -1
  20. package/lib/Grid/Grid.d.ts +4 -0
  21. package/lib/Grid/Grid.js +23 -10
  22. package/lib/Grid/index.d.ts +1 -1
  23. package/lib/Grid/index.js +10 -2
  24. package/lib/LinkBlock/LinkBlock.d.ts +2 -7
  25. package/lib/LinkBlock/LinkBlock.js +5 -5
  26. package/lib/LinkBlock/LinkBlockSection.d.ts +13 -0
  27. package/lib/{Article/ArticleContent.js → LinkBlock/LinkBlockSection.js} +22 -27
  28. package/lib/LinkBlock/index.d.ts +1 -0
  29. package/lib/LinkBlock/index.js +7 -0
  30. package/lib/Topic/Topic.d.ts +10 -17
  31. package/lib/Topic/Topic.js +62 -44
  32. package/lib/index.d.ts +3 -2
  33. package/lib/index.js +19 -6
  34. package/lib/types.d.ts +1 -1
  35. package/package.json +3 -3
  36. package/src/Article/Article.tsx +6 -16
  37. package/src/Article/ArticleNotions.tsx +9 -78
  38. package/src/Article/index.ts +1 -10
  39. package/src/Embed/ImageEmbed.tsx +2 -2
  40. package/src/Grid/Grid.tsx +9 -1
  41. package/src/Grid/index.ts +1 -1
  42. package/src/LinkBlock/LinkBlock.stories.tsx +18 -8
  43. package/src/LinkBlock/LinkBlock.tsx +2 -8
  44. package/src/LinkBlock/LinkBlockSection.tsx +42 -0
  45. package/src/LinkBlock/index.ts +1 -0
  46. package/src/Topic/Topic.tsx +58 -39
  47. package/src/index.ts +3 -2
  48. package/src/types.ts +1 -1
  49. package/lib/Article/ArticleContent.d.ts +0 -7
  50. package/src/Article/ArticleContent.tsx +0 -40
@@ -6,7 +6,7 @@
6
6
  *
7
7
  */
8
8
 
9
- import { ReactNode, MouseEvent, ComponentType } from 'react';
9
+ import { ReactNode, MouseEvent, useMemo } from 'react';
10
10
  import styled from '@emotion/styled';
11
11
  import { animations, breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
12
12
 
@@ -17,12 +17,14 @@ import { ButtonV2 } from '@ndla/button';
17
17
  import { CursorClick, ExpandTwoArrows } from '@ndla/icons/action';
18
18
  import { css } from '@emotion/react';
19
19
  import { useTranslation } from 'react-i18next';
20
+ import { EmbedMetaData } from '@ndla/types-embed';
20
21
  import Loader from './Loader';
21
22
  import { ItemProps } from '../Navigation/NavigationBox';
22
23
  import { NavigationBox } from '../Navigation';
23
24
  import { makeSrcQueryString, ImageCrop, ImageFocalPoint } from '../Image';
24
25
  import { MessageBox } from '../Messages';
25
26
  import { Heading } from '../Typography';
27
+ import { getCrop, getFocalPoint } from '../Embed/ImageEmbed';
26
28
 
27
29
  type InvertItProps = {
28
30
  invertedStyle?: boolean;
@@ -71,6 +73,7 @@ const ShowVisualElementWrapper = styled.div`
71
73
  height: 100%;
72
74
  overflow: hidden;
73
75
  aspect-ratio: 1;
76
+ mask-image: radial-gradient(white, black);
74
77
  -webkit-mask-image: -webkit-radial-gradient(white, black); /* Safari fix */
75
78
  `;
76
79
 
@@ -187,31 +190,16 @@ const StyledModalHeader = styled(ModalHeader)`
187
190
  padding: ${spacing.small} ${spacing.nsmall};
188
191
  `;
189
192
 
190
- const icons: Record<VisualElementProps['type'], ComponentType> = {
191
- image: ExpandTwoArrows,
192
- video: PlayCircleFilled,
193
- other: CursorClick,
194
- };
195
-
196
- type VisualElementProps = {
197
- type: 'image' | 'video' | 'other';
198
- element: ReactNode;
199
- };
200
-
201
193
  export type TopicProps = {
202
194
  id?: string;
203
- topic?: {
204
- title: string;
205
- introduction: string;
206
- image?: {
207
- url: string;
208
- alt: string;
209
- crop?: ImageCrop;
210
- focalPoint?: ImageFocalPoint;
211
- };
212
- visualElement?: VisualElementProps;
213
- resources?: ReactNode;
195
+ metaImage?: {
196
+ url: string;
197
+ alt: string;
214
198
  };
199
+ title: string;
200
+ introduction: string;
201
+ resources?: ReactNode;
202
+ visualElementEmbedMeta?: EmbedMetaData;
215
203
  subTopics?: ItemProps[] | null | undefined;
216
204
  onSubTopicSelected?: (event: MouseEvent<HTMLElement>, id?: string) => void;
217
205
  isLoading?: boolean;
@@ -223,11 +211,21 @@ export type TopicProps = {
223
211
  frame?: boolean;
224
212
  messageBox?: string;
225
213
  children?: ReactNode;
214
+ visualElement?: ReactNode;
226
215
  };
227
216
 
217
+ interface MetaImageType {
218
+ url: string;
219
+ alt: string;
220
+ crop?: ImageCrop;
221
+ focalPoint?: ImageFocalPoint;
222
+ }
223
+
228
224
  const Topic = ({
229
225
  id,
230
- topic,
226
+ title,
227
+ introduction,
228
+ resources,
231
229
  subTopics,
232
230
  onSubTopicSelected,
233
231
  isLoading,
@@ -235,17 +233,40 @@ const Topic = ({
235
233
  invertedStyle,
236
234
  onToggleShowContent,
237
235
  showContent,
236
+ metaImage: articleMetaImage,
238
237
  isAdditionalTopic,
239
238
  frame,
240
239
  messageBox,
240
+ visualElementEmbedMeta,
241
241
  children,
242
+ visualElement,
242
243
  }: TopicProps) => {
243
244
  const { t } = useTranslation();
244
245
  const contentId = `expanded-description-${id}`;
245
246
  const testId = 'nav-topic-about';
246
- const VisualElementIcon = topic?.visualElement?.type ? icons[topic.visualElement.type] : null;
247
+
248
+ const VisualElementIcon = useMemo(() => {
249
+ if (!visualElementEmbedMeta || visualElementEmbedMeta.status === 'error') return null;
250
+ else if (visualElementEmbedMeta.resource === 'brightcove') {
251
+ return PlayCircleFilled;
252
+ } else if (visualElementEmbedMeta.resource === 'image') {
253
+ return ExpandTwoArrows;
254
+ } else return CursorClick;
255
+ }, [visualElementEmbedMeta]);
256
+
257
+ const metaImage: MetaImageType | undefined = useMemo(() => {
258
+ if (visualElementEmbedMeta?.resource === 'image' && visualElementEmbedMeta.status === 'success') {
259
+ return {
260
+ url: visualElementEmbedMeta.data.image?.imageUrl,
261
+ alt: visualElementEmbedMeta.data.alttext?.alttext,
262
+ crop: getCrop(visualElementEmbedMeta.embedData),
263
+ focalPoint: getFocalPoint(visualElementEmbedMeta.embedData),
264
+ };
265
+ } else return articleMetaImage;
266
+ }, [articleMetaImage, visualElementEmbedMeta]);
267
+
247
268
  const wrapperStyle = [frame ? frameStyle : undefined, invertedStyle ? _invertedStyle : undefined];
248
- if (isLoading || !topic) {
269
+ if (isLoading) {
249
270
  return (
250
271
  <Wrapper css={wrapperStyle} data-testid={testId}>
251
272
  {isLoading ? <Loader /> : null}
@@ -259,7 +280,7 @@ const Topic = ({
259
280
  <div>
260
281
  <HeadingWrapper>
261
282
  <Heading element="h1" headingStyle="h2" id={id} tabIndex={-1}>
262
- {topic.title}
283
+ {title}
263
284
  </Heading>
264
285
  {isAdditionalTopic && (
265
286
  <>
@@ -268,23 +289,21 @@ const Topic = ({
268
289
  </>
269
290
  )}
270
291
  </HeadingWrapper>
271
- <TopicIntroduction>
272
- {renderMarkdown ? parse(renderMarkdown(topic.introduction)) : topic.introduction}
273
- </TopicIntroduction>
292
+ <TopicIntroduction>{renderMarkdown ? parse(renderMarkdown(introduction)) : introduction}</TopicIntroduction>
274
293
  </div>
275
- {topic.image && (
294
+ {metaImage && (
276
295
  <TopicHeaderVisualElementWrapper>
277
- {topic.visualElement ? (
296
+ {visualElementEmbedMeta?.status === 'success' ? (
278
297
  <Modal>
279
298
  <ModalTrigger>
280
299
  <VisualElementButton
281
300
  variant="stripped"
282
- title={topic.visualElement.type === 'image' ? t('image.largeSize') : t('visualElement.show')}
301
+ title={visualElementEmbedMeta.resource === 'image' ? t('image.largeSize') : t('visualElement.show')}
283
302
  >
284
303
  <ShowVisualElementWrapper>
285
304
  <TopicHeaderImage
286
- src={`${topic.image.url}?${makeSrcQueryString(800, topic.image.crop, topic.image.focalPoint)}`}
287
- alt={topic.image.alt}
305
+ src={`${metaImage.url}?${makeSrcQueryString(800, metaImage.crop, metaImage.focalPoint)}`}
306
+ alt={metaImage.alt}
288
307
  />
289
308
  <TopicHeaderOverlay />
290
309
  </ShowVisualElementWrapper>
@@ -300,13 +319,13 @@ const Topic = ({
300
319
  <StyledModalHeader>
301
320
  <ModalCloseButton />
302
321
  </StyledModalHeader>
303
- {topic.visualElement && topic.visualElement.element}
322
+ {visualElement}
304
323
  </ModalContent>
305
324
  </Modal>
306
325
  ) : (
307
326
  <TopicHeaderImage
308
- src={`${topic.image.url}?${makeSrcQueryString(400, topic.image.crop, topic.image.focalPoint)}`}
309
- alt={topic.image.alt}
327
+ src={`${metaImage.url}?${makeSrcQueryString(400, metaImage.crop, metaImage.focalPoint)}`}
328
+ alt={metaImage.alt}
310
329
  />
311
330
  )}
312
331
  </TopicHeaderVisualElementWrapper>
@@ -349,7 +368,7 @@ const Topic = ({
349
368
  invertedStyle={invertedStyle}
350
369
  />
351
370
  )}
352
- {topic.resources}
371
+ {resources}
353
372
  </Wrapper>
354
373
  );
355
374
  };
package/src/index.ts CHANGED
@@ -28,7 +28,6 @@ export {
28
28
 
29
29
  export {
30
30
  ArticleByline,
31
- ArticleContent,
32
31
  ArticleFootNotes,
33
32
  ArticleIntroduction,
34
33
  ArticleTitle,
@@ -264,7 +263,7 @@ export { KeyFigure } from './KeyFigure';
264
263
  export { default as ContactBlock } from './ContactBlock';
265
264
  export type { HeartButtonType } from './Embed';
266
265
  export { CampaignBlock } from './CampaignBlock';
267
- export { Grid } from './Grid';
266
+ export { Grid, GridParallaxItem } from './Grid';
268
267
  export type { GridType } from './Grid';
269
268
  export { Heading } from './Typography';
270
269
  export type { HeadingLevel } from './types';
@@ -274,3 +273,5 @@ export { DefinitionTerm, DefinitionDescription } from './DefinitionList';
274
273
  export type { ProgrammeV2 } from './ProgrammeCard';
275
274
 
276
275
  export { Gloss } from './Gloss';
276
+
277
+ export { LinkBlock, LinkBlockSection } from './LinkBlock';
package/src/types.ts CHANGED
@@ -68,7 +68,7 @@ export interface FootNote {
68
68
  export interface Article {
69
69
  title: string;
70
70
  introduction: string;
71
- content: string | ReactNode;
71
+ content: ReactNode;
72
72
  footNotes: Array<FootNote>;
73
73
  copyright: Copyright;
74
74
  published: string;
@@ -1,7 +0,0 @@
1
- import { Locale } from '../types';
2
- type Props = {
3
- content: string;
4
- locale: Locale;
5
- };
6
- declare const ArticleContent: ({ content, locale, ...rest }: Props) => import("@emotion/react/jsx-runtime").JSX.Element;
7
- export default ArticleContent;
@@ -1,40 +0,0 @@
1
- /*
2
- * Copyright (c) 2016-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 { useEffect } from 'react';
10
- import {
11
- initArticleScripts,
12
- removeEventListenerForResize,
13
- // @ts-ignore
14
- } from '@ndla/article-scripts';
15
- import { initPopovers, initTooltips } from '@ndla/tooltip';
16
- import { initAudioPlayers } from '../AudioPlayer';
17
- import { initCopyParagraphButtons } from '../CopyParagraphButton';
18
- import { Locale } from '../types';
19
-
20
- type Props = {
21
- content: string;
22
- locale: Locale;
23
- };
24
- const ArticleContent = ({ content, locale, ...rest }: Props) => {
25
- useEffect(() => {
26
- removeEventListenerForResize();
27
- initArticleScripts();
28
- initAudioPlayers(locale);
29
- initTooltips();
30
- initPopovers();
31
- initCopyParagraphButtons();
32
- return () => {
33
- removeEventListenerForResize();
34
- };
35
- }, [content, locale]);
36
-
37
- return <div dangerouslySetInnerHTML={{ __html: content }} {...rest} />;
38
- };
39
-
40
- export default ArticleContent;