@ndla/ui 36.0.1 → 36.0.2

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 (57) hide show
  1. package/es/BlogPost/BlogPost.js +4 -4
  2. package/es/CampaignBlock/CampaignBlock.js +77 -0
  3. package/es/CampaignBlock/index.js +9 -0
  4. package/es/ContactBlock/ContactBlock.js +63 -39
  5. package/es/Embed/BrightcoveEmbed.js +3 -3
  6. package/es/LicenseByline/EmbedByline.js +90 -0
  7. package/es/LicenseByline/LicenseDescription.js +37 -0
  8. package/es/LicenseByline/LicenseLink.js +36 -0
  9. package/es/LicenseByline/index.js +1 -0
  10. package/es/locale/messages-en.js +8 -1
  11. package/es/locale/messages-nb.js +8 -1
  12. package/es/locale/messages-nn.js +8 -1
  13. package/es/locale/messages-se.js +8 -1
  14. package/es/locale/messages-sma.js +8 -1
  15. package/lib/BlogPost/BlogPost.js +4 -4
  16. package/lib/CampaignBlock/CampaignBlock.d.ts +31 -0
  17. package/lib/CampaignBlock/CampaignBlock.js +82 -0
  18. package/lib/CampaignBlock/index.d.ts +8 -0
  19. package/lib/CampaignBlock/index.js +13 -0
  20. package/lib/ContactBlock/ContactBlock.js +63 -39
  21. package/lib/Embed/BrightcoveEmbed.js +3 -3
  22. package/lib/LicenseByline/EmbedByline.d.ts +43 -0
  23. package/lib/LicenseByline/EmbedByline.js +95 -0
  24. package/lib/LicenseByline/LicenseDescription.d.ts +12 -0
  25. package/lib/LicenseByline/LicenseDescription.js +43 -0
  26. package/lib/LicenseByline/LicenseLink.d.ts +14 -0
  27. package/lib/LicenseByline/LicenseLink.js +44 -0
  28. package/lib/LicenseByline/index.d.ts +1 -0
  29. package/lib/LicenseByline/index.js +13 -0
  30. package/lib/locale/messages-en.d.ts +7 -0
  31. package/lib/locale/messages-en.js +8 -1
  32. package/lib/locale/messages-nb.d.ts +7 -0
  33. package/lib/locale/messages-nb.js +8 -1
  34. package/lib/locale/messages-nn.d.ts +7 -0
  35. package/lib/locale/messages-nn.js +8 -1
  36. package/lib/locale/messages-se.d.ts +7 -0
  37. package/lib/locale/messages-se.js +8 -1
  38. package/lib/locale/messages-sma.d.ts +7 -0
  39. package/lib/locale/messages-sma.js +8 -1
  40. package/package.json +2 -2
  41. package/src/BlogPost/BlogPost.tsx +0 -4
  42. package/src/CampaignBlock/CampaignBlock.stories.tsx +63 -0
  43. package/src/CampaignBlock/CampaignBlock.tsx +99 -0
  44. package/src/CampaignBlock/index.ts +9 -0
  45. package/src/ContactBlock/ContactBlock.tsx +27 -19
  46. package/src/ContactBlock/Contactblock.stories.tsx +0 -1
  47. package/src/Embed/BrightcoveEmbed.tsx +1 -1
  48. package/src/LicenseByline/EmbedByline.stories.tsx +82 -0
  49. package/src/LicenseByline/EmbedByline.tsx +117 -0
  50. package/src/LicenseByline/LicenseDescription.tsx +37 -0
  51. package/src/LicenseByline/LicenseLink.tsx +42 -0
  52. package/src/LicenseByline/index.tsx +1 -0
  53. package/src/locale/messages-en.ts +7 -0
  54. package/src/locale/messages-nb.ts +7 -0
  55. package/src/locale/messages-nn.ts +7 -0
  56. package/src/locale/messages-se.ts +7 -0
  57. package/src/locale/messages-sma.ts +7 -0
@@ -0,0 +1,99 @@
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 styled from '@emotion/styled';
11
+ import { css } from '@emotion/react';
12
+ import SafeLink from '@ndla/safelink';
13
+ import { Forward } from '@ndla/icons/common';
14
+ import { breakpoints, colors, fonts, spacing, mq, misc } from '@ndla/core';
15
+ import { HeadingLevel } from '../types';
16
+
17
+ interface Image {
18
+ src: string;
19
+ alt: string;
20
+ }
21
+ interface Props {
22
+ title: {
23
+ title: string;
24
+ language: string;
25
+ };
26
+ description: {
27
+ text: string;
28
+ language: string;
29
+ };
30
+ headingLevel?: HeadingLevel;
31
+ url: {
32
+ url: string;
33
+ text: string;
34
+ };
35
+ imageBefore?: Image;
36
+ imageAfter?: Image;
37
+ }
38
+
39
+ const Container = styled.div`
40
+ max-width: 390px;
41
+ display: flex;
42
+ flex-direction: column;
43
+ gap: ${spacing.xsmall};
44
+ border: 1px ${colors.brand.lighter} solid;
45
+ border-radius: ${misc.borderRadius};
46
+ padding: ${spacing.normal} ${spacing.small};
47
+ ${mq.range({ from: breakpoints.tabletWide })} {
48
+ max-width: 1100px;
49
+ flex-direction: row;
50
+ }
51
+ `;
52
+
53
+ const headingStyle = css`
54
+ margin: 0;
55
+ `;
56
+
57
+ const StyledDescription = styled.p`
58
+ font-family: ${fonts.serif};
59
+ margin: ${spacing.normal} 0 ${spacing.medium};
60
+ `;
61
+
62
+ const StyledImg = styled.img`
63
+ max-height: 200px;
64
+ ${mq.range({ until: breakpoints.tabletWide })} {
65
+ align-self: center;
66
+ }
67
+ ${mq.range({ from: breakpoints.tabletWide })} {
68
+ align-self: center;
69
+ }
70
+ `;
71
+
72
+ const StyledLink = styled(SafeLink)`
73
+ box-shadow: none;
74
+ text-decoration: underline;
75
+ color: ${colors.brand.primary};
76
+ &:hover,
77
+ &:focus-visible {
78
+ text-decoration: none;
79
+ }
80
+ `;
81
+
82
+ const CampaignBlock = ({ title, imageBefore, description, headingLevel: Heading = 'h2', imageAfter, url }: Props) => {
83
+ return (
84
+ <Container>
85
+ {imageBefore && <StyledImg src={imageBefore.src} data-left={true} />}
86
+ <div>
87
+ <Heading css={headingStyle}>{title.title}</Heading>
88
+ <StyledDescription>{description.text}</StyledDescription>
89
+ <StyledLink to={url.url}>
90
+ {url.text}
91
+ <Forward />
92
+ </StyledLink>
93
+ </div>
94
+ {imageAfter && <StyledImg src={imageAfter.src} data-right={true} />}
95
+ </Container>
96
+ );
97
+ };
98
+
99
+ export default CampaignBlock;
@@ -0,0 +1,9 @@
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
+ export { default as CampaignBlock } from './CampaignBlock';
@@ -25,16 +25,16 @@ interface Props {
25
25
  name: string;
26
26
  email: string;
27
27
  }
28
- const CardWrapper = styled.div`
28
+ const BlockWrapper = styled.div`
29
29
  display: flex;
30
30
  flex-direction: column;
31
31
  padding: 0 0 ${spacing.medium} ${spacing.medium};
32
32
  font-family: ${fonts.sans};
33
- justify-content: center;
34
33
  border-radius: ${misc.borderRadius};
35
34
  border: 1px solid ${colors.brand.lighter};
36
- margin-top: ${spacing.mediumlarge};
35
+ max-width: 348px;
37
36
  ${mq.range({ from: breakpoints.tabletWide })} {
37
+ max-width: 773px;
38
38
  flex-direction: row;
39
39
  padding: 0 0 ${spacing.medium} ${spacing.medium};
40
40
  gap: ${spacing.nsmall};
@@ -48,12 +48,13 @@ const StyledHeader = styled.div`
48
48
  padding-top: ${spacing.medium};
49
49
  `;
50
50
 
51
- const StyledDescriptionInformation = styled.div`
51
+ const StyledText = styled.div`
52
52
  display: flex;
53
53
  overflow-wrap: anywhere;
54
54
  color: ${colors.text.light};
55
55
  gap: ${spacing.xxsmall};
56
56
  `;
57
+
57
58
  const EmailLink = styled.a`
58
59
  color: ${colors.text.light};
59
60
  text-decoration-color: ${colors.text.light};
@@ -70,7 +71,7 @@ const SummaryBlock = styled.p`
70
71
  }
71
72
  `;
72
73
 
73
- const ContentWrapper = styled.div`
74
+ const TextWrapper = styled.div`
74
75
  display: flex;
75
76
  overflow: hidden;
76
77
  justify-content: space-between;
@@ -82,18 +83,16 @@ const BlobWrapper = styled.div`
82
83
  `;
83
84
 
84
85
  const ImageWrapper = styled.div`
85
- aspect-ratio: 16/9;
86
86
  display: flex;
87
87
  flex-direction: column;
88
88
  gap: ${spacing.small};
89
89
  padding: ${spacing.medium} ${spacing.medium} 0 0;
90
-
91
90
  ${mq.range({ from: breakpoints.tabletWide })} {
92
91
  padding-right: 0;
93
92
  }
94
93
  `;
95
94
 
96
- const blobStyle = css`
95
+ const blobStyling = css`
97
96
  width: 165px;
98
97
  height: 180px;
99
98
  transform: translate(10%, 0);
@@ -101,6 +100,15 @@ const blobStyle = css`
101
100
  const Email = styled.div`
102
101
  min-width: 60px;
103
102
  `;
103
+
104
+ const ContentWrapper = styled.div`
105
+ width: 100%;
106
+ `;
107
+
108
+ const StyledImage = styled.img`
109
+ object-fit: cover;
110
+ `;
111
+
104
112
  const ContactBlock = ({ image, jobTitle, description, name, email, blobColor = 'green', blob = 'pointy' }: Props) => {
105
113
  const { t } = useTranslation();
106
114
  const isGreenBlob = blobColor === 'green';
@@ -108,11 +116,11 @@ const ContactBlock = ({ image, jobTitle, description, name, email, blobColor = '
108
116
  const authors = concat(image?.copyright.processors, image?.copyright.creators, image?.copyright.rightsholders);
109
117
 
110
118
  return (
111
- <CardWrapper>
119
+ <BlockWrapper>
112
120
  <ImageWrapper>
113
121
  {image ? (
114
122
  <>
115
- <img alt={image.alttext.alttext} src={`${image.image.imageUrl}?width=286`} />
123
+ <StyledImage alt={image.alttext.alttext} src={`${image.image.imageUrl}?width=286`} />
116
124
  {`${t('photo')}: ${authors.reduce((acc, name) => (acc = `${acc} ${name?.name}`), '')} ${
117
125
  image.copyright.license.license
118
126
  }`}
@@ -121,23 +129,23 @@ const ContactBlock = ({ image, jobTitle, description, name, email, blobColor = '
121
129
  <img alt={t('image.error.url')} src={errorSvgSrc} />
122
130
  )}
123
131
  </ImageWrapper>
124
- <div>
125
- <ContentWrapper>
132
+ <ContentWrapper>
133
+ <TextWrapper>
126
134
  <div>
127
135
  <StyledHeader>{name}</StyledHeader>
128
- <StyledDescriptionInformation>{jobTitle}</StyledDescriptionInformation>
129
- <StyledDescriptionInformation>
136
+ <StyledText>{jobTitle}</StyledText>
137
+ <StyledText>
130
138
  <Email>{`${t('email')}:`}</Email>
131
139
  <EmailLink href={`mailto:${email}?subject=Contact us`}>{email}</EmailLink>
132
- </StyledDescriptionInformation>
140
+ </StyledText>
133
141
  </div>
134
142
  <BlobWrapper>
135
- <Blob css={blobStyle} color={isGreenBlob ? colors.support.greenLight : colors.support.redLight} />
143
+ <Blob css={blobStyling} color={isGreenBlob ? colors.support.greenLight : colors.support.redLight} />
136
144
  </BlobWrapper>
137
- </ContentWrapper>
145
+ </TextWrapper>
138
146
  <SummaryBlock>{description}</SummaryBlock>
139
- </div>
140
- </CardWrapper>
147
+ </ContentWrapper>
148
+ </BlockWrapper>
141
149
  );
142
150
  };
143
151
 
@@ -77,7 +77,6 @@ export default {
77
77
  },
78
78
  parameters: {
79
79
  ...defaultParameters,
80
- layout: 'centered',
81
80
  },
82
81
  } as Meta<typeof ContactBlock>;
83
82
 
@@ -144,7 +144,7 @@ const BrightcoveEmbed = ({ embed, isConcept }: Props) => {
144
144
  size="small"
145
145
  onClick={() => setShowOriginalVideo((p) => !p)}
146
146
  >
147
- {t(`figure.button.${showOriginalVideo ? 'original' : 'alternative'}`)}
147
+ {t(`figure.button.${!showOriginalVideo ? 'original' : 'alternative'}`)}
148
148
  </LinkedVideoButton>
149
149
  }
150
150
  licenseRights={license.rights}
@@ -0,0 +1,82 @@
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, StoryFn } from '@storybook/react';
11
+ import styled from '@emotion/styled';
12
+ import { breakpoints, mq, spacing } from '@ndla/core';
13
+ import { IconButtonV2 } from '@ndla/button';
14
+ import { Switch } from '@ndla/switch';
15
+ import { HeartOutline } from '@ndla/icons/action';
16
+ import EmbedByline from './EmbedByline';
17
+ import { defaultParameters } from '../../../../stories/defaults';
18
+
19
+ const ButtonWrapper = styled.div`
20
+ display: flex;
21
+ gap: ${spacing.small};
22
+ ${mq.range({ until: breakpoints.tablet })} {
23
+ flex: 1;
24
+ width: 100%;
25
+ justify-content: space-between;
26
+ align-items: space-between;
27
+ }
28
+ `;
29
+
30
+ export default {
31
+ title: 'Enkle komponenter/EmbedByline',
32
+ component: EmbedByline,
33
+ tags: ['autodocs'],
34
+ parameters: {
35
+ ...defaultParameters,
36
+ },
37
+ args: {
38
+ topRounded: true,
39
+ visibleAlt: 'Synlig alt-tekst kan legges her, eller fjernes helt',
40
+ type: 'image',
41
+ children: (
42
+ <ButtonWrapper>
43
+ <Switch checked={false} label="Bytt til synstolket video" onChange={() => {}} id="switch" />
44
+ <IconButtonV2 variant="ghost" aria-label="Legg til i favoritter">
45
+ <HeartOutline />
46
+ </IconButtonV2>
47
+ </ButtonWrapper>
48
+ ),
49
+ description:
50
+ 'Bildetekst som kan være ganske lang. Denne roboten er laget av DALLE2, en helt vaskeekte AI. Hvis denne teksten blir veldig lang kommer den på flere linjer.',
51
+ copyright: {
52
+ license: {
53
+ license: 'CC-BY-SA-4.0',
54
+ description: 'Creative Commons Attribution-ShareAlike 4.0 International',
55
+ url: 'https://creativecommons.org/licenses/by-sa/4.0/',
56
+ },
57
+ origin: 'http://floradania.dk/forside/',
58
+ creators: [],
59
+ processors: [],
60
+ rightsholders: [
61
+ {
62
+ type: 'Supplier',
63
+ name: 'Floradania',
64
+ },
65
+ ],
66
+ },
67
+ },
68
+ argTypes: {
69
+ children: {
70
+ control: {
71
+ type: null,
72
+ },
73
+ },
74
+ },
75
+ } as Meta<typeof EmbedByline>;
76
+
77
+ export const EmbedBylineStory: StoryFn<typeof EmbedByline> = (args) => {
78
+ const { children, ...rest } = args;
79
+ return <EmbedByline {...rest}>{children}</EmbedByline>;
80
+ };
81
+
82
+ EmbedBylineStory.storyName = 'EmbedByline';
@@ -0,0 +1,117 @@
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 { ReactNode } from 'react';
10
+ import { useTranslation } from 'react-i18next';
11
+ import styled from '@emotion/styled';
12
+ import { breakpoints, colors, misc, mq, spacing } from '@ndla/core';
13
+ import { getLicenseByAbbreviation, getLicenseCredits } from '@ndla/licenses';
14
+ import { ICopyright as ImageCopyright } from '@ndla/types-backend/image-api';
15
+ import { ICopyright as AudioCopyright } from '@ndla/types-backend/audio-api';
16
+ import { ICopyright as ConceptCopyright } from '@ndla/types-backend/concept-api';
17
+ import { BrightcoveCopyright } from '@ndla/types-embed';
18
+ import LicenseLink from './LicenseLink';
19
+ import LicenseDescription from './LicenseDescription';
20
+
21
+ interface BaseProps {
22
+ topRounded?: boolean;
23
+ description?: string;
24
+ children?: ReactNode;
25
+ visibleAlt?: string;
26
+ }
27
+
28
+ interface ImageProps extends BaseProps {
29
+ type: 'image';
30
+ copyright: ImageCopyright;
31
+ }
32
+
33
+ interface BrightcoveProps extends BaseProps {
34
+ type: 'video';
35
+ copyright: BrightcoveCopyright;
36
+ }
37
+
38
+ interface AudioProps extends BaseProps {
39
+ type: 'audio';
40
+ copyright: AudioCopyright;
41
+ }
42
+
43
+ interface PodcastProps extends BaseProps {
44
+ type: 'podcast';
45
+ copyright: AudioCopyright;
46
+ }
47
+
48
+ interface ConceptProps extends BaseProps {
49
+ type: 'concept';
50
+ copyright: ConceptCopyright;
51
+ }
52
+
53
+ type Props = ImageProps | BrightcoveProps | AudioProps | PodcastProps | ConceptProps;
54
+
55
+ export type LicenseType = ReturnType<typeof getLicenseByAbbreviation>;
56
+
57
+ const BylineWrapper = styled.div`
58
+ display: flex;
59
+ flex-direction: column;
60
+ gap: ${spacing.small};
61
+ background-color: ${colors.brand.lightest};
62
+ padding: ${spacing.small} ${spacing.normal};
63
+ border: 1px solid ${colors.brand.tertiary};
64
+ border-bottom-right-radius: ${misc.borderRadius};
65
+ border-bottom-left-radius: ${misc.borderRadius};
66
+
67
+ &[data-top-rounded='true'] {
68
+ border-radius: ${misc.borderRadius};
69
+ }
70
+ `;
71
+
72
+ const RightsWrapper = styled.div`
73
+ display: flex;
74
+ align-items: center;
75
+ gap: ${spacing.nsmall};
76
+
77
+ ${mq.range({ until: breakpoints.tablet })} {
78
+ align-items: flex-start;
79
+ gap: ${spacing.xsmall};
80
+ flex-direction: column;
81
+ }
82
+ `;
83
+
84
+ const StyledSpan = styled.span`
85
+ font-style: italic;
86
+ color: grey;
87
+ `;
88
+
89
+ const LicenseInformationWrapper = styled.div`
90
+ flex: 1;
91
+ `;
92
+
93
+ const EmbedByline = ({ type, topRounded, description, copyright, children, visibleAlt }: Props) => {
94
+ const { t, i18n } = useTranslation();
95
+ const license = getLicenseByAbbreviation(copyright.license?.license ?? '', i18n.language);
96
+ const authors = getLicenseCredits(copyright);
97
+ const captionAuthors = Object.values(authors).find((i) => i.length > 0) ?? [];
98
+
99
+ return (
100
+ <BylineWrapper data-top-rounded={topRounded}>
101
+ {!!description && <LicenseDescription description={description} />}
102
+ {visibleAlt ? <StyledSpan>{`Alt: ${visibleAlt}`}</StyledSpan> : null}
103
+ <RightsWrapper>
104
+ <LicenseLink license={license} asLink={!!license.url.length} />
105
+ <LicenseInformationWrapper>
106
+ <span>
107
+ <b>{t(`embed.type.${type}`)}: </b>
108
+ {captionAuthors.map((author) => author.name).join(', ')}
109
+ </span>
110
+ </LicenseInformationWrapper>
111
+ {children}
112
+ </RightsWrapper>
113
+ </BylineWrapper>
114
+ );
115
+ };
116
+
117
+ export default EmbedByline;
@@ -0,0 +1,37 @@
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 styled from '@emotion/styled';
10
+ import { colors, spacing } from '@ndla/core';
11
+
12
+ interface Props {
13
+ description: string;
14
+ }
15
+
16
+ const StyledParagraph = styled.p`
17
+ margin: 0;
18
+ padding-bottom: ${spacing.xsmall};
19
+ border-bottom: 1px solid ${colors.brand.tertiary};
20
+ `;
21
+
22
+ const StyledFigCaption = styled.figcaption`
23
+ background: unset;
24
+ padding: unset;
25
+ font-size: unset;
26
+ color: unset;
27
+ `;
28
+
29
+ const LicenseDescription = ({ description }: Props) => {
30
+ return (
31
+ <StyledFigCaption>
32
+ <StyledParagraph>{description}</StyledParagraph>
33
+ </StyledFigCaption>
34
+ );
35
+ };
36
+
37
+ export default LicenseDescription;
@@ -0,0 +1,42 @@
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 styled from '@emotion/styled';
10
+ import SafeLink from '@ndla/safelink';
11
+ import { colors, fonts } from '@ndla/core';
12
+ import { LicenseType } from './EmbedByline';
13
+
14
+ interface Props {
15
+ license: LicenseType;
16
+ asLink?: boolean;
17
+ }
18
+
19
+ const StyledSafeLink = styled(SafeLink)`
20
+ color: ${colors.brand.primary};
21
+ font-weight: ${fonts.weight.bold};
22
+ text-decoration: underline;
23
+ box-shadow: none;
24
+ &:hover,
25
+ &:focus-within {
26
+ text-decoration: none;
27
+ }
28
+ `;
29
+
30
+ const StyledSpan = styled.span`
31
+ font-weight: ${fonts.weight.bold};
32
+ `;
33
+
34
+ const LicenseLink = ({ license, asLink = true }: Props) => {
35
+ if (asLink) {
36
+ return <StyledSafeLink to={license.url}>{license.abbreviation}</StyledSafeLink>;
37
+ } else {
38
+ return <StyledSpan>{license.abbreviation}</StyledSpan>;
39
+ }
40
+ };
41
+
42
+ export default LicenseLink;
@@ -0,0 +1 @@
1
+ export { default as EmbedByline } from './EmbedByline';
@@ -1305,6 +1305,13 @@ const messages = {
1305
1305
  conceptListError: 'Failed to show concept list',
1306
1306
  linkError: 'Failed to show link.',
1307
1307
  unsupported: `Embed {{type}} not supported.`,
1308
+ type: {
1309
+ image: 'Image',
1310
+ video: 'Video',
1311
+ audio: 'Audio',
1312
+ podcast: 'Podcast',
1313
+ concept: 'Concept',
1314
+ },
1308
1315
  },
1309
1316
  };
1310
1317
 
@@ -1301,6 +1301,13 @@ const messages = {
1301
1301
  conceptListError: 'Klarte ikke å vise forklaringsliste',
1302
1302
  linkError: 'Klarte ikke å vise lenke.',
1303
1303
  unsupported: `Embed {{type}} er ikke støttet.`,
1304
+ type: {
1305
+ image: 'Bilde',
1306
+ video: 'Video',
1307
+ audio: 'Lyd',
1308
+ podcast: 'Podkast',
1309
+ concept: 'Forklaring',
1310
+ },
1304
1311
  },
1305
1312
  };
1306
1313
 
@@ -1301,6 +1301,13 @@ const messages = {
1301
1301
  conceptListError: 'Klarte ikkje å vise forklaringsliste',
1302
1302
  linkError: 'Klarte ikkje å vise lenke.',
1303
1303
  unsupported: `Embed {{type}} er ikkje støtta.`,
1304
+ type: {
1305
+ image: 'Bilde',
1306
+ video: 'Video',
1307
+ audio: 'Lyd',
1308
+ podcast: 'Podkast',
1309
+ concept: 'Forklaring',
1310
+ },
1304
1311
  },
1305
1312
  };
1306
1313
 
@@ -1302,6 +1302,13 @@ const messages = {
1302
1302
  conceptListError: 'Ii sáhttán čájehit čilgehuslisttu',
1303
1303
  linkError: 'Ii sáhttán čájehit liŋkka.',
1304
1304
  unsupported: `Embed {{type}} ii dorjojuvvo.`,
1305
+ type: {
1306
+ image: 'Bilde',
1307
+ video: 'Video',
1308
+ audio: 'Lyd',
1309
+ podcast: 'Podkast',
1310
+ concept: 'Forklaring',
1311
+ },
1305
1312
  },
1306
1313
  };
1307
1314
 
@@ -1306,6 +1306,13 @@ const messages = {
1306
1306
  conceptListError: 'Klarte ikkje å vise forklaringsliste',
1307
1307
  linkError: 'Klarte ikkje å vise lenke.',
1308
1308
  unsupported: `Embed {{type}} er ikkje støtta.`,
1309
+ type: {
1310
+ image: 'Bilde',
1311
+ video: 'Video',
1312
+ audio: 'Lyd',
1313
+ podcast: 'Podkast',
1314
+ concept: 'Forklaring',
1315
+ },
1309
1316
  },
1310
1317
  };
1311
1318