From 6e50a145247ad946eb6cbc15220d2d90554fe55a Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Fri, 31 May 2024 09:57:48 -0400 Subject: [PATCH 1/9] only pass primary goal id not all the rolled up ids --- src/services/recipient.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/services/recipient.js b/src/services/recipient.js index 732049ba07..02ea3700e2 100644 --- a/src/services/recipient.js +++ b/src/services/recipient.js @@ -889,12 +889,15 @@ export async function getGoalsByActivityRecipient( }, }); + // For checkbox selection we only need the primary goal id. + const rolledUpGoalIds = r.goalRows.map((goal) => goal.id); + if (limitNum) { return { count: r.goalRows.length, goalRows: r.goalRows.slice(offSetNum, offSetNum + limitNum), statuses, - allGoalIds, + allGoalIds: rolledUpGoalIds, }; } @@ -902,7 +905,7 @@ export async function getGoalsByActivityRecipient( count: r.goalRows.length, goalRows: r.goalRows.slice(offSetNum), statuses, - allGoalIds, + allGoalIds: rolledUpGoalIds, }; } From 44058193b52c567bf595394e3c2eac50067883de Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Fri, 31 May 2024 11:28:02 -0400 Subject: [PATCH 2/9] fix tests --- src/services/recipient.test.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/recipient.test.js b/src/services/recipient.test.js index a9cb260035..54622228b7 100644 --- a/src/services/recipient.test.js +++ b/src/services/recipient.test.js @@ -868,8 +868,10 @@ describe('Recipient DB service', () => { }); it('properly de-duplicates based on responses', async () => { - const { goalRows } = await getGoalsByActivityRecipient(recipient.id, region, {}); + const { goalRows, allGoalIds } = await getGoalsByActivityRecipient(recipient.id, region, {}); expect(goalRows.length).toBe(3); + expect(allGoalIds.length).toBe(3); + expect(allGoalIds).toContain(goals[3].id); const doubler = goalRows.find((r) => r.responsesForComparison === 'not sure,dont have to'); expect(doubler).toBeTruthy(); @@ -890,8 +892,9 @@ describe('Recipient DB service', () => { goals[0].destroy(); goals[3].destroy(); - const { goalRows } = await getGoalsByActivityRecipient(recipient.id, region, {}); + const { goalRows, allGoalIds } = await getGoalsByActivityRecipient(recipient.id, region, {}); expect(goalRows.length).toBe(1); + expect(allGoalIds.length).toBe(1); // Verify goal 2 and 3 have empty creators/collaborators expect(goalRows[0].collaborators[0].goalCreator).toBe(undefined); // Verify goal 2 and 3 are rolled up @@ -1077,7 +1080,7 @@ describe('Recipient DB service', () => { expect(goalsForRecord.count).toBe(1); expect(goalsForRecord.goalRows.length).toBe(1); - expect(goalsForRecord.allGoalIds.length).toBe(2); + expect(goalsForRecord.allGoalIds.length).toBe(1); const goal = goalsForRecord.goalRows[0]; expect(goal.reasons.length).toBe(1); From 7f8bac81b09db6660960640739e95e6b19a4ba6e Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Mon, 3 Jun 2024 11:47:07 -0400 Subject: [PATCH 3/9] fix for print --- .../src/components/GoalCards/GoalCards.js | 6 +++ .../GoalCards/GoalDataController.js | 5 +- .../components/GoalCards/GoalsCardsHeader.js | 15 +++++- .../GoalCards/__tests__/GoalCards.js | 33 +++++++++++- .../pages/__tests__/GoalsObjectives.js | 54 ++++++++++++++++--- src/services/recipient.js | 8 ++- src/services/recipient.test.js | 5 +- 7 files changed, 115 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/GoalCards/GoalCards.js b/frontend/src/components/GoalCards/GoalCards.js index 05dcc948b6..f056de4d02 100644 --- a/frontend/src/components/GoalCards/GoalCards.js +++ b/frontend/src/components/GoalCards/GoalCards.js @@ -31,6 +31,7 @@ function GoalCards({ canMergeGoals, shouldDisplayMergeSuccess, dismissMergeSuccess, + goalBuckets, }) { const history = useHistory(); const [rttapaValidation, setRttapaValidation] = useState(false); @@ -241,6 +242,7 @@ function GoalCards({ canMergeGoals={canMergeGoals} shouldDisplayMergeSuccess={shouldDisplayMergeSuccess} dismissMergeSuccess={dismissMergeSuccess} + goalBuckets={goalBuckets} />
{goals.map((goal, index) => ( @@ -291,6 +293,10 @@ GoalCards.propTypes = { canMergeGoals: PropTypes.bool.isRequired, shouldDisplayMergeSuccess: PropTypes.bool, dismissMergeSuccess: PropTypes.func.isRequired, + goalBuckets: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.number, + goalIds: PropTypes.arrayOf(PropTypes.number), + })).isRequired, }; GoalCards.defaultProps = { diff --git a/frontend/src/components/GoalCards/GoalDataController.js b/frontend/src/components/GoalCards/GoalDataController.js index aaa8c74bab..d50dc898ed 100644 --- a/frontend/src/components/GoalCards/GoalDataController.js +++ b/frontend/src/components/GoalCards/GoalDataController.js @@ -132,7 +132,9 @@ function GoalDataController({ query, mergedGoals || [], ); - setData(response); + const rolledUpGoalIds = response.allGoalIds.map((goal) => goal.id); + const goalBuckets = response.allGoalIds; + setData({ ...response, allGoalIds: rolledUpGoalIds, goalBuckets }); setError(''); // display success message if we have merged goals setShouldDisplayMergedSuccess((mergedGoals && mergedGoals.length > 0)); @@ -259,6 +261,7 @@ function GoalDataController({ canMergeGoals={canMergeGoals} shouldDisplayMergeSuccess={shouldDisplayMergeSuccess} dismissMergeSuccess={dismissMergeSuccess} + goalBuckets={data.goalBuckets} />
diff --git a/frontend/src/components/GoalCards/GoalsCardsHeader.js b/frontend/src/components/GoalCards/GoalsCardsHeader.js index ad0c33b153..5e6118ef41 100644 --- a/frontend/src/components/GoalCards/GoalsCardsHeader.js +++ b/frontend/src/components/GoalCards/GoalsCardsHeader.js @@ -38,6 +38,7 @@ export default function GoalCardsHeader({ canMergeGoals, shouldDisplayMergeSuccess, dismissMergeSuccess, + goalBuckets, }) { const [goalMergeGroups, setGoalMergeGroups] = useState([]); const history = useHistory(); @@ -71,8 +72,16 @@ export default function GoalCardsHeader({ const showAddNewButton = hasActiveGrants && hasButtonPermissions; const onPrint = () => { + let goalIdsToPrint = selectedGoalIds; + if (!goalIdsToPrint.length) { + // Loop goalBuckets and get and flat array of all the goalIds. + goalIdsToPrint = goalBuckets.filter( + (bucket) => pageGoalIds.includes(bucket.id), + ).map((bucket) => bucket.goalIds).flat(); + } + history.push(`/recipient-tta-records/${recipientId}/region/${regionId}/rttapa/print${window.location.search}`, { - sortConfig, selectedGoalIds: !selectedGoalIds.length ? pageGoalIds : selectedGoalIds, + sortConfig, selectedGoalIds: goalIdsToPrint, }); }; @@ -297,6 +306,10 @@ GoalCardsHeader.propTypes = { canMergeGoals: PropTypes.bool.isRequired, shouldDisplayMergeSuccess: PropTypes.bool, dismissMergeSuccess: PropTypes.func.isRequired, + goalBuckets: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.number, + goals: PropTypes.arrayOf(PropTypes.number), + })).isRequired, }; GoalCardsHeader.defaultProps = { diff --git a/frontend/src/components/GoalCards/__tests__/GoalCards.js b/frontend/src/components/GoalCards/__tests__/GoalCards.js index 7edad74bdc..5447292664 100644 --- a/frontend/src/components/GoalCards/__tests__/GoalCards.js +++ b/frontend/src/components/GoalCards/__tests__/GoalCards.js @@ -36,7 +36,7 @@ const defaultUser = { const baseGoals = [{ id: 4598, - ids: [4598], + ids: [4598, 4599], goalStatus: 'In Progress', createdOn: '2021-06-15', goalText: 'This is goal text 1.', @@ -220,6 +220,7 @@ const setGoals = jest.fn(); const history = createMemoryHistory(); const renderTable = ({ goals, goalsCount, allGoalIds = null }, user, hasActiveGrants = true) => { + const goalBuckets = !goals ? [] : goals.map((g) => ({ id: g.id, goalIds: g.ids })); render( @@ -246,6 +247,7 @@ const renderTable = ({ goals, goalsCount, allGoalIds = null }, user, hasActiveGr allGoalIds={allGoalIds || goals.map((g) => g.id)} shouldDisplayMergeSuccess={false} dismissMergeSuccess={jest.fn()} + goalBuckets={goalBuckets} /> @@ -604,6 +606,35 @@ describe('Goals Table', () => { expect(history.push).toHaveBeenCalled(); }); + + it('calls print passing all goal ids on the page', async () => { + // print goals + const printButton = await screen.findByRole('button', { name: /Preview and print/i }); + userEvent.click(printButton); + expect(history.push).toHaveBeenCalledWith('/recipient-tta-records/1000/region/1/rttapa/print', { + selectedGoalIds: [4598, 4599, 65479], + sortConfig: { + activePage: 1, direction: 'asc', offset: 0, sortBy: 'goalStatus', + }, + }); + }); + + it('calls print passing all selected goal ids', async () => { + // print goals + const printButton = await screen.findByRole('button', { name: /Preview and print/i }); + + // select the checkbox with the value of 4598. + const checkBox = screen.queryAllByTestId('selectGoalTestId')[0]; + fireEvent.click(checkBox); + + userEvent.click(printButton); + expect(history.push).toHaveBeenCalledWith('/recipient-tta-records/1000/region/1/rttapa/print', { + selectedGoalIds: [4598, 4599], + sortConfig: { + activePage: 1, direction: 'asc', offset: 0, sortBy: 'goalStatus', + }, + }); + }); }); describe('Context Menu with Different User Permissions', () => { diff --git a/frontend/src/pages/RecipientRecord/pages/__tests__/GoalsObjectives.js b/frontend/src/pages/RecipientRecord/pages/__tests__/GoalsObjectives.js index 60009aaac8..1f0d5b3148 100644 --- a/frontend/src/pages/RecipientRecord/pages/__tests__/GoalsObjectives.js +++ b/frontend/src/pages/RecipientRecord/pages/__tests__/GoalsObjectives.js @@ -145,17 +145,37 @@ describe('Goals and Objectives', () => { fetchMock.reset(); // Default. const goalsUrl = `/api/recipient/401/region/1/goals?sortBy=goalStatus&sortDir=asc&offset=0&limit=10&createDate.win=${yearToDate}`; - fetchMock.get(goalsUrl, { count: 1, goalRows: goals, statuses: defaultStatuses }); + fetchMock.get(goalsUrl, { + count: 1, + goalRows: goals, + statuses: defaultStatuses, + allGoalIds: [ + { id: goals[0].id, goalIds: goals[0].ids }, + ], + }); // Filters Status. const filterStatusUrl = '/api/recipient/401/region/1/goals?sortBy=goalStatus&sortDir=asc&offset=0&limit=10&status.in[]=Not%20started'; fetchMock.get(filterStatusUrl, { - count: 1, goalRows: filterStatusGoals, statuses: defaultStatuses, + count: 1, + goalRows: filterStatusGoals, + statuses: defaultStatuses, + allGoalIds: [ + { id: filterStatusGoals[0].id, goalIds: filterStatusGoals[0].ids }, + ], }); // No Filters. const noFilterUrl = '/api/recipient/401/region/1/goals?sortBy=goalStatus&sortDir=asc&offset=0&limit=10'; - fetchMock.get(noFilterUrl, { count: 2, goalRows: noFilterGoals, statuses: defaultStatuses }); + fetchMock.get(noFilterUrl, { + count: 2, + goalRows: noFilterGoals, + statuses: defaultStatuses, + allGoalIds: [ + { id: noFilterGoals[0].id, goalIds: noFilterGoals[0].ids }, + { id: noFilterGoals[1].id, goalIds: noFilterGoals[1].ids }, + ], + }); fetchMock.get( '/api/communication-logs/region/1/recipient/401?sortBy=communicationDate&direction=desc&offset=0&limit=5&format=json&purpose.in[]=RTTAPA%20updates&purpose.in[]=RTTAPA%20Initial%20Plan%20%2F%20New%20Recipient', @@ -243,7 +263,7 @@ describe('Goals and Objectives', () => { fetchMock.get( '/api/communication-logs/region/1/recipient/401?sortBy=communicationDate&direction=desc&offset=0&limit=5&format=json&purpose.in[]=RTTAPA%20updates&purpose.in[]=RTTAPA%20Initial%20Plan%20%2F%20New%20Recipient', - { rows: [], count: 0 }, + { rows: [], count: 0, allGoalIds: [] }, ); const response = [{ @@ -262,7 +282,12 @@ describe('Goals and Objectives', () => { ]; const noFilterUrl = '/api/recipient/401/region/1/goals?sortBy=goalStatus&sortDir=asc&offset=0&limit=10'; - fetchMock.get(noFilterUrl, { count: 2, goalRows: response, statuses: defaultStatuses }); + fetchMock.get(noFilterUrl, { + count: 2, + goalRows: response, + statuses: defaultStatuses, + allGoalIds: [{ id: 4598, goalIds: [4598] }], + }); act(() => renderGoalsAndObjectives()); @@ -305,6 +330,19 @@ describe('Goals and Objectives', () => { { ...goals[0], id: 3 }, ], statuses: defaultStatuses, + allGoalIds: [{ + id: 1, + goalIds: [1], + }, + { + id: 2, + goalIds: [2], + }, + { + id: 3, + goalIds: [3], + }, + ], }); act(() => renderGoalsAndObjectives([1])); // If api request contains 3 we know it included the desired sort. @@ -331,7 +369,7 @@ describe('Goals and Objectives', () => { fetchMock.get( '/api/communication-logs/region/1/recipient/401?sortBy=communicationDate&direction=desc&offset=0&limit=5&format=json&purpose.in[]=RTTAPA%20updates&purpose.in[]=RTTAPA%20Initial%20Plan%20%2F%20New%20Recipient', - { rows: [], count: 0 }, + { rows: [], count: 0, allGoalIds: [] }, ); const goalToUse = { id: 1, @@ -348,10 +386,12 @@ describe('Goals and Objectives', () => { }; const goalCount = 60; const goalsToDisplay = []; + const allGoalIds = []; // eslint-disable-next-line no-plusplus for (let i = 1; i <= goalCount; i++) { const goalText = `This is goal text ${i}.`; goalsToDisplay.push({ ...goalToUse, id: i, goalText }); + allGoalIds.push({ id: i, goalIds: [i] }); } const noFilterUrl = '/api/recipient/401/region/1/goals?sortBy=goalStatus&sortDir=asc&offset=0&limit=10'; fetchMock.get(noFilterUrl, @@ -359,6 +399,7 @@ describe('Goals and Objectives', () => { count: goalCount, goalRows: goalsToDisplay.slice(0, 10), statuses: defaultStatuses, + allGoalIds, }); // Render. @@ -376,6 +417,7 @@ describe('Goals and Objectives', () => { count: goalCount, goalRows: goalsToDisplay.slice(0, 25), statuses: defaultStatuses, + allGoalIds, }); const perPageDropDown = await screen.findByRole('combobox', { name: /select goals per page/i }); userEvent.selectOptions(perPageDropDown, '25'); diff --git a/src/services/recipient.js b/src/services/recipient.js index 02ea3700e2..35068cb2a5 100644 --- a/src/services/recipient.js +++ b/src/services/recipient.js @@ -890,7 +890,13 @@ export async function getGoalsByActivityRecipient( }); // For checkbox selection we only need the primary goal id. - const rolledUpGoalIds = r.goalRows.map((goal) => goal.id); + const rolledUpGoalIds = r.goalRows.map((goal) => { + const bucket = { + id: goal.id, + goalIds: goal.ids, + }; + return bucket; + }); if (limitNum) { return { diff --git a/src/services/recipient.test.js b/src/services/recipient.test.js index 551c57da6b..67ee8dce06 100644 --- a/src/services/recipient.test.js +++ b/src/services/recipient.test.js @@ -878,7 +878,10 @@ describe('Recipient DB service', () => { const { goalRows, allGoalIds } = await getGoalsByActivityRecipient(recipient.id, region, {}); expect(goalRows.length).toBe(3); expect(allGoalIds.length).toBe(3); - expect(allGoalIds).toContain(goals[3].id); + + const goalWithMultipleIds = allGoalIds.find((g) => g.id === goals[2].id); + expect(goalWithMultipleIds).not.toBeNull(); + expect(goalWithMultipleIds.goalIds).toStrictEqual([goals[2].id, goals[1].id]); const doubler = goalRows.find((r) => r.responsesForComparison === 'not sure,dont have to'); expect(doubler).toBeTruthy(); From a207ee639c597392c03fea42a735c874c222b456 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Mon, 3 Jun 2024 22:02:01 -0400 Subject: [PATCH 4/9] much cleaner approach to rtr goals still need to update FE tests --- .../src/components/GoalCards/GoalCards.js | 47 ++++++++++--------- .../components/GoalCards/GoalsCardsHeader.js | 30 +++++++----- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/frontend/src/components/GoalCards/GoalCards.js b/frontend/src/components/GoalCards/GoalCards.js index f056de4d02..f8b009c3d7 100644 --- a/frontend/src/components/GoalCards/GoalCards.js +++ b/frontend/src/components/GoalCards/GoalCards.js @@ -39,7 +39,6 @@ function GoalCards({ // Goal select check boxes. const [selectedGoalCheckBoxes, setSelectedGoalCheckBoxes] = useState({}); const [allGoalsChecked, setAllGoalsChecked] = useState(false); - const [printAllGoals, setPrintAllGoals] = useState(false); // Close/Suspend Reason Modal. const [closeSuspendGoalIds, setCloseSuspendGoalIds] = useState([]); @@ -117,29 +116,35 @@ function GoalCards({ goalsArr.reduce((obj, g) => ({ ...obj, [g.id]: checked }), {}) ); - useEffect(() => { - const checkValues = Object.values(selectedGoalCheckBoxes); - if (checkValues.length - && (checkValues.length === goals.length || checkValues.length === goalsCount) - && checkValues.every((v) => v === true)) { - setAllGoalsChecked(true); - } else if (printAllGoals === true) { - setPrintAllGoals(false); - } - }, [selectedGoalCheckBoxes, allGoalsChecked, printAllGoals, goalsCount, goals.length]); - const selectAllGoalCheckboxSelect = (event) => { const { target: { checked = null } = {} } = event; + // Preserve checked goals on other pages. + const thisPagesGoalIds = goals.map((g) => g.id); + const preservedCheckboxes = Object.keys(selectedGoalCheckBoxes).reduce((obj, key) => { + if (!thisPagesGoalIds.includes(parseInt(key, DECIMAL_BASE))) { + return { ...obj, [key]: selectedGoalCheckBoxes[key] }; + } + return { ...obj }; + }, {}); + if (checked === true) { - setSelectedGoalCheckBoxes(makeGoalCheckboxes(goals, true)); + setSelectedGoalCheckBoxes({ ...makeGoalCheckboxes(goals, true), ...preservedCheckboxes }); + } else { + setSelectedGoalCheckBoxes({ ...makeGoalCheckboxes(goals, false), ...preservedCheckboxes }); + } + }; + + // Check if all goals on the page are checked. + useEffect(() => { + const goalIds = goals.map((g) => g.id); + const countOfCheckedOnThisPage = goalIds.filter((id) => selectedGoalCheckBoxes[id]).length; + if (goals.length === countOfCheckedOnThisPage) { setAllGoalsChecked(true); } else { - setSelectedGoalCheckBoxes(makeGoalCheckboxes(goals, false)); setAllGoalsChecked(false); - setPrintAllGoals(false); } - }; + }, [goals, selectedGoalCheckBoxes]); const handleGoalCheckboxSelect = (event) => { const { target: { checked = null, value = null } = {} } = event; @@ -153,7 +158,6 @@ function GoalCards({ const checkAllGoals = () => { const allIdCheckBoxes = allGoalIds.reduce((obj, g) => ({ ...obj, [g]: true }), {}); setSelectedGoalCheckBoxes(allIdCheckBoxes); - setPrintAllGoals(true); }; const numberOfSelectedGoals = Object.values(selectedGoalCheckBoxes).filter((g) => g).length; @@ -165,14 +169,14 @@ function GoalCards({ const selectedGoalIdsButNumerical = selectedCheckBoxes.map((id) => parseInt(id, DECIMAL_BASE)); const draftSelectedRttapa = goals.filter((g) => selectedGoalIdsButNumerical.includes(g.id) && g.goalStatus === 'Draft').map((g) => g.id); - const allSelectedGoalIds = (() => { + const allSelectedPageGoalIds = (() => { const selection = goals.filter((g) => selectedGoalCheckBoxes[g.id]); - return selection.map((g) => g.ids).flat(); + return selection.map((g) => g.id); })(); const rttapaLink = (() => { if (selectedCheckBoxes && selectedCheckBoxes.length) { - const selectedGoalIdsQuery = allSelectedGoalIds.map((id) => `goalId[]=${encodeURIComponent(id)}`).join('&'); + const selectedGoalIdsQuery = allSelectedPageGoalIds.map((id) => `goalId[]=${encodeURIComponent(id)}`).join('&'); return `/recipient-tta-records/${recipientId}/region/${regionId}/rttapa/new?${selectedGoalIdsQuery}`; } @@ -233,7 +237,7 @@ function GoalCards({ allGoalsChecked={allGoalsChecked} selectAllGoalCheckboxSelect={selectAllGoalCheckboxSelect} selectAllGoals={checkAllGoals} - selectedGoalIds={allSelectedGoalIds} + pageSelectedGoalIds={allSelectedPageGoalIds} perPageChange={perPageChange} pageGoalIds={goals.map((g) => g.id)} showRttapaValidation={showRttapaValidation} @@ -243,6 +247,7 @@ function GoalCards({ shouldDisplayMergeSuccess={shouldDisplayMergeSuccess} dismissMergeSuccess={dismissMergeSuccess} goalBuckets={goalBuckets} + allSelectedGoalIds={selectedGoalCheckBoxes} />
{goals.map((goal, index) => ( diff --git a/frontend/src/components/GoalCards/GoalsCardsHeader.js b/frontend/src/components/GoalCards/GoalsCardsHeader.js index 5e6118ef41..6868639b12 100644 --- a/frontend/src/components/GoalCards/GoalsCardsHeader.js +++ b/frontend/src/components/GoalCards/GoalsCardsHeader.js @@ -30,7 +30,7 @@ export default function GoalCardsHeader({ allGoalsChecked, selectAllGoalCheckboxSelect, selectAllGoals, - selectedGoalIds, + pageSelectedGoalIds, perPageChange, pageGoalIds, showRttapaValidation, @@ -38,6 +38,7 @@ export default function GoalCardsHeader({ canMergeGoals, shouldDisplayMergeSuccess, dismissMergeSuccess, + allSelectedGoalIds, goalBuckets, }) { const [goalMergeGroups, setGoalMergeGroups] = useState([]); @@ -72,16 +73,22 @@ export default function GoalCardsHeader({ const showAddNewButton = hasActiveGrants && hasButtonPermissions; const onPrint = () => { - let goalIdsToPrint = selectedGoalIds; - if (!goalIdsToPrint.length) { - // Loop goalBuckets and get and flat array of all the goalIds. - goalIdsToPrint = goalBuckets.filter( - (bucket) => pageGoalIds.includes(bucket.id), - ).map((bucket) => bucket.goalIds).flat(); + // See if we have goals selected. + let goalsToPrint = Object.keys(allSelectedGoalIds).filter( + (key) => allSelectedGoalIds[key], + ).map((key) => parseInt(key, DECIMAL_BASE)); + + // If we don't just print the page. + if (!goalsToPrint.length) { + goalsToPrint = pageGoalIds; } + // Get all the goals and associated goals from the buckets. + goalsToPrint = goalBuckets.filter( + (bucket) => goalsToPrint.includes(bucket.id), + ).map((bucket) => bucket.goalIds).flat(); history.push(`/recipient-tta-records/${recipientId}/region/${regionId}/rttapa/print${window.location.search}`, { - sortConfig, selectedGoalIds: goalIdsToPrint, + sortConfig, selectedGoalIds: goalsToPrint, }); }; @@ -208,7 +215,7 @@ export default function GoalCardsHeader({ className="display-flex flex-align-center margin-left-3 margin-y-0" onClick={onPrint} > - {`Preview and print ${selectedGoalIds.length > 0 ? 'selected' : ''}`} + {`Preview and print ${pageSelectedGoalIds.length > 0 ? 'selected' : ''}`}
@@ -234,7 +241,7 @@ export default function GoalCardsHeader({ !showRttapaValidation && allGoalsChecked && (numberOfSelectedGoals !== count) ? ( - {`All ${numberOfSelectedGoals} goals on this page are selected.`} + {`All ${pageSelectedGoalIds.length} goals on this page are selected.`}
From b73b6ef0ce7b483c3c5e0e5f49f2a2c3191962af Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Tue, 4 Jun 2024 10:29:24 -0400 Subject: [PATCH 6/9] added test for now logic --- .../pages/__tests__/GoalsObjectives.js | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/frontend/src/pages/RecipientRecord/pages/__tests__/GoalsObjectives.js b/frontend/src/pages/RecipientRecord/pages/__tests__/GoalsObjectives.js index 1f0d5b3148..92352568d0 100644 --- a/frontend/src/pages/RecipientRecord/pages/__tests__/GoalsObjectives.js +++ b/frontend/src/pages/RecipientRecord/pages/__tests__/GoalsObjectives.js @@ -427,4 +427,146 @@ describe('Goals and Objectives', () => { goalsPerPage = screen.queryAllByTestId('goalCard'); expect(goalsPerPage.length).toBe(25); }); + + it('respects select all on a per page basis', async () => { + const goalUrl = '/api/recipient/401/region/1/goals?sortBy=createdOn&sortDir=desc&offset=0&limit=10'; + fetchMock.get(goalUrl, { + count: 12, + goalRows: [ + { ...goals[0], id: 1 }, + { ...goals[0], id: 2 }, + { ...goals[0], id: 3 }, + { ...goals[0], id: 4 }, + { ...goals[0], id: 5 }, + { ...goals[0], id: 6 }, + { ...goals[0], id: 7 }, + { ...goals[0], id: 8 }, + { ...goals[0], id: 9 }, + { ...goals[0], id: 10 }, + ], + statuses: defaultStatuses, + allGoalIds: [{ + id: 1, + goalIds: [1], + }, + { + id: 2, + goalIds: [2], + }, + { + id: 3, + goalIds: [3], + }, + { + id: 4, + goalIds: [4], + }, + { + id: 5, + goalIds: [5], + }, + { + id: 6, + goalIds: [6], + }, + { + id: 7, + goalIds: [7], + }, + { + id: 8, + goalIds: [8], + }, + { + id: 9, + goalIds: [9], + }, + { + id: 10, + goalIds: [10], + }, + ], + }); + act(() => renderGoalsAndObjectives([1])); + expect(await screen.findByText(/1-10 of 12/i)).toBeVisible(); + + // Select All. + const selectAll = await screen.findByRole('checkbox', { name: /select all goals/i }); + userEvent.click(selectAll); + + // Assert all are selected. + const checkboxes = screen.queryAllByRole('checkbox', { name: /select goal/i }); + expect(checkboxes.length).toBe(10); + checkboxes.forEach((checkbox) => { + expect(checkbox).toBeChecked(); + }); + + // Shows 10 selected. + expect(await screen.findByText(/10 selected/i)).toBeVisible(); + + // Change per page. + const goalUrlMore = '/api/recipient/401/region/1/goals?sortBy=createdOn&sortDir=desc&offset=10&limit=10'; + fetchMock.get(goalUrlMore, { + count: 12, + goalRows: [ + { ...goals[0], id: 11 }, + { ...goals[0], id: 12 }, + ], + statuses: defaultStatuses, + allGoalIds: [{ + id: 11, + goalIds: [11], + }, + { + id: 12, + goalIds: [12], + }, + ], + }); + + // Click page 2. + const pageTwo = await screen.findByRole('link', { name: /go to page number 2/i }); + userEvent.click(pageTwo); + + expect(await screen.findByText(/11-12 of 12/i)).toBeVisible(); + + // Shows 10 selected. + expect(await screen.findByText(/10 selected/i)).toBeVisible(); + + // Assert all selected is NOT checked. + const selectAllNext = await screen.findByRole('checkbox', { name: /select all goals/i }); + expect(selectAllNext).not.toBeChecked(); + + // Get all the checkboxes. + const checkboxesNext = screen.queryAllByRole('checkbox', { name: /select goal/i }); + expect(checkboxesNext.length).toBe(2); + + // Check the second one. + userEvent.click(checkboxesNext[1]); + + // Shows 11 selected. + expect(await screen.findByText(/11 selected/i)).toBeVisible(); + + // Select All. + userEvent.click(selectAllNext); + + // Assert all are selected. + const checkboxesNextAll = screen.queryAllByRole('checkbox', { name: /select goal/i }); + expect(checkboxesNextAll.length).toBe(2); + checkboxesNextAll.forEach((checkbox) => { + expect(checkbox).toBeChecked(); + }); + + // Shows 12 selected. + expect(await screen.findByText(/12 selected/i)).toBeVisible(); + + // Uncheck the second checkbox. + userEvent.click(checkboxesNext[1]); + + // Assert the select all check box is not checked. + expect(selectAllNext).not.toBeChecked(); + + // Shows 11 selected. + expect(await screen.findByText(/11 selected/i)).toBeVisible(); + }); }); From afaa385b6f2bcff4153d8bfc08b8baca17e5386e Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Tue, 4 Jun 2024 11:01:47 -0400 Subject: [PATCH 7/9] put on sandbox --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index da6d6a55e2..f4e2f4c314 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -423,7 +423,7 @@ parameters: default: "mb/TTAHUB-2660/activity-report-updates-for-reduced-objectives" type: string sandbox_git_branch: # change to feature branch to test deployment - default: "mb/TTAHUB-2530/update-RTR-objective-form" + default: "al-ttahub-2942-fix-number-of-goals" type: string prod_new_relic_app_id: default: "877570491" From 391c16fcd00739f6251d3892a38924917bde15a5 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Wed, 5 Jun 2024 10:39:18 -0400 Subject: [PATCH 8/9] add new clear selection feature from two year old design --- .../src/components/GoalCards/GoalCards.js | 4 ++-- .../components/GoalCards/GoalsCardsHeader.js | 13 ++++++++---- .../GoalCards/__tests__/GoalCards.js | 21 +++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/GoalCards/GoalCards.js b/frontend/src/components/GoalCards/GoalCards.js index f8b009c3d7..b3e0876b89 100644 --- a/frontend/src/components/GoalCards/GoalCards.js +++ b/frontend/src/components/GoalCards/GoalCards.js @@ -155,8 +155,8 @@ function GoalCards({ } }; - const checkAllGoals = () => { - const allIdCheckBoxes = allGoalIds.reduce((obj, g) => ({ ...obj, [g]: true }), {}); + const checkAllGoals = (isClear) => { + const allIdCheckBoxes = allGoalIds.reduce((obj, g) => ({ ...obj, [g]: !isClear }), {}); setSelectedGoalCheckBoxes(allIdCheckBoxes); }; diff --git a/frontend/src/components/GoalCards/GoalsCardsHeader.js b/frontend/src/components/GoalCards/GoalsCardsHeader.js index 21d029cecc..4304b805e5 100644 --- a/frontend/src/components/GoalCards/GoalsCardsHeader.js +++ b/frontend/src/components/GoalCards/GoalsCardsHeader.js @@ -106,6 +106,7 @@ export default function GoalCardsHeader({ })(); const hasGoalsSelected = pageSelectedGoalIds ? pageSelectedGoalIds.length > 0 : false; + const showClearAllAlert = numberOfSelectedGoals === count; return (
@@ -240,16 +241,20 @@ export default function GoalCardsHeader({ )} { - !showRttapaValidation && allGoalsChecked && (numberOfSelectedGoals !== count) + !showRttapaValidation && allGoalsChecked ? ( - {`All ${pageSelectedGoalIds.length} goals on this page are selected.`} + {showClearAllAlert + ? 'All 15 goals are selected.' + : `All ${pageSelectedGoalIds.length} goals on this page are selected.`} ) diff --git a/frontend/src/components/GoalCards/__tests__/GoalCards.js b/frontend/src/components/GoalCards/__tests__/GoalCards.js index 5447292664..31fb57d644 100644 --- a/frontend/src/components/GoalCards/__tests__/GoalCards.js +++ b/frontend/src/components/GoalCards/__tests__/GoalCards.js @@ -491,6 +491,27 @@ describe('Goals Table', () => { expect(screen.queryByText(/7 selected/i)).toBeNull(); }); + it('Shows the clear selection button and clears when clicked', async () => { + const selectAll = await screen.findByRole('checkbox', { name: /deselect all goals/i }); + fireEvent.click(selectAll); + expect(await screen.findByText(/6 selected/i)).toBeVisible(); + + const selectAllPages = await screen.findByRole('button', { name: /select all 7 goals/i }); + fireEvent.click(selectAllPages); + + expect(screen.queryByText(/7 selected/i)).toBeVisible(); + + const clearSelection = await screen.findByRole('button', { name: /clear selection/i }); + fireEvent.click(clearSelection); + + expect(screen.queryByText(/7 selected/i)).toBeNull(); + // verify all check boxes are unchecked. + const checkBoxes = screen.queryAllByTestId('selectGoalTestId'); + checkBoxes.forEach((checkBox) => { + expect(checkBox.checked).toBe(false); + }); + }); + it('Deselect via pill', async () => { const selectAll = await screen.findByRole('checkbox', { name: /deselect all goals/i }); fireEvent.click(selectAll); From ee833cc5f14d517151b5b59fa6918e79ed02f624 Mon Sep 17 00:00:00 2001 From: Adam Levin Date: Wed, 5 Jun 2024 10:52:39 -0400 Subject: [PATCH 9/9] update test and removed hard coded count --- frontend/src/components/GoalCards/GoalsCardsHeader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/GoalCards/GoalsCardsHeader.js b/frontend/src/components/GoalCards/GoalsCardsHeader.js index 4304b805e5..1086b88a46 100644 --- a/frontend/src/components/GoalCards/GoalsCardsHeader.js +++ b/frontend/src/components/GoalCards/GoalsCardsHeader.js @@ -245,7 +245,7 @@ export default function GoalCardsHeader({ ? ( {showClearAllAlert - ? 'All 15 goals are selected.' + ? `All ${count} goals are selected.` : `All ${pageSelectedGoalIds.length} goals on this page are selected.`}