@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.
- package/es/Article/Article.js +7 -13
- package/es/Article/ArticleByline.js +79 -123
- package/es/Article/ArticleFootNotes.js +16 -11
- package/es/AudioPlayer/AudioPlayer.js +33 -35
- package/es/AudioPlayer/initAudioPlayers.js +6 -1
- package/es/BlogPost/BlogPost.js +4 -4
- package/es/CampaignBlock/CampaignBlock.js +77 -0
- package/es/CampaignBlock/index.js +9 -0
- package/es/ContactBlock/ContactBlock.js +63 -39
- package/es/Embed/AudioEmbed.js +44 -188
- package/es/Embed/BrightcoveEmbed.js +27 -123
- package/es/Embed/ConceptEmbed.js +53 -75
- package/es/Embed/EmbedErrorPlaceholder.js +41 -0
- package/es/Embed/ExternalEmbed.js +5 -12
- package/es/Embed/H5pEmbed.js +4 -14
- package/es/Embed/IframeEmbed.js +4 -4
- package/es/Embed/ImageEmbed.js +41 -153
- package/es/Embed/conceptComponents.js +62 -228
- package/es/Embed/types.js +1 -0
- package/es/KeyFigure/KeyFigure.js +57 -0
- package/es/{KeyPerformanceIndicator → KeyFigure}/index.js +1 -1
- package/es/LicenseByline/EmbedByline.js +115 -0
- package/es/LicenseByline/LicenseDescription.js +39 -0
- package/es/LicenseByline/LicenseLink.js +36 -0
- package/es/LicenseByline/index.js +1 -0
- package/es/List/OrderedList.js +48 -0
- package/es/List/UnOrderedList.js +36 -0
- package/es/List/index.js +10 -0
- package/es/Navigation/NavigationBox.js +41 -48
- package/es/Navigation/NavigationHeading.js +18 -29
- package/es/Notion/Notion.js +5 -5
- package/es/Resource/resourceComponents.js +12 -11
- package/es/Typography/Heading.js +38 -0
- package/es/Typography/index.js +9 -0
- package/es/all.css +1 -1
- package/es/index.js +4 -2
- package/es/locale/messages-en.js +13 -2
- package/es/locale/messages-nb.js +13 -2
- package/es/locale/messages-nn.js +13 -2
- package/es/locale/messages-se.js +13 -2
- package/es/locale/messages-sma.js +13 -2
- package/es/model/ContentType.js +7 -1
- package/lib/Article/Article.d.ts +1 -3
- package/lib/Article/Article.js +7 -13
- package/lib/Article/ArticleByline.d.ts +3 -5
- package/lib/Article/ArticleByline.js +83 -126
- package/lib/Article/ArticleFootNotes.js +16 -11
- package/lib/AudioPlayer/AudioPlayer.d.ts +1 -2
- package/lib/AudioPlayer/AudioPlayer.js +33 -36
- package/lib/AudioPlayer/initAudioPlayers.d.ts +1 -0
- package/lib/AudioPlayer/initAudioPlayers.js +9 -3
- package/lib/BlogPost/BlogPost.js +4 -4
- package/lib/CampaignBlock/CampaignBlock.d.ts +31 -0
- package/lib/CampaignBlock/CampaignBlock.js +82 -0
- package/lib/CampaignBlock/index.d.ts +8 -0
- package/lib/CampaignBlock/index.js +13 -0
- package/lib/ContactBlock/ContactBlock.js +63 -39
- package/lib/Embed/AudioEmbed.d.ts +3 -2
- package/lib/Embed/AudioEmbed.js +53 -192
- package/lib/Embed/BrightcoveEmbed.d.ts +3 -1
- package/lib/Embed/BrightcoveEmbed.js +27 -122
- package/lib/Embed/ConceptEmbed.d.ts +7 -2
- package/lib/Embed/ConceptEmbed.js +51 -73
- package/lib/Embed/EmbedErrorPlaceholder.d.ts +17 -0
- package/lib/Embed/EmbedErrorPlaceholder.js +48 -0
- package/lib/Embed/ExternalEmbed.js +5 -11
- package/lib/Embed/H5pEmbed.js +4 -13
- package/lib/Embed/IframeEmbed.d.ts +2 -2
- package/lib/Embed/IframeEmbed.js +4 -4
- package/lib/Embed/ImageEmbed.d.ts +3 -10
- package/lib/Embed/ImageEmbed.js +48 -161
- package/lib/Embed/conceptComponents.d.ts +4 -2
- package/lib/Embed/conceptComponents.js +67 -231
- package/lib/Embed/index.d.ts +1 -0
- package/lib/Embed/types.d.ts +14 -0
- package/lib/Embed/types.js +5 -0
- package/lib/KeyFigure/KeyFigure.d.ts +10 -0
- package/lib/KeyFigure/KeyFigure.js +62 -0
- package/lib/KeyFigure/index.d.ts +1 -0
- package/lib/KeyFigure/index.js +13 -0
- package/lib/LicenseByline/EmbedByline.d.ts +51 -0
- package/lib/LicenseByline/EmbedByline.js +120 -0
- package/lib/LicenseByline/LicenseDescription.d.ts +14 -0
- package/lib/LicenseByline/LicenseDescription.js +44 -0
- package/lib/LicenseByline/LicenseLink.d.ts +14 -0
- package/lib/LicenseByline/LicenseLink.js +44 -0
- package/lib/LicenseByline/index.d.ts +1 -0
- package/lib/LicenseByline/index.js +13 -0
- package/lib/List/OrderedList.d.ts +15 -0
- package/lib/List/OrderedList.js +56 -0
- package/lib/List/UnOrderedList.d.ts +10 -0
- package/lib/List/UnOrderedList.js +43 -0
- package/lib/List/index.d.ts +9 -0
- package/lib/List/index.js +20 -0
- package/lib/Navigation/NavigationBox.js +40 -47
- package/lib/Navigation/NavigationHeading.js +17 -28
- package/lib/Notion/Notion.js +5 -5
- package/lib/Resource/resourceComponents.js +12 -11
- package/lib/Typography/Heading.d.ts +26 -0
- package/lib/Typography/Heading.js +45 -0
- package/lib/Typography/index.d.ts +8 -0
- package/lib/Typography/index.js +13 -0
- package/lib/all.css +1 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +23 -3
- package/lib/locale/messages-en.d.ts +11 -0
- package/lib/locale/messages-en.js +13 -2
- package/lib/locale/messages-nb.d.ts +11 -0
- package/lib/locale/messages-nb.js +13 -2
- package/lib/locale/messages-nn.d.ts +11 -0
- package/lib/locale/messages-nn.js +13 -2
- package/lib/locale/messages-se.d.ts +11 -0
- package/lib/locale/messages-se.js +13 -2
- package/lib/locale/messages-sma.d.ts +11 -0
- package/lib/locale/messages-sma.js +13 -2
- package/lib/model/ContentType.d.ts +1 -0
- package/lib/model/ContentType.js +9 -2
- package/package.json +15 -15
- package/src/Article/Article.tsx +1 -8
- package/src/Article/ArticleByline.tsx +78 -127
- package/src/Article/ArticleFootNotes.tsx +33 -10
- package/src/Article/component.article.scss +1 -52
- package/src/Article/component.footnotes.scss +2 -2
- package/src/Aside/component.aside.scss +3 -3
- package/src/AudioPlayer/AudioPlayer.tsx +11 -24
- package/src/AudioPlayer/initAudioPlayers.tsx +7 -2
- package/src/BlogPost/BlogPost.tsx +0 -4
- package/src/CampaignBlock/CampaignBlock.stories.tsx +63 -0
- package/src/CampaignBlock/CampaignBlock.tsx +99 -0
- package/src/CampaignBlock/index.ts +9 -0
- package/src/ContactBlock/ContactBlock.tsx +27 -19
- package/src/ContactBlock/Contactblock.stories.tsx +0 -1
- package/src/Dialog/component.dialog.scss +4 -5
- package/src/Embed/AudioEmbed.stories.tsx +5 -3
- package/src/Embed/AudioEmbed.tsx +45 -192
- package/src/Embed/BrightcoveEmbed.stories.tsx +5 -1
- package/src/Embed/BrightcoveEmbed.tsx +20 -95
- package/src/Embed/ConceptEmbed.stories.tsx +5 -0
- package/src/Embed/ConceptEmbed.tsx +43 -54
- package/src/Embed/EmbedErrorPlaceholder.tsx +59 -0
- package/src/Embed/ExternalEmbed.stories.tsx +86 -0
- package/src/Embed/ExternalEmbed.tsx +3 -8
- package/src/Embed/H5pEmbed.stories.tsx +92 -0
- package/src/Embed/H5pEmbed.tsx +2 -10
- package/src/Embed/IframeEmbed.stories.tsx +130 -0
- package/src/Embed/IframeEmbed.tsx +3 -3
- package/src/Embed/ImageEmbed.stories.tsx +3 -1
- package/src/Embed/ImageEmbed.tsx +21 -116
- package/src/Embed/conceptComponents.tsx +67 -257
- package/src/Embed/index.ts +1 -0
- package/src/Embed/types.ts +12 -0
- package/src/FactBox/component.factbox.scss +3 -3
- package/src/Figure/component.figure-license.scss +4 -4
- package/src/Figure/component.figure.scss +1 -1
- package/src/KeyFigure/KeyFigure.stories.tsx +36 -0
- package/src/{KeyPerformanceIndicator/KeyPerformanceIndicator.tsx → KeyFigure/KeyFigure.tsx} +9 -7
- package/src/{KeyPerformanceIndicator → KeyFigure}/index.ts +1 -1
- package/src/LicenseByline/EmbedByline.stories.tsx +83 -0
- package/src/LicenseByline/EmbedByline.tsx +165 -0
- package/src/LicenseByline/LicenseDescription.tsx +43 -0
- package/src/LicenseByline/LicenseLink.tsx +42 -0
- package/src/LicenseByline/index.tsx +1 -0
- package/src/List/OrderedList.tsx +115 -0
- package/src/List/UnOrderedList.tsx +49 -0
- package/src/List/index.ts +10 -0
- package/src/MediaList/component.medialist.scss +2 -2
- package/src/Navigation/NavigationBox.tsx +10 -14
- package/src/Navigation/NavigationHeading.tsx +15 -24
- package/src/Notion/Notion.tsx +1 -1
- package/src/RelatedArticleList/component.related-articles.scss +3 -13
- package/src/Resource/resourceComponents.tsx +4 -2
- package/src/Table/component.tables.scss +0 -46
- package/src/Translation/component.translation.scss +3 -5
- package/src/Typography/Heading.tsx +96 -0
- package/src/Typography/index.ts +9 -0
- package/src/index.ts +5 -1
- package/src/locale/messages-en.ts +11 -0
- package/src/locale/messages-nb.ts +11 -0
- package/src/locale/messages-nn.ts +11 -0
- package/src/locale/messages-se.ts +11 -0
- package/src/locale/messages-sma.ts +11 -0
- package/src/model/ContentType.ts +7 -0
- package/es/KeyPerformanceIndicator/KeyPerformanceIndicator.js +0 -57
- package/lib/KeyPerformanceIndicator/KeyPerformanceIndicator.d.ts +0 -8
- package/lib/KeyPerformanceIndicator/KeyPerformanceIndicator.js +0 -62
- package/lib/KeyPerformanceIndicator/index.d.ts +0 -1
- package/lib/KeyPerformanceIndicator/index.js +0 -13
- package/src/KeyPerformanceIndicator/KeyPerformanceIndicator.stories.tsx +0 -79
|
@@ -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;
|
|
@@ -25,16 +25,16 @@ interface Props {
|
|
|
25
25
|
name: string;
|
|
26
26
|
email: string;
|
|
27
27
|
}
|
|
28
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
<
|
|
119
|
+
<BlockWrapper>
|
|
112
120
|
<ImageWrapper>
|
|
113
121
|
{image ? (
|
|
114
122
|
<>
|
|
115
|
-
<
|
|
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
|
-
<
|
|
125
|
-
<
|
|
132
|
+
<ContentWrapper>
|
|
133
|
+
<TextWrapper>
|
|
126
134
|
<div>
|
|
127
135
|
<StyledHeader>{name}</StyledHeader>
|
|
128
|
-
<
|
|
129
|
-
<
|
|
136
|
+
<StyledText>{jobTitle}</StyledText>
|
|
137
|
+
<StyledText>
|
|
130
138
|
<Email>{`${t('email')}:`}</Email>
|
|
131
139
|
<EmailLink href={`mailto:${email}?subject=Contact us`}>{email}</EmailLink>
|
|
132
|
-
</
|
|
140
|
+
</StyledText>
|
|
133
141
|
</div>
|
|
134
142
|
<BlobWrapper>
|
|
135
|
-
<Blob css={
|
|
143
|
+
<Blob css={blobStyling} color={isGreenBlob ? colors.support.greenLight : colors.support.redLight} />
|
|
136
144
|
</BlobWrapper>
|
|
137
|
-
</
|
|
145
|
+
</TextWrapper>
|
|
138
146
|
<SummaryBlock>{description}</SummaryBlock>
|
|
139
|
-
</
|
|
140
|
-
</
|
|
147
|
+
</ContentWrapper>
|
|
148
|
+
</BlockWrapper>
|
|
141
149
|
);
|
|
142
150
|
};
|
|
143
151
|
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
}
|
|
18
18
|
&:not(&--large, &--fullscreen) {
|
|
19
19
|
h1 {
|
|
20
|
-
@include
|
|
20
|
+
@include font-size(22px, 26px);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
&--small-heading {
|
|
24
24
|
h1 {
|
|
25
|
-
@include
|
|
25
|
+
@include font-size(22px, 26px);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
@include mq(tablet) {
|
|
46
46
|
min-width: 20rem;
|
|
47
47
|
}
|
|
48
|
-
@include font-size(18px);
|
|
48
|
+
@include font-size(18px, 1.33);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
&--active &__content {
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
background: transparent;
|
|
91
91
|
border: none;
|
|
92
92
|
color: $brand-color;
|
|
93
|
-
@include font-size(
|
|
93
|
+
@include font-size(18px, 1.33);
|
|
94
94
|
font-weight: $font-weight-normal;
|
|
95
95
|
padding: 0;
|
|
96
96
|
box-shadow: $link;
|
|
@@ -124,7 +124,6 @@
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
|
|
128
127
|
&--fullscreen &__content {
|
|
129
128
|
@include mq(tablet) {
|
|
130
129
|
height: 100vh;
|
|
@@ -11,6 +11,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
|
|
11
11
|
import { AudioEmbedData, AudioMeta } from '@ndla/types-embed';
|
|
12
12
|
import AudioEmbed from './AudioEmbed';
|
|
13
13
|
import { defaultParameters } from '../../../../stories/defaults';
|
|
14
|
+
import StoryFavoriteButton from '../../../../stories/StoryFavoriteButton';
|
|
14
15
|
|
|
15
16
|
const embedData: AudioEmbedData = {
|
|
16
17
|
resource: 'audio',
|
|
@@ -156,9 +157,6 @@ const meta: Meta<typeof AudioEmbed> = {
|
|
|
156
157
|
title: 'Enkle komponenter/Embeds/AudioEmbed',
|
|
157
158
|
component: AudioEmbed,
|
|
158
159
|
tags: ['autodocs'],
|
|
159
|
-
args: {
|
|
160
|
-
articlePath: undefined,
|
|
161
|
-
},
|
|
162
160
|
decorators: [
|
|
163
161
|
(Story) => (
|
|
164
162
|
<div className="o-wrapper">
|
|
@@ -179,6 +177,7 @@ export default meta;
|
|
|
179
177
|
|
|
180
178
|
export const AudioEmbedStory: StoryObj<typeof AudioEmbed> = {
|
|
181
179
|
args: {
|
|
180
|
+
heartButton: StoryFavoriteButton,
|
|
182
181
|
embed: {
|
|
183
182
|
resource: 'audio',
|
|
184
183
|
status: 'success',
|
|
@@ -191,6 +190,7 @@ export const AudioEmbedStory: StoryObj<typeof AudioEmbed> = {
|
|
|
191
190
|
|
|
192
191
|
export const AudioEmbedFailed: StoryObj<typeof AudioEmbed> = {
|
|
193
192
|
args: {
|
|
193
|
+
heartButton: StoryFavoriteButton,
|
|
194
194
|
embed: {
|
|
195
195
|
resource: 'audio',
|
|
196
196
|
status: 'error',
|
|
@@ -202,6 +202,7 @@ export const AudioEmbedFailed: StoryObj<typeof AudioEmbed> = {
|
|
|
202
202
|
|
|
203
203
|
export const Podcast: StoryObj<typeof AudioEmbed> = {
|
|
204
204
|
args: {
|
|
205
|
+
heartButton: StoryFavoriteButton,
|
|
205
206
|
embed: {
|
|
206
207
|
resource: 'audio',
|
|
207
208
|
status: 'success',
|
|
@@ -214,6 +215,7 @@ export const Podcast: StoryObj<typeof AudioEmbed> = {
|
|
|
214
215
|
|
|
215
216
|
export const PodcastFailed: StoryObj<typeof AudioEmbed> = {
|
|
216
217
|
args: {
|
|
218
|
+
heartButton: StoryFavoriteButton,
|
|
217
219
|
embed: {
|
|
218
220
|
resource: 'audio',
|
|
219
221
|
status: 'error',
|
package/src/Embed/AudioEmbed.tsx
CHANGED
|
@@ -6,30 +6,21 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { AudioMetaData } from '@ndla/types-embed';
|
|
10
|
-
import { ICopyright } from '@ndla/types-backend/image-api';
|
|
11
|
-
import {
|
|
12
|
-
figureApa7CopyString,
|
|
13
|
-
getGroupedContributorDescriptionList,
|
|
14
|
-
getLicenseByAbbreviation,
|
|
15
|
-
getLicenseCredits,
|
|
16
|
-
} from '@ndla/licenses';
|
|
17
|
-
import { ModalV2 } from '@ndla/modal';
|
|
18
|
-
import { useState } from 'react';
|
|
19
|
-
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { AudioMetaData, ImageMetaData } from '@ndla/types-embed';
|
|
20
10
|
//@ts-ignore
|
|
21
11
|
import { Remarkable } from 'remarkable';
|
|
22
|
-
import { ButtonV2, CopyButton } from '@ndla/button';
|
|
23
|
-
import { SafeLinkButton } from '@ndla/safelink';
|
|
24
12
|
import AudioPlayer from '../AudioPlayer';
|
|
25
|
-
import { Figure
|
|
26
|
-
import { FigureLicenseDialogContent } from '../Figure/FigureLicenseDialogContent';
|
|
13
|
+
import { Figure } from '../Figure';
|
|
27
14
|
import { Author } from './ImageEmbed';
|
|
15
|
+
import { EmbedByline } from '../LicenseByline';
|
|
16
|
+
import EmbedErrorPlaceholder from './EmbedErrorPlaceholder';
|
|
17
|
+
import { HeartButtonType } from './types';
|
|
28
18
|
|
|
29
19
|
interface Props {
|
|
30
20
|
embed: AudioMetaData;
|
|
31
|
-
|
|
21
|
+
heartButton?: HeartButtonType;
|
|
32
22
|
}
|
|
23
|
+
|
|
33
24
|
export const getFirstNonEmptyLicenseCredits = (authors: {
|
|
34
25
|
creators: Author[];
|
|
35
26
|
rightsholders: Author[];
|
|
@@ -42,29 +33,25 @@ const renderMarkdown = (text: string) => {
|
|
|
42
33
|
return <span dangerouslySetInnerHTML={{ __html: rendered }} />;
|
|
43
34
|
};
|
|
44
35
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
const imageMetaToMockEmbed = (
|
|
37
|
+
imageMeta: Extract<AudioMetaData, { status: 'success' }>,
|
|
38
|
+
): Extract<ImageMetaData, { status: 'success' }> => ({
|
|
39
|
+
resource: 'image',
|
|
40
|
+
status: 'success',
|
|
41
|
+
// Make sure the seq is unused. It's rarely used, but it's nice to ensure uniqueness.
|
|
42
|
+
seq: imageMeta.seq + 0.1,
|
|
43
|
+
// We check that this exists where the function is used.
|
|
44
|
+
data: imageMeta.data.imageMeta!,
|
|
45
|
+
embedData: {
|
|
46
|
+
resource: 'image',
|
|
47
|
+
resourceId: imageMeta.data.imageMeta?.id?.toString() || '',
|
|
48
|
+
alt: imageMeta.data.imageMeta?.alttext.alttext ?? '',
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const AudioEmbed = ({ embed, heartButton: HeartButton }: Props) => {
|
|
48
53
|
if (embed.status === 'error') {
|
|
49
|
-
return
|
|
50
|
-
<Figure>
|
|
51
|
-
<svg
|
|
52
|
-
fill="#8A8888"
|
|
53
|
-
height="50"
|
|
54
|
-
viewBox="0 0 24 12"
|
|
55
|
-
width="100%"
|
|
56
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
57
|
-
style={{ backgroundColor: '#EFF0F2' }}
|
|
58
|
-
>
|
|
59
|
-
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
60
|
-
<path
|
|
61
|
-
transform="scale(0.3) translate(28, 8.5)"
|
|
62
|
-
d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
|
|
63
|
-
/>
|
|
64
|
-
</svg>
|
|
65
|
-
<figcaption>{t('audio.error.caption')}</figcaption>
|
|
66
|
-
</Figure>
|
|
67
|
-
);
|
|
54
|
+
return <EmbedErrorPlaceholder type={embed.embedData.type === 'standard' ? 'audio' : 'podcast'} />;
|
|
68
55
|
}
|
|
69
56
|
|
|
70
57
|
const { data, embedData, seq } = embed;
|
|
@@ -75,181 +62,47 @@ const AudioEmbed = ({ embed, articlePath }: Props) => {
|
|
|
75
62
|
|
|
76
63
|
const subtitle = data.series ? { title: data.series.title.title, url: `/podkast/${data.series.id}` } : undefined;
|
|
77
64
|
|
|
78
|
-
const textVersion = data.manuscript
|
|
79
|
-
const description = renderMarkdown(data.podcastMeta?.introduction ?? '');
|
|
65
|
+
const textVersion = data.manuscript?.manuscript.length ? renderMarkdown(data.manuscript.manuscript) : undefined;
|
|
80
66
|
|
|
81
67
|
const coverPhoto = data.podcastMeta?.coverPhoto;
|
|
82
68
|
|
|
83
69
|
const img = coverPhoto && { url: coverPhoto.url, alt: coverPhoto.altText };
|
|
84
70
|
|
|
85
|
-
const authors = getLicenseCredits(data.copyright);
|
|
86
|
-
|
|
87
|
-
const license = getLicenseByAbbreviation(data.copyright.license.license, i18n.language);
|
|
88
|
-
|
|
89
|
-
const contributors = getGroupedContributorDescriptionList(data.copyright, i18n.language).map((item) => ({
|
|
90
|
-
name: item.description,
|
|
91
|
-
type: item.label,
|
|
92
|
-
}));
|
|
93
|
-
|
|
94
71
|
const figureId = `figure-${seq}-${data.id}`;
|
|
95
72
|
|
|
96
|
-
const copyString =
|
|
97
|
-
data.audioType === 'podcast'
|
|
98
|
-
? figureApa7CopyString(
|
|
99
|
-
data.title.title,
|
|
100
|
-
undefined,
|
|
101
|
-
data.audioFile.url,
|
|
102
|
-
articlePath,
|
|
103
|
-
data.copyright,
|
|
104
|
-
data.copyright.license.license,
|
|
105
|
-
'',
|
|
106
|
-
(id: string) => t(id),
|
|
107
|
-
i18n.language,
|
|
108
|
-
)
|
|
109
|
-
: undefined;
|
|
110
|
-
const captionAuthors = getFirstNonEmptyLicenseCredits(authors);
|
|
111
73
|
return (
|
|
112
74
|
<Figure id={figureId} type="full">
|
|
113
75
|
<AudioPlayer
|
|
114
|
-
description={
|
|
76
|
+
description={data.podcastMeta?.introduction ?? ''}
|
|
115
77
|
img={img}
|
|
116
78
|
src={data.audioFile.url}
|
|
117
79
|
textVersion={textVersion}
|
|
118
80
|
title={data.title.title}
|
|
119
81
|
subtitle={subtitle}
|
|
120
82
|
/>
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
authors={captionAuthors}
|
|
131
|
-
locale={i18n.language}
|
|
132
|
-
/>
|
|
133
|
-
<ModalV2 controlled isOpen={isOpen} onClose={() => setIsOpen(false)} labelledBy="license-dialog-rules-heading">
|
|
134
|
-
{(close) => (
|
|
135
|
-
<FigureLicenseDialogContent
|
|
136
|
-
onClose={close}
|
|
137
|
-
title={data.title.title}
|
|
138
|
-
license={license}
|
|
139
|
-
authors={contributors}
|
|
140
|
-
origin={data.copyright.origin}
|
|
141
|
-
locale={i18n.language}
|
|
142
|
-
type="audio"
|
|
143
|
-
>
|
|
144
|
-
{data.copyright.license.license !== 'COPYRIGHT' && (
|
|
145
|
-
<>
|
|
146
|
-
{copyString && (
|
|
147
|
-
<CopyButton
|
|
148
|
-
variant="outline"
|
|
149
|
-
copyNode={t('license.hasCopiedTitle')}
|
|
150
|
-
onClick={() => navigator.clipboard.writeText(copyString)}
|
|
151
|
-
>
|
|
152
|
-
{t('license.copyTitle')}
|
|
153
|
-
</CopyButton>
|
|
154
|
-
)}
|
|
155
|
-
<SafeLinkButton download to={data.audioFile.url} variant="outline">
|
|
156
|
-
{t('audio.download')}
|
|
157
|
-
</SafeLinkButton>
|
|
158
|
-
</>
|
|
159
|
-
)}
|
|
160
|
-
</FigureLicenseDialogContent>
|
|
161
|
-
)}
|
|
162
|
-
</ModalV2>
|
|
83
|
+
<EmbedByline
|
|
84
|
+
error={false}
|
|
85
|
+
type={embedData.type === 'standard' ? 'audio' : 'podcast'}
|
|
86
|
+
topRounded={false}
|
|
87
|
+
bottomRounded={!data.imageMeta}
|
|
88
|
+
copyright={embed.data.copyright}
|
|
89
|
+
>
|
|
90
|
+
{HeartButton && <HeartButton embed={embed} />}
|
|
91
|
+
</EmbedByline>
|
|
163
92
|
{data.imageMeta && (
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
93
|
+
<EmbedByline
|
|
94
|
+
error={false}
|
|
95
|
+
first={false}
|
|
96
|
+
type="image"
|
|
97
|
+
topRounded={false}
|
|
98
|
+
bottomRounded
|
|
167
99
|
copyright={data.imageMeta.copyright}
|
|
168
|
-
|
|
169
|
-
|
|
100
|
+
>
|
|
101
|
+
{HeartButton && <HeartButton embed={imageMetaToMockEmbed(embed)} />}
|
|
102
|
+
</EmbedByline>
|
|
170
103
|
)}
|
|
171
104
|
</Figure>
|
|
172
105
|
);
|
|
173
106
|
};
|
|
174
107
|
|
|
175
|
-
interface ImageLicenseProps {
|
|
176
|
-
title: string;
|
|
177
|
-
imageUrl: string;
|
|
178
|
-
copyright: ICopyright;
|
|
179
|
-
articlePath?: string;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const ImageLicense = ({ articlePath, title, imageUrl, copyright }: ImageLicenseProps) => {
|
|
183
|
-
const { t, i18n } = useTranslation();
|
|
184
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
185
|
-
const copyString = figureApa7CopyString(
|
|
186
|
-
title,
|
|
187
|
-
undefined,
|
|
188
|
-
imageUrl,
|
|
189
|
-
articlePath,
|
|
190
|
-
copyright,
|
|
191
|
-
copyright.license.license,
|
|
192
|
-
undefined,
|
|
193
|
-
(id: string) => t(id),
|
|
194
|
-
i18n.language,
|
|
195
|
-
);
|
|
196
|
-
const license = getLicenseByAbbreviation(copyright.license.license, i18n.language);
|
|
197
|
-
const authors = getLicenseCredits(copyright);
|
|
198
|
-
|
|
199
|
-
const contributors = getGroupedContributorDescriptionList(copyright, i18n.language).map((item) => ({
|
|
200
|
-
name: item.description,
|
|
201
|
-
type: item.label,
|
|
202
|
-
}));
|
|
203
|
-
|
|
204
|
-
const captionAuthors = getFirstNonEmptyLicenseCredits(authors);
|
|
205
|
-
|
|
206
|
-
return (
|
|
207
|
-
<>
|
|
208
|
-
<FigureCaption
|
|
209
|
-
figureId=""
|
|
210
|
-
id=""
|
|
211
|
-
licenseRights={license.rights}
|
|
212
|
-
modalButton={
|
|
213
|
-
<ButtonV2 variant="outline" shape="pill" size="small" onClick={() => setIsOpen(true)}>
|
|
214
|
-
{t('image.reuse')}
|
|
215
|
-
</ButtonV2>
|
|
216
|
-
}
|
|
217
|
-
authors={captionAuthors}
|
|
218
|
-
locale={i18n.language}
|
|
219
|
-
>
|
|
220
|
-
<ModalV2 controlled isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
|
221
|
-
{(close) => (
|
|
222
|
-
<FigureLicenseDialogContent
|
|
223
|
-
onClose={close}
|
|
224
|
-
title={title}
|
|
225
|
-
license={license}
|
|
226
|
-
authors={contributors}
|
|
227
|
-
origin={copyright.origin}
|
|
228
|
-
locale={i18n.language}
|
|
229
|
-
type="image"
|
|
230
|
-
>
|
|
231
|
-
{copyright.license.license !== 'COPYRIGHTED' && (
|
|
232
|
-
<>
|
|
233
|
-
{copyString && (
|
|
234
|
-
<CopyButton
|
|
235
|
-
variant="outline"
|
|
236
|
-
copyNode={t('license.hasCopiedTitle')}
|
|
237
|
-
onClick={() => navigator.clipboard.writeText(copyString)}
|
|
238
|
-
>
|
|
239
|
-
{t('license.copyTitle')}
|
|
240
|
-
</CopyButton>
|
|
241
|
-
)}
|
|
242
|
-
<SafeLinkButton download to={imageUrl} variant="outline">
|
|
243
|
-
{t('image.download')}
|
|
244
|
-
</SafeLinkButton>
|
|
245
|
-
</>
|
|
246
|
-
)}
|
|
247
|
-
</FigureLicenseDialogContent>
|
|
248
|
-
)}
|
|
249
|
-
</ModalV2>
|
|
250
|
-
</FigureCaption>
|
|
251
|
-
</>
|
|
252
|
-
);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
108
|
export default AudioEmbed;
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import { Meta, StoryObj } from '@storybook/react';
|
|
11
|
-
import { BrightcoveData, BrightcoveEmbedData, BrightcoveMetaData } from '@ndla/types-embed';
|
|
11
|
+
import { BrightcoveData, BrightcoveEmbedData, BrightcoveMetaData, EmbedMetaData } from '@ndla/types-embed';
|
|
12
12
|
import BrightcoveEmbed from './BrightcoveEmbed';
|
|
13
13
|
import { defaultParameters } from '../../../../stories/defaults';
|
|
14
|
+
import StoryFavoriteButton from '../../../../stories/StoryFavoriteButton';
|
|
14
15
|
|
|
15
16
|
const embedData: BrightcoveEmbedData = {
|
|
16
17
|
resource: 'brightcove',
|
|
@@ -179,6 +180,7 @@ export default meta;
|
|
|
179
180
|
|
|
180
181
|
export const BrightcoveEmbedStory: StoryObj<typeof BrightcoveEmbed> = {
|
|
181
182
|
args: {
|
|
183
|
+
heartButton: StoryFavoriteButton,
|
|
182
184
|
embed: {
|
|
183
185
|
resource: 'brightcove',
|
|
184
186
|
status: 'success',
|
|
@@ -191,12 +193,14 @@ export const BrightcoveEmbedStory: StoryObj<typeof BrightcoveEmbed> = {
|
|
|
191
193
|
|
|
192
194
|
export const VisuallyInterpreted: StoryObj<typeof BrightcoveEmbed> = {
|
|
193
195
|
args: {
|
|
196
|
+
heartButton: StoryFavoriteButton,
|
|
194
197
|
embed: visuallyInterpretedEmbedMetaData,
|
|
195
198
|
},
|
|
196
199
|
};
|
|
197
200
|
|
|
198
201
|
export const BrightcoveFailed: StoryObj<typeof BrightcoveEmbed> = {
|
|
199
202
|
args: {
|
|
203
|
+
heartButton: StoryFavoriteButton,
|
|
200
204
|
embed: {
|
|
201
205
|
resource: 'brightcove',
|
|
202
206
|
status: 'error',
|