@orangesk/orange-design-system 3.6.2 → 3.8.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/Alert/style.css +1 -1
- package/build/components/Alert/style.css.map +1 -1
- package/build/components/Bar/style.css +1 -1
- package/build/components/Bar/style.css.map +1 -1
- package/build/components/Drawer/style.css +2 -0
- package/build/components/Drawer/style.css.map +1 -0
- package/build/components/Grid/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/Modal/style.css +1 -1
- package/build/components/Modal/style.css.map +1 -1
- package/build/components/StatusIndicator/style.css +2 -0
- package/build/components/StatusIndicator/style.css.map +1 -0
- package/build/components/Table/style.css +1 -1
- package/build/components/Table/style.css.map +1 -1
- package/build/components/index.js +1 -1
- package/build/components/index.js.map +1 -1
- package/build/components/types/index.d.ts +89 -3
- 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.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/lib/utilities.css +1 -1
- package/build/lib/utilities.css.map +1 -1
- package/package.json +29 -29
- package/src/components/Alert/Alert.tsx +2 -2
- package/src/components/Alert/data.ts +8 -5
- package/src/components/Alert/styles/config.scss +4 -4
- package/src/components/Alert/styles/mixins.scss +8 -1
- package/src/components/Alert/tests/Alert.test.jsx +11 -1
- package/src/components/Bar/Bar.tsx +1 -1
- package/src/components/Bar/styles/config.scss +1 -0
- package/src/components/Bar/tests/Bar.unit.test.jsx +1 -1
- package/src/components/Carousel/Carousel.static.ts +103 -12
- package/src/components/Carousel/tests/Carousel.static.test.jsx +134 -0
- package/src/components/DocumentationSidebar/DocumentationSidebar.tsx +12 -4
- package/src/components/Drawer/Drawer.static.ts +281 -0
- package/src/components/Drawer/Drawer.tsx +82 -0
- package/src/components/Drawer/DrawerBody.tsx +21 -0
- package/src/components/Drawer/DrawerFooter.tsx +21 -0
- package/src/components/Drawer/DrawerHeader.tsx +21 -0
- package/src/components/Drawer/index.ts +5 -0
- package/src/components/Drawer/styles/config.scss +17 -0
- package/src/components/Drawer/styles/mixins.scss +119 -0
- package/src/components/Drawer/styles/style.scss +71 -0
- package/src/components/Drawer/tests/Drawer.unit.test.jsx +149 -0
- package/src/components/Footer/data.ts +4 -0
- package/src/components/Forms/SegmentedControl/SegmentedControl.tsx +122 -0
- package/src/components/Forms/SegmentedControl/index.ts +5 -0
- package/src/components/Forms/SegmentedControl/styles/mixins.scss +52 -0
- package/src/components/Forms/SegmentedControl/styles/style.scss +35 -0
- package/src/components/Forms/SegmentedControl/tests/SegmentedControl.conformance.test.jsx +36 -0
- package/src/components/Forms/SegmentedControl/tests/SegmentedControl.unit.test.jsx +125 -0
- package/src/components/Forms/SelectionTile/PLAN.md +206 -0
- package/src/components/Forms/SelectionTile/SelectionTile.tsx +145 -0
- package/src/components/Forms/SelectionTile/index.ts +5 -0
- package/src/components/Forms/SelectionTile/styles/mixins.scss +83 -0
- package/src/components/Forms/SelectionTile/styles/style.scss +58 -0
- package/src/components/Forms/SelectionTile/tests/SelectionTile.conformance.test.jsx +32 -0
- package/src/components/Forms/SelectionTile/tests/SelectionTile.unit.test.jsx +146 -0
- package/src/components/Forms/index.ts +2 -0
- package/src/components/Icon/index.ts +1 -0
- package/src/components/Megamenu/Megamenu.tsx +20 -16
- package/src/components/Megamenu/MegamenuBlog.tsx +10 -7
- package/src/components/Megamenu/MyOrangeMobilePanel.tsx +2 -3
- package/src/components/Megamenu/constants.ts +3 -1
- package/src/components/Megamenu/styles/mixins.scss +53 -18
- package/src/components/Megamenu/styles/style.scss +12 -4
- package/src/components/Megamenu/tests/Megamenu.unit.test.jsx +32 -0
- package/src/components/Modal/styles/config.scss +3 -2
- package/src/components/StatusIndicator/StatusIndicator.tsx +29 -0
- package/src/components/StatusIndicator/index.ts +2 -0
- package/src/components/StatusIndicator/styles/config.scss +12 -0
- package/src/components/StatusIndicator/styles/mixins.scss +36 -0
- package/src/components/StatusIndicator/styles/style.scss +16 -0
- package/src/components/StatusIndicator/tests/StatusIndicator.conformance.test.jsx +22 -0
- package/src/components/StatusIndicator/tests/StatusIndicator.unit.test.jsx +70 -0
- package/src/components/Table/Table.static.ts +23 -10
- package/src/components/Table/Table.tsx +7 -2
- package/src/components/Table/styles/mixins.scss +59 -0
- package/src/components/Table/styles/style.scss +25 -0
- package/src/components/Table/tests/Table.static.unit.test.jsx +72 -0
- package/src/components/Table/tests/Table.unit.test.jsx +33 -0
- package/src/components/index.ts +13 -0
- package/src/styles/tokens/overlay.scss +3 -0
- package/src/styles/typography/style.scss +0 -13
- package/src/styles/utilities/border.scss +4 -0
- package/src/styles/utilities/border.styles.unit.test.jsx +9 -0
- package/src/styles/utilities/text.scss +12 -0
- package/src/components/Carousel/CHANGELOG.md +0 -11
|
@@ -4,6 +4,38 @@ import "@testing-library/jest-dom/vitest";
|
|
|
4
4
|
import { Megamenu, MegamenuBlog } from "../";
|
|
5
5
|
|
|
6
6
|
describe("Megamenu logo markup", () => {
|
|
7
|
+
it("separates top links from the language switcher without a spacer item", () => {
|
|
8
|
+
const { container } = render(<Megamenu notSticky />);
|
|
9
|
+
const topContent = container.querySelector(".megamenu__top-content");
|
|
10
|
+
const lists = topContent?.querySelectorAll(":scope > ul");
|
|
11
|
+
|
|
12
|
+
expect(lists).toHaveLength(2);
|
|
13
|
+
expect(lists?.[1]).toHaveAttribute("aria-label", "Výber jazyka");
|
|
14
|
+
expect(
|
|
15
|
+
topContent?.querySelector(".megamenu__nav-spacer"),
|
|
16
|
+
).not.toBeInTheDocument();
|
|
17
|
+
expect(
|
|
18
|
+
topContent?.querySelector(".megamenu__nav-item--push"),
|
|
19
|
+
).toHaveTextContent("Velké firmy");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("pushes mobile utility controls after the main logo", () => {
|
|
23
|
+
const { container, rerender } = render(<Megamenu notSticky />);
|
|
24
|
+
const mainLogoItem = container.querySelector(
|
|
25
|
+
".megamenu__main .megamenu__nav-item--main:first-child",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
expect(mainLogoItem).toHaveClass("megamenu__nav-item--push");
|
|
29
|
+
|
|
30
|
+
rerender(<MegamenuBlog notSticky />);
|
|
31
|
+
|
|
32
|
+
expect(
|
|
33
|
+
container.querySelector(
|
|
34
|
+
".megamenu__main .megamenu__nav-item--main:first-child",
|
|
35
|
+
),
|
|
36
|
+
).toHaveClass("megamenu__nav-item--push");
|
|
37
|
+
});
|
|
38
|
+
|
|
7
39
|
it("is sticky by default and supports disabling stickiness", () => {
|
|
8
40
|
const { container, rerender } = render(<Megamenu />);
|
|
9
41
|
const nav = container.querySelector("[data-megamenu]");
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
@use "../../../styles/tokens/space";
|
|
2
2
|
@use "../../../styles/tokens/color";
|
|
3
|
+
@use "../../../styles/tokens/overlay";
|
|
3
4
|
@use "../../../styles/tools/convert";
|
|
4
5
|
|
|
5
|
-
$overlay-bg-color:
|
|
6
|
+
$overlay-bg-color: overlay.$background !default;
|
|
6
7
|
$overlay-z-index: 500 !default;
|
|
7
|
-
$overlay-e:
|
|
8
|
+
$overlay-e: overlay.$transition !default;
|
|
8
9
|
|
|
9
10
|
$overlay: (
|
|
10
11
|
background-color: $overlay-bg-color,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import cx from "classnames";
|
|
2
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
3
|
+
import React from "react";
|
|
4
|
+
|
|
5
|
+
export interface StatusIndicatorProps extends HTMLAttributes<HTMLSpanElement> {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
type: "success" | "warning" | "danger";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const CLASS_ROOT = "status-indicator";
|
|
11
|
+
|
|
12
|
+
const StatusIndicator: React.FC<StatusIndicatorProps> = ({
|
|
13
|
+
children,
|
|
14
|
+
className,
|
|
15
|
+
type,
|
|
16
|
+
...other
|
|
17
|
+
}) => (
|
|
18
|
+
<span
|
|
19
|
+
className={cx(CLASS_ROOT, `${CLASS_ROOT}--${type}`, className)}
|
|
20
|
+
{...other}
|
|
21
|
+
>
|
|
22
|
+
<span className={`${CLASS_ROOT}__marker`} aria-hidden="true" />
|
|
23
|
+
<span className={`${CLASS_ROOT}__content`}>{children}</span>
|
|
24
|
+
</span>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
StatusIndicator.displayName = "StatusIndicator";
|
|
28
|
+
|
|
29
|
+
export { StatusIndicator };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
@use "../../../styles/tools/generate";
|
|
2
|
+
@use "../../../styles/typography/config" as typography;
|
|
3
|
+
@use "./config";
|
|
4
|
+
|
|
5
|
+
@mixin base($config: config.$base) {
|
|
6
|
+
display: inline-flex;
|
|
7
|
+
align-items: flex-start;
|
|
8
|
+
|
|
9
|
+
@include generate.css-map($config);
|
|
10
|
+
@include generate.css-map(typography.$caption, "default");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@mixin marker($config: config.$marker) {
|
|
14
|
+
flex: 0 0 auto;
|
|
15
|
+
border-radius: 50%;
|
|
16
|
+
|
|
17
|
+
@include generate.css-map($config);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@mixin content() {
|
|
21
|
+
min-width: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@mixin marker-types() {
|
|
25
|
+
.status-indicator--success & {
|
|
26
|
+
background-color: var(--color-icon-positive);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.status-indicator--warning & {
|
|
30
|
+
background-color: var(--color-icon-warning);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.status-indicator--danger & {
|
|
34
|
+
background-color: var(--color-icon-negative);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
import { axe } from "vitest-axe";
|
|
3
|
+
|
|
4
|
+
import { StatusIndicator } from "../";
|
|
5
|
+
|
|
6
|
+
const example = (
|
|
7
|
+
<>
|
|
8
|
+
<StatusIndicator type="success">Available</StatusIndicator>
|
|
9
|
+
<StatusIndicator type="warning">Limited availability</StatusIndicator>
|
|
10
|
+
<StatusIndicator type="danger">Unavailable</StatusIndicator>
|
|
11
|
+
</>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
it("is valid html", () => {
|
|
15
|
+
const { container } = render(example);
|
|
16
|
+
expect(container).toHTMLValidate();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("is accessible", async () => {
|
|
20
|
+
const { container } = render(example);
|
|
21
|
+
expect(await axe(container)).toHaveNoViolations();
|
|
22
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import * as components from "../../";
|
|
4
|
+
|
|
5
|
+
describe("StatusIndicator", () => {
|
|
6
|
+
it("is available from the public component entry point", () => {
|
|
7
|
+
expect(components.StatusIndicator).toBeTypeOf("function");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it.each([
|
|
11
|
+
["success", "status-indicator--success"],
|
|
12
|
+
["warning", "status-indicator--warning"],
|
|
13
|
+
["danger", "status-indicator--danger"],
|
|
14
|
+
])("renders the %s type", (type, expectedClass) => {
|
|
15
|
+
const { getByText } = render(
|
|
16
|
+
<components.StatusIndicator type={type}>
|
|
17
|
+
{type}
|
|
18
|
+
</components.StatusIndicator>,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
expect(getByText(type).closest(".status-indicator")).toHaveClass(
|
|
22
|
+
"status-indicator",
|
|
23
|
+
expectedClass,
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("renders a decorative marker before the label", () => {
|
|
28
|
+
const { getByTestId } = render(
|
|
29
|
+
<components.StatusIndicator data-testid="status" type="success">
|
|
30
|
+
Available
|
|
31
|
+
</components.StatusIndicator>,
|
|
32
|
+
);
|
|
33
|
+
const statusIndicator = getByTestId("status");
|
|
34
|
+
const marker = statusIndicator.firstElementChild;
|
|
35
|
+
|
|
36
|
+
expect(marker).toHaveClass("status-indicator__marker");
|
|
37
|
+
expect(marker).toHaveAttribute("aria-hidden", "true");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("groups text and inline controls in one wrapping content item", () => {
|
|
41
|
+
const { getByTestId, getByRole } = render(
|
|
42
|
+
<components.StatusIndicator data-testid="status" type="danger">
|
|
43
|
+
Temporarily unavailable <button type="button">More information</button>
|
|
44
|
+
</components.StatusIndicator>,
|
|
45
|
+
);
|
|
46
|
+
const content = getByTestId("status").querySelector(
|
|
47
|
+
".status-indicator__content",
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(content).toHaveTextContent("Temporarily unavailable");
|
|
51
|
+
expect(content).toContainElement(
|
|
52
|
+
getByRole("button", { name: "More information" }),
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("passes native span props and additional classes", () => {
|
|
57
|
+
const { getByTestId } = render(
|
|
58
|
+
<components.StatusIndicator
|
|
59
|
+
className="custom-class"
|
|
60
|
+
data-testid="status"
|
|
61
|
+
type="warning"
|
|
62
|
+
>
|
|
63
|
+
Limited
|
|
64
|
+
</components.StatusIndicator>,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(getByTestId("status")).toHaveClass("custom-class");
|
|
68
|
+
expect(getByTestId("status")).toHaveTextContent("Limited");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import toggle from "../../scripts/modules/toggleUtil";
|
|
2
2
|
|
|
3
|
+
const SCROLLABLE_OVERFLOW_CLASS = "table-scrollable--overflowing";
|
|
4
|
+
|
|
3
5
|
interface TableConfig {
|
|
4
6
|
expandButtonSelector: string;
|
|
5
7
|
scrollableContainerSelector: string;
|
|
@@ -32,18 +34,26 @@ export default class Table {
|
|
|
32
34
|
|
|
33
35
|
(this.element as any).ODS_Table = this;
|
|
34
36
|
|
|
35
|
-
this.resizeObserver = new ResizeObserver((
|
|
37
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
36
38
|
if (!this.scrollableContainer) return;
|
|
37
39
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
const overflowX = getComputedStyle(this.scrollableContainer).overflowX;
|
|
41
|
+
const canScroll = overflowX === "auto" || overflowX === "scroll";
|
|
42
|
+
const hasOverflow =
|
|
43
|
+
this.scrollableContainer.scrollWidth >
|
|
44
|
+
this.scrollableContainer.clientWidth;
|
|
45
|
+
const isScrollable = canScroll && hasOverflow;
|
|
46
|
+
|
|
47
|
+
this.scrollableContainer.classList.toggle(
|
|
48
|
+
SCROLLABLE_OVERFLOW_CLASS,
|
|
49
|
+
isScrollable,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (isScrollable) {
|
|
53
|
+
this.scrollableContainer.setAttribute("tabindex", "0");
|
|
54
|
+
} else {
|
|
55
|
+
this.scrollableContainer.removeAttribute("tabindex");
|
|
56
|
+
}
|
|
47
57
|
});
|
|
48
58
|
|
|
49
59
|
this.init();
|
|
@@ -120,6 +130,7 @@ export default class Table {
|
|
|
120
130
|
);
|
|
121
131
|
if (this.scrollableContainer) {
|
|
122
132
|
this.resizeObserver.observe(this.element);
|
|
133
|
+
this.resizeObserver.observe(this.scrollableContainer);
|
|
123
134
|
return true;
|
|
124
135
|
}
|
|
125
136
|
return undefined;
|
|
@@ -131,6 +142,8 @@ export default class Table {
|
|
|
131
142
|
});
|
|
132
143
|
|
|
133
144
|
this.resizeObserver.disconnect();
|
|
145
|
+
this.scrollableContainer?.classList.remove(SCROLLABLE_OVERFLOW_CLASS);
|
|
146
|
+
this.scrollableContainer?.removeAttribute("tabindex");
|
|
134
147
|
|
|
135
148
|
this.expandButtons = [];
|
|
136
149
|
}
|
|
@@ -26,6 +26,8 @@ interface TableProps extends React.TableHTMLAttributes<HTMLTableElement> {
|
|
|
26
26
|
isResponsive?: boolean;
|
|
27
27
|
/** If table has horizontal scrollbar */
|
|
28
28
|
isScrollable?: boolean;
|
|
29
|
+
/** Keep the first column visible while the table scrolls horizontally */
|
|
30
|
+
isFirstColumnPinned?: boolean;
|
|
29
31
|
/** Different colors for even and odd rows. */
|
|
30
32
|
isStriped?: boolean;
|
|
31
33
|
/** rows to print out in the table. */
|
|
@@ -45,6 +47,7 @@ const Table: React.FC<TableProps> = ({
|
|
|
45
47
|
isResponsive,
|
|
46
48
|
isStriped,
|
|
47
49
|
isScrollable,
|
|
50
|
+
isFirstColumnPinned,
|
|
48
51
|
expandableRowsCaptions = {
|
|
49
52
|
header: "Rozšíriteľný",
|
|
50
53
|
emptyRow: "Nerozšíriteľný riadok",
|
|
@@ -60,11 +63,13 @@ const Table: React.FC<TableProps> = ({
|
|
|
60
63
|
[`${CLASS_ROOT}--striped`]: isStriped,
|
|
61
64
|
[`${CLASS_ROOT}--compact`]: isCompact,
|
|
62
65
|
[`${CLASS_ROOT}--responsive`]: isResponsive,
|
|
66
|
+
[`${CLASS_ROOT}--pinned-first-column`]: isFirstColumnPinned,
|
|
63
67
|
},
|
|
64
68
|
className,
|
|
65
69
|
);
|
|
66
70
|
|
|
67
71
|
const captionId = `${idPrefix}-caption`;
|
|
72
|
+
const hasScrollableRegion = isScrollable || isFirstColumnPinned;
|
|
68
73
|
|
|
69
74
|
const expandableRows = {
|
|
70
75
|
hasExpandableRows: rows.some(
|
|
@@ -98,7 +103,7 @@ const Table: React.FC<TableProps> = ({
|
|
|
98
103
|
const table = (
|
|
99
104
|
<TableContext.Provider value={context}>
|
|
100
105
|
<ConditionalWrapper
|
|
101
|
-
condition={
|
|
106
|
+
condition={hasScrollableRegion || false}
|
|
102
107
|
wrapper={
|
|
103
108
|
<div
|
|
104
109
|
className={`${CLASS_ROOT}-scrollable`}
|
|
@@ -114,7 +119,7 @@ const Table: React.FC<TableProps> = ({
|
|
|
114
119
|
{...other}
|
|
115
120
|
>
|
|
116
121
|
{caption && (
|
|
117
|
-
<caption {...(
|
|
122
|
+
<caption {...(hasScrollableRegion && { id: captionId })}>
|
|
118
123
|
{caption}
|
|
119
124
|
</caption>
|
|
120
125
|
)}
|
|
@@ -57,17 +57,33 @@
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
@mixin scrollable-wrapper {
|
|
60
|
+
container-type: inline-size;
|
|
60
61
|
display: block;
|
|
61
62
|
width: 100%;
|
|
62
63
|
overflow-x: auto;
|
|
63
64
|
-webkit-overflow-scrolling: touch;
|
|
64
65
|
|
|
66
|
+
caption {
|
|
67
|
+
position: sticky;
|
|
68
|
+
left: 0;
|
|
69
|
+
width: 100cqw;
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
td,
|
|
66
73
|
th {
|
|
67
74
|
white-space: nowrap;
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
|
|
78
|
+
@mixin responsive-scrollable-wrapper {
|
|
79
|
+
overflow-x: visible;
|
|
80
|
+
|
|
81
|
+
td,
|
|
82
|
+
th {
|
|
83
|
+
white-space: normal;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
71
87
|
@mixin sort-button {
|
|
72
88
|
position: relative;
|
|
73
89
|
display: inline-block;
|
|
@@ -117,6 +133,49 @@
|
|
|
117
133
|
width: auto;
|
|
118
134
|
}
|
|
119
135
|
|
|
136
|
+
@mixin pinned-first-column {
|
|
137
|
+
tr > :first-child {
|
|
138
|
+
position: sticky;
|
|
139
|
+
left: 0;
|
|
140
|
+
z-index: 1;
|
|
141
|
+
background: var(
|
|
142
|
+
--table-pinned-column-background,
|
|
143
|
+
var(--color-background-primary)
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
&::after {
|
|
147
|
+
content: "";
|
|
148
|
+
display: none;
|
|
149
|
+
position: absolute;
|
|
150
|
+
top: 0;
|
|
151
|
+
right: -12px;
|
|
152
|
+
bottom: 0;
|
|
153
|
+
width: 12px;
|
|
154
|
+
background: linear-gradient(
|
|
155
|
+
to right,
|
|
156
|
+
color-mix(in srgb, var(--color-text-default) 10%, transparent),
|
|
157
|
+
transparent
|
|
158
|
+
);
|
|
159
|
+
pointer-events: none;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
thead > tr > :first-child {
|
|
164
|
+
z-index: 2;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@mixin unpin-first-column {
|
|
169
|
+
tr > :first-child {
|
|
170
|
+
position: static;
|
|
171
|
+
background: transparent;
|
|
172
|
+
|
|
173
|
+
&::after {
|
|
174
|
+
display: none;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
120
179
|
@mixin responsive-table {
|
|
121
180
|
thead {
|
|
122
181
|
display: none;
|
|
@@ -38,20 +38,39 @@
|
|
|
38
38
|
|
|
39
39
|
&--striped tbody tr:nth-of-type(odd) {
|
|
40
40
|
@include mixins.odd-row;
|
|
41
|
+
--table-pinned-column-background: var(--color-background-secondary);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
&--striped.background-secondary tbody tr:nth-of-type(odd),
|
|
44
45
|
.background-secondary &--striped tbody tr:nth-of-type(odd) {
|
|
45
46
|
background-color: config.$striped-color-on-secondary;
|
|
47
|
+
--table-pinned-column-background: var(--color-background-primary);
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
&--compact {
|
|
49
51
|
@include mixins.compact;
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
&--pinned-first-column {
|
|
55
|
+
@include mixins.pinned-first-column;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&--pinned-first-column.background-secondary,
|
|
59
|
+
.background-secondary &--pinned-first-column {
|
|
60
|
+
--table-pinned-column-background: var(--color-background-secondary);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&-scrollable--overflowing &--pinned-first-column tr > :first-child::after {
|
|
64
|
+
display: block;
|
|
65
|
+
}
|
|
66
|
+
|
|
52
67
|
&--responsive {
|
|
53
68
|
@include breakpoint.get("md", "down") {
|
|
54
69
|
@include mixins.responsive-table;
|
|
70
|
+
|
|
71
|
+
&.table--pinned-first-column {
|
|
72
|
+
@include mixins.unpin-first-column;
|
|
73
|
+
}
|
|
55
74
|
}
|
|
56
75
|
}
|
|
57
76
|
|
|
@@ -63,6 +82,12 @@
|
|
|
63
82
|
}
|
|
64
83
|
}
|
|
65
84
|
|
|
85
|
+
@include breakpoint.get("md", "down") {
|
|
86
|
+
&-scrollable:has(> &--responsive) {
|
|
87
|
+
@include mixins.responsive-scrollable-wrapper;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
66
91
|
&__sort-button {
|
|
67
92
|
@include mixins.sort-button;
|
|
68
93
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import Table from "../Table.static";
|
|
4
|
+
|
|
5
|
+
let resizeCallback;
|
|
6
|
+
let observe;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
observe = vi.fn();
|
|
10
|
+
vi.stubGlobal(
|
|
11
|
+
"ResizeObserver",
|
|
12
|
+
vi.fn(function (callback) {
|
|
13
|
+
resizeCallback = callback;
|
|
14
|
+
this.observe = observe;
|
|
15
|
+
this.unobserve = vi.fn();
|
|
16
|
+
this.disconnect = vi.fn();
|
|
17
|
+
}),
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
vi.unstubAllGlobals();
|
|
23
|
+
document.body.innerHTML = "";
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("does not make a responsive non-scrolling wrapper focusable", () => {
|
|
27
|
+
document.body.innerHTML = `
|
|
28
|
+
<div class="table-scrollable table-scrollable--overflowing" style="overflow-x: visible" tabindex="0">
|
|
29
|
+
<table class="table table--responsive"></table>
|
|
30
|
+
</div>
|
|
31
|
+
`;
|
|
32
|
+
const table = document.querySelector("table");
|
|
33
|
+
const scrollableRegion = document.querySelector(".table-scrollable");
|
|
34
|
+
Object.defineProperties(scrollableRegion, {
|
|
35
|
+
clientWidth: { configurable: true, value: 400 },
|
|
36
|
+
scrollWidth: { configurable: true, value: 800 },
|
|
37
|
+
});
|
|
38
|
+
new Table(table);
|
|
39
|
+
|
|
40
|
+
resizeCallback([{ target: table, contentRect: { width: 800 } }]);
|
|
41
|
+
|
|
42
|
+
expect(scrollableRegion).not.toHaveAttribute("tabindex");
|
|
43
|
+
expect(scrollableRegion).not.toHaveClass("table-scrollable--overflowing");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("marks a horizontally overflowing wrapper", () => {
|
|
47
|
+
document.body.innerHTML = `
|
|
48
|
+
<div class="table-scrollable" style="overflow-x: auto">
|
|
49
|
+
<table class="table table--pinned-first-column"></table>
|
|
50
|
+
</div>
|
|
51
|
+
`;
|
|
52
|
+
const table = document.querySelector("table");
|
|
53
|
+
const scrollableRegion = document.querySelector(".table-scrollable");
|
|
54
|
+
Object.defineProperties(scrollableRegion, {
|
|
55
|
+
clientWidth: { configurable: true, value: 400 },
|
|
56
|
+
scrollWidth: { configurable: true, value: 800 },
|
|
57
|
+
});
|
|
58
|
+
const instance = new Table(table);
|
|
59
|
+
|
|
60
|
+
expect(observe).toHaveBeenCalledWith(table);
|
|
61
|
+
expect(observe).toHaveBeenCalledWith(scrollableRegion);
|
|
62
|
+
|
|
63
|
+
resizeCallback([{ target: table, contentRect: { width: 800 } }]);
|
|
64
|
+
|
|
65
|
+
expect(scrollableRegion).toHaveClass("table-scrollable--overflowing");
|
|
66
|
+
expect(scrollableRegion).toHaveAttribute("tabindex", "0");
|
|
67
|
+
|
|
68
|
+
instance.destroy();
|
|
69
|
+
|
|
70
|
+
expect(scrollableRegion).not.toHaveClass("table-scrollable--overflowing");
|
|
71
|
+
expect(scrollableRegion).not.toHaveAttribute("tabindex");
|
|
72
|
+
});
|
|
@@ -194,6 +194,39 @@ describe("rendering Table", () => {
|
|
|
194
194
|
expect(getByTestId("test-id")).toHaveClass("table");
|
|
195
195
|
expect(getByTestId("test-id")).toHaveClass("table--compact");
|
|
196
196
|
});
|
|
197
|
+
it("pins the first column and enables overflow handling when isFirstColumnPinned is passed", () => {
|
|
198
|
+
const { container, getByTestId } = render(
|
|
199
|
+
<Table
|
|
200
|
+
data-testid="test-id"
|
|
201
|
+
headers={[]}
|
|
202
|
+
rows={[]}
|
|
203
|
+
isFirstColumnPinned
|
|
204
|
+
/>,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
expect(getByTestId("test-id")).toHaveClass("table--pinned-first-column");
|
|
208
|
+
expect(container.querySelector(".table-scrollable")).toContainElement(
|
|
209
|
+
getByTestId("test-id"),
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
it("labels the pinned-column scroll region with its caption", () => {
|
|
213
|
+
const { container } = render(
|
|
214
|
+
<Table
|
|
215
|
+
headers={[]}
|
|
216
|
+
rows={[]}
|
|
217
|
+
caption="Pinned table"
|
|
218
|
+
isFirstColumnPinned
|
|
219
|
+
/>,
|
|
220
|
+
);
|
|
221
|
+
const scrollableRegion = container.querySelector(".table-scrollable");
|
|
222
|
+
const caption = container.querySelector("caption");
|
|
223
|
+
|
|
224
|
+
expect(caption).toHaveAttribute(
|
|
225
|
+
"id",
|
|
226
|
+
scrollableRegion.getAttribute("aria-labelledby"),
|
|
227
|
+
);
|
|
228
|
+
expect(caption).toHaveTextContent("Pinned table");
|
|
229
|
+
});
|
|
197
230
|
it("renders div wrapper with class table-scrollable when isScrollable is passed", () => {
|
|
198
231
|
const { container } = render(
|
|
199
232
|
<Table headers={[]} rows={[]} isScrollable />,
|
package/src/components/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { Controls } from "./Controls";
|
|
|
29
29
|
import { Countdown, CountdownTag } from "./Countdown";
|
|
30
30
|
import { Cover } from "./Cover";
|
|
31
31
|
import { Divider } from "./Divider";
|
|
32
|
+
import { Drawer, DrawerBody, DrawerFooter, DrawerHeader } from "./Drawer";
|
|
32
33
|
import {
|
|
33
34
|
Dropdown,
|
|
34
35
|
DropdownDivider,
|
|
@@ -50,7 +51,10 @@ import {
|
|
|
50
51
|
Message,
|
|
51
52
|
Radio,
|
|
52
53
|
RangeSlider,
|
|
54
|
+
SegmentedControl,
|
|
55
|
+
SegmentedControlItem,
|
|
53
56
|
Select,
|
|
57
|
+
SelectionTile,
|
|
54
58
|
TextArea,
|
|
55
59
|
TextInput,
|
|
56
60
|
} from "./Forms";
|
|
@@ -88,6 +92,7 @@ import { Skeleton } from "./Skeleton";
|
|
|
88
92
|
import { SkipLink } from "./SkipLink";
|
|
89
93
|
import { SocialButton } from "./SocialButton";
|
|
90
94
|
import { StarRating } from "./StarRating";
|
|
95
|
+
import { StatusIndicator } from "./StatusIndicator";
|
|
91
96
|
import { Stepbar } from "./Stepbar";
|
|
92
97
|
import { Sticker } from "./Sticker";
|
|
93
98
|
import { Table } from "./Table";
|
|
@@ -134,6 +139,10 @@ export {
|
|
|
134
139
|
CountdownTag,
|
|
135
140
|
Cover,
|
|
136
141
|
Divider,
|
|
142
|
+
Drawer,
|
|
143
|
+
DrawerBody,
|
|
144
|
+
DrawerFooter,
|
|
145
|
+
DrawerHeader,
|
|
137
146
|
Dropdown,
|
|
138
147
|
DropdownDivider,
|
|
139
148
|
DropdownItem,
|
|
@@ -185,13 +194,17 @@ export {
|
|
|
185
194
|
Radio,
|
|
186
195
|
RangeSlider,
|
|
187
196
|
Section,
|
|
197
|
+
SegmentedControl,
|
|
198
|
+
SegmentedControlItem,
|
|
188
199
|
Select,
|
|
200
|
+
SelectionTile,
|
|
189
201
|
Skeleton,
|
|
190
202
|
SkipLink,
|
|
191
203
|
SocialButton,
|
|
192
204
|
SpritePathContext,
|
|
193
205
|
SpritePathProvider,
|
|
194
206
|
StarRating,
|
|
207
|
+
StatusIndicator,
|
|
195
208
|
Stepbar,
|
|
196
209
|
Sticker,
|
|
197
210
|
Tab,
|