@orangesk/orange-design-system 2.0.0-beta.48 → 2.0.0-beta.49
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/Card/style.css +1 -1
- package/build/components/Card/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/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 +1 -0
- package/build/components/types/src/components/CarouselHero/CarouselHero.d.ts +1 -0
- package/build/lib/components.css +1 -1
- package/build/lib/components.css.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 +1 -1
- package/package.json +7 -7
- package/src/components/Card/styles/config.scss +1 -1
- package/src/components/CarouselHero/CarouselHero.static.ts +7 -0
- package/src/components/CarouselHero/CarouselHero.tsx +61 -3
- package/src/components/CarouselHero/tests/CarouselHero.unit.test.jsx +31 -1
- package/src/components/Tile/styles/mixins.scss +12 -0
package/build/search-index.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"href": "/components/alert",
|
|
16
|
-
"content": "Alert If you need to notify user about something specific, or if you want to explain something shortly, this is the element to do it. Variants Title Title and Description Title and Buttons Prihlásiť sa, Skryť, ]} /> Title, Description and
|
|
16
|
+
"content": "Alert If you need to notify user about something specific, or if you want to explain something shortly, this is the element to do it. Variants Title Title and Description Title and Buttons Prihlásiť sa, Skryť, ]} /> Title, Description and Links Popis toho čo by bolo dobré vysvetliť. Aj na viac riadkov. Niekedy veci nie sú samozrejmé, ale ak sa ich pokúsime vysvetliť, tak sa môžme stretnúť s pochopením. </p> } actionButtons={[ Link Base , Link Base , ]} /> Types There are four types of alert messages you can choose from. - Info - Success - Warning - Danger Info info is default type of alert. It's used for simple, not most important, messages for the user if something happend. }> Success Best feeling is when something went well. You can notify the user about successful things with success type. Warning If you need to warn user about anything use warning type. Danger If warning is not enought and you need to show error message, use danger type. Custom title renderer Alert spacing is quite strict and works 99% of time. In rare cases, the title requires a little more space to breathe. To achieve that, you can render a custom title using the renderTitle prop. ( <h3 className=\"alerttitle mb-medium\">{props.title}</h3> )} description={ } actionButtons={Prihlásiť sa} /> Full width If you need full width Alert, you can use isFullWidth prop. Max width on text remamains the same, because of readability. API React Props | Prop | Type | Default | Description | | --------------- | ---------------------------------------------- | -------- | ---------------------------------------------------------- | | title | string | - | Required.** Main text message | | description | React.ReactNode | - | Description of the alert | | type | \"info\" \\| \"success\" \\| \"warning\" \\| \"danger\" | \"info\" | Type of alert, sets icon and color | | headingLevel | number | 3 | Level of the heading in heading hierarchy (1-6) | | isFullWidth | boolean | - | Sets Alert to full width | | actionButtons | React.ReactNode | - | Additional buttons or links to add interactivity | | renderTitle | (props: AlertProps) => React.ReactNode | - | Custom title renderer. Passes props as function parameter. | | onClose | () => void | - | Callback function when close button is clicked | | className | string | - | Additional CSS classes |"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"href": "/components/anchor-navigation",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangesk/orange-design-system",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.49",
|
|
4
4
|
"private": false,
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.x"
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"classnames": "^2.5.1",
|
|
64
64
|
"daypickr": "^0.3.4",
|
|
65
65
|
"diff2html": "^3.4.56",
|
|
66
|
-
"dompurify": "^3.4.
|
|
66
|
+
"dompurify": "^3.4.6",
|
|
67
67
|
"html-react-parser": "6.1.2",
|
|
68
68
|
"lorem-ipsum": "3.0.0",
|
|
69
69
|
"minisearch": "7.2.0",
|
|
@@ -86,10 +86,10 @@
|
|
|
86
86
|
"wnumb": "^1.2.0"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
-
"@babel/core": "^7.29.
|
|
90
|
-
"@babel/preset-env": "^7.29.
|
|
91
|
-
"@babel/preset-react": "^7.
|
|
92
|
-
"@babel/preset-typescript": "^7.
|
|
89
|
+
"@babel/core": "^7.29.7",
|
|
90
|
+
"@babel/preset-env": "^7.29.7",
|
|
91
|
+
"@babel/preset-react": "^7.29.7",
|
|
92
|
+
"@babel/preset-typescript": "^7.29.7",
|
|
93
93
|
"@biomejs/biome": "latest",
|
|
94
94
|
"@rollup/plugin-alias": "6.0.0",
|
|
95
95
|
"@rollup/plugin-babel": "7.0.0",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"canvas": "^3.2.3",
|
|
117
117
|
"fs-extra": "^11.3.5",
|
|
118
118
|
"glob": "13.0.6",
|
|
119
|
-
"html-validate": "11.
|
|
119
|
+
"html-validate": "11.4.0",
|
|
120
120
|
"husky": "^9.1.7",
|
|
121
121
|
"identity-obj-proxy": "^3.0.0",
|
|
122
122
|
"jsdom": "29.1.1",
|
|
@@ -6,7 +6,7 @@ $card-base: (
|
|
|
6
6
|
background-color: var(--color-surface-primary),
|
|
7
7
|
margin-bottom: space.get("large"),
|
|
8
8
|
border-radius: convert.to-rem(10px),
|
|
9
|
-
border: 1px solid var(--color-border-
|
|
9
|
+
border: 1px solid var(--color-border-subtle),
|
|
10
10
|
overflow: hidden,
|
|
11
11
|
);
|
|
12
12
|
|
|
@@ -128,6 +128,13 @@ export default class CarouselHero {
|
|
|
128
128
|
? parseInt(this.element.getAttribute("data-interval")!) || 0
|
|
129
129
|
: 0;
|
|
130
130
|
|
|
131
|
+
if (typeof this.config.a11y === "object" && this.config.a11y !== null) {
|
|
132
|
+
this.config.a11y = {
|
|
133
|
+
...this.config.a11y,
|
|
134
|
+
slideRole: this.tabs.length > 0 ? "tabpanel" : "group",
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
131
138
|
if (this.element.hasAttribute("data-swiper-options")) {
|
|
132
139
|
try {
|
|
133
140
|
const customOptions = JSON.parse(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import cx from "classnames";
|
|
4
|
-
import React, { ReactNode } from "react";
|
|
4
|
+
import React, { ReactNode, useId } from "react";
|
|
5
5
|
import { useStatic } from "@/utils/hooks";
|
|
6
6
|
import { Container } from "../Container";
|
|
7
7
|
import { Controls } from "../Controls";
|
|
@@ -21,11 +21,35 @@ import {
|
|
|
21
21
|
CLASS_VIEWPORT_WRAPPER,
|
|
22
22
|
} from "./constants";
|
|
23
23
|
|
|
24
|
+
function getCarouselHeroSlides(children: ReactNode): React.ReactElement[] {
|
|
25
|
+
const slides: React.ReactElement[] = [];
|
|
26
|
+
|
|
27
|
+
React.Children.forEach(children, (child) => {
|
|
28
|
+
if (!React.isValidElement(child)) return;
|
|
29
|
+
|
|
30
|
+
if (child.type === React.Fragment) {
|
|
31
|
+
slides.push(
|
|
32
|
+
...getCarouselHeroSlides(
|
|
33
|
+
(child.props as { children?: ReactNode }).children,
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
slides.push(child);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return slides;
|
|
43
|
+
}
|
|
44
|
+
|
|
24
45
|
interface TabItem {
|
|
25
46
|
label: string;
|
|
47
|
+
id?: string;
|
|
26
48
|
[key: string]: any;
|
|
27
49
|
}
|
|
28
50
|
|
|
51
|
+
type CarouselHeroSlideElement = React.ReactElement<Record<string, any>>;
|
|
52
|
+
|
|
29
53
|
interface CarouselHeroProps {
|
|
30
54
|
className?: string;
|
|
31
55
|
swiperOptions?: Record<string, any>;
|
|
@@ -48,6 +72,24 @@ const CarouselHero: React.FC<CarouselHeroProps> = ({
|
|
|
48
72
|
const [carouselRef] = useStatic(CarouselHeroStatic);
|
|
49
73
|
|
|
50
74
|
const classes = cx(CLASS_ROOT, className);
|
|
75
|
+
const generatedId = `carousel-hero-${useId().replace(/:/g, "-")}`;
|
|
76
|
+
const carouselId =
|
|
77
|
+
typeof other.id === "string" && other.id ? other.id : generatedId;
|
|
78
|
+
const slideItems = getCarouselHeroSlides(
|
|
79
|
+
children,
|
|
80
|
+
) as CarouselHeroSlideElement[];
|
|
81
|
+
const hasTabs = tabs.length > 0 && slideItems.length > 0;
|
|
82
|
+
const getTabId = (tab: TabItem | undefined, index: number) =>
|
|
83
|
+
typeof tab?.id === "string" && tab.id
|
|
84
|
+
? tab.id
|
|
85
|
+
: `${carouselId}-tab-${index}`;
|
|
86
|
+
const getPanelId = (
|
|
87
|
+
slide: CarouselHeroSlideElement | undefined,
|
|
88
|
+
index: number,
|
|
89
|
+
) =>
|
|
90
|
+
slide && typeof slide.props.id === "string" && slide.props.id
|
|
91
|
+
? slide.props.id
|
|
92
|
+
: `${carouselId}-panel-${index}`;
|
|
51
93
|
|
|
52
94
|
const elementClasses = {
|
|
53
95
|
prev: CLASS_PREV,
|
|
@@ -75,6 +117,20 @@ const CarouselHero: React.FC<CarouselHeroProps> = ({
|
|
|
75
117
|
!!(interval && interval >= 1000));
|
|
76
118
|
|
|
77
119
|
const playPauseIcon = "pause";
|
|
120
|
+
const tabSlides = slideItems.map((slide, index) => {
|
|
121
|
+
if (!hasTabs) {
|
|
122
|
+
return slide;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const tabId = getTabId(tabs[index], index);
|
|
126
|
+
const panelId = getPanelId(slide, index);
|
|
127
|
+
|
|
128
|
+
return React.cloneElement(slide as CarouselHeroSlideElement, {
|
|
129
|
+
id: panelId,
|
|
130
|
+
role: "tabpanel",
|
|
131
|
+
"aria-labelledby": tabId,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
78
134
|
|
|
79
135
|
return (
|
|
80
136
|
<div
|
|
@@ -91,21 +147,23 @@ const CarouselHero: React.FC<CarouselHeroProps> = ({
|
|
|
91
147
|
>
|
|
92
148
|
<div className={CLASS_VIEWPORT_WRAPPER}>
|
|
93
149
|
<div className={CLASS_VIEWPORT}>
|
|
94
|
-
<div className={CLASS_TRACK}>{
|
|
150
|
+
<div className={CLASS_TRACK}>{tabSlides}</div>
|
|
95
151
|
</div>
|
|
96
152
|
</div>
|
|
97
153
|
|
|
98
154
|
{/* Controls container */}
|
|
99
155
|
<Container className="d-flex">
|
|
100
156
|
{/* Tab navigation */}
|
|
101
|
-
{
|
|
157
|
+
{hasTabs && (
|
|
102
158
|
<div className={elementClasses.tabs} role="tablist">
|
|
103
159
|
{tabs.map((tab, index) => (
|
|
104
160
|
<button
|
|
161
|
+
id={getTabId(tab, index)}
|
|
105
162
|
key={index}
|
|
106
163
|
type="button"
|
|
107
164
|
className={elementClasses.tab}
|
|
108
165
|
role="tab"
|
|
166
|
+
aria-controls={getPanelId(slideItems[index], index)}
|
|
109
167
|
aria-selected={index === 0 ? "true" : "false"}
|
|
110
168
|
tabIndex={index === 0 ? 0 : -1}
|
|
111
169
|
>
|
|
@@ -156,13 +156,20 @@ describe("rendering CarouselHero", () => {
|
|
|
156
156
|
|
|
157
157
|
it("sets proper ARIA attributes on tabs", () => {
|
|
158
158
|
const { container } = render(
|
|
159
|
-
<CarouselHero tabs={tabs}>
|
|
159
|
+
<CarouselHero id="test-carousel" tabs={tabs}>
|
|
160
|
+
{children}
|
|
161
|
+
</CarouselHero>,
|
|
160
162
|
);
|
|
161
163
|
|
|
162
164
|
const tabButtons = container.querySelectorAll(".carousel-hero__tab");
|
|
163
165
|
|
|
164
166
|
// First tab should be active
|
|
165
167
|
expect(tabButtons[0]).toHaveAttribute("role", "tab");
|
|
168
|
+
expect(tabButtons[0]).toHaveAttribute("id", "test-carousel-tab-0");
|
|
169
|
+
expect(tabButtons[0]).toHaveAttribute(
|
|
170
|
+
"aria-controls",
|
|
171
|
+
"test-carousel-panel-0",
|
|
172
|
+
);
|
|
166
173
|
expect(tabButtons[0]).toHaveAttribute("aria-selected", "true");
|
|
167
174
|
expect(tabButtons[0]).toHaveAttribute("tabIndex", "0");
|
|
168
175
|
|
|
@@ -173,6 +180,29 @@ describe("rendering CarouselHero", () => {
|
|
|
173
180
|
expect(tabButtons[2]).toHaveAttribute("tabIndex", "-1");
|
|
174
181
|
});
|
|
175
182
|
|
|
183
|
+
it("links tabs to corresponding tabpanels", () => {
|
|
184
|
+
const { container } = render(
|
|
185
|
+
<CarouselHero id="test-carousel" tabs={tabs}>
|
|
186
|
+
{children}
|
|
187
|
+
</CarouselHero>,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const slides = container.getElementsByClassName("carousel-hero__slide");
|
|
191
|
+
|
|
192
|
+
expect(slides[0]).toHaveAttribute("role", "tabpanel");
|
|
193
|
+
expect(slides[0]).toHaveAttribute("id", "test-carousel-panel-0");
|
|
194
|
+
expect(slides[0]).toHaveAttribute(
|
|
195
|
+
"aria-labelledby",
|
|
196
|
+
"test-carousel-tab-0",
|
|
197
|
+
);
|
|
198
|
+
expect(slides[1]).toHaveAttribute("role", "tabpanel");
|
|
199
|
+
expect(slides[1]).toHaveAttribute("id", "test-carousel-panel-1");
|
|
200
|
+
expect(slides[1]).toHaveAttribute(
|
|
201
|
+
"aria-labelledby",
|
|
202
|
+
"test-carousel-tab-1",
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
176
206
|
it("shows play/pause button when autoplay is enabled and interval is provided", () => {
|
|
177
207
|
const { container } = render(
|
|
178
208
|
<CarouselHero enableAutoplay interval={3000}>
|
|
@@ -27,9 +27,21 @@
|
|
|
27
27
|
margin-bottom: 0;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
> .icon,
|
|
31
|
+
> svg {
|
|
32
|
+
width: convert.to-rem(24px);
|
|
33
|
+
height: convert.to-rem(24px);
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
@include breakpoint.get("md") {
|
|
31
37
|
flex-direction: column;
|
|
32
38
|
padding: space.get("large");
|
|
39
|
+
|
|
40
|
+
> .icon,
|
|
41
|
+
> svg {
|
|
42
|
+
width: convert.to-rem(32px);
|
|
43
|
+
height: convert.to-rem(32px);
|
|
44
|
+
}
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
|