diff --git a/src/components/Notification/Notification.stories.tsx b/src/components/Notification/Notification.stories.tsx index 865bf3b23..e3c0566a9 100644 --- a/src/components/Notification/Notification.stories.tsx +++ b/src/components/Notification/Notification.stories.tsx @@ -36,3 +36,46 @@ CustomIcon.args = { title: 'New Event Sync', isVisible: true, }; + +export const PositionRelative = Template.bind({}); +PositionRelative.args = { + id: 'test-Notification', + message: 'Somebody messaged you', + isVisible: true, + title: 'New Notification', + isPositionRelative: true, +}; + +export const PositionRelativeCustomBreakPoints = Template.bind({}); +PositionRelativeCustomBreakPoints.args = { + id: 'test-Notification', + message: 'Somebody messaged you', + isVisible: true, + title: 'New Notification', + isPositionRelative: true, + positionRelativeConfig: { + top: '30px', + left: '30px', + width: '100%', + }, +}; + +export const RelativePositioningTopLeft = Template.bind({}); +RelativePositioningTopLeft.args = { + id: 'test-Notification', + message: 'Somebody messaged you', + isVisible: true, + title: 'New Notification', + isPositionRelative: true, + position: 'topL', +}; + +export const RelativePositioningBottomRight = Template.bind({}); +RelativePositioningBottomRight.args = { + id: 'test-Notification', + message: 'Somebody messaged you', + isVisible: true, + title: 'New Notification', + isPositionRelative: true, + position: 'bottomR', +}; diff --git a/src/components/Notification/Notification.styles.tsx b/src/components/Notification/Notification.styles.tsx index a79d70608..8a2f25030 100644 --- a/src/components/Notification/Notification.styles.tsx +++ b/src/components/Notification/Notification.styles.tsx @@ -2,6 +2,8 @@ import { css } from 'styled-components'; import color from '../../styles/colors'; import fonts from '../../styles/fonts'; import resetCSS from '../../styles/reset'; +import styled, { keyframes } from 'styled-components'; +import { IPositionRelativeConfig } from './types'; const initialStyles = css` ${resetCSS} @@ -39,10 +41,80 @@ const flex = css` justify-content: space-between; `; -export const notificationStyles = { - box, - flex, - initialStyles, - message, - title, -}; +export const moveOpen = keyframes` + from { + transform: translate(0,-100px); + } + 10% {transform: translate(0,20px);} + 12% {transform: translate(0,22px);} + 16% {transform: translate(0,20px);} + to {transform: translate(0,20px);} +`; + +interface INotificationStyled { + isVisible: boolean; + isPositionRelative: boolean; + positionRelativeConfig: IPositionRelativeConfig; +} + +export const NotificationStyled = styled.div` + ${initialStyles} + ${(props) => + props.isPositionRelative && + `position: absolute; top:${ + props.positionRelativeConfig.top + ? props.positionRelativeConfig.top + : '' + }; left:${ + props.positionRelativeConfig.left + ? props.positionRelativeConfig.left + : '' + }; height: ${ + props.positionRelativeConfig.height + ? props.positionRelativeConfig.height + : 'fit-content' + }; + bottom:${ + props.positionRelativeConfig.bottom + ? props.positionRelativeConfig.bottom + : '' + }; + right: ${ + props.positionRelativeConfig.right + ? props.positionRelativeConfig.right + : '' + }; + width: ${ + props.positionRelativeConfig.width + ? props.positionRelativeConfig.width + : 'fit-content' + }; + + `} + animation:${moveOpen} 4s; + animation-iteration-count: 1; + animation-fill-mode: forwards; + ${(props) => + !props.isVisible && + ` + visibility: hidden; + opacity: 0; + transition: visibility 0s 0.5s, opacity 0.5s ease-out; + `} +`; + +export const BoxStyled = styled.div` + ${box} +`; + +export const SpanStyled = styled.span` + ${message} +`; + +export const ParagraphStyled = styled.p` + ${title} +`; + +export const FlexStyled = styled.div` + ${flex} +`; diff --git a/src/components/Notification/Notification.test.tsx b/src/components/Notification/Notification.test.tsx index e10d19228..213a9b7b6 100644 --- a/src/components/Notification/Notification.test.tsx +++ b/src/components/Notification/Notification.test.tsx @@ -3,8 +3,16 @@ import * as stories from './Notification.stories'; import React from 'react'; import ReactDOM from 'react-dom'; import { composeStories } from '@storybook/testing-react'; - -const { Regular, Standard, CustomIcon, Inactive } = composeStories(stories); +const { + Regular, + Standard, + CustomIcon, + Inactive, + PositionRelative, + PositionRelativeCustomBreakPoints, + RelativePositioningTopLeft, + RelativePositioningBottomRight, +} = composeStories(stories); describe('Notification - Standard - Active - Regular Text - Regular Icon', () => { let container: HTMLDivElement; @@ -165,8 +173,6 @@ describe('Notification - Active - Custom Text - Custom Icon', () => { describe('Notification - Inactive', () => { let container: HTMLDivElement; - const testId = 'test-notification-id'; - beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); @@ -179,7 +185,132 @@ describe('Notification - Inactive', () => { }); it('should not render the component', () => { + const element = container.querySelector( + `[data-testid="test-notification-id"]`, + ); + const styles = element && getComputedStyle(element); + expect(styles?.opacity).toEqual('0'); + }); +}); + +describe('Notification - Standard - Active - Regular Text - Regular Icon - Relative', () => { + let container: HTMLDivElement; + const message = PositionRelative.args?.message; + const messageId = 'test-notification-message'; + const testId = 'test-notification-id'; + const closeId = 'test-notification-x'; + const title = 'New Notification'; + const titleId = 'test-notification-title'; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + ReactDOM.render(, container); + }); + + afterEach(() => { + document.body.removeChild(container); + container.remove(); + }); + + it('renders the component', () => { const element = container.querySelector(`[data-testid="${testId}"]`); - expect(element).toBeNull(); + expect(element).not.toBeNull(); + }); + + it('renders title correctly', () => { + const element = container.querySelector(`[data-testid="${titleId}"]`); + expect(element?.textContent).toBe(title); + }); + + it('renders message correctly', () => { + const element = container.querySelector(`[data-testid="${messageId}"]`); + expect(element?.textContent).toBe(message); + }); + + it('should render left icon', () => { + const iconSVG = container.querySelector( + `[data-testid="${testId}"] > svg`, + ); + expect(iconSVG).not.toBeNull(); + }); + + it('should render close icon', () => { + const closeSVGtitle = container.querySelector( + `[data-testid="${closeId}"] > svg > title`, + ); + expect(closeSVGtitle?.innerHTML).toBe('x icon'); + }); + + it('should render correct', () => { + const iconSVG = container.querySelector( + `[data-testid="${closeId}"] > svg`, + ); + expect(iconSVG).not.toBeNull(); + }); +}); + +describe('Notification - Standard - Active - Regular Text - Regular Icon - Relative - Config', () => { + let container: HTMLDivElement; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + ReactDOM.render(, container); + }); + + afterEach(() => { + document.body.removeChild(container); + container.remove(); + }); + + it('should render custom style comp', () => { + const element = container.querySelector( + `[data-testid="test-notification-id"]`, + ); + expect(element).toBeDefined(); + }); +}); + +describe('Notification - Standard - Active - Regular Text - Regular Icon - Relative - Top-Left', () => { + let container: HTMLDivElement; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + ReactDOM.render(, container); + }); + + afterEach(() => { + document.body.removeChild(container); + container.remove(); + }); + + it('should render custom style comp', () => { + const element = container.querySelector( + `[data-testid="test-notification-id"]`, + ); + expect(element).toBeDefined(); + }); +}); +describe('Notification - Standard - Active - Regular Text - Regular Icon - Relative - Bottom - Right', () => { + let container: HTMLDivElement; + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + ReactDOM.render(, container); + }); + + afterEach(() => { + document.body.removeChild(container); + container.remove(); + }); + + it('should render custom style comp', () => { + const element = container.querySelector( + `[data-testid="test-notification-id"]`, + ); + expect(element).toBeDefined(); }); }); diff --git a/src/components/Notification/Notification.tsx b/src/components/Notification/Notification.tsx index 489bc840a..94afff5da 100644 --- a/src/components/Notification/Notification.tsx +++ b/src/components/Notification/Notification.tsx @@ -1,50 +1,78 @@ import { NotificationProps } from './types'; import React, { useEffect, useState } from 'react'; -import styled from 'styled-components'; import { Icon } from '../Icon'; import { iconTypes } from '../Icon/collection'; -import { notificationStyles } from './Notification.styles'; +import { + BoxStyled, + FlexStyled, + NotificationStyled, + ParagraphStyled, + SpanStyled, +} from './Notification.styles'; import color from '../../styles/colors'; -const NotificationStyled = styled.div` - ${notificationStyles.initialStyles} -`; - -const BoxStyled = styled.div` - ${notificationStyles.box} -`; - -const SpanStyled = styled.span` - ${notificationStyles.message} -`; - -const ParagraphStyled = styled.p` - ${notificationStyles.title} -`; - -const FlexStyled = styled.div` - ${notificationStyles.flex} -`; - const Notification: React.FC = ({ id = String(Date.now()), icon, message, title = 'New Message', isVisible = false, + isPositionRelative = false, + positionRelativeConfig = {}, + position, }: NotificationProps) => { const [visible, setVisible] = useState(isVisible); - + const [positionRelativeConfigState, setPositionRelativeConfigState] = + useState({}); useEffect(() => { setVisible(isVisible); }, [isVisible]); + useEffect(() => { + if (isPositionRelative) { + switch (position) { + case 'topL': + setPositionRelativeConfigState({ + ...positionRelativeConfig, + top: '0px', + left: '0px', + }); + break; + case 'topR': + console.log('in'); + setPositionRelativeConfigState({ + ...positionRelativeConfig, + top: '0px', + right: '0px', + }); + break; + case 'bottomL': + setPositionRelativeConfigState({ + ...positionRelativeConfig, + bottom: '0px', + left: '0px', + }); + break; + case 'bottomR': + setPositionRelativeConfigState({ + ...positionRelativeConfig, + bottom: '0px', + right: '0px', + }); + break; + } + } + }, [position]); + return ( <> - {visible && ( + {positionRelativeConfigState && (