Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Created SelectOption #7969

Merged
merged 9 commits into from
Dec 19, 2023
1 change: 1 addition & 0 deletions .storybook/storybook.requires.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const getStories = () => {
"./app/component-library/components/Pickers/PickerAccount/PickerAccount.stories.tsx": require("../app/component-library/components/Pickers/PickerAccount/PickerAccount.stories.tsx"),
"./app/component-library/components/Pickers/PickerNetwork/PickerNetwork.stories.tsx": require("../app/component-library/components/Pickers/PickerNetwork/PickerNetwork.stories.tsx"),
"./app/component-library/components/RadioButton/RadioButton.stories.tsx": require("../app/component-library/components/RadioButton/RadioButton.stories.tsx"),
"./app/component-library/components/Select/SelectOption/SelectOption.stories.tsx": require("../app/component-library/components/Select/SelectOption/SelectOption.stories.tsx"),
"./app/component-library/components/Select/SelectValue/SelectValue.stories.tsx": require("../app/component-library/components/Select/SelectValue/SelectValue.stories.tsx"),
"./app/component-library/components/Sheet/SheetBottom/SheetBottom.stories.tsx": require("../app/component-library/components/Sheet/SheetBottom/SheetBottom.stories.tsx"),
"./app/component-library/components/Sheet/SheetHeader/SheetHeader.stories.tsx": require("../app/component-library/components/Sheet/SheetHeader/SheetHeader.stories.tsx"),
Expand Down
144 changes: 144 additions & 0 deletions app/component-library/components/Select/SelectOption/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# SelectOption

`SelectOption` is a selectable option used in the SelectMenu and is part of the Select component

## Props

This component extends [SelectValueProps](../SelectValue/SelectValue.types.ts) and [ListItemSelectProps](../../List/ListItemSelect/ListItemSelect.types.ts) from the [SelectValue](../SelectValue/SelectValue.tsx) and [ListItemSelect](../../List/ListItemSelect/ListItemSelect.tsx) components.
brianacnguyen marked this conversation as resolved.
Show resolved Hide resolved

### `iconEl`

Optional prop to replace the start Icon Element

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| Avatar-like Elements | No |

### `iconProps`

Optional prop for the start Icon Element

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| AvatarProps | No |

### `label`

Optional prop for the label of SelectOption

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| string or ReactNode | No |

### `description`

Optional description below the label

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| string or ReactNode | No |

### `isSelected`

Optional prop to determine if the item is selected

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> | <span style="color:gray;font-size:14px">DEFAULT</span> |
| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
| boolean | No | false |

### `isDisabled`

Optional prop to determine if the item is disabled

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> | <span style="color:gray;font-size:14px">DEFAULT</span> |
| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
| boolean | No | false |

## Custom Props

### `startAccessory`

Optional content to be displayed before the info section

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| ReactNode | No |

### `children`

Optional content to be displayed in the info section

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| ReactNode | No |

### `endAccessory`

Optional content to be displayed after the info section

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| ReactNode | No |

### `gap`

Optional prop to configure the gap between items inside the ListItem

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> | <span style="color:gray;font-size:14px">DEFAULT</span> |
| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
| number or string | No | 16 |

### `verticalAlignment`

Optional prop to configure the vertical alignment between items inside the ListItem

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> | <span style="color:gray;font-size:14px">DEFAULT</span> |
| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
| VerticalAlignment | No | VerticalAlignment.Top |

## Usage

```javascript
// Common use case
<SelectOption
iconProps={SAMPLE_ICON_PROPS}
label={SAMPLE_LABEL_TEXT}
description={SAMPLE_DESCRIPTION_TEXT}
isSelected={false}
isDisabled={false}
/>

// Custom starting icon
<SelectOption
label={SAMPLE_LABEL_TEXT}
description={SAMPLE_DESCRIPTION_TEXT}
iconEl={SAMPLE_ICON_EL}
isSelected={false}
isDisabled={false}
/>

// Custom starting accessory
<SelectOption
label={SAMPLE_LABEL_TEXT}
description={SAMPLE_DESCRIPTION_TEXT}
startAccessory={SAMPLE_STARTACCESSORY_EL}
isSelected={false}
isDisabled={false}
/>

// Custom ending accessory
<SelectOption
label={SAMPLE_LABEL_TEXT}
description={SAMPLE_DESCRIPTION_TEXT}
endAccessory={SAMPLE_ENDACCESSORY_EL}
isSelected={false}
isDisabled={false}
/>

// Custom info section
<SelectOption
isSelected={false}
isDisabled={false}>
{SAMPLE_CHILDREN}
</SelectOption>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable import/prefer-default-export */
// External dependencies.
import { SAMPLE_AVATAR_PROPS } from '../../Avatars/Avatar/Avatar.constants';

// Internal dependencies.
import { SelectOptionProps } from './SelectOption.types';

// Sample consts
export const SAMPLE_SELECTOPTION_PROPS: SelectOptionProps = {
iconProps: SAMPLE_AVATAR_PROPS,
label: 'Sample SelectOption label',
description: 'Sample SelectOption description',
isSelected: false,
isDisabled: false,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* eslint-disable react/display-name */
// Third party dependencies.
import React from 'react';

// Internal dependencies.
import { default as SelectOptionComponent } from './SelectOption';
import { SAMPLE_SELECTOPTION_PROPS } from './SelectOption.constants';

const SelectOptionStoryMeta = {
title: 'Component Library / Select',
component: SelectOptionComponent,
argTypes: {
label: {
control: { type: 'text' },
},
description: {
control: { type: 'text' },
},
isSelected: {
control: { type: 'boolean' },
},
isDisabled: {
control: { type: 'boolean' },
},
},
};

export default SelectOptionStoryMeta;

export const SelectOption = {
args: {
label: SAMPLE_SELECTOPTION_PROPS.label,
description: SAMPLE_SELECTOPTION_PROPS.description,
isSelected: SAMPLE_SELECTOPTION_PROPS.isSelected,
isDisabled: SAMPLE_SELECTOPTION_PROPS.isDisabled,
},
render: (args: any) => (
<SelectOptionComponent
{...args}
iconProps={SAMPLE_SELECTOPTION_PROPS.iconProps}
/>
),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Third party dependencies.
import { StyleSheet, ViewStyle } from 'react-native';

// Internal dependencies.
import { SelectOptionStyleSheetVars } from './SelectOption.types';

/**
* Style sheet function for SelectOption component.
*
* @param params Style sheet params.
* @param params.theme App theme from ThemeContext.
* @param params.vars Inputs that the style sheet depends on.
* @returns StyleSheet object.
*/
const styleSheet = (params: { vars: SelectOptionStyleSheetVars }) => {
const { vars } = params;
const { style } = vars;
return StyleSheet.create({
base: Object.assign({} as ViewStyle, style) as ViewStyle,
});
};

export default styleSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Third party dependencies.
import React from 'react';
import { render } from '@testing-library/react-native';

// Internal dependencies.
import SelectOption from './SelectOption';
import { SAMPLE_SELECTOPTION_PROPS } from './SelectOption.constants';

describe('SelectOption', () => {
it('should render snapshot correctly', () => {
const wrapper = render(<SelectOption {...SAMPLE_SELECTOPTION_PROPS} />);
expect(wrapper).toMatchSnapshot();
});

it('should not render the selected view if isSelected is false', () => {
const { queryByRole } = render(
<SelectOption {...SAMPLE_SELECTOPTION_PROPS} />,
);
expect(queryByRole('checkbox')).toBeNull();
});

it('should render the selected view if isSelected is true', () => {
const { queryByRole } = render(
<SelectOption {...SAMPLE_SELECTOPTION_PROPS} isSelected />,
);
expect(queryByRole('checkbox')).not.toBeNull();
});
});
brianacnguyen marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* eslint-disable react/prop-types */

// Third party dependencies.
import React from 'react';

// External dependencies.
import { useStyles } from '../../../hooks';
import ListItemSelect from '../../List/ListItemSelect/ListItemSelect';
import SelectValue from '../SelectValue/SelectValue';

// Internal dependencies.
import styleSheet from './SelectOption.styles';
import { SelectOptionProps } from './SelectOption.types';

const SelectOption: React.FC<SelectOptionProps> = ({
style,
isSelected,
isDisabled,
gap = 12,
verticalAlignment,
...props
}) => {
const { styles } = useStyles(styleSheet, {
style,
});

return (
<ListItemSelect
style={styles.base}
gap={gap}
verticalAlignment={verticalAlignment}
isSelected={isSelected}
isDisabled={isDisabled}
accessibilityRole="menuitem"
>
<SelectValue {...props} />
</ListItemSelect>
);
};

export default SelectOption;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// External dependencies.
import { ListItemSelectProps } from '../../List/ListItemSelect/ListItemSelect.types';
import { SelectValueProps } from '../SelectValue/SelectValue.types';

/**
* SelectOption component props.
*/
export interface SelectOptionProps
extends ListItemSelectProps,
Omit<SelectValueProps, 'style'> {}

/**
* Style sheet input parameters.
*/
export type SelectOptionStyleSheetVars = Pick<SelectOptionProps, 'style'>;
Loading
Loading