diff --git a/src/lib/components/table/Table.tsx b/src/lib/components/table/Table.tsx
index b237ba3..3e4545a 100644
--- a/src/lib/components/table/Table.tsx
+++ b/src/lib/components/table/Table.tsx
@@ -1,6 +1,6 @@
import React, { useState } from "react";
import classNames from "classnames";
-import { VuiCheckbox, VuiTextInput } from "../form";
+import { TextInputProps, VuiCheckbox, VuiTextInput } from "../form";
import { VuiSpacer } from "../spacer/Spacer";
import { Props as TableRowActionsProps, VuiTableRowActions } from "./TableRowActions";
import { VuiTableCell } from "./TableCell";
@@ -16,6 +16,12 @@ import { VuiTableContent } from "./TableContent";
import { VuiButtonSecondary } from "../button/ButtonSecondary";
import { Row } from "./types";
+const verticalAlignToClass = {
+ top: "vuiTable--verticalAlignTop",
+ middle: "vuiTable--verticalAlignMiddle",
+ bottom: "vuiTable--verticalAlignBottom"
+} as const;
+
// Type guard to determine type of pagination.
const isComplexPagination = (pagination: Pagination | Pager): pagination is Pagination => {
return (pagination as Pagination).onSelectPage !== undefined;
@@ -25,13 +31,14 @@ type Column = {
name: string;
width?: string;
header: TableHeaderCellProps["header"];
- render?: (row: T) => React.ReactNode;
+ render?: (row: T, rowIndex: number) => React.ReactNode;
className?: string;
};
type Props = {
isLoading?: boolean;
idField: keyof T | ((row: T) => string);
+ rowDecorator?: (row: T) => Record;
columns: Column[];
rows: T[];
actions?: TableRowActionsProps["actions"];
@@ -46,6 +53,11 @@ type Props = {
className?: string;
fluid?: boolean;
isDisabled?: boolean;
+ bodyStyle?: BodyStyle;
+};
+
+type BodyStyle = {
+ verticalAlign?: "top" | "middle" | "bottom";
};
type Selection = {
@@ -54,12 +66,7 @@ type Selection = {
selectedRows?: T[];
};
-type Search = {
- searchValue?: string;
- searchPlaceholder?: string;
- onSearchChange?: (value: string) => void;
- "data-testid"?: string;
-};
+type Search = TextInputProps;
const extractId = (row: T, idField: Props["idField"]) => {
return typeof idField === "function" ? idField(row) : row[idField];
@@ -70,6 +77,7 @@ const extractId = (row: T, idField: Props["idField"]) => {
export const VuiTable = ({
isLoading,
idField,
+ rowDecorator,
columns,
rows,
actions,
@@ -84,12 +92,13 @@ export const VuiTable = ({
className,
fluid,
isDisabled = false,
+ bodyStyle,
...rest
}: Props) => {
const [rowBeingActedUpon, setRowBeingActedUpon] = useState(undefined);
const { bulkActions, onSelectRow, selectedRows } = selection || {};
- const { searchValue, searchPlaceholder, onSearchChange } = search || {};
+ const { value: searchValue } = search || {};
const isEmpty = !isLoading && rows.length === 0;
// The user interacts with the table rows by selecting them or performing actions on them.
@@ -104,12 +113,13 @@ export const VuiTable = ({
return acc;
}, {} as Record) || {};
- const hasSearch = searchValue !== undefined && onSearchChange;
+ const hasSearch = search !== undefined;
const hasBulkActions = bulkActions !== undefined;
const columnCount = columns.length + (onSelectRow ? 1 : 0) + (actions ? 1 : 0);
const classes = classNames(
"vuiTable",
+ bodyStyle?.verticalAlign && verticalAlignToClass[bodyStyle.verticalAlign],
{
"vuiTable--fluid": fluid
},
@@ -145,10 +155,16 @@ export const VuiTable = ({
);
} else {
- tableContent = rows.map((row) => {
+ tableContent = rows.map((row, rowIndex) => {
const rowId = extractId(row, idField);
+ const rowAttributes = rowDecorator?.(row) ?? {};
+ const { className: rowClassNameAttribute, ...restRowAttributes } = rowAttributes;
+ const rowClassName = classNames(rowClassNameAttribute, {
+ "vuiTableRow-isBeingActedUpon": rowBeingActedUpon === row
+ });
+
return (
-
+
{/* Checkbox column */}
{onSelectRow && (
@@ -178,7 +194,7 @@ export const VuiTable = ({
return (
- {render ? render(row) : row[column.name]}
+ {render ? render(row, rowIndex) : row[column.name]}
|
);
})}
@@ -225,13 +241,7 @@ export const VuiTable = ({
{/* Search */}
{hasSearch && (
- onSearchChange(event.target.value)}
- data-testid={search?.["data-testid"]}
- />
+
)}
diff --git a/src/lib/components/table/TableBulkActions.tsx b/src/lib/components/table/TableBulkActions.tsx
index 099caf0..b9e46d1 100644
--- a/src/lib/components/table/TableBulkActions.tsx
+++ b/src/lib/components/table/TableBulkActions.tsx
@@ -22,6 +22,7 @@ export const VuiTableBulkActions = ({ selectedRows, actions }: Pr
actions[0].onClick && actions[0].onClick(selectedRows)}
>
{actions[0].label}
diff --git a/src/lib/components/table/_index.scss b/src/lib/components/table/_index.scss
index 90eee5a..940d660 100644
--- a/src/lib/components/table/_index.scss
+++ b/src/lib/components/table/_index.scss
@@ -47,6 +47,24 @@
}
}
+.vuiTable--verticalAlignTop {
+ tbody td {
+ vertical-align: top;
+ }
+}
+
+.vuiTable--verticalAlignMiddle {
+ tbody td {
+ vertical-align: middle;
+ }
+}
+
+.vuiTable--verticalAlignBottom {
+ tbody td {
+ vertical-align: bottom;
+ }
+}
+
.vuiTable--fluid {
table-layout: auto;
}
diff --git a/src/lib/components/timeline/Timeline.tsx b/src/lib/components/timeline/Timeline.tsx
new file mode 100644
index 0000000..1dc8b1e
--- /dev/null
+++ b/src/lib/components/timeline/Timeline.tsx
@@ -0,0 +1,17 @@
+import classNames from "classnames";
+import React from "react";
+
+type Props = {
+ children: React.ReactNode;
+};
+
+export const VuiTimeline = ({ children }: Props) => {
+ const childrenCount = React.Children.count(children);
+ const wrappedChildren = React.Children.map(children, (child, index) => {
+ const isLast = index === childrenCount - 1;
+ const classes = classNames("vuiTimelineContainer", { "vuiTimelineContainer--bordered": !isLast });
+ return {child} ;
+ });
+
+ return <>{wrappedChildren}>;
+};
diff --git a/src/lib/components/timeline/TimelineItem.tsx b/src/lib/components/timeline/TimelineItem.tsx
new file mode 100644
index 0000000..d987180
--- /dev/null
+++ b/src/lib/components/timeline/TimelineItem.tsx
@@ -0,0 +1,21 @@
+import { VuiFlexContainer } from "../flex/FlexContainer";
+import { VuiFlexItem } from "../flex/FlexItem";
+
+type Props = {
+ icon: React.ReactNode;
+ children: React.ReactNode;
+};
+
+export const VuiTimelineItem = ({ icon, children }: Props) => {
+ return (
+
+
+ {icon}
+
+
+
+ {children}
+
+
+ );
+};
diff --git a/src/lib/components/timeline/_index.scss b/src/lib/components/timeline/_index.scss
new file mode 100644
index 0000000..6ca97a3
--- /dev/null
+++ b/src/lib/components/timeline/_index.scss
@@ -0,0 +1,26 @@
+$iconWidth: 36px;
+
+.vuiTimelineContainer {
+ margin-left: $iconWidth;
+ border-left: 1px solid transparent;
+ padding-bottom: $sizeM;
+}
+
+.vuiTimelineContainer--bordered {
+ border-left: 2px solid $borderColorLight;
+}
+
+.vuiTimelineItem {
+ margin-left: -$iconWidth * 0.5;
+}
+
+.vuiTimelineItem__icon {
+ border-radius: 100%;
+ padding: $sizeXs;
+ background-color: $colorEmptyShade;
+}
+
+.vuiTimelineItem__content {
+ padding: $sizeXxs $sizeM $sizeS;
+ width: 100%;
+}
diff --git a/src/lib/components/timeline/index.ts b/src/lib/components/timeline/index.ts
new file mode 100644
index 0000000..db9ffb8
--- /dev/null
+++ b/src/lib/components/timeline/index.ts
@@ -0,0 +1,2 @@
+export { VuiTimeline } from "./Timeline";
+export { VuiTimelineItem } from "./TimelineItem";
diff --git a/src/lib/components/typography/_title.scss b/src/lib/components/typography/_title.scss
index 6ef9516..d606eec 100644
--- a/src/lib/components/typography/_title.scss
+++ b/src/lib/components/typography/_title.scss
@@ -9,7 +9,7 @@ $size: (
xxs: (
size: $fontSizeSmall,
line-height: 1.4,
- weight: $fontWeightStrong,
+ weight: $fontWeightBold,
color: $colorText
),
xs: (
@@ -22,30 +22,30 @@ $size: (
size: $fontSizeLarge,
line-height: 1.3,
weight: $fontWeightBold,
- color: $colorSubdued
+ color: $colorText
),
m: (
size: $fontSizeXLarge,
- weight: $fontWeightBold,
+ weight: $fontWeightHeavy,
line-height: 1.2,
color: $colorText
),
l: (
size: $fontSizeXxLarge,
- weight: $fontWeightNormal,
+ weight: $fontWeightHeavy,
line-height: 1.1,
color: $colorText
),
xl: (
size: $fontSizeXxxLarge,
- weight: $fontWeightNormal,
+ weight: $fontWeightBold,
line-height: 1,
color: $colorText
),
xxl: (
size: $fontSizeXxxLarge,
line-height: 1,
- weight: $fontWeightBold,
+ weight: $fontWeightHeavy,
color: $colorText
)
);
diff --git a/src/lib/sassUtils/_depth.scss b/src/lib/sassUtils/_depth.scss
index c8c4cd6..642d7a8 100644
--- a/src/lib/sassUtils/_depth.scss
+++ b/src/lib/sassUtils/_depth.scss
@@ -3,6 +3,7 @@ $chatZIndex: 9;
$screenBlockZIndex: 10;
$drawerZIndex: 11;
$modalZIndex: 12;
+$fullscreenZIndex: 13;
// Enable popovers to be placed inside of modals and drawers.
-$popoverZIndex: 13;
+$popoverZIndex: 14;
$notificationsZIndex: 1000;
diff --git a/src/lib/sassUtils/_typography.scss b/src/lib/sassUtils/_typography.scss
index ea12529..6e22c1c 100644
--- a/src/lib/sassUtils/_typography.scss
+++ b/src/lib/sassUtils/_typography.scss
@@ -14,6 +14,7 @@ $colorSubdued: $colorDarkShade;
$fontWeightNormal: 400;
$fontWeightStrong: 500;
$fontWeightBold: 600;
+$fontWeightHeavy: 800;
$labelFontSize: $fontSizeStandard;
$labelFontWeight: $fontWeightBold;
diff --git a/src/lib/styles/styles.scss b/src/lib/styles/styles.scss
index e66b764..4e13412 100644
--- a/src/lib/styles/styles.scss
+++ b/src/lib/styles/styles.scss
@@ -3,7 +3,6 @@
@import "../sassUtils/";
@import "../components/accordion/index";
@import "../components/app/index";
-@import "../components/accountMenu/index";
@import "../components/badge/index";
@import "../components/button/index";
@import "../components/callout/index";
@@ -16,6 +15,8 @@
@import "../components/grid/index";
@import "../components/horizontalRule/index";
@import "../components/icon/index";
+@import "../components/infoList/index";
+@import "../components/infoMenu/index";
@import "../components/infoTable/index";
@import "../components/link/index";
@import "../components/list/index";
@@ -37,6 +38,7 @@
@import "../components/summary/index";
@import "../components/table/index";
@import "../components/tabs/index";
+@import "../components/timeline/index";
@import "../components/toggle/index";
@import "../components/topicButton/index";
@import "../components/typography/index";
|