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: extend PickerBase component functionality #11851

Merged
merged 3 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable react/display-name */
/* eslint-disable react-native/no-inline-styles */
// External dependencies.
import React from 'react';
import { View, Text } from 'react-native';

// Internal dependencies.
import PickerBase from './PickerBase';
import { IconSize } from '../../Icons/Icon';

const PickerBaseMeta = {
title: 'Component Library / Pickers',
component: PickerBase,
argTypes: {
children: {
control: { type: 'text' },
defaultValue: 'Select an option',
},
iconSize: {
options: Object.values(IconSize),
control: { type: 'select' },
defaultValue: IconSize.Md,
},
},
};

export default PickerBaseMeta;

export const Default = {
render: ({
children,
iconSize,
}: {
children: string;
iconSize: IconSize;
}) => (
<View style={{ alignItems: 'flex-start' }}>
<PickerBase onPress={() => null} iconSize={iconSize}>
<Text>{children}</Text>
</PickerBase>
</View>
),
};

export const WithCustomStyles = {
render: () => (
<View style={{ alignItems: 'flex-start' }}>
<PickerBase
onPress={() => null}
style={{ width: 200 }}
dropdownIconStyle={{ marginLeft: 20 }}
>
<Text>Custom Styled Picker</Text>
</PickerBase>
</View>
),
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const styleSheet = (params: {
}) => {
const { vars, theme } = params;
const { colors } = theme;
const { style } = vars;
const { style, dropdownIconStyle } = vars;

return StyleSheet.create({
base: Object.assign(
{
Expand All @@ -35,9 +36,12 @@ const styleSheet = (params: {
} as ViewStyle,
style,
) as ViewStyle,
dropdownIcon: {
marginLeft: 16,
},
dropdownIcon: Object.assign(
{
marginLeft: 16,
} as ViewStyle,
dropdownIconStyle,
),
});
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,78 @@
// Third party dependencies.
import React from 'react';
import { View } from 'react-native';
import { render } from '@testing-library/react-native';
import { Text } from 'react-native';
import { render, fireEvent } from '@testing-library/react-native';

// Internal dependencies.
import PickerBase from './PickerBase';
import { IconName, IconSize } from '../../Icons/Icon';

describe('PickerBase', () => {
it('should render correctly', () => {
const { toJSON } = render(
<PickerBase onPress={jest.fn}>
<View />
<PickerBase onPress={jest.fn()}>
<Text>Test Content</Text>
</PickerBase>,
);
expect(toJSON()).toMatchSnapshot();
});

it('should call onPress when pressed', () => {
const onPressMock = jest.fn();
const { getByText } = render(
<PickerBase onPress={onPressMock}>
<Text>Test Content</Text>
</PickerBase>,
);

fireEvent.press(getByText('Test Content'));
expect(onPressMock).toHaveBeenCalledTimes(1);
});

it('should render children correctly', () => {
const { getByText } = render(
<PickerBase onPress={jest.fn()}>
<Text>Child Component</Text>
</PickerBase>,
);

expect(getByText('Child Component')).toBeTruthy();
});

it('should render dropdown icon', () => {
const { UNSAFE_getByProps } = render(
<PickerBase onPress={jest.fn()}>
<Text>Test Content</Text>
</PickerBase>,
);

const icon = UNSAFE_getByProps({ name: IconName.ArrowDown });
expect(icon).toBeTruthy();
});

it('should apply custom icon size', () => {
const { UNSAFE_getByProps } = render(
<PickerBase onPress={jest.fn()} iconSize={IconSize.Lg}>
<Text>Test Content</Text>
</PickerBase>,
);

const icon = UNSAFE_getByProps({
name: IconName.ArrowDown,
size: IconSize.Lg,
});
expect(icon).toBeTruthy();
});

it('should apply custom dropdown icon style', () => {
const customStyle = { marginLeft: 20 };
const { UNSAFE_getByProps } = render(
<PickerBase onPress={jest.fn()} dropdownIconStyle={customStyle}>
<Text>Test Content</Text>
</PickerBase>,
);

const icon = UNSAFE_getByProps({ name: IconName.ArrowDown });
expect(icon.props.style).toEqual(expect.objectContaining(customStyle));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ import styleSheet from './PickerBase.styles';
const PickerBase: React.ForwardRefRenderFunction<
TouchableOpacity,
PickerBaseProps
> = ({ style, children, ...props }, ref) => {
const { styles, theme } = useStyles(styleSheet, { style });
> = (
{ iconSize = IconSize.Md, style, dropdownIconStyle, children, ...props },
ref,
) => {
const { styles, theme } = useStyles(styleSheet, { style, dropdownIconStyle });
const { colors } = theme;

return (
<TouchableOpacity style={styles.base} {...props} ref={ref}>
{children}
<Icon
size={IconSize.Md}
size={iconSize}
color={colors.icon.default}
name={IconName.ArrowDown}
style={styles.dropdownIcon}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Third party dependencies.
import { TouchableOpacityProps } from 'react-native';
import { TouchableOpacityProps, ViewStyle } from 'react-native';
import { IconSize } from '../../Icons/Icon';

/**
* PickerBase component props.
Expand All @@ -13,9 +14,20 @@ export interface PickerBaseProps extends TouchableOpacityProps {
* Content to wrap in PickerBase.
*/
children: React.ReactNode;
/**
* Icon size.
*/
iconSize?: IconSize;
/**
* Dropdown icon styles.
*/
dropdownIconStyle?: ViewStyle;
vinnyhoward marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Style sheet input parameters.
*/
export type PickerBaseStyleSheetVars = Pick<PickerBaseProps, 'style'>;
export type PickerBaseStyleSheetVars = Pick<
PickerBaseProps,
'style' | 'dropdownIconStyle'
>;
56 changes: 50 additions & 6 deletions app/component-library/components/Pickers/PickerBase/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# PickerBase

PickerBase is a **wrapper** component used for providing a dropdown icon next to wrapped content.
PickerBase is a **wrapper** component used for providing a dropdown icon next to wrapped content. It's designed to be a flexible base for various picker-style components.

## Props

This component extends `TouchableOpacityProps` from React Native's [TouchableOpacityProps](https://reactnative.dev/docs/touchableOpacity) opacity.
This component extends `TouchableOpacityProps` from React Native's [TouchableOpacity](https://reactnative.dev/docs/touchableopacity).

### `onPress`

Expand All @@ -22,11 +22,55 @@ Content to wrap in PickerBase.
| :-------------------------------------------------- | :------------------------------------------------------ |
| ReactNode | Yes |

### `iconSize`

Size of the dropdown icon.

| <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> |
| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
| IconSize | No | IconSize.Md |

### `dropdownIconStyle`

Custom styles for the dropdown icon.

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

### `style`

Custom styles for the main container.

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

## Usage

```javascript
// Replace import with relative path.
import React from 'react';
import { Text } from 'react-native';
import PickerBase from 'app/component-library/components/Pickers/PickerBase';
import { IconSize } from 'app/component-library/components/Icons/Icon';

const ExampleComponent = () => (
<PickerBase
onPress={() => console.log('Picker pressed')}
iconSize={IconSize.Lg}
dropdownIconStyle={{ marginLeft: 20 }}
style={{ backgroundColor: 'lightgray' }}
>
<Text>Select an option</Text>
</PickerBase>
);

<PickerBase onPress={ONPRESS_CALLBACK}>
<SampleContent />
</PickerBase>;
export default ExampleComponent;
```

## Notes

- The component uses a `TouchableOpacity` as its base, providing press feedback.
- It automatically includes a dropdown icon (ArrowDown) to the right of the content.
- The component is designed to be flexible and can be customized using the `style` and `dropdownIconStyle` props.
- The dropdown icon color is determined by the theme's `colors.icon.default`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`PickerBase should render correctly 1`] = `
<TouchableOpacity
onPress={[Function]}
onPress={[MockFunction]}
style={
{
"alignItems": "center",
Expand All @@ -15,7 +15,9 @@ exports[`PickerBase should render correctly 1`] = `
}
}
>
<View />
<Text>
Test Content
</Text>
<SvgMock
color="#141618"
height={20}
Expand Down
Loading