Skip to content

Commit

Permalink
feat(colorinput): convert to TypeScript and export types
Browse files Browse the repository at this point in the history
  • Loading branch information
WesSouza authored and arturbien committed Jul 26, 2022
1 parent 2616fe5 commit d5887d4
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 82 deletions.
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
import React from 'react';
import { fireEvent } from '@testing-library/react';
import { renderWithTheme } from '../../test/utils';
import ColorInput from './ColorInput';
import { ColorInput } from './ColorInput';

function rgb2hex(str) {
function rgb2hex(str: string) {
const rgb = str.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
function hex(x) {
function hex(x: string) {
return `0${parseInt(x, 10).toString(16)}`.slice(-2);
}
return `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}`;
return rgb ? `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}` : '';
}

describe('<ColorInput />', () => {
it('should call handlers', () => {
const color = '#f0f0dd';
const onChange = jest.fn();
const { container } = renderWithTheme(<ColorInput onChange={onChange} />);
const input = container.querySelector(`[type="color"]`);
const input = container.querySelector(`[type="color"]`) as HTMLInputElement;
fireEvent.change(input, { target: { value: color } });
expect(onChange).toBeCalledTimes(1);
});

it('should properly pass value to input element', () => {
const color = '#f0f0dd';
const { container } = renderWithTheme(<ColorInput value={color} />);
const input = container.querySelector(`[type="color"]`);
const input = container.querySelector(`[type="color"]`) as HTMLInputElement;

expect(input.value).toBe(color);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react';

import { ComponentMeta } from '@storybook/react';
import styled from 'styled-components';

import { ColorInput, Cutout } from '..';
import { ColorInput, Cutout } from 'react95';

const Wrapper = styled.div`
background: ${({ theme }) => theme.material};
Expand Down Expand Up @@ -31,7 +30,7 @@ export default {
title: 'ColorInput',
component: ColorInput,
decorators: [story => <Wrapper>{story()}</Wrapper>]
};
} as ComponentMeta<typeof ColorInput>;

export function Default() {
return (
Expand Down
142 changes: 72 additions & 70 deletions src/ColorInput/ColorInput.js → src/ColorInput/ColorInput.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import React from 'react';
import propTypes from 'prop-types';

import React, { forwardRef } from 'react';
import styled, { css } from 'styled-components';
import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';
import { focusOutline } from '../common';
import { StyledButton } from '../Button/Button';
import { focusOutline } from '../common';
import useControlledOrUncontrolled from '../common/hooks/useControlledOrUncontrolled';
import { noOp } from '../common/utils';
import { Divider } from '../Divider/Divider';
import { CommonStyledProps } from '../types';

type ColorInputProps = {
value?: string;
defaultValue?: string;
onChange?: React.ChangeEventHandler<HTMLInputElement>;
disabled?: boolean;
variant?: 'default' | 'flat';
} & React.InputHTMLAttributes<HTMLInputElement> &
CommonStyledProps;

const Trigger = styled(StyledButton)`
padding-left: 8px;
Expand Down Expand Up @@ -33,7 +42,10 @@ export const StyledColorInput = styled.input`
`;

// TODO replace with SVG icon
const ColorPreview = styled.div`
const ColorPreview = styled.div<{
color: string;
$disabled: boolean;
}>`
box-sizing: border-box;
height: 19px;
display: inline-block;
Expand All @@ -42,8 +54,8 @@ const ColorPreview = styled.div`
background: ${({ color }) => color};
${({ isDisabled }) =>
isDisabled
${({ $disabled }) =>
$disabled
? css`
border: 2px solid ${({ theme }) => theme.materialTextDisabled};
filter: drop-shadow(
Expand All @@ -65,16 +77,20 @@ const ColorPreview = styled.div`
}
`;

const ChevronIcon = styled.span`
const ChevronIcon = styled.span<
Required<Pick<ColorInputProps, 'variant'>> & {
$disabled: boolean;
}
>`
width: 0px;
height: 0px;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
display: inline-block;
margin-left: 6px;
${({ isDisabled }) =>
isDisabled
${({ $disabled }) =>
$disabled
? css`
border-top: 6px solid ${({ theme }) => theme.materialTextDisabled};
filter: drop-shadow(
Expand All @@ -96,66 +112,52 @@ const ChevronIcon = styled.span`
`;

// TODO make sure all aria and role attributes are in place
const ColorInput = React.forwardRef(function ColorInput(props, ref) {
const { value, defaultValue, onChange, disabled, variant, ...otherProps } =
props;

const [valueDerived, setValueState] = useControlledOrUncontrolled({
value,
defaultValue
});
const ColorInput = forwardRef<HTMLInputElement, ColorInputProps>(
function ColorInput(
{
value,
defaultValue,
onChange = noOp,
disabled = false,
variant = 'default',
...otherProps
},
ref
) {
const [valueDerived, setValueState] = useControlledOrUncontrolled({
value,
defaultValue
});

const handleChange = e => {
const color = e.target.value;
setValueState(color);
if (onChange) {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const color = e.target.value;
setValueState(color);
onChange(e);
}
};
};

return (
// we only need button styles, so we display
// it as a div and reset type attribute
<Trigger
isDisabled={disabled}
as='div'
type={null}
variant={variant}
size='md'
>
<StyledColorInput
onChange={handleChange}
readOnly={disabled}
disabled={disabled}
value={valueDerived || '#008080'}
type='color'
ref={ref}
{...otherProps}
/>
<ColorPreview
color={valueDerived}
isDisabled={disabled}
role='presentation'
/>
{variant === 'default' && <StyledDivider orientation='vertical' />}
<ChevronIcon isDisabled={disabled} variant={variant} />
</Trigger>
);
});

ColorInput.defaultProps = {
value: undefined,
defaultValue: undefined,
disabled: false,
variant: 'default',
onChange: () => {}
};
return (
// we only need button styles, so we display
// it as a div and reset type attribute
<Trigger disabled={disabled} as='div' variant={variant} size='md'>
<StyledColorInput
onChange={handleChange}
readOnly={disabled}
disabled={disabled}
value={valueDerived || '#008080'}
type='color'
ref={ref}
{...otherProps}
/>
<ColorPreview
$disabled={disabled}
color={valueDerived}
role='presentation'
/>
{variant === 'default' && <StyledDivider orientation='vertical' />}
<ChevronIcon $disabled={disabled} variant={variant} />
</Trigger>
);
}
);

ColorInput.propTypes = {
value: propTypes.string,
defaultValue: propTypes.string,
onChange: propTypes.func,
disabled: propTypes.bool,
variant: propTypes.oneOf(['default', 'flat'])
};
export default ColorInput;
export { ColorInput, ColorInputProps };
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export * from './Avatar/Avatar';
export * from './Bar/Bar';
export * from './Button/Button';
export * from './Checkbox/Checkbox';
export { default as ColorInput } from './ColorInput/ColorInput';
export * from './ColorInput/ColorInput';
export * from './Counter/Counter';
export * from './Cutout/Cutout';
export * from './Desktop/Desktop';
Expand Down

0 comments on commit d5887d4

Please sign in to comment.