@ndla/ui 47.3.1 → 47.4.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 (77) hide show
  1. package/es/CampaignBlock/CampaignBlock.js +8 -6
  2. package/es/ContactBlock/ContactBlock.js +17 -14
  3. package/es/CopyParagraphButton/CopyParagraphButtonV2.js +5 -3
  4. package/es/Embed/ConceptEmbed.js +22 -13
  5. package/es/Embed/ConceptListEmbed.js +6 -3
  6. package/es/Embed/ImageEmbed.js +5 -3
  7. package/es/Embed/conceptComponents.js +14 -11
  8. package/es/Footer/Footer.js +11 -9
  9. package/es/Footer/FooterLinks.js +19 -34
  10. package/es/FrontpageArticle/FrontpageArticle.js +5 -2
  11. package/es/Image/Image.js +8 -5
  12. package/es/KeyFigure/KeyFigure.js +8 -5
  13. package/es/Notion/Notion.js +7 -5
  14. package/es/Table/Table.js +6 -6
  15. package/es/locale/messages-en.js +1 -0
  16. package/es/locale/messages-nb.js +1 -0
  17. package/es/locale/messages-nn.js +2 -1
  18. package/es/locale/messages-se.js +1 -0
  19. package/es/locale/messages-sma.js +1 -0
  20. package/lib/CampaignBlock/CampaignBlock.js +8 -6
  21. package/lib/ContactBlock/ContactBlock.d.ts +2 -1
  22. package/lib/ContactBlock/ContactBlock.js +17 -14
  23. package/lib/CopyParagraphButton/CopyParagraphButtonV2.d.ts +2 -1
  24. package/lib/CopyParagraphButton/CopyParagraphButtonV2.js +5 -3
  25. package/lib/Embed/ConceptEmbed.d.ts +4 -3
  26. package/lib/Embed/ConceptEmbed.js +22 -13
  27. package/lib/Embed/ConceptListEmbed.d.ts +2 -1
  28. package/lib/Embed/ConceptListEmbed.js +6 -3
  29. package/lib/Embed/ImageEmbed.d.ts +2 -1
  30. package/lib/Embed/ImageEmbed.js +5 -3
  31. package/lib/Embed/conceptComponents.d.ts +1 -0
  32. package/lib/Embed/conceptComponents.js +14 -11
  33. package/lib/Footer/Footer.d.ts +6 -1
  34. package/lib/Footer/Footer.js +11 -9
  35. package/lib/Footer/FooterLinks.d.ts +7 -2
  36. package/lib/Footer/FooterLinks.js +19 -34
  37. package/lib/FrontpageArticle/FrontpageArticle.d.ts +2 -1
  38. package/lib/FrontpageArticle/FrontpageArticle.js +5 -2
  39. package/lib/Image/Image.d.ts +2 -1
  40. package/lib/Image/Image.js +8 -5
  41. package/lib/KeyFigure/KeyFigure.d.ts +2 -1
  42. package/lib/KeyFigure/KeyFigure.js +8 -5
  43. package/lib/Notion/Notion.d.ts +2 -1
  44. package/lib/Notion/Notion.js +7 -5
  45. package/lib/Table/Table.d.ts +1 -0
  46. package/lib/Table/Table.js +6 -6
  47. package/lib/locale/messages-en.d.ts +1 -0
  48. package/lib/locale/messages-en.js +1 -0
  49. package/lib/locale/messages-nb.d.ts +1 -0
  50. package/lib/locale/messages-nb.js +1 -0
  51. package/lib/locale/messages-nn.d.ts +1 -0
  52. package/lib/locale/messages-nn.js +2 -1
  53. package/lib/locale/messages-se.d.ts +1 -0
  54. package/lib/locale/messages-se.js +1 -0
  55. package/lib/locale/messages-sma.d.ts +1 -0
  56. package/lib/locale/messages-sma.js +1 -0
  57. package/package.json +2 -2
  58. package/src/CampaignBlock/CampaignBlock.tsx +4 -2
  59. package/src/ContactBlock/ContactBlock.tsx +13 -3
  60. package/src/CopyParagraphButton/CopyParagraphButtonV2.tsx +3 -2
  61. package/src/Embed/ConceptEmbed.tsx +10 -1
  62. package/src/Embed/ConceptListEmbed.tsx +4 -3
  63. package/src/Embed/ImageEmbed.tsx +3 -1
  64. package/src/Embed/conceptComponents.tsx +4 -2
  65. package/src/Footer/Footer.stories.tsx +22 -0
  66. package/src/Footer/Footer.tsx +24 -18
  67. package/src/Footer/FooterLinks.tsx +17 -24
  68. package/src/FrontpageArticle/FrontpageArticle.tsx +4 -3
  69. package/src/Image/Image.tsx +11 -2
  70. package/src/KeyFigure/KeyFigure.tsx +4 -3
  71. package/src/Notion/Notion.tsx +3 -2
  72. package/src/Table/Table.tsx +1 -0
  73. package/src/locale/messages-en.ts +1 -0
  74. package/src/locale/messages-nb.ts +1 -0
  75. package/src/locale/messages-nn.ts +2 -1
  76. package/src/locale/messages-se.ts +1 -0
  77. package/src/locale/messages-sma.ts +1 -0
@@ -35,6 +35,7 @@ export interface ConceptNotionData {
35
35
  visualElement?: ConceptVisualElementMeta;
36
36
  conceptType: ConceptData['concept']['conceptType'];
37
37
  glossData?: ConceptData['concept']['glossData'];
38
+ lang?: string;
38
39
  }
39
40
 
40
41
  interface ConceptNotionProps extends RefAttributes<HTMLDivElement>, ConceptNotionData {
@@ -179,6 +180,7 @@ export const ConceptNotionV2 = forwardRef<HTMLDivElement, ConceptNotionProps>(
179
180
  conceptType,
180
181
  glossData,
181
182
  headerButtons,
183
+ lang,
182
184
  ...rest
183
185
  },
184
186
  ref,
@@ -202,7 +204,7 @@ export const ConceptNotionV2 = forwardRef<HTMLDivElement, ConceptNotionProps>(
202
204
  <>
203
205
  <StyledNotionDialogContent>
204
206
  {visualElement?.resource === 'image' ? (
205
- <ImageEmbed embed={visualElement} heartButton={heartButton} />
207
+ <ImageEmbed embed={visualElement} heartButton={heartButton} lang={lang} />
206
208
  ) : visualElement?.resource === 'brightcove' ? (
207
209
  <BrightcoveEmbed embed={visualElement} heartButton={heartButton} />
208
210
  ) : visualElement?.resource === 'h5p' ? (
@@ -212,7 +214,7 @@ export const ConceptNotionV2 = forwardRef<HTMLDivElement, ConceptNotionProps>(
212
214
  ) : visualElement?.resource === 'external' ? (
213
215
  <ExternalEmbed embed={visualElement} />
214
216
  ) : null}
215
- {content && <NotionDialogText>{content}</NotionDialogText>}
217
+ {content && <NotionDialogText lang={lang}>{content}</NotionDialogText>}
216
218
  </StyledNotionDialogContent>
217
219
  {tags && (
218
220
  <ListWrapper>
@@ -14,6 +14,24 @@ import { FooterText } from './FooterText';
14
14
  import { EditorName } from './EditorName';
15
15
  import { LanguageSelector } from '../LanguageSelector';
16
16
 
17
+ const mockCommonLinks = [
18
+ {
19
+ to: 'https://ndla.no/about/om-ndla',
20
+ text: 'Om NDLA',
21
+ external: false,
22
+ },
23
+ {
24
+ to: 'https://ndla.no/about/about-ndla',
25
+ text: 'About NDLA',
26
+ external: false,
27
+ },
28
+ {
29
+ to: 'https://blogg.ndla.no/',
30
+ text: 'NDLA fagblogg',
31
+ external: true,
32
+ },
33
+ ];
34
+
17
35
  const mockFooterLinks = [
18
36
  {
19
37
  to: 'https://www.facebook.com/ndla.no',
@@ -84,6 +102,7 @@ export const Default: StoryObj<typeof Footer> = {};
84
102
  export const WithContentAndLinks: StoryObj<typeof Footer> = {
85
103
  args: {
86
104
  privacyLinks: privacyLinks,
105
+ commonLinks: mockCommonLinks,
87
106
  links: mockFooterLinks,
88
107
  },
89
108
  };
@@ -91,6 +110,7 @@ export const WithContentAndLinks: StoryObj<typeof Footer> = {
91
110
  export const WithoutContent: StoryObj<typeof Footer> = {
92
111
  args: {
93
112
  children: undefined,
113
+ commonLinks: mockCommonLinks,
94
114
  links: mockFooterLinks,
95
115
  privacyLinks: privacyLinks,
96
116
  },
@@ -99,6 +119,7 @@ export const WithoutContent: StoryObj<typeof Footer> = {
99
119
  export const WithLanguageSelector: StoryObj<typeof Footer> = {
100
120
  args: {
101
121
  privacyLinks: privacyLinks,
122
+ commonLinks: mockCommonLinks,
102
123
  links: mockFooterLinks,
103
124
  // eslint-disable-next-line no-console
104
125
  languageSelector: <LanguageSelector inverted locales={['nn', 'nb']} onSelect={console.log} />,
@@ -108,6 +129,7 @@ export const WithLanguageSelector: StoryObj<typeof Footer> = {
108
129
  export const WithAuthBlock: StoryObj<typeof Footer> = {
109
130
  args: {
110
131
  privacyLinks: privacyLinks,
132
+ commonLinks: mockCommonLinks,
111
133
  links: mockFooterLinks,
112
134
  // eslint-disable-next-line no-console
113
135
  languageSelector: <LanguageSelector inverted locales={['nn', 'nb']} onSelect={console.log} />,
@@ -113,6 +113,11 @@ const StyledLanguageWrapper = styled.div`
113
113
  type Props = {
114
114
  children: ReactNode;
115
115
  lang: Locale;
116
+ commonLinks?: {
117
+ to: string;
118
+ text: string;
119
+ external: boolean;
120
+ }[];
116
121
  links?: {
117
122
  to: string;
118
123
  text: string;
@@ -126,7 +131,7 @@ type Props = {
126
131
  auth?: ReactNode;
127
132
  };
128
133
 
129
- const Footer = ({ children, links, languageSelector, auth, privacyLinks }: Props) => {
134
+ const Footer = ({ children, commonLinks, links, languageSelector, auth, privacyLinks }: Props) => {
130
135
  const { t } = useTranslation();
131
136
 
132
137
  const mainContent = (
@@ -136,23 +141,24 @@ const Footer = ({ children, links, languageSelector, auth, privacyLinks }: Props
136
141
  </>
137
142
  );
138
143
 
139
- const footerContent = links ? (
140
- <>
141
- <StyledColumns>
142
- <div>
143
- <StyledFooterHeaderIcon />
144
- </div>
145
- <div>
146
- <StyledHeader>{t('footer.vision')}</StyledHeader>
147
- <FooterLinks links={links} />
148
- </div>
149
- </StyledColumns>
150
- <StyledHr />
151
- {mainContent}
152
- </>
153
- ) : (
154
- mainContent
155
- );
144
+ const footerContent =
145
+ links || commonLinks ? (
146
+ <>
147
+ <StyledColumns>
148
+ <div>
149
+ <StyledFooterHeaderIcon />
150
+ </div>
151
+ <div>
152
+ <StyledHeader>{t('footer.vision')}</StyledHeader>
153
+ <FooterLinks commonLinks={commonLinks} links={links} />
154
+ </div>
155
+ </StyledColumns>
156
+ <StyledHr />
157
+ {mainContent}
158
+ </>
159
+ ) : (
160
+ mainContent
161
+ );
156
162
 
157
163
  return (
158
164
  <>
@@ -26,24 +26,18 @@ const StyledLinksWrapper = styled.div`
26
26
  `;
27
27
 
28
28
  type FooterLinksProps = {
29
- links: {
29
+ commonLinks?: {
30
+ to: string;
31
+ text: string;
32
+ external: boolean;
33
+ }[];
34
+ links?: {
30
35
  to: string;
31
36
  text: string;
32
37
  icon: ReactNode;
33
38
  }[];
34
39
  };
35
40
 
36
- const commonLinks = [
37
- { key: 'omNdla', url: 'https://om.ndla.no' },
38
- {
39
- key: 'aboutNdla',
40
- url: 'https://om.ndla.no/about-ndla',
41
- },
42
- { key: 'blog', url: 'https://blogg.ndla.no' },
43
- { key: 'tips', url: 'https://blogg.ndla.no/elever' },
44
- { key: 'vacancies', url: 'https://om.ndla.no/jobb-for-ndla/' },
45
- ];
46
-
47
41
  const StyledNav = styled.nav`
48
42
  display: flex;
49
43
  flex-direction: column;
@@ -89,33 +83,32 @@ const StyledHeaderLinks = styled.h3`
89
83
  margin: ${spacing.xsmall} 0;
90
84
  `;
91
85
 
92
- const FooterLinks = ({ links }: FooterLinksProps) => {
86
+ const FooterLinks = ({ links, commonLinks }: FooterLinksProps) => {
93
87
  const { t } = useTranslation();
94
88
  return (
95
89
  <>
96
90
  <StyledLinksWrapper>
97
91
  <div>
98
- <StyledHeaderLinks id="otherLinks">
99
- {t('footer.linksHeader')} <Launch />
100
- </StyledHeaderLinks>
92
+ <StyledHeaderLinks id="otherLinks">{t('footer.linksHeader')}</StyledHeaderLinks>
101
93
  <StyledNav aria-labelledby="otherLinks">
102
- {commonLinks.map((link) => (
103
- <div key={link.url}>
94
+ {commonLinks?.map((link) => (
95
+ <div key={link.to}>
104
96
  <StyledSafeLink
105
- key={t(`footer.ndlaLinks.${link.key}`)}
106
- aria-label={t(`footer.ndlaLinks.${link.key}`)}
107
- to={link.url}
108
- target="_blank"
97
+ key={link.text}
98
+ aria-label={link.text}
99
+ to={link.to}
100
+ target={link.external ? '_blank' : ''}
109
101
  rel="noopener noreferrer"
110
102
  >
111
- {t(`footer.ndlaLinks.${link.key}`)}
103
+ {link.text}
104
+ {link.external && <Launch />}
112
105
  </StyledSafeLink>
113
106
  </div>
114
107
  ))}
115
108
  </StyledNav>
116
109
  </div>
117
110
  <StyledNav aria-label={t('footer.socialMedia')}>
118
- {links.map((link) => (
111
+ {links?.map((link) => (
119
112
  <StyledSocialMediaLinkWrapper key={link.to}>
120
113
  <StyledSocialMediaIcon>{link.icon}</StyledSocialMediaIcon>
121
114
  <StyledSafeLink to={link.to}>
@@ -19,6 +19,7 @@ interface Props {
19
19
  id: string;
20
20
  isWide?: boolean;
21
21
  licenseBox?: ReactNode;
22
+ lang?: string;
22
23
  }
23
24
 
24
25
  export const FRONTPAGE_ARTICLE_MAX_WIDTH = '773px';
@@ -56,7 +57,7 @@ const StyledArticle = styled.article`
56
57
  }
57
58
  `;
58
59
 
59
- export const FrontpageArticle = ({ article, id, isWide, licenseBox }: Props) => {
60
+ export const FrontpageArticle = ({ article, id, isWide, licenseBox, lang }: Props) => {
60
61
  const { height = 0 } = useMastheadHeight();
61
62
  const cssVars = useMemo(() => ({ '--masthead-height': `${height}px` } as unknown as CSSProperties), [height]);
62
63
  const { title, introduction, content } = article;
@@ -71,10 +72,10 @@ export const FrontpageArticle = ({ article, id, isWide, licenseBox }: Props) =>
71
72
 
72
73
  return (
73
74
  <StyledArticle style={cssVars}>
74
- <Heading id={id} headingStyle="h1-resource" element="h1" margin="normal" tabIndex={-1}>
75
+ <Heading id={id} headingStyle="h1-resource" element="h1" margin="normal" tabIndex={-1} lang={lang}>
75
76
  {title}
76
77
  </Heading>
77
- <Text textStyle="ingress" element="div">
78
+ <Text textStyle="ingress" element="div" lang={lang}>
78
79
  {introduction}
79
80
  </Text>
80
81
  {content}
@@ -78,6 +78,7 @@ interface Props {
78
78
  crop?: ImageCrop;
79
79
  focalPoint?: ImageFocalPoint;
80
80
  border?: string;
81
+ lang?: string;
81
82
  }
82
83
 
83
84
  const Image = ({
@@ -92,6 +93,7 @@ const Image = ({
92
93
  expandButton,
93
94
  fallbackWidth = 1024,
94
95
  border,
96
+ lang,
95
97
  ...rest
96
98
  }: Props) => {
97
99
  const srcSet = rest.srcSet ?? getSrcSet(src, crop, focalPoint);
@@ -101,7 +103,7 @@ const Image = ({
101
103
  if (contentType && contentType === 'image/gif') {
102
104
  return (
103
105
  <StyledImageWrapper data-border={border}>
104
- <StyledImage alt={alt} loading={loading} src={`${src}`} {...rest} data-border={border} />
106
+ <StyledImage alt={alt} loading={loading} src={`${src}`} {...rest} data-border={border} lang={lang} />
105
107
  </StyledImageWrapper>
106
108
  );
107
109
  }
@@ -110,7 +112,14 @@ const Image = ({
110
112
  <StyledImageWrapper data-svg={contentType === 'image/svg+xml'} data-border={border}>
111
113
  <picture>
112
114
  <source type={contentType} srcSet={srcSet} sizes={sizes} />
113
- <StyledImage alt={alt} loading={loading} src={`${src}?${queryString}`} {...rest} data-border={border} />
115
+ <StyledImage
116
+ alt={alt}
117
+ loading={loading}
118
+ src={`${src}?${queryString}`}
119
+ {...rest}
120
+ data-border={border}
121
+ lang={lang}
122
+ />
114
123
  </picture>
115
124
  {expandButton}
116
125
  </StyledImageWrapper>
@@ -59,14 +59,15 @@ interface Props {
59
59
  };
60
60
  title: string;
61
61
  subtitle: string;
62
+ lang?: string;
62
63
  }
63
64
 
64
- const KeyFigure = ({ image, title, subtitle }: Props) => {
65
+ const KeyFigure = ({ image, title, subtitle, lang }: Props) => {
65
66
  return (
66
67
  <ContentWrapper>
67
68
  <StyledImage src={image?.src} width={150} height={150} alt={image?.alt} />
68
- <TitleWrapper>{title}</TitleWrapper>
69
- <SubTitleWrapper>{subtitle}</SubTitleWrapper>
69
+ <TitleWrapper lang={lang}>{title}</TitleWrapper>
70
+ <SubTitleWrapper lang={lang}>{subtitle}</SubTitleWrapper>
70
71
  </ContentWrapper>
71
72
  );
72
73
  };
@@ -71,9 +71,10 @@ export type NotionProps = {
71
71
  visualElement: ReactNode;
72
72
  imageElement?: ReactNode;
73
73
  children?: ReactNode;
74
+ lang?: string;
74
75
  };
75
76
 
76
- const Notion = ({ id, labels = [], text, title, visualElement, imageElement, children }: NotionProps) => {
77
+ const Notion = ({ id, labels = [], text, title, visualElement, imageElement, children, lang }: NotionProps) => {
77
78
  const { t } = useTranslation();
78
79
 
79
80
  return (
@@ -81,7 +82,7 @@ const Notion = ({ id, labels = [], text, title, visualElement, imageElement, chi
81
82
  <ContentWrapper>
82
83
  {imageElement}
83
84
  {visualElement}
84
- <TextWrapper hasVisualElement={!!(imageElement || visualElement)}>
85
+ <TextWrapper hasVisualElement={!!(imageElement || visualElement)} lang={lang}>
85
86
  <b>{title.trim()}</b>
86
87
  {text}
87
88
  {!!labels.length && (
@@ -22,6 +22,7 @@ interface Props {
22
22
  dangerouslySetInnerHTML?: {
23
23
  __html: string;
24
24
  };
25
+ lang?: string;
25
26
  }
26
27
 
27
28
  const ScrollBorder = styled.div`
@@ -1209,6 +1209,7 @@ const messages = {
1209
1209
  listView: 'List view',
1210
1210
  detailView: 'Detailed listview',
1211
1211
  shortView: 'Card view',
1212
+ userPictureAltText: 'Profile picture',
1212
1213
  sharedFolder: {
1213
1214
  folderCopied: 'The folder was copied.',
1214
1215
  info: 'This folder contains learning resources and tasks from NDLA, gathered by a teacher.',
@@ -1206,6 +1206,7 @@ const messages = {
1206
1206
  listView: 'Listevisning',
1207
1207
  detailView: 'Detaljert listevisning',
1208
1208
  shortView: 'Kort visning',
1209
+ userPictureAltText: 'Profilbilde',
1209
1210
  sharedFolder: {
1210
1211
  folderCopied: 'Mappen har blitt kopiert.',
1211
1212
  info: 'Denne mappa inneholder fagstoff og oppgaver fra NDLA, samlet av en lærer. ',
@@ -516,7 +516,7 @@ const messages = {
516
516
  associatedTopics: 'Tilhøyrande emne',
517
517
  },
518
518
  subjectFrontPage: {
519
- buildsOn: 'Byggjer på',
519
+ buildsOn: 'Bygger på',
520
520
  connectedTo: 'Felles programfag saman med',
521
521
  leadsTo: 'Leier til',
522
522
  },
@@ -1206,6 +1206,7 @@ const messages = {
1206
1206
  listView: 'Listevisning',
1207
1207
  detailView: 'Detaljert listevisning',
1208
1208
  shortView: 'Kortvisning',
1209
+ userPictureAltText: 'Profilbilete',
1209
1210
  sharedFolder: {
1210
1211
  folderCopied: 'Mappa vart kopiert.',
1211
1212
  info: 'Denne mappa inneheld fagstoff og oppgåver frå NDLA, samla av ein lærar.',
@@ -1206,6 +1206,7 @@ const messages = {
1206
1206
  listView: 'Oppalašlistu',
1207
1207
  detailView: 'Bienalaš oppalašlistu',
1208
1208
  shortView: 'Oanehis listu',
1209
+ userPictureAltText: 'Profiilagova',
1209
1210
  sharedFolder: {
1210
1211
  folderCopied: 'Máŋgejuvvon máhppa.',
1211
1212
  info: 'Dán máhpas lea NDLA fágasisdoallu ja bargobihtát, čohkkejuvvon oahpaheaddjis.',
@@ -1211,6 +1211,7 @@ const messages = {
1211
1211
  listView: 'Listevisning',
1212
1212
  detailView: 'Detaljert listevisning',
1213
1213
  shortView: 'Kort visning',
1214
+ userPictureAltText: 'Profijleguvviem',
1214
1215
  sharedFolder: {
1215
1216
  folderCopied: 'Mappen har blitt kopiert.',
1216
1217
  info: 'Denne mappa inneheld fagstoff og oppgåver frå NDLA, samla av ein lærar.',