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: expandable section for use in confirmation pages #11528

Merged
merged 17 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions .storybook/storybook.requires.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ const getStories = () => {
"./app/component-library/base-components/TagBase/TagBase.stories.tsx": require("../app/component-library/base-components/TagBase/TagBase.stories.tsx"),
"./app/component-library/components-temp/TagColored/TagColored.stories.tsx": require("../app/component-library/components-temp/TagColored/TagColored.stories.tsx"),
"./app/component-library/components-temp/KeyValueRow/KeyValueRow.stories.tsx": require("../app/component-library/components-temp/KeyValueRow/KeyValueRow.stories.tsx"),
"./app/components/Views/confirmations/components/UI/InfoRow/InfoRow.stories.tsx": require("../app/components/Views/confirmations/components/UI/InfoRow/InfoRow.stories.tsx"),
"./app/components/Views/confirmations/components/UI/ExpandableSection/ExpandableSection.stories.tsx": require("../app/components/Views/confirmations/components/UI/ExpandableSection/ExpandableSection.stories.tsx"),
};
};

Expand Down
2 changes: 1 addition & 1 deletion app/components/Views/confirmations/Confirm/Confirm.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import { View } from 'react-native';

import BottomModal from '../../../../components/UI/BottomModal';
import { useTheme } from '../../../../util/theme';
import BottomModal from '../components/UI/BottomModal';
import Footer from '../components/Confirm/Footer';
import Title from '../components/Confirm/Title';
import useConfirmationRedesignEnabled from '../hooks/useConfirmationRedesignEnabled';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React from 'react';
import { Text, View } from 'react-native';
import { TransactionType } from '@metamask/transaction-controller';

Expand All @@ -20,11 +20,8 @@ const Title = () => {
const { approvalRequest } = useApprovalRequest();
const { colors } = useTheme();

const title = getTitle(approvalRequest?.type);
const styles = createStyles(colors);
const title = useMemo(
() => getTitle(approvalRequest?.type),
[approvalRequest?.type],
);

return (
<View style={styles.titleContainer}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { Text, View } from 'react-native';

import renderWithProvider from '../../../util/test/renderWithProvider';
import renderWithProvider from '../../../../../../util/test/renderWithProvider';
import BottomModal from '.';

describe('BottomModal', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { ReactChild } from 'react';
import React, { ReactNode } from 'react';
import Modal from 'react-native-modal';
import { StyleSheet } from 'react-native';

import { useTheme } from '../../../util/theme';
import { useTheme } from '../../../../../../util/theme';

interface BottomModalProps {
children: ReactChild;
children: ReactNode;
onClose?: () => void;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { storiesOf } from '@storybook/react-native';
import { Text, View } from 'react-native';

import InfoSection from '../InfoRow/InfoSection';
import InfoRow from '../InfoRow';
import ExpandableSection from './ExpandableSection';

storiesOf('Confirmations / ExpandableSection', module)
.addDecorator((getStory) => getStory())
.add('Default', () => (
<ExpandableSection
content={
<View>
<Text>Open</Text>
</View>
}
modalContent={
<InfoSection>
<InfoRow label="label-Key">Value-Text</InfoRow>
</InfoSection>
}
modalTitle={'Title'}
/>
));
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { Text, View } from 'react-native';
import { fireEvent, render } from '@testing-library/react-native';

import InfoSection from '../InfoRow/InfoSection';
import InfoRow from '../InfoRow';
import ExpandableSection from './ExpandableSection';

describe('ExpandableSection', () => {
it('should match snapshot for simple ExpandableSection', async () => {
const container = render(
<ExpandableSection
content={
<View>
<Text>Open</Text>
</View>
}
modalContent={
<InfoSection>
<InfoRow label="label-Key">Value-Text</InfoRow>
</InfoSection>
}
modalTitle={'Title'}
/>,
);
expect(container).toMatchSnapshot();
});

it('should display default content', async () => {
const { getByText } = render(
<ExpandableSection
content={
<View>
<Text>Open</Text>
</View>
}
modalContent={
<InfoSection>
<InfoRow label="label-Key">Value-Text</InfoRow>
</InfoSection>
}
modalTitle={'Title'}
/>,
);
expect(getByText('Open')).toBeDefined();
});

it('should open modal when right icon is pressed', async () => {
const { getByTestId, getByText } = render(
<ExpandableSection
content={
<View>
<Text>Open</Text>
</View>
}
modalContent={
<InfoSection>
<InfoRow label="label-Key">Value-Text</InfoRow>
</InfoSection>
}
modalTitle={'Title'}
/>,
);
expect(getByText('Open')).toBeDefined();
fireEvent.press(getByTestId('openButtonTestId'));
expect(getByText('Value-Text')).toBeDefined();
fireEvent.press(getByTestId('closeButtonTestId'));
expect(getByText('Open')).toBeDefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { useState } from 'react';
import { Text, View } from 'react-native';

import ButtonIcon, {
ButtonIconSizes,
} from '../../../../../../component-library/components/Buttons/ButtonIcon';
import {
IconColor,
IconName,
} from '../../../../../../component-library/components/Icons/Icon';
import { useTheme } from '../../../../../../util/theme';
import createStyles from './style';
jpuri marked this conversation as resolved.
Show resolved Hide resolved
import BottomModal from '../BottomModal';

interface ExpandableSectionProps {
content: React.ReactNode;
jpuri marked this conversation as resolved.
Show resolved Hide resolved
modalContent: React.ReactNode;
jpuri marked this conversation as resolved.
Show resolved Hide resolved
modalTitle: string;
openButtonTestId?: string;
closeButtonTestId?: string;
}

const ExpandableSection = ({
content,
modalContent,
modalTitle,
openButtonTestId,
closeButtonTestId,
}: ExpandableSectionProps) => {
const { colors } = useTheme();
const styles = createStyles(colors);
jpuri marked this conversation as resolved.
Show resolved Hide resolved
const [expanded, setExpanded] = useState(false);

return (
<View>
<View style={styles.container}>
{content}
<ButtonIcon
iconColor={IconColor.Muted}
size={ButtonIconSizes.Sm}
onPress={() => setExpanded(true)}
iconName={IconName.ArrowRight}
testID={openButtonTestId ?? 'openButtonTestId'}
/>
</View>
{expanded && (
<BottomModal>
<View style={styles.modalContainer}></View>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<ButtonIcon
iconColor={IconColor.Default}
size={ButtonIconSizes.Sm}
onPress={() => setExpanded(false)}
iconName={IconName.ArrowLeft}
testID={closeButtonTestId ?? 'closeButtonTestId'}
/>
<Text style={styles.modalTitle}>{modalTitle}</Text>
</View>
{modalContent}
</View>
</BottomModal>
)}
</View>
);
};

export default ExpandableSection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ExpandableSection should match snapshot for simple ExpandableSection 1`] = `
<View>
<View
style={
{
"alignItems": "center",
"backgroundColor": "#ffffff",
"borderColor": "#bbc0c566",
"borderRadius": 8,
"borderWidth": 1,
"display": "flex",
"flexDirection": "row",
"justifyContent": "space-between",
"padding": 16,
}
}
>
<View>
<Text>
Open
</Text>
</View>
<TouchableOpacity
accessible={true}
activeOpacity={1}
disabled={false}
onPress={[Function]}
onPressIn={[Function]}
onPressOut={[Function]}
style={
{
"alignItems": "center",
"borderRadius": 8,
"height": 24,
"justifyContent": "center",
"opacity": 1,
"width": 24,
}
}
testID="openButtonTestId"
>
<SvgMock
color="#9fa6ae"
height={16}
name="ArrowRight"
style={
{
"height": 16,
"width": 16,
}
}
width={16}
/>
</TouchableOpacity>
</View>
</View>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ExpandableSection';
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { StyleSheet } from 'react-native';

import { Colors } from '../../../../../../util/theme/models';
import { fontStyles } from '../../../../../../styles/common';

const createStyles = (colors: Colors) =>
StyleSheet.create({
container: {
backgroundColor: colors.background.default,
borderColor: colors.border.muted,
borderRadius: 8,
borderWidth: 1,
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 16,
},
// eslint-disable-next-line react-native/no-color-literals
modalContainer: {
backgroundColor: '#414141',
minHeight: '100%',
},
modalContent: {
backgroundColor: colors.background.alternative,
paddingTop: 24,
paddingBottom: 34,
paddingHorizontal: 16,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
},
modalHeader: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
paddingBottom: 16,
},
modalTitle: {
color: colors.text.default,
...fontStyles.bold,
fontSize: 14,
fontWeight: '700',
width: '90%',
textAlign: 'center',
},
});

export default createStyles;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { storiesOf } from '@storybook/react-native';
import { StyleProp, Text, TextStyle, View } from 'react-native';

import InfoRow from './InfoRow';
import InfoSection from './InfoSection';

const style = {
container: { padding: 8 },
title: { marginTop: 20, fontSize: 20, fontWeight: '700' },
};

storiesOf('Confirmations / InfoRow', module)
.addDecorator((getStory) => getStory())
.add('Default', () => (
<View style={style.container}>
<Text style={style.title as StyleProp<TextStyle>}>Simple Info Row</Text>
<InfoSection>
<InfoRow label="label-Key">Value-Text</InfoRow>
</InfoSection>
<Text style={style.title as StyleProp<TextStyle>}>Value wrapped</Text>
<InfoSection>
<InfoRow label="label-Key">
Value-Text Value-Text Value-Text Value-Text Value-Text Value-Text
Value-Text Value-Text Value-Text Value-Text Value-Text Value-Text
Value-Text Value-Text Value-Text Value-Text
</InfoRow>
</InfoSection>
</View>
));
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { render } from '@testing-library/react-native';

import InfoRow from './index';

describe('InfoRow', () => {
it('should match snapshot for simple text value', async () => {
const container = render(<InfoRow label="label-Key">Value-Text</InfoRow>);
expect(container).toMatchSnapshot();
});
});
Loading
Loading