Skip to content

Commit 0f2a427

Browse files
authored
Merge pull request #1594 from fedspendingtransparency/FDG-9970
FDG-9970 Data table - All Tables Selected
2 parents 16397fd + 041f126 commit 0f2a427

30 files changed

+667
-341
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
$cell-height: 2.125rem;
2+
3+
.tableNotice {
4+
position: absolute;
5+
top: calc($cell-height * 4);
6+
width: 100%;
7+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
import ChartTableDisplay from './data-preview-chart-table-display';
3+
import { render } from '@testing-library/react';
4+
5+
describe('Chart Table Display', () => {
6+
it('displays table', () => {
7+
const mockTable = <table></table>;
8+
const { getByRole } = render(<ChartTableDisplay table={mockTable} allTablesSelected={false} />);
9+
expect(getByRole('table')).toBeInTheDocument();
10+
});
11+
12+
it(`display "All Data Tables" banner when all tables is selected`, () => {
13+
const mockTable = <table></table>;
14+
const { getByText } = render(<ChartTableDisplay table={mockTable} allTablesSelected={true} />);
15+
expect(getByText('The current "All Data Tables" selection is for download only')).toBeInTheDocument();
16+
expect(getByText("To download the data, select the 'Download' button and choose the desired format.")).toBeInTheDocument();
17+
});
18+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React, { FunctionComponent } from 'react';
2+
import NotShownMessage from '../../dataset-data/table-section-container/not-shown-message/not-shown-message';
3+
import { getMessageForDefaultApiFilter, getMessageForUnmatchedUserFilter } from '../../filter-download-container/user-filter/user-filter';
4+
import { allTablesSelectedBody, emptyDataMessageBody } from '../../dataset-data/chart-table-toggle/chart-table-toggle';
5+
import TableNotice from './table-notice/table-notice';
6+
import EmptyTable from './empty-table/empty-table';
7+
import { tableNotice } from './data-preview-chart-table-display.module.scss';
8+
import { pxToNumber } from '../../../helpers/styles-helper/styles-helper';
9+
import { breakpointLg } from '../../../variables.module.scss';
10+
11+
const ChartTableDisplay: FunctionComponent = ({
12+
table,
13+
allTablesSelected,
14+
selectedTable,
15+
emptyData,
16+
unchartable,
17+
legend,
18+
selectedTab,
19+
chart,
20+
userFilterUnmatchedForDateRange,
21+
apiFilterDefault,
22+
pivotSelected,
23+
width,
24+
}) => {
25+
let emptyDataMessage = null;
26+
27+
const allTableHeading = 'The current "All Data Tables" selection is for download only';
28+
const allTableBody = "To download the data, select the 'Download' button and choose the desired format.";
29+
30+
if (allTablesSelected) {
31+
emptyDataMessage = <TableNotice heading={allTableHeading} bodyText={allTableBody} />;
32+
}
33+
//TODO: Add in additional cases for the table notice
34+
// else if (userFilterUnmatchedForDateRange) {
35+
// emptyDataMessage = getMessageForUnmatchedUserFilter(selectedTable);
36+
// } else if (apiFilterDefault) {
37+
// emptyDataMessage = getMessageForDefaultApiFilter(selectedTable);
38+
// } else if (emptyData) {
39+
// emptyDataMessage = <NotShownMessage heading="Change selections in order to preview data" bodyText={emptyDataMessageBody} />;
40+
// }
41+
42+
return (
43+
<>
44+
{emptyDataMessage ? (
45+
<>
46+
<EmptyTable mobileDisplay={width < pxToNumber(breakpointLg)} />
47+
<div className={tableNotice}>{emptyDataMessage}</div>
48+
</>
49+
) : (
50+
table
51+
)}
52+
</>
53+
);
54+
};
55+
56+
export default ChartTableDisplay;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@import 'src/variables.module';
2+
$cell-height: 2.125rem;
3+
4+
.emptyTable {
5+
opacity: 40%;
6+
width: 100%;
7+
border-spacing: 0;
8+
border: $table-border-style;
9+
border-collapse: collapse;
10+
11+
th,
12+
td {
13+
height: $cell-height;
14+
border-right: $table-border-style;
15+
}
16+
17+
tr:not(:last-child) td {
18+
border-bottom: $table-border-style;
19+
}
20+
21+
th {
22+
background-color: #f1f1f1;
23+
border-bottom: $table-border-style;
24+
}
25+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import EmptyTable from './empty-table';
3+
import { render } from '@testing-library/react';
4+
5+
describe('Empty Table', () => {
6+
it('renders a table', () => {
7+
const { getByRole, getAllByRole } = render(<EmptyTable />);
8+
9+
expect(getByRole('table')).toBeInTheDocument();
10+
expect(getAllByRole('row')).toHaveLength(11);
11+
});
12+
13+
it('renders a table with specified number of rows', () => {
14+
const { getByRole, getAllByRole } = render(<EmptyTable rowCount={5} />);
15+
16+
expect(getByRole('table')).toBeInTheDocument();
17+
expect(getAllByRole('row')).toHaveLength(6);
18+
});
19+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, { FunctionComponent } from 'react';
2+
import { emptyTable } from './empty-table.module.scss';
3+
import DataTableFooter from '../../../data-table/data-table-footer/data-table-footer';
4+
5+
const EmptyTable: FunctionComponent = ({ rowCount = 10, mobileDisplay }) => {
6+
const columnCount = mobileDisplay ? 2 : 6;
7+
8+
return (
9+
<>
10+
<table className={emptyTable}>
11+
<tbody>
12+
<tr>
13+
{Array.from({ length: columnCount }, (_, index) => (
14+
<th key={`header-${index}`} />
15+
))}
16+
</tr>
17+
{Array.from({ length: rowCount }, (_, index) => (
18+
<tr key={`row-${index}`}>
19+
{Array.from({ length: columnCount }, (_, index) => (
20+
<td key={`cell-${index}`} />
21+
))}
22+
</tr>
23+
))}
24+
</tbody>
25+
</table>
26+
<DataTableFooter
27+
rowsShowing={{ begin: 0, end: 0 }}
28+
manualPagination={true}
29+
pagingProps={{ maxRows: 0, itemsPerPage: rowCount, disablePerPage: true, showWhenEmpty: true, currentPage: 1, maxPage: 1 }}
30+
showPaginationControls={true}
31+
/>
32+
</>
33+
);
34+
};
35+
36+
export default EmptyTable;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
@import 'src/variables.module.scss';
2+
3+
.container {
4+
text-align: center;
5+
}
6+
7+
.info {
8+
display: inline-flex;
9+
padding: 1.5rem 2rem;
10+
background-color: $table-notice-color;
11+
text-align: left;
12+
max-width: 47.5rem;
13+
}
14+
15+
.icon {
16+
color: $primary;
17+
font-size: $font-size-16;
18+
margin-right: 0.75rem;
19+
margin-top: 0.125rem;
20+
}
21+
22+
.notShownHeading {
23+
@include headingStyle6;
24+
margin-bottom: 0.5rem;
25+
}
26+
27+
.notShownBodyText {
28+
@include bodyStyleRegular;
29+
}
30+
31+
@media screen and (max-width: $breakpoint-lg - 1) {
32+
.container {
33+
margin: 0 1.5rem;
34+
}
35+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
import TableNotice from './table-notice';
3+
import { render } from '@testing-library/react';
4+
5+
describe('Table Notice', () => {
6+
it('renders the table notice', () => {
7+
const heading = 'Mock Heading!';
8+
const bodyText = 'Mock body text.';
9+
const { getByText } = render(<TableNotice heading={heading} bodyText={bodyText} />);
10+
11+
expect(getByText(heading)).toBeInTheDocument();
12+
expect(getByText(bodyText)).toBeInTheDocument();
13+
});
14+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React, { FunctionComponent } from 'react';
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3+
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
4+
import { container, icon, info, notShownBodyText, notShownHeading } from './table-notice.module.scss';
5+
6+
interface iTableMessage {
7+
heading: string;
8+
bodyText: string;
9+
}
10+
11+
const TableNotice: FunctionComponent<iTableMessage> = ({ heading, bodyText }) => {
12+
return (
13+
<div className={container} data-testid="container">
14+
<div className={info}>
15+
<div className={icon}>
16+
<FontAwesomeIcon icon={faInfoCircle} />
17+
</div>
18+
<div>
19+
{heading && (
20+
<div className={notShownHeading} data-testid="heading">
21+
{heading}
22+
</div>
23+
)}
24+
<div className={notShownBodyText} data-testid="bodyText">
25+
{bodyText}
26+
</div>
27+
</div>
28+
</div>
29+
</div>
30+
);
31+
};
32+
33+
export default TableNotice;

src/components/data-preview/data-preview-data-table/data-preview-data-table-body/data-preview-data-table-body.module.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
@import 'src/variables.module';
2-
$table-border-style: 1px solid $light-grey-border;
32

43
.fillCellGrey,
54
.fillCellWhite {

src/components/data-preview/data-preview-data-table/data-preview-data-table-header/data-preview-data-table-header.module.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
@import 'src/variables.module.scss';
2-
@import '../data-preview-data-table.module';
32

43
.tableHeader {
54
cursor: default;

src/components/data-preview/data-preview-data-table/data-preview-data-table.module.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@import 'src/variables.module.scss';
22

33
$borderRadius: 5px;
4-
$table-border-style: 1px solid $light-grey-border;
54

65
.overlayContainerNoFooter {
76
position: relative;

src/components/data-preview/data-preview-dropdown/data-preview-table-select-dropdown.module.scss

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/components/data-preview/data-preview-dropdown/data-preview-table-select-dropdown.spec.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,28 @@ describe('Data Preview Dropdown Dialog', () => {
8585
pivotView: { dimensionField: 'account_number', title: 'By Account Number' },
8686
});
8787
});
88+
89+
it('updates dropdown when All Data Tables is selected', () => {
90+
const mockSetSelectedTable = jest.fn();
91+
const mockSetSelectedPivot = jest.fn();
92+
93+
const { getByRole, getAllByText, queryByRole } = render(
94+
<DataPreviewTableSelectDropdown
95+
selectedTable={mockSelectedTable}
96+
setSelectedTable={mockSetSelectedTable}
97+
apis={mockApis}
98+
setSelectedPivot={mockSetSelectedPivot}
99+
/>
100+
);
101+
102+
const dropdownButton = getByRole('button', { name: 'Data Table: Mock Table Name' });
103+
fireEvent.click(dropdownButton);
104+
105+
const allTablesButton = getByRole('button', { name: 'All Data Tables (Download Only)' });
106+
fireEvent.click(allTablesButton);
107+
108+
expect(getAllByText('All Data Tables (Download Only)').length).toBe(2);
109+
expect(getByRole('radio', { name: 'Raw Data' })).toBeInTheDocument();
110+
expect(queryByRole('radio', { name: 'Pivot Data' })).not.toBeInTheDocument();
111+
});
88112
});

src/components/data-preview/data-preview-dropdown/data-preview-table-select-dropdown.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import DropdownContainer from '../../dropdown-container/dropdown-container';
55
import DataPreviewDropdownDialogContainer from '../data-preview-dropdown-dialog/data-preview-dropdown-dialog';
66
import DataPreviewPivotSelect from '../data-preview-pivot-select/data-preview-pivot-select';
77
import { ITableSelectDropdown } from '../../../models/data-preview/ITableSelectDropdown';
8-
import { allTablesOption } from '../../datatable-select/datatable-select';
98
import DataPreviewDropdownDialogSearch from '../data-preview-dropdown-search/data-preview-dropdown-dialog-search';
109

1110
const DataPreviewTableSelectDropdown: FunctionComponent<ITableSelectDropdown> = ({
@@ -20,8 +19,16 @@ const DataPreviewTableSelectDropdown: FunctionComponent<ITableSelectDropdown> =
2019
setSelectedPivot,
2120
hideDropdown,
2221
}) => {
22+
const allTablesOption = {
23+
allDataTables: true,
24+
pathName: 'all-data-tables',
25+
tableName: 'All Data Tables (Download Only)',
26+
valueFieldOptions: null,
27+
};
28+
29+
const initialSelectedTable = allTablesSelected ? allTablesOption : selectedTable;
2330
const [active, setActive] = useState(false);
24-
const [tableToApply, setTableToApply] = useState(selectedTable);
31+
const [tableToApply, setTableToApply] = useState(initialSelectedTable);
2532
const [pivotToApply, setPivotToApply] = useState(selectedPivot);
2633
const [appliedTableView, setAppliedTableView] = useState('rawData');
2734
const [tableViewSelection, setTableViewSelection] = useState(appliedTableView);
@@ -39,7 +46,7 @@ const DataPreviewTableSelectDropdown: FunctionComponent<ITableSelectDropdown> =
3946
<DropdownLabelButton
4047
label="Data Table"
4148
icon={faDatabase}
42-
selectedOption={selectedTable?.tableName}
49+
selectedOption={allTablesSelected ? allTablesOption.tableName : selectedTable?.tableName}
4350
active={active}
4451
setActive={setActive}
4552
dropdownWidth="30rem"
@@ -48,7 +55,7 @@ const DataPreviewTableSelectDropdown: FunctionComponent<ITableSelectDropdown> =
4855

4956
const handleApply = () => {
5057
setAppliedTableView(tableViewSelection);
51-
if (tableToApply !== selectedTable) {
58+
if (tableToApply !== selectedTable || (allTablesSelected && !tableToApply.allDataTables)) {
5259
setSelectedTable(tableToApply);
5360
}
5461
if (tableViewSelection === 'pivotData') {
@@ -87,11 +94,14 @@ const DataPreviewTableSelectDropdown: FunctionComponent<ITableSelectDropdown> =
8794
useEffect(() => {
8895
if (!active) {
8996
setTableViewSelection(appliedTableView);
90-
setTableToApply(selectedTable);
97+
if (!allTablesSelected) {
98+
setTableToApply(selectedTable);
99+
}
91100
}
92101
}, [active]);
93102

94103
useEffect(() => {
104+
//initialize pivot options
95105
if (selectedTable && !selectedTable.allDataTables && !selectedPivot) {
96106
const localPivotFields = getPivotFields(selectedTable);
97107
const pivot = {

src/components/data-preview/data-preview-filter-section/column-filter/column-filter.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1-
import React, { FunctionComponent, useState } from 'react';
1+
import React, { FunctionComponent, useEffect, useState } from 'react';
22
import { faTable } from '@fortawesome/free-solid-svg-icons';
33
import DropdownLabelButton from '../../../dropdown-label-button/dropdown-label-button';
44
import DropdownContainer from '../../../dropdown-container/dropdown-container';
55
import ColumnFilterContainer from '../column-filter-container/column-filter-container';
66

7-
const ColumnFilter: FunctionComponent = () => {
7+
interface iColumnFilter {
8+
allTablesSelected: boolean;
9+
}
10+
11+
const ColumnFilter: FunctionComponent<iColumnFilter> = ({ allTablesSelected }) => {
812
const [visibleColumns, setVisibleColumns] = useState([]);
913
const totalColumns = 17;
1014
const [active, setActive] = useState(false);
15+
1116
const filterDropdownButton = (
1217
<DropdownLabelButton
13-
label="Filters"
18+
label="Columns"
1419
selectedOption={visibleColumns.length + '/' + totalColumns}
1520
icon={faTable}
1621
active={active}
1722
setActive={setActive}
23+
disabled={allTablesSelected}
1824
/>
1925
);
2026

0 commit comments

Comments
 (0)