Skip to content

Commit

Permalink
feat(select): L3-3818 updated select
Browse files Browse the repository at this point in the history
  • Loading branch information
constantinehuzenko committed Nov 15, 2024
1 parent edb1261 commit 96360f2
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 4 deletions.
43 changes: 43 additions & 0 deletions src/components/Select/Select.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Meta } from '@storybook/react';

import Select, { SelectProps } from './Select';
import { SelectVariants } from './types';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta = {
title: 'Components/Select',
Expand All @@ -24,6 +25,11 @@ const argTypes = {
type: 'boolean',
},
},
showCustomIcon: {
control: {
type: 'boolean',
},
},
invalid: {
control: {
type: 'boolean',
Expand Down Expand Up @@ -54,6 +60,12 @@ const argTypes = {
type: 'select',
},
},
variant: {
options: [SelectVariants.default, SelectVariants.tertiary],
control: {
type: 'select',
},
},
value: {
control: {
type: 'text',
Expand Down Expand Up @@ -90,14 +102,45 @@ Playground.args = {
playgroundWidth: 300,
className: 'input-test-class',
defaultValue: 'Option 2',
showCustomIcon: false,
disabled: false,
invalid: false,
invalidText: 'Error message',
labelText: 'Label text',
readOnly: false,
size: 'md',
variant: SelectVariants.default,
warn: false,
warnText: 'Warning message that is really long can wrap to more lines.',
};

Playground.argTypes = argTypes;

export const Tertiary = ({ playgroundWidth, ...args }: StoryProps) => (
<div style={{ width: playgroundWidth, margin: '1rem' }}>
<Select key={args.defaultValue} {...args} id="Input-1" value="show-lot" hideLabel={true}>
<option key="0" value="show-lot" hidden>
Show Lot No.
</option>

{Array.from({ length: 20 }, (_, i) => (
<option key={i}>Option {i + 1}</option>
))}
</Select>
</div>
);

Tertiary.args = {
showCustomIcon: true,
variant: SelectVariants.tertiary,
};

Tertiary.argTypes = {
variant: {
options: [SelectVariants.default, SelectVariants.tertiary],
control: {
type: 'select',
},
defaultValue: SelectVariants.tertiary,
},
};
40 changes: 40 additions & 0 deletions src/components/Select/Select.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ import userEvent from '@testing-library/user-event';
import * as React from 'react';

import Select from './Select';
import { px } from '../../utils';

describe('A Select', () => {
const reqProps = { labelText: 'My Test Label', id: 'test-id' };
const mockLabel = 'Test Label';
const mockOptions = (
<>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</>
);

it('will render a default value if passed', () => {
const testRef = React.createRef<HTMLSelectElement>();
Expand Down Expand Up @@ -54,4 +62,36 @@ describe('A Select', () => {
await userEvent.selectOptions(screen.getByTestId('test-id'), ['option one']);
await waitFor(() => expect(mockedOnChange.mock.calls).toHaveLength(0));
});

it('should toggle --open and --closed classes on click', async () => {
render(
<Select id="test-select" labelText={mockLabel} showCustomIcon>
{mockOptions}
</Select>,
);

const selectElement = screen.getByTestId('test-select');

// Initial state should be closed
expect(selectElement).toHaveClass(`${px}-input__select--closed`);

// Simulate click to open
await userEvent.click(selectElement);
expect(selectElement).toHaveClass(`${px}-input__select--open`);

// Simulate blur to close
await userEvent.tab();
expect(selectElement).toHaveClass(`${px}-input__select--closed`);
});

it('should apply --tertiary class when variant is tertiary', () => {
render(
<Select id="test-select-tertiary" labelText={mockLabel} variant="tertiary">
{mockOptions}
</Select>,
);

const selectElement = screen.getByTestId('test-select-tertiary');
expect(selectElement).toHaveClass(`${px}-input__select--tertiary`);
});
});
34 changes: 30 additions & 4 deletions src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import * as React from 'react';
import classnames from 'classnames';

import { px, useNormalizedInputProps } from '../../utils';

import { InputProps } from '../Input/Input';
import './select.scss';
import { SelectVariants } from './types';

export interface SelectProps extends InputProps {
/**
* Option elements that are selectable
*/
children: React.ReactNode;
/**
* Determines if you want to show the icon
*/
showCustomIcon?: boolean;
/**
* Determines the variant of the select
*/
variant: SelectVariants;
}

/**
Expand All @@ -31,6 +39,8 @@ const Select = React.forwardRef(
disabled,
hideLabel,
id,
showCustomIcon = false,
variant = SelectVariants.default,
inline,
invalid,
invalidText,
Expand All @@ -48,6 +58,9 @@ const Select = React.forwardRef(
) => {
const type = 'select';
const inputProps = useNormalizedInputProps({ disabled, id, invalid, invalidText, readOnly, type, warn, warnText });
const [isOpen, setIsOpen] = React.useState(false);
const handleIsOpen = () => setIsOpen((prev) => !prev);
const closeDropdown = () => setIsOpen(false);

const wrapperClassnames = classnames(`${px}-${type}-input`, `${px}-input`, `${px}-input--${size}`, {
[`${px}-input--inline`]: inline,
Expand All @@ -58,19 +71,32 @@ const Select = React.forwardRef(
[`${className}__wrapper`]: className,
});

const selectClassnames = classnames(`${px}-input__input`, {
className,
[`${px}-input__select--open`]: isOpen && showCustomIcon,
[`${px}-input__select--closed`]: !isOpen && showCustomIcon,
[`${px}-input__select--tertiary`]: variant === SelectVariants.tertiary,
});

const handleClick = (e: React.MouseEvent<HTMLSelectElement>) => {
handleIsOpen();
onClick?.(e);
};

return (
<div className={wrapperClassnames}>
<label htmlFor={id} className={classnames(`${px}-input__label`, { [`${px}-input__label--hidden`]: hideLabel })}>
{labelText}
</label>
<select
className={classnames(`${px}-input__input`, { className })}
className={selectClassnames}
data-testid={id}
defaultValue={defaultValue}
disabled={inputProps.disabled}
id={id}
onChange={onChange}
onClick={onClick}
onBlur={closeDropdown}
onClick={handleClick}
ref={ref}
value={value}
{...rest}
Expand Down
42 changes: 42 additions & 0 deletions src/components/Select/select.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@use '#scss/allPartials' as *;

.#{$px}-select-input {
select.#{$px}-input {
&__select {
&--tertiary {
border: 0;
color: inherit;

&:hover {
cursor: pointer;
}

&:focus-visible {
background-color: $pure-white;
border-radius: 0;
outline-color: $soft-black;
outline-offset: 4.5px;
}
}

&--closed {
background: url("data:image/svg+xml,<svg height='16' width='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M8.06566 9.12623L12.2097 4.98219C12.3561 4.83574 12.7442 4.80954 12.9673 5.0327C13.1905 5.25585 13.1643 5.64386 13.0178 5.79031L8.42162 10.3865C8.34739 10.4607 8.21109 10.5041 8.06566 10.4997C7.92022 10.5041 7.78392 10.4607 7.70969 10.3865L3.1135 5.79031C2.96705 5.64386 2.94085 5.25585 3.16401 5.0327C3.38716 4.80954 3.77517 4.83574 3.92162 4.98219L8.06566 9.12623Z' fill='black'/></svg>")
no-repeat;
}

&--open {
background: url("data:image/svg+xml,<svg height='16' width='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'><path fill-rule='evenodd' clip-rule='evenodd' d='M8.06566 6.37378L12.2097 10.5178C12.3561 10.6643 12.7442 10.6905 12.9673 10.4673C13.1905 10.2442 13.1643 9.85614 13.0178 9.70969L8.42162 5.1135C8.34739 5.03927 8.21109 4.99593 8.06566 5.00031C7.92022 4.99593 7.78392 5.03927 7.70969 5.1135L3.1135 9.70969C2.96705 9.85614 2.94085 10.2442 3.16401 10.4673C3.38716 10.6905 3.77517 10.6643 3.92162 10.5178L8.06566 6.37378Z' fill='black'/></svg>")
no-repeat;
}

&--closed,
&--open {
appearance: none;
appearance: none;
appearance: none;
background-position: calc(100% - 0.75rem) center;
padding-right: $spacing-md;
}
}
}
}
4 changes: 4 additions & 0 deletions src/components/Select/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum SelectVariants {
default = 'default',
tertiary = 'tertiary',
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export { default as GridItem, type GridItemProps } from './components/GridItem/G
export { GridItemAlign } from './components/GridItem/types';
export { default as Search, type SearchProps } from './components/Search/Search';
export { default as Select, type SelectProps } from './components/Select/Select';
export { SelectVariants } from './components/Select/types';
export { default as SplitPanel, type SplitPanelProps } from './components/SplitPanel/SplitPanel';
export { default as Subscribe, type SubscribeProps } from './patterns/Subscribe/Subscribe';
export { SubscriptionState } from './patterns/Subscribe/types';
Expand Down

0 comments on commit 96360f2

Please sign in to comment.