Skip to content

Commit

Permalink
Merge pull request #55 from lshadler/lshadler/business-card-design
Browse files Browse the repository at this point in the history
  • Loading branch information
sumwatshade committed Dec 14, 2020
2 parents 1abf8a4 + fbfee4d commit 5dec682
Show file tree
Hide file tree
Showing 19 changed files with 680 additions and 17 deletions.
45 changes: 45 additions & 0 deletions components/BusinessCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';

import {BusinessInfoType} from '../test-data/data-types';
import {Card, Box, Button, withStyles} from '@material-ui/core';
import {useState} from 'react';

import BusinessCardDetail from './BusinessCardDetail';
import BusinessCardSummary from './BusinessCardSummary';

const ShowMoreButton = withStyles(({spacing}) => ({
root: {
width: '80%',
marginTop: spacing(2),
marginBottom: spacing(2),
},
}))(Button);

const BusinessCard = ({businessInfo}) => {
const [isExpanded, setIsExpanded] = useState(false);
const buttonText = isExpanded ? 'Show less...' : 'Show more...';


return (
<Box component={Card} m={3} p={1}
variant="outlined"
justifyContent="space-evenly"
alignItems="center"
display="flex"
minHeight="200px"
flexDirection="column">
<BusinessCardSummary businessInfo={businessInfo} />
<BusinessCardDetail expanded={isExpanded} businessInfo={businessInfo}/>
<ShowMoreButton
color="primary"
variant="contained"
onClick={() => setIsExpanded(!isExpanded)}>{buttonText}</ShowMoreButton>
</Box>
);
};

BusinessCard.propTypes = {
businessInfo: BusinessInfoType.isRequired,
};

export default BusinessCard;
22 changes: 22 additions & 0 deletions components/BusinessCardDetail.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import {bool} from 'prop-types';
import {BusinessInfoType} from '../test-data/data-types';
import {Box, Typography, Collapse} from '@material-ui/core';

const CardDetail = ({businessInfo, expanded}) => {
const {tags} = businessInfo;
return (
<Box component={Collapse} in={expanded} m={1}>
{tags ? (
<Typography>Tags: {tags.join(', ')}</Typography>
) : 'No details yet...'}
</Box>
);
};

CardDetail.propTypes = {
businessInfo: BusinessInfoType.isRequired,
expanded: bool.isRequired,
};

export default CardDetail;
29 changes: 29 additions & 0 deletions components/BusinessCardSummary.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';

import {BusinessInfoType} from '../test-data/data-types';
import {Typography} from '@material-ui/core';
import WebsiteLink from './WebsiteLink';

const CardSummary = ({businessInfo}) => {
const {name, website} = businessInfo;

return (
<>
<Typography variant="h4">{name}</Typography>
<Typography>
{website ? (
<WebsiteLink
href={website}
color="primary"
label='Website' />
) : ''}
</Typography>
</>
);
};

CardSummary.propTypes = {
businessInfo: BusinessInfoType.isRequired,
};

export default CardSummary;
23 changes: 23 additions & 0 deletions components/BusinessGrid.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';
import {BusinessInfoType} from '../test-data/data-types';
import {Grid} from '@material-ui/core';
import BusinessCard from './BusinessCard';

const BusinessGrid = ({places}) => {
return (
<Grid container spacing={1}>
{places.map((businessInfo, index) => (
<Grid key={index} item xs={12} sm={6} md={4} lg={3}>
<BusinessCard businessInfo={businessInfo} />
</Grid>
))}
</Grid>
);
};

BusinessGrid.propTypes = {
places: PropTypes.arrayOf(BusinessInfoType),
};

export default BusinessGrid;
12 changes: 7 additions & 5 deletions components/SearchableTable.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, {useState, useEffect} from 'react';
import {useDebounce} from '../utils/hooks';
import {TextField, Box} from '@material-ui/core';
import Fuse from 'fuse.js'; ;

import Table from './Table';
import BusinessGrid from './BusinessGrid';

import places from '../test-data/places';

Expand Down Expand Up @@ -46,6 +47,7 @@ const SearchableTable = ({dataSet}) => {
const [fuse, setFuse] = useState(null);
const [rows, setRows] = useState(dataSet);
const [search, setSearch] = useState('');
const debouncedSearchTerm = useDebounce(search, 300);

useEffect(() => {
const data = dataSet.sort(() => Math.random() - 0.5);
Expand All @@ -54,12 +56,12 @@ const SearchableTable = ({dataSet}) => {
}, [dataSet]);

useEffect(() => {
if (search.trim().length === 0) {
if (debouncedSearchTerm.trim().length === 0) {
setRows(dataSet);
} else if (fuse) {
setRows(fuse.search(search).map((r) => r.item));
setRows(fuse.search(debouncedSearchTerm).map((r) => r.item));
}
}, [search]);
}, [debouncedSearchTerm]);

return (
<Box>
Expand All @@ -75,7 +77,7 @@ const SearchableTable = ({dataSet}) => {
color="primary"/>
</Box>
<Box mt={3}>
<Table rows={rows} />
<BusinessGrid places={rows} />
</Box>
</Box>
);
Expand Down
2 changes: 1 addition & 1 deletion lint-staged.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
'.{js,jsx}': 'eslint --fix',
'*.{js,jsx}': 'eslint --fix',
};
8 changes: 3 additions & 5 deletions pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import React from 'react';
import Head from 'next/head';
import Maps from '../components/Maps';
import SearchableTable from '../components/SearchableTable';
import {Container, useTheme, Box, useMediaQuery} from '@material-ui/core';
import {Container, Box} from '@material-ui/core';
import CenteredTypography from '../components/CenteredTypography';

const Home = () => {
const theme = useTheme();
const isLargeScreen = useMediaQuery(theme.breakpoints.up('md'));
return (
<div>
<Head>
Expand All @@ -20,11 +18,11 @@ const Home = () => {
<CenteredTypography variant="subtitle1" color="secondary">Support local San Diego business through COVID-19</CenteredTypography>
<Box
display='flex'
flexDirection={isLargeScreen ? 'row' : 'column'}
flexDirection='column'
justifyContent="space-evenly"
alignItems="space-evenly">
<SearchableTable />
<Box mt={3} flexGrow={1} mx={isLargeScreen ? 3 : 0}>
<Box mt={3}>
<Maps />
</Box>
</Box>
Expand Down
11 changes: 11 additions & 0 deletions test-data/data-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {shape, string, number, array} from 'prop-types';

export const BusinessInfoType = shape({
name: string.isRequired,
iconType: string,
venmo: string,
website: string,
lat: number,
lng: number,
tags: array,
});
32 changes: 32 additions & 0 deletions test/components/BusinessCard.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import {render, fireEvent} from '@testing-library/react';

import BusinessCard from '../../components/BusinessCard';

describe('BusinessCard Component', () => {
let businessInfo;

beforeEach(() => {
businessInfo = {
name: 'Heartwork',
website: 'https://heartwork.com',
tags: ['coffee', 'test'],
};
});
test('renders in default state', () => {
const {asFragment} = render(<BusinessCard
businessInfo={businessInfo}/>);

expect(asFragment()).toMatchSnapshot();
});

test('click to expand', () => {
const {asFragment, getByText} = render(<BusinessCard
businessInfo={businessInfo} />);

expect(asFragment()).toMatchSnapshot();

fireEvent.click(getByText(/Show more\.\.\./));
expect(asFragment()).toMatchSnapshot();
});
});
33 changes: 33 additions & 0 deletions test/components/BusinessCardDetail.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import {render} from '@testing-library/react';

import BusinessCardDetail from '../../components/BusinessCardDetail';

describe('BusinessCardDetail Component', () => {
let businessInfo;

beforeEach(() => {
businessInfo = {
name: 'Heartwork',
website: 'https://heartwork.com',
tags: ['coffee', 'test'],
};
});
test('renders in default collapsed state', () => {
const {asFragment} = render(<BusinessCardDetail
businessInfo={businessInfo}
expanded={false}/>);

expect(asFragment()).toMatchSnapshot();
});

test('handles empty tags gracefully', () => {
delete businessInfo.tags;

const {asFragment} = render(<BusinessCardDetail
businessInfo={businessInfo}
expanded={false}/>);

expect(asFragment()).toMatchSnapshot();
});
});
31 changes: 31 additions & 0 deletions test/components/BusinessCardSummary.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import {render} from '@testing-library/react';

import BusinessCardSummary from '../../components/BusinessCardSummary';

describe('BusinessCardSummary Component', () => {
let businessInfo;

beforeEach(() => {
businessInfo = {
name: 'Heartwork',
website: 'https://heartwork.com',
tags: ['coffee', 'test'],
};
});
test('renders in default state', () => {
const {asFragment} = render(<BusinessCardSummary
businessInfo={businessInfo}/>);

expect(asFragment()).toMatchSnapshot();
});

test('handles empty website gracefully', () => {
delete businessInfo.website;

const {asFragment} = render(<BusinessCardSummary
businessInfo={businessInfo}/>);

expect(asFragment()).toMatchSnapshot();
});
});
21 changes: 21 additions & 0 deletions test/components/BusinessGrid.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import {render} from '@testing-library/react';

import BusinessGrid from '../../components/BusinessGrid';

describe('BusinessGrid Component', () => {
let places;

beforeEach(() => {
places = [{
name: 'Heartwork',
website: 'https://heartwork.com',
tags: ['coffee', 'test'],
}];
});
test('renders in default collapsed state', () => {
const {asFragment} = render(<BusinessGrid places={places}/>);

expect(asFragment()).toMatchSnapshot();
});
});
9 changes: 6 additions & 3 deletions test/components/NextMuiLink.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ jest.mock('clsx', () => () => {});

describe('NextMuiLink Component', () => {
test('renders in default state', () => {
const {asFragment} = render(<NextMuiLink href="/test"/>);
const {asFragment} = render(
<NextMuiLink href="/test">Test</NextMuiLink>);

expect(asFragment()).toMatchSnapshot();
});

test('renders in naked state', () => {
const {asFragment} = render(<NextMuiLink href="/test" naked={true}/>);
const {asFragment} = render(
<NextMuiLink href="/test" naked={true}>Test</NextMuiLink>);

expect(asFragment()).toMatchSnapshot();
});
Expand All @@ -26,7 +28,8 @@ describe('NextMuiLink Component', () => {
const href = {
pathname: '/test',
};
const {asFragment} = render(<NextMuiLink href={href} naked={true}/>);
const {asFragment} = render(
<NextMuiLink href={href} naked={true}>Test</NextMuiLink>);

expect(asFragment()).toMatchSnapshot();
});
Expand Down

1 comment on commit 5dec682

@vercel
Copy link

@vercel vercel bot commented on 5dec682 Dec 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.