@ndla/ui 37.0.6 → 37.1.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.
- package/README.md +1 -1
- package/es/Article/Article.js +3 -4
- package/es/Embed/BrightcoveEmbed.js +4 -5
- package/es/Embed/IframeEmbed.js +2 -3
- package/es/Embed/ImageEmbed.js +2 -3
- package/es/KeyFigure/KeyFigure.js +6 -6
- package/es/List/OrderedList.js +1 -1
- package/es/List/UnOrderedList.js +1 -1
- package/es/ProgrammeCard/ProgrammeCard.js +53 -0
- package/es/ProgrammeCard/index.js +9 -0
- package/es/index.js +1 -0
- package/lib/Article/Article.js +3 -4
- package/lib/Embed/BrightcoveEmbed.js +4 -5
- package/lib/Embed/IframeEmbed.js +2 -4
- package/lib/Embed/ImageEmbed.js +3 -4
- package/lib/KeyFigure/KeyFigure.js +6 -6
- package/lib/List/OrderedList.js +1 -1
- package/lib/List/UnOrderedList.js +1 -1
- package/lib/ProgrammeCard/ProgrammeCard.d.ts +23 -0
- package/lib/ProgrammeCard/ProgrammeCard.js +60 -0
- package/lib/ProgrammeCard/index.d.ts +8 -0
- package/lib/ProgrammeCard/index.js +13 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +7 -0
- package/package.json +11 -11
- package/src/Article/Article.tsx +1 -2
- package/src/Embed/BrightcoveEmbed.tsx +2 -3
- package/src/Embed/IframeEmbed.tsx +2 -3
- package/src/Embed/ImageEmbed.tsx +5 -6
- package/src/KeyFigure/KeyFigure.tsx +10 -6
- package/src/List/OrderedList.tsx +4 -4
- package/src/List/UnOrderedList.tsx +13 -16
- package/src/ProgrammeCard/ProgrammeCard.stories.tsx +40 -0
- package/src/ProgrammeCard/ProgrammeCard.tsx +83 -0
- package/src/ProgrammeCard/index.tsx +9 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "37.
|
|
3
|
+
"version": "37.1.2",
|
|
4
4
|
"description": "UI component library for NDLA.",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -31,21 +31,21 @@
|
|
|
31
31
|
"types"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ndla/accordion": "^2.2.
|
|
34
|
+
"@ndla/accordion": "^2.2.3",
|
|
35
35
|
"@ndla/article-scripts": "^3.0.17",
|
|
36
|
-
"@ndla/button": "^10.1.
|
|
36
|
+
"@ndla/button": "^10.1.2",
|
|
37
37
|
"@ndla/carousel": "^3.1.0",
|
|
38
38
|
"@ndla/core": "^4.0.0",
|
|
39
|
-
"@ndla/forms": "^4.3.
|
|
40
|
-
"@ndla/hooks": "^2.0.
|
|
39
|
+
"@ndla/forms": "^4.3.2",
|
|
40
|
+
"@ndla/hooks": "^2.0.6",
|
|
41
41
|
"@ndla/icons": "^2.3.0",
|
|
42
42
|
"@ndla/licenses": "^7.0.4",
|
|
43
43
|
"@ndla/modal": "^2.3.0",
|
|
44
|
-
"@ndla/notion": "^4.3.
|
|
45
|
-
"@ndla/safelink": "^4.1.
|
|
44
|
+
"@ndla/notion": "^4.3.2",
|
|
45
|
+
"@ndla/safelink": "^4.1.2",
|
|
46
46
|
"@ndla/switch": "^1.1.0",
|
|
47
|
-
"@ndla/tabs": "^2.2.
|
|
48
|
-
"@ndla/tooltip": "^4.1.
|
|
47
|
+
"@ndla/tabs": "^2.2.2",
|
|
48
|
+
"@ndla/tooltip": "^4.1.2",
|
|
49
49
|
"@ndla/util": "^3.1.12",
|
|
50
50
|
"@radix-ui/react-dropdown-menu": "2.0.2",
|
|
51
51
|
"@radix-ui/react-popover": "^1.0.3",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
|
|
77
77
|
"@ndla/types-backend": "^0.2.5",
|
|
78
|
-
"@ndla/types-embed": "^2.0.
|
|
78
|
+
"@ndla/types-embed": "^2.0.2",
|
|
79
79
|
"@types/reach__dialog": "^0.1.0",
|
|
80
80
|
"css-loader": "^6.7.3",
|
|
81
81
|
"mini-css-extract-plugin": "^2.7.5",
|
|
@@ -86,5 +86,5 @@
|
|
|
86
86
|
"publishConfig": {
|
|
87
87
|
"access": "public"
|
|
88
88
|
},
|
|
89
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "e5a5fe7892ddafb32b82798589fce56016772ce0"
|
|
90
90
|
}
|
package/src/Article/Article.tsx
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
import React, { ComponentType, ReactNode, useEffect, useRef, useState, forwardRef } from 'react';
|
|
10
10
|
import BEMHelper from 'react-bem-helper';
|
|
11
|
-
import isString from 'lodash/isString';
|
|
12
11
|
import parse from 'html-react-parser';
|
|
13
12
|
import styled from '@emotion/styled';
|
|
14
13
|
|
|
@@ -80,7 +79,7 @@ export const ArticleIntroduction = ({
|
|
|
80
79
|
return text;
|
|
81
80
|
},
|
|
82
81
|
}: ArticleIntroductionProps) => {
|
|
83
|
-
if (
|
|
82
|
+
if (typeof children === 'string') {
|
|
84
83
|
return <div className="article_introduction">{parse(renderMarkdown(children))}</div>;
|
|
85
84
|
}
|
|
86
85
|
if (children) {
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import sortBy from 'lodash/sortBy';
|
|
10
|
-
import isNumber from 'lodash/isNumber';
|
|
11
10
|
import styled from '@emotion/styled';
|
|
12
11
|
import { spacing } from '@ndla/core';
|
|
13
12
|
import { COPYRIGHTED } from '@ndla/licenses';
|
|
@@ -35,8 +34,8 @@ const BrightcoveIframe = styled.iframe`
|
|
|
35
34
|
`;
|
|
36
35
|
|
|
37
36
|
export const makeIframeString = (url: string, width: string | number, height: string | number, title: string = '') => {
|
|
38
|
-
const strippedWidth =
|
|
39
|
-
const strippedHeight =
|
|
37
|
+
const strippedWidth = typeof width === 'number' ? width : width.replace(/\s*px/, '');
|
|
38
|
+
const strippedHeight = typeof height === 'number' ? height : height.replace(/\s*px/, '');
|
|
40
39
|
const urlOrTitle = title || url;
|
|
41
40
|
return `<iframe title="${urlOrTitle}" aria-label="${urlOrTitle}" src="${url}" width="${strippedWidth}" height="${strippedHeight}" allowfullscreen scrolling="no" frameborder="0" loading="lazy"></iframe>`;
|
|
42
41
|
};
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import isNumber from 'lodash/isNumber';
|
|
10
9
|
import { useEffect, useRef } from 'react';
|
|
11
10
|
import { IframeMetaData } from '@ndla/types-embed';
|
|
12
11
|
import { useTranslation } from 'react-i18next';
|
|
@@ -58,8 +57,8 @@ const IframeEmbed = ({ embed, isConcept }: Props) => {
|
|
|
58
57
|
|
|
59
58
|
const { width, height, title, url } = embedData;
|
|
60
59
|
|
|
61
|
-
const strippedWidth =
|
|
62
|
-
const strippedHeight =
|
|
60
|
+
const strippedWidth = typeof width === 'number' ? width : width?.replace(/\s*px/, '');
|
|
61
|
+
const strippedHeight = typeof height === 'number' ? height : height?.replace(/\s*px/, '');
|
|
63
62
|
const urlOrTitle = title || url;
|
|
64
63
|
|
|
65
64
|
return (
|
package/src/Embed/ImageEmbed.tsx
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import isNumber from 'lodash/isNumber';
|
|
10
9
|
import { ImageEmbedData, ImageMetaData } from '@ndla/types-embed';
|
|
11
10
|
import { useTranslation } from 'react-i18next';
|
|
12
11
|
import { MouseEventHandler, useState } from 'react';
|
|
@@ -74,7 +73,7 @@ const getSizes = (size?: string, align?: string) => {
|
|
|
74
73
|
};
|
|
75
74
|
|
|
76
75
|
const getFocalPoint = (data: ImageEmbedData) => {
|
|
77
|
-
if (
|
|
76
|
+
if (typeof data.focalX === 'number' && typeof data.focalY === 'number') {
|
|
78
77
|
return { x: data.focalX, y: data.focalY };
|
|
79
78
|
}
|
|
80
79
|
return undefined;
|
|
@@ -82,10 +81,10 @@ const getFocalPoint = (data: ImageEmbedData) => {
|
|
|
82
81
|
|
|
83
82
|
const getCrop = (data: ImageEmbedData) => {
|
|
84
83
|
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
typeof data.lowerRightX === 'number' &&
|
|
85
|
+
typeof data.lowerRightY === 'number' &&
|
|
86
|
+
typeof data.upperLeftX === 'number' &&
|
|
87
|
+
typeof data.upperLeftY === 'number'
|
|
89
88
|
) {
|
|
90
89
|
return {
|
|
91
90
|
startX: data.lowerRightX,
|
|
@@ -28,22 +28,26 @@ const TitleWrapper = styled.div`
|
|
|
28
28
|
font-weight: ${fonts.weight.bold};
|
|
29
29
|
overflow-wrap: break-word;
|
|
30
30
|
${fonts.sizes('38px', '48px')};
|
|
31
|
+
text-align: center;
|
|
32
|
+
max-width: 240px;
|
|
33
|
+
|
|
31
34
|
${mq.range({ until: breakpoints.tabletWide })} {
|
|
32
35
|
${fonts.sizes('30px', '36px')};
|
|
33
36
|
}
|
|
34
|
-
max-width: 240px;
|
|
35
37
|
`;
|
|
36
38
|
|
|
37
39
|
const SubTitleWrapper = styled.div`
|
|
38
40
|
overflow-wrap: 'break-word';
|
|
41
|
+
text-align: center;
|
|
42
|
+
${fonts.sizes('18px', '29px')};
|
|
43
|
+
color: ${colors.text.primary};
|
|
44
|
+
font-weight: ${fonts.weight.normal};
|
|
45
|
+
font-family: ${fonts.sans};
|
|
46
|
+
max-width: 240px;
|
|
47
|
+
|
|
39
48
|
${mq.range({ until: breakpoints.tabletWide })} {
|
|
40
49
|
padding-top: ${spacing.xxsmall};
|
|
41
|
-
${fonts.sizes('18px', '29px')};
|
|
42
|
-
color: ${colors.text.primary};
|
|
43
|
-
font-weight: ${fonts.weight.normal};
|
|
44
|
-
font-family: ${fonts.sans};
|
|
45
50
|
}
|
|
46
|
-
max-width: 240px;
|
|
47
51
|
`;
|
|
48
52
|
|
|
49
53
|
interface Props {
|
package/src/List/OrderedList.tsx
CHANGED
|
@@ -23,8 +23,8 @@ export const generateListResets = () => {
|
|
|
23
23
|
|
|
24
24
|
const StyledOl = styled.ol`
|
|
25
25
|
margin-top: 0;
|
|
26
|
-
margin-left: ${spacing.
|
|
27
|
-
${fonts.sizes('
|
|
26
|
+
margin-left: ${spacing.normal};
|
|
27
|
+
${fonts.sizes('20px', '29px')};
|
|
28
28
|
list-style-type: none;
|
|
29
29
|
padding-left: ${spacing.medium} !important;
|
|
30
30
|
|
|
@@ -35,10 +35,10 @@ const StyledOl = styled.ol`
|
|
|
35
35
|
}
|
|
36
36
|
// List item
|
|
37
37
|
li {
|
|
38
|
-
margin-top: ${spacing.
|
|
38
|
+
margin-top: ${spacing.nsmall};
|
|
39
39
|
|
|
40
40
|
p {
|
|
41
|
-
margin-bottom: ${spacing.
|
|
41
|
+
margin-bottom: ${spacing.nsmall} !important;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -12,35 +12,32 @@ import { forwardRef, HTMLAttributes } from 'react';
|
|
|
12
12
|
import { generateListResets } from './OrderedList';
|
|
13
13
|
|
|
14
14
|
const StyledUl = styled.ul`
|
|
15
|
-
padding-left: ${spacing.
|
|
16
|
-
margin-left: ${spacing.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
}
|
|
15
|
+
padding-left: ${spacing.nsmall} !important;
|
|
16
|
+
margin-left: ${spacing.normal} !important;
|
|
17
|
+
margin-top: 0;
|
|
18
|
+
${fonts.sizes('20px', '29px')};
|
|
19
|
+
|
|
22
20
|
ul {
|
|
23
21
|
list-style-image: none;
|
|
24
|
-
padding-left: ${spacing.
|
|
22
|
+
padding-left: ${spacing.nsmall};
|
|
25
23
|
|
|
26
24
|
margin-left: 0 !important;
|
|
27
25
|
}
|
|
28
|
-
margin-top: 0;
|
|
29
|
-
${fonts.sizes('18px', '29px')};
|
|
30
26
|
|
|
27
|
+
> li {
|
|
28
|
+
::marker {
|
|
29
|
+
color: ${colors.brand.secondary};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
31
32
|
// List item
|
|
32
33
|
li {
|
|
33
34
|
padding-left: ${spacing.nsmall};
|
|
34
|
-
margin-top: ${spacing.
|
|
35
|
+
margin-top: ${spacing.nsmall};
|
|
35
36
|
p {
|
|
36
|
-
margin-bottom: ${spacing.
|
|
37
|
+
margin-bottom: ${spacing.nsmall} !important;
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
// Child unordered lists
|
|
41
|
-
ul {
|
|
42
|
-
padding-left: ${spacing.nsmall};
|
|
43
|
-
}
|
|
44
41
|
// List reset classes
|
|
45
42
|
${generateListResets()}
|
|
46
43
|
`;
|
|
@@ -0,0 +1,40 @@
|
|
|
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 ProgrammeCard from './ProgrammeCard';
|
|
12
|
+
import { defaultParameters } from '../../../../stories/defaults';
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
title: 'Enkle komponenter/ProgrammeCard',
|
|
16
|
+
component: ProgrammeCard,
|
|
17
|
+
tags: ['autodocs'],
|
|
18
|
+
parameters: {
|
|
19
|
+
...defaultParameters,
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
id: 'test ID',
|
|
23
|
+
title: { title: 'Elektro og datateknologi', language: 'nb' },
|
|
24
|
+
desktopImage: {
|
|
25
|
+
src: 'https://api.test.ndla.no/image-api/raw/ajvkVKKR.svg?width=600&ts=1682591987993',
|
|
26
|
+
alt: '',
|
|
27
|
+
},
|
|
28
|
+
mobileImage: {
|
|
29
|
+
src: 'https://api.test.ndla.no/image-api/raw/YIAprLg9.svg?width=600&ts=1682592022017',
|
|
30
|
+
alt: '',
|
|
31
|
+
},
|
|
32
|
+
url: '#',
|
|
33
|
+
},
|
|
34
|
+
} as Meta<typeof ProgrammeCard>;
|
|
35
|
+
|
|
36
|
+
export const ProgrammeCardStory: StoryFn<typeof ProgrammeCard> = ({ ...args }) => {
|
|
37
|
+
return <ProgrammeCard {...args} />;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
ProgrammeCardStory.storyName = 'ProgrammeCard';
|
|
@@ -0,0 +1,83 @@
|
|
|
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 SafeLink from '@ndla/safelink';
|
|
12
|
+
import { spacing, colors, misc, breakpoints, mq } from '@ndla/core';
|
|
13
|
+
|
|
14
|
+
interface Image {
|
|
15
|
+
src: string;
|
|
16
|
+
alt: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Programme {
|
|
19
|
+
id: string;
|
|
20
|
+
title: {
|
|
21
|
+
title: string;
|
|
22
|
+
language: string;
|
|
23
|
+
};
|
|
24
|
+
desktopImage: Image;
|
|
25
|
+
mobileImage: Image;
|
|
26
|
+
url: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const StyledCardContainer = styled(SafeLink)`
|
|
30
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
31
|
+
height: 350px;
|
|
32
|
+
width: 250px;
|
|
33
|
+
}
|
|
34
|
+
max-width: 350px;
|
|
35
|
+
height: 195px;
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
background-color: ${colors.background.default};
|
|
39
|
+
border-radius: ${misc.borderRadius};
|
|
40
|
+
align-self: center;
|
|
41
|
+
box-shadow: none;
|
|
42
|
+
&:hover,
|
|
43
|
+
&:focus-visible {
|
|
44
|
+
text-decoration: underline ${colors.text.primary};
|
|
45
|
+
text-underline-offset: 3px;
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const StyledImg = styled.img`
|
|
50
|
+
display: none;
|
|
51
|
+
border-radius: ${misc.borderRadius} ${misc.borderRadius} 0 0;
|
|
52
|
+
&[data-is-mobile='true'] {
|
|
53
|
+
${mq.range({ until: breakpoints.tablet })} {
|
|
54
|
+
display: block;
|
|
55
|
+
width: auto;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
&[data-is-mobile='false'] {
|
|
59
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
60
|
+
display: block;
|
|
61
|
+
width: 350px;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const StyledTitle = styled.span`
|
|
67
|
+
color: ${colors.text.primary};
|
|
68
|
+
padding: ${spacing.normal} 0 ${spacing.normal} ${spacing.nsmall};
|
|
69
|
+
border: 1px solid ${colors.brand.lighter};
|
|
70
|
+
border-radius: 0 0 ${misc.borderRadius} ${misc.borderRadius};
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
const ProgrammeCard = ({ title, desktopImage, mobileImage, url }: Programme) => {
|
|
74
|
+
return (
|
|
75
|
+
<StyledCardContainer to={url}>
|
|
76
|
+
<StyledImg data-is-mobile="false" src={desktopImage.src} alt={desktopImage.alt} />
|
|
77
|
+
<StyledImg data-is-mobile="true" src={mobileImage.src} alt={mobileImage.alt} />
|
|
78
|
+
<StyledTitle>{title.title}</StyledTitle>
|
|
79
|
+
</StyledCardContainer>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default ProgrammeCard;
|
package/src/index.ts
CHANGED
|
@@ -273,6 +273,7 @@ export { default as LetterFilter } from './LetterFilter';
|
|
|
273
273
|
|
|
274
274
|
export { OrderedList, UnOrderedList } from './List';
|
|
275
275
|
export { BlogPostV2 } from './BlogPost';
|
|
276
|
+
export { ProgrammeCard } from './ProgrammeCard';
|
|
276
277
|
export { KeyFigure } from './KeyFigure';
|
|
277
278
|
export { default as ContactBlock } from './ContactBlock';
|
|
278
279
|
export type { HeartButtonType } from './Embed';
|