@ndla/ui 48.0.0 → 49.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 (161) hide show
  1. package/es/BlogPost/BlogPost.js +6 -6
  2. package/es/CampaignBlock/CampaignBlock.js +8 -8
  3. package/es/CopyParagraphButton/CopyParagraphButton.js +30 -58
  4. package/es/CopyParagraphButton/index.js +1 -3
  5. package/es/Embed/RelatedContentEmbed.js +3 -3
  6. package/es/FactBox/FactBox.js +64 -19
  7. package/es/FactBox/index.js +0 -1
  8. package/es/Figure/index.js +0 -2
  9. package/es/FileList/File.js +46 -24
  10. package/es/FileList/FileList.js +18 -14
  11. package/es/FileList/index.js +0 -2
  12. package/es/LinkBlock/LinkBlock.js +7 -7
  13. package/es/Messages/index.js +1 -2
  14. package/es/Navigation/index.js +1 -2
  15. package/es/RelatedArticleList/RelatedArticleList.js +80 -35
  16. package/es/RelatedArticleList/index.js +2 -3
  17. package/es/Search/index.js +0 -1
  18. package/es/all.css +1 -1
  19. package/es/i18n/i18n.js +2 -1
  20. package/es/index.js +9 -11
  21. package/es/utils/relativeUrl.js +17 -2
  22. package/lib/BlogPost/BlogPost.js +5 -5
  23. package/lib/CampaignBlock/CampaignBlock.js +7 -7
  24. package/lib/CopyParagraphButton/CopyParagraphButton.d.ts +5 -4
  25. package/lib/CopyParagraphButton/CopyParagraphButton.js +29 -57
  26. package/lib/CopyParagraphButton/index.d.ts +1 -3
  27. package/lib/CopyParagraphButton/index.js +0 -14
  28. package/lib/Embed/RelatedContentEmbed.js +3 -3
  29. package/lib/FactBox/FactBox.d.ts +1 -4
  30. package/lib/FactBox/FactBox.js +72 -27
  31. package/lib/FactBox/index.d.ts +0 -1
  32. package/lib/FactBox/index.js +0 -7
  33. package/lib/Figure/index.d.ts +0 -2
  34. package/lib/Figure/index.js +0 -14
  35. package/lib/FileList/File.d.ts +22 -3
  36. package/lib/FileList/File.js +45 -25
  37. package/lib/FileList/FileList.d.ts +10 -14
  38. package/lib/FileList/FileList.js +17 -15
  39. package/lib/FileList/index.d.ts +0 -2
  40. package/lib/FileList/index.js +0 -14
  41. package/lib/LinkBlock/LinkBlock.js +6 -6
  42. package/lib/Messages/index.d.ts +1 -2
  43. package/lib/Messages/index.js +0 -7
  44. package/lib/Navigation/index.d.ts +1 -2
  45. package/lib/Navigation/index.js +0 -7
  46. package/lib/RelatedArticleList/RelatedArticleList.d.ts +16 -17
  47. package/lib/RelatedArticleList/RelatedArticleList.js +78 -35
  48. package/lib/RelatedArticleList/index.d.ts +2 -3
  49. package/lib/RelatedArticleList/index.js +2 -12
  50. package/lib/Search/index.d.ts +0 -1
  51. package/lib/Search/index.js +0 -7
  52. package/lib/all.css +1 -1
  53. package/lib/i18n/i18n.d.ts +1 -0
  54. package/lib/i18n/i18n.js +4 -2
  55. package/lib/index.d.ts +9 -11
  56. package/lib/index.js +9 -89
  57. package/lib/utils/relativeUrl.d.ts +1 -1
  58. package/lib/utils/relativeUrl.js +19 -4
  59. package/package.json +8 -8
  60. package/src/BlogPost/BlogPost.tsx +2 -2
  61. package/src/CampaignBlock/CampaignBlock.tsx +2 -2
  62. package/src/CopyParagraphButton/CopyParagraphButton.tsx +24 -46
  63. package/src/CopyParagraphButton/index.tsx +1 -3
  64. package/src/Embed/RelatedContentEmbed.stories.tsx +9 -9
  65. package/src/Embed/RelatedContentEmbed.tsx +3 -3
  66. package/src/FactBox/FactBox.tsx +29 -16
  67. package/src/FactBox/Factbox.stories.tsx +4 -4
  68. package/src/FactBox/index.ts +0 -2
  69. package/src/Figure/index.ts +0 -2
  70. package/src/FileList/File.tsx +62 -32
  71. package/src/FileList/FileList.stories.tsx +15 -15
  72. package/src/FileList/FileList.tsx +21 -27
  73. package/src/FileList/index.ts +0 -2
  74. package/src/LinkBlock/LinkBlock.tsx +2 -2
  75. package/src/Messages/index.ts +1 -2
  76. package/src/Navigation/index.ts +1 -2
  77. package/src/RelatedArticleList/RelatedArticleList.tsx +53 -47
  78. package/src/RelatedArticleList/index.ts +2 -3
  79. package/src/Search/index.ts +0 -1
  80. package/src/i18n/i18n.ts +2 -1
  81. package/src/index.ts +10 -13
  82. package/src/main.scss +0 -1
  83. package/src/utils/__tests__/relativeUrl-test.tsx +72 -0
  84. package/src/utils/relativeUrl.ts +19 -2
  85. package/es/CopyParagraphButton/CopyParagraphButtonV2.js +0 -87
  86. package/es/CopyParagraphButton/initCopyParagraphButtons.js +0 -29
  87. package/es/FactBox/FactBoxV2.js +0 -93
  88. package/es/Figure/FigureBylineExpandButton.js +0 -29
  89. package/es/Figure/FigureExpandButton.js +0 -30
  90. package/es/FileList/FileListV2.js +0 -47
  91. package/es/FileList/FileV2.js +0 -32
  92. package/es/Masthead/MastheadSearchModal.js +0 -82
  93. package/es/Messages/MessageBoxTag.js +0 -33
  94. package/es/MultidisciplinarySubject/List.js +0 -52
  95. package/es/MultidisciplinarySubject/ListItem.js +0 -90
  96. package/es/MultidisciplinarySubject/MultidisciplinarySubject.js +0 -125
  97. package/es/MultidisciplinarySubject/index.js +0 -10
  98. package/es/Navigation/NavigationTopicAbout.js +0 -164
  99. package/es/RelatedArticleList/RelatedArticleV2.js +0 -125
  100. package/es/Search/ToggleSearchButton.js +0 -51
  101. package/es/Translation/Translation.js +0 -33
  102. package/es/Translation/TranslationLine.js +0 -47
  103. package/es/Translation/index.js +0 -2
  104. package/lib/CopyParagraphButton/CopyParagraphButtonV2.d.ts +0 -15
  105. package/lib/CopyParagraphButton/CopyParagraphButtonV2.js +0 -92
  106. package/lib/CopyParagraphButton/initCopyParagraphButtons.d.ts +0 -2
  107. package/lib/CopyParagraphButton/initCopyParagraphButtons.js +0 -38
  108. package/lib/FactBox/FactBoxV2.d.ts +0 -13
  109. package/lib/FactBox/FactBoxV2.js +0 -98
  110. package/lib/Figure/FigureBylineExpandButton.d.ts +0 -16
  111. package/lib/Figure/FigureBylineExpandButton.js +0 -35
  112. package/lib/Figure/FigureExpandButton.d.ts +0 -16
  113. package/lib/Figure/FigureExpandButton.js +0 -35
  114. package/lib/FileList/FileListV2.d.ts +0 -13
  115. package/lib/FileList/FileListV2.js +0 -52
  116. package/lib/FileList/FileV2.d.ts +0 -15
  117. package/lib/FileList/FileV2.js +0 -40
  118. package/lib/Masthead/MastheadSearchModal.d.ts +0 -8
  119. package/lib/Masthead/MastheadSearchModal.js +0 -89
  120. package/lib/Messages/MessageBoxTag.d.ts +0 -12
  121. package/lib/Messages/MessageBoxTag.js +0 -40
  122. package/lib/MultidisciplinarySubject/List.d.ts +0 -7
  123. package/lib/MultidisciplinarySubject/List.js +0 -59
  124. package/lib/MultidisciplinarySubject/ListItem.d.ts +0 -10
  125. package/lib/MultidisciplinarySubject/ListItem.js +0 -97
  126. package/lib/MultidisciplinarySubject/MultidisciplinarySubject.d.ts +0 -13
  127. package/lib/MultidisciplinarySubject/MultidisciplinarySubject.js +0 -135
  128. package/lib/MultidisciplinarySubject/index.d.ts +0 -9
  129. package/lib/MultidisciplinarySubject/index.js +0 -17
  130. package/lib/Navigation/NavigationTopicAbout.d.ts +0 -13
  131. package/lib/Navigation/NavigationTopicAbout.js +0 -171
  132. package/lib/RelatedArticleList/RelatedArticleV2.d.ts +0 -26
  133. package/lib/RelatedArticleList/RelatedArticleV2.js +0 -131
  134. package/lib/Search/ToggleSearchButton.d.ts +0 -14
  135. package/lib/Search/ToggleSearchButton.js +0 -57
  136. package/lib/Translation/Translation.d.ts +0 -14
  137. package/lib/Translation/Translation.js +0 -38
  138. package/lib/Translation/TranslationLine.d.ts +0 -16
  139. package/lib/Translation/TranslationLine.js +0 -51
  140. package/lib/Translation/index.d.ts +0 -2
  141. package/lib/Translation/index.js +0 -20
  142. package/src/CopyParagraphButton/CopyParagraphButtonV2.tsx +0 -85
  143. package/src/CopyParagraphButton/initCopyParagraphButtons.tsx +0 -27
  144. package/src/FactBox/FactBoxV2.tsx +0 -56
  145. package/src/Figure/FigureBylineExpandButton.tsx +0 -34
  146. package/src/Figure/FigureExpandButton.tsx +0 -35
  147. package/src/FileList/FileListV2.tsx +0 -58
  148. package/src/FileList/FileV2.tsx +0 -33
  149. package/src/Masthead/MastheadSearchModal.tsx +0 -101
  150. package/src/Messages/MessageBoxTag.tsx +0 -34
  151. package/src/MultidisciplinarySubject/List.tsx +0 -49
  152. package/src/MultidisciplinarySubject/ListItem.tsx +0 -74
  153. package/src/MultidisciplinarySubject/MultidisciplinarySubject.tsx +0 -117
  154. package/src/MultidisciplinarySubject/index.ts +0 -11
  155. package/src/Navigation/NavigationTopicAbout.tsx +0 -171
  156. package/src/RelatedArticleList/RelatedArticleV2.tsx +0 -101
  157. package/src/Search/ToggleSearchButton.tsx +0 -64
  158. package/src/Translation/Translation.tsx +0 -29
  159. package/src/Translation/TranslationLine.tsx +0 -42
  160. package/src/Translation/component.translation.scss +0 -53
  161. package/src/Translation/index.ts +0 -2
@@ -6,10 +6,12 @@
6
6
  *
7
7
  */
8
8
 
9
- import { ReactNode, MouseEvent } from 'react';
9
+ import { ReactNode, useState } from 'react';
10
10
  import BEMHelper from 'react-bem-helper';
11
- import { ButtonV2 } from '@ndla/button';
12
11
  import { useTranslation } from 'react-i18next';
12
+ import styled from '@emotion/styled';
13
+ import { IconButtonV2 } from '@ndla/button';
14
+ import { ChevronDown, ChevronUp } from '@ndla/icons/common';
13
15
 
14
16
  const classes = new BEMHelper({
15
17
  name: 'factbox',
@@ -17,26 +19,37 @@ const classes = new BEMHelper({
17
19
  });
18
20
 
19
21
  interface Props {
20
- dangerouslySetInnerHTML?: { __html: string };
21
22
  children?: ReactNode;
22
23
  }
23
24
 
24
- const toggleFactBox = (event: MouseEvent<HTMLButtonElement>) => {
25
- const button = event.currentTarget;
26
- const aside = button?.previousSibling?.parentElement;
27
- aside?.classList?.toggle('expanded');
28
- };
29
- const FactBox = ({ children, dangerouslySetInnerHTML }: Props) => {
25
+ const StyledAside = styled.aside`
26
+ display: flex;
27
+ flex-direction: column;
28
+ align-items: center;
29
+ `;
30
+
31
+ const StyledDiv = styled.div`
32
+ width: 100%;
33
+ `;
34
+
35
+ const StyledIconButton = styled(IconButtonV2)`
36
+ margin-top: -20px;
37
+ z-index: 1;
38
+ `;
39
+
40
+ const FactBox = ({ children }: Props) => {
30
41
  const { t } = useTranslation();
42
+ const [isOpen, setIsOpen] = useState(false);
43
+
44
+ const additional = isOpen ? 'expanded' : '';
31
45
 
32
46
  return (
33
- <aside {...classes()}>
34
- <div {...classes('content')} dangerouslySetInnerHTML={dangerouslySetInnerHTML}>
35
- {children}
36
- </div>
37
- <ButtonV2 {...classes('button', 'collapsed')} onClick={toggleFactBox} title={t('factbox.open')} />
38
- <ButtonV2 {...classes('button', 'open')} onClick={toggleFactBox} title={t('factbox.close')} />
39
- </aside>
47
+ <StyledAside {...classes(undefined, undefined, additional)}>
48
+ <StyledDiv {...classes('content')}>{children}</StyledDiv>
49
+ <StyledIconButton onClick={() => setIsOpen((p) => !p)} aria-label={t(`factbox.${isOpen ? 'close' : 'open'}`)}>
50
+ {isOpen ? <ChevronUp /> : <ChevronDown />}
51
+ </StyledIconButton>
52
+ </StyledAside>
40
53
  );
41
54
  };
42
55
 
@@ -10,7 +10,7 @@ import styled from '@emotion/styled';
10
10
  import { Meta, StoryObj } from '@storybook/react';
11
11
  import { spacing } from '@ndla/core';
12
12
  import { Heading, Text } from '@ndla/typography';
13
- import FactBoxV2 from './FactBoxV2';
13
+ import FactBox from './FactBox';
14
14
  import { defaultParameters } from '../../../../stories/defaults';
15
15
 
16
16
  const Wrapper = styled.div`
@@ -22,7 +22,7 @@ const Wrapper = styled.div`
22
22
  */
23
23
  export default {
24
24
  title: 'Components/FactBox',
25
- component: FactBoxV2,
25
+ component: FactBox,
26
26
  tags: ['autodocs'],
27
27
  paramemeters: {
28
28
  inlineStories: true,
@@ -57,6 +57,6 @@ export default {
57
57
  </>
58
58
  ),
59
59
  },
60
- } as Meta<typeof FactBoxV2>;
60
+ } as Meta<typeof FactBox>;
61
61
 
62
- export const Default: StoryObj<typeof FactBoxV2> = {};
62
+ export const Default: StoryObj<typeof FactBox> = {};
@@ -1,5 +1,3 @@
1
1
  import FactBox from './FactBox';
2
2
 
3
- export { default as FactBoxV2 } from './FactBoxV2';
4
-
5
3
  export default FactBox;
@@ -7,8 +7,6 @@
7
7
  */
8
8
 
9
9
  export { default as Figure } from './Figure';
10
- export { FigureExpandButton } from './FigureExpandButton';
11
- export { FigureBylineExpandButton } from './FigureBylineExpandButton';
12
10
  export { FigureOpenDialogButton } from './FigureOpenDialogButton';
13
11
 
14
12
  export type { FigureType } from './Figure';
@@ -1,9 +1,35 @@
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
+
1
9
  import styled from '@emotion/styled';
10
+ import { useTranslation } from 'react-i18next';
11
+ import SafeLink from '@ndla/safelink';
2
12
  import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
3
13
  import { Download } from '@ndla/icons/common';
4
- import SafeLink from '@ndla/safelink';
5
- import Tooltip from '@ndla/tooltip';
6
- import { FileFormat, FileType } from './FileList';
14
+
15
+ interface Props {
16
+ title: string;
17
+ url: string;
18
+ fileExists: boolean;
19
+ fileType: string;
20
+ }
21
+
22
+ export interface FileType {
23
+ title: string;
24
+ formats: FileFormat[];
25
+ fileExists?: boolean;
26
+ }
27
+
28
+ export interface FileFormat {
29
+ url: string;
30
+ fileType: string;
31
+ tooltip: string;
32
+ }
7
33
 
8
34
  const LinkTextWrapper = styled.div`
9
35
  & > span {
@@ -31,7 +57,29 @@ const FileLink = styled(SafeLink)`
31
57
  }
32
58
  `;
33
59
 
34
- const renderFormat = (format: FileFormat, title: string, isPrimary: boolean, isDeadLink: boolean) => {
60
+ const FileListItem = styled.li`
61
+ ${fonts.sizes('18px', '26px')};
62
+ font-weight: ${fonts.weight.semibold};
63
+ min-height: 60px;
64
+ background: ${colors.brand.greyLighter};
65
+ display: flex;
66
+ align-items: center;
67
+ flex-wrap: wrap;
68
+ margin-bottom: ${spacing.xsmall};
69
+ padding: ${spacing.small};
70
+ ${mq.range({ from: breakpoints.tablet })} {
71
+ padding: ${spacing.small} ${spacing.normal};
72
+ }
73
+ `;
74
+
75
+ interface FormatProps {
76
+ format: FileFormat;
77
+ title: string;
78
+ isPrimary: boolean;
79
+ isDeadLink: boolean;
80
+ }
81
+
82
+ const Format = ({ format, title, isPrimary, isDeadLink }: FormatProps) => {
35
83
  const titleWithFormat = `${title} (${format.fileType.toUpperCase()})`;
36
84
 
37
85
  if (isDeadLink) {
@@ -46,40 +94,22 @@ const renderFormat = (format: FileFormat, title: string, isPrimary: boolean, isD
46
94
  return (
47
95
  <FileLink key={format.url} to={format.url} target="_blank" aria-label={titleWithFormat}>
48
96
  <Download />
49
- <Tooltip tooltip={format.tooltip}>
50
- <LinkTextWrapper>
51
- <span>{isPrimary ? titleWithFormat : `(${format.fileType.toUpperCase()})`}</span>
52
- </LinkTextWrapper>
53
- </Tooltip>
97
+ <LinkTextWrapper aria-label={format.tooltip}>
98
+ <span>{isPrimary ? titleWithFormat : `(${format.fileType.toUpperCase()})`}</span>
99
+ </LinkTextWrapper>
54
100
  </FileLink>
55
101
  );
56
102
  };
57
103
 
58
- interface Props {
59
- file: FileType;
60
- }
104
+ const File = ({ title, url, fileExists, fileType }: Props) => {
105
+ const { t } = useTranslation();
106
+ const tooltip = `${t('download')} ${url.split('/').pop()}`;
61
107
 
62
- const FileListItem = styled.li`
63
- ${fonts.sizes('18px', '26px')};
64
- font-weight: ${fonts.weight.semibold};
65
- min-height: 60px;
66
- background: ${colors.brand.greyLighter};
67
- display: flex;
68
- align-items: center;
69
- flex-wrap: wrap;
70
- margin-bottom: ${spacing.xsmall};
71
- padding: ${spacing.small};
72
- ${mq.range({ from: breakpoints.tablet })} {
73
- padding: ${spacing.small} ${spacing.normal};
74
- }
75
- `;
76
-
77
- const File = ({ file }: Props) => {
78
- const formatLinks = file.formats.map((format, index) =>
79
- renderFormat(format, file.title, index === 0, !file.fileExists),
108
+ return (
109
+ <FileListItem>
110
+ <Format format={{ url, fileType, tooltip }} isPrimary title={title} isDeadLink={!fileExists} />
111
+ </FileListItem>
80
112
  );
81
-
82
- return <FileListItem key={file.title}>{formatLinks}</FileListItem>;
83
113
  };
84
114
 
85
115
  export default File;
@@ -7,14 +7,14 @@
7
7
  */
8
8
 
9
9
  import { Meta, StoryObj } from '@storybook/react';
10
- import FileV2 from './FileV2';
10
+ import File from './File';
11
11
  import { defaultParameters } from '../../../../stories/defaults';
12
- import FileListV2 from './FileListV2';
12
+ import FileList from './FileList';
13
13
 
14
14
  export default {
15
15
  title: 'Components/FileList',
16
16
  tags: ['autodocs'],
17
- component: FileV2,
17
+ component: File,
18
18
  parameters: {
19
19
  inlineStories: true,
20
20
  ...defaultParameters,
@@ -26,23 +26,23 @@ export default {
26
26
  fileType: 'pdf',
27
27
  },
28
28
  render: (args) => (
29
- <FileListV2>
30
- <FileV2 {...args} />
31
- </FileListV2>
29
+ <FileList>
30
+ <File {...args} />
31
+ </FileList>
32
32
  ),
33
- } as Meta<typeof FileV2>;
33
+ } as Meta<typeof File>;
34
34
 
35
- export const FileNotFound: StoryObj<typeof FileV2> = {
35
+ export const FileNotFound: StoryObj<typeof File> = {
36
36
  args: { fileExists: false },
37
37
  };
38
38
 
39
- export const SeveralFiles: StoryObj<typeof FileV2> = {
39
+ export const SeveralFiles: StoryObj<typeof File> = {
40
40
  render: () => (
41
- <FileListV2>
42
- <FileV2 title="Fil 1" url="https://ndla.no/1" fileExists fileType="mp4" />
43
- <FileV2 title="Fil 2" url="https://ndla.no/2" fileExists={false} fileType="pdf" />
44
- <FileV2 title="Fil 3" url="https://ndla.no/3" fileExists fileType="docx" />
45
- <FileV2 title="Fil 4" url="https://ndla.no/4" fileExists fileType="docx" />
46
- </FileListV2>
41
+ <FileList>
42
+ <File title="Fil 1" url="https://ndla.no/1" fileExists fileType="mp4" />
43
+ <File title="Fil 2" url="https://ndla.no/2" fileExists={false} fileType="pdf" />
44
+ <File title="Fil 3" url="https://ndla.no/3" fileExists fileType="docx" />
45
+ <File title="Fil 4" url="https://ndla.no/4" fileExists fileType="docx" />
46
+ </FileList>
47
47
  ),
48
48
  };
@@ -1,23 +1,18 @@
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
+
1
9
  import styled from '@emotion/styled';
2
10
  import { colors, fonts, spacing } from '@ndla/core';
3
- import File from './File';
4
-
5
- export interface FileType {
6
- title: string;
7
- formats: FileFormat[];
8
- fileExists?: boolean;
9
- }
10
-
11
- export interface FileFormat {
12
- url: string;
13
- fileType: string;
14
- tooltip: string;
15
- }
11
+ import { ReactNode } from 'react';
12
+ import { useTranslation } from 'react-i18next';
16
13
 
17
14
  interface Props {
18
- id: string;
19
- heading: string;
20
- files: FileType[];
15
+ children: ReactNode;
21
16
  }
22
17
 
23
18
  const FileListSection = styled.section`
@@ -35,7 +30,7 @@ const FileListSection = styled.section`
35
30
  }
36
31
  `;
37
32
 
38
- const FileListHeading = styled.h1`
33
+ const FileListHeading = styled.h3`
39
34
  ${fonts.sizes('16px', '18px')};
40
35
  letter-spacing: 0.05em;
41
36
  margin: 0 0 ${spacing.xsmall} 0;
@@ -50,15 +45,14 @@ const FilesList = styled.ul`
50
45
  padding: 0;
51
46
  `;
52
47
 
53
- const FileList = ({ files, heading, id }: Props) => (
54
- <FileListSection>
55
- <FileListHeading>{heading}</FileListHeading>
56
- <FilesList>
57
- {files.map((file) => (
58
- <File key={`file-${id}-${file.title}`} file={file} />
59
- ))}
60
- </FilesList>
61
- </FileListSection>
62
- );
48
+ const FileList = ({ children }: Props) => {
49
+ const { t } = useTranslation();
50
+ return (
51
+ <FileListSection>
52
+ <FileListHeading>{t('files')}</FileListHeading>
53
+ <FilesList>{children}</FilesList>
54
+ </FileListSection>
55
+ );
56
+ };
63
57
 
64
58
  export default FileList;
@@ -1,8 +1,6 @@
1
1
  import FileList from './FileList';
2
2
 
3
3
  export { default as File } from './File';
4
- export { default as FileListV2 } from './FileListV2';
5
4
  export { default as PdfFile } from './PdfFile';
6
- export { default as FileV2 } from './FileV2';
7
5
 
8
6
  export default FileList;
@@ -15,7 +15,7 @@ import { breakpoints, colors, spacing, mq } from '@ndla/core';
15
15
  import { LinkBlockEmbedData } from '@ndla/types-embed';
16
16
  import { useMemo } from 'react';
17
17
  import { Heading } from '@ndla/typography';
18
- import { usePossiblyRelativeUrl } from '../utils/relativeUrl';
18
+ import { getPossiblyRelativeUrl } from '../utils/relativeUrl';
19
19
 
20
20
  const StyledForward = styled(Forward)`
21
21
  margin: 0 ${spacing.nsmall};
@@ -78,7 +78,7 @@ interface Props extends Omit<LinkBlockEmbedData, 'resource'> {
78
78
  }
79
79
 
80
80
  const LinkBlock = ({ title, language, date, url, path }: Props) => {
81
- const href = usePossiblyRelativeUrl(url, path);
81
+ const href = getPossiblyRelativeUrl(url, path);
82
82
  const formattedDate = useMemo(() => {
83
83
  if (!date) return null;
84
84
  const locale = language === 'nb' ? nb : language === 'nn' ? nn : enGB;
@@ -6,8 +6,7 @@
6
6
  *
7
7
  */
8
8
 
9
- import MessageBoxTag from './MessageBoxTag';
10
9
  import MessageBox from './MessageBox';
11
10
  import MessageBanner from './MessageBanner';
12
11
 
13
- export { MessageBox, MessageBoxTag, MessageBanner };
12
+ export { MessageBox, MessageBanner };
@@ -7,6 +7,5 @@
7
7
  */
8
8
 
9
9
  import NavigationBox from './NavigationBox';
10
- import NavigationTopicAbout from './NavigationTopicAbout';
11
10
 
12
- export { NavigationBox, NavigationTopicAbout };
11
+ export { NavigationBox };
@@ -1,9 +1,21 @@
1
- import { Children, cloneElement, ReactElement } from 'react';
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 { Children, HTMLProps, ReactNode, useMemo, useState } from 'react';
2
10
  import BEMHelper from 'react-bem-helper';
11
+ import { useTranslation } from 'react-i18next';
12
+ import styled from '@emotion/styled';
3
13
  import { ButtonV2 } from '@ndla/button';
4
14
  import SafeLink from '@ndla/safelink';
5
15
  import { HeadingLevel } from '@ndla/typography';
6
16
  import SectionHeading from '../SectionHeading';
17
+ import ContentTypeBadge from '../ContentTypeBadge';
18
+ import { contentTypes } from '../model/ContentType';
7
19
 
8
20
  const classes = new BEMHelper({
9
21
  name: 'related-articles',
@@ -11,29 +23,26 @@ const classes = new BEMHelper({
11
23
  });
12
24
 
13
25
  interface RelatedArticleProps {
14
- icon: ReactElement;
15
26
  title: string;
16
- modifier?: string;
17
27
  introduction: string;
18
28
  to: string;
19
29
  linkInfo?: string;
20
30
  target?: string;
31
+ type?: string;
21
32
  }
22
33
 
23
34
  export const RelatedArticle = ({
24
35
  title,
25
36
  introduction,
26
- icon,
27
- modifier,
28
37
  to,
29
38
  linkInfo = '',
30
39
  target = '',
40
+ type = contentTypes.SUBJECT_MATERIAL,
31
41
  }: RelatedArticleProps) => {
32
- const iconWithClass = cloneElement(icon, { className: 'c-icon--medium' });
33
42
  return (
34
- <article {...classes('item', modifier)}>
43
+ <article {...classes('item', type)}>
35
44
  <h3 {...classes('title')}>
36
- {iconWithClass}
45
+ <ContentTypeBadge type={type} background size="small" />
37
46
  <span {...classes('link-wrapper')}>
38
47
  <SafeLink to={to} {...classes('link')} target={target} rel={linkInfo ? 'noopener noreferrer' : undefined}>
39
48
  {title}
@@ -46,50 +55,47 @@ export const RelatedArticle = ({
46
55
  );
47
56
  };
48
57
 
49
- interface Props {
50
- messages: {
51
- title: string;
52
- showMore: string;
53
- showLess: string;
54
- };
55
- headingLevel: HeadingLevel;
56
- children?: ReactElement;
57
- dangerouslySetInnerHTML?: {
58
- __html: string;
59
- };
58
+ const HeadingWrapper = styled.div`
59
+ display: flex;
60
+ justify-content: space-between;
61
+ align-items: center;
62
+ `;
63
+
64
+ interface Props extends HTMLProps<HTMLElement> {
65
+ children?: JSX.Element[];
60
66
  articleCount?: number;
67
+ headingLevel?: HeadingLevel;
68
+ headingButtons?: ReactNode;
61
69
  }
62
- const RelatedArticleList = ({ messages, children, articleCount, dangerouslySetInnerHTML, headingLevel }: Props) => {
63
- const clonedChildren =
64
- !dangerouslySetInnerHTML && children
65
- ? Children.map(children, (article, i) =>
66
- cloneElement(article, {
67
- modifier: i >= 2 ? `${article.props.modifier} hidden` : article.props.modifier,
68
- }),
69
- )
70
- : null;
71
- const childrenCount = articleCount || Children.count(children);
70
+ export const RelatedArticleList = ({
71
+ children = [],
72
+ articleCount,
73
+ headingLevel = 'h3',
74
+ headingButtons,
75
+ ...rest
76
+ }: Props) => {
77
+ const [expanded, setExpanded] = useState(false);
78
+ const { t } = useTranslation();
79
+ const childCount = useMemo(() => articleCount ?? Children.count(children), [children, articleCount]);
80
+ const childrenToShow = useMemo(
81
+ () => (childCount > 2 && !expanded ? children?.slice(0, 2) : children),
82
+ [childCount, children, expanded],
83
+ );
72
84
 
73
85
  return (
74
- <section {...classes('')}>
75
- <SectionHeading headingLevel={headingLevel} className={classes('component-title').className}>
76
- {messages.title}
77
- </SectionHeading>
78
- <div {...classes('articles')} dangerouslySetInnerHTML={dangerouslySetInnerHTML}>
79
- {clonedChildren}
80
- </div>
81
- {childrenCount > 2 && (
82
- <ButtonV2
83
- data-type="related-article-button"
84
- data-showmore={messages.showMore}
85
- data-showless={messages.showLess}
86
- variant="outline"
87
- >
88
- {messages.showMore}
86
+ <section {...classes('')} {...rest}>
87
+ <HeadingWrapper>
88
+ <SectionHeading headingLevel={headingLevel} className={classes('component-title').className}>
89
+ {t('related.title')}
90
+ </SectionHeading>
91
+ {headingButtons}
92
+ </HeadingWrapper>
93
+ <div {...classes('articles')}>{childrenToShow}</div>
94
+ {childCount > 2 ? (
95
+ <ButtonV2 onClick={() => setExpanded((p) => !p)} variant="outline">
96
+ {t(`related.show${expanded ? 'Less' : 'More'}`)}
89
97
  </ButtonV2>
90
- )}
98
+ ) : null}
91
99
  </section>
92
100
  );
93
101
  };
94
-
95
- export default RelatedArticleList;
@@ -6,9 +6,8 @@
6
6
  *
7
7
  */
8
8
 
9
- import RelatedArticleList, { RelatedArticle } from './RelatedArticleList';
10
- import { RelatedArticleListV2 } from './RelatedArticleV2';
9
+ import { RelatedArticle, RelatedArticleList } from './RelatedArticleList';
11
10
 
12
- export { RelatedArticle, RelatedArticleListV2 };
11
+ export { RelatedArticle };
13
12
 
14
13
  export default RelatedArticleList;
@@ -11,6 +11,5 @@ import SearchField from './SearchField';
11
11
  import { SearchResultList, SearchResultItem } from './SearchResult';
12
12
 
13
13
  import ActiveFilters from './ActiveFilters';
14
- export { default as ToggleSearchButton } from './ToggleSearchButton';
15
14
 
16
15
  export { SearchField, SearchResultItem, SearchResultList, ActiveFilters };
package/src/i18n/i18n.ts CHANGED
@@ -21,13 +21,14 @@ const DETECTION_OPTIONS = {
21
21
  lookupLocalStorage: 'i18nextLng',
22
22
  };
23
23
 
24
+ export const supportedTranslationLanguages = ['nb', 'nn', 'en', 'se', 'sma'] as const;
24
25
  const i18nInstance = i18n.use(initReactI18next).use(LanguageDetector);
25
26
 
26
27
  i18nInstance.init({
27
28
  compatibilityJSON: 'v3',
28
29
  detection: DETECTION_OPTIONS,
29
30
  fallbackLng: 'nb',
30
- supportedLngs: ['nb', 'nn', 'en', 'se', 'sma'],
31
+ supportedLngs: supportedTranslationLanguages,
31
32
  resources: {
32
33
  en: {
33
34
  translation: messagesEN,