From d1b2551b991647fdefb904c2a6701e666ecd3833 Mon Sep 17 00:00:00 2001 From: Frank Niessink Date: Fri, 11 Feb 2022 23:46:20 +0100 Subject: [PATCH] Add the metric sort column to the settings panel so that the button to reset all settinga also resets the sort column. Closes #3412. --- components/frontend/src/AppUI.js | 25 +++++++ components/frontend/src/PageContent.js | 6 ++ .../frontend/src/header_footer/ViewPanel.js | 72 ++++++++++++++----- .../src/header_footer/ViewPanel.test.js | 20 +++++- components/frontend/src/report/Report.js | 25 ++----- components/frontend/src/report/Report.test.js | 40 +++++------ docs/src/changelog.md | 1 + 7 files changed, 129 insertions(+), 60 deletions(-) diff --git a/components/frontend/src/AppUI.js b/components/frontend/src/AppUI.js index 441e329464..68a61f32e8 100644 --- a/components/frontend/src/AppUI.js +++ b/components/frontend/src/AppUI.js @@ -42,7 +42,25 @@ export function AppUI({ const [hiddenColumns, toggleHiddenColumn, clearHiddenColumns] = useURLSearchQuery(history, "hidden_columns", "array"); const [hideMetricsNotRequiringAction, setHideMetricsNotRequiringAction] = useURLSearchQuery(history, "hide_metrics_not_requiring_action", "boolean", false); const [nrDates, setNrDates] = useURLSearchQuery(history, "nr_dates", "integer", 1); + const [sortColumn, setSortColumn] = useURLSearchQuery(history, "sort_column", "string", null) + const [sortDirection, setSortDirection] = useURLSearchQuery(history, "sort_direction", "string", "ascending") const [visibleDetailsTabs, toggleVisibleDetailsTab, clearVisibleDetailsTabs] = useURLSearchQuery(history, "tabs", "array"); + + function handleSort(column) { + if (column === null) { + setSortColumn(null) // Stop sorting + return + } + if (sortColumn === column) { + if (sortDirection === 'descending') { + setSortColumn(null) // Cycle through ascending->descending->no sort as long as the user clicks the same column + } + setSortDirection(sortDirection === 'ascending' ? 'descending' : 'ascending') + } else { + setSortColumn(column) + } + } + return (
@@ -66,6 +84,10 @@ export function AppUI({ setDateOrder={setDateOrder} setHideMetricsNotRequiringAction={setHideMetricsNotRequiringAction} setNrDates={setNrDates} + setSortColumn={setSortColumn} + setSortDirection={setSortDirection} + sortColumn={sortColumn} + sortDirection={sortDirection} toggleHiddenColumn={toggleHiddenColumn} visibleDetailsTabs={visibleDetailsTabs} />} @@ -79,6 +101,7 @@ export function AppUI({ dateInterval={dateInterval} dateOrder={dateOrder} go_home={go_home} + handleSort={handleSort} hiddenColumns={hiddenColumns} hideMetricsNotRequiringAction={hideMetricsNotRequiringAction} history={history} @@ -91,6 +114,8 @@ export function AppUI({ report_uuid={report_uuid} reports={reports} reports_overview={reports_overview} + sortColumn={sortColumn} + sortDirection={sortDirection} toggleVisibleDetailsTab={toggleVisibleDetailsTab} visibleDetailsTabs={visibleDetailsTabs} /> diff --git a/components/frontend/src/PageContent.js b/components/frontend/src/PageContent.js index 62a09f8f88..b594251c0a 100644 --- a/components/frontend/src/PageContent.js +++ b/components/frontend/src/PageContent.js @@ -8,6 +8,7 @@ export function PageContent({ current_report, dateInterval, dateOrder, + handleSort, hiddenColumns, hideMetricsNotRequiringAction, history, @@ -21,6 +22,8 @@ export function PageContent({ report_uuid, reports, reports_overview, + sortColumn, + sortDirection, toggleVisibleDetailsTab, visibleDetailsTabs }) { @@ -33,6 +36,7 @@ export function PageContent({ dateInterval={dateInterval} dateOrder={dateOrder} go_home={go_home} + handleSort={handleSort} hiddenColumns={hiddenColumns} hideMetricsNotRequiringAction={hideMetricsNotRequiringAction} history={history} @@ -42,6 +46,8 @@ export function PageContent({ report={current_report} reports={reports} report_date={report_date} + sortColumn={sortColumn} + sortDirection={sortDirection} toggleVisibleDetailsTab={toggleVisibleDetailsTab} visibleDetailsTabs={visibleDetailsTabs} /> diff --git a/components/frontend/src/header_footer/ViewPanel.js b/components/frontend/src/header_footer/ViewPanel.js index 636babca32..e702a8109a 100644 --- a/components/frontend/src/header_footer/ViewPanel.js +++ b/components/frontend/src/header_footer/ViewPanel.js @@ -17,6 +17,10 @@ export function ViewPanel({ setDateOrder, setHideMetricsNotRequiringAction, setNrDates, + setSortColumn, + setSortDirection, + sortColumn, + sortDirection, toggleHiddenColumn, visibleDetailsTabs }) { @@ -48,8 +52,9 @@ export function ViewPanel({ hiddenColumns.length === 0 && nrDates === 1 && dateInterval === 7 && - dateOrder === "descending" - + dateOrder === "descending" && + sortColumn === null && + sortDirection === "ascending" } onClick={() => { clearVisibleDetailsTabs(); @@ -58,6 +63,8 @@ export function ViewPanel({ setNrDates(1); setDateInterval(7); setDateOrder("descending"); + setSortColumn(null); + setSortDirection("ascending") }} inverted > @@ -77,15 +84,36 @@ export function ViewPanel({
Visible columns
- - - - - - - - - + + + + + + + + + + +
+ +
Sort column
+ + + + + + + + + + + +
+ +
Sort direction
+ + +
@@ -110,15 +138,15 @@ export function ViewPanel({
Date order
- - + +
) } -function ColumnMenuItem({ column, hiddenColumns, toggleHiddenColumn }) { +function VisibleColumnMenuItem({ column, hiddenColumns, toggleHiddenColumn }) { return (
{ event.preventDefault(); toggleHiddenColumn(column) }} tabIndex={0}> toggleHiddenColumn(column)}> @@ -128,6 +156,16 @@ function ColumnMenuItem({ column, hiddenColumns, toggleHiddenColumn }) { ) } +function SortColumnMenuItem({ column, sortColumn, setSortColumn }) { + return ( +
{ event.preventDefault(); setSortColumn(column) }} tabIndex={0}> + setSortColumn(sortColumn === column ? null : column)}> + {capitalize(column === "name" ? "metric" : column)} + +
+ ) +} + function DateIntervalMenuItem({ nr, dateInterval, setDateInterval }) { return (
{ event.preventDefault(); setDateInterval(nr) }} tabIndex={0}> @@ -138,10 +176,10 @@ function DateIntervalMenuItem({ nr, dateInterval, setDateInterval }) { ) } -function DateOrderMenuItem({ order, dateOrder, setDateOrder }) { +function SortOrderMenuItem({ order, sortOrder, setSortOrder }) { return ( -
{ event.preventDefault(); setDateOrder(order) }} tabIndex={0}> - setDateOrder(order)}> +
{ event.preventDefault(); setSortOrder(order) }} tabIndex={0}> + setSortOrder(order)}> {capitalize(order)}
diff --git a/components/frontend/src/header_footer/ViewPanel.test.js b/components/frontend/src/header_footer/ViewPanel.test.js index 81f2acf4cc..aec20dbb9a 100644 --- a/components/frontend/src/header_footer/ViewPanel.test.js +++ b/components/frontend/src/header_footer/ViewPanel.test.js @@ -39,6 +39,8 @@ it('resets the settings', async () => { const setDateOrder = jest.fn(); const setHideMetricsNotRequiringAction = jest.fn(); const setNrDates = jest.fn(); + const setSortColumn = jest.fn(); + const setSortDirection = jest.fn(); await act(async () => { render( { setDateOrder={setDateOrder} setHideMetricsNotRequiringAction={setHideMetricsNotRequiringAction} setNrDates={setNrDates} + setSortColumn={setSortColumn} + setSortDirection={setSortDirection} + sortColumn="status" + sortDirection="descending" visibleDetailsTabs={["tab"]} /> ) @@ -64,6 +70,8 @@ it('resets the settings', async () => { expect(setDateOrder).toHaveBeenCalled() expect(setNrDates).toHaveBeenCalled() expect(setHideMetricsNotRequiringAction).toHaveBeenCalled() + expect(setSortColumn).toHaveBeenCalledWith(null) + expect(setSortDirection).toHaveBeenCalledWith("ascending") }) it('does not reset the settings when all have the default value', async () => { @@ -73,6 +81,8 @@ it('does not reset the settings when all have the default value', async () => { const setDateOrder = jest.fn(); const setHideMetricsNotRequiringAction = jest.fn(); const setNrDates = jest.fn(); + const setSortColumn = jest.fn(); + const setSortDirection = jest.fn(); await act(async () => { render( { setDateOrder={setDateOrder} setHideMetricsNotRequiringAction={setHideMetricsNotRequiringAction} setNrDates={setNrDates} + sortColumn={null} + sortDirection="ascending" visibleDetailsTabs={[]} /> ) @@ -98,6 +110,8 @@ it('does not reset the settings when all have the default value', async () => { expect(setDateOrder).not.toHaveBeenCalled() expect(setNrDates).not.toHaveBeenCalled() expect(setHideMetricsNotRequiringAction).not.toHaveBeenCalled() + expect(setSortColumn).not.toHaveBeenCalled() + expect(setSortDirection).not.toHaveBeenCalled() }) it("hides the metrics not requiring action", async () => { @@ -173,7 +187,7 @@ it("hides a column by keypress", async () => { visibleDetailsTabs={[]} /> ) - userEvent.type(screen.getByText(/Comment/), "{Enter}") + userEvent.type(screen.getAllByText(/Comment/)[0], "{Enter}") }); expect(toggleHiddenColumn).toHaveBeenCalledWith("comment") }) @@ -284,7 +298,7 @@ it("sorts the dates descending", async () => { visibleDetailsTabs={[]} /> ) - fireEvent.click(screen.getByText(/Descending/)) + fireEvent.click(screen.getAllByText(/Descending/)[1]) }); expect(setDateOrder).toHaveBeenCalledWith("descending") }) @@ -300,7 +314,7 @@ it("sorts the dates ascending by keypress", async () => { visibleDetailsTabs={[]} /> ) - userEvent.type(screen.getByText(/Ascending/), "{Enter}") + userEvent.type(screen.getAllByText(/Ascending/)[1], "{Enter}") }); expect(setDateOrder).toHaveBeenCalledWith("ascending") }) \ No newline at end of file diff --git a/components/frontend/src/report/Report.js b/components/frontend/src/report/Report.js index c6123d5333..b351d29bb0 100644 --- a/components/frontend/src/report/Report.js +++ b/components/frontend/src/report/Report.js @@ -8,7 +8,7 @@ import { CardDashboard } from '../dashboard/CardDashboard'; import { DataModel } from '../context/DataModel'; import { accessGranted, EDIT_REPORT_PERMISSION, Permissions } from '../context/Permissions'; import { set_report_attribute } from '../api/report'; -import { get_subject_name, useURLSearchQuery } from '../utils'; +import { get_subject_name } from '../utils'; import { ReportTitle } from './ReportTitle'; function ReportDashboard({ report, onClick, setTags, tags, reload }) { @@ -59,6 +59,7 @@ export function Report({ dateInterval, dateOrder, go_home, + handleSort, hiddenColumns, hideMetricsNotRequiringAction, history, @@ -68,6 +69,8 @@ export function Report({ report, report_date, reports, + sortColumn, + sortDirection, toggleVisibleDetailsTab, visibleDetailsTabs }) { @@ -84,24 +87,6 @@ export function Report({ setTags(prev_tags => prev_tags.filter(tag => Object.keys(report.summary_by_tag || {}).includes(tag))) }, [report]); - const [sortColumn, setSortColumn] = useURLSearchQuery(history, "sort_column", "string", null) - const [sortDirection, setSortDirection] = useURLSearchQuery(history, "sort_direction", "string", "ascending") - - function handleSort(column) { - if (column === null) { - setSortColumn(null) // Stop sorting - return - } - if (sortColumn === column) { - if (sortDirection === 'descending') { - setSortColumn(null) // Cycle through ascending->descending->no sort as long as the user clicks the same column - } - setSortDirection(sortDirection === 'ascending' ? 'descending' : 'ascending') - } else { - setSortColumn(column) - } - } - if (!report) { return } @@ -128,7 +113,7 @@ export function Report({ changed_fields={changed_fields} dateInterval={dateInterval} dateOrder={dateOrder} - handleSort={(column) => handleSort(column)} + handleSort={handleSort} hiddenColumns={hiddenColumns} hideMetricsNotRequiringAction={hideMetricsNotRequiringAction} nrDates={nrDates} diff --git a/components/frontend/src/report/Report.test.js b/components/frontend/src/report/Report.test.js index 1acfb2fe0d..641e53caaf 100644 --- a/components/frontend/src/report/Report.test.js +++ b/components/frontend/src/report/Report.test.js @@ -4,7 +4,6 @@ import { Report } from './Report'; import { DataModel } from '../context/DataModel'; import { EDIT_REPORT_PERMISSION, Permissions } from '../context/Permissions'; -let mockHistory = { location: {}, replace: () => {/* No implementatin needed */ } }; const datamodel = { subjects: { subject_type: { name: "Subject type", metrics: ['metric_type'] } }, metrics: { metric_type: { tags: [] } } } const report = { report_uuid: "report_uuid", @@ -35,16 +34,18 @@ const report = { } }; -function renderReport(reportToRender, { report_date = null, hiddenColumns = [], history = mockHistory } = {}) { +function renderReport(reportToRender, { report_date = null, hiddenColumns = [], handleSort = null, sortColumn = null, sortDirection = "ascending" } = {}) { render( @@ -68,42 +69,41 @@ it('shows an error message if there was no report', () => { }); it('hides columns on load', async () => { - mockHistory.location.search = "?hidden_columns=status" renderReport(report, { hiddenColumns: ["status"] }) expect(screen.queryByText(/Status/)).toBe(null) }); it('sorts the column', async () => { - let history = { location: {}, replace: jest.fn() }; - renderReport(report, { history: history }) - fireEvent.click(screen.getByText(/Tags/)) - expect(history.replace).toHaveBeenCalledWith({ search: "?sort_column=tags" }) + let handleSort = jest.fn(); + renderReport(report, { handleSort: handleSort }) + fireEvent.click(screen.getByText(/Comment/)) + expect(handleSort).toHaveBeenCalledWith("comment") }); it('sorts the column descending', async () => { - let history = { location: { search: "?sort_column=comment" }, replace: jest.fn() }; - renderReport(report, { history: history }) + let handleSort = jest.fn(); + renderReport(report, { sortColumn: "comment", handleSort: handleSort }) fireEvent.click(screen.getByText(/Comment/)) - expect(history.replace).toHaveBeenCalledWith({ search: "?sort_column=comment&sort_direction=descending" }) + expect(handleSort).toHaveBeenCalledWith("comment") }); it('stops sorting', async () => { - let history = { location: { search: "?sort_column=issues&sort_direction=descending" }, replace: jest.fn() }; - renderReport(report, { history: history }) + let handleSort = jest.fn(); + renderReport(report, { sortColumn: "issues", sortDirection: "descending", handleSort: handleSort }) fireEvent.click(screen.getByText(/Issues/)) - expect(history.replace).toHaveBeenCalledWith({ search: "?sort_direction=descending" }) + expect(handleSort).toHaveBeenCalledWith("issues") }); it('stop sorting on add metric', async () => { - let history = { location: { search: "?sort_column=measurement&sort_direction=descending" }, replace: jest.fn() }; - renderReport(report, { history: history }) + let handleSort = jest.fn(); + renderReport(report, { sortColumn: "status", handleSort: handleSort }) await act(async () => fireEvent.click(screen.getByText(/Add metric/))) - expect(history.replace).toHaveBeenCalledWith({ search: "?sort_direction=descending" }) + expect(handleSort).toHaveBeenCalledWith(null) }) it('sorts another column', async () => { - let history = { location: { search: "?sort_column=tags" }, replace: jest.fn() }; - renderReport(report, { history: history }) + let handleSort = jest.fn(); + renderReport(report, { sortColumn: "issues", handleSort: handleSort }) fireEvent.click(screen.getByText(/Comment/)) - expect(history.replace).toHaveBeenCalledWith({ search: "?sort_column=comment" }) + expect(handleSort).toHaveBeenCalledWith("comment") }); diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 1362aaa544..964fb8c133 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -26,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add a button to the settings panel to reset all settings to their default values. Closes [#3183](https://github.com/ICTU/quality-time/issues/3183). - Allow for adding an end date to the status of measurement entities. After the end date passes, the measurement entity is considered to be 'Unconfirmed' again. This makes it possible to mark an entity as e.g. won't fix for a certain period of time. Closes [#3332](https://github.com/ICTU/quality-time/issues/3332). - Add a 'time remaining' metric that measures the number of days remaining until a future date. Use the 'calendar date' source to set that date. Closes [#3366](https://github.com/ICTU/quality-time/issues/3366). +- Add the metric sort column to the settings panel so that the button to reset all settinga also resets the sort column. Closes [#3412](https://github.com/ICTU/quality-time/issues/3412). ## v3.32.0 - 2022-01-24