diff --git a/src/components/Table/__snapshots__/table.test.tsx.snap b/src/components/Table/__snapshots__/table.test.tsx.snap index 6965e46b..85632cdb 100644 --- a/src/components/Table/__snapshots__/table.test.tsx.snap +++ b/src/components/Table/__snapshots__/table.test.tsx.snap @@ -11,12 +11,83 @@ exports[`Render Table component Matches the snapshot 1`] = `
Table caption
+
+
+ + + + + +
+
+ + + + + +
+
diff --git a/src/components/Table/_table.scss b/src/components/Table/_table.scss index ea80fa45..b38dca02 100644 --- a/src/components/Table/_table.scss +++ b/src/components/Table/_table.scss @@ -7,7 +7,7 @@ .ssb-table { @include roboto; line-height: 1.75rem; - width: 100%; + min-width: $desktop-screen; // We need a fixed width for overflow-x to work border-collapse: collapse; border: 1px solid variables.$ssb-dark-5; @@ -32,25 +32,18 @@ th, td { text-align: left; - } + padding: 0.5rem 1rem; - thead, - tbody { - th, - td { - padding: 0.5rem 1rem; - - &.level1 { - padding-left: 1.75rem; - } + &.level1 { + padding-left: 1.75rem; + } - &.level2 { - padding-left: 2.5rem; - } + &.level2 { + padding-left: 2.5rem; + } - &.level3 { - padding-left: 3.25rem; - } + &.level3 { + padding-left: 3.25rem; } th { @@ -60,59 +53,114 @@ caption { text-align: left; + position: relative; + padding: 1.75rem 1rem; + + @media (width <= $desktop-screen) { + padding-top: 55px; + + .scroll-icon-wrapper { + margin-top: -100px; + } + } + + .icon-container { + padding: 0 1rem; + } .caption-wrapper { display: flex; - align-items: center; justify-content: space-between; - padding: 1.75rem 1rem; + position: relative; .caption-text-wrapper { font-weight: bold; + text-align: left; + width: 998px; + } + + .scroll-icon-wrapper { + display: flex; + align-items: center; + position: sticky; + right: 40px; + left: 40px; + visibility: hidden; + + &.visible { + visibility: visible; + } + } + + .scroll-icon { + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + width: 44px; + height: 44px; + overflow: hidden; + background: transparent; + + &:focus { + svg { + fill: variables.$ssb-green-4; + transition: fill 0.18s; + } + + svg line, + svg polyline { + stroke: variables.$ssb-white; + } + + svg circle { + stroke: variables.$ssb-green-4; + transition: stroke 0.16s; + } + } + + &:hover { + svg { + fill: variables.$ssb-green-4; + transition: fill 0.18s; + } + + svg line, + svg polyline { + stroke: variables.$ssb-white; + } + + svg circle { + stroke: variables.$ssb-green-4; + transition: stroke 0.16s; + } + } + + &:active { + svg { + fill: variables.$ssb-dark-5; + transition: fill 0.18s; + } + + svg line, + svg polyline { + stroke: variables.$ssb-white; + } + + svg circle { + stroke: variables.$ssb-dark-5; + transition: stroke 0.16s; + } + } + } + + .scroll-icon svg { + width: 32px; + height: 32px; + color: variables.$ssb-green-4; + stroke-width: 1px; } - // TODO: Can be reused (partly?) for later implementation of scroll functionality - // .scoll-icons-wrapper { - // display: grid; - // grid-auto-flow: column; - // grid-column-gap: 1.25em; - // padding: 0 2.5em 0 1.5em; // padding-left with caption-text-wrapper padding-left is a total of 2.5em - - // .scroll-icon-left, .scroll-icon-right { - // height: 32px; // icon height - - // // remove default styling for button - // background: none; - // color: inherit; - // border: none; - // padding: 0; - // font: inherit; - // cursor: pointer; - // outline: inherit; - - // &:focus, &:hover { - // @include focus-ring; - // } - - // &.disabled { - // color: $ssb-dark-2; - // cursor: default; - // } - - // svg { - // color: $ssb-green-4; - // } - // } - // } - - // @media #{$mobile} { - // flex-direction: column-reverse; - // align-items: start; - - // .caption-text-wrapper, .scoll-icons-wrapper { - // padding-top: 1rem; - // } - // } } } @@ -190,3 +238,21 @@ } } } + +.scroll-icon-active, +.scroll-icon:active { + svg { + fill: var(--ssb-dark-5); + transition: fill 0.18s; + } + + svg line, + svg polyline { + stroke: var(--ssb-white); + } + + svg circle { + stroke: var(--ssb-dark-5); + transition: stroke 0.16s; + } +} \ No newline at end of file diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index dac1fb88..45c606f6 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -1,4 +1,5 @@ -import React, { forwardRef, ReactNode } from 'react' +import React, { forwardRef, ReactNode, useEffect, useState, useRef } from 'react' +import { ArrowLeftCircle, ArrowRightCircle } from 'react-feather' export interface TableElementProps { className?: string @@ -24,23 +25,89 @@ export interface TableCellProps { } const Table = forwardRef(({ className, caption, dataNoteRefs, children }, ref) => { - if (children) { - return ( -
- - {caption && ( - - )} - {children} -
-
-
{caption}
-
-
-
- ) + const tableWrapperRef = useRef(null) + const iconWrapperRef = useRef(null) + const [isOverflowing, setIsOverflowing] = useState(false) + const [isActive, setIsActive] = useState<{ left: boolean; right: boolean }>({ left: false, right: false }) + + type Direction = 'left' | 'right' + + const handleScroll = (direction: Direction) => { + if (tableWrapperRef.current) { + const scrollAmount = direction === 'left' ? -380 : 380 + tableWrapperRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' }) + } } - return null + + const handleMouseClick = (direction: Direction) => { + handleScroll(direction) + if (document.activeElement instanceof HTMLElement) { + document.activeElement.blur() // Force blur to remove hover styles + } + } + + const handleKeyPress = (event: React.KeyboardEvent, direction: Direction) => { + if (event.key === 'Enter') { + setIsActive((prev) => ({ ...prev, [direction]: true })) + handleScroll(direction) + setTimeout(() => { + setIsActive((prev) => ({ ...prev, [direction]: false })) + }, 150) // Reset active state after a short delay + } + } + + useEffect(() => { + const checkOverflow = () => { + if (tableWrapperRef.current) { + const hasOverflow = tableWrapperRef.current.scrollWidth > tableWrapperRef.current.clientWidth + setIsOverflowing(hasOverflow) + } + } + + checkOverflow() + window.addEventListener('resize', checkOverflow) + + return () => { + window.removeEventListener('resize', checkOverflow) + } + }, []) + + return ( +
+ + {caption && ( + + )} + {children} +
+
+
{caption}
+
+
handleMouseClick('left')} + onKeyDown={(event) => handleKeyPress(event, 'left')} + > + +
+
handleMouseClick('right')} + onKeyDown={(event) => handleKeyPress(event, 'right')} + > + +
+
+
+
+
+ ) }) export default Table diff --git a/src/components/Table/table.story.jsx b/src/components/Table/table.story.jsx index 49acfb9b..c3961d28 100644 --- a/src/components/Table/table.story.jsx +++ b/src/components/Table/table.story.jsx @@ -38,7 +38,7 @@ export const Default = () => ( ) export const Statistics = () => ( -
+