Skip to content

Commit

Permalink
Component/4507 button primary (MetaMask#4523)
Browse files Browse the repository at this point in the history
* [WIP] Push up icon changes

* Introduce assets

* Create script to generate Icon types based on icons in assets directory

* Change Icon component to use SVGs. Create script to auto generate SVG types. Use an svg transformer to allow for importing SVGs as components

* Update ignores on Icon.assets

* Update generate-assets script to add ignores

* Remove icon test - SVG mock issue. Update Icon docs with color prop

* Fix useStyles for other components

* Create BaseButton component

* Update Icon README

* Clean up BaseButton

* Create ButtomPrimary

* Add more stories to BaseButton

* Finalize ButtonPrimary

* Simplify ButtonPrimary types

* Clean up BaseButton

* Update test

* Update ButtonPrimary

* Add variant

* Update BaseButton type to extend TouchableOpacity

* Add pressed color

* Clean up types
  • Loading branch information
Cal-L authored Jun 28, 2022
1 parent 0e6dfc6 commit 6989b6b
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* eslint-disable no-console */
import React from 'react';
import { storiesOf } from '@storybook/react-native';
import { select, text } from '@storybook/addon-knobs';
import { IconName } from '../Icon';
import { BaseButtonSize } from '../BaseButton';
import ButtonPrimary from './ButtonPrimary';
import { ButtonPrimaryVariant } from './ButtonPrimary.types';

storiesOf('Component Library / ButtonPrimary', module)
.addDecorator((getStory) => getStory())
.add('Default', () => {
const groupId = 'Props';
const sizeSelector = select(
'size',
BaseButtonSize,
BaseButtonSize.Md,
groupId,
);
const iconSelector = select(
'icon',
IconName,
IconName.AddSquareFilled,
groupId,
);
const labelSelector = text('label', 'Click Me!', groupId);

return (
<ButtonPrimary
icon={iconSelector}
size={sizeSelector}
label={labelSelector}
onPress={() => console.log("I'm clicked!")}
variant={ButtonPrimaryVariant.Normal}
/>
);
})
.add('Without icon', () => (
<ButtonPrimary
size={BaseButtonSize.Md}
label={'Click Me!'}
onPress={() => console.log("I'm clicked!")}
variant={ButtonPrimaryVariant.Normal}
/>
))
.add('Danger variant', () => (
<ButtonPrimary
size={BaseButtonSize.Md}
label={'Click Me!'}
onPress={() => console.log("I'm clicked!")}
variant={ButtonPrimaryVariant.Danger}
/>
));
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { StyleSheet, ViewStyle } from 'react-native';
import { Theme } from '../../../util/theme/models';
import {
ButtonPrimaryStyleSheet,
ButtonPrimaryStyleSheetVars,
ButtonPrimaryVariant,
} from './ButtonPrimary.types';

/**
* Style sheet function for ButtonPrimary component.
*
* @param params Style sheet params.
* @param params.vars Inputs that the style sheet depends on.
* @returns StyleSheet object.
*/
const styleSheet = (params: {
theme: Theme;
vars: ButtonPrimaryStyleSheetVars;
}): ButtonPrimaryStyleSheet => {
const { vars, theme } = params;
const { colors } = theme;
const { style, variant, pressed } = vars;
let backgroundColor: string;
switch (variant) {
case ButtonPrimaryVariant.Normal:
backgroundColor = pressed
? colors.primary.alternative
: colors.primary.default;
break;
case ButtonPrimaryVariant.Danger:
backgroundColor = pressed
? colors.error.alternative
: colors.error.default;
break;
}

return StyleSheet.create({
base: Object.assign(
{
backgroundColor,
} as ViewStyle,
style,
) as ViewStyle,
});
};

export default styleSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { shallow } from 'enzyme';
import { IconName } from '../Icon';
import { BaseButtonSize } from '../BaseButton';
import ButtonPrimary from './ButtonPrimary';
import { ButtonPrimaryVariant } from './ButtonPrimary.types';

describe('ButtonPrimary', () => {
it('should render correctly', () => {
const wrapper = shallow(
<ButtonPrimary
icon={IconName.BankFilled}
size={BaseButtonSize.Md}
label={'Click me!'}
onPress={() => null}
variant={ButtonPrimaryVariant.Normal}
/>,
);
expect(wrapper).toMatchSnapshot();
});
});
61 changes: 61 additions & 0 deletions app/component-library/components/ButtonPrimary/ButtonPrimary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-disable react/prop-types */
import React, { useCallback, useMemo, useState } from 'react';
import { GestureResponderEvent } from 'react-native';
import { useStyles } from '../../hooks';
import BaseButton from '../BaseButton';
import styleSheet from './ButtonPrimary.styles';
import {
ButtonPrimaryProps,
ButtonPrimaryVariant,
} from './ButtonPrimary.types';

const ButtonPrimary = ({
style,
onPressIn,
onPressOut,
variant,
...props
}: ButtonPrimaryProps): JSX.Element => {
const [pressed, setPressed] = useState(false);
const { styles, theme } = useStyles(styleSheet, { style, variant, pressed });
const labelColor = useMemo(() => {
let color: string;
switch (variant) {
case ButtonPrimaryVariant.Normal:
color = theme.colors.primary.inverse;
break;
case ButtonPrimaryVariant.Danger:
color = theme.colors.error.inverse;
break;
}
return color;
}, [theme, variant]);

const triggerOnPressedIn = useCallback(
(e: GestureResponderEvent) => {
setPressed(true);
onPressIn?.(e);
},
[setPressed, onPressIn],
);

const triggerOnPressedOut = useCallback(
(e: GestureResponderEvent) => {
setPressed(false);
onPressOut?.(e);
},
[setPressed, onPressOut],
);

return (
<BaseButton
style={styles.base}
labelColor={labelColor}
onPressIn={triggerOnPressedIn}
onPressOut={triggerOnPressedOut}
{...props}
/>
);
};

export default ButtonPrimary;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ViewStyle } from 'react-native';
import { BaseButtonProps } from '../BaseButton';

/**
* ButtonPrimary variant
*/
export enum ButtonPrimaryVariant {
Normal = 'Normal',
Danger = 'Danger',
}

/**
* ButtonPrimary component props.
*/
export interface ButtonPrimaryProps
extends Omit<BaseButtonProps, 'labelColor'> {
variant: ButtonPrimaryVariant;
}

/**
* ButtonPrimary component style sheet.
*/
export interface ButtonPrimaryStyleSheet {
base: ViewStyle;
}

/**
* Style sheet input parameters.
*/
export interface ButtonPrimaryStyleSheetVars
extends Pick<ButtonPrimaryProps, 'style' | 'variant'> {
pressed: boolean;
}
47 changes: 47 additions & 0 deletions app/component-library/components/ButtonPrimary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# ButtonPrimary

ButtonPrimary is used for primary call to actions.

## Props

This component extends `ViewProps` from React Native's [View Component](https://reactnative.dev/docs/view).

### `label`

Button text.

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

### `size`

Enum to select between size variants.

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :----------------------------------------------------- | :------------------------------------------------------ |
| [BaseButtonSize](../BaseButton/BaseButton.types.ts#L4) | Yes |

### `onPress`

Function to trigger when pressing the button.

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

### `iconName`

Icon to use.

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| [IconName](../Icon/Icon.types.ts#53) | False |

### `variant`

Enum use to select between variants.

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| [ButtonPrimaryVariant](./ButtonPrimary.types.ts#L7) | Yes |
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ButtonPrimary should render correctly 1`] = `
<BaseButton
icon="BankFilled"
label="Click me!"
labelColor="#FCFCFC"
onPress={[Function]}
onPressIn={[Function]}
onPressOut={[Function]}
size="40"
style={
Object {
"backgroundColor": "#037DD6",
}
}
/>
`;
1 change: 1 addition & 0 deletions app/component-library/components/ButtonPrimary/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ButtonPrimary';
2 changes: 2 additions & 0 deletions storybook/storyLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function loadStories() {
require('../app/component-library/components/AccountAvatar/AccountAvatar.stories');
require('../app/component-library/components/BaseAvatar/BaseAvatar.stories');
require('../app/component-library/components/BaseButton/BaseButton.stories');
require('../app/component-library/components/ButtonPrimary/ButtonPrimary.stories');
require('../app/component-library/components/BaseText/BaseText.stories');
require('../app/component-library/components/Icon/Icon.stories');
require('../app/component-library/components/Link/Link.stories');
Expand All @@ -32,6 +33,7 @@ const stories = [
'../app/component-library/components/AccountAvatar/AccountAvatar.stories',
'../app/component-library/components/BaseAvatar/BaseAvatar.stories',
'../app/component-library/components/BaseButton/BaseButton.stories',
'../app/component-library/components/ButtonPrimary/ButtonPrimary.stories',
'../app/component-library/components/BaseText/BaseText.stories',
'../app/component-library/components/Icon/Icon.stories',
'../app/component-library/components/Link/Link.stories',
Expand Down

0 comments on commit 6989b6b

Please sign in to comment.