Skip to content

Commit

Permalink
DSEGOG-348 view favourite filters
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuadkitenge committed Oct 15, 2024
1 parent a1c663a commit 757b8f0
Show file tree
Hide file tree
Showing 12 changed files with 768 additions and 67 deletions.
6 changes: 6 additions & 0 deletions cypress/e2e/filtering.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,12 @@ describe('Filtering Component', () => {
});
});

it('display favourite filters', () => {
cy.findByDisplayValue('test 1').should('exist');
cy.findByDisplayValue('test 2').should('exist');
cy.findByDisplayValue('test 3').should('exist');
});

it('renders the save button as disabled if name field is empty', () => {
cy.findByRole('button', { name: 'Add new favourite filter' }).click();
cy.findByText('Add Favourite filter').should('exist');
Expand Down
30 changes: 30 additions & 0 deletions e2e/real/filtering.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,33 @@ test('should be able to add a multiple filters', async ({ page }) => {

await expect(page.getByText('1–7 of 7')).toBeVisible();
});

test('should be able to add a favourite filter', async ({ page }) => {
await page.getByRole('button', { name: 'Filters' }).click();
await page.getByText('Favourite filters').click();

await page.getByRole('button', { name: 'Add new favourite filter' }).click();

const firstFilterInput = page
.getByRole('combobox', {
name: 'Filter',
exact: true,
})
.first();

const nameFields = await page.locator('label:has-text("Name")');

await nameFields.last().fill('test');

await firstFilterInput.pressSequentially('Relative humidity 209 < 42 ');

// unfocus so combobox menu is not blocking add new filter button
await firstFilterInput.blur();

await page.getByRole('button', { name: 'Save' }).click();

const element = page.locator('input[value="test 1"]'); // Adjust the selector as necessary

// Assert that the element exists
await expect(element).toBeVisible();
});
4 changes: 2 additions & 2 deletions src/api/favouriteFilters.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { renderHook, waitFor } from '@testing-library/react';
import { FavouriteFilter } from '../app.types';
import { FavouriteFilterPost } from '../app.types';
import { hooksWrapperWithProviders } from '../testUtils';
import { useAddFavouriteFilter } from './favouriteFilters';

describe('session api functions', () => {
let mockData: FavouriteFilter;
let mockData: FavouriteFilterPost;
beforeEach(() => {
mockData = {
name: 'test',
Expand Down
37 changes: 33 additions & 4 deletions src/api/favouriteFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import {
useMutation,
UseMutationResult,
useQuery,
useQueryClient,
UseQueryResult,
} from '@tanstack/react-query';
import axios, { AxiosError } from 'axios';
import { FavouriteFilter } from '../app.types';
import { FavouriteFilter, FavouriteFilterPost } from '../app.types';
import { readSciGatewayToken } from '../parseTokens';
import { useAppSelector } from '../state/hooks';
import { selectUrls } from '../state/slices/configSlice';

const addFavouriteFilter = (
apiUrl: string,
favouriteFilter: FavouriteFilter
favouriteFilter: FavouriteFilterPost
): Promise<string> => {
const queryParams = new URLSearchParams();

Expand All @@ -35,12 +37,12 @@ const addFavouriteFilter = (
export const useAddFavouriteFilter = (): UseMutationResult<
string,
AxiosError,
FavouriteFilter
FavouriteFilterPost
> => {
const { apiUrl } = useAppSelector(selectUrls);
const queryClient = useQueryClient();
return useMutation({
mutationFn: (favouriteFilter: FavouriteFilter) =>
mutationFn: (favouriteFilter: FavouriteFilterPost) =>
addFavouriteFilter(apiUrl, favouriteFilter),
onError: (error) => {
console.log('Got error ' + error.message);
Expand All @@ -50,3 +52,30 @@ export const useAddFavouriteFilter = (): UseMutationResult<
},
});
};

const fetchFavouriteFilters = (apiUrl: string): Promise<FavouriteFilter[]> => {
return axios
.get(`${apiUrl}/users/filters`, {
headers: {
Authorization: `Bearer ${readSciGatewayToken()}`,
},
})
.then((response) => {
return response.data;
});
};

export const useFavouriteFilters = (): UseQueryResult<
FavouriteFilter[],
AxiosError
> => {
const { apiUrl } = useAppSelector(selectUrls);

return useQuery({
queryKey: ['favouriteFilters'],

queryFn: () => {
return fetchFavouriteFilters(apiUrl);
},
});
};
6 changes: 5 additions & 1 deletion src/app.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@ export interface APIError {
detail: string | APIErrorResponse[];
}

export interface FavouriteFilter {
export interface FavouriteFilterPost {
name: string;
filter: string;
}

export interface FavouriteFilter extends FavouriteFilterPost {
_id: string;
}
557 changes: 547 additions & 10 deletions src/filtering/__snapshots__/filterDialogue.component.test.tsx.snap

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/filtering/favouriteFiltersDialogue.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@mui/material';
import React from 'react';
import { useAddFavouriteFilter } from '../api/favouriteFilters';
import { FavouriteFilter } from '../app.types';
import { FavouriteFilterPost } from '../app.types';
import { FilterPageHelp } from './filterDialogue.component';
import FilterInput from './filterInput.component';
import { Token } from './filterParser';
Expand Down Expand Up @@ -60,7 +60,7 @@ const FavouriteFiltersDialogue = (props: FavouriteFiltersDialogueProps) => {
const { mutateAsync: addFavouriteFilter } = useAddFavouriteFilter();

const handleSubmit = React.useCallback(() => {
const data: FavouriteFilter = {
const data: FavouriteFilterPost = {
name: favouriteFilter.name,
filter: JSON.stringify(
favouriteFilter.filter.length === 0 ? '' : favouriteFilter.filter
Expand Down
148 changes: 105 additions & 43 deletions src/filtering/filterDialogue.component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { AddCircle, Delete, Warning } from '@mui/icons-material';
import DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit';
import {
Box,
Button,
Expand All @@ -10,12 +12,14 @@ import {
Grid,
IconButton,
Tabs,
TextField,
Tooltip,
Typography,
} from '@mui/material';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';
import { useChannels } from '../api/channels';
import { useFavouriteFilters } from '../api/favouriteFilters';
import { useIncomingRecordCount } from '../api/records';
import { timeChannelName } from '../app.types';
import { useAppDispatch, useAppSelector } from '../state/hooks';
Expand Down Expand Up @@ -345,6 +349,8 @@ const FilterDialogue = (props: FilterDialogueProps) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [incomingCount, incomingFilters]);

const { data: favouriteFilterData } = useFavouriteFilters();

return (
<Dialog open={open} onClose={handleClose} maxWidth="lg" fullWidth>
<Tabs value={tabValue} onChange={handleTabChange} aria-label="view tabs">
Expand Down Expand Up @@ -433,6 +439,61 @@ const FilterDialogue = (props: FilterDialogueProps) => {
Add new favourite filter
</Button>
</Grid>
<Grid item container flexDirection="column" mt={1} rowSpacing={1}>
{favouriteFilterData?.map((data) => {
return (
<Grid item container spacing={1} key={data._id}>
<Grid item xs={5.5}>
<TextField
fullWidth
inputProps={{
readOnly: true,
disabled: true,
}}
sx={{
// change label and border color when readonly
'&:has([readonly]) ': {
'& .MuiOutlinedInput-notchedOutline': {
borderColor: '#cecece',
},
},
}}
label="Name"
value={data.name}
size="small"
/>
</Grid>
<Grid item xs={5.5}>
<FilterInput
channels={channels ?? []}
value={JSON.parse(data.filter) as Token[]}
setValue={() => {}}
setError={() => {}}
readOnly
/>
</Grid>
<Grid item xs={0.5}>
<Tooltip title={`Edit ${data.name}`}>
<IconButton
aria-label={`Edit ${data.name} favourite filter`}
>
<EditIcon />
</IconButton>
</Tooltip>
</Grid>
<Grid item xs={0.5}>
<Tooltip title={`Delete ${data.name}`}>
<IconButton
aria-label={`Delete ${data.name} favourite filter`}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</Grid>
</Grid>
);
})}
</Grid>

<FavouriteFiltersDialogue
open={favouriteFiltersOpen}
Expand All @@ -447,57 +508,58 @@ const FilterDialogue = (props: FilterDialogueProps) => {
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Close</Button>
{displayingWarningMessage ? (
<Tooltip
componentsProps={{
tooltip: {
sx: {
backgroundColor: 'yellow',
color: 'black',
border: '1px solid black',
{tabValue !== 'Favourite filters' &&
(displayingWarningMessage ? (
<Tooltip
componentsProps={{
tooltip: {
sx: {
backgroundColor: 'yellow',
color: 'black',
border: '1px solid black',
},
},
},
}}
arrow
placement="bottom"
title={
<Box
sx={{
display: 'flex',
flexDirection: 'row',
cursor: 'pointer',
overflow: 'hidden',
}}
>
<Warning sx={{ fontSize: 25, padding: '10px 5px 5px 0px' }} />
<div>
<Typography variant="caption" align="center">
{`This search will return over ${recordLimitWarning}
}}
arrow
placement="bottom"
title={
<Box
sx={{
display: 'flex',
flexDirection: 'row',
cursor: 'pointer',
overflow: 'hidden',
}}
>
<Warning sx={{ fontSize: 25, padding: '10px 5px 5px 0px' }} />
<div>
<Typography variant="caption" align="center">
{`This search will return over ${recordLimitWarning}
results.`}
</Typography>
<br />
<Typography variant="caption" align="center">
Click Apply again to continue
</Typography>
</div>
</Box>
}
>
</Typography>
<br />
<Typography variant="caption" align="center">
Click Apply again to continue
</Typography>
</div>
</Box>
}
>
<Button
disabled={errors.some((e) => e !== undefined)}
onClick={() => applyFilters()}
>
Apply
</Button>
</Tooltip>
) : (
<Button
disabled={errors.some((e) => e !== undefined)}
onClick={() => applyFilters()}
>
Apply
</Button>
</Tooltip>
) : (
<Button
disabled={errors.some((e) => e !== undefined)}
onClick={() => applyFilters()}
>
Apply
</Button>
)}
))}
</DialogActions>
</Dialog>
);
Expand Down
15 changes: 13 additions & 2 deletions src/filtering/filterInput.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface FilterInputProps {
error?: string;
setError: (error?: string) => void;
flashingFilterValue?: string;
readOnly?: boolean;
}

export const useClickHandler = (props: {
Expand Down Expand Up @@ -302,8 +303,15 @@ const filterOptions = createFilterOptions<Token>({
});

const FilterInput = (props: FilterInputProps) => {
const { channels, value, setValue, error, setError, flashingFilterValue } =
props;
const {
channels,
value,
setValue,
error,
setError,
flashingFilterValue,
readOnly,
} = props;
const options = React.useMemo(() => {
return [...operators, ...channels];
}, [channels]);
Expand Down Expand Up @@ -362,6 +370,7 @@ const FilterInput = (props: FilterInputProps) => {
autoHighlight
filterOptions={filterOptions}
multiple
readOnly={readOnly}
options={options}
freeSolo
size="small"
Expand Down Expand Up @@ -409,6 +418,8 @@ const FilterInput = (props: FilterInputProps) => {
'data-id': 'Input',
startAdornment: tags.slice(0, inputIndex),
endAdornment: tags.slice(inputIndex),
readOnly: readOnly,
disabled: readOnly,
}}
/>
)}
Expand Down
Loading

0 comments on commit 757b8f0

Please sign in to comment.