Skip to content

Commit d3f052f

Browse files
authored
Merge pull request #62 from CruGlobal/MPDX-6971
MPDX-6971: Improve Contact Details Tab Bar UI
2 parents 5e9ee72 + 251ebe0 commit d3f052f

14 files changed

Lines changed: 167 additions & 127 deletions
Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { AppBar, Box, Tab, Tabs } from '@material-ui/core';
1+
import { Box, styled, Tab, Tabs } from '@material-ui/core';
22
import React from 'react';
33
import { useTranslation } from 'react-i18next';
4+
import theme from '../../../theme';
45
import { ContactDetailsHeader } from './ContactDetailsHeader/ContactDetailsHeader';
56

67
interface Props {
@@ -9,27 +10,74 @@ interface Props {
910
onClose: () => void;
1011
}
1112

13+
const ContactDetailsWrapper = styled(Box)(({}) => ({
14+
width: '100%',
15+
}));
16+
17+
const ContactTabsWrapper = styled(Box)(({}) => ({
18+
width: '100%',
19+
backgroundColor: 'transparent',
20+
boxShadow: 'none',
21+
borderBottom: '1px solid #DCDCDC',
22+
}));
23+
24+
const ContactTabs = styled(Tabs)(({}) => ({
25+
width: '100%',
26+
minHeight: 40,
27+
indicator: {
28+
display: 'flex',
29+
'& > span': {
30+
width: '100%',
31+
height: 2,
32+
backgroundColor: '#FFCF07',
33+
},
34+
},
35+
}));
36+
37+
const ContactTab = styled(Tab)(({}) => ({
38+
textTransform: 'none',
39+
minWidth: 64,
40+
minHeight: 40,
41+
marginRight: theme.spacing(1),
42+
color: theme.palette.text.primary,
43+
opacity: 0.75,
44+
'&:hover': { opacity: 1 },
45+
}));
46+
1247
export const ContactDetails: React.FC<Props> = ({
1348
accountListId,
1449
contactId,
1550
}: Props) => {
1651
const { t } = useTranslation();
1752

53+
const [selectedTabIndex, setSelectedTabIndex] = React.useState(0);
54+
55+
const handleChange = (
56+
_event: React.ChangeEvent<Record<string, unknown>>,
57+
newIndex: number,
58+
) => {
59+
setSelectedTabIndex(newIndex);
60+
};
61+
1862
return (
19-
<Box position="fixed">
63+
<ContactDetailsWrapper>
2064
<ContactDetailsHeader
2165
accountListId={accountListId}
2266
contactId={contactId}
2367
/>
24-
<AppBar position="static">
25-
<Tabs>
26-
<Tab label={t('Tasks')} />
27-
<Tab label={t('Donations')} />
28-
<Tab label={t('Referrals')} />
29-
<Tab label={t('Contact Details')} />
30-
<Tab label={t('Notes')} />
31-
</Tabs>
32-
</AppBar>
33-
</Box>
68+
<ContactTabsWrapper>
69+
<ContactTabs
70+
value={selectedTabIndex}
71+
onChange={handleChange}
72+
TabIndicatorProps={{ children: <span /> }}
73+
>
74+
<ContactTab label={t('Tasks')} />
75+
<ContactTab label={t('Donations')} />
76+
<ContactTab label={t('Referrals')} />
77+
<ContactTab label={t('Contact Details')} />
78+
<ContactTab label={t('Notes')} />
79+
</ContactTabs>
80+
</ContactTabsWrapper>
81+
</ContactDetailsWrapper>
3482
);
3583
};

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactDetailsHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const ContactDetailsHeader: React.FC<Props> = ({
7373
const { t } = useTranslation();
7474

7575
return (
76-
<Box style={{ padding: 24 }}>
76+
<Box style={{ padding: 24, backgroundColor: 'transparent' }}>
7777
<HeaderBar>
7878
<ContactAvatar src={data?.contact?.avatar || ''} />
7979
<HeaderBarContactWrap>

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderAddressSection.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ export const Default = (): ReactElement => {
2020
};
2121

2222
export const Loading = (): ReactElement => {
23-
return <ContactHeaderAddressSection loading={true} contact={null} />;
23+
return <ContactHeaderAddressSection loading={true} contact={undefined} />;
2424
};

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderAddressSection.test.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,20 @@ const contact = gqlMock<ContactDetailsHeaderFragment>(
1414
describe('ContactHeaderAddressSection', () => {
1515
it('should show loading state', async () => {
1616
const { queryByText } = render(
17-
<ContactHeaderAddressSection loading={true} contact={null} />,
17+
<ContactHeaderAddressSection loading={true} contact={undefined} />,
1818
);
1919

20-
expect(queryByText(contact.primaryAddress.street)).toBeNull();
20+
expect(queryByText(contact.primaryAddress?.street || '')).toBeNull();
2121
});
2222

2323
it('should render with contact details', async () => {
2424
const { queryByText } = render(
2525
<ContactHeaderAddressSection loading={false} contact={contact} />,
2626
);
2727

28-
expect(queryByText(contact.greeting)).toBeInTheDocument();
29-
expect(queryByText(contact.primaryAddress.street)).toBeInTheDocument();
28+
expect(queryByText(contact.greeting || '')).toBeInTheDocument();
29+
expect(
30+
queryByText(contact.primaryAddress?.street || ''),
31+
).toBeInTheDocument();
3032
});
3133
});

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderAddressSection.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export const ContactHeaderAddressSection = ({
3232
}: Props): ReactElement => {
3333
const { t } = useTranslation();
3434

35+
const greeting = contact?.greeting;
36+
const primaryAddress = contact?.primaryAddress;
37+
3538
if (loading) {
3639
return (
3740
<ContactHeaderSection icon={<LocationIcon />}>
@@ -40,13 +43,10 @@ export const ContactHeaderAddressSection = ({
4043
<TextSkeleton variant="text" />
4144
</ContactHeaderSection>
4245
);
43-
} else if (contact) {
44-
const {
45-
greeting,
46-
primaryAddress: { street, city, state, postalCode } = {},
47-
} = contact;
46+
} else if (greeting && primaryAddress) {
47+
const { street, city, state, postalCode } = primaryAddress;
4848

49-
if (!!greeting && !!street && !!city && !!state && !!postalCode) {
49+
if (street && city && state && postalCode) {
5050
return (
5151
<ContactHeaderSection icon={<LocationIcon />}>
5252
<Typography variant="subtitle1">{greeting}</Typography>

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderEmailSection.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ export const Default = (): ReactElement => {
2020
};
2121

2222
export const Loading = (): ReactElement => {
23-
return <ContactHeaderEmailSection loading={true} contact={null} />;
23+
return <ContactHeaderEmailSection loading={true} contact={undefined} />;
2424
};

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderEmailSection.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ const contact = gqlMock<ContactDetailsHeaderFragment>(
1414
describe('ContactHeaderEmailSection', () => {
1515
it('should show loading state', async () => {
1616
const { queryByText } = render(
17-
<ContactHeaderEmailSection loading={true} contact={null} />,
17+
<ContactHeaderEmailSection loading={true} contact={undefined} />,
1818
);
1919

2020
expect(
21-
queryByText(contact.primaryPerson.primaryEmailAddress.email),
21+
queryByText(contact.primaryPerson?.primaryEmailAddress?.email || ''),
2222
).toBeNull();
2323
});
2424

@@ -28,7 +28,7 @@ describe('ContactHeaderEmailSection', () => {
2828
);
2929

3030
expect(
31-
queryByText(contact.primaryPerson.primaryEmailAddress.email),
31+
queryByText(contact.primaryPerson?.primaryEmailAddress?.email || ''),
3232
).toBeInTheDocument();
3333
});
3434
});

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderEmailSection.tsx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,20 @@ export const ContactHeaderEmailSection = ({
2929
loading,
3030
contact,
3131
}: Props): ReactElement => {
32+
const email = contact?.primaryPerson?.primaryEmailAddress?.email;
33+
3234
if (loading) {
3335
return (
3436
<ContactHeaderSection icon={<EmailIcon />}>
3537
<TextSkeleton variant="text" />
3638
</ContactHeaderSection>
3739
);
38-
} else if (contact) {
39-
const {
40-
primaryPerson: { primaryEmailAddress: { email } = {} } = {},
41-
} = contact;
42-
43-
if (!!email) {
44-
return (
45-
<ContactHeaderSection icon={<EmailIcon />}>
46-
<Typography variant="subtitle1">{email}</Typography>
47-
</ContactHeaderSection>
48-
);
49-
}
40+
} else if (email) {
41+
return (
42+
<ContactHeaderSection icon={<EmailIcon />}>
43+
<Typography variant="subtitle1">{email}</Typography>
44+
</ContactHeaderSection>
45+
);
5046
}
5147

5248
return <ContactHeaderSection icon={<EmailIcon />} />;

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderPhoneSection.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ export const Default = (): ReactElement => {
2020
};
2121

2222
export const Loading = (): ReactElement => {
23-
return <ContactHeaderPhoneSection loading={true} contact={null} />;
23+
return <ContactHeaderPhoneSection loading={true} contact={undefined} />;
2424
};

src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderPhoneSection.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ const contact = gqlMock<ContactDetailsHeaderFragment>(
1414
describe('ContactHeaderPhoneSection', () => {
1515
it('should show loading state', async () => {
1616
const { queryByText } = render(
17-
<ContactHeaderPhoneSection loading={true} contact={null} />,
17+
<ContactHeaderPhoneSection loading={true} contact={undefined} />,
1818
);
1919

2020
expect(
21-
queryByText(contact.primaryPerson.primaryPhoneNumber.number),
21+
queryByText(contact.primaryPerson?.primaryPhoneNumber?.number || ''),
2222
).toBeNull();
2323
});
2424

@@ -28,7 +28,7 @@ describe('ContactHeaderPhoneSection', () => {
2828
);
2929

3030
expect(
31-
queryByText(contact.primaryPerson.primaryPhoneNumber.number),
31+
queryByText(contact.primaryPerson?.primaryPhoneNumber?.number || ''),
3232
).toBeInTheDocument();
3333
});
3434
});

0 commit comments

Comments
 (0)