-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Select): Added new Select component DEV-53
- Loading branch information
Showing
11 changed files
with
1,080 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import React, { useState } from "react" | ||
import { Meta, Story } from "@storybook/react/types-6-0" | ||
|
||
import Select, { SelectProps, SelectedItems } from "./Select" | ||
import { mockedOptions } from "./__mocks__/options" | ||
|
||
const mockedChildrenOptions = mockedOptions.map(({ label, value }) => ( | ||
<Select.Option key={value} value={value}> | ||
{label} | ||
</Select.Option> | ||
)) | ||
|
||
export default { | ||
title: "Design System/Select", | ||
component: Select, | ||
} as Meta | ||
|
||
const Template: Story<SelectProps> = (props: SelectProps) => { | ||
const [value, setValue] = useState<SelectProps["value"]>( | ||
props.value || props.defaultValue || "" | ||
) | ||
|
||
return ( | ||
<Select | ||
{...props} | ||
value={value} | ||
onChange={(newValue) => { | ||
props.onChange?.(newValue) | ||
setValue(newValue) | ||
}} | ||
/> | ||
) | ||
} | ||
|
||
/** Multi selector controlled to make sure the selected items become persistent through state changes */ | ||
const MultiTemplate: Story<SelectProps> = (props: SelectProps) => { | ||
const [selectedItems, setSelectedItems] = useState<SelectedItems>( | ||
props.defaultSelectedItems || props.selectedItems || [] | ||
) | ||
|
||
return ( | ||
<Select | ||
{...props} | ||
selectedItems={selectedItems} | ||
onSelectedItemsChange={(newSelectedItems) => { | ||
setSelectedItems(newSelectedItems) | ||
}} | ||
> | ||
{mockedOptions.map(({ label, value }) => ( | ||
<Select.Option key={value} value={value}> | ||
{label} | ||
</Select.Option> | ||
))} | ||
</Select> | ||
) | ||
} | ||
|
||
export const Default = Template.bind({}) | ||
Default.args = { | ||
children: mockedChildrenOptions.slice(0, 5), | ||
id: "default", | ||
} | ||
|
||
export const WithLabel = Template.bind({}) | ||
WithLabel.args = { | ||
children: mockedChildrenOptions, | ||
label: "Select an element: ", | ||
} | ||
|
||
export const WithDefaultValue = Template.bind({}) | ||
WithDefaultValue.args = { | ||
children: [ | ||
<Select.Option key={200} value={200}> | ||
<span | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
}} | ||
> | ||
<span | ||
style={{ | ||
marginRight: "8px", | ||
borderRadius: "50%", | ||
background: "#30D158", | ||
width: "8px", | ||
height: "8px", | ||
}} | ||
/> | ||
Active | ||
</span> | ||
</Select.Option>, | ||
...mockedChildrenOptions, | ||
], | ||
defaultValue: 200, | ||
} | ||
|
||
export const MultipleSelection = MultiTemplate.bind({}) | ||
MultipleSelection.args = { | ||
children: mockedChildrenOptions, | ||
label: "Select one or multiple elements: ", | ||
multi: true, | ||
} | ||
|
||
export const WithDefaultSelectedItems = MultiTemplate.bind({}) | ||
WithDefaultSelectedItems.args = { | ||
children: mockedChildrenOptions, | ||
label: "Select one or multiple elements: ", | ||
multi: true, | ||
defaultSelectedItems: [1, 5], | ||
} | ||
|
||
export const Disabled = Template.bind({}) | ||
Disabled.args = { | ||
children: mockedChildrenOptions, | ||
label: "Select an element: ", | ||
disabled: true, | ||
} | ||
|
||
export const WithoutPlaceholder = Template.bind({}) | ||
WithoutPlaceholder.args = { | ||
children: mockedChildrenOptions.slice(0, 5), | ||
placeholder: " ", | ||
fullWidth: true, | ||
} | ||
|
||
const TemplateObjectValues: Story<SelectProps> = (props: SelectProps) => { | ||
const [value, setValue] = useState<SelectProps["value"]>( | ||
props.value || props.defaultValue || "" | ||
) | ||
|
||
return ( | ||
<Select | ||
{...props} | ||
value={value} | ||
onChange={(newValue) => { | ||
props.onChange?.(newValue) | ||
setValue(newValue) | ||
}} | ||
/> | ||
) | ||
} | ||
export const WithObjectValues = TemplateObjectValues.bind({}) | ||
WithObjectValues.args = { | ||
children: [ | ||
<Select.Option | ||
key={JSON.stringify({ from: "10/10/10", to: "12/12/12" })} | ||
value={{ from: "10/10/10", to: "12/12/12" }} | ||
> | ||
Today | ||
</Select.Option>, | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import { useState } from "react" | ||
import { fireEvent, render } from "../../tests/utils" | ||
|
||
import { mockedOptions } from "./__mocks__/options" | ||
import Select, { SelectProps } from "./Select" | ||
const children = mockedOptions.map(({ label, value }) => ( | ||
<Select.Option key={value} label={label} value={value} /> | ||
)) | ||
|
||
const ControlledMultiSelectTemplate = (props: SelectProps) => { | ||
const [selectedItems, setSelectedItems] = useState< | ||
SelectProps["selectedItems"] | ||
>([]) | ||
|
||
return ( | ||
<Select | ||
selectedItems={selectedItems} | ||
multi={true} | ||
{...props} | ||
onSelectedItemsChange={(selectedItems) => { | ||
setSelectedItems(selectedItems) | ||
props.onSelectedItemsChange?.(selectedItems) | ||
}} | ||
/> | ||
) | ||
} | ||
|
||
describe("Select", () => { | ||
it("renders successfully", () => { | ||
const { getByRole } = render(<Select>{children}</Select>) | ||
|
||
/** | ||
* Check if the element exists | ||
*/ | ||
expect( | ||
getByRole("button", { name: "Select an option" }) | ||
).toBeInTheDocument() | ||
}) | ||
|
||
it("renders the label succesfully", () => { | ||
const { getByText } = render( | ||
<Select label="Select an element:">{children}</Select> | ||
) | ||
|
||
expect(getByText("Select an element:")).toBeInTheDocument() | ||
}) | ||
|
||
it("renders all the options and allows to select", () => { | ||
const onChangeMock = jest.fn() | ||
const { getByText, queryByTitle, getByRole } = render( | ||
<Select onChange={onChangeMock}>{children}</Select> | ||
) | ||
|
||
/** Act: enable the options by clicking on the button */ | ||
const button = getByRole("button") | ||
fireEvent.click(button) | ||
|
||
/** | ||
* Check if the options are rendered | ||
*/ | ||
mockedOptions.forEach((option) => { | ||
expect(getByText(option.label)).toBeInTheDocument() | ||
}) | ||
|
||
/** | ||
* Check for an invalid option | ||
*/ | ||
expect(queryByTitle("Random")).not.toBeInTheDocument() | ||
|
||
/** Try to select an option */ | ||
const option = getByText(mockedOptions[1].label) | ||
fireEvent.click(option) | ||
|
||
expect(onChangeMock).toHaveBeenCalled() | ||
}) | ||
|
||
describe("onSelectedItemsChange", () => { | ||
it("allows to add and remove a multi select item", () => { | ||
const onSelectedItemsChangeMock = jest.fn() | ||
|
||
const { getByText, getByRole } = render( | ||
<ControlledMultiSelectTemplate | ||
onSelectedItemsChange={onSelectedItemsChangeMock} | ||
> | ||
{children} | ||
</ControlledMultiSelectTemplate> | ||
) | ||
|
||
/** Act: enable the options by clicking on the button */ | ||
const button = getByRole("button") | ||
fireEvent.click(button) | ||
|
||
/** Try to select an option */ | ||
const option = getByText(mockedOptions[1].label) | ||
|
||
fireEvent.click(option) | ||
|
||
/** Select again to remove it */ | ||
fireEvent.click(option) | ||
|
||
expect(onSelectedItemsChangeMock).toHaveBeenCalledTimes(2) | ||
}) | ||
}) | ||
|
||
describe("handleBulkAction", () => { | ||
it("allows to user to perform bulk actions to select and deselect items", () => { | ||
const onSelectedItemsChangeMock = jest.fn() | ||
|
||
const { getByText, getByRole } = render( | ||
<ControlledMultiSelectTemplate | ||
onSelectedItemsChange={onSelectedItemsChangeMock} | ||
> | ||
{children} | ||
</ControlledMultiSelectTemplate> | ||
) | ||
|
||
/** Act: enable the options by clicking on the button */ | ||
const button = getByRole("button") | ||
fireEvent.click(button) | ||
|
||
/** Trigger bulk actions */ | ||
const selectAllOption = getByText("Select all") | ||
fireEvent.click(selectAllOption) | ||
|
||
const deselectAllOptions = getByText("Deselect all") | ||
fireEvent.click(deselectAllOptions) | ||
|
||
expect(onSelectedItemsChangeMock).toHaveBeenCalledTimes(2) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.