Skip to content

Commit

Permalink
feat(counter): add Counter component
Browse files Browse the repository at this point in the history
  • Loading branch information
arturbien committed Oct 20, 2020
1 parent 087e235 commit 8c77b7a
Show file tree
Hide file tree
Showing 7 changed files with 374 additions and 7 deletions.
51 changes: 51 additions & 0 deletions src/Counter/Counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import propTypes from 'prop-types';
import styled from 'styled-components';

import { createWellBorderStyles } from '../common';
import Digit from './Digit';

const CounterWrapper = styled.div`
${createWellBorderStyles(true)}
display: inline-flex;
background: #000000;
`;

const pixelSizes = {
sm: 1,
md: 2,
lg: 3,
xl: 4
};

const Counter = React.forwardRef(function Counter(props, ref) {
const { value, minLength, size, ...otherProps } = props;
let stringValue = value.toString();
if (minLength && minLength > stringValue.length) {
stringValue =
Array(minLength - stringValue.length)
.fill('0')
.join('') + stringValue;
}
return (
<CounterWrapper ref={ref} {...otherProps}>
{stringValue.split('').map((digit, i) => (
<Digit digit={digit} pixelSize={pixelSizes[size]} key={i} />
))}
</CounterWrapper>
);
});

Counter.defaultProps = {
minLength: 3,
size: 'md',
value: 0
};

Counter.propTypes = {
minLength: propTypes.number,
size: propTypes.oneOf(['sm', 'md', 'lg']),
value: propTypes.number
};

export default Counter;
24 changes: 24 additions & 0 deletions src/Counter/Counter.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: Bar
menu: Components
---

import { Playground, Props } from 'docz';

import Counter from '../Counter/Counter'

# Counter

## Usage

<Playground>
<Counter value={4017} minLength={7} size='lg' />
</Playground>

## API

### Import

### Props

<Props of={Counter} />
50 changes: 50 additions & 0 deletions src/Counter/Counter.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { renderWithTheme } from '../../test/utils';

import Counter from './Counter';

describe('<Counter />', () => {
it('should render', () => {
const { container } = renderWithTheme(<Counter />);
const counter = container.firstChild;

expect(counter).toBeInTheDocument();
});

it('should handle custom style', () => {
const { container } = renderWithTheme(
<Counter style={{ backgroundColor: 'papayawhip' }} />
);
const counter = container.firstChild;

expect(counter).toHaveAttribute('style', 'background-color: papayawhip;');
});

it('should handle custom props', () => {
const customProps = { title: 'potatoe' };
const { container } = renderWithTheme(<Counter {...customProps} />);
const counter = container.firstChild;

expect(counter).toHaveAttribute('title', 'potatoe');
});

describe('prop: minLength', () => {
it('renders correct number of digits', () => {
const { container } = renderWithTheme(
<Counter value={32} minLength={7} />
);
const counter = container.firstChild;

expect(counter.childElementCount).toBe(7);
});

it('value length takes priority if bigger than minLength', () => {
const { container } = renderWithTheme(
<Counter value={1234} minLength={2} />
);
const counter = container.firstChild;

expect(counter.childElementCount).toBe(4);
});
});
});
45 changes: 45 additions & 0 deletions src/Counter/Counter.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useState } from 'react';
import styled from 'styled-components';

import { Counter, Panel, Button } from 'react95';

const Wrapper = styled.div`
padding: 5rem;
background: teal;
.counter-wrapper {
display: flex;
margin-top: 1rem;
}
.counter-wrapper button {
margin-left: 0.5rem;
height: 51px;
}
.wrapper {
padding: 1rem;
}
`;

export default {
title: 'Counter',
component: Counter,
decorators: [story => <Wrapper>{story()}</Wrapper>]
};

export const Default = () => {
const [count, setCount] = useState(13);
const handleClick = () => setCount(count + 1);
return (
<Panel className='wrapper'>
<Counter value={123456789} minLength={11} size='lg' />

<div className='counter-wrapper'>
<Counter value={count} minLength={3} />
<Button onClick={handleClick}>Click!</Button>
</div>
</Panel>
);
};

Default.story = {
name: 'default'
};
193 changes: 193 additions & 0 deletions src/Counter/Digit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import React from 'react';
import propTypes from 'prop-types';
import styled, { css } from 'styled-components';

import { createHatchedBackground } from '../common';

const DigitWrapper = styled.div`
position: relative;
--react95-digit-primary-color: #ff0102;
--react95-digit-secondary-color: #740201;
--react95-digit-bg-color: #000000;
${({ pixelSize }) => css`
width: ${11 * pixelSize}px;
height: ${21 * pixelSize}px;
margin: ${pixelSize}px;
span,
span:before,
span:after {
box-sizing: border-box;
display: inline-block;
position: absolute;
}
span.active,
span.active:before,
span.active:after {
background: var(--react95-digit-primary-color);
}
span:not(.active),
span:not(.active):before,
span:not(.active):after {
${createHatchedBackground({
mainColor: 'var(--react95-digit-bg-color)',
secondaryColor: 'var(--react95-digit-secondary-color)',
pixelSize
})}
}
span.horizontal,
span.horizontal:before,
span.horizontal:after {
height: ${pixelSize}px;
border-left: ${pixelSize}px solid var(--react95-digit-bg-color);
border-right: ${pixelSize}px solid var(--react95-digit-bg-color);
}
span.horizontal.active,
span.horizontal.active:before,
span.horizontal.active:after {
height: ${pixelSize}px;
border-left: ${pixelSize}px solid var(--react95-digit-primary-color);
border-right: ${pixelSize}px solid var(--react95-digit-primary-color);
}
span.horizontal {
left: ${pixelSize}px;
width: ${9 * pixelSize}px;
}
span.horizontal:before {
content: '';
width: 100%;
top: ${pixelSize}px;
left: ${0}px;
}
span.horizontal:after {
content: '';
width: calc(100% - ${pixelSize * 2}px);
top: ${2 * pixelSize}px;
left: ${pixelSize}px;
}
span.horizontal.top {
top: 0;
}
span.horizontal.bottom {
bottom: 0;
transform: rotateX(180deg);
}
span.center,
span.center:before,
span.center:after {
height: ${pixelSize}px;
border-left: ${pixelSize}px solid var(--react95-digit-bg-color);
border-right: ${pixelSize}px solid var(--react95-digit-bg-color);
}
span.center.active,
span.center.active:before,
span.center.active:after {
border-left: ${pixelSize}px solid var(--react95-digit-primary-color);
border-right: ${pixelSize}px solid var(--react95-digit-primary-color);
}
span.center {
top: 50%;
transform: translateY(-50%);
left: ${pixelSize}px;
width: ${9 * pixelSize}px;
}
span.center:before,
span.center:after {
content: '';
width: 100%;
}
span.center:before {
top: ${pixelSize}px;
}
span.center:after {
bottom: ${pixelSize}px;
}
span.vertical,
span.vertical:before,
span.vertical:after {
width: ${pixelSize}px;
border-top: ${pixelSize}px solid var(--react95-digit-bg-color);
border-bottom: ${pixelSize}px solid var(--react95-digit-bg-color);
}
span.vertical {
height: ${11 * pixelSize}px;
}
span.vertical.left {
left: 0;
}
span.vertical.right {
right: 0;
transform: rotateY(180deg);
}
span.vertical.top {
top: 0px;
}
span.vertical.bottom {
bottom: 0px;
}
span.vertical:before {
content: '';
height: 100%;
top: ${0}px;
left: ${pixelSize}px;
}
span.vertical:after {
content: '';
height: calc(100% - ${pixelSize * 2}px);
top: ${pixelSize}px;
left: ${pixelSize * 2}px;
}
`}
`;

const segments = [
'horizontal top',
'center',
'horizontal bottom',
'vertical top left',
'vertical top right',
'vertical bottom left',
'vertical bottom right'
];

const digitActiveSegments = [
[1, 0, 1, 1, 1, 1, 1], // 0
[0, 0, 0, 0, 1, 0, 1], // 1
[1, 1, 1, 0, 1, 1, 0], // 2
[1, 1, 1, 0, 1, 0, 1], // 3
[0, 1, 0, 1, 1, 0, 1], // 4
[1, 1, 1, 1, 0, 0, 1], // 5
[1, 1, 1, 1, 0, 1, 1], // 6
[1, 0, 0, 0, 1, 0, 1], // 7
[1, 1, 1, 1, 1, 1, 1], // 8
[1, 1, 1, 1, 1, 0, 1] // 9
];

const Digit = ({ digit, pixelSize, ...otherProps }) => {
const segmentClasses = digitActiveSegments[digit].map((isActive, i) =>
isActive ? `${segments[i]} active` : segments[i]
);
return (
<DigitWrapper pixelSize={pixelSize} {...otherProps}>
{segmentClasses.map((className, i) => (
<span className={className} key={i} />
))}
</DigitWrapper>
);
};

Digit.defaultProps = {
pixelSize: 2,
digit: 0
};

Digit.propTypes = {
pixelSize: propTypes.number,
digit: propTypes.oneOfType(propTypes.number, propTypes.string)
};

export default Digit;
Loading

1 comment on commit 8c77b7a

@vercel
Copy link

@vercel vercel bot commented on 8c77b7a Oct 20, 2020

Choose a reason for hiding this comment

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

Please sign in to comment.