Skip to content

Commit

Permalink
Merge pull request #443 from PagerDuty/improve-filters
Browse files Browse the repository at this point in the history
  • Loading branch information
gsreynolds authored Jun 13, 2024
2 parents eb32d5f + e568de2 commit eb7ca7b
Show file tree
Hide file tree
Showing 18 changed files with 181 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cd-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- name: Install project dependencies (via cache)
run: yarn --prefer-offline
run: yarn --frozen-lockfile --prefer-offline --ignore-optional
- name: Build application bundle
run: yarn build
- name: Deploy
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ jobs:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- name: Install project dependencies (via cache)
run: yarn --prefer-offline
run: yarn --frozen-lockfile --prefer-offline --ignore-optional
- name: Run ESLint
run: npx eslint src --ext .js,.jsx,.ts,.tsx --exit-on-fatal-error
9 changes: 5 additions & 4 deletions .github/workflows/test-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- name: Install project dependencies (via cache)
run: yarn --prefer-offline
run: yarn --frozen-lockfile --prefer-offline --ignore-optional

jest:
needs: install
Expand All @@ -54,15 +54,15 @@ jobs:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- name: Install project dependencies (via cache)
run: yarn --prefer-offline
run: yarn --frozen-lockfile --prefer-offline --ignore-optional
- name: Run Jest Tests
run: yarn jest

cypress-run:
needs: install
runs-on: ubuntu-latest
container:
image: cypress/browsers:node-20.5.0-chrome-114.0.5735.133-1-ff-114.0.2-edge-114.0.1823.51-1
image: cypress/browsers:node-20.14.0-chrome-125.0.6422.141-1-ff-126.0.1-edge-125.0.2535.85-1
options: --user 1001
strategy:
fail-fast: false
Expand Down Expand Up @@ -102,7 +102,7 @@ jobs:
# Starts web server for E2E tests - replace with your own server invocation
# https://docs.cypress.io/guides/continuous-integration/introduction#Boot-your-server
start: yarn start
wait-on: 'http://localhost:3000' # Waits for above
wait-on: 'http://127.0.0.1:3000' # Waits for above
browser: chrome
# Records to Cypress Cloud
# https://docs.cypress.io/guides/cloud/projects#Set-up-a-project-to-record
Expand All @@ -111,6 +111,7 @@ jobs:
spec: cypress/e2e/${{ matrix.e2e }}
parallel: false # Don't use Cypress in-built parallelization
install: true
install-command: yarn --frozen-lockfile --prefer-offline --ignore-optional
cache-key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
env:
# For recording and parallelization to work you must set your CYPRESS_RECORD_KEY
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# MUST match .node-version
nodejs 20.5.1
yarn 1.22.19
nodejs 20.14.0
yarn 1.22.22
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ If you wish to maintain + deploy your own version of PagerDuty Live, we recommen

#### Local Development

1. Install [NodeJS v20.5.1](https://nodejs.org/en/blog/release/v20.5.1/) via [`asdf install`](https://github.com/asdf-vm/asdf) / [`nvm`](https://github.com/nvm-sh/nvm)
1. Install [NodeJS v20.14.0](https://nodejs.org/en/blog/release/v20.14.0/) via [`asdf install`](https://github.com/asdf-vm/asdf) / [`nvm`](https://github.com/nvm-sh/nvm)

2. `$ git clone` repo to desired destination and `$ cd pd-live-react` into directory

Expand Down
2 changes: 1 addition & 1 deletion cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = defineConfig({
config.env.PD_USER_TOKEN = process.env.VITE_PD_USER_TOKEN;
return config;
},
baseUrl: 'http://localhost:3000/pd-live-react',
baseUrl: 'http://127.0.0.1:3000/pd-live-react',
specPattern: 'cypress/e2e/**/*.spec.{js,ts,jsx,tsx}',
testIsolation: true,
},
Expand Down
8 changes: 8 additions & 0 deletions cypress/e2e/Search/search.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,12 @@ describe('Search Incidents', { failFast: { enabled: true } }, () => {
cy.get('#service-filter-icon').realHover();
cy.get('button[aria-label="Clear Filter"]').filter(':visible').click();
});

it('Column filtering on Service column for `zzzzzz` returns no incidents and clear filters button', () => {
cy.get('#service-filter-icon').realHover();
cy.get('input[placeholder="Filter"]').filter(':visible').click().type('zzzzzz');
cy.get('.empty-incidents-badge').should('be.visible');
cy.get('#clear-filters-button').filter(':visible').click();
waitForIncidentTable();
});
});
55 changes: 31 additions & 24 deletions src/components/IncidentTable/IncidentTableComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
import {
selectIncidentTableRows as selectIncidentTableRowsConnected,
updateIncidentTableState as updateIncidentTableStateConnected,
CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED,
} from 'src/redux/incident_table/actions';

import EmptyIncidentsComponent from './subcomponents/EmptyIncidentsComponent';
Expand Down Expand Up @@ -120,7 +121,7 @@ const doCsvExport = (tableData) => {

const IncidentTableComponent = () => {
const {
incidentTableState, incidentTableColumns,
incidentTableState, incidentTableColumns, status: incidentTableStatus,
} = useSelector((state) => state.incidentTable);
const {
status: incidentActionsStatus,
Expand Down Expand Up @@ -221,7 +222,8 @@ const IncidentTableComponent = () => {
// Debouncing for table state
const debouncedUpdateIncidentTableState = useDebouncedCallback((state, action) => {
// Only update store with sorted and column resizing state
if (action.type === 'toggleSortBy' || action.type === 'columnDoneResizing') {
// and filter state
if (action.type === 'toggleSortBy' || action.type === 'columnDoneResizing' || action.type === 'setFilter') {
updateIncidentTableState(state);
}
}, 100);
Expand Down Expand Up @@ -251,7 +253,7 @@ const IncidentTableComponent = () => {
// Set initial state from store
initialState: incidentTableState,
// Handle updates to table
stateReducer: (newState, action) => debouncedUpdateIncidentTableState(newState, action),
stateReducer: debouncedUpdateIncidentTableState,
},
// Plugins
useFilters,
Expand Down Expand Up @@ -327,19 +329,17 @@ const IncidentTableComponent = () => {
},
);

// save filters when the user changes them
useEffect(() => {
updateIncidentTableState({
...incidentTableState,
filters: tableInstance.state.filters,
});
}, [tableInstance.state.filters]);

// Update table filters when columns change
useEffect(() => {
tableInstance.setAllFilters(incidentTableState.filters);
}, [columns]);

useEffect(() => {
if (incidentTableStatus === CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED) {
tableInstance.setAllFilters(incidentTableState.filters);
}
}, [incidentTableStatus]);

const {
getTableProps,
getTableBodyProps,
Expand Down Expand Up @@ -536,19 +536,26 @@ const IncidentTableComponent = () => {
</MenuItem>
</ContextMenu>
</Box>
<Box {...getTableBodyProps()}>
<FixedSizeList
className="incident-table-fixed-list"
height={tableHeight - 45}
itemCount={rows.length}
itemSize={60}
itemKey={(index) => rows[index].id}
itemData={rows}
width={totalColumnsWidth + scrollBarSize}
>
{MyIncidentRow}
</FixedSizeList>
</Box>
{ rows.length > 0 && (
<Box {...getTableBodyProps()}>
<FixedSizeList
className="incident-table-fixed-list"
height={tableHeight - 45}
itemCount={rows.length}
itemSize={60}
itemKey={(index) => rows[index].id}
itemData={rows}
width={totalColumnsWidth + scrollBarSize}
>
{MyIncidentRow}
</FixedSizeList>
</Box>
)}
{ rows.length === 0 && (
<EmptyIncidentsComponent
message={t('No incidents match your search criteria.')}
/>
)}
<GetAllModal
isOpen={displayGetAllModal}
onClose={() => setDisplayGetAllModal(false)}
Expand Down
22 changes: 20 additions & 2 deletions src/components/QuerySettings/QuerySettingsComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React from 'react';
import React, {
useMemo,
} from 'react';

import {
useSelector, useDispatch,
Expand Down Expand Up @@ -28,6 +30,7 @@ import DatePickerComponent from './subcomponents/DatePickerComponent';
import StatusQueryComponent from './subcomponents/StatusQueryComponent';
import UrgencyQueryComponent from './subcomponents/UrgencyQueryComponent';
import PriorityQueryComponent from './subcomponents/PriorityQueryComponent';
import ColumnFilterIndicatorComponent from './subcomponents/ColumnFilterIndicatorComponent';

import './QuerySettingsComponent.scss';

Expand All @@ -52,6 +55,10 @@ const QuerySettingsComponent = () => {
} = useSelector(
(state) => state.querySettings,
);
const {
filters,
} = useSelector((state) => state.incidentTable.incidentTableState);

const dispatch = useDispatch();
const updateQuerySettingsServices = (newServiceIds) => {
dispatch(updateQuerySettingsServicesConnected(newServiceIds));
Expand All @@ -66,6 +73,13 @@ const QuerySettingsComponent = () => {
dispatch(updateQuerySettingsTeamsConnected(newTeamIds));
};

const filterCount = useMemo(() => {
if (filters instanceof Array) {
return filters.length;
}
return 0;
}, [filters]);

return (
<Box m={4}>
<Flex
Expand Down Expand Up @@ -117,7 +131,11 @@ const QuerySettingsComponent = () => {
isMulti
/>
</BoxForInput>
{/* <GlobalSearchComponent /> */}
{ filterCount > 0 && (
<BoxForInput label={`${t('Column Filters')}:`}>
<ColumnFilterIndicatorComponent />
</BoxForInput>
)}
</Flex>
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, {
useMemo,
} from 'react';

import {
useSelector,
useDispatch,
} from 'react-redux';

import {
useTranslation,
} from 'react-i18next';

import {
Flex,
Button,
Tag,
} from '@chakra-ui/react';

import {
clearIncidentTableFilters as clearIncidentTableFiltersConnected,
} from 'src/redux/incident_table/actions';

const ColumnFilterIndicatorComponent = () => {
const {
t,
} = useTranslation();
const {
filters,
} = useSelector((state) => state.incidentTable.incidentTableState);

const dispatch = useDispatch();
const clearIncidentTableFilters = () => {
dispatch(clearIncidentTableFiltersConnected());
};

const filterCount = useMemo(() => {
if (filters instanceof Array) {
return filters.length;
}
return 0;
}, [filters]);

return (
<Flex>
<Tag colorScheme="blue">
{filterCount}
</Tag>
<Button
id="clear-filters-button"
size="xs"
ml={1}
colorScheme="red"
aria-label="clear"
onClick={clearIncidentTableFilters}
>
{t('Clear')}
</Button>
</Flex>
);
};

export default ColumnFilterIndicatorComponent;
7 changes: 7 additions & 0 deletions src/redux/incident_table/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const UPDATE_INCIDENT_TABLE_STATE_COMPLETED = 'UPDATE_INCIDENT_TABLE_STAT
export const SELECT_INCIDENT_TABLE_ROWS_REQUESTED = 'SELECT_INCIDENT_TABLE_ROWS_REQUESTED';
export const SELECT_INCIDENT_TABLE_ROWS_COMPLETED = 'SELECT_INCIDENT_TABLE_ROWS_COMPLETED';

export const CLEAR_INCIDENT_TABLE_FILTERS_REQUESTED = 'CLEAR_INCIDENT_TABLE_FILTERS_REQUESTED';
export const CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED = 'CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED';

// Define Actions

export const saveIncidentTable = (updatedIncidentTableColumns) => ({
Expand All @@ -35,3 +38,7 @@ export const selectIncidentTableRows = (allSelected, selectedCount, selectedRows
selectedCount,
selectedRows,
});

export const clearIncidentTableFilters = () => ({
type: CLEAR_INCIDENT_TABLE_FILTERS_REQUESTED,
});
14 changes: 14 additions & 0 deletions src/redux/incident_table/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
UPDATE_INCIDENT_TABLE_STATE_COMPLETED,
SELECT_INCIDENT_TABLE_ROWS_REQUESTED,
SELECT_INCIDENT_TABLE_ROWS_COMPLETED,
CLEAR_INCIDENT_TABLE_FILTERS_REQUESTED,
CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED,
} from './actions';

const defaultColumns = [
Expand Down Expand Up @@ -70,6 +72,18 @@ const incidentTable = produce(
draft.status = SELECT_INCIDENT_TABLE_ROWS_COMPLETED;
break;

case CLEAR_INCIDENT_TABLE_FILTERS_REQUESTED:
draft.status = CLEAR_INCIDENT_TABLE_FILTERS_REQUESTED;
break;

case CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED:
draft.incidentTableState = {
...draft.incidentTableState,
filters: [],
};
draft.status = CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED;
break;

default:
break;
}
Expand Down
12 changes: 12 additions & 0 deletions src/redux/incident_table/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
UPDATE_INCIDENT_TABLE_STATE_COMPLETED,
SELECT_INCIDENT_TABLE_ROWS_REQUESTED,
SELECT_INCIDENT_TABLE_ROWS_COMPLETED,
CLEAR_INCIDENT_TABLE_FILTERS_REQUESTED,
CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED,
} from './actions';

import selectIncidentTable from './selectors';
Expand Down Expand Up @@ -122,3 +124,13 @@ export function* selectIncidentTableRowsImpl(action) {
selectedRows,
});
}

export function* clearIncidentTableFilters() {
yield takeLatest(CLEAR_INCIDENT_TABLE_FILTERS_REQUESTED, clearIncidentTableFiltersImpl);
}

export function* clearIncidentTableFiltersImpl() {
yield put({
type: CLEAR_INCIDENT_TABLE_FILTERS_COMPLETED,
});
}
1 change: 1 addition & 0 deletions src/redux/incidents/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const incidents = produce(
case FETCH_INCIDENTS_REQUESTED:
draft.fetchingIncidents = true;
draft.status = FETCH_INCIDENTS_REQUESTED;
draft.error = null;
break;

case FETCH_INCIDENTS_COMPLETED:
Expand Down
Loading

0 comments on commit eb7ca7b

Please sign in to comment.