@orangesk/orange-design-system 2.1.0 → 3.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/build/components/Accordion/style.css +1 -1
- package/build/components/Accordion/style.css.map +1 -1
- package/build/components/BlockAction/style.css +1 -1
- package/build/components/BlockAction/style.css.map +1 -1
- package/build/components/Card/style.css +1 -1
- package/build/components/Card/style.css.map +1 -1
- package/build/components/Carousel/style.css +1 -1
- package/build/components/Carousel/style.css.map +1 -1
- package/build/components/CarouselHero/style.css +1 -1
- package/build/components/CarouselHero/style.css.map +1 -1
- package/build/components/CarouselPromotions/style.css +1 -1
- package/build/components/CarouselPromotions/style.css.map +1 -1
- package/build/components/Cover/style.css +1 -1
- package/build/components/Cover/style.css.map +1 -1
- package/build/components/Dropdown/style.css +1 -1
- package/build/components/Dropdown/style.css.map +1 -1
- package/build/components/FeatureAccordion/style.css +1 -1
- package/build/components/FeatureAccordion/style.css.map +1 -1
- package/build/components/Footer/style.css +1 -1
- package/build/components/Footer/style.css.map +1 -1
- package/build/components/Icon/style.css +1 -1
- package/build/components/Icon/style.css.map +1 -1
- package/build/components/IconList/style.css +1 -1
- package/build/components/IconList/style.css.map +1 -1
- package/build/components/Link/style.css +1 -1
- package/build/components/Link/style.css.map +1 -1
- package/build/components/Megamenu/style.css +1 -1
- package/build/components/Megamenu/style.css.map +1 -1
- package/build/components/PromoBanner/style.css +1 -1
- package/build/components/PromoBanner/style.css.map +1 -1
- package/build/components/Tile/style.css +1 -1
- package/build/components/Tile/style.css.map +1 -1
- package/build/components/Timeline/style.css +2 -0
- package/build/components/Timeline/style.css.map +1 -0
- package/build/components/Tooltip/style.css +1 -1
- package/build/components/Tooltip/style.css.map +1 -1
- package/build/components/index.js +1 -1
- package/build/components/index.js.map +1 -1
- package/build/components/tsconfig.tsbuildinfo +1 -1
- package/build/components/types/index.d.ts +30 -2
- package/build/components/types/src/components/FeatureAccordion/FeatureAccordion.d.ts +3 -0
- package/build/components/types/src/components/FeatureAccordion/FeatureAccordion.static.d.ts +0 -3
- package/build/components/types/src/components/FeatureAccordion/FeatureAccordionItem.d.ts +2 -1
- package/build/components/types/src/components/Timeline/Timeline.d.ts +13 -0
- package/build/components/types/src/components/Timeline/index.d.ts +2 -0
- package/build/components/types/src/components/index.d.ts +2 -1
- package/build/images/card-device-example.png +0 -0
- package/build/lib/base.css +1 -1
- package/build/lib/base.css.map +1 -1
- package/build/lib/components.css +1 -1
- package/build/lib/components.css.map +1 -1
- package/build/lib/footer.css +1 -1
- package/build/lib/footer.css.map +1 -1
- package/build/lib/footer.js +1 -1
- package/build/lib/footer.js.map +1 -1
- package/build/lib/megamenu.css +1 -1
- package/build/lib/megamenu.css.map +1 -1
- package/build/lib/megamenu.js +1 -1
- package/build/lib/megamenu.js.map +1 -1
- package/build/lib/scripts.js +1 -1
- package/build/lib/scripts.js.map +1 -1
- package/build/lib/style.css +1 -1
- package/build/lib/style.css.map +1 -1
- package/build/search-index.json +10 -6
- package/build/sprite.svg +1 -1
- package/package.json +21 -21
- package/src/assets/icons/pictogram-communication--dark.svg +4 -0
- package/src/assets/icons/pictogram-communication.svg +4 -0
- package/src/assets/icons/pictogram-coverage--dark.svg +5 -0
- package/src/assets/icons/pictogram-coverage.svg +5 -0
- package/src/assets/icons/pictogram-credit--dark.svg +7 -0
- package/src/assets/icons/pictogram-credit.svg +6 -0
- package/src/assets/icons/pictogram-disney--dark.svg +5 -14
- package/src/assets/icons/pictogram-disney.svg +5 -9
- package/src/assets/icons/pictogram-modem--dark.svg +9 -0
- package/src/assets/icons/pictogram-modem.svg +9 -0
- package/src/assets/icons/pictogram-speed--dark.svg +7 -0
- package/src/assets/icons/pictogram-speed.svg +7 -0
- package/src/assets/icons/pictogram-sunset--dark.svg +10 -0
- package/src/assets/icons/pictogram-sunset.svg +10 -0
- package/src/assets/icons/pictogram-tariffs--dark.svg +4 -8
- package/src/assets/icons/pictogram-tariffs.svg +4 -8
- package/src/assets/icons/pictogram-wallet--dark.svg +6 -0
- package/src/assets/icons/pictogram-wallet.svg +6 -0
- package/src/components/Accordion/styles/mixins.scss +2 -4
- package/src/components/BlockAction/styles/mixins.scss +1 -1
- package/src/components/BlockAction/styles/style.scss +1 -1
- package/src/components/BlockAction/tests/BlockAction.styles.unit.test.jsx +18 -0
- package/src/components/Card/styles/mixins.scss +4 -0
- package/src/components/Card/styles/style.scss +1 -0
- package/src/components/Carousel/Carousel.tsx +6 -3
- package/src/components/CarouselHero/styles/mixins.scss +1 -3
- package/src/components/CarouselPromotions/styles/mixins.scss +2 -2
- package/src/components/Cover/styles/mixins.scss +2 -0
- package/src/components/FeatureAccordion/FeatureAccordion.static.ts +3 -71
- package/src/components/FeatureAccordion/FeatureAccordion.tsx +23 -59
- package/src/components/FeatureAccordion/FeatureAccordionItem.tsx +9 -14
- package/src/components/FeatureAccordion/styles/mixins.scss +67 -23
- package/src/components/FeatureAccordion/tests/FeatureAccordion.unit.test.jsx +9 -0
- package/src/components/Forms/File/styles/style.scss +1 -1
- package/src/components/Forms/styles/mixins.scss +1 -1
- package/src/components/Icon/styles/mixins.scss +4 -4
- package/src/components/IconList/styles/mixins.scss +2 -1
- package/src/components/Link/styles/style.scss +4 -1
- package/src/components/PromoBanner/styles/mixins.scss +2 -0
- package/src/components/Tile/styles/mixins.scss +25 -7
- package/src/components/Tile/styles/style.scss +5 -1
- package/src/components/Timeline/Timeline.tsx +60 -0
- package/src/components/Timeline/index.ts +2 -0
- package/src/components/Timeline/styles/config.scss +25 -0
- package/src/components/Timeline/styles/mixins.scss +102 -0
- package/src/components/Timeline/styles/style.scss +60 -0
- package/src/components/Timeline/tests/Timeline.conformance.test.jsx +29 -0
- package/src/components/Timeline/tests/Timeline.unit.test.jsx +89 -0
- package/src/components/index.ts +2 -0
- package/src/styles/tokens/base.scss +0 -16
- package/src/styles/typography/mixins.scss +13 -2
|
@@ -1,48 +1,92 @@
|
|
|
1
|
-
@use "sass:map" as sass-map;
|
|
2
1
|
@use "../../../styles/tools/convert";
|
|
3
|
-
@use "../../../styles/tools/generate";
|
|
4
2
|
@use "../../../styles/tokens/space";
|
|
5
3
|
@use "../../../styles/tokens/breakpoint";
|
|
6
4
|
|
|
5
|
+
$preview-width: convert.to-rem(640px);
|
|
6
|
+
$preview-height: convert.to-rem(480px);
|
|
7
|
+
|
|
8
|
+
@mixin desktop-layout {
|
|
9
|
+
@include breakpoint.get("xl") {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: flex-start;
|
|
12
|
+
min-height: $preview-height;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
@mixin base {
|
|
8
17
|
color: var(--color-text-default);
|
|
9
18
|
background-color: var(--color-background-secondary);
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
width: 100%;
|
|
20
|
+
@include desktop-layout();
|
|
12
21
|
}
|
|
13
22
|
|
|
14
23
|
@mixin accordion {
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
width: 100%;
|
|
25
|
+
|
|
26
|
+
@include breakpoint.get("xl") {
|
|
27
|
+
flex: 1 1 0;
|
|
28
|
+
min-width: 0;
|
|
29
|
+
min-height: $preview-height;
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
padding: convert.to-rem(40px) convert.to-rem(60px);
|
|
34
|
+
}
|
|
17
35
|
|
|
18
36
|
.accordion {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@include breakpoint.get("md", "down") {
|
|
22
|
-
margin: space.get("xlarge") space.get("large");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
@include breakpoint.get("sm", "down") {
|
|
26
|
-
margin: 0;
|
|
27
|
-
}
|
|
37
|
+
width: 100%;
|
|
38
|
+
margin: 0;
|
|
28
39
|
}
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
@mixin preview {
|
|
32
|
-
display:
|
|
33
|
-
|
|
43
|
+
display: block;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
width: 100%;
|
|
47
|
+
|
|
48
|
+
@include breakpoint.get("xl") {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex: 0 0 $preview-width;
|
|
51
|
+
width: $preview-width;
|
|
52
|
+
align-self: flex-start;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@include breakpoint.get("xl") {
|
|
56
|
+
align-items: stretch;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@include breakpoint.get("xl", "down") {
|
|
60
|
+
display: block;
|
|
61
|
+
height: auto;
|
|
62
|
+
}
|
|
34
63
|
|
|
35
64
|
.img {
|
|
36
|
-
margin-bottom:
|
|
65
|
+
margin-bottom: 0;
|
|
37
66
|
width: 100%;
|
|
67
|
+
|
|
68
|
+
@include breakpoint.get("xl") {
|
|
69
|
+
width: 100%;
|
|
70
|
+
height: auto;
|
|
71
|
+
}
|
|
38
72
|
}
|
|
39
73
|
|
|
40
74
|
img {
|
|
75
|
+
display: block;
|
|
76
|
+
max-width: 100%;
|
|
41
77
|
width: 100%;
|
|
42
|
-
|
|
43
|
-
object-
|
|
44
|
-
|
|
45
|
-
|
|
78
|
+
object-fit: contain;
|
|
79
|
+
object-position: right center;
|
|
80
|
+
margin: 0 !important;
|
|
81
|
+
|
|
82
|
+
@include breakpoint.get("xl") {
|
|
83
|
+
height: auto;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@include breakpoint.get("xl", "down") {
|
|
87
|
+
height: auto;
|
|
88
|
+
width: 100%;
|
|
89
|
+
}
|
|
46
90
|
}
|
|
47
91
|
}
|
|
48
92
|
|
|
@@ -74,7 +118,7 @@
|
|
|
74
118
|
margin-bottom: 0;
|
|
75
119
|
}
|
|
76
120
|
|
|
77
|
-
@include breakpoint.get("
|
|
121
|
+
@include breakpoint.get("lg", "down") {
|
|
78
122
|
.accordion__header,
|
|
79
123
|
.feature-accordion__content {
|
|
80
124
|
padding-left: convert.to-rem(15px);
|
|
@@ -92,4 +92,13 @@ describe("FeatureAccordion", () => {
|
|
|
92
92
|
const { container } = renderAccordion({ "data-testid": "fa" });
|
|
93
93
|
expect(container.firstChild).toHaveAttribute("data-testid", "fa");
|
|
94
94
|
});
|
|
95
|
+
|
|
96
|
+
it("applies previewClassName to preview area", () => {
|
|
97
|
+
const { container } = renderAccordion({
|
|
98
|
+
previewClassName: "align-self-middle--xl",
|
|
99
|
+
});
|
|
100
|
+
expect(container.querySelector(".feature-accordion__preview")).toHaveClass(
|
|
101
|
+
"align-self-middle--xl",
|
|
102
|
+
);
|
|
103
|
+
});
|
|
95
104
|
});
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
@use "../../../styles/tools/generate";
|
|
4
4
|
|
|
5
5
|
@mixin base() {
|
|
6
|
+
--icon-size: 1em;
|
|
6
7
|
fill: currentColor;
|
|
7
|
-
width:
|
|
8
|
-
height:
|
|
8
|
+
width: var(--icon-size);
|
|
9
|
+
height: var(--icon-size);
|
|
9
10
|
vertical-align: text-bottom;
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -14,8 +15,7 @@
|
|
|
14
15
|
@include breakpoint.get($breakpoint) {
|
|
15
16
|
@each $variant, $size in $props {
|
|
16
17
|
&#{generate.variant-name($variant)} {
|
|
17
|
-
|
|
18
|
-
height: $size;
|
|
18
|
+
--icon-size: #{$size};
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
@use "../../../styles/tokens/space";
|
|
2
2
|
@use "../../../styles/tools/convert";
|
|
3
3
|
@use "../../../styles/typography/mixins" as typoMixins;
|
|
4
|
+
@use "../../../styles/tools/layout";
|
|
4
5
|
@use "./config";
|
|
5
6
|
|
|
6
7
|
@mixin base() {
|
|
7
8
|
@include typoMixins.list-unstyled;
|
|
8
9
|
@include typoMixins.text-readable;
|
|
10
|
+
@include layout.reset-last-child-margin-bottom;
|
|
9
11
|
|
|
10
12
|
margin: 0 0 space.get("large") 0;
|
|
11
13
|
padding: 0;
|
|
@@ -25,7 +27,6 @@
|
|
|
25
27
|
|
|
26
28
|
@mixin content() {
|
|
27
29
|
flex: 1 0 1px;
|
|
28
|
-
margin-top: convert.to-rem(2px);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
@mixin content-text() {
|
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
color: var(--color-text-accent);
|
|
28
28
|
text-decoration: underline;
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
&.text-accent:hover {
|
|
31
|
+
color: var(--color-text-default) !important;
|
|
32
|
+
}
|
|
33
|
+
&:focus-visible {
|
|
31
34
|
outline: 2px solid var(--color-border-contrast);
|
|
32
35
|
outline-offset: 2px;
|
|
33
36
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@use "sass:map" as sassmap;
|
|
2
2
|
@use "./../../../styles/tools/generate";
|
|
3
|
+
@use "./../../../styles/tools/layout";
|
|
3
4
|
@use "./../../../styles/tokens/breakpoint";
|
|
4
5
|
@use "./../../../styles/tokens/space";
|
|
5
6
|
@use "./../../Section/styles/mixins" as section;
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
flex-direction: column;
|
|
27
28
|
justify-content: center;
|
|
28
29
|
min-width: 0;
|
|
30
|
+
@include layout.reset-last-child-margin-bottom;
|
|
29
31
|
|
|
30
32
|
> * {
|
|
31
33
|
width: fit-content;
|
|
@@ -1,6 +1,21 @@
|
|
|
1
|
+
@use "sass:map";
|
|
1
2
|
@use "../../../styles/tokens/space";
|
|
2
3
|
@use "../../../styles/tokens/breakpoint";
|
|
3
4
|
@use "../../../styles/tools/convert";
|
|
5
|
+
@use "../../Icon/styles/config" as icon-config;
|
|
6
|
+
|
|
7
|
+
@function build-unsized-icon-selector($selector) {
|
|
8
|
+
$result: $selector;
|
|
9
|
+
|
|
10
|
+
@each $size-name in map.keys(map.get(icon-config.$sizes, default)) {
|
|
11
|
+
$result: "#{$result}:not(.icon--#{$size-name})";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@return $result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
$tile-unsized-icon-selector: build-unsized-icon-selector(">.icon") + ", " +
|
|
18
|
+
build-unsized-icon-selector(">svg");
|
|
4
19
|
|
|
5
20
|
@mixin tile-base {
|
|
6
21
|
display: flex;
|
|
@@ -19,6 +34,7 @@
|
|
|
19
34
|
display: flex;
|
|
20
35
|
flex-direction: column;
|
|
21
36
|
align-items: flex-start;
|
|
37
|
+
justify-content: space-between;
|
|
22
38
|
gap: space.get("medium");
|
|
23
39
|
padding: space.get("medium");
|
|
24
40
|
z-index: 1;
|
|
@@ -27,18 +43,20 @@
|
|
|
27
43
|
margin-bottom: 0;
|
|
28
44
|
}
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
@include breakpoint.get("md") {
|
|
47
|
+
flex-direction: column;
|
|
48
|
+
padding: space.get("large");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@mixin tile-body-icon-ratio {
|
|
53
|
+
#{$tile-unsized-icon-selector} {
|
|
32
54
|
width: convert.to-rem(24px);
|
|
33
55
|
height: convert.to-rem(24px);
|
|
34
56
|
}
|
|
35
57
|
|
|
36
58
|
@include breakpoint.get("md") {
|
|
37
|
-
|
|
38
|
-
padding: space.get("large");
|
|
39
|
-
|
|
40
|
-
> .icon,
|
|
41
|
-
> svg {
|
|
59
|
+
#{$tile-unsized-icon-selector} {
|
|
42
60
|
width: convert.to-rem(32px);
|
|
43
61
|
height: convert.to-rem(32px);
|
|
44
62
|
}
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
@include mixins.tile-body;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
&:not(.tile--action) .tile__body {
|
|
12
|
+
@include mixins.tile-body-icon-ratio;
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
&--compact .tile__body {
|
|
12
16
|
@include mixins.tile-body-compact;
|
|
13
17
|
}
|
|
@@ -19,7 +23,7 @@
|
|
|
19
23
|
@include mixins.tile-action;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
.icon {
|
|
26
|
+
.tile__body > .icon:last-child {
|
|
23
27
|
@include mixins.tile-action-icon;
|
|
24
28
|
}
|
|
25
29
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import cx from "classnames";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
type HeadingTag = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
5
|
+
|
|
6
|
+
export interface TimelineItem {
|
|
7
|
+
description: React.ReactNode;
|
|
8
|
+
label?: React.ReactNode;
|
|
9
|
+
title: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TimelineProps
|
|
13
|
+
extends React.OlHTMLAttributes<HTMLOListElement> {
|
|
14
|
+
items: TimelineItem[];
|
|
15
|
+
titleTag?: HeadingTag;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const CLASS_ROOT = "timeline";
|
|
19
|
+
|
|
20
|
+
const Timeline: React.FC<TimelineProps> = ({
|
|
21
|
+
items,
|
|
22
|
+
className,
|
|
23
|
+
start = 1,
|
|
24
|
+
titleTag: TitleTag = "h3",
|
|
25
|
+
...other
|
|
26
|
+
}) => {
|
|
27
|
+
return (
|
|
28
|
+
<ol className={cx(CLASS_ROOT, className)} start={start} {...other}>
|
|
29
|
+
{items.map((item, index) => {
|
|
30
|
+
const itemNumber = start + index;
|
|
31
|
+
const label = item.label ?? null;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<li
|
|
35
|
+
key={`${CLASS_ROOT}-item-${itemNumber}`}
|
|
36
|
+
className={`${CLASS_ROOT}__item`}
|
|
37
|
+
>
|
|
38
|
+
<div className={`${CLASS_ROOT}__number-column`} aria-hidden="true">
|
|
39
|
+
<span className={`${CLASS_ROOT}__number`}>{itemNumber}</span>
|
|
40
|
+
{index !== items.length - 1 && (
|
|
41
|
+
<div className={`${CLASS_ROOT}__line`} />
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
<span className={`${CLASS_ROOT}__label`}>{label}</span>
|
|
45
|
+
<TitleTag className={`${CLASS_ROOT}__headline`}>
|
|
46
|
+
{item.title}
|
|
47
|
+
</TitleTag>
|
|
48
|
+
<div className={`${CLASS_ROOT}__description`}>
|
|
49
|
+
{item.description}
|
|
50
|
+
</div>
|
|
51
|
+
</li>
|
|
52
|
+
);
|
|
53
|
+
})}
|
|
54
|
+
</ol>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
Timeline.displayName = "Timeline";
|
|
59
|
+
|
|
60
|
+
export { Timeline };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@use "sass:map";
|
|
2
|
+
@use "../../../styles/tools/convert";
|
|
3
|
+
@use "../../../styles/typography/config" as typography;
|
|
4
|
+
|
|
5
|
+
$number-column-width: convert.to-rem(64px);
|
|
6
|
+
$column-gap: convert.to-rem(20px);
|
|
7
|
+
$content-max-width: convert.to-rem(420px);
|
|
8
|
+
$line-width: convert.to-rem(2px);
|
|
9
|
+
$label-slot-height: convert.to-rem(20px);
|
|
10
|
+
$label-gap: convert.to-rem(5px);
|
|
11
|
+
$label-space: $label-slot-height + $label-gap;
|
|
12
|
+
$line-indent: convert.to-rem(30px);
|
|
13
|
+
$item-spacing-bottom: convert.to-rem(25px);
|
|
14
|
+
|
|
15
|
+
$headline: map.get(typography.$headings, 3);
|
|
16
|
+
$body: map.get(typography.$body-text, default);
|
|
17
|
+
$caption: (
|
|
18
|
+
default: map.merge(
|
|
19
|
+
map.get(typography.$caption, small),
|
|
20
|
+
(
|
|
21
|
+
font-weight: 700,
|
|
22
|
+
letter-spacing: convert.to-rem(0px),
|
|
23
|
+
)
|
|
24
|
+
),
|
|
25
|
+
);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
@use "./config";
|
|
2
|
+
@use "../../../styles/tools/generate";
|
|
3
|
+
@use "../../../styles/tools/convert";
|
|
4
|
+
@use "../../../styles/tokens/breakpoint";
|
|
5
|
+
@use "../../../styles/tokens/space";
|
|
6
|
+
|
|
7
|
+
@mixin timeline() {
|
|
8
|
+
list-style: none;
|
|
9
|
+
margin: 0 0 space.get("large") 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@mixin item() {
|
|
14
|
+
display: grid;
|
|
15
|
+
grid-template-columns: config.$number-column-width minmax(0, 1fr);
|
|
16
|
+
column-gap: convert.to-rem(10px);
|
|
17
|
+
align-items: stretch;
|
|
18
|
+
padding-bottom: config.$item-spacing-bottom;
|
|
19
|
+
|
|
20
|
+
@include breakpoint.get("md") {
|
|
21
|
+
column-gap: convert.to-rem(20px);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@mixin item-last-child() {
|
|
26
|
+
padding-bottom: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@mixin number-column() {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
align-items: center;
|
|
33
|
+
width: config.$number-column-width;
|
|
34
|
+
grid-column: 1;
|
|
35
|
+
grid-row: 1 / span 3;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@mixin number() {
|
|
39
|
+
@include generate.responsive-css-map(config.$headline);
|
|
40
|
+
color: var(--color-text-accent);
|
|
41
|
+
display: block;
|
|
42
|
+
text-align: center;
|
|
43
|
+
margin: 0;
|
|
44
|
+
margin-top: config.$label-space;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@mixin number-first-item() {
|
|
48
|
+
margin-top: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@mixin line() {
|
|
52
|
+
flex: 1 1 auto;
|
|
53
|
+
width: config.$line-width;
|
|
54
|
+
background-color: var(--color-border-strong);
|
|
55
|
+
margin-bottom: convert.to-rem(-30px);
|
|
56
|
+
margin-top: config.$line-indent;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@mixin label() {
|
|
60
|
+
@include generate.responsive-css-map(config.$caption);
|
|
61
|
+
color: var(--color-text-default);
|
|
62
|
+
height: config.$label-slot-height;
|
|
63
|
+
min-height: config.$label-slot-height;
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
margin: 0;
|
|
67
|
+
grid-column: 2;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@mixin label-spaced() {
|
|
71
|
+
margin-bottom: config.$label-gap;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@mixin empty-label() {
|
|
75
|
+
height: 0;
|
|
76
|
+
min-height: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@mixin headline() {
|
|
80
|
+
@include generate.responsive-css-map(config.$headline);
|
|
81
|
+
color: var(--color-text-default);
|
|
82
|
+
margin: 0 0 space.get("small");
|
|
83
|
+
grid-column: 2;
|
|
84
|
+
max-width: config.$content-max-width;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
|
|
87
|
+
> :last-child {
|
|
88
|
+
margin-bottom: 0;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@mixin description() {
|
|
93
|
+
@include generate.responsive-css-map(config.$body);
|
|
94
|
+
color: var(--color-text-default);
|
|
95
|
+
grid-column: 2;
|
|
96
|
+
max-width: config.$content-max-width;
|
|
97
|
+
min-width: 0;
|
|
98
|
+
|
|
99
|
+
> :last-child {
|
|
100
|
+
margin-bottom: 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
@use "./config";
|
|
2
|
+
@use "./mixins";
|
|
3
|
+
|
|
4
|
+
@layer components {
|
|
5
|
+
.timeline {
|
|
6
|
+
@include mixins.timeline();
|
|
7
|
+
|
|
8
|
+
&__item {
|
|
9
|
+
@include mixins.item();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&__item:last-child {
|
|
13
|
+
@include mixins.item-last-child();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&__number-column {
|
|
17
|
+
@include mixins.number-column();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&__number {
|
|
21
|
+
@include mixins.number();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&__line {
|
|
25
|
+
@include mixins.line();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&__label {
|
|
29
|
+
@include mixins.label();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&__item:not(:first-child) &__label {
|
|
33
|
+
@include mixins.label-spaced();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&__item:first-child &__number {
|
|
37
|
+
@include mixins.number-first-item();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&__item:first-child:has(.timeline__label:not(:empty)) &__number {
|
|
41
|
+
margin-top: config.$label-space;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&__item:first-child:has(.timeline__label:not(:empty)) &__label {
|
|
45
|
+
@include mixins.label-spaced();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&__item:first-child &__label:empty {
|
|
49
|
+
@include mixins.empty-label();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&__headline {
|
|
53
|
+
@include mixins.headline();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&__description {
|
|
57
|
+
@include mixins.description();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
import { axe } from "vitest-axe";
|
|
3
|
+
|
|
4
|
+
import { Timeline } from "../";
|
|
5
|
+
|
|
6
|
+
const example = (
|
|
7
|
+
<Timeline
|
|
8
|
+
items={[
|
|
9
|
+
{
|
|
10
|
+
title: "Lorem ipsum dolor",
|
|
11
|
+
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
title: "Dolor sit amet",
|
|
15
|
+
description: "Sed do eiusmod tempor incididunt ut labore.",
|
|
16
|
+
},
|
|
17
|
+
]}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
it("is valid html", () => {
|
|
22
|
+
const { container } = render(example);
|
|
23
|
+
expect(container).toHTMLValidate();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("is accessible", async () => {
|
|
27
|
+
const { container } = render(example);
|
|
28
|
+
expect(await axe(container)).toHaveNoViolations();
|
|
29
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import { Timeline } from "../Timeline";
|
|
4
|
+
|
|
5
|
+
const items = [
|
|
6
|
+
{
|
|
7
|
+
title: "First item",
|
|
8
|
+
description: "First description",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
title: "Second item",
|
|
12
|
+
description: "Second description",
|
|
13
|
+
label: "June 2026",
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
describe("rendering Timeline", () => {
|
|
18
|
+
it("has default class timeline", () => {
|
|
19
|
+
const { getByTestId } = render(
|
|
20
|
+
<Timeline data-testid="test-id" items={items} />,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
expect(getByTestId("test-id")).toHaveClass("timeline");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("renders item for each timeline entry", () => {
|
|
27
|
+
const { container } = render(<Timeline items={items} />);
|
|
28
|
+
|
|
29
|
+
expect(container.getElementsByClassName("timeline__item")).toHaveLength(2);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("renders connector only for non-last items", () => {
|
|
33
|
+
const { container } = render(<Timeline items={items} />);
|
|
34
|
+
const renderedItems = container.getElementsByClassName("timeline__item");
|
|
35
|
+
|
|
36
|
+
expect(
|
|
37
|
+
renderedItems[0].getElementsByClassName("timeline__line"),
|
|
38
|
+
).toHaveLength(1);
|
|
39
|
+
expect(
|
|
40
|
+
renderedItems[1].getElementsByClassName("timeline__line"),
|
|
41
|
+
).toHaveLength(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("has additional class when className is set", () => {
|
|
45
|
+
const { getByTestId } = render(
|
|
46
|
+
<Timeline data-testid="test-id" items={items} className="test-class" />,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(getByTestId("test-id")).toHaveClass("timeline");
|
|
50
|
+
expect(getByTestId("test-id")).toHaveClass("test-class");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("renders label slots for all items and visible text only for labeled items", () => {
|
|
54
|
+
const { container, getByText } = render(<Timeline items={items} />);
|
|
55
|
+
const renderedLabels = container.getElementsByClassName("timeline__label");
|
|
56
|
+
|
|
57
|
+
expect(renderedLabels).toHaveLength(2);
|
|
58
|
+
expect(getByText("June 2026")).toBeInTheDocument();
|
|
59
|
+
expect(renderedLabels[0].textContent).toBe("");
|
|
60
|
+
expect(renderedLabels[1].textContent).toBe("June 2026");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("uses start prop for visible numbering", () => {
|
|
64
|
+
const { container } = render(<Timeline items={items} start={4} />);
|
|
65
|
+
const renderedNumbers =
|
|
66
|
+
container.getElementsByClassName("timeline__number");
|
|
67
|
+
|
|
68
|
+
expect(renderedNumbers[0].textContent).toBe("4");
|
|
69
|
+
expect(renderedNumbers[1].textContent).toBe("5");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("renders `h3` headlines by default", () => {
|
|
73
|
+
const { container } = render(<Timeline items={items} />);
|
|
74
|
+
const renderedHeadlines = container.querySelectorAll(
|
|
75
|
+
"h3.timeline__headline",
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
expect(renderedHeadlines).toHaveLength(2);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("renders custom headline tag when titleTag is set", () => {
|
|
82
|
+
const { container } = render(<Timeline items={items} titleTag="h4" />);
|
|
83
|
+
const renderedHeadlines = container.querySelectorAll(
|
|
84
|
+
"h4.timeline__headline",
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(renderedHeadlines).toHaveLength(2);
|
|
88
|
+
});
|
|
89
|
+
});
|