diff --git a/__test__/containers/CampaignList.test.js b/__test__/containers/CampaignList.test.js
index a81702f13..069f7d710 100644
--- a/__test__/containers/CampaignList.test.js
+++ b/__test__/containers/CampaignList.test.js
@@ -3,14 +3,31 @@
*/
import React from "react";
import { mount } from "enzyme";
-import { AdminCampaignList } from "../../src/containers/AdminCampaignList";
-import { TIMEZONE_SORT } from "../../src/components/AdminCampaignList/SortBy";
+import { act } from "react-dom/test-utils";
+import {
+ render,
+ screen,
+ waitFor,
+ cleanup
+} from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
import { StyleSheetTestUtils } from "aphrodite";
+import { AdminCampaignList } from "../../src/containers/AdminCampaignList";
+
+// https://github.com/Khan/aphrodite/issues/62#issuecomment-267026726
+beforeEach(() => {
+ StyleSheetTestUtils.suppressStyleInjection();
+});
+afterEach(() => {
+ StyleSheetTestUtils.clearBufferAndResumeStyleInjection();
+ cleanup();
+});
+
describe("CampaignList", () => {
const params = {
adminPerms: true,
- organizationId: 77
+ organizationId: "77"
};
const mutations = {
@@ -110,19 +127,21 @@ describe("CampaignList", () => {
describe("Campaign list sorting", () => {
const campaignWithCreator = {
- id: 1,
+ id: "1",
+ title: "test",
creator: {
displayName: "Lorem Ipsum"
},
completionStats: {},
organization: {
id: 1
- }
+ },
+ timezone: "US/Eastern"
};
const data = {
organization: {
- id: 1,
+ id: "1",
cacheable: 2,
campaigns: {
campaigns: [campaignWithCreator],
@@ -132,26 +151,58 @@ describe("CampaignList", () => {
total: 1
}
}
- }
+ },
+ refetch: () => {}
};
- test("Timezone column is displayed when timezone is current sort", () => {
+ test("Timezone column is displayed when timezone is current sort", async () => {
StyleSheetTestUtils.suppressStyleInjection();
- const wrapper = mount(
-
- );
- wrapper.setState({
- sortBy: TIMEZONE_SORT.value
+ act(() => {
+ render(
+
+ );
+ });
+
+ act(() => {
+ userEvent.click(
+ screen.getByRole("button", { name: /sort: created, newest/i }),
+ { skipHover: true }
+ );
});
- expect(wrapper.containsMatchingElement("Timezone")).toBeTruthy();
+
+ act(() => {
+ userEvent.click(
+ screen.getByRole("option", { name: /sort: timezone/i }),
+ { skipHover: true }
+ );
+ });
+
+ await waitFor(() =>
+ expect(
+ screen.getByRole("columnheader", { name: /timezone/i })
+ ).toBeTruthy()
+ );
});
test("Timezone column is hidden when it isn't the current sort", () => {
StyleSheetTestUtils.suppressStyleInjection();
- const wrapper = mount(
-
- );
- expect(wrapper.containsMatchingElement("Timezone")).toBeFalsy();
+ act(() => {
+ render(
+
+ );
+ });
+ const timezoneButton = screen.queryByText("columnheader", {
+ name: /timezone/i
+ });
+ expect(timezoneButton).toBeNull();
});
});
});
diff --git a/package.json b/package.json
index 4b6fcebae..574fe5ce1 100644
--- a/package.json
+++ b/package.json
@@ -182,6 +182,9 @@
},
"devDependencies": {
"@babel/eslint-parser": "^7.24.7",
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/react": "^12.1.5",
+ "@testing-library/user-event": "^12.1.5",
"babel-jest": "^29.3.1",
"babel-preset-es2017": "^6.24.1",
"cypress": "^13.11.0",
diff --git a/src/components/AdminCampaignList/CampaignTable.jsx b/src/components/AdminCampaignList/CampaignTable.jsx
index aa44a3e92..a86e0ffbd 100644
--- a/src/components/AdminCampaignList/CampaignTable.jsx
+++ b/src/components/AdminCampaignList/CampaignTable.jsx
@@ -1,5 +1,5 @@
import PropTypes from "prop-types";
-import React from "react";
+import React, { useState } from "react";
import { Link as RouterLink } from "react-router";
import moment from "moment";
@@ -20,35 +20,39 @@ const inlineStyles = {
whiteSpace: "nowrap"
}
};
+
+const CampaignTable = ({
+ data,
+ campaignsToArchive,
+ campaignsWithChangingStatus,
+ currentSortBy,
+ onNextPageClick,
+ onPreviousPageClick,
+ onRowSizeChange,
+ adminPerms,
+ selectMultiple,
+ organizationId,
+ handleChecked,
+ archiveCampaign,
+ unarchiveCampaign
+ }) => {
-export class CampaignTable extends React.Component {
- static propTypes = {
- adminPerms: PropTypes.bool,
- selectMultiple: PropTypes.bool,
- organizationId: PropTypes.string,
- data: PropTypes.object,
- handleChecked: PropTypes.func,
- archiveCampaign: PropTypes.func,
- unarchiveCampaign: PropTypes.func,
- onNextPageClick: PropTypes.func,
- onPreviousPageClick: PropTypes.func,
- onRowSizeChange: PropTypes.func,
- campaignsToArchive: PropTypes.array,
- campaignsWithChangingStatus: PropTypes.array,
- currentSortBy: PropTypes.oneOf(SORTS.map(s => s.value))
- };
+ const [campaigns, setCampaigns] = useState(data.organization.campaigns.campaigns.map((campaign) => ({...campaign})));
- state = {
- dataTableKey: "initial",
- campaigns: [...this.props.data.organization.campaigns.campaigns]
- };
+ const [state, setState] = useState({
+ dataTableKey: "initial"
+ });
- statusIsChanging = campaign => {
- return this.props.campaignsWithChangingStatus.includes(campaign.id);
+ const { limit, offset, total } = data.organization.campaigns.pageInfo;
+ const displayPage = Math.floor(offset / limit) + 1;
+ let rowSizeList = [10, 20, 50, 100];
+
+ const statusIsChanging = campaign => {
+ return campaignsWithChangingStatus.includes(campaign.id);
};
- renderArchiveIcon(campaign) {
- if (this.statusIsChanging(campaign)) {
+ const renderArchiveIcon = campaign => {
+ if (statusIsChanging(campaign)) {
return ;
}
if (campaign.isArchived) {
@@ -58,7 +62,10 @@ export class CampaignTable extends React.Component {
return (
await this.props.unarchiveCampaign(campaign.id)}
+ onClick={async () => {
+ await unarchiveCampaign(campaign.id);
+ setCampaigns(campaigns.filter(e => e.id != campaign.id));
+ }}
>
@@ -67,14 +74,17 @@ export class CampaignTable extends React.Component {
return (
await this.props.archiveCampaign(campaign.id)}
+ onClick={async () => {
+ await archiveCampaign(campaign.id);
+ setCampaigns(campaigns.filter(e => e.id != campaign.id));
+ }}
>
);
}
- sortFunc(key) {
+ const sortFunc = key => {
const sorts = {
id: (a, b) => b.id - a.id,
title: (a, b) => (b.title > a.title ? 1 : -1),
@@ -92,7 +102,7 @@ export class CampaignTable extends React.Component {
return sorts[key];
}
- prepareTableColumns(organization, campaigns) {
+ const prepareTableColumns = (organization, campaigns) => {
const extraRows = [];
const needsResponseCol = campaigns.some(
c => c.completionStats.needsResponseCount
@@ -112,14 +122,14 @@ export class CampaignTable extends React.Component {
}
});
}
- if (this.props.adminPerms) {
+ if (adminPerms) {
extraRows.push({
label: "Archive",
name: "archive",
options: {
customBodyRender: (value, tableMeta) => {
const campaign = campaigns.find(c => c.id === tableMeta.rowData[0]);
- return this.renderArchiveIcon(campaign);
+ return renderArchiveIcon(campaign);
},
sort: false
},
@@ -131,7 +141,7 @@ export class CampaignTable extends React.Component {
const timezoneColumn = [];
// only show the timezone column when we're currently sorting by timezone
- if (this.props.currentSortBy === TIMEZONE_SORT.value) {
+ if (currentSortBy === TIMEZONE_SORT.value) {
timezoneColumn.push({
key: "timezone",
name: "timezone",
@@ -157,7 +167,7 @@ export class CampaignTable extends React.Component {
customBodyRender: (value, tableMeta) => {
const campaign = campaigns.find(c => c.id === tableMeta.rowData[0]);
let org = "";
- if (this.props.organizationId != campaign.organization.id) {
+ if (organizationId != campaign.organization.id) {
org = ` (${campaign.organization.id})`;
}
return `${campaign.id}${org}`;
@@ -280,11 +290,11 @@ export class CampaignTable extends React.Component {
];
}
- getSelectedRowIndexes = () => {
- const campaignIds = this.props.data.organization.campaigns.campaigns.map(
+ const getSelectedRowIndexes = () => {
+ const campaignIds = campaigns.map(
c => c.id
);
- const indexes = this.props.campaignsToArchive.map(campaignId =>
+ const indexes = campaignsToArchive.map(campaignId =>
campaignIds.indexOf(campaignId)
);
if (indexes.includes(-1)) {
@@ -297,96 +307,106 @@ export class CampaignTable extends React.Component {
return indexes;
};
- clearCampaignSelection = () => {
- this.props.handleChecked([]);
+ const clearCampaignSelection = () => {
+ handleChecked([]);
// Terrible hack around buggy DataTables: we have to force the component
// to remount if we want clear the "select all" status
- this.setState({
+ setState({
dataTableKey: new Date().getTime()
});
};
- render() {
- const { limit, offset, total } = this.props.data.organization.campaigns.pageInfo;
- const displayPage = Math.floor(offset / limit) + 1;
- let rowSizeList = [10, 20, 50, 100];
-
- const options = {
- filterType: "checkbox",
- selectableRows: "multiple", // this.props.selectMultiple ? "multiple" : "none",
- elevation: 0,
- download: false,
- print: false,
- searchable: false,
- filter: false,
- sort: true,
- search: false,
- viewColumns: false,
- page: displayPage - 1,
- count: total,
- rowsPerPage: limit,
- rowsPerPageOptions: rowSizeList,
- serverSide: true,
- rowsSelected: this.getSelectedRowIndexes(),
- customToolbarSelect: () => null,
- onTableChange: (action, tableState) => {
- switch (action) {
- case "changePage":
- if (tableState.page > displayPage - 1) {
- this.clearCampaignSelection();
- this.props.onNextPageClick();
- } else {
- this.clearCampaignSelection();
- this.props.onPreviousPageClick();
- }
- break;
- case "changeRowsPerPage":
- this.clearCampaignSelection();
- const _ = undefined;
- this.props.onRowSizeChange(_, tableState.rowsPerPage);
- break;
- case "sort":
- this.clearCampaignSelection();
- this.state.campaigns.sort(this.sortFunc(tableState.sortOrder.name));
- if (tableState.sortOrder.direction === "desc") {
- this.state.campaigns.reverse()
- }
- break;
- case "rowSelectionChange":
- const ids = tableState.selectedRows.data.map(({ index }) => {
- return this.state.campaigns[index].id;
- });
- this.props.handleChecked(ids);
- break;
- case "propsUpdate":
- break;
- default:
- break;
- }
+ const options = {
+ filterType: "checkbox",
+ selectableRows: "multiple", // selectMultiple ? "multiple" : "none",
+ elevation: 0,
+ download: false,
+ print: false,
+ searchable: false,
+ filter: false,
+ sort: true,
+ search: false,
+ viewColumns: false,
+ page: displayPage - 1,
+ count: total,
+ rowsPerPage: limit,
+ rowsPerPageOptions: rowSizeList,
+ serverSide: true,
+ rowsSelected: getSelectedRowIndexes(),
+ customToolbarSelect: () => null,
+ onTableChange: (action, tableState) => {
+ switch (action) {
+ case "changePage":
+ if (tableState.page > displayPage - 1) {
+ clearCampaignSelection();
+ onNextPageClick();
+ } else {
+ clearCampaignSelection();
+ onPreviousPageClick();
+ }
+ break;
+ case "changeRowsPerPage":
+ clearCampaignSelection();
+ const _ = undefined;
+ onRowSizeChange(_, tableState.rowsPerPage);
+ break;
+ case "sort":
+ clearCampaignSelection();
+ campaigns.sort(sortFunc(tableState.sortOrder.name));
+ if (tableState.sortOrder.direction === "desc") {
+ campaigns.reverse()
+ }
+ break;
+ case "rowSelectionChange":
+ const ids = tableState.selectedRows.data.map(({ index }) => {
+ return campaigns[index].id;
+ });
+ handleChecked(ids);
+ break;
+ case "propsUpdate":
+ break;
+ default:
+ break;
}
- };
+ }
+ };
- return this.state.campaigns.length === 0 ? (
- } />
- ) : (
-
-
-
-
- {/* make space for Floating Action Button */}
-
-
-
-
- );
- }
+ return campaigns.length === 0 ? (
+ } />
+ ) : (
+
+
+
+
+ {/* make space for Floating Action Button */}
+
+
+
+
+ );
+}
+
+CampaignTable.propTypes = {
+ adminPerms: PropTypes.bool,
+ selectMultiple: PropTypes.bool,
+ organizationId: PropTypes.string,
+ data: PropTypes.object,
+ handleChecked: PropTypes.func,
+ archiveCampaign: PropTypes.func,
+ unarchiveCampaign: PropTypes.func,
+ onNextPageClick: PropTypes.func,
+ onPreviousPageClick: PropTypes.func,
+ onRowSizeChange: PropTypes.func,
+ campaignsToArchive: PropTypes.array,
+ campaignsWithChangingStatus: PropTypes.array,
+ currentSortBy: PropTypes.oneOf(SORTS.map(s => s.value))
}
export default CampaignTable;
diff --git a/src/containers/AdminCampaignList.jsx b/src/containers/AdminCampaignList.jsx
index 8ed20f5aa..80c986ba6 100644
--- a/src/containers/AdminCampaignList.jsx
+++ b/src/containers/AdminCampaignList.jsx
@@ -1,5 +1,5 @@
import PropTypes from "prop-types";
-import React from "react";
+import React, { useState } from "react";
import { StyleSheet, css } from "aphrodite";
import { withRouter } from "react-router";
import { gql } from "@apollo/client";
@@ -18,9 +18,7 @@ import LoadingIndicator from "../components/LoadingIndicator";
import { dataTest } from "../lib/attributes";
import loadData from "./hoc/load-data";
import theme from "../styles/theme";
-import SortBy, {
- ID_DESC_SORT
-} from "../components/AdminCampaignList/SortBy";
+import SortBy, { ID_DESC_SORT } from "../components/AdminCampaignList/SortBy";
import Search from "../components/Search";
import CampaignTable from "../components/AdminCampaignList/CampaignTable";
@@ -33,41 +31,32 @@ const styles = StyleSheet.create({
}
});
-const INITIAL_ROW_SIZE = 50;
-const INITIAL_FILTER = {
- isArchived: false,
- searchString: ""
-};
const INITIAL_SORT_BY = ID_DESC_SORT.value;
-export class AdminCampaignList extends React.Component {
- static propTypes = {
- params: PropTypes.object,
- mutations: PropTypes.exact({
- createCampaign: PropTypes.func,
- archiveCampaigns: PropTypes.func,
- unarchiveCampaign: PropTypes.func
- }),
- data: PropTypes.object,
- router: PropTypes.object
- };
-
- state = {
- pageSize: INITIAL_ROW_SIZE,
+// Exported for testing
+export const AdminCampaignList = ({ params, mutations, router, data }) => {
+ const [state, setState] = useState({
+ pageSize: 50,
page: 0,
isLoading: false,
- campaignsFilter: INITIAL_FILTER,
+ campaignsFilter: {
+ isArchived: false,
+ searchString: ""
+ },
archiveMultiple: false,
campaignsToArchive: [],
campaignsWithChangingStatus: [],
sortBy: INITIAL_SORT_BY,
archiveMultipleMenu: false
- };
+ });
- handleClickNewButton = async () => {
- const { organizationId } = this.props.params;
- this.setState({ isLoading: true });
- const newCampaign = await this.props.mutations.createCampaign({
+ const handleClickNewButton = async () => {
+ const { organizationId } = params;
+ setState({
+ ...state,
+ isLoading: true
+ });
+ const newCampaign = await mutations.createCampaign({
title: "New Campaign",
description: "",
dueBy: null,
@@ -83,17 +72,21 @@ export class AdminCampaignList extends React.Component {
throw new Error(newCampaign.errors);
}
- await this.props.router.push(
+ await router.push(
`/admin/${organizationId}/campaigns/${newCampaign.data.createCampaign.id}/edit?new=true`
);
};
- handleClickArchiveMultipleButton = async keys => {
+ const handleClickArchiveMultipleButton = async keys => {
if (keys.length) {
- this.setState({ isLoading: true });
- await this.props.mutations.archiveCampaigns(keys);
- await this.props.data.refetch();
- this.setState({
+ setState({
+ ...state,
+ isLoading: true
+ });
+ await mutations.archiveCampaigns(keys);
+ await data.refetch();
+ setState({
+ ...state,
archiveMultiple: false,
isLoading: false,
campaignsToArchive: []
@@ -101,90 +94,98 @@ export class AdminCampaignList extends React.Component {
}
};
- handleArchiveFilterChange = async event => {
- this.changeFilter({ isArchived: event.target.value });
+ const handleArchiveFilterChange = async event => {
+ changeFilter({ isArchived: event.target.value });
};
- handleChecked = campaignIds => {
- this.setState({
+ const handleChecked = campaignIds => {
+ setState({
+ ...state,
campaignsToArchive: [...campaignIds]
});
};
- handleSearchRequested = searchString => {
+ const handleSearchRequested = searchString => {
const campaignsFilter = {
- ...this.state.campaignsFilter,
+ ...state.campaignsFilter,
searchString
};
- this.changeFilter(campaignsFilter);
+ changeFilter(campaignsFilter);
};
- handleCancelSearch = () => {
+ const handleCancelSearch = () => {
const campaignsFilter = {
- ...this.state.campaignsFilter,
+ ...state.campaignsFilter,
searchString: ""
};
- this.changeFilter(campaignsFilter);
+ changeFilter(campaignsFilter);
};
- renderArchivedAndSortBy = () => {
+ const renderArchivedAndSortBy = () => {
return (
- !this.state.archiveMultiple && (
+ !state.archiveMultiple && (
-
+
)
);
};
- renderSearch = () => {
+ const renderSearch = () => {
return (
- !this.state.archiveMultiple && (
+ !state.archiveMultiple && (
)
);
};
- renderFilters = () => (
+ const renderFilters = () => (
- {this.props.params.adminPerms && this.renderArchiveMultiple()}
- {this.renderArchivedAndSortBy()}
+ {params.adminPerms && renderArchiveMultiple()}
+ {renderArchivedAndSortBy()}
- {this.renderSearch()}
+ {renderSearch()}
);
- handleMenuClick = event => {
+ const handleMenuClick = event => {
console.log("event.target", event.target);
- this.setState({ menuAnchorEl: event.target });
+ setState({
+ ...state,
+ menuAnchorEl: event.target
+ });
};
- handleMenuClose = () => {
- this.setState({ menuAnchorEl: null });
+ const handleMenuClose = () => {
+ setState({
+ ...state,
+ menuAnchorEl: null
+ });
};
- renderArchiveMultiple() {
+ const renderArchiveMultiple = () => {
const iconButton = (
{
- this.handleMenuClick(event);
- this.setState({
- archiveMultipleMenu: !this.state.archiveMultipleMenu
+ handleMenuClick(event);
+ setState({
+ ...state,
+ archiveMultipleMenu: !state.archiveMultipleMenu
});
}}
>
@@ -192,23 +193,21 @@ export class AdminCampaignList extends React.Component {
);
- if (this.state.campaignsFilter.isArchived) {
+ if (state.campaignsFilter.isArchived) {
return iconButton;
}
return (
{iconButton}
-
);
- }
+ };
- changePage = (pageDelta, pageSize) => {
- const {
- limit,
- offset,
- total
- } = this.props.data.organization.campaigns.pageInfo;
+ const changePage = (pageDelta, pageSize) => {
+ const { limit, offset, total } = data.organization.campaigns.pageInfo;
const currentPage = Math.floor(offset / limit);
const pageSizeAdjustedCurrentPage = Math.floor(
(currentPage * limit) / pageSize
);
const maxPage = Math.floor(total / pageSize);
const newPage = Math.min(maxPage, pageSizeAdjustedCurrentPage + pageDelta);
- this.props.data.fetchMore({
+ data.fetchMore({
variables: {
cursor: {
offset: newPage * pageSize,
@@ -259,81 +255,91 @@ export class AdminCampaignList extends React.Component {
});
};
- changeFilter = async newFilter => {
- this.setState({
- isLoading: true,
- campaignsFilter: newFilter
+ const changeFilter = async newFilter => {
+ setState({
+ ...state,
+ campaignsFilter: newFilter,
+ isLoading: true
});
- await this.props.data.refetch({
+ await data.refetch({
campaignsFilter: newFilter
});
- this.setState({ isLoading: false });
+ setState({
+ ...state,
+ campaignsFilter: newFilter,
+ isLoading: false
+ });
};
- changeSortBy = async newSort => {
- this.setState({
+ const changeSortBy = async newSort => {
+ setState({
+ ...state,
isLoading: true,
sortBy: newSort
});
- await this.props.data.refetch({
+ await data.refetch({
+ sortBy: newSort
+ });
+ setState({
+ ...state,
+ isLoading: false,
sortBy: newSort
});
- this.setState({ isLoading: false });
};
- handleNextPageClick = () => {
- this.changePage(1, this.state.pageSize);
+ const handleNextPageClick = () => {
+ changePage(1, state.pageSize);
};
- handlePreviousPageClick = () => {
- this.changePage(-1, this.state.pageSize);
+ const handlePreviousPageClick = () => {
+ changePage(-1, state.pageSize);
};
- handleRowSizeChanged = (index, value) => {
+ const handleRowSizeChanged = (index, value) => {
console.log("rowsizechanged", index, value); // eslint-disable-line no-console
- this.changePage(0, value);
- this.setState({
+ changePage(0, value);
+ setState({
+ ...state,
pageSize: value
});
};
- changeCampaignStatus = async (campaignId, changeFn) => {
- this.setState({
- campaignsWithChangingStatus: this.state.campaignsWithChangingStatus.concat(
- [campaignId]
- )
+ const changeCampaignStatus = async (campaignId, changeFn) => {
+ setState({
+ ...state,
+ campaignsWithChangingStatus: state.campaignsWithChangingStatus.concat([
+ campaignId
+ ])
});
await changeFn(campaignId);
- await this.props.data.refetch();
- this.setState({
- campaignsWithChangingStatus: this.state.campaignsWithChangingStatus.filter(
+ await data.refetch();
+ setState({
+ ...state,
+ campaignsWithChangingStatus: state.campaignsWithChangingStatus.filter(
id => id !== campaignId
)
});
};
- handleArchiveCampaign = async campaignId => {
- await this.changeCampaignStatus(campaignId, async id => {
- await this.props.mutations.archiveCampaigns([id]);
+ const handleArchiveCampaign = async campaignId => {
+ await changeCampaignStatus(campaignId, async id => {
+ await mutations.archiveCampaigns([id]);
});
};
- handleUnarchiveCampaign = async campaignId => {
- await this.changeCampaignStatus(
- campaignId,
- this.props.mutations.unarchiveCampaign
- );
+ const handleUnarchiveCampaign = async campaignId => {
+ await changeCampaignStatus(campaignId, mutations.unarchiveCampaign);
};
- renderActionButton() {
- if (this.state.archiveMultiple) {
- const keys = this.state.campaignsToArchive;
+ const renderActionButton = () => {
+ if (state.archiveMultiple) {
+ const keys = state.campaignsToArchive;
return (
this.handleClickArchiveMultipleButton(keys)}
+ onClick={() => handleClickArchiveMultipleButton(keys)}
disabled={!keys.length}
>
@@ -345,43 +351,40 @@ export class AdminCampaignList extends React.Component {
color="primary"
{...dataTest("addCampaign")}
style={theme.components.floatingButton}
- onClick={this.handleClickNewButton}
+ onClick={handleClickNewButton}
>
);
- }
-
- render() {
- const { adminPerms } = this.props.params;
- return (
-
- {this.renderFilters()}
- {this.state.isLoading ? (
-
- ) : (
-
- )}
-
- {adminPerms && this.renderActionButton()}
-
- );
- }
-}
+ };
+ // don't think this is right
+ return (
+
+ {renderFilters()}
+ {state.isLoading ? (
+
+ ) : (
+
+ )}
+
+ {params.adminPerms && renderActionButton()}
+
+ );
+};
const campaignInfoFragment = `
id
@@ -478,7 +481,10 @@ const queries = {
variables: {
cursor: { offset: 0, limit: 50 },
organizationId: ownProps.params.organizationId,
- campaignsFilter: INITIAL_FILTER,
+ campaignsFilter: {
+ isArchived: false,
+ searchString: ""
+ },
sortBy: INITIAL_SORT_BY
},
fetchPolicy: "network-only"
@@ -517,4 +523,15 @@ const mutations = {
})
};
+AdminCampaignList.propTypes = {
+ params: PropTypes.object,
+ mutations: PropTypes.exact({
+ createCampaign: PropTypes.func,
+ archiveCampaigns: PropTypes.func,
+ unarchiveCampaign: PropTypes.func
+ }),
+ data: PropTypes.object,
+ router: PropTypes.object
+};
+
export default loadData({ queries, mutations })(withRouter(AdminCampaignList));
diff --git a/yarn.lock b/yarn.lock
index 00fb26e8a..5d12e2647 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5051,6 +5051,50 @@
"@svgr/plugin-svgo" "^5.5.0"
loader-utils "^2.0.0"
+"@testing-library/dom@^10.4.0":
+ version "10.4.0"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8"
+ integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/runtime" "^7.12.5"
+ "@types/aria-query" "^5.0.1"
+ aria-query "5.3.0"
+ chalk "^4.1.0"
+ dom-accessibility-api "^0.5.9"
+ lz-string "^1.5.0"
+ pretty-format "^27.0.2"
+
+"@testing-library/dom@^8.0.0":
+ version "8.20.1"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f"
+ integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/runtime" "^7.12.5"
+ "@types/aria-query" "^5.0.1"
+ aria-query "5.1.3"
+ chalk "^4.1.0"
+ dom-accessibility-api "^0.5.9"
+ lz-string "^1.5.0"
+ pretty-format "^27.0.2"
+
+"@testing-library/react@^12.1.5":
+ version "12.1.5"
+ resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b"
+ integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ "@testing-library/dom" "^8.0.0"
+ "@types/react-dom" "<18.0.0"
+
+"@testing-library/user-event@^12.1.5":
+ version "12.8.3"
+ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-12.8.3.tgz#1aa3ed4b9f79340a1e1836bc7f57c501e838704a"
+ integrity sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+
"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@@ -5071,6 +5115,11 @@
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+"@types/aria-query@^5.0.1":
+ version "5.0.4"
+ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708"
+ integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
+
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
@@ -5335,6 +5384,13 @@
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
+"@types/react-dom@<18.0.0":
+ version "17.0.25"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5"
+ integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==
+ dependencies:
+ "@types/react" "^17"
+
"@types/react-transition-group@^4.2.0":
version "4.4.10"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac"
@@ -5351,6 +5407,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
+"@types/react@^17":
+ version "17.0.82"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.82.tgz#eb84c38ee1023cd61be1b909fde083ac83fc163f"
+ integrity sha512-wTW8Lu/PARGPFE8tOZqCvprOKg5sen/2uS03yKn2xbCDFP9oLncm7vMDQ2+dEQXHVIXrOpW6u72xUXEXO0ypSw==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "^0.16"
+ csstype "^3.0.2"
+
"@types/resolve@1.17.1":
version "1.17.1"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
@@ -5363,7 +5428,7 @@
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
-"@types/scheduler@*":
+"@types/scheduler@*", "@types/scheduler@^0.16":
version "0.16.8"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff"
integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==
@@ -6130,20 +6195,20 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
-aria-query@^5.3.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
- integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==
- dependencies:
- dequal "^2.0.3"
-
-aria-query@~5.1.3:
+aria-query@5.1.3, aria-query@~5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==
dependencies:
deep-equal "^2.0.5"
+aria-query@5.3.0, aria-query@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e"
+ integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==
+ dependencies:
+ dequal "^2.0.3"
+
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@@ -9003,6 +9068,11 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dom-accessibility-api@^0.5.9:
+ version "0.5.16"
+ resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
+ integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
+
dom-converter@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
@@ -14041,6 +14111,11 @@ lru-cache@~2.2.1:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
integrity sha512-Q5pAgXs+WEAfoEdw2qKQhNFFhMoFMTYqRVKKUMnzuiR7oKFHS7fWo848cPcTKw+4j/IdN17NyzdhVKgabFV0EA==
+lz-string@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
+ integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
+
magic-string@^0.25.0, magic-string@^0.25.7:
version "0.25.9"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
@@ -16380,7 +16455,7 @@ pretty-error@^4.0.0:
lodash "^4.17.20"
renderkid "^3.0.0"
-pretty-format@^27.5.1:
+pretty-format@^27.0.2, pretty-format@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==