Skip to content

Commit

Permalink
feat(colordropdown): added new colordropdown component
Browse files Browse the repository at this point in the history
  • Loading branch information
bjornalm committed Nov 10, 2020
1 parent 4b69f4d commit 31cc83e
Show file tree
Hide file tree
Showing 8 changed files with 860 additions and 0 deletions.
112 changes: 112 additions & 0 deletions src/components/ColorDropdown/ColorDropdown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Dropdown } from 'carbon-components-react';
import {
purple70,
cyan50,
teal70,
magenta70,
red50,
red90,
green60,
blue80,
magenta50,
purple50,
teal50,
cyan90,
} from '@carbon/colors';

import { settings } from '../../constants/Settings';

const { iotPrefix } = settings;

const colorPropType = PropTypes.shape({
carbonColor: PropTypes.string,
name: PropTypes.string,
});

const propTypes = {
colors: PropTypes.arrayOf(colorPropType),
/** Internationalisation strings */
label: PropTypes.string,
titleText: PropTypes.string,
id: PropTypes.string.isRequired,
light: PropTypes.bool,
/** Callback for when any of the Dropdown color value changes */
onChange: PropTypes.func.isRequired,
selectedColor: colorPropType,
testID: PropTypes.string,
};

const defaultProps = {
colors: [
{ carbonColor: purple70, name: 'purple70' },
{ carbonColor: cyan50, name: 'cyan50' },
{ carbonColor: teal70, name: 'teal70' },
{ carbonColor: magenta70, name: 'magenta70' },
{ carbonColor: red50, name: 'red50' },
{ carbonColor: red90, name: 'red90' },
{ carbonColor: green60, name: 'green60' },
{ carbonColor: blue80, name: 'blue80' },
{ carbonColor: magenta50, name: 'magenta50' },
{ carbonColor: purple50, name: 'purple50' },
{ carbonColor: teal50, name: 'teal50' },
{ carbonColor: cyan90, name: 'cyan90' },
],
label: 'Select a color',
light: false,
selectedColor: undefined,
testID: undefined,
titleText: 'Color',
};

const ColorDropdown = ({
colors,
id,
label,
light,
onChange,
selectedColor,
titleText,
testID,
}) => {
const renderColorItem = (item) => {
return (
<div
title={`${item.name}`}
className={`${iotPrefix}--color-dropdown__item`}>
<div
title={`${item.carbonColor}`}
className={`${iotPrefix}--color-dropdown__color-sample`}
style={{ backgroundColor: item.carbonColor }}
/>
<div className={`${iotPrefix}--color-dropdown__color-name`}>
{item.name}
</div>
</div>
);
};

return (
<Dropdown
className={`${iotPrefix}--color-dropdown`}
id={id}
itemToString={renderColorItem}
items={colors}
label={label}
light={light}
onChange={(change) => {
onChange({ color: change.selectedItem });
}}
selectedItem={selectedColor}
titleText={titleText}
type="default"
test-id={testID}
/>
);
};

ColorDropdown.propTypes = propTypes;
ColorDropdown.defaultProps = defaultProps;

export default ColorDropdown;
64 changes: 64 additions & 0 deletions src/components/ColorDropdown/ColorDropdown.story.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import { red50, blue50, green50, teal70 } from '@carbon/colors';

import ColorDropdown from './ColorDropdown';

export default {
title: 'Watson IoT Experimental/ColorDropdown',
decorators: [withKnobs],
parameters: {
component: ColorDropdown,
},
excludeStories: [],
};

export const DefaultExample = () => (
<div style={{ width: '200px' }}>
<ColorDropdown
id="myColorDropdown"
label={text('label', 'Select a color')}
light={boolean('light', false)}
titleText={text('titleText', 'Color')}
onChange={action('onChange')}
/>
</div>
);

export const PresetSelectionExample = () => (
<div style={{ width: '200px' }}>
<ColorDropdown
id="myColorDropdown"
label={text('label', 'Select a color')}
titleText={text('titleText', 'Color')}
onChange={action('onChange')}
selectedColor={{ carbonColor: teal70, name: 'teal70' }}
/>
</div>
);

export const CustomColorsExample = () => (
<div style={{ width: '200px' }}>
<ColorDropdown
id="myColorDropdown"
label={text('label', 'Select a color')}
titleText={text('titleText', 'Color')}
colors={[
{ carbonColor: red50, name: 'red' },
{ carbonColor: green50, name: 'green' },
{ carbonColor: blue50, name: 'blue' },
]}
onChange={action('onChange')}
/>
</div>
);

DefaultExample.story = {
parameters: {
info: {
propTables: [ColorDropdown],
propTablesExclude: [],
},
},
};
152 changes: 152 additions & 0 deletions src/components/ColorDropdown/ColorDropdown.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import React from 'react';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { red50, blue50, green50 } from '@carbon/colors';

import { settings } from '../../constants/Settings';

import ColorDropdown from './ColorDropdown';

const { iotPrefix } = settings;

describe('ColorDropdown', () => {
const getColors = () => [
{ carbonColor: red50, name: 'red' },
{ carbonColor: green50, name: 'green' },
{ carbonColor: blue50, name: 'blue' },
];

const getHexColor = (name) =>
getColors().find((obj) => obj.name === name).carbonColor;

const hexToRgbStyle = (hexColor) => {
const regexResult = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
hexColor
);
const radix = 16;
const r = parseInt(regexResult[1], radix);
const g = parseInt(regexResult[2], radix);
const b = parseInt(regexResult[3], radix);
return `rgb(${r}, ${g}, ${b})`;
};

it('renders default labels', () => {
render(<ColorDropdown id="myColorDropdown" onChange={() => {}} />);

expect(
within(screen.getByRole('button')).getByText('Select a color')
).toBeTruthy();

expect(screen.getByText('Color')).toBeVisible();
});

it('renders custom labels', () => {
const label = 'my label';
const titleText = 'My title text';
render(
<ColorDropdown
id="myColorDropdown"
label={label}
titleText={titleText}
onChange={() => {}}
/>
);

expect(within(screen.getByRole('button')).getByText(label)).toBeTruthy();
expect(screen.getByText(titleText)).toBeVisible();
});

it('renders preset color and shows selected color sample', () => {
render(
<ColorDropdown
colors={getColors()}
selectedColor={{ carbonColor: green50, name: 'green' }}
id="myColorDropdown"
onChange={() => {}}
/>
);
const button = screen.getByRole('button');
expect(within(button).getByText('green')).toBeVisible();
expect(within(button).getByTitle(getHexColor('green'))).toHaveClass(
`${iotPrefix}--color-dropdown__color-sample`
);
});

it('renders the selected value correctly', () => {
const onChange = jest.fn();
render(
<ColorDropdown
id="myColorDropdown"
colors={getColors()}
onChange={onChange}
/>
);
userEvent.click(screen.getByText('Select a color'));

const firstItem = screen.getAllByRole('option')[0];
expect(within(firstItem).getByText('red')).toBeVisible();
userEvent.click(firstItem);

const button = screen.getByRole('button');
expect(within(button).getByText('red')).toBeVisible();
const colorSample = within(button).getByTitle(getHexColor('red'));
expect(colorSample).toHaveClass(
`${iotPrefix}--color-dropdown__color-sample`
);
expect(colorSample).toHaveStyle({
backgroundColor: hexToRgbStyle(getHexColor('red')),
});
});

it('renders the selectable items correctly', () => {
const onChange = jest.fn();
render(
<ColorDropdown
id="myColorDropdown"
colors={getColors()}
onChange={onChange}
/>
);
userEvent.click(screen.getByText('Select a color'));

const firstItem = screen.getAllByRole('option')[0];
const firstColorSample = within(firstItem).getByTitle(getHexColor('red'));
expect(within(firstItem).getByText('red')).toBeVisible();
expect(firstColorSample).toHaveClass(
`${iotPrefix}--color-dropdown__color-sample`
);
expect(firstColorSample).toHaveStyle({
backgroundColor: hexToRgbStyle(getHexColor('red')),
});

const secondItem = screen.getAllByRole('option')[1];
const secondColorSample = within(secondItem).getByTitle(
getHexColor('green')
);
expect(within(secondItem).getByText('green')).toBeVisible();
expect(secondColorSample).toHaveClass(
`${iotPrefix}--color-dropdown__color-sample`
);
expect(secondColorSample).toHaveStyle({
backgroundColor: hexToRgbStyle(getHexColor('green')),
});
});

it('calls onChange with the selected color when a color is selected', () => {
const onChange = jest.fn();
render(
<ColorDropdown
colors={getColors()}
id="myColorDropdown"
onChange={onChange}
/>
);
userEvent.click(screen.getByText('Select a color'));
const firstItem = screen.getAllByRole('option')[0];
userEvent.click(firstItem);

expect(onChange).toHaveBeenCalledWith({
color: { carbonColor: getHexColor('red'), name: 'red' },
});
});
});
Loading

0 comments on commit 31cc83e

Please sign in to comment.