From 234048f50779db0dee1e299e8eb4801683498379 Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Mon, 25 Mar 2024 17:40:44 +0000 Subject: [PATCH 1/2] feat: establish Tooltip as its own ably-ui component --- src/core/Tooltip/Tooltip.stories.tsx | 27 +++++++++++++ src/core/Tooltip/component.tsx | 60 ++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/core/Tooltip/Tooltip.stories.tsx create mode 100644 src/core/Tooltip/component.tsx diff --git a/src/core/Tooltip/Tooltip.stories.tsx b/src/core/Tooltip/Tooltip.stories.tsx new file mode 100644 index 000000000..a73f2f957 --- /dev/null +++ b/src/core/Tooltip/Tooltip.stories.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import Tooltip from "./component"; + +export default { + title: "Components/Tooltip", + component: Tooltip, + tags: ["autodocs"], + args: { + children: "Example content", + }, +}; + +export const Central = { + render: (args) => ( +
+ {args.children} +
+ ), +}; + +export const LeftBound = { + render: (args) => ( +
+ {args.children} +
+ ), +}; diff --git a/src/core/Tooltip/component.tsx b/src/core/Tooltip/component.tsx new file mode 100644 index 000000000..1ba88d0dd --- /dev/null +++ b/src/core/Tooltip/component.tsx @@ -0,0 +1,60 @@ +import React, { PropsWithChildren, useEffect, useRef, useState } from "react"; +import Icon from "../Icon/component.tsx"; + +const Tooltip = ({ children }: PropsWithChildren) => { + const [open, setOpen] = useState(false); + const [position, setPosition] = useState({ x: 0, y: 0 }); + const offset = 8; + const reference = useRef(null); + const floating = useRef(null); + + useEffect(() => { + if (open) { + const floatingRect = floating.current?.getBoundingClientRect(); + const referenceRect = reference.current?.getBoundingClientRect(); + + if (floatingRect && referenceRect) { + setPosition({ + x: + Math.min(floatingRect.width / 2, floatingRect.left) - + referenceRect.width / 2, + y: Math.min(floatingRect.height, floatingRect.top) + offset, + }); + } + } else { + setPosition({ x: 0, y: 0 }); + } + }, [open]); + + return ( +
+ + + {open ? ( +
+
{children}
+
+ ) : null} +
+ ); +}; + +export default Tooltip; From 787e444ed942aeab511d20e31f38c7d557d59a1e Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Wed, 20 Mar 2024 17:12:24 +0000 Subject: [PATCH 2/2] feat: migrate pricing page table, convert to semantic HTML tags, open up structure like Radix --- src/core/Table/Table.tsx | 30 ++++++ src/core/Table/TableCell.tsx | 84 +++++++++++++++++ src/core/Table/TableRow.tsx | 25 +++++ src/core/Table/index.ts | 24 +++++ src/core/Table/stories/Table.stories.tsx | 12 +++ src/core/Table/stories/data.tsx | 113 +++++++++++++++++++++++ src/core/Tooltip/component.tsx | 2 +- 7 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 src/core/Table/Table.tsx create mode 100644 src/core/Table/TableCell.tsx create mode 100644 src/core/Table/TableRow.tsx create mode 100644 src/core/Table/index.ts create mode 100644 src/core/Table/stories/Table.stories.tsx create mode 100644 src/core/Table/stories/data.tsx diff --git a/src/core/Table/Table.tsx b/src/core/Table/Table.tsx new file mode 100644 index 000000000..42bbe647d --- /dev/null +++ b/src/core/Table/Table.tsx @@ -0,0 +1,30 @@ +import React, { PropsWithChildren, ReactElement, cloneElement } from "react"; + +type TableProps = { + id?: string; +}; + +export const Table = ({ id, children }: PropsWithChildren) => ( + + {children} +
+); + +export const TableBody = ({ children }: PropsWithChildren) => ( + {children} +); + +export const TableHeader = ({ children }: PropsWithChildren) => ( + + {cloneElement(children as ReactElement, { isHeader: true })} + +); + +export const TableRowHeader = ({ children }: PropsWithChildren) => ( + + {cloneElement(children as ReactElement, { isRowHeader: true })} + +); diff --git a/src/core/Table/TableCell.tsx b/src/core/Table/TableCell.tsx new file mode 100644 index 000000000..8944940f2 --- /dev/null +++ b/src/core/Table/TableCell.tsx @@ -0,0 +1,84 @@ +import React, { PropsWithChildren } from "react"; +import Icon from "../Icon/component.tsx"; + +type TableCellProps = { + isRowHeader?: boolean; +} & React.TdHTMLAttributes; + +const Supported = () => ( + +); + +const Unsupported = () => ( + +); + +const LabelCell = ({ + children, + ...rest +}: PropsWithChildren>) => { + const classes = ` + ui-text-p1 !font-bold pt-24 pb-8 border-light-grey sm:p-24 sm:relative sm:top-2 flex sm:table-cell ${ + rest.className ?? "" + } + `; + + return ( + + {children} + + ); +}; + +const TableCell = ({ + children, + isRowHeader, + ...rest +}: PropsWithChildren) => ( + + {children} + +); + +const HeaderCell = ({ + children, + ...rest +}: PropsWithChildren>) => ( + + {children} + +); + +const CtaCell = ({ + children, + ...rest +}: PropsWithChildren>) => ( + + {children} + +); + +export { TableCell, LabelCell, HeaderCell, CtaCell, Supported, Unsupported }; diff --git a/src/core/Table/TableRow.tsx b/src/core/Table/TableRow.tsx new file mode 100644 index 000000000..d2d6609c0 --- /dev/null +++ b/src/core/Table/TableRow.tsx @@ -0,0 +1,25 @@ +import React, { PropsWithChildren } from "react"; + +const CtaRow = ({ children }: PropsWithChildren) => ( + + + {children} + +); + +type RowProps = { + isHeader?: boolean; +} & React.HTMLAttributes; + +const TableRow = ({ + children, + isHeader, + ...rest +}: PropsWithChildren) => ( + + {isHeader && } + {children} + +); + +export { TableRow, CtaRow }; diff --git a/src/core/Table/index.ts b/src/core/Table/index.ts new file mode 100644 index 000000000..587800530 --- /dev/null +++ b/src/core/Table/index.ts @@ -0,0 +1,24 @@ +import { Table, TableRowHeader, TableHeader, TableBody } from "./Table"; +import { TableRow } from "./TableRow"; +import { + TableCell, + LabelCell, + HeaderCell, + CtaCell, + Supported, + Unsupported, +} from "./TableCell"; + +export default { + Root: Table, + Row: TableRow, + Cell: TableCell, + LabelCell, + HeaderCell, + CtaCell, + RowHeader: TableRowHeader, + Body: TableBody, + Header: TableHeader, + Supported, + Unsupported, +}; diff --git a/src/core/Table/stories/Table.stories.tsx b/src/core/Table/stories/Table.stories.tsx new file mode 100644 index 000000000..8e0a3fb93 --- /dev/null +++ b/src/core/Table/stories/Table.stories.tsx @@ -0,0 +1,12 @@ +import { PricingPageTable } from "./data"; + +export default { + title: "Components/Table", + component: PricingPageTable, + tags: ["autodocs"], + parameters: { + layout: "fullscreen", + }, +}; + +export const PricingPage = {}; diff --git a/src/core/Table/stories/data.tsx b/src/core/Table/stories/data.tsx new file mode 100644 index 000000000..1232bbe01 --- /dev/null +++ b/src/core/Table/stories/data.tsx @@ -0,0 +1,113 @@ +import React, { Fragment } from "react"; + +import Tooltip from "../../Tooltip/component"; +import { Supported, Unsupported } from "../TableCell"; +import Table from ".."; + +const testRow = (index) => ({ + label: `Label ${index + 1}`, + cells: [ + { label: "text", content: "Cell content", column: "Free" }, + { + label: "yes", + content: ( +
+ Supported + + + +
+ ), + column: "PAYG", + }, + { + label: "no", + content: ( +
+ Unsupported + + + +
+ ), + column: "Custom", + }, + ], +}); + +const sections = ["Features", "Support", "Technical Support"].map((label) => ({ + label, + rows: [...Array(5)].map((_, i) => testRow(i)), +})); + +export const PricingPageTable = () => { + return ( +
+

Pricing Page Table

+

Example content

+ + + + + Free + + + PAYG + + + Custom + + + + + {sections.map((section) => ( + + + {section.label} + + {section.rows.map((row) => ( + + + + {row.label} + + Example tooltip + + {row.cells.map((cell) => ( + +
+ {cell.column} +
+ {cell.content} +
+ ))} +
+ ))} +
+ ))} + + + + + Get started + + + + + Get started + + + + + Contact sales + + + +
+
+
+ ); +}; diff --git a/src/core/Tooltip/component.tsx b/src/core/Tooltip/component.tsx index 1ba88d0dd..97253f650 100644 --- a/src/core/Tooltip/component.tsx +++ b/src/core/Tooltip/component.tsx @@ -31,7 +31,7 @@ const Tooltip = ({ children }: PropsWithChildren) => {