mirror of
https://github.com/react95-io/React95.git
synced 2026-04-26 03:00:18 -04:00
Merge pull request #100 from arturbien/feature/window-enhancements-and-tests
Feature/window enhancements and tests
This commit is contained in:
@@ -30,7 +30,7 @@ const commonButtonStyles = css`
|
||||
padding-top: ${({ active, isDisabled }) => active && !isDisabled && '2px'};
|
||||
`;
|
||||
|
||||
const StyledButton = styled.button`
|
||||
export const StyledButton = styled.button`
|
||||
${({ variant, theme, active, isDisabled, primary }) =>
|
||||
variant === 'flat'
|
||||
? css`
|
||||
|
||||
@@ -1,31 +1,62 @@
|
||||
import React from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { createBorderStyles, createBoxStyles } from '../common';
|
||||
|
||||
const StyledWindow = styled.div`
|
||||
position: relative;
|
||||
padding: 2px;
|
||||
padding: 4px;
|
||||
${createBorderStyles({ windowBorders: true })}
|
||||
${createBoxStyles()}
|
||||
`;
|
||||
const ResizeHandle = styled.span`
|
||||
${({ theme }) => css`
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-image: linear-gradient(
|
||||
135deg,
|
||||
${theme.borderLightest} 16.67%,
|
||||
${theme.material} 16.67%,
|
||||
${theme.material} 33.33%,
|
||||
${theme.borderDark} 33.33%,
|
||||
${theme.borderDark} 50%,
|
||||
${theme.borderLightest} 50%,
|
||||
${theme.borderLightest} 66.67%,
|
||||
${theme.material} 66.67%,
|
||||
${theme.material} 83.33%,
|
||||
${theme.borderDark} 83.33%,
|
||||
${theme.borderDark} 100%
|
||||
);
|
||||
background-size: 8.49px 8.49px;
|
||||
clip-path: polygon(100% 0px, 0px 100%, 100% 100%);
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-color: ${theme.material};
|
||||
cursor: nwse-resize;
|
||||
`}
|
||||
`;
|
||||
|
||||
const Window = ({ shadow, className, children, ...otherProps }) => (
|
||||
<StyledWindow shadow={shadow} className={className} {...otherProps} swag>
|
||||
const Window = ({ resizable, shadow, children, ...otherProps }) => (
|
||||
<StyledWindow shadow={shadow} {...otherProps}>
|
||||
{children}
|
||||
{resizable && <ResizeHandle data-testid='resizeHandle' />}
|
||||
</StyledWindow>
|
||||
);
|
||||
|
||||
Window.defaultProps = {
|
||||
resizable: false,
|
||||
shadow: true,
|
||||
className: '',
|
||||
children: null
|
||||
};
|
||||
|
||||
Window.propTypes = {
|
||||
shadow: propTypes.bool,
|
||||
className: propTypes.string,
|
||||
resizable: propTypes.bool,
|
||||
children: propTypes.node
|
||||
};
|
||||
|
||||
|
||||
37
src/components/Window/Window.spec.js
Normal file
37
src/components/Window/Window.spec.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
|
||||
import { renderWithTheme } from '../../../test/utils';
|
||||
|
||||
import Window from './Window';
|
||||
|
||||
describe('<Window />', () => {
|
||||
it('renders Window', () => {
|
||||
const { container } = renderWithTheme(<Window />);
|
||||
const window = container.firstChild;
|
||||
|
||||
expect(window).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders children', () => {
|
||||
const textContent = 'Hi there!';
|
||||
const { getByText } = renderWithTheme(
|
||||
<Window>
|
||||
<span>{textContent}</span>
|
||||
</Window>
|
||||
);
|
||||
expect(getByText(textContent)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('prop: resizable', () => {
|
||||
it('does not render resize handle by default', () => {
|
||||
const { queryByTestId } = renderWithTheme(<Window />);
|
||||
|
||||
expect(queryByTestId('resizeHandle')).not.toBeInTheDocument();
|
||||
});
|
||||
it('renders resize handle when set to true', () => {
|
||||
const { queryByTestId } = renderWithTheme(<Window resizable />);
|
||||
|
||||
expect(queryByTestId('resizeHandle')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -28,13 +28,9 @@ storiesOf('Window', module)
|
||||
}}
|
||||
>
|
||||
<span>react95.exe</span>
|
||||
<Button
|
||||
style={{ marginRight: '-6px', marginTop: '1px' }}
|
||||
size='sm'
|
||||
square
|
||||
>
|
||||
<Button>
|
||||
<span style={{ fontWeight: 'bold', transform: 'translateY(-1px)' }}>
|
||||
x
|
||||
X
|
||||
</span>
|
||||
</Button>
|
||||
</WindowHeader>
|
||||
@@ -59,16 +55,17 @@ storiesOf('Window', module)
|
||||
</WindowContent>
|
||||
</Window>
|
||||
))
|
||||
.add('no shadow', () => (
|
||||
<Window shadow={false}>
|
||||
.add('resizable', () => (
|
||||
<Window resizable>
|
||||
<WindowHeader>react95.exe</WindowHeader>
|
||||
<WindowContent>
|
||||
<ul>
|
||||
<li>something here</li>
|
||||
<li>something here</li>
|
||||
<li>something here</li>
|
||||
<li>something here</li>
|
||||
</ul>
|
||||
Resizable Window displays resize handle in bottom right corner
|
||||
</WindowContent>
|
||||
</Window>
|
||||
))
|
||||
.add('not Active', () => (
|
||||
<Window>
|
||||
<WindowHeader isActive={false}>react95.exe</WindowHeader>
|
||||
<WindowContent>I am not active</WindowContent>
|
||||
</Window>
|
||||
));
|
||||
|
||||
@@ -9,21 +9,15 @@ const StyledWindowContent = styled.div`
|
||||
margin-right: 2px;
|
||||
`;
|
||||
|
||||
const WindowContent = ({ className, children, style, ...otherProps }) => (
|
||||
<StyledWindowContent className={className} style={style} {...otherProps}>
|
||||
{children}
|
||||
</StyledWindowContent>
|
||||
const WindowContent = ({ children, ...otherProps }) => (
|
||||
<StyledWindowContent {...otherProps}>{children}</StyledWindowContent>
|
||||
);
|
||||
|
||||
WindowContent.defaultProps = {
|
||||
className: '',
|
||||
style: {},
|
||||
children: null
|
||||
};
|
||||
|
||||
WindowContent.propTypes = {
|
||||
className: propTypes.string,
|
||||
style: propTypes.shape([propTypes.string, propTypes.number]),
|
||||
children: propTypes.node
|
||||
};
|
||||
|
||||
|
||||
24
src/components/WindowContent/WindowContent.spec.js
Normal file
24
src/components/WindowContent/WindowContent.spec.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
import { renderWithTheme } from '../../../test/utils';
|
||||
|
||||
import WindowContent from './WindowContent';
|
||||
|
||||
describe('<WindowContent />', () => {
|
||||
it('renders WindowContent', () => {
|
||||
const { container } = renderWithTheme(<WindowContent />);
|
||||
const windowContent = container.firstChild;
|
||||
|
||||
expect(windowContent).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders children', () => {
|
||||
const textContent = 'Hi there!';
|
||||
const { getByText } = renderWithTheme(
|
||||
<WindowContent>
|
||||
<span>{textContent}</span>
|
||||
</WindowContent>
|
||||
);
|
||||
expect(getByText(textContent)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -2,41 +2,49 @@ import React from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { padding } from '../common/system';
|
||||
import { StyledButton } from '../Button/Button';
|
||||
|
||||
const SlyledWindowHeader = styled.div`
|
||||
height: 33px;
|
||||
line-height: 33px;
|
||||
padding: 0 ${padding.sm};
|
||||
margin-right: 2px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 3px;
|
||||
font-weight: bold;
|
||||
color: ${({ theme }) => theme.textInvert};
|
||||
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
${({ theme }) => theme.headerMaterialDark},
|
||||
${({ theme }) => theme.headerMaterialLight}
|
||||
);
|
||||
&[data-active='false'] {
|
||||
background: ${({ theme }) => theme.headerNotActive};
|
||||
color: ${({ theme }) => theme.material};
|
||||
}
|
||||
&[data-active='true'] {
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
${({ theme }) => theme.headerMaterialDark},
|
||||
${({ theme }) => theme.headerMaterialLight}
|
||||
);
|
||||
color: ${({ theme }) => theme.textInvert};
|
||||
}
|
||||
${StyledButton} {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
height: 27px;
|
||||
width: 31px;
|
||||
}
|
||||
`;
|
||||
|
||||
const WindowHeader = ({ className, style, children, ...otherProps }) => (
|
||||
<SlyledWindowHeader className={className} style={style} {...otherProps}>
|
||||
// TODO - should we add some aria label indicating if window is currently active?
|
||||
const WindowHeader = ({ isActive, children, ...otherProps }) => (
|
||||
<SlyledWindowHeader data-active={isActive.toString()} {...otherProps}>
|
||||
{children}
|
||||
</SlyledWindowHeader>
|
||||
);
|
||||
|
||||
WindowHeader.defaultProps = {
|
||||
className: '',
|
||||
style: {},
|
||||
children: null
|
||||
children: null,
|
||||
isActive: true
|
||||
};
|
||||
|
||||
WindowHeader.propTypes = {
|
||||
className: propTypes.string,
|
||||
style: propTypes.shape([propTypes.string, propTypes.number]),
|
||||
children: propTypes.node
|
||||
children: propTypes.node,
|
||||
isActive: propTypes.bool
|
||||
};
|
||||
|
||||
export default WindowHeader;
|
||||
|
||||
40
src/components/WindowHeader/WindowHeader.spec.js
Normal file
40
src/components/WindowHeader/WindowHeader.spec.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
|
||||
import { renderWithTheme } from '../../../test/utils';
|
||||
|
||||
import WindowHeader from './WindowHeader';
|
||||
|
||||
describe('<WindowHeader />', () => {
|
||||
it('renders WindowHeader', () => {
|
||||
const { container } = renderWithTheme(<WindowHeader />);
|
||||
const windowHeader = container.firstChild;
|
||||
|
||||
expect(windowHeader).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders children', () => {
|
||||
const textContent = 'Hi there!';
|
||||
const { getByText } = renderWithTheme(
|
||||
<WindowHeader>
|
||||
<span>{textContent}</span>
|
||||
</WindowHeader>
|
||||
);
|
||||
expect(getByText(textContent)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('prop: isActive', () => {
|
||||
it('displays active header by default', () => {
|
||||
const { container } = renderWithTheme(<WindowHeader />);
|
||||
const windowHeader = container.firstChild;
|
||||
|
||||
expect(windowHeader).toHaveAttribute('data-active', 'true');
|
||||
});
|
||||
|
||||
it('renders non-active header when set to false', () => {
|
||||
const { container } = renderWithTheme(<WindowHeader isActive={false} />);
|
||||
const windowHeader = container.firstChild;
|
||||
|
||||
expect(windowHeader).toHaveAttribute('data-active', 'false');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -12,8 +12,9 @@ themes.default = {
|
||||
borderLight: '#dfe0e3',
|
||||
|
||||
headerMaterialDark: '#000080',
|
||||
headerMaterialLight: '#1034a6',
|
||||
headerMaterialLight: '#000080',
|
||||
headerText: '#ffffff',
|
||||
headerNotActive: '#7f7f7f',
|
||||
|
||||
text: '#050608',
|
||||
textInvert: '#ffffff',
|
||||
@@ -53,6 +54,7 @@ themes.water = {
|
||||
headerMaterialDark: '#72b3b4',
|
||||
headerMaterialLight: '#72b3b4',
|
||||
headerText: '#ffffff',
|
||||
headerNotActive: '#9a9e9c',
|
||||
|
||||
text: '#050608',
|
||||
textInvert: '#ffffff',
|
||||
@@ -95,6 +97,7 @@ themes.coldGray = {
|
||||
headerMaterialDark: '#3B3D64',
|
||||
headerMaterialLight: '#8d88c2',
|
||||
headerText: '#010601',
|
||||
headerNotActive: '#6063a5',
|
||||
|
||||
text: '#010601',
|
||||
textInvert: '#c7c7df',
|
||||
@@ -135,6 +138,7 @@ themes.lilacRoseDark = {
|
||||
headerMaterialDark: '#4C0030',
|
||||
headerMaterialLight: '#8d88c2',
|
||||
headerText: '#010601',
|
||||
headerNotActive: '#763a60',
|
||||
|
||||
text: '#000000',
|
||||
textInvert: '#ecbfe3',
|
||||
@@ -163,6 +167,7 @@ themes.violetDark = {
|
||||
canvas: '#c47bcc',
|
||||
material: '#652a6d',
|
||||
materialDark: '#210e23',
|
||||
|
||||
borderDarkest: '#18051a',
|
||||
borderLightest: '#c47bcc',
|
||||
borderDark: '#3c1f3e',
|
||||
@@ -171,6 +176,7 @@ themes.violetDark = {
|
||||
|
||||
headerMaterialDark: '#1034a6',
|
||||
headerMaterialLight: '#512155',
|
||||
headerNotActive: '#210e23',
|
||||
|
||||
text: '#c57ece',
|
||||
textInvert: '#c47bcc',
|
||||
|
||||
Reference in New Issue
Block a user