Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 1 addition & 1 deletion public/app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/style.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/components/Link.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default function Link({ state, meeting, setState }) {
href={formatUrl({ ...state.input, meeting: meeting.slug })}
onClick={e => {
e.preventDefault();
e.stopPropagation();
setState({
...state,
input: {
Expand Down
59 changes: 32 additions & 27 deletions src/components/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {
import Button from './Button';
import Link from './Link';

export default function Table({ state, setState, filteredSlugs, inProgress }) {
export default function Table({
state,
setState,
filteredSlugs = [],
inProgress = [],
listButtons = false,
}) {
const meetingsPerPage = 10;
const [limit, setLimit] = useState(meetingsPerPage);
const [showInProgress, setShowInProgress] = useState(false);
Expand All @@ -31,27 +37,23 @@ export default function Table({ state, setState, filteredSlugs, inProgress }) {
if (meeting.isInPerson) {
buttons.push({
className: 'in-person',
href: settings.show.listButtons
? formatDirectionsUrl(meeting)
: undefined,
href: listButtons ? formatDirectionsUrl(meeting) : undefined,
icon: 'geo',
text: meeting.address,
});
}
if (meeting.conference_provider) {
buttons.push({
className: 'online',
href: settings.show.listButtons ? meeting.conference_url : undefined,
href: listButtons ? meeting.conference_url : undefined,
icon: 'camera',
text: meeting.conference_provider,
});
}
if (meeting.conference_phone) {
buttons.push({
className: 'online',
href: settings.show.listButtons
? `tel:${meeting.conference_phone}`
: undefined,
href: listButtons ? `tel:${meeting.conference_phone}` : undefined,
icon: 'phone',
text: strings.phone,
});
Expand All @@ -70,13 +72,13 @@ export default function Table({ state, setState, filteredSlugs, inProgress }) {
))}
</div>
);
} else if (key === 'distance') {
return meeting.distance ? (
} else if (key === 'distance' && meeting.distance) {
return (
<>
{meeting.distance}
<small className="ms-1 text-muted">{settings.distance_unit}</small>
</>
) : null;
);
} else if (key === 'location_group') {
return meeting.isInPerson ? meeting.location : meeting.group;
} else if (key === 'name' && meeting.slug) {
Expand All @@ -95,7 +97,7 @@ export default function Table({ state, setState, filteredSlugs, inProgress }) {
strings.appointment
);
}
return meeting[key];
return null;
};

const Row = ({ slug }) => {
Expand All @@ -104,7 +106,7 @@ export default function Table({ state, setState, filteredSlugs, inProgress }) {
<tr
className="d-block d-md-table-row"
onClick={() => {
if (settings.show.listButtons) return;
if (listButtons) return;
setState({
...state,
input: {
Expand All @@ -128,7 +130,7 @@ export default function Table({ state, setState, filteredSlugs, inProgress }) {
<div className="row">
<table
className={cx('table table-striped flex-grow-1 my-0', {
'clickable-rows': !settings.show.listButtons,
'clickable-rows': !listButtons,
})}
>
<thead>
Expand All @@ -141,30 +143,33 @@ export default function Table({ state, setState, filteredSlugs, inProgress }) {
</tr>
</thead>
{!!inProgress.length && (
<tbody className="tsml-in-progress">
<tbody className="border-0">
{showInProgress ? (
inProgress.map((slug, index) => <Row slug={slug} key={index} />)
) : (
<tr>
<td colSpan={columns.length}>
<a
onClick={() => setShowInProgress(true)}
className="d-block text-center py-3 py-md-1"
>
{inProgress.length === 1
? strings.in_progress_single
: strings.in_progress_multiple.replace(
'%count%',
inProgress.length
)}
</a>
<td className="p-0" colSpan={columns.length}>
<div className="alert alert-warning m-0 opacity-50 p-2 rounded-0">
<button
onClick={() => setShowInProgress(true)}
className="alert-link bg-transparent border-0 d-block fw-normal mx-auto py-2 py-md-1 text-center text-decoration-underline"
>
{inProgress.length === 1
? strings.in_progress_single
: strings.in_progress_multiple.replace(
'%count%',
inProgress.length
)}
</button>
</div>
</td>
</tr>
)}
</tbody>
)}
<InfiniteScroll
element="tbody"
className="border-0"
loadMore={() => {
setLimit(limit + meetingsPerPage);
}}
Expand Down
138 changes: 138 additions & 0 deletions src/components/Table.spec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { fireEvent, render, screen } from '@testing-library/react';
import moment from 'moment-timezone';

import { strings } from '../helpers';
import Table from './Table';

describe('<Table />', () => {
const mockState = {
capabilities: {
region: true,
location: true,
distance: true,
},
meetings: {
foo: {
slug: 'foo',
name: 'Foo',
location: 'Bar',
address: '123 Main St',
distance: 1,
types: ['M'],
isInPerson: true,
isOnline: true,
formatted_address: '123 Main St, Anytown, NY 12345, USA',
conference_url: 'https://zoom.us/d/12356789',
conference_provider: 'Zoom',
conference_phone: '+12121234123',
regions: ['Anytown'],
start: moment(),
},
bar: {
slug: 'bar',
name: 'Bar',
location: 'Baz',
types: ['M'],
isInPerson: false,
isOnline: false,
formatted_address: 'Anytown, NY 12345, USA',
regions: ['Anytown'],
},
},
};

const filteredSlugs = Object.keys(mockState.meetings);

it('renders with clickable rows', () => {
const mockSetState = jest.fn();
render(
<Table
state={mockState}
setState={mockSetState}
filteredSlugs={filteredSlugs}
/>
);

//clickable rows
const rows = screen.getAllByRole('row');
rows.forEach(row => fireEvent.click(row));
expect(mockSetState).toHaveBeenCalledTimes(filteredSlugs.length);
});

it('renders with clickable listButtons', () => {
const mockSetState = jest.fn();
render(
<Table
state={mockState}
setState={mockSetState}
filteredSlugs={filteredSlugs}
listButtons={true}
/>
);

//clickable links
const anchors = screen.getAllByRole('link');
anchors.forEach(anchor => fireEvent.click(anchor));
expect(mockSetState).toHaveBeenCalledTimes(filteredSlugs.length);

//unclickable rows
const rows = screen.getAllByRole('row');
rows.forEach(row => fireEvent.click(row));
expect(mockSetState).toHaveBeenCalledTimes(filteredSlugs.length);
});

it('displays single meeting in progress', () => {
const inProgress = [filteredSlugs[0]];

render(
<Table
state={mockState}
setState={jest.fn()}
filteredSlugs={filteredSlugs}
inProgress={inProgress}
/>
);

//count rows (data rows + header + show in progress)
expect(screen.getAllByRole('row')).toHaveLength(filteredSlugs.length + 2);

const button = screen.getByRole('button');
expect(button).toHaveTextContent(strings.in_progress_single);

fireEvent.click(button);
expect(button).not.toBeVisible();

//recount rows
expect(screen.getAllByRole('row')).toHaveLength(
filteredSlugs.length + inProgress.length + 1
);
});

it('displays multiple meetings in progress', () => {
const inProgress = [...filteredSlugs];
render(
<Table
state={mockState}
setState={jest.fn()}
filteredSlugs={filteredSlugs}
inProgress={inProgress}
/>
);

//count rows (data rows + header + show in progress)
expect(screen.getAllByRole('row')).toHaveLength(filteredSlugs.length + 2);

const button = screen.getByRole('button');
expect(button).toHaveTextContent(
strings.in_progress_multiple.replace('%count%', inProgress.length)
);

fireEvent.click(button);
expect(button).not.toBeVisible();

//recount rows
expect(screen.getAllByRole('row')).toHaveLength(
filteredSlugs.length + inProgress.length + 1
);
});
});
1 change: 1 addition & 0 deletions src/components/TsmlUI.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export default function TsmlUI({ json, mapbox, timezone }) {
setState={setState}
filteredSlugs={filteredSlugs}
inProgress={inProgress}
listButtons={settings.show.listButtons}
/>
)}
{filteredSlugs && state.input.view === 'map' && (
Expand Down
23 changes: 0 additions & 23 deletions src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -269,29 +269,6 @@ div.tsml-ui {
border-bottom: 1px solid $gray-400;
}

tbody {
border: 0 !important;
&.tsml-in-progress {
tr {
background-color: tint-color($yellow, 95%) !important;
border-bottom-color: $yellow-200;
td {
border-bottom-color: $yellow-200 !important;
}
a {
color: $yellow-700;
cursor: pointer;
&:hover {
color: $yellow-800 !important;
}
}
&:nth-of-type(odd) {
background-color: tint-color($yellow, 90%);
}
}
}
}

.distance {
text-align: right;
}
Expand Down