@purpurds/content-block 5.9.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/dist/LICENSE.txt +66 -0
- package/dist/content-block-group.d.ts +13 -0
- package/dist/content-block-group.d.ts.map +1 -0
- package/dist/content-block.cjs.js +34 -0
- package/dist/content-block.cjs.js.map +1 -0
- package/dist/content-block.d.ts +32 -0
- package/dist/content-block.d.ts.map +1 -0
- package/dist/content-block.es.js +800 -0
- package/dist/content-block.es.js.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +65 -0
- package/src/content-block-group.module.scss +13 -0
- package/src/content-block-group.test.tsx +56 -0
- package/src/content-block-group.tsx +47 -0
- package/src/content-block.module.scss +93 -0
- package/src/content-block.stories.tsx +219 -0
- package/src/content-block.test.tsx +135 -0
- package/src/content-block.tsx +124 -0
- package/src/global.d.ts +4 -0
package/dist/styles.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._purpur-content-block_1c7ua_1{display:flex;flex-direction:column;align-items:flex-start;gap:var(--purpur-spacing-400)}@media (min-width: 1024px){._purpur-content-block_1c7ua_1{align-items:center;flex-direction:row;gap:var(--purpur-spacing-300)}._purpur-content-block_1c7ua_1:nth-child(2n){flex-direction:row-reverse}}@media (min-width: 1024px){._purpur-content-block_1c7ua_1 ._purpur-content-block__section_1c7ua_18{width:50%}}._purpur-content-block_1c7ua_1 ._purpur-content-block__image-wrapper_1c7ua_22{width:100%}._purpur-content-block_1c7ua_1 ._purpur-content-block__video-wrapper_1c7ua_25 video,._purpur-content-block_1c7ua_1 ._purpur-content-block__image-wrapper_1c7ua_22 img{border-radius:var(--purpur-border-radius-md)}@media (min-width: 600px){._purpur-content-block_1c7ua_1 ._purpur-content-block__video-wrapper_1c7ua_25 video,._purpur-content-block_1c7ua_1 ._purpur-content-block__image-wrapper_1c7ua_22 img{border-radius:var(--purpur-border-radius-lg)}}._purpur-content-block_1c7ua_1 ._purpur-content-block__content-wrapper_1c7ua_35{display:flex;flex-direction:column;gap:var(--purpur-spacing-150)}._purpur-content-block_1c7ua_1 ._purpur-content-block__badge_1c7ua_40{width:fit-content}._purpur-content-block_1c7ua_1 ._purpur-content-block__content_1c7ua_35{max-width:40rem}._purpur-content-block_1c7ua_1 ._purpur-content-block__usp-list_1c7ua_46{margin:0;padding:0;list-style:none}._purpur-content-block_1c7ua_1 ._purpur-content-block__usp-list-item_1c7ua_51{display:flex;align-items:center;justify-content:left}._purpur-content-block_1c7ua_1 ._purpur-content-block__usp-list-item_1c7ua_51:not(:last-child){margin-bottom:var(--purpur-spacing-100)}._purpur-content-block_1c7ua_1 ._purpur-content-block__usp-list-item-icon_1c7ua_59{margin-right:var(--purpur-spacing-50)}._purpur-content-block_1c7ua_1 ._purpur-content-block__links_1c7ua_62{margin-top:var(--purpur-spacing-250);display:flex;flex-direction:column;gap:var(--purpur-spacing-200)}@media (min-width: 600px){._purpur-content-block_1c7ua_1 ._purpur-content-block__links_1c7ua_62{flex-direction:row}}._purpur-content-block--negative_1c7ua_73 ._purpur-content-block__text-spacing_1c7ua_73 p{color:var(--purpur-color-text-default-negative)}._purpur-content-block--negative_1c7ua_73 ._purpur-content-block__usp-list-item-icon_1c7ua_59 svg{fill:var(--purpur-color-text-default-negative)}._purpur-content-block-group_1g4rh_1{display:flex;flex-direction:column;gap:var(--purpur-spacing-1200);padding:var(--purpur-spacing-600) 0}@media (min-width: 600px){._purpur-content-block-group_1g4rh_1{gap:var(--purpur-spacing-1600);padding:var(--purpur-spacing-800) 0}}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@purpurds/content-block",
|
|
3
|
+
"version": "5.9.0",
|
|
4
|
+
"license": "AGPL-3.0-only",
|
|
5
|
+
"main": "./dist/content-block.cjs.js",
|
|
6
|
+
"types": "./dist/content-block.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": "./dist/content-block.cjs.js",
|
|
10
|
+
"types": "./dist/content-block.d.ts",
|
|
11
|
+
"default": "./dist/content-block.es.js"
|
|
12
|
+
},
|
|
13
|
+
"./styles": "./dist/styles.css"
|
|
14
|
+
},
|
|
15
|
+
"source": "src/content-block.tsx",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"classnames": "~2.5.0",
|
|
18
|
+
"@purpurds/badge": "5.11.2",
|
|
19
|
+
"@purpurds/grid": "5.11.2",
|
|
20
|
+
"@purpurds/cta-link": "5.11.2",
|
|
21
|
+
"@purpurds/icon": "5.11.2",
|
|
22
|
+
"@purpurds/heading": "5.11.2",
|
|
23
|
+
"@purpurds/paragraph": "5.11.2",
|
|
24
|
+
"@purpurds/tokens": "5.11.2"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@rushstack/eslint-patch": "~1.10.0",
|
|
28
|
+
"@storybook/blocks": "^8.2.6",
|
|
29
|
+
"@storybook/react": "^8.2.6",
|
|
30
|
+
"@telia/base-rig": "~8.2.0",
|
|
31
|
+
"@telia/react-rig": "~3.2.0",
|
|
32
|
+
"@testing-library/dom": "~9.3.3",
|
|
33
|
+
"@testing-library/jest-dom": "~6.4.0",
|
|
34
|
+
"@testing-library/react": "~14.3.0",
|
|
35
|
+
"@types/node": "20.12.12",
|
|
36
|
+
"@types/react-dom": "^18.3.0",
|
|
37
|
+
"@types/react": "^18.3.3",
|
|
38
|
+
"eslint-plugin-testing-library": "~6.2.0",
|
|
39
|
+
"eslint": "^8.57.0",
|
|
40
|
+
"jsdom": "~22.1.0",
|
|
41
|
+
"lint-staged": "~10.5.3",
|
|
42
|
+
"prettier": "~2.8.8",
|
|
43
|
+
"react-dom": "^18.3.1",
|
|
44
|
+
"react": "^18.3.1",
|
|
45
|
+
"storybook": "^8.2.6",
|
|
46
|
+
"typescript": "^5.5.4",
|
|
47
|
+
"vite": "5.3.4",
|
|
48
|
+
"vitest": "~1.5.0",
|
|
49
|
+
"@purpurds/component-rig": "1.0.0"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build:dev": "vite",
|
|
53
|
+
"build:watch": "vite build --watch",
|
|
54
|
+
"build": "vite build",
|
|
55
|
+
"ci:build": "rushx build",
|
|
56
|
+
"coverage": "vitest run --coverage",
|
|
57
|
+
"lint:fix": "eslint . --fix",
|
|
58
|
+
"lint": "lint-staged --no-stash 2>&1",
|
|
59
|
+
"sbdev": "rush sbdev",
|
|
60
|
+
"test:unit": "vitest run --passWithNoTests",
|
|
61
|
+
"test:watch": "vitest --watch",
|
|
62
|
+
"test": "rushx test:unit",
|
|
63
|
+
"typecheck": "tsc -p ./tsconfig.json"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
@import "@purpurds/tokens/breakpoint/variables";
|
|
2
|
+
|
|
3
|
+
.purpur-content-block-group {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
gap: var(--purpur-spacing-1200);
|
|
7
|
+
padding: var(--purpur-spacing-600) 0;
|
|
8
|
+
|
|
9
|
+
@media (min-width: $purpur-breakpoint-md) {
|
|
10
|
+
gap: var(--purpur-spacing-1600);
|
|
11
|
+
padding: var(--purpur-spacing-800) 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import * as matchers from "@testing-library/jest-dom/matchers";
|
|
3
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { ContentBlock } from "./content-block";
|
|
7
|
+
import { ContentBlockGroup } from "./content-block-group";
|
|
8
|
+
|
|
9
|
+
expect.extend(matchers);
|
|
10
|
+
|
|
11
|
+
const mockData = {
|
|
12
|
+
title: "Title goes here lorem ipsum",
|
|
13
|
+
usp: ["List item 1", "List item 2"],
|
|
14
|
+
imgUrl: "https://www.telia.se/images",
|
|
15
|
+
imgAltText: "test image",
|
|
16
|
+
ctas: [
|
|
17
|
+
{
|
|
18
|
+
href: "/test",
|
|
19
|
+
text: "CTA link 1",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
href: "/test2",
|
|
23
|
+
text: "CTA link 2",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
content: "Test text content",
|
|
27
|
+
videoUrl: "https://www.telia.se/video.mp4",
|
|
28
|
+
videoPosterImgUrl: "https://www.telia.se/image.png",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const setup = () =>
|
|
32
|
+
render(
|
|
33
|
+
<ContentBlockGroup>
|
|
34
|
+
<ContentBlock title={mockData.title} usp={mockData.usp} ctas={mockData.ctas}>
|
|
35
|
+
{mockData.content}
|
|
36
|
+
</ContentBlock>
|
|
37
|
+
<ContentBlock title="test" headingTag="h1" usp={mockData.usp} ctas={mockData.ctas}>
|
|
38
|
+
{mockData.content}
|
|
39
|
+
</ContentBlock>
|
|
40
|
+
</ContentBlockGroup>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
describe("ContentBlockGroup", () => {
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
vi.clearAllMocks();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterEach(cleanup);
|
|
49
|
+
|
|
50
|
+
it("should render both content blocks and its titles", () => {
|
|
51
|
+
setup();
|
|
52
|
+
|
|
53
|
+
expect(screen.getByRole("heading", { level: 2, name: mockData.title }));
|
|
54
|
+
expect(screen.getByRole("heading", { level: 1, name: "test" }));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { Children, DetailedHTMLProps, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import c from "classnames/bind";
|
|
3
|
+
|
|
4
|
+
import { ContentBlockProps } from "./content-block";
|
|
5
|
+
import styles from "./content-block-group.module.scss";
|
|
6
|
+
|
|
7
|
+
const cx = c.bind(styles);
|
|
8
|
+
const rootClassName = "purpur-content-block-group";
|
|
9
|
+
|
|
10
|
+
export type ContentBlockGroupProps = {
|
|
11
|
+
["data-testid"]?: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
negative?: boolean;
|
|
15
|
+
} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
|
16
|
+
|
|
17
|
+
export const ContentBlockGroup = ({
|
|
18
|
+
children,
|
|
19
|
+
className,
|
|
20
|
+
negative,
|
|
21
|
+
["data-testid"]: dataTestId,
|
|
22
|
+
...props
|
|
23
|
+
}: ContentBlockGroupProps) => {
|
|
24
|
+
const classes = cx([
|
|
25
|
+
className,
|
|
26
|
+
rootClassName,
|
|
27
|
+
{
|
|
28
|
+
[`${rootClassName}--negative`]: negative,
|
|
29
|
+
},
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const renderChildren = () =>
|
|
33
|
+
Children.map(children, (child) => {
|
|
34
|
+
if (React.isValidElement<ContentBlockProps>(child)) {
|
|
35
|
+
return React.cloneElement(child, { negative });
|
|
36
|
+
}
|
|
37
|
+
return child;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className={classes} data-testid={dataTestId} {...props}>
|
|
42
|
+
{renderChildren()}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
ContentBlockGroup.displayName = "ContentBlockGroup";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
@import "@purpurds/tokens/breakpoint/variables";
|
|
2
|
+
|
|
3
|
+
.purpur-content-block {
|
|
4
|
+
$root: &;
|
|
5
|
+
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: flex-start;
|
|
9
|
+
gap: var(--purpur-spacing-400);
|
|
10
|
+
|
|
11
|
+
@media (min-width: $purpur-breakpoint-lg) {
|
|
12
|
+
align-items: center;
|
|
13
|
+
flex-direction: row;
|
|
14
|
+
gap: var(--purpur-spacing-300);
|
|
15
|
+
|
|
16
|
+
&:nth-child(even) {
|
|
17
|
+
flex-direction: row-reverse;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#{$root}__section {
|
|
22
|
+
@media (min-width: $purpur-breakpoint-lg) {
|
|
23
|
+
width: 50%;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#{$root}__image-wrapper {
|
|
28
|
+
width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#{$root}__video-wrapper video,
|
|
32
|
+
#{$root}__image-wrapper img {
|
|
33
|
+
border-radius: var(--purpur-border-radius-md);
|
|
34
|
+
|
|
35
|
+
@media (min-width: $purpur-breakpoint-md) {
|
|
36
|
+
border-radius: var(--purpur-border-radius-lg);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#{$root}__content-wrapper {
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: column;
|
|
43
|
+
gap: var(--purpur-spacing-150);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#{$root}__badge {
|
|
47
|
+
width: fit-content;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#{$root}__content {
|
|
51
|
+
max-width: 40rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#{$root}__usp-list {
|
|
55
|
+
margin: 0;
|
|
56
|
+
padding: 0;
|
|
57
|
+
list-style: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#{$root}__usp-list-item {
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: left;
|
|
64
|
+
&:not(:last-child) {
|
|
65
|
+
margin-bottom: var(--purpur-spacing-100);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#{$root}__usp-list-item-icon {
|
|
70
|
+
margin-right: var(--purpur-spacing-50);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#{$root}__links {
|
|
74
|
+
margin-top: var(--purpur-spacing-250);
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
gap: var(--purpur-spacing-200);
|
|
78
|
+
|
|
79
|
+
@media (min-width: $purpur-breakpoint-md) {
|
|
80
|
+
flex-direction: row;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&--negative {
|
|
85
|
+
#{$root}__text-spacing p {
|
|
86
|
+
color: var(--purpur-color-text-default-negative);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#{$root}__usp-list-item-icon svg {
|
|
90
|
+
fill: var(--purpur-color-text-default-negative);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/* eslint-disable jsx-a11y/media-has-caption */
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { HeadingTag, TitleVariant } from "@purpurds/heading";
|
|
5
|
+
import { Paragraph } from "@purpurds/paragraph";
|
|
6
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
7
|
+
|
|
8
|
+
import "@purpurds/cta-link/styles";
|
|
9
|
+
import "@purpurds/heading/styles";
|
|
10
|
+
import "@purpurds/badge/styles";
|
|
11
|
+
import "@purpurds/icon/styles";
|
|
12
|
+
import { ContentBlock } from "./content-block";
|
|
13
|
+
import { ContentBlockGroup as ContentBlockGroupComponent } from "./content-block-group";
|
|
14
|
+
|
|
15
|
+
const mockData = [
|
|
16
|
+
{
|
|
17
|
+
id: "1",
|
|
18
|
+
content:
|
|
19
|
+
"Body text lorem ipsum dolor sit amet consectetur. At velit mi ultricies in orci est condimentum. Et venenatis fringilla vel dictum. Diam mattis ut hac dui lacus.",
|
|
20
|
+
title: "Title goes here lorem ipsum",
|
|
21
|
+
tagline: "Badge label",
|
|
22
|
+
usp: ["List item 1", "List item 2", "List item 3"],
|
|
23
|
+
imgUrl:
|
|
24
|
+
"https://www.telia.se/images/i15skfqwpurk/5YYelnwdIJGush05RYsE6A/04d4eeb571bca6d5c72b557f6da92c92/Telia_Company_Reinvention_69.jpg",
|
|
25
|
+
altText: "Familjens unga ser på tv tillsammans mysig stämning men spännande.",
|
|
26
|
+
callToAction: [
|
|
27
|
+
{
|
|
28
|
+
href: "/test",
|
|
29
|
+
text: "CTA link text",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
href: "/test2",
|
|
33
|
+
text: "CTA link text",
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "2",
|
|
39
|
+
content:
|
|
40
|
+
"Body text lorem ipsum dolor sit amet consectetur. At velit mi ultricies in orci est condimentum. Et venenatis fringilla vel dictum. Diam mattis ut hac dui lacus.",
|
|
41
|
+
title: "Title goes here lorem ipsum",
|
|
42
|
+
tagline: undefined,
|
|
43
|
+
imgUrl:
|
|
44
|
+
"https://www.telia.se/images/i15skfqwpurk/6QB5jxWQfeES4NN279kSYY/604e28cbc2fffb5e77bb0f978ec689d4/Telia_Company_071.jpg",
|
|
45
|
+
altText: "Manager i rutig kavaj skriver på laptop med whiteboard i bakgrunden.",
|
|
46
|
+
callToAction: [],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "3",
|
|
50
|
+
content:
|
|
51
|
+
"Body text lorem ipsum dolor sit amet consectetur. At velit mi ultricies in orci est condimentum. Et venenatis fringilla vel dictum. Diam mattis ut hac dui lacus.",
|
|
52
|
+
title: "Title goes here lorem ipsum",
|
|
53
|
+
tagline: undefined,
|
|
54
|
+
videoUrl:
|
|
55
|
+
"https://www.telia.se/videos/i15skfqwpurk/1HSDMQWf6TwfcCgtHCbi5w/f57ba932da3c3587cd1af11697ac265a/TSC_MobilVaxel_16x9_K2-2.mp4",
|
|
56
|
+
videoPosterImgUrl:
|
|
57
|
+
"https://www.telia.se/images/i15skfqwpurk/5p8TxmyWh57hZzgSoKxPmp/7177d8e471a4bef04c2526483fb84372/Telia_Smart_Connect_Video_Poster_Image.png",
|
|
58
|
+
callToAction: [],
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const meta = {
|
|
63
|
+
title: "Components/ContentBlock",
|
|
64
|
+
component: ContentBlock,
|
|
65
|
+
parameters: {
|
|
66
|
+
design: [
|
|
67
|
+
{
|
|
68
|
+
name: "ContentBlock",
|
|
69
|
+
type: "figma",
|
|
70
|
+
url: "https://www.figma.com/design/XEaIIFskrrxIBHMZDkIuIg/Purpur-DS---Component-library-%26-guidelines?node-id=33776-10598&t=iXwBPd5zGeNrD5Ou-0",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
argTypes: {
|
|
75
|
+
headingTag: {
|
|
76
|
+
control: "select",
|
|
77
|
+
options: Object.values(HeadingTag),
|
|
78
|
+
table: {
|
|
79
|
+
type: {
|
|
80
|
+
summary: Object.values(HeadingTag)
|
|
81
|
+
.map((x) => `"${x}"`)
|
|
82
|
+
.join(" | "),
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
headingVariant: {
|
|
87
|
+
control: "select",
|
|
88
|
+
options: Object.values(TitleVariant),
|
|
89
|
+
table: {
|
|
90
|
+
type: {
|
|
91
|
+
summary: Object.values(TitleVariant)
|
|
92
|
+
.map((x) => `"${x}"`)
|
|
93
|
+
.join(" | "),
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
} satisfies Meta<typeof ContentBlock>;
|
|
99
|
+
|
|
100
|
+
export default meta;
|
|
101
|
+
type Story = StoryObj<typeof ContentBlock>;
|
|
102
|
+
|
|
103
|
+
export const Showcase: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
negative: false,
|
|
106
|
+
ctas: mockData[0].callToAction,
|
|
107
|
+
title: mockData[0].title,
|
|
108
|
+
children: mockData[0].content,
|
|
109
|
+
image: (
|
|
110
|
+
<img
|
|
111
|
+
style={{
|
|
112
|
+
width: "100%",
|
|
113
|
+
objectFit: "cover",
|
|
114
|
+
}}
|
|
115
|
+
src={mockData[0].imgUrl}
|
|
116
|
+
alt={mockData[0].altText || ""}
|
|
117
|
+
/>
|
|
118
|
+
),
|
|
119
|
+
tagline: mockData[0].tagline,
|
|
120
|
+
usp: mockData[0].usp,
|
|
121
|
+
},
|
|
122
|
+
render: ({ children, ...args }) => (
|
|
123
|
+
<div
|
|
124
|
+
style={{
|
|
125
|
+
padding: "var(--purpur-spacing-250)",
|
|
126
|
+
background: args.negative ? "var(--purpur-color-purple-900)" : "transparent",
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
<ContentBlock {...args}>
|
|
130
|
+
<Paragraph negative={args.negative} variant="paragraph-100">
|
|
131
|
+
{children}
|
|
132
|
+
</Paragraph>
|
|
133
|
+
</ContentBlock>
|
|
134
|
+
</div>
|
|
135
|
+
),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const VideoContent: Story = {
|
|
139
|
+
args: {
|
|
140
|
+
negative: false,
|
|
141
|
+
ctas: mockData[0].callToAction,
|
|
142
|
+
title: mockData[0].title,
|
|
143
|
+
children: mockData[0].content,
|
|
144
|
+
video: (
|
|
145
|
+
<video
|
|
146
|
+
style={{ width: "100%" }}
|
|
147
|
+
controls
|
|
148
|
+
src={mockData[2].videoUrl}
|
|
149
|
+
poster={mockData[2].videoPosterImgUrl}
|
|
150
|
+
/>
|
|
151
|
+
),
|
|
152
|
+
tagline: mockData[0].tagline,
|
|
153
|
+
usp: mockData[0].usp,
|
|
154
|
+
},
|
|
155
|
+
render: ({ children, ...args }) => (
|
|
156
|
+
<div
|
|
157
|
+
style={{
|
|
158
|
+
padding: "var(--purpur-spacing-250)",
|
|
159
|
+
background: args.negative ? "var(--purpur-color-purple-900)" : "transparent",
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
<ContentBlock {...args}>
|
|
163
|
+
<Paragraph negative={args.negative} variant="paragraph-100">
|
|
164
|
+
{children}
|
|
165
|
+
</Paragraph>
|
|
166
|
+
</ContentBlock>
|
|
167
|
+
</div>
|
|
168
|
+
),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const ContentBlockGroup: Story = {
|
|
172
|
+
render: ({ negative }) => (
|
|
173
|
+
<div
|
|
174
|
+
style={{
|
|
175
|
+
padding: "var(--purpur-spacing-250)",
|
|
176
|
+
background: negative ? "var(--purpur-color-purple-900)" : "transparent",
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
<ContentBlockGroupComponent negative={negative}>
|
|
180
|
+
{mockData.map((item) => (
|
|
181
|
+
<ContentBlock
|
|
182
|
+
key={item.id}
|
|
183
|
+
negative={negative}
|
|
184
|
+
ctas={item.callToAction}
|
|
185
|
+
title={item.title}
|
|
186
|
+
tagline={item.tagline}
|
|
187
|
+
usp={item.usp}
|
|
188
|
+
image={
|
|
189
|
+
item.imgUrl ? (
|
|
190
|
+
<img
|
|
191
|
+
style={{
|
|
192
|
+
width: "100%",
|
|
193
|
+
objectFit: "cover",
|
|
194
|
+
}}
|
|
195
|
+
src={item.imgUrl}
|
|
196
|
+
alt={item.altText}
|
|
197
|
+
/>
|
|
198
|
+
) : undefined
|
|
199
|
+
}
|
|
200
|
+
video={
|
|
201
|
+
item.videoUrl ? (
|
|
202
|
+
<video
|
|
203
|
+
style={{ width: "100%" }}
|
|
204
|
+
controls
|
|
205
|
+
src={item.videoUrl}
|
|
206
|
+
poster={item.videoPosterImgUrl}
|
|
207
|
+
/>
|
|
208
|
+
) : undefined
|
|
209
|
+
}
|
|
210
|
+
>
|
|
211
|
+
<Paragraph negative={negative} variant="paragraph-100">
|
|
212
|
+
{item.content}
|
|
213
|
+
</Paragraph>
|
|
214
|
+
</ContentBlock>
|
|
215
|
+
))}
|
|
216
|
+
</ContentBlockGroupComponent>
|
|
217
|
+
</div>
|
|
218
|
+
),
|
|
219
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/* eslint-disable jsx-a11y/media-has-caption, react/jsx-wrap-multilines */
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import * as matchers from "@testing-library/jest-dom/matchers";
|
|
5
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
|
|
8
|
+
import { ContentBlock } from "./content-block";
|
|
9
|
+
|
|
10
|
+
expect.extend(matchers);
|
|
11
|
+
|
|
12
|
+
const mockData = {
|
|
13
|
+
title: "Title goes here lorem ipsum",
|
|
14
|
+
usp: ["List item 1", "List item 2"],
|
|
15
|
+
imgUrl: "https://www.telia.se/images",
|
|
16
|
+
imgAltText: "test image",
|
|
17
|
+
ctas: [
|
|
18
|
+
{
|
|
19
|
+
href: "/test",
|
|
20
|
+
text: "CTA link 1",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
href: "/test2",
|
|
24
|
+
text: "CTA link 2",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
content: "Test text content",
|
|
28
|
+
videoUrl: "https://www.telia.se/video.mp4",
|
|
29
|
+
videoPosterImgUrl: "https://www.telia.se/image.png",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const setup = () =>
|
|
33
|
+
render(
|
|
34
|
+
<ContentBlock
|
|
35
|
+
title={mockData.title}
|
|
36
|
+
usp={mockData.usp}
|
|
37
|
+
image={
|
|
38
|
+
<img
|
|
39
|
+
style={{
|
|
40
|
+
width: "100%",
|
|
41
|
+
objectFit: "cover",
|
|
42
|
+
}}
|
|
43
|
+
src={mockData.imgUrl}
|
|
44
|
+
alt={mockData.imgAltText}
|
|
45
|
+
/>
|
|
46
|
+
}
|
|
47
|
+
ctas={mockData.ctas}
|
|
48
|
+
>
|
|
49
|
+
{mockData.content}
|
|
50
|
+
</ContentBlock>
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
describe("ContentBlock", () => {
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
vi.clearAllMocks();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
afterEach(cleanup);
|
|
59
|
+
|
|
60
|
+
it("should render title", () => {
|
|
61
|
+
setup();
|
|
62
|
+
|
|
63
|
+
expect(screen.getByRole("heading", { level: 2, name: mockData.title }));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should render usp list", () => {
|
|
67
|
+
setup();
|
|
68
|
+
|
|
69
|
+
expect(screen.getByRole("list")).toBeInTheDocument();
|
|
70
|
+
|
|
71
|
+
const listItems = screen.getAllByRole("listitem");
|
|
72
|
+
expect(listItems).toHaveLength(mockData.usp.length);
|
|
73
|
+
|
|
74
|
+
listItems.forEach((item, index) => {
|
|
75
|
+
expect(item).toHaveTextContent(mockData.usp[index]);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should render ctas", () => {
|
|
80
|
+
setup();
|
|
81
|
+
|
|
82
|
+
mockData.ctas.forEach((cta) => {
|
|
83
|
+
const link = screen.getByRole("link", { name: cta.text });
|
|
84
|
+
expect(link).toBeInTheDocument();
|
|
85
|
+
expect(link).toHaveAttribute("href", cta.href);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should render richText content", () => {
|
|
90
|
+
setup();
|
|
91
|
+
|
|
92
|
+
screen.getByText(mockData.content);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should render image", () => {
|
|
96
|
+
setup();
|
|
97
|
+
|
|
98
|
+
const img = screen.getByAltText(mockData.imgAltText);
|
|
99
|
+
expect(img).toBeInTheDocument();
|
|
100
|
+
|
|
101
|
+
expect(img).toHaveAttribute("src", mockData.imgUrl);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should render video", () => {
|
|
105
|
+
render(
|
|
106
|
+
<ContentBlock
|
|
107
|
+
title={mockData.title}
|
|
108
|
+
usp={mockData.usp}
|
|
109
|
+
ctas={mockData.ctas}
|
|
110
|
+
video={
|
|
111
|
+
<video
|
|
112
|
+
data-testid="content-block-video"
|
|
113
|
+
controls
|
|
114
|
+
src={mockData.videoUrl}
|
|
115
|
+
poster={mockData.videoPosterImgUrl}
|
|
116
|
+
/>
|
|
117
|
+
}
|
|
118
|
+
data-testid="content-block"
|
|
119
|
+
>
|
|
120
|
+
{mockData.content}
|
|
121
|
+
</ContentBlock>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const img = screen.queryByAltText(mockData.imgAltText);
|
|
125
|
+
// img should not exist when video is presented
|
|
126
|
+
expect(img).not.toBeInTheDocument();
|
|
127
|
+
|
|
128
|
+
const video = screen.getByTestId("content-block-video");
|
|
129
|
+
expect(video).toBeInTheDocument();
|
|
130
|
+
|
|
131
|
+
expect(video).toHaveAttribute("src", mockData.videoUrl);
|
|
132
|
+
|
|
133
|
+
expect(video).toHaveAttribute("poster", mockData.videoPosterImgUrl);
|
|
134
|
+
});
|
|
135
|
+
});
|