@ndla/ui 47.3.0 → 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.
- package/es/CampaignBlock/CampaignBlock.js +8 -6
- package/es/ContactBlock/ContactBlock.js +17 -14
- package/es/CopyParagraphButton/CopyParagraphButtonV2.js +5 -3
- package/es/Embed/ConceptEmbed.js +22 -13
- package/es/Embed/ConceptListEmbed.js +6 -3
- package/es/Embed/ImageEmbed.js +5 -3
- package/es/Embed/conceptComponents.js +19 -13
- package/es/Footer/Footer.js +11 -9
- package/es/Footer/FooterLinks.js +19 -34
- package/es/FrontpageArticle/FrontpageArticle.js +5 -2
- package/es/Gloss/Gloss.js +8 -8
- package/es/Image/Image.js +8 -5
- package/es/KeyFigure/KeyFigure.js +8 -5
- package/es/Notion/Notion.js +7 -5
- package/es/Table/Table.js +6 -6
- package/es/locale/messages-en.js +1 -0
- package/es/locale/messages-nb.js +1 -0
- package/es/locale/messages-nn.js +2 -1
- package/es/locale/messages-se.js +1 -0
- package/es/locale/messages-sma.js +1 -0
- package/lib/CampaignBlock/CampaignBlock.js +8 -6
- package/lib/ContactBlock/ContactBlock.d.ts +2 -1
- package/lib/ContactBlock/ContactBlock.js +17 -14
- package/lib/CopyParagraphButton/CopyParagraphButtonV2.d.ts +2 -1
- package/lib/CopyParagraphButton/CopyParagraphButtonV2.js +5 -3
- package/lib/Embed/ConceptEmbed.d.ts +4 -3
- package/lib/Embed/ConceptEmbed.js +22 -13
- package/lib/Embed/ConceptListEmbed.d.ts +2 -1
- package/lib/Embed/ConceptListEmbed.js +6 -3
- package/lib/Embed/ImageEmbed.d.ts +2 -1
- package/lib/Embed/ImageEmbed.js +5 -3
- package/lib/Embed/conceptComponents.d.ts +1 -0
- package/lib/Embed/conceptComponents.js +19 -13
- package/lib/Footer/Footer.d.ts +6 -1
- package/lib/Footer/Footer.js +11 -9
- package/lib/Footer/FooterLinks.d.ts +7 -2
- package/lib/Footer/FooterLinks.js +19 -34
- package/lib/FrontpageArticle/FrontpageArticle.d.ts +2 -1
- package/lib/FrontpageArticle/FrontpageArticle.js +5 -2
- package/lib/Gloss/Gloss.js +8 -8
- package/lib/Image/Image.d.ts +2 -1
- package/lib/Image/Image.js +8 -5
- package/lib/KeyFigure/KeyFigure.d.ts +2 -1
- package/lib/KeyFigure/KeyFigure.js +8 -5
- package/lib/Notion/Notion.d.ts +2 -1
- package/lib/Notion/Notion.js +7 -5
- package/lib/Table/Table.d.ts +1 -0
- package/lib/Table/Table.js +6 -6
- package/lib/locale/messages-en.d.ts +1 -0
- package/lib/locale/messages-en.js +1 -0
- package/lib/locale/messages-nb.d.ts +1 -0
- package/lib/locale/messages-nb.js +1 -0
- package/lib/locale/messages-nn.d.ts +1 -0
- package/lib/locale/messages-nn.js +2 -1
- package/lib/locale/messages-se.d.ts +1 -0
- package/lib/locale/messages-se.js +1 -0
- package/lib/locale/messages-sma.d.ts +1 -0
- package/lib/locale/messages-sma.js +1 -0
- package/package.json +17 -17
- package/src/CampaignBlock/CampaignBlock.tsx +4 -2
- package/src/ContactBlock/ContactBlock.tsx +13 -3
- package/src/CopyParagraphButton/CopyParagraphButtonV2.tsx +3 -2
- package/src/Embed/ConceptEmbed.tsx +10 -1
- package/src/Embed/ConceptListEmbed.tsx +4 -3
- package/src/Embed/ImageEmbed.tsx +3 -1
- package/src/Embed/conceptComponents.tsx +14 -9
- package/src/Footer/Footer.stories.tsx +22 -0
- package/src/Footer/Footer.tsx +24 -18
- package/src/Footer/FooterLinks.tsx +17 -24
- package/src/FrontpageArticle/FrontpageArticle.tsx +4 -3
- package/src/Gloss/Gloss.tsx +1 -0
- package/src/Image/Image.tsx +11 -2
- package/src/KeyFigure/KeyFigure.tsx +4 -3
- package/src/Notion/Notion.tsx +3 -2
- package/src/Table/Table.tsx +1 -0
- package/src/locale/messages-en.ts +1 -0
- package/src/locale/messages-nb.ts +1 -0
- package/src/locale/messages-nn.ts +2 -1
- package/src/locale/messages-se.ts +1 -0
- package/src/locale/messages-sma.ts +1 -0
|
@@ -1164,6 +1164,7 @@ var messages = _objectSpread(_objectSpread({
|
|
|
1164
1164
|
listView: 'Listevisning',
|
|
1165
1165
|
detailView: 'Detaljert listevisning',
|
|
1166
1166
|
shortView: 'Kort visning',
|
|
1167
|
+
userPictureAltText: 'Profijleguvviem',
|
|
1167
1168
|
sharedFolder: {
|
|
1168
1169
|
folderCopied: 'Mappen har blitt kopiert.',
|
|
1169
1170
|
info: 'Denne mappa inneheld fagstoff og oppgåver frå NDLA, samla av ein lærar.',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "47.
|
|
3
|
+
"version": "47.4.0",
|
|
4
4
|
"description": "UI component library for NDLA.",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -31,23 +31,23 @@
|
|
|
31
31
|
"types"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ndla/accordion": "^2.2.
|
|
35
|
-
"@ndla/button": "^12.0.
|
|
36
|
-
"@ndla/carousel": "^4.0.
|
|
37
|
-
"@ndla/core": "^4.2.
|
|
38
|
-
"@ndla/dropdown-menu": "^1.0.
|
|
39
|
-
"@ndla/forms": "^5.0.
|
|
34
|
+
"@ndla/accordion": "^2.2.32",
|
|
35
|
+
"@ndla/button": "^12.0.6",
|
|
36
|
+
"@ndla/carousel": "^4.0.9",
|
|
37
|
+
"@ndla/core": "^4.2.3",
|
|
38
|
+
"@ndla/dropdown-menu": "^1.0.12",
|
|
39
|
+
"@ndla/forms": "^5.0.8",
|
|
40
40
|
"@ndla/hooks": "^2.1.1",
|
|
41
|
-
"@ndla/icons": "^4.1.
|
|
41
|
+
"@ndla/icons": "^4.1.4",
|
|
42
42
|
"@ndla/licenses": "^7.2.2",
|
|
43
|
-
"@ndla/modal": "^5.0.
|
|
44
|
-
"@ndla/notion": "^6.0.
|
|
45
|
-
"@ndla/safelink": "^4.1.
|
|
46
|
-
"@ndla/select": "^3.1.
|
|
47
|
-
"@ndla/switch": "^1.1.
|
|
48
|
-
"@ndla/tabs": "^3.1.
|
|
49
|
-
"@ndla/tooltip": "^5.0.
|
|
50
|
-
"@ndla/typography": "^0.2.
|
|
43
|
+
"@ndla/modal": "^5.0.6",
|
|
44
|
+
"@ndla/notion": "^6.0.8",
|
|
45
|
+
"@ndla/safelink": "^4.1.31",
|
|
46
|
+
"@ndla/select": "^3.1.4",
|
|
47
|
+
"@ndla/switch": "^1.1.18",
|
|
48
|
+
"@ndla/tabs": "^3.1.3",
|
|
49
|
+
"@ndla/tooltip": "^5.0.6",
|
|
50
|
+
"@ndla/typography": "^0.2.5",
|
|
51
51
|
"@ndla/util": "^4.0.0",
|
|
52
52
|
"@radix-ui/react-popover": "^1.0.7",
|
|
53
53
|
"@radix-ui/react-radio-group": "^1.1.3",
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "726e4d5d54cad9fdd7090a2a1f92a36bc5893f65"
|
|
84
84
|
}
|
|
@@ -99,8 +99,10 @@ const CampaignBlock = ({
|
|
|
99
99
|
<Container className={className} data-type="campaign-block" data-image-side={imageSide}>
|
|
100
100
|
{image && <StyledImg src={image.src} height={200} width={240} alt={image.alt} />}
|
|
101
101
|
<TextWrapper>
|
|
102
|
-
<Heading css={headingStyle}
|
|
103
|
-
|
|
102
|
+
<Heading css={headingStyle} lang={title.language}>
|
|
103
|
+
{title.title}
|
|
104
|
+
</Heading>
|
|
105
|
+
<StyledDescription lang={description.language}>{description.text}</StyledDescription>
|
|
104
106
|
<StyledLink to={href}>
|
|
105
107
|
{url.text}
|
|
106
108
|
<Forward />
|
|
@@ -27,6 +27,7 @@ interface Props {
|
|
|
27
27
|
imageWidth?: number;
|
|
28
28
|
name: string;
|
|
29
29
|
email: string;
|
|
30
|
+
lang?: string;
|
|
30
31
|
}
|
|
31
32
|
const BlockWrapper = styled.div`
|
|
32
33
|
display: flex;
|
|
@@ -126,7 +127,16 @@ const StyledImage = styled(Image)`
|
|
|
126
127
|
object-fit: cover;
|
|
127
128
|
`;
|
|
128
129
|
|
|
129
|
-
const ContactBlock = ({
|
|
130
|
+
const ContactBlock = ({
|
|
131
|
+
image,
|
|
132
|
+
jobTitle,
|
|
133
|
+
description,
|
|
134
|
+
name,
|
|
135
|
+
email,
|
|
136
|
+
blobColor = 'green',
|
|
137
|
+
blob = 'pointy',
|
|
138
|
+
lang,
|
|
139
|
+
}: Props) => {
|
|
130
140
|
const { t } = useTranslation();
|
|
131
141
|
const isGreenBlob = blobColor === 'green';
|
|
132
142
|
const Blob = blob === 'pointy' ? BlobPointy : BlobRound;
|
|
@@ -153,8 +163,8 @@ const ContactBlock = ({ image, jobTitle, description, name, email, blobColor = '
|
|
|
153
163
|
<ContentWrapper>
|
|
154
164
|
<TextWrapper>
|
|
155
165
|
<InfoWrapper>
|
|
156
|
-
<StyledHeader>{name}</StyledHeader>
|
|
157
|
-
<StyledText>{jobTitle}</StyledText>
|
|
166
|
+
<StyledHeader lang={lang}>{name}</StyledHeader>
|
|
167
|
+
<StyledText lang={lang}>{jobTitle}</StyledText>
|
|
158
168
|
<StyledText>
|
|
159
169
|
<Email>{`${t('email')}:`}</Email>
|
|
160
170
|
<EmailLink href={`mailto:${email}`}>{email}</EmailLink>
|
|
@@ -44,8 +44,9 @@ interface Props {
|
|
|
44
44
|
// What to render within the h2
|
|
45
45
|
children: ReactNode;
|
|
46
46
|
copyText: string;
|
|
47
|
+
lang?: string;
|
|
47
48
|
}
|
|
48
|
-
const CopyParagraphButtonV2 = ({ children, copyText }: Props) => {
|
|
49
|
+
const CopyParagraphButtonV2 = ({ children, copyText, lang }: Props) => {
|
|
49
50
|
const [hasCopied, setHasCopied] = useState(false);
|
|
50
51
|
const { t } = useTranslation();
|
|
51
52
|
const sanitizedTitle = useMemo(() => encodeURIComponent(copyText.replace(/ /g, '-')), [copyText]);
|
|
@@ -74,7 +75,7 @@ const CopyParagraphButtonV2 = ({ children, copyText }: Props) => {
|
|
|
74
75
|
<Link />
|
|
75
76
|
</IconButton>
|
|
76
77
|
</Tooltip>
|
|
77
|
-
<h2 id={sanitizedTitle} tabIndex={-1}>
|
|
78
|
+
<h2 id={sanitizedTitle} tabIndex={-1} lang={lang}>
|
|
78
79
|
{children}
|
|
79
80
|
</h2>
|
|
80
81
|
</ContainerDiv>
|
|
@@ -71,6 +71,7 @@ interface Props {
|
|
|
71
71
|
embed: ConceptMetaData;
|
|
72
72
|
fullWidth?: boolean;
|
|
73
73
|
heartButton?: HeartButtonType;
|
|
74
|
+
lang?: string;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
const StyledButton = styled.button`
|
|
@@ -92,7 +93,7 @@ const StyledButton = styled.button`
|
|
|
92
93
|
}
|
|
93
94
|
`;
|
|
94
95
|
|
|
95
|
-
export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Props) => {
|
|
96
|
+
export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton, lang }: Props) => {
|
|
96
97
|
const parsedContent = useMemo(() => {
|
|
97
98
|
if (embed.status === 'error' || !embed.data.concept.content) return undefined;
|
|
98
99
|
return parse(embed.data.concept.content.content);
|
|
@@ -121,6 +122,7 @@ export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Pro
|
|
|
121
122
|
conceptHeartButton={HeartButton && <HeartButton embed={embed} />}
|
|
122
123
|
conceptType={concept.conceptType}
|
|
123
124
|
glossData={concept.glossData}
|
|
125
|
+
lang={lang}
|
|
124
126
|
/>
|
|
125
127
|
);
|
|
126
128
|
} else if (embed.embedData.type === 'inline') {
|
|
@@ -137,6 +139,7 @@ export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Pro
|
|
|
137
139
|
conceptHeartButton={HeartButton && <HeartButton embed={embed} />}
|
|
138
140
|
conceptType={concept.conceptType}
|
|
139
141
|
glossData={concept.glossData}
|
|
142
|
+
lang={lang}
|
|
140
143
|
/>
|
|
141
144
|
);
|
|
142
145
|
} else {
|
|
@@ -152,6 +155,7 @@ export const ConceptEmbed = ({ embed, fullWidth, heartButton: HeartButton }: Pro
|
|
|
152
155
|
conceptHeartButton={HeartButton && <HeartButton embed={embed} />}
|
|
153
156
|
conceptType={concept.conceptType}
|
|
154
157
|
glossData={concept.glossData}
|
|
158
|
+
lang={lang}
|
|
155
159
|
/>
|
|
156
160
|
);
|
|
157
161
|
}
|
|
@@ -234,6 +238,7 @@ export const InlineConcept = ({
|
|
|
234
238
|
glossData,
|
|
235
239
|
conceptType,
|
|
236
240
|
headerButtons,
|
|
241
|
+
lang,
|
|
237
242
|
}: InlineConceptProps) => {
|
|
238
243
|
const { t } = useTranslation();
|
|
239
244
|
const anchorRef = useRef<HTMLDivElement>(null);
|
|
@@ -275,6 +280,7 @@ export const InlineConcept = ({
|
|
|
275
280
|
heartButton={heartButton}
|
|
276
281
|
headerButtons={headerButtons}
|
|
277
282
|
conceptHeartButton={conceptHeartButton}
|
|
283
|
+
lang={lang}
|
|
278
284
|
closeButton={
|
|
279
285
|
<Close asChild>
|
|
280
286
|
<IconButtonV2 aria-label={t('close')} variant="ghost">
|
|
@@ -310,6 +316,7 @@ export const BlockConcept = ({
|
|
|
310
316
|
conceptHeartButton,
|
|
311
317
|
glossData,
|
|
312
318
|
conceptType,
|
|
319
|
+
lang,
|
|
313
320
|
}: ConceptProps) => {
|
|
314
321
|
const { t } = useTranslation();
|
|
315
322
|
const anchorRef = useRef<HTMLDivElement>(null);
|
|
@@ -339,6 +346,7 @@ export const BlockConcept = ({
|
|
|
339
346
|
id=""
|
|
340
347
|
title={title.title}
|
|
341
348
|
text={content}
|
|
349
|
+
lang={lang}
|
|
342
350
|
visualElement={
|
|
343
351
|
visualElement?.status === 'success' && (
|
|
344
352
|
<>
|
|
@@ -383,6 +391,7 @@ export const BlockConcept = ({
|
|
|
383
391
|
heartButton={heartButton}
|
|
384
392
|
conceptHeartButton={conceptHeartButton}
|
|
385
393
|
inPopover
|
|
394
|
+
lang={lang}
|
|
386
395
|
closeButton={
|
|
387
396
|
<Close asChild>
|
|
388
397
|
<IconButtonV2 aria-label={t('close')} variant="ghost">
|
|
@@ -15,6 +15,7 @@ import { BlockConcept } from './ConceptEmbed';
|
|
|
15
15
|
|
|
16
16
|
interface Props {
|
|
17
17
|
embed: ConceptListMetaData;
|
|
18
|
+
lang?: string;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const ConceptList = styled.div`
|
|
@@ -30,7 +31,7 @@ const StyledSpan = styled.span`
|
|
|
30
31
|
color: ${colors.support.red};
|
|
31
32
|
`;
|
|
32
33
|
|
|
33
|
-
const ConceptListEmbed = ({ embed }: Props) => {
|
|
34
|
+
const ConceptListEmbed = ({ embed, lang }: Props) => {
|
|
34
35
|
const { t } = useTranslation();
|
|
35
36
|
if (embed.status === 'error') {
|
|
36
37
|
return <StyledSpan>{t('embed.conceptListError')}</StyledSpan>;
|
|
@@ -39,9 +40,9 @@ const ConceptListEmbed = ({ embed }: Props) => {
|
|
|
39
40
|
return (
|
|
40
41
|
<div>
|
|
41
42
|
<Figure type="full" resizeIframe>
|
|
42
|
-
{embedData.title && <h2>{embedData.title}</h2>}
|
|
43
|
+
{embedData.title && <h2 lang={lang}>{embedData.title}</h2>}
|
|
43
44
|
<ConceptList>
|
|
44
|
-
<ul>
|
|
45
|
+
<ul lang={lang}>
|
|
45
46
|
{data.concepts.map(({ concept, visualElement }) => (
|
|
46
47
|
<li key={concept.id}>
|
|
47
48
|
<BlockConcept
|
package/src/Embed/ImageEmbed.tsx
CHANGED
|
@@ -27,6 +27,7 @@ interface Props {
|
|
|
27
27
|
path?: string;
|
|
28
28
|
heartButton?: HeartButtonType;
|
|
29
29
|
inGrid?: boolean;
|
|
30
|
+
lang?: string;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export interface Author {
|
|
@@ -104,7 +105,7 @@ export const getCrop = (data: ImageEmbedData) => {
|
|
|
104
105
|
|
|
105
106
|
const expandedSizes = '(min-width: 1024px) 1024px, 100vw';
|
|
106
107
|
|
|
107
|
-
const ImageEmbed = ({ embed, previewAlt, heartButton: HeartButton, inGrid, path }: Props) => {
|
|
108
|
+
const ImageEmbed = ({ embed, previewAlt, heartButton: HeartButton, inGrid, path, lang }: Props) => {
|
|
108
109
|
const [isBylineHidden, setIsBylineHidden] = useState(hideByline(embed.embedData.size));
|
|
109
110
|
const [imageSizes, setImageSizes] = useState<string | undefined>(undefined);
|
|
110
111
|
|
|
@@ -162,6 +163,7 @@ const ImageEmbed = ({ embed, previewAlt, heartButton: HeartButton, inGrid, path
|
|
|
162
163
|
onHideByline={() => setIsBylineHidden((p) => !p)}
|
|
163
164
|
/>
|
|
164
165
|
}
|
|
166
|
+
lang={lang}
|
|
165
167
|
/>
|
|
166
168
|
</ImageWrapper>
|
|
167
169
|
{isBylineHidden || (isSmall(embedData.size) && !imageSizes) ? null : (
|
|
@@ -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 {
|
|
@@ -112,9 +113,11 @@ const NotionHeader = styled.div`
|
|
|
112
113
|
${fonts.sizes('22px', 1.2)};
|
|
113
114
|
}
|
|
114
115
|
small {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
&[data-show-separator='true'] {
|
|
117
|
+
border-left: 1px solid ${colors.brand.greyLight};
|
|
118
|
+
padding-left: ${spacing.small};
|
|
119
|
+
margin-left: ${spacing.xsmall};
|
|
120
|
+
}
|
|
118
121
|
${fonts.sizes('20px', 1.2)};
|
|
119
122
|
font-weight: ${fonts.weight.normal};
|
|
120
123
|
}
|
|
@@ -177,29 +180,31 @@ export const ConceptNotionV2 = forwardRef<HTMLDivElement, ConceptNotionProps>(
|
|
|
177
180
|
conceptType,
|
|
178
181
|
glossData,
|
|
179
182
|
headerButtons,
|
|
183
|
+
lang,
|
|
180
184
|
...rest
|
|
181
185
|
},
|
|
182
186
|
ref,
|
|
183
187
|
) => {
|
|
184
188
|
const { t } = useTranslation();
|
|
185
|
-
|
|
189
|
+
const isConcept = conceptType === 'concept';
|
|
186
190
|
return (
|
|
187
191
|
<div css={inPopover ? notionContentCss : undefined} {...rest} ref={ref}>
|
|
188
192
|
<ContentPadding>
|
|
189
|
-
<NotionHeader>
|
|
193
|
+
<NotionHeader data-show-separator={isConcept}>
|
|
190
194
|
<h1>
|
|
191
|
-
{title.title}
|
|
195
|
+
{isConcept && title.title}
|
|
196
|
+
{<small data-show-separator={isConcept}>{t(`searchPage.resultType.${conceptType}`)}</small>}
|
|
192
197
|
</h1>
|
|
193
198
|
<ButtonWrapper>
|
|
194
199
|
{headerButtons}
|
|
195
200
|
{closeButton}
|
|
196
201
|
</ButtonWrapper>
|
|
197
202
|
</NotionHeader>
|
|
198
|
-
{
|
|
203
|
+
{isConcept ? (
|
|
199
204
|
<>
|
|
200
205
|
<StyledNotionDialogContent>
|
|
201
206
|
{visualElement?.resource === 'image' ? (
|
|
202
|
-
<ImageEmbed embed={visualElement} heartButton={heartButton} />
|
|
207
|
+
<ImageEmbed embed={visualElement} heartButton={heartButton} lang={lang} />
|
|
203
208
|
) : visualElement?.resource === 'brightcove' ? (
|
|
204
209
|
<BrightcoveEmbed embed={visualElement} heartButton={heartButton} />
|
|
205
210
|
) : visualElement?.resource === 'h5p' ? (
|
|
@@ -209,7 +214,7 @@ export const ConceptNotionV2 = forwardRef<HTMLDivElement, ConceptNotionProps>(
|
|
|
209
214
|
) : visualElement?.resource === 'external' ? (
|
|
210
215
|
<ExternalEmbed embed={visualElement} />
|
|
211
216
|
) : null}
|
|
212
|
-
{content && <NotionDialogText>{content}</NotionDialogText>}
|
|
217
|
+
{content && <NotionDialogText lang={lang}>{content}</NotionDialogText>}
|
|
213
218
|
</StyledNotionDialogContent>
|
|
214
219
|
{tags && (
|
|
215
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} />,
|
package/src/Footer/Footer.tsx
CHANGED
|
@@ -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 =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
|
103
|
-
<div key={link.
|
|
94
|
+
{commonLinks?.map((link) => (
|
|
95
|
+
<div key={link.to}>
|
|
104
96
|
<StyledSafeLink
|
|
105
|
-
key={
|
|
106
|
-
aria-label={
|
|
107
|
-
to={link.
|
|
108
|
-
target=
|
|
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
|
-
{
|
|
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
|
|
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}
|
package/src/Gloss/Gloss.tsx
CHANGED
package/src/Image/Image.tsx
CHANGED
|
@@ -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
|
|
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
|
};
|
package/src/Notion/Notion.tsx
CHANGED
|
@@ -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 && (
|