@orangesk/orange-design-system 2.0.0-beta.11 → 2.0.0-beta.13
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/index.js +4 -4
- package/build/components/index.js.map +1 -1
- package/build/components/tsconfig.tsbuildinfo +1 -1
- package/build/components/types/index.d.ts +14 -8
- package/build/components/types/src/components/Carousel/Carousel.static.d.ts +34 -1
- package/build/components/types/src/components/Cover/Cover.d.ts +3 -3
- package/build/components/types/src/components/Modal/Modal.d.ts +0 -2
- package/build/components/types/src/components/Modal/index.d.ts +0 -1
- package/build/components/types/src/components/Pill/Pill.d.ts +1 -1
- package/build/components/types/src/components/Table/Table.d.ts +2 -0
- package/build/components/types/src/components/Table/docsData.d.ts +2 -0
- package/build/components/types/src/components/Table/types.d.ts +1 -0
- package/build/components/types/src/components/Tag/Tag.d.ts +0 -2
- package/build/components/types/src/components/index.d.ts +2 -2
- package/build/fonts/HelveticaNeue-Bold.woff2 +0 -0
- package/build/fonts/HelveticaNeue-Light.woff2 +0 -0
- package/build/fonts/HelveticaNeue-Roman.woff2 +0 -0
- package/build/lib/components.css +1 -1
- package/build/lib/components.css.map +1 -1
- package/build/lib/scripts.js +4 -4
- package/build/lib/scripts.js.map +1 -1
- package/build/lib/style.css +1 -1
- package/build/lib/style.css.map +1 -1
- package/package.json +9 -9
- package/public/fonts/HelveticaNeue-Bold.woff2 +0 -0
- package/public/fonts/HelveticaNeue-Light.woff2 +0 -0
- package/public/fonts/HelveticaNeue-Roman.woff2 +0 -0
- package/src/components/AnchorNavigation/AnchorNavigation.static.ts +3 -0
- package/src/components/AnchorNavigation/styles/mixins.scss +4 -4
- package/src/components/Button/styles/config.scss +5 -4
- package/src/components/Carousel/Carousel.static.ts +204 -1
- package/src/components/Carousel/tests/Carousel.static.test.js +403 -0
- package/src/components/Carousel/tests/Carousel.unit.test.js +68 -0
- package/src/components/Cover/Cover.tsx +22 -20
- package/src/components/Cover/styles/config.scss +23 -12
- package/src/components/Cover/styles/mixins.scss +6 -5
- package/src/components/Cover/tests/Cover.unit.test.js +52 -52
- package/src/components/Forms/Group/styles/mixins.scss +14 -0
- package/src/components/Modal/Modal.tsx +1 -9
- package/src/components/Modal/index.ts +0 -1
- package/src/components/Modal/styles/config.scss +4 -4
- package/src/components/Modal/styles/mixins.scss +13 -59
- package/src/components/Modal/styles/style.scss +0 -16
- package/src/components/Modal/tests/Modal.unit.test.js +0 -37
- package/src/components/Pill/Pill.tsx +13 -2
- package/src/components/Pill/styles/config.scss +2 -21
- package/src/components/Pill/styles/style.scss +18 -6
- package/src/components/Pill/tests/Pill.conformance.test.js +8 -2
- package/src/components/Pill/tests/Pill.unit.test.js +69 -11
- package/src/components/Table/Row.tsx +9 -3
- package/src/components/Table/Table.tsx +11 -1
- package/src/components/Table/TableContext.ts +1 -0
- package/src/components/Table/docsData.ts +25 -0
- package/src/components/Table/styles/mixins.scss +37 -0
- package/src/components/Table/styles/style.scss +6 -0
- package/src/components/Table/types.ts +1 -0
- package/src/components/Tag/Tag.tsx +0 -2
- package/src/components/Tag/styles/style.scss +32 -0
- package/src/components/Tooltip/InfoTooltip.tsx +1 -5
- package/src/components/index.ts +2 -0
- package/src/styles/tokens/color.scss +1 -1
- package/build/components/types/src/components/Modal/ModalProductBody.d.ts +0 -10
- package/src/components/Modal/ModalProductBody.tsx +0 -52
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Swiper } from "swiper";
|
|
6
|
+
import Carousel from "../Carousel.static";
|
|
7
|
+
|
|
8
|
+
// Mock Swiper
|
|
9
|
+
jest.mock("swiper", () => ({
|
|
10
|
+
Swiper: jest.fn().mockImplementation(() => ({
|
|
11
|
+
slideNext: jest.fn(),
|
|
12
|
+
slidePrev: jest.fn(),
|
|
13
|
+
destroy: jest.fn(),
|
|
14
|
+
update: jest.fn(),
|
|
15
|
+
on: jest.fn(),
|
|
16
|
+
activeIndex: 0,
|
|
17
|
+
isBeginning: true,
|
|
18
|
+
isEnd: false,
|
|
19
|
+
})),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
// Mock Swiper modules
|
|
23
|
+
jest.mock("swiper/modules", () => ({
|
|
24
|
+
Navigation: {},
|
|
25
|
+
Pagination: {},
|
|
26
|
+
Scrollbar: {},
|
|
27
|
+
A11y: {},
|
|
28
|
+
Keyboard: {},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
describe("Carousel Static - External Controls", () => {
|
|
32
|
+
let carouselElement;
|
|
33
|
+
let carouselInstance;
|
|
34
|
+
let mockSwiperInstance;
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
// Clear all mocks
|
|
38
|
+
jest.clearAllMocks();
|
|
39
|
+
|
|
40
|
+
// Create mock Swiper instance
|
|
41
|
+
mockSwiperInstance = {
|
|
42
|
+
slideNext: jest.fn(),
|
|
43
|
+
slidePrev: jest.fn(),
|
|
44
|
+
destroy: jest.fn(),
|
|
45
|
+
update: jest.fn(),
|
|
46
|
+
on: jest.fn(),
|
|
47
|
+
activeIndex: 0,
|
|
48
|
+
isBeginning: true,
|
|
49
|
+
isEnd: false,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
Swiper.mockImplementation(() => mockSwiperInstance);
|
|
53
|
+
|
|
54
|
+
// Set up DOM
|
|
55
|
+
document.body.innerHTML = `
|
|
56
|
+
<div class="carousel" data-carousel-id="test-carousel" id="test-carousel">
|
|
57
|
+
<div class="carousel__viewport">
|
|
58
|
+
<div class="carousel__track">
|
|
59
|
+
<div class="carousel__slide">Slide 1</div>
|
|
60
|
+
<div class="carousel__slide">Slide 2</div>
|
|
61
|
+
<div class="carousel__slide">Slide 3</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="carousel__pagination"></div>
|
|
65
|
+
</div>
|
|
66
|
+
<button data-carousel-controls="test-carousel" data-carousel-action="prev" id="prev-btn">Previous</button>
|
|
67
|
+
<button data-carousel-controls="test-carousel" data-carousel-action="next" id="next-btn">Next</button>
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
carouselElement = document.querySelector(".carousel");
|
|
71
|
+
|
|
72
|
+
// Mock requestAnimationFrame to execute synchronously
|
|
73
|
+
global.requestAnimationFrame = jest.fn((cb) => cb());
|
|
74
|
+
|
|
75
|
+
carouselInstance = new Carousel(carouselElement);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterEach(() => {
|
|
79
|
+
document.body.innerHTML = "";
|
|
80
|
+
// Restore requestAnimationFrame
|
|
81
|
+
global.requestAnimationFrame = undefined;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("External Controls Initialization", () => {
|
|
85
|
+
it("should find and initialize external controls", () => {
|
|
86
|
+
const prevButton = document.getElementById("prev-btn");
|
|
87
|
+
const nextButton = document.getElementById("next-btn");
|
|
88
|
+
|
|
89
|
+
expect(prevButton.hasAttribute("data-carousel-initialized")).toBe(true);
|
|
90
|
+
expect(nextButton.hasAttribute("data-carousel-initialized")).toBe(true);
|
|
91
|
+
expect(prevButton.getAttribute("aria-label")).toBe(
|
|
92
|
+
"Predchádzajúci snímok",
|
|
93
|
+
);
|
|
94
|
+
expect(nextButton.getAttribute("aria-label")).toBe("Nasledujúci snímok");
|
|
95
|
+
expect(prevButton.getAttribute("type")).toBe("button");
|
|
96
|
+
expect(nextButton.getAttribute("type")).toBe("button");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should not initialize controls without carousel ID", () => {
|
|
100
|
+
// Create carousel without ID
|
|
101
|
+
document.body.innerHTML = `
|
|
102
|
+
<div class="carousel">
|
|
103
|
+
<div class="carousel__viewport">
|
|
104
|
+
<div class="carousel__track">
|
|
105
|
+
<div class="carousel__slide">Slide 1</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<button data-carousel-controls="missing-carousel" data-carousel-action="next">Next</button>
|
|
110
|
+
`;
|
|
111
|
+
|
|
112
|
+
const element = document.querySelector(".carousel");
|
|
113
|
+
new Carousel(element);
|
|
114
|
+
const button = document.querySelector("button");
|
|
115
|
+
|
|
116
|
+
expect(button.hasAttribute("data-carousel-initialized")).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should skip already initialized controls", () => {
|
|
120
|
+
// Mark button as already initialized
|
|
121
|
+
const prevButton = document.getElementById("prev-btn");
|
|
122
|
+
prevButton.setAttribute("data-carousel-initialized", "true");
|
|
123
|
+
|
|
124
|
+
// Create new carousel instance
|
|
125
|
+
new Carousel(carouselElement);
|
|
126
|
+
|
|
127
|
+
// Should not double-initialize
|
|
128
|
+
expect(prevButton.getAttribute("data-carousel-initialized")).toBe("true");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("External Controls Navigation", () => {
|
|
133
|
+
it("should call slideNext when next button is clicked", () => {
|
|
134
|
+
const nextButton = document.getElementById("next-btn");
|
|
135
|
+
nextButton.click();
|
|
136
|
+
|
|
137
|
+
expect(mockSwiperInstance.slideNext).toHaveBeenCalledTimes(1);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should call slidePrev when prev button is clicked", () => {
|
|
141
|
+
// Ensure button is not disabled by setting carousel state
|
|
142
|
+
mockSwiperInstance.isBeginning = false;
|
|
143
|
+
mockSwiperInstance.isEnd = false;
|
|
144
|
+
carouselInstance.updateExternalControlsState();
|
|
145
|
+
|
|
146
|
+
const prevButton = document.getElementById("prev-btn");
|
|
147
|
+
prevButton.click();
|
|
148
|
+
|
|
149
|
+
expect(mockSwiperInstance.slidePrev).toHaveBeenCalledTimes(1);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("should prevent default behavior on button click", () => {
|
|
153
|
+
const nextButton = document.getElementById("next-btn");
|
|
154
|
+
const mockEvent = new Event("click");
|
|
155
|
+
mockEvent.preventDefault = jest.fn();
|
|
156
|
+
|
|
157
|
+
nextButton.dispatchEvent(mockEvent);
|
|
158
|
+
|
|
159
|
+
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should not navigate when button is disabled", () => {
|
|
163
|
+
const nextButton = document.getElementById("next-btn");
|
|
164
|
+
nextButton.setAttribute("disabled", "true");
|
|
165
|
+
|
|
166
|
+
nextButton.click();
|
|
167
|
+
|
|
168
|
+
expect(mockSwiperInstance.slideNext).not.toHaveBeenCalled();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe("External Controls State Management", () => {
|
|
173
|
+
it("should disable prev button when at beginning", () => {
|
|
174
|
+
mockSwiperInstance.isBeginning = true;
|
|
175
|
+
mockSwiperInstance.isEnd = false;
|
|
176
|
+
|
|
177
|
+
carouselInstance.updateExternalControlsState();
|
|
178
|
+
|
|
179
|
+
const prevButton = document.getElementById("prev-btn");
|
|
180
|
+
const nextButton = document.getElementById("next-btn");
|
|
181
|
+
|
|
182
|
+
expect(prevButton.hasAttribute("disabled")).toBe(true);
|
|
183
|
+
expect(prevButton.getAttribute("aria-disabled")).toBe("true");
|
|
184
|
+
|
|
185
|
+
expect(nextButton.hasAttribute("disabled")).toBe(false);
|
|
186
|
+
expect(nextButton.getAttribute("aria-disabled")).toBe("false");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should disable next button when at end", () => {
|
|
190
|
+
mockSwiperInstance.isBeginning = false;
|
|
191
|
+
mockSwiperInstance.isEnd = true;
|
|
192
|
+
|
|
193
|
+
carouselInstance.updateExternalControlsState();
|
|
194
|
+
|
|
195
|
+
const prevButton = document.getElementById("prev-btn");
|
|
196
|
+
const nextButton = document.getElementById("next-btn");
|
|
197
|
+
|
|
198
|
+
expect(nextButton.hasAttribute("disabled")).toBe(true);
|
|
199
|
+
expect(nextButton.getAttribute("aria-disabled")).toBe("true");
|
|
200
|
+
|
|
201
|
+
expect(prevButton.hasAttribute("disabled")).toBe(false);
|
|
202
|
+
expect(prevButton.getAttribute("aria-disabled")).toBe("false");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("should enable both buttons when in middle", () => {
|
|
206
|
+
mockSwiperInstance.isBeginning = false;
|
|
207
|
+
mockSwiperInstance.isEnd = false;
|
|
208
|
+
|
|
209
|
+
carouselInstance.updateExternalControlsState();
|
|
210
|
+
|
|
211
|
+
const prevButton = document.getElementById("prev-btn");
|
|
212
|
+
const nextButton = document.getElementById("next-btn");
|
|
213
|
+
|
|
214
|
+
expect(prevButton.hasAttribute("disabled")).toBe(false);
|
|
215
|
+
expect(nextButton.hasAttribute("disabled")).toBe(false);
|
|
216
|
+
expect(prevButton.getAttribute("aria-disabled")).toBe("false");
|
|
217
|
+
expect(nextButton.getAttribute("aria-disabled")).toBe("false");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("should update controls state on slide change transition end", () => {
|
|
221
|
+
const updateStatesSpy = jest.spyOn(
|
|
222
|
+
carouselInstance,
|
|
223
|
+
"updateExternalControlsState",
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Get the slideChangeTransitionEnd callback from Swiper initialization
|
|
227
|
+
const swiperOnCallArgs = mockSwiperInstance.on.mock.calls.find(
|
|
228
|
+
(call) => call[0] === "slideChangeTransitionEnd",
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
if (swiperOnCallArgs) {
|
|
232
|
+
const callback = swiperOnCallArgs[1];
|
|
233
|
+
callback();
|
|
234
|
+
expect(updateStatesSpy).toHaveBeenCalled();
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe("Multiple Control Sets", () => {
|
|
240
|
+
beforeEach(() => {
|
|
241
|
+
document.body.innerHTML = `
|
|
242
|
+
<div class="carousel" data-carousel-id="test-carousel">
|
|
243
|
+
<div class="carousel__viewport">
|
|
244
|
+
<div class="carousel__track">
|
|
245
|
+
<div class="carousel__slide">Slide 1</div>
|
|
246
|
+
<div class="carousel__slide">Slide 2</div>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
<button data-carousel-controls="test-carousel" data-carousel-action="prev" id="prev-btn-1">Prev 1</button>
|
|
251
|
+
<button data-carousel-controls="test-carousel" data-carousel-action="next" id="next-btn-1">Next 1</button>
|
|
252
|
+
<button data-carousel-controls="test-carousel" data-carousel-action="prev" id="prev-btn-2">Prev 2</button>
|
|
253
|
+
<button data-carousel-controls="test-carousel" data-carousel-action="next" id="next-btn-2">Next 2</button>
|
|
254
|
+
`;
|
|
255
|
+
|
|
256
|
+
carouselElement = document.querySelector(".carousel");
|
|
257
|
+
|
|
258
|
+
// Mock requestAnimationFrame to execute synchronously
|
|
259
|
+
global.requestAnimationFrame = jest.fn((cb) => cb());
|
|
260
|
+
|
|
261
|
+
carouselInstance = new Carousel(carouselElement);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("should initialize multiple control sets", () => {
|
|
265
|
+
const buttons = document.querySelectorAll(
|
|
266
|
+
'[data-carousel-controls="test-carousel"]',
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
buttons.forEach((button) => {
|
|
270
|
+
expect(button.hasAttribute("data-carousel-initialized")).toBe(true);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("should update state for all control sets", () => {
|
|
275
|
+
mockSwiperInstance.isBeginning = true;
|
|
276
|
+
mockSwiperInstance.isEnd = false;
|
|
277
|
+
|
|
278
|
+
carouselInstance.updateExternalControlsState();
|
|
279
|
+
|
|
280
|
+
const prevButtons = document.querySelectorAll(
|
|
281
|
+
'[data-carousel-action="prev"]',
|
|
282
|
+
);
|
|
283
|
+
const nextButtons = document.querySelectorAll(
|
|
284
|
+
'[data-carousel-action="next"]',
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
prevButtons.forEach((button) => {
|
|
288
|
+
expect(button.hasAttribute("disabled")).toBe(true);
|
|
289
|
+
expect(button.getAttribute("aria-disabled")).toBe("true");
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
nextButtons.forEach((button) => {
|
|
293
|
+
expect(button.hasAttribute("disabled")).toBe(false);
|
|
294
|
+
expect(button.getAttribute("aria-disabled")).toBe("false");
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe("Cleanup", () => {
|
|
300
|
+
it("should clean up external controls on destroy", () => {
|
|
301
|
+
const prevButton = document.getElementById("prev-btn");
|
|
302
|
+
const nextButton = document.getElementById("next-btn");
|
|
303
|
+
|
|
304
|
+
// Verify controls are initialized
|
|
305
|
+
expect(prevButton.hasAttribute("data-carousel-initialized")).toBe(true);
|
|
306
|
+
expect(nextButton.hasAttribute("data-carousel-initialized")).toBe(true);
|
|
307
|
+
|
|
308
|
+
// Destroy carousel
|
|
309
|
+
carouselInstance.destroy();
|
|
310
|
+
|
|
311
|
+
// Verify cleanup
|
|
312
|
+
expect(prevButton.hasAttribute("data-carousel-initialized")).toBe(false);
|
|
313
|
+
expect(nextButton.hasAttribute("data-carousel-initialized")).toBe(false);
|
|
314
|
+
expect(mockSwiperInstance.destroy).toHaveBeenCalled();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it("should remove event listeners on destroy", () => {
|
|
318
|
+
const nextButton = document.getElementById("next-btn");
|
|
319
|
+
|
|
320
|
+
// Store reference to click handler
|
|
321
|
+
const clickHandler = nextButton._carouselClickHandler;
|
|
322
|
+
expect(clickHandler).toBeDefined();
|
|
323
|
+
|
|
324
|
+
carouselInstance.destroy();
|
|
325
|
+
|
|
326
|
+
// Handler reference should be removed
|
|
327
|
+
expect(nextButton._carouselClickHandler).toBeUndefined();
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
describe("Static Methods", () => {
|
|
332
|
+
it("should get instance by ID", () => {
|
|
333
|
+
const foundInstance = Carousel.getInstanceById("test-carousel");
|
|
334
|
+
expect(foundInstance).toBe(carouselInstance);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("should get instance by data-carousel-id", () => {
|
|
338
|
+
document.body.innerHTML = `
|
|
339
|
+
<div class="carousel" data-carousel-id="data-id-carousel">
|
|
340
|
+
<div class="carousel__viewport">
|
|
341
|
+
<div class="carousel__track">
|
|
342
|
+
<div class="carousel__slide">Slide 1</div>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
`;
|
|
347
|
+
|
|
348
|
+
const element = document.querySelector(".carousel");
|
|
349
|
+
const instance = new Carousel(element);
|
|
350
|
+
const foundInstance = Carousel.getInstanceById("data-id-carousel");
|
|
351
|
+
|
|
352
|
+
expect(foundInstance).toBe(instance);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("should return null for non-existent carousel", () => {
|
|
356
|
+
const foundInstance = Carousel.getInstanceById("non-existent");
|
|
357
|
+
expect(foundInstance).toBe(null);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe("Edge Cases", () => {
|
|
362
|
+
it("should handle controls without valid action", () => {
|
|
363
|
+
document.body.innerHTML = `
|
|
364
|
+
<div class="carousel" data-carousel-id="test-carousel">
|
|
365
|
+
<div class="carousel__viewport">
|
|
366
|
+
<div class="carousel__track">
|
|
367
|
+
<div class="carousel__slide">Slide 1</div>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
<button data-carousel-controls="test-carousel" data-carousel-action="invalid">Invalid</button>
|
|
372
|
+
`;
|
|
373
|
+
|
|
374
|
+
const element = document.querySelector(".carousel");
|
|
375
|
+
new Carousel(element);
|
|
376
|
+
|
|
377
|
+
const button = document.querySelector("button");
|
|
378
|
+
button.click();
|
|
379
|
+
|
|
380
|
+
// Should not crash and should not call slideNext/slidePrev
|
|
381
|
+
expect(mockSwiperInstance.slideNext).not.toHaveBeenCalled();
|
|
382
|
+
expect(mockSwiperInstance.slidePrev).not.toHaveBeenCalled();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("should handle updateExternalControlsState without instance", () => {
|
|
386
|
+
carouselInstance.instance = null;
|
|
387
|
+
|
|
388
|
+
// Should not crash
|
|
389
|
+
expect(() => {
|
|
390
|
+
carouselInstance.updateExternalControlsState();
|
|
391
|
+
}).not.toThrow();
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("should handle updateExternalControlsState without carouselId", () => {
|
|
395
|
+
carouselInstance.carouselId = null;
|
|
396
|
+
|
|
397
|
+
// Should not crash
|
|
398
|
+
expect(() => {
|
|
399
|
+
carouselInstance.updateExternalControlsState();
|
|
400
|
+
}).not.toThrow();
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
});
|
|
@@ -204,4 +204,72 @@ describe("rendering Carousel", () => {
|
|
|
204
204
|
expect(carousel).not.toHaveClass("is-light");
|
|
205
205
|
});
|
|
206
206
|
});
|
|
207
|
+
|
|
208
|
+
describe("external controls integration", () => {
|
|
209
|
+
const items = [
|
|
210
|
+
<p key="item1">item1</p>,
|
|
211
|
+
<p key="item2">item2</p>,
|
|
212
|
+
<p key="item3">item3</p>,
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
it("should render with data-carousel-id when id is provided", () => {
|
|
216
|
+
const { container } = render(
|
|
217
|
+
<Carousel id="test-carousel" items={items} />,
|
|
218
|
+
);
|
|
219
|
+
const carousel = container.querySelector(".carousel");
|
|
220
|
+
expect(carousel).toHaveAttribute("id", "test-carousel");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should support external controls via data attributes", () => {
|
|
224
|
+
const { container } = render(
|
|
225
|
+
<div>
|
|
226
|
+
<Carousel id="external-carousel" items={items} />
|
|
227
|
+
<button
|
|
228
|
+
data-carousel-controls="external-carousel"
|
|
229
|
+
data-carousel-action="prev"
|
|
230
|
+
data-testid="external-prev"
|
|
231
|
+
>
|
|
232
|
+
Previous
|
|
233
|
+
</button>
|
|
234
|
+
<button
|
|
235
|
+
data-carousel-controls="external-carousel"
|
|
236
|
+
data-carousel-action="next"
|
|
237
|
+
data-testid="external-next"
|
|
238
|
+
>
|
|
239
|
+
Next
|
|
240
|
+
</button>
|
|
241
|
+
</div>,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const carousel = container.querySelector(".carousel");
|
|
245
|
+
const prevButton = container.querySelector(
|
|
246
|
+
'[data-testid="external-prev"]',
|
|
247
|
+
);
|
|
248
|
+
const nextButton = container.querySelector(
|
|
249
|
+
'[data-testid="external-next"]',
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
expect(carousel).toHaveAttribute("id", "external-carousel");
|
|
253
|
+
expect(prevButton).toHaveAttribute(
|
|
254
|
+
"data-carousel-controls",
|
|
255
|
+
"external-carousel",
|
|
256
|
+
);
|
|
257
|
+
expect(prevButton).toHaveAttribute("data-carousel-action", "prev");
|
|
258
|
+
expect(nextButton).toHaveAttribute(
|
|
259
|
+
"data-carousel-controls",
|
|
260
|
+
"external-carousel",
|
|
261
|
+
);
|
|
262
|
+
expect(nextButton).toHaveAttribute("data-carousel-action", "next");
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("should work with both id and data-carousel-id", () => {
|
|
266
|
+
const { container } = render(
|
|
267
|
+
<Carousel id="test-id" data-carousel-id="test-data-id" items={items} />,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const carousel = container.querySelector(".carousel");
|
|
271
|
+
expect(carousel).toHaveAttribute("id", "test-id");
|
|
272
|
+
expect(carousel).toHaveAttribute("data-carousel-id", "test-data-id");
|
|
273
|
+
});
|
|
274
|
+
});
|
|
207
275
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import cx from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import cx from "classnames";
|
|
3
3
|
|
|
4
|
-
import { Image } from
|
|
4
|
+
import { Image } from "../Image";
|
|
5
5
|
|
|
6
6
|
interface CoverProps {
|
|
7
7
|
/** Background image source. URL string or an object of breakpoint: url pairs
|
|
@@ -19,22 +19,24 @@ interface CoverProps {
|
|
|
19
19
|
}
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
bgSrc:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
bgSrc:
|
|
23
|
+
| string
|
|
24
|
+
| {
|
|
25
|
+
default: string;
|
|
26
|
+
xs?: string;
|
|
27
|
+
sm?: string;
|
|
28
|
+
md?: string;
|
|
29
|
+
lg?: string;
|
|
30
|
+
xl?: string;
|
|
31
|
+
xxl?: string;
|
|
32
|
+
xxxl?: string;
|
|
33
|
+
};
|
|
32
34
|
/** Set vertical position of image */
|
|
33
|
-
bgPosition?:
|
|
35
|
+
bgPosition?: "top" | "center" | "bottom";
|
|
34
36
|
/** Applies custom classNames to overlay content */
|
|
35
37
|
contentClass?: string;
|
|
36
38
|
/** Cover size */
|
|
37
|
-
size?:
|
|
39
|
+
size?: "small" | "medium" | "large" | "xlarge";
|
|
38
40
|
/** Additional className for the cover container */
|
|
39
41
|
className?: string;
|
|
40
42
|
/** Children content */
|
|
@@ -43,13 +45,13 @@ interface CoverProps {
|
|
|
43
45
|
[key: string]: any;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
const CLASS_ROOT =
|
|
48
|
+
const CLASS_ROOT = "cover";
|
|
47
49
|
|
|
48
50
|
const Cover: React.FC<CoverProps> = ({
|
|
49
51
|
className,
|
|
50
52
|
children,
|
|
51
53
|
contentClass,
|
|
52
|
-
bgPosition =
|
|
54
|
+
bgPosition = "center",
|
|
53
55
|
bgSrc,
|
|
54
56
|
size,
|
|
55
57
|
...other
|
|
@@ -59,13 +61,13 @@ const Cover: React.FC<CoverProps> = ({
|
|
|
59
61
|
{
|
|
60
62
|
[`${CLASS_ROOT}--${size}`]: size,
|
|
61
63
|
},
|
|
62
|
-
className
|
|
64
|
+
className,
|
|
63
65
|
);
|
|
64
66
|
|
|
65
67
|
const contentClasses = cx(`${CLASS_ROOT}__content`, contentClass);
|
|
66
68
|
|
|
67
69
|
const bgImgClassName = cx(`${CLASS_ROOT}__image`, {
|
|
68
|
-
[`${CLASS_ROOT}__image--${bgPosition}`]: bgPosition !==
|
|
70
|
+
[`${CLASS_ROOT}__image--${bgPosition}`]: bgPosition !== "center",
|
|
69
71
|
});
|
|
70
72
|
|
|
71
73
|
return (
|
|
@@ -76,6 +78,6 @@ const Cover: React.FC<CoverProps> = ({
|
|
|
76
78
|
);
|
|
77
79
|
};
|
|
78
80
|
|
|
79
|
-
Cover.displayName =
|
|
81
|
+
Cover.displayName = "Cover";
|
|
80
82
|
|
|
81
83
|
export { Cover };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
@use
|
|
2
|
-
@use
|
|
1
|
+
@use "../../../styles/tokens/space";
|
|
2
|
+
@use "../../../styles/tools/convert";
|
|
3
3
|
|
|
4
4
|
$sizes: (
|
|
5
5
|
small: (
|
|
@@ -8,7 +8,7 @@ $sizes: (
|
|
|
8
8
|
),
|
|
9
9
|
md: (
|
|
10
10
|
min-height: convert.to-rem(255px),
|
|
11
|
-
)
|
|
11
|
+
),
|
|
12
12
|
),
|
|
13
13
|
medium: (
|
|
14
14
|
default: (
|
|
@@ -19,7 +19,7 @@ $sizes: (
|
|
|
19
19
|
),
|
|
20
20
|
lg: (
|
|
21
21
|
min-height: convert.to-rem(295px),
|
|
22
|
-
)
|
|
22
|
+
),
|
|
23
23
|
),
|
|
24
24
|
large: (
|
|
25
25
|
default: (
|
|
@@ -30,13 +30,24 @@ $sizes: (
|
|
|
30
30
|
),
|
|
31
31
|
lg: (
|
|
32
32
|
min-height: convert.to-rem(460px),
|
|
33
|
-
)
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
xlarge: (
|
|
36
|
+
default: (
|
|
37
|
+
min-height: convert.to-rem(600px),
|
|
38
|
+
),
|
|
39
|
+
md: (
|
|
40
|
+
min-height: convert.to-rem(760px),
|
|
41
|
+
),
|
|
42
|
+
lg: (
|
|
43
|
+
min-height: convert.to-rem(760px),
|
|
44
|
+
),
|
|
34
45
|
),
|
|
35
46
|
);
|
|
36
47
|
|
|
37
48
|
$spacing: (
|
|
38
49
|
default: (
|
|
39
|
-
padding-top: space.get(
|
|
50
|
+
padding-top: space.get("small"),
|
|
40
51
|
padding-bottom: 0.1px,
|
|
41
52
|
),
|
|
42
53
|
md: (
|
|
@@ -44,12 +55,12 @@ $spacing: (
|
|
|
44
55
|
padding-bottom: space.get(),
|
|
45
56
|
),
|
|
46
57
|
lg: (
|
|
47
|
-
padding-top: space.get(
|
|
48
|
-
padding-bottom: space.get(
|
|
49
|
-
)
|
|
58
|
+
padding-top: space.get("large"),
|
|
59
|
+
padding-bottom: space.get("large"),
|
|
60
|
+
),
|
|
50
61
|
);
|
|
51
62
|
|
|
52
63
|
$positions: (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
);
|
|
64
|
+
"top": 50% 0,
|
|
65
|
+
"bottom": 50% 100%,
|
|
66
|
+
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
@use "sass:map";
|
|
2
|
-
@use
|
|
3
|
-
@use
|
|
4
|
-
@use
|
|
2
|
+
@use "../../../styles/tokens/color";
|
|
3
|
+
@use "../../../styles/tools/generate";
|
|
4
|
+
@use "./config";
|
|
5
5
|
|
|
6
6
|
@mixin base {
|
|
7
7
|
position: relative;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
position: relative;
|
|
13
13
|
transform: translate3d(0, 0, 0);
|
|
14
14
|
z-index: 1;
|
|
15
|
+
width: 100%;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
@mixin content-size($size, $config: config.$sizes) {
|
|
@@ -34,10 +35,10 @@
|
|
|
34
35
|
img {
|
|
35
36
|
width: 100%;
|
|
36
37
|
height: 100%;
|
|
37
|
-
object-fit: cover;
|
|
38
|
+
object-fit: cover;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
@mixin image-position($position, $config: config.$positions) {
|
|
42
43
|
object-position: map.get($config, $position);
|
|
43
|
-
}
|
|
44
|
+
}
|