From 486bd9eba8d639460d22239f3e9b393f4b4b258a Mon Sep 17 00:00:00 2001 From: Kati Salin Date: Tue, 26 Sep 2023 16:05:55 +0300 Subject: [PATCH 1/5] Tooltip component --- src/components/Tooltip/Tooltip.stories.tsx | 96 +++++++++++++ src/components/Tooltip/Tooltip.tsx | 150 +++++++++++++++++++++ src/components/Tooltip/styles.ts | 13 ++ 3 files changed, 259 insertions(+) create mode 100644 src/components/Tooltip/Tooltip.stories.tsx create mode 100644 src/components/Tooltip/Tooltip.tsx create mode 100644 src/components/Tooltip/styles.ts diff --git a/src/components/Tooltip/Tooltip.stories.tsx b/src/components/Tooltip/Tooltip.stories.tsx new file mode 100644 index 0000000..c9e2126 --- /dev/null +++ b/src/components/Tooltip/Tooltip.stories.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { StoryFn, Meta } from '@storybook/react'; +import { Tooltip } from './Tooltip'; + +export default { + title: 'Components/Tooltip', + component: Tooltip, + args: { + // Shaping the stories through args composition. + arrowUp: false, + arrowDown: false, + arrowLeft: false, + arrowRight: false, + }, + parameters: { + design: [ + { + name: 'light', + type: 'figma', + url: 'https://www.figma.com/file/qUvylGh5ubOWlpqlplVORt/IZ-Design-System---%F0%9F%9A%80-Live?type=design&node-id=790-15542&mode=design&t=CX7vD9P6YQtwJF9w-4', + }, + { + name: 'dark', + type: 'figma', + url: 'https://www.figma.com/file/qUvylGh5ubOWlpqlplVORt/IZ-Design-System---%F0%9F%9A%80-Live?type=design&node-id=1538-93439&mode=design&t=CX7vD9P6YQtwJF9w-4', + }, + ], + }, +} as Meta; + +// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args +const Template: StoryFn = (args) => { + return Example tooltip text; +}; + +const LongTextTemplate: StoryFn = (args) => { + return ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus + scelerisque non lacus vitae tempus. Nullam at vehicula erat. Aliquam erat + volutpat. Pellentesque et fringilla purus, ac blandit odio. Ut volutpat, + mauris sed luctus hendrerit, dui nunc sodales erat, non volutpat nisi + lorem eu dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Vivamus scelerisque non lacus vitae tempus. Nullam at vehicula erat. + Aliquam erat volutpat. Pellentesque et fringilla purus, ac blandit odio. + Ut volutpat, mauris sed luctus hendrerit, dui nunc sodales erat, non + volutpat nisi lorem eu dolor. + + ); +}; + +/** + * Default (no arrows) + */ +export const Default = Template.bind({}); + +/** + * Arrow up + */ +export const ArrowUp = Template.bind({}); +ArrowUp.args = { arrowUp: true }; + +/** + * Arrow down + */ +export const ArrowDown = Template.bind({}); +ArrowDown.args = { arrowDown: true }; + +/** + * Arrow left + */ +export const ArrowLeft = Template.bind({}); +ArrowLeft.args = { arrowLeft: true }; + +/** + * Arrow right + */ +export const ArrowRight = Template.bind({}); +ArrowRight.args = { arrowRight: true }; + +/** + * All arrows + */ +export const AllArrows = Template.bind({}); +AllArrows.args = { + arrowUp: true, + arrowDown: true, + arrowLeft: true, + arrowRight: true, +}; + +/** + * Very long text, arrow right + */ +export const LongText = LongTextTemplate.bind({}); +LongText.args = { arrowRight: true }; diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx new file mode 100644 index 0000000..e2dac16 --- /dev/null +++ b/src/components/Tooltip/Tooltip.tsx @@ -0,0 +1,150 @@ +import React from 'react'; +import styled from 'styled-components'; +import { ComponentBaseProps, typography } from '../../shared'; +import { + BiSolidUpArrow, + BiSolidDownArrow, + BiSolidLeftArrow, + BiSolidRightArrow, +} from 'react-icons/bi'; +import { Wrapper } from '../Wrapper'; +import { arrowVisibleSize, tooltipPadding } from './styles'; + +/** + * Tooltip component properties + * Extends html attributes + * https://developer.mozilla.org/en-US/docs/Web/HTML/ + */ +export interface TooltipProps extends ComponentBaseProps { + /** + * Arrow up + */ + arrowUp?: boolean; + + /** + * Arrow down + */ + arrowDown?: boolean; + + /** + * Arrow left + */ + arrowLeft?: boolean; + + /** + * Arrow right + */ + arrowRight?: boolean; + + /** + * Children of the element, i.e. just the text that the tooltip contains + */ + children?: React.ReactNode; +} + +/** + * Styled container for arrow icons + */ +const ArrowWrapper = styled.div` + display: flex; + flex: 0 0 auto; + align-items: center; + justify-content: center; + color: ${(props) => props.theme.colors.grayScale.digitalBlack100}; + font-size: ${typography.size.paragraph2}; + padding: 0; + margin: 0; + border: 0; + overflow: hidden; + object-fit: none; + object-position: center; +`; + +/** + * Styled container for the tooltip text + */ +const TextWrapper = styled.div` + display: flex; + flex: 1 0 auto; + color: ${(props) => props.theme.colors.grayScale.digitalBlack}; + font-size: ${typography.size.paragraph2}; + line-height: ${typography.lineHeight.paragraph2}; + background-color: ${(props) => props.theme.colors.grayScale.digitalBlack100}; + padding: ${tooltipPadding}; + margin: 0; + border: 0; + cursor: default; +`; + +/** + * Up arrow component + */ +const UpArrow = () => { + return ( + + + + ); +}; + +/** + * Down arrow component + */ +const DownArrow = () => { + return ( + + + + ); +}; + +/** + * Left arrow component + */ +const LeftArrow = () => { + return ( + +
+ +
+
+ ); +}; + +/** + * Right arrow component + */ +const RightArrow = () => { + return ( + +
+ +
+
+ ); +}; + +/** + * Tooltip component + * @param props Tooltip props + * @returns Tooltip component + */ +export const Tooltip = ({ + arrowUp, + arrowDown, + arrowLeft, + arrowRight, + children, +}: TooltipProps) => { + return ( + + {arrowLeft && } +
+ {arrowUp && } + {children} + {arrowDown && } +
+ {arrowRight && } +
+ ); +}; diff --git a/src/components/Tooltip/styles.ts b/src/components/Tooltip/styles.ts new file mode 100644 index 0000000..825676a --- /dev/null +++ b/src/components/Tooltip/styles.ts @@ -0,0 +1,13 @@ +import { pxToRem } from '../../shared/styles'; + +/** + * Padding of the tooltip + */ +export const tooltipPadding = pxToRem(16); + +/** + * React-icons arrows have a small empty surrounding space we don't want + * to see on tooltips, so set up a specific width or height which crops the + * arrow sufficiently + */ +export const arrowVisibleSize = pxToRem(8); From 17f04e7c1bbbae70b421fc414c976546f1d1c6af Mon Sep 17 00:00:00 2001 From: Kati Salin Date: Fri, 29 Sep 2023 13:38:14 +0300 Subject: [PATCH 2/5] PR fixes - Replace new tooltip implementation by using existing Popup component - Add a floating example of tooltip using Floating UI in the story - Modify existing Popup component so that empty title does not take space --- src/components/Popup/Popup.tsx | 2 +- src/components/Tooltip/Tooltip.stories.tsx | 69 +++++++--- src/components/Tooltip/Tooltip.tsx | 143 +-------------------- src/components/Tooltip/styles.ts | 13 -- 4 files changed, 54 insertions(+), 173 deletions(-) delete mode 100644 src/components/Tooltip/styles.ts diff --git a/src/components/Popup/Popup.tsx b/src/components/Popup/Popup.tsx index a524403..21c0515 100644 --- a/src/components/Popup/Popup.tsx +++ b/src/components/Popup/Popup.tsx @@ -172,7 +172,7 @@ export const Popup = ({ return ( - {title} + {title && {title}} {children} ); diff --git a/src/components/Tooltip/Tooltip.stories.tsx b/src/components/Tooltip/Tooltip.stories.tsx index c9e2126..12ebef4 100644 --- a/src/components/Tooltip/Tooltip.stories.tsx +++ b/src/components/Tooltip/Tooltip.stories.tsx @@ -1,16 +1,15 @@ -import React from 'react'; +import React, { useState } from 'react'; import { StoryFn, Meta } from '@storybook/react'; import { Tooltip } from './Tooltip'; +import { Button } from '../Button'; +import { useFloating, useHover, useInteractions } from '@floating-ui/react'; export default { title: 'Components/Tooltip', component: Tooltip, args: { // Shaping the stories through args composition. - arrowUp: false, - arrowDown: false, - arrowLeft: false, - arrowRight: false, + arrow: 'none', }, parameters: { design: [ @@ -49,48 +48,76 @@ const LongTextTemplate: StoryFn = (args) => { ); }; +const FloatingTemplate: StoryFn = (args) => { + const [isOpen, setIsOpen] = useState(false); + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + }); + + const hover = useHover(context); + + const { getReferenceProps, getFloatingProps } = useInteractions([hover]); + + return ( +
+
+
+ {isOpen && ( +
+ Floating tooltip for the button +
+ )} +
+ ); +}; + /** * Default (no arrows) */ export const Default = Template.bind({}); +/** + * Floating tooltip example + */ +export const FloatingTooltip = FloatingTemplate.bind({}); + /** * Arrow up */ export const ArrowUp = Template.bind({}); -ArrowUp.args = { arrowUp: true }; +ArrowUp.args = { arrow: 'top' }; /** * Arrow down */ export const ArrowDown = Template.bind({}); -ArrowDown.args = { arrowDown: true }; +ArrowDown.args = { arrow: 'bottom' }; /** * Arrow left */ export const ArrowLeft = Template.bind({}); -ArrowLeft.args = { arrowLeft: true }; +ArrowLeft.args = { arrow: 'left' }; /** * Arrow right */ export const ArrowRight = Template.bind({}); -ArrowRight.args = { arrowRight: true }; - -/** - * All arrows - */ -export const AllArrows = Template.bind({}); -AllArrows.args = { - arrowUp: true, - arrowDown: true, - arrowLeft: true, - arrowRight: true, -}; +ArrowRight.args = { arrow: 'right' }; /** * Very long text, arrow right */ export const LongText = LongTextTemplate.bind({}); -LongText.args = { arrowRight: true }; +LongText.args = { arrow: 'right' }; diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index e2dac16..6c70683 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -1,150 +1,17 @@ import React from 'react'; -import styled from 'styled-components'; -import { ComponentBaseProps, typography } from '../../shared'; -import { - BiSolidUpArrow, - BiSolidDownArrow, - BiSolidLeftArrow, - BiSolidRightArrow, -} from 'react-icons/bi'; -import { Wrapper } from '../Wrapper'; -import { arrowVisibleSize, tooltipPadding } from './styles'; +import { Popup, PopupProps } from '../Popup'; /** * Tooltip component properties - * Extends html attributes - * https://developer.mozilla.org/en-US/docs/Web/HTML/ + * Extends PopupProps */ -export interface TooltipProps extends ComponentBaseProps { - /** - * Arrow up - */ - arrowUp?: boolean; - - /** - * Arrow down - */ - arrowDown?: boolean; - - /** - * Arrow left - */ - arrowLeft?: boolean; - - /** - * Arrow right - */ - arrowRight?: boolean; - - /** - * Children of the element, i.e. just the text that the tooltip contains - */ - children?: React.ReactNode; -} - -/** - * Styled container for arrow icons - */ -const ArrowWrapper = styled.div` - display: flex; - flex: 0 0 auto; - align-items: center; - justify-content: center; - color: ${(props) => props.theme.colors.grayScale.digitalBlack100}; - font-size: ${typography.size.paragraph2}; - padding: 0; - margin: 0; - border: 0; - overflow: hidden; - object-fit: none; - object-position: center; -`; - -/** - * Styled container for the tooltip text - */ -const TextWrapper = styled.div` - display: flex; - flex: 1 0 auto; - color: ${(props) => props.theme.colors.grayScale.digitalBlack}; - font-size: ${typography.size.paragraph2}; - line-height: ${typography.lineHeight.paragraph2}; - background-color: ${(props) => props.theme.colors.grayScale.digitalBlack100}; - padding: ${tooltipPadding}; - margin: 0; - border: 0; - cursor: default; -`; - -/** - * Up arrow component - */ -const UpArrow = () => { - return ( - - - - ); -}; - -/** - * Down arrow component - */ -const DownArrow = () => { - return ( - - - - ); -}; - -/** - * Left arrow component - */ -const LeftArrow = () => { - return ( - -
- -
-
- ); -}; - -/** - * Right arrow component - */ -const RightArrow = () => { - return ( - -
- -
-
- ); -}; +export interface TooltipProps extends PopupProps {} /** * Tooltip component * @param props Tooltip props * @returns Tooltip component */ -export const Tooltip = ({ - arrowUp, - arrowDown, - arrowLeft, - arrowRight, - children, -}: TooltipProps) => { - return ( - - {arrowLeft && } -
- {arrowUp && } - {children} - {arrowDown && } -
- {arrowRight && } -
- ); +export const Tooltip = ({ arrow, children }: TooltipProps) => { + return {children}; }; diff --git a/src/components/Tooltip/styles.ts b/src/components/Tooltip/styles.ts deleted file mode 100644 index 825676a..0000000 --- a/src/components/Tooltip/styles.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { pxToRem } from '../../shared/styles'; - -/** - * Padding of the tooltip - */ -export const tooltipPadding = pxToRem(16); - -/** - * React-icons arrows have a small empty surrounding space we don't want - * to see on tooltips, so set up a specific width or height which crops the - * arrow sufficiently - */ -export const arrowVisibleSize = pxToRem(8); From fce2e4b501ca60320e2564d6d32ad3798651bdc1 Mon Sep 17 00:00:00 2001 From: Harry-Pekka Laakso Date: Thu, 19 Oct 2023 14:10:26 +0300 Subject: [PATCH 3/5] Run prettier --- src/components/Sidebar/Sidebar.stories.tsx | 4 ++++ src/components/Sidebar/Sidebar.tsx | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/Sidebar/Sidebar.stories.tsx b/src/components/Sidebar/Sidebar.stories.tsx index 6debfe3..2d644cb 100644 --- a/src/components/Sidebar/Sidebar.stories.tsx +++ b/src/components/Sidebar/Sidebar.stories.tsx @@ -61,6 +61,7 @@ const Template: StoryFn = (args) => ( * Default variant */ export const DefaultVariant = Template.bind({}); + /** * Sidebar with overlay, content and icon button in header */ @@ -70,6 +71,7 @@ OverlayContentIcon.args = { sidebarContent: , headerContent: , }; + /** * Sidebar with overlay */ @@ -77,6 +79,7 @@ export const Overlay = Template.bind({}); Overlay.args = { overlay: true, }; + /** * Sidebar with content */ @@ -84,6 +87,7 @@ export const SidebarContent = Template.bind({}); SidebarContent.args = { sidebarContent: , }; + /** * Sidebar with icon button in header */ diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx index 1e44b50..e5e217a 100644 --- a/src/components/Sidebar/Sidebar.tsx +++ b/src/components/Sidebar/Sidebar.tsx @@ -11,10 +11,12 @@ export interface SidebarProps extends ComponentBaseProps { * Header icon */ headerContent?: React.ReactNode; + /** * Sidebar Content */ sidebarContent?: React.ReactNode; + /** * Show overlay */ @@ -32,6 +34,7 @@ const sidebarDimensions = { // Header and content container width contentWidth: 368, }; + /** * Sidebar and overlay */ @@ -40,6 +43,7 @@ const SidebarWrapper = styled.div` display: flex; flex-direction: row; `; + /** * Sidebar overlay */ @@ -48,6 +52,7 @@ const SidebarOverlay = styled.div` width: 100%; background-color: rgba(0, 0, 0, 0.82); `; + /** * Wrapper for sidebar */ @@ -55,9 +60,11 @@ const SidebarBody = styled.div` width: ${pxToRem(sidebarDimensions.bodyWidth)}; height: 100%; background-color: ${(props) => props.theme.colors.neutral}; - border-left: 1px solid ${(props) => props.theme.colors.grayScale.digitalBlack200}; + border-left: 1px solid + ${(props) => props.theme.colors.grayScale.digitalBlack200}; ${(props) => props.theme.styles.dropshadow}; `; + /** * Wrapper for sidebar header */ @@ -69,6 +76,7 @@ const SidebarHeader = styled.div` justify-content: flex-end; margin: ${pxToRem(16)}; `; + /** * Wrapper for content inside sidebar */ From 9eb82dc82d2c6de548f29721375e63ae80452010 Mon Sep 17 00:00:00 2001 From: Harry-Pekka Laakso Date: Fri, 20 Oct 2023 14:22:15 +0300 Subject: [PATCH 4/5] Fix popup styles, floating logic --- src/components/Popup/Popup.stories.tsx | 22 +-- src/components/Popup/Popup.tsx | 213 +++++++++++---------- src/components/Tooltip/Tooltip.stories.tsx | 89 +-------- src/components/Tooltip/Tooltip.tsx | 12 +- 4 files changed, 138 insertions(+), 198 deletions(-) diff --git a/src/components/Popup/Popup.stories.tsx b/src/components/Popup/Popup.stories.tsx index 9865506..7ae3310 100644 --- a/src/components/Popup/Popup.stories.tsx +++ b/src/components/Popup/Popup.stories.tsx @@ -2,8 +2,7 @@ import React from 'react'; import { Meta, StoryFn } from '@storybook/react'; import { withDesign } from 'storybook-addon-designs'; import { Popup, PopupProps } from './Popup'; -import { styled } from 'styled-components'; -import { Button } from '../Button/Button'; +import { height } from 'styled-system'; // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export export default { @@ -11,8 +10,11 @@ export default { component: Popup, // More on argTypes: https://storybook.js.org/docs/react/api/argtypes args: { - arrow: 'top', title: 'Title', + content: 'Example Popup content', + showArrow: true, + initiallyOpen: false, + openWith: 'click', }, parameters: { design: [ @@ -31,19 +33,14 @@ export default { decorators: [withDesign], } as Meta; -const RightButton = styled(Button)` - align-self: end; -`; - /* * Example Popup story */ const ExamplePopup = ({ children, ...restProps }: PopupProps) => { return ( - - {children} - - +
+ {children} +
); }; @@ -59,3 +56,6 @@ const Template: StoryFn = (args) => ( * Default variant (not specified) */ export const DefaultVariant = Template.bind({}); + +export const InitiallyOpen = Template.bind({}); +InitiallyOpen.args = { initiallyOpen: true }; diff --git a/src/components/Popup/Popup.tsx b/src/components/Popup/Popup.tsx index 21c0515..a8dc3cf 100644 --- a/src/components/Popup/Popup.tsx +++ b/src/components/Popup/Popup.tsx @@ -1,25 +1,34 @@ -import React from 'react'; -import styled, { DefaultTheme, css } from 'styled-components'; -import { variant } from 'styled-system'; +import React, { useRef, useState } from 'react'; +import styled from 'styled-components'; import { pxToRem, ComponentBaseProps, typography } from '../../shared'; -export type ArrowDirection = 'none' | 'top' | 'right' | 'bottom' | 'left'; +import { + useFloating, + useInteractions, + Placement, + offset, + FloatingArrow, + arrow, + useClick, + useHover, + flip, +} from '@floating-ui/react'; /** * Dimensions of Popup component */ const popupDimensions = { arrow: { - arrowSize: 8, - arrowOffset: 146, + arrowHeight: 8, + arrowWidth: 20, }, innerPopup: { popupWidth: 300, }, popupTitle: { - paddingTop: 25.5, - paddingRigth: 16, - paddingBottom: 17.5, + paddingTop: 24, + paddingRight: 16, + paddingBottom: 4, paddingLeft: 16, }, popupContents: { @@ -34,87 +43,46 @@ const popupDimensions = { */ export interface PopupProps extends ComponentBaseProps { /** - * Popup arrow direction + * Title */ - arrow?: ArrowDirection; + title?: string; /** - * Title + * Content */ - title?: string; + content?: React.ReactNode; /** - * Children + * The element that has the popup */ children?: React.ReactNode; + + /** + * Placement of Popup component + */ + placement?: Placement; + + /** + * Popup arrow direction + */ + showArrow?: boolean; + + /** + * Is popup open initially + */ + initiallyOpen?: boolean; + + // How to open popup + openWith?: 'hover' | 'click' | 'both'; } /** - * Helper function to calculate arrow styles depending on side - * @param props theme props - * @returns modified css + * Arrow component */ -const arrowStyles = (props: DefaultTheme) => { - return css` - ${variant({ - prop: 'arrow', - variants: { - top: { - borderLeft: `${pxToRem( - popupDimensions.arrow.arrowSize - )} solid transparent`, - borderRight: `${pxToRem( - popupDimensions.arrow.arrowSize - )} solid transparent`, - borderBottom: `${pxToRem(popupDimensions.arrow.arrowSize)} solid ${ - props.theme.colors.grayScale.digitalBlack100 - }`, - right: pxToRem(popupDimensions.arrow.arrowOffset), - top: pxToRem(-popupDimensions.arrow.arrowSize), - }, - right: { - borderTop: `${pxToRem( - popupDimensions.arrow.arrowSize - )} solid transparent`, - borderBottom: `${pxToRem( - popupDimensions.arrow.arrowSize - )} solid transparent`, - borderLeft: `${pxToRem(popupDimensions.arrow.arrowSize)} solid ${ - props.theme.colors.grayScale.digitalBlack100 - }`, - top: `calc(50% - ${pxToRem(popupDimensions.arrow.arrowSize)})`, - right: pxToRem(-popupDimensions.arrow.arrowSize), - }, - bottom: { - borderLeft: `${pxToRem( - popupDimensions.arrow.arrowSize - )} solid transparent`, - borderRight: `${pxToRem( - popupDimensions.arrow.arrowSize - )} solid transparent`, - borderTop: `${pxToRem(popupDimensions.arrow.arrowSize)} solid ${ - props.theme.colors.grayScale.digitalBlack100 - }`, - bottom: pxToRem(-popupDimensions.arrow.arrowSize), - right: pxToRem(popupDimensions.arrow.arrowOffset), - }, - left: { - borderTop: `${pxToRem( - popupDimensions.arrow.arrowSize - )} solid transparent`, - borderBottom: `${pxToRem( - popupDimensions.arrow.arrowSize - )} solid transparent`, - borderRight: `${pxToRem(popupDimensions.arrow.arrowSize)} solid ${ - props.theme.colors.grayScale.digitalBlack100 - }`, - top: `calc(50% - ${pxToRem(popupDimensions.arrow.arrowSize)})`, - left: pxToRem(-popupDimensions.arrow.arrowSize), - }, - }, - })}; - `; -}; +const StyledArrow = styled(FloatingArrow)` + fill: ${(props) => + (props.fill = props.theme.colors.grayScale.digitalBlack100)}; +`; /** * Wrapper for popup component @@ -131,10 +99,10 @@ const InternalPopup = styled.div` const PopupTitle = styled.div` font-weight: ${typography.weight.bold}; font-size: ${typography.size.paragraph}; - padding: ${pxToRem(popupDimensions.popupTitle.paddingTop)} - ${pxToRem(popupDimensions.popupTitle.paddingRigth)} - ${pxToRem(popupDimensions.popupTitle.paddingBottom)} - ${pxToRem(popupDimensions.popupTitle.paddingLeft)}; + padding-top: ${pxToRem(popupDimensions.popupTitle.paddingTop)}; + padding-right: ${pxToRem(popupDimensions.popupTitle.paddingRight)}; + padding-bottom: ${pxToRem(popupDimensions.popupTitle.paddingBottom)}; + padding-left: ${pxToRem(popupDimensions.popupTitle.paddingLeft)}; `; /** @@ -149,31 +117,78 @@ const PopupContents = styled.div` flex-direction: column; `; -/** - * Popup side arrow component - */ -const Arrow = styled.div` - width: 0; - height: 0; - position: absolute; - - ${arrowStyles}; -`; - /** * Popup component */ export const Popup = ({ - arrow = 'none', + showArrow = false, children, title, + placement = 'top', + initiallyOpen, + openWith = 'click', + content, ...restProps }: PopupProps) => { + const [isOpen, setIsOpen] = useState(initiallyOpen); + + const arrowRef = useRef(null); + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + placement: placement, + middleware: [ + offset(10), + flip(), + arrow({ + element: arrowRef, + }), + ], + }); + + const hover = useHover(context, { + enabled: openWith === 'hover', + }); + + const click = useClick(context, { + enabled: openWith === 'click', + }); + + const { getReferenceProps, getFloatingProps } = useInteractions([ + click, + hover, + ]); + return ( - - - {title && {title}} - {children} - + <> +
+ {children} +
+ {isOpen && ( +
+ {showArrow && ( + + )} + + {title && {title}} + {content} + +
+ )} + ); }; diff --git a/src/components/Tooltip/Tooltip.stories.tsx b/src/components/Tooltip/Tooltip.stories.tsx index 12ebef4..4a8ab50 100644 --- a/src/components/Tooltip/Tooltip.stories.tsx +++ b/src/components/Tooltip/Tooltip.stories.tsx @@ -2,14 +2,14 @@ import React, { useState } from 'react'; import { StoryFn, Meta } from '@storybook/react'; import { Tooltip } from './Tooltip'; import { Button } from '../Button'; -import { useFloating, useHover, useInteractions } from '@floating-ui/react'; export default { title: 'Components/Tooltip', component: Tooltip, args: { // Shaping the stories through args composition. - arrow: 'none', + showArrow: true, + content: 'Example Tooltip content', }, parameters: { design: [ @@ -29,55 +29,11 @@ export default { // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args const Template: StoryFn = (args) => { - return Example tooltip text; -}; - -const LongTextTemplate: StoryFn = (args) => { - return ( - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus - scelerisque non lacus vitae tempus. Nullam at vehicula erat. Aliquam erat - volutpat. Pellentesque et fringilla purus, ac blandit odio. Ut volutpat, - mauris sed luctus hendrerit, dui nunc sodales erat, non volutpat nisi - lorem eu dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. - Vivamus scelerisque non lacus vitae tempus. Nullam at vehicula erat. - Aliquam erat volutpat. Pellentesque et fringilla purus, ac blandit odio. - Ut volutpat, mauris sed luctus hendrerit, dui nunc sodales erat, non - volutpat nisi lorem eu dolor. - - ); -}; - -const FloatingTemplate: StoryFn = (args) => { - const [isOpen, setIsOpen] = useState(false); - - const { refs, floatingStyles, context } = useFloating({ - open: isOpen, - onOpenChange: setIsOpen, - }); - - const hover = useHover(context); - - const { getReferenceProps, getFloatingProps } = useInteractions([hover]); - return ( -
-
+
+
- {isOpen && ( -
- Floating tooltip for the button -
- )} +
); }; @@ -86,38 +42,3 @@ const FloatingTemplate: StoryFn = (args) => { * Default (no arrows) */ export const Default = Template.bind({}); - -/** - * Floating tooltip example - */ -export const FloatingTooltip = FloatingTemplate.bind({}); - -/** - * Arrow up - */ -export const ArrowUp = Template.bind({}); -ArrowUp.args = { arrow: 'top' }; - -/** - * Arrow down - */ -export const ArrowDown = Template.bind({}); -ArrowDown.args = { arrow: 'bottom' }; - -/** - * Arrow left - */ -export const ArrowLeft = Template.bind({}); -ArrowLeft.args = { arrow: 'left' }; - -/** - * Arrow right - */ -export const ArrowRight = Template.bind({}); -ArrowRight.args = { arrow: 'right' }; - -/** - * Very long text, arrow right - */ -export const LongText = LongTextTemplate.bind({}); -LongText.args = { arrow: 'right' }; diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index 6c70683..29625ad 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -5,13 +5,17 @@ import { Popup, PopupProps } from '../Popup'; * Tooltip component properties * Extends PopupProps */ -export interface TooltipProps extends PopupProps {} +export type TooltipProps = Omit; /** * Tooltip component - * @param props Tooltip props + * @param restProps Tooltip props * @returns Tooltip component */ -export const Tooltip = ({ arrow, children }: TooltipProps) => { - return {children}; +export const Tooltip = ({ children, ...restProps }: TooltipProps) => { + return ( + + {children} + + ); }; From 0841d1025bad538e794dcc8fd8912b00cce49525 Mon Sep 17 00:00:00 2001 From: Harry-Pekka Laakso Date: Fri, 20 Oct 2023 14:26:27 +0300 Subject: [PATCH 5/5] Add examples --- src/components/Popup/Popup.stories.tsx | 16 ++++++++++++++-- src/components/Popup/Popup.tsx | 2 +- src/components/Tooltip/Tooltip.stories.tsx | 9 +++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/components/Popup/Popup.stories.tsx b/src/components/Popup/Popup.stories.tsx index 7ae3310..8b8d437 100644 --- a/src/components/Popup/Popup.stories.tsx +++ b/src/components/Popup/Popup.stories.tsx @@ -3,6 +3,7 @@ import { Meta, StoryFn } from '@storybook/react'; import { withDesign } from 'storybook-addon-designs'; import { Popup, PopupProps } from './Popup'; import { height } from 'styled-system'; +import { Button } from '../Button'; // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export export default { @@ -47,8 +48,7 @@ const ExamplePopup = ({ children, ...restProps }: PopupProps) => { // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args const Template: StoryFn = (args) => ( - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque et - odio sed est pellentesque gravida sit amet at orci. +