@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndla/ui",
3
- "version": "37.0.6",
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.1",
34
+ "@ndla/accordion": "^2.2.3",
35
35
  "@ndla/article-scripts": "^3.0.17",
36
- "@ndla/button": "^10.1.0",
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.0",
40
- "@ndla/hooks": "^2.0.4",
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.0",
45
- "@ndla/safelink": "^4.1.0",
44
+ "@ndla/notion": "^4.3.2",
45
+ "@ndla/safelink": "^4.1.2",
46
46
  "@ndla/switch": "^1.1.0",
47
- "@ndla/tabs": "^2.2.0",
48
- "@ndla/tooltip": "^4.1.0",
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.1",
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": "8fbb556198d4854e5a5299964b10fe67adf7adb1"
89
+ "gitHead": "e5a5fe7892ddafb32b82798589fce56016772ce0"
90
90
  }
@@ -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 (isString(children)) {
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 = isNumber(width) ? width : width.replace(/\s*px/, '');
39
- const strippedHeight = isNumber(height) ? height : height.replace(/\s*px/, '');
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 = isNumber(width) ? width : width?.replace(/\s*px/, '');
62
- const strippedHeight = isNumber(height) ? height : height?.replace(/\s*px/, '');
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 (
@@ -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 (isNumber(data.focalX) && isNumber(data.focalY)) {
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
- isNumber(data.lowerRightX) &&
86
- isNumber(data.lowerRightY) &&
87
- isNumber(data.upperLeftX) &&
88
- isNumber(data.upperLeftY)
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 {
@@ -23,8 +23,8 @@ export const generateListResets = () => {
23
23
 
24
24
  const StyledOl = styled.ol`
25
25
  margin-top: 0;
26
- margin-left: ${spacing.medium};
27
- ${fonts.sizes('18px', '29px')};
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.normal};
38
+ margin-top: ${spacing.nsmall};
39
39
 
40
40
  p {
41
- margin-bottom: ${spacing.normal} !important;
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.small} !important;
16
- margin-left: ${spacing.medium} !important;
17
- > li {
18
- ::marker {
19
- color: ${colors.brand.secondary};
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.small};
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.normal};
35
+ margin-top: ${spacing.nsmall};
35
36
  p {
36
- margin-bottom: ${spacing.normal} !important;
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;
@@ -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 ProgrammeCard } from './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';