Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/info message component #644

Merged
merged 5 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules/
dist/
coverage/

# Logs
logs
Expand Down
54 changes: 54 additions & 0 deletions src/lib/components/infomessage/InfoMessage.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import styled from 'styled-components';
import { Link, Text } from '../text/Text.component';
import { Icon } from '../icon/Icon.component';
import { defaultTheme } from '../../style/theme';
import { useComputeBackgroundColor } from './InfoMessageUtils';

type Props = {
title: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would make it possible to provide a ReactNode or a string here. It would allow use cases where we want to add icons or actions button towards the end of the title line.

Suggested change
title: string;
title: string | ReactNode;

content: React.ReactNode;
link?: string;
};

const InfoMessageContainer = styled.div`
background-color: ${defaultTheme.darkRebrand.backgroundLevel2};
border-radius: 3px;
padding: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
color: white;
`;
const TitleContainer = styled.div`
align-items: center;
display: flex;
gap: 0.5rem;
`;

function InfoMessage({ title, content, link }: Props) {
const { containerRef, backgroundColor } = useComputeBackgroundColor();

return (
<InfoMessageContainer
ref={containerRef}
style={{ backgroundColor: backgroundColor }}
>
<TitleContainer>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component can likely be replaced by a Stack

<Icon
name="Info-circle"
color={defaultTheme.darkRebrand.infoPrimary}
size="lg"
/>
<Text isEmphazed>{title}</Text>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would check the typeOf title if it is a string then I would render it within the Text isEmphazed component, If it is a react node (else) I would render it directly

</TitleContainer>
<Text color="textSecondary">{content}</Text>
{link && (
<Link href={link} target="_blank" style={{ alignSelf: 'flex-end' }}>
More infos <Icon name="External-link"></Icon>
</Link>
)}
</InfoMessageContainer>
);
}

export default InfoMessage;
96 changes: 96 additions & 0 deletions src/lib/components/infomessage/InfoMessageUtilis.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { defaultTheme } from '../../style/theme';
import React from 'react';
import { useComputeBackgroundColor } from './InfoMessageUtils';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { CoreUiThemeProvider } from '../coreuithemeprovider/CoreUiThemeProvider';

describe('useComputeBackgroundColor', () => {
it('should return backgroundlevel2 by default', () => {
//S
const SUT = jest.fn();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be declared once and be more explicit

const Component = () => {
const { containerRef, backgroundColor } = useComputeBackgroundColor();
SUT(backgroundColor);
return <div ref={containerRef}>{backgroundColor}</div>;
};
render(
<CoreUiThemeProvider theme={defaultTheme.darkRebrand}>
<Component />
</CoreUiThemeProvider>,
);
//V
expect(SUT).toHaveBeenCalledWith(defaultTheme.darkRebrand.backgroundLevel2);
});

it('should return backgroundlevel3 if parent element backgroundColor is level 2', () => {
//S
const SUT = jest.fn();
const Component = () => {
const { containerRef, backgroundColor } = useComputeBackgroundColor();
SUT(backgroundColor);
return (
<div
style={{ backgroundColor: defaultTheme.darkRebrand.backgroundLevel2 }}
>
<div ref={containerRef}>{backgroundColor}</div>
</div>
);
};
render(
<CoreUiThemeProvider theme={defaultTheme.darkRebrand}>
<Component />
</CoreUiThemeProvider>,
);
//V
expect(SUT).toHaveBeenCalledWith(defaultTheme.darkRebrand.backgroundLevel3);
});
it('should return backgroundlevel3 if parent of parent element backgroundColor is level 2', () => {
//S
const SUT = jest.fn();
const Component = () => {
const { containerRef, backgroundColor } = useComputeBackgroundColor();
SUT(backgroundColor);
return (
<div
style={{ backgroundColor: defaultTheme.darkRebrand.backgroundLevel2 }}
>
<div>
<div ref={containerRef}>{backgroundColor}</div>
</div>
</div>
);
};
render(
<CoreUiThemeProvider theme={defaultTheme.darkRebrand}>
<Component />
</CoreUiThemeProvider>,
);
//V
expect(SUT).toHaveBeenCalledWith(defaultTheme.darkRebrand.backgroundLevel3);
});
it('should return backgroundlevel2 if parent of parent element backgroundColor is level 3', () => {
//S
const SUT = jest.fn();
const Component = () => {
const { containerRef, backgroundColor } = useComputeBackgroundColor();
SUT(backgroundColor);
return (
<div
style={{ backgroundColor: defaultTheme.darkRebrand.backgroundLevel3 }}
>
<div>
<div ref={containerRef}>{backgroundColor}</div>
</div>
</div>
);
};
render(
<CoreUiThemeProvider theme={defaultTheme.darkRebrand}>
<Component />
</CoreUiThemeProvider>,
);
//V
expect(SUT).toHaveBeenCalledWith(defaultTheme.darkRebrand.backgroundLevel2);
});
});
39 changes: 39 additions & 0 deletions src/lib/components/infomessage/InfoMessageUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { DefaultTheme, useTheme } from "styled-components";
import { hex2RGB } from "../../utils";
import { useEffect, useRef, useState } from "react";

export const useComputeBackgroundColor = () => {
const theme = useTheme();
const containerRef = useRef<HTMLDivElement | null>(null);
const [backgroundColor, setBackgroundColor] = useState('');

useEffect(() => {
containerRef.current &&
setBackgroundColor(getBackgroundColor(containerRef.current, theme));
}, [containerRef,theme]);

return {
containerRef,
backgroundColor,
};
};

export const getBackgroundColor = (element: HTMLElement, theme: DefaultTheme) => {
if (element.parentElement) {
const parentElementBackgroundColor = window.getComputedStyle(
element.parentElement,
)['background-color'];
if (/rgba\([0-9]+, [0-9]+, [0-9]+, 0\)/.test(parentElementBackgroundColor) || !window.getComputedStyle(element.parentElement,)['background-color']) {
return getBackgroundColor(element.parentElement, theme);
} else {
const rgbArray = hex2RGB(theme.backgroundLevel2);
if (
`rgb(${rgbArray[0]}, ${rgbArray[1]}, ${rgbArray[2]})` ===
parentElementBackgroundColor
) {
return theme.backgroundLevel3;
}
}
}
return theme.backgroundLevel2;
};
107 changes: 107 additions & 0 deletions stories/infomessage.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React from 'react';
import InfoMessage from '../src/lib/components/infomessage/InfoMessage.component';
import { Wrapper } from './common';
import { defaultTheme } from '../src/lib/style/theme';
import { Meta, StoryObj } from '@storybook/react';

type Story = StoryObj<typeof InfoMessage>;

const meta: Meta<typeof InfoMessage> = {
title: 'Components/InfoMessage',
component: InfoMessage,
decorators: [(story) => <Wrapper>{story()}</Wrapper>],
};

export default meta;

export const Playground: StoryObj<
React.ComponentProps<typeof InfoMessage> & { backgroundColor?: string }
> = {
render: ({ backgroundColor, ...args }) => (
<div
style={{
backgroundColor,
padding: '1rem',
}}
>
<div
style={{
padding: '1rem',
}}
>
<InfoMessage
title="What to do with this key?"
content="This key is needed by the Veeam repository to access ARTESCA for data backup."
link="test"
/>
</div>
</div>
),
argTypes: {
backgroundColor: {
description: 'Background color of the parent element',
options: ['Level1', 'Level2', 'Level3', 'Level4'],
mapping: {
Level1: defaultTheme.darkRebrand.backgroundLevel1,
Level2: defaultTheme.darkRebrand.backgroundLevel2,
Level3: defaultTheme.darkRebrand.backgroundLevel3,
Level4: defaultTheme.darkRebrand.backgroundLevel4,
},
control: {
type: 'radio',
},
},
},
};

export const Default: Story = {
args: {
title: 'Title for the provided info',
content: 'Some text that will help the user to understand what to do',
},
};

export const WithLink: Story = {
args: {
...Default.args,
link: 'toDocs',
},
};

export const WithDifferentBackground: Story = {
render: (args) => {
return (
<div
style={{
padding: '1rem',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-around',
height: '30rem',
}}
>
<div
style={{
height: '10rem',
backgroundColor: defaultTheme.darkRebrand.backgroundLevel3,
padding: '1rem',
}}
>
<InfoMessage {...args} />
</div>
<div
style={{
height: '10rem',
backgroundColor: defaultTheme.darkRebrand.backgroundLevel2,
padding: '1rem',
}}
>
<InfoMessage {...args} />
</div>
</div>
);
},
args: {
...Default.args,
},
};
Loading