mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
feat: group id clickable in event search (#10277)
Now when pressing the group id, the query params get updated. Also the FilterItem appears and it is possible to discard the group id selection through it. 
This commit is contained in:
parent
1664c71b83
commit
30fbc62f9b
@ -6,6 +6,7 @@ import { Link } from 'react-router-dom';
|
||||
import { styled } from '@mui/material';
|
||||
import type { EventSchema } from 'openapi';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
interface IEventCardProps {
|
||||
entry: EventSchema;
|
||||
@ -74,17 +75,41 @@ export const StyledCodeSection = styled('div')(({ theme }) => ({
|
||||
const EventCard = ({ entry }: IEventCardProps) => {
|
||||
const { locationSettings } = useLocationSettings();
|
||||
const eventGroupingEnabled = useUiFlag('eventGrouping');
|
||||
const location = useLocation();
|
||||
|
||||
const createdAtFormatted = formatDateYMDHMS(
|
||||
entry.createdAt,
|
||||
locationSettings.locale,
|
||||
);
|
||||
|
||||
const getGroupIdLink = () => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
searchParams.set('groupId', `IS:${entry.groupId}`);
|
||||
return `${location.pathname}?${searchParams.toString()}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledContainerListItem>
|
||||
<dl>
|
||||
<StyledDefinitionTerm>Event id:</StyledDefinitionTerm>
|
||||
<dd>{entry.id}</dd>
|
||||
<ConditionallyRender
|
||||
condition={
|
||||
eventGroupingEnabled && entry.groupId !== undefined
|
||||
}
|
||||
show={
|
||||
<>
|
||||
<StyledDefinitionTerm>
|
||||
Group id:
|
||||
</StyledDefinitionTerm>
|
||||
<dd>
|
||||
<Link to={getGroupIdLink()}>
|
||||
{entry.groupId}
|
||||
</Link>
|
||||
</dd>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<StyledDefinitionTerm>Changed at:</StyledDefinitionTerm>
|
||||
<dd>{createdAtFormatted}</dd>
|
||||
<StyledDefinitionTerm>Event:</StyledDefinitionTerm>
|
||||
|
@ -1,12 +1,23 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { useEventLogFilters } from './EventLogFilters.tsx';
|
||||
|
||||
const allFilterKeys = ['from', 'to', 'createdBy', 'type', 'project', 'feature'];
|
||||
|
||||
allFilterKeys.sort();
|
||||
|
||||
const renderWithRouter = (callback: () => any, initialEntries = ['/']) => {
|
||||
return renderHook(callback, {
|
||||
wrapper: ({ children }) => (
|
||||
<MemoryRouter initialEntries={initialEntries}>
|
||||
{children}
|
||||
</MemoryRouter>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
test('When you have no projects or flags, you should not get a project or flag filters', () => {
|
||||
const { result } = renderHook(() => useEventLogFilters([], []));
|
||||
const { result } = renderWithRouter(() => useEventLogFilters([], []));
|
||||
const filterKeys = result.current.map((filter) => filter.filterKey);
|
||||
filterKeys.sort();
|
||||
|
||||
@ -15,7 +26,7 @@ test('When you have no projects or flags, you should not get a project or flag f
|
||||
});
|
||||
|
||||
test('When you have no projects, you should not get a project filter', () => {
|
||||
const { result } = renderHook(() =>
|
||||
const { result } = renderWithRouter(() =>
|
||||
useEventLogFilters(
|
||||
[],
|
||||
// @ts-expect-error: omitting other properties we don't need
|
||||
@ -29,7 +40,7 @@ test('When you have no projects, you should not get a project filter', () => {
|
||||
});
|
||||
|
||||
test('When you have only one project, you should not get a project filter', () => {
|
||||
const { result } = renderHook(() =>
|
||||
const { result } = renderWithRouter(() =>
|
||||
useEventLogFilters([{ id: 'a', name: 'A' }], []),
|
||||
);
|
||||
const filterKeys = result.current.map((filter) => filter.filterKey);
|
||||
@ -39,7 +50,7 @@ test('When you have only one project, you should not get a project filter', () =
|
||||
});
|
||||
|
||||
test('When you have two one project, you should not get a project filter', () => {
|
||||
const { result } = renderHook(() =>
|
||||
const { result } = renderWithRouter(() =>
|
||||
useEventLogFilters(
|
||||
[
|
||||
{ id: 'a', name: 'A' },
|
||||
@ -53,3 +64,20 @@ test('When you have two one project, you should not get a project filter', () =>
|
||||
|
||||
expect(filterKeys).toContain('project');
|
||||
});
|
||||
|
||||
test('When groupId is in URL params, should include groupId filter', () => {
|
||||
const { result } = renderWithRouter(
|
||||
() => useEventLogFilters([], []),
|
||||
['/?groupId=IS:123'],
|
||||
);
|
||||
const filterKeys = result.current.map((filter) => filter.filterKey);
|
||||
|
||||
expect(filterKeys).toContain('groupId');
|
||||
});
|
||||
|
||||
test('When no groupId in URL params, should not include groupId filter', () => {
|
||||
const { result } = renderWithRouter(() => useEventLogFilters([], []));
|
||||
const filterKeys = result.current.map((filter) => filter.filterKey);
|
||||
|
||||
expect(filterKeys).not.toContain('groupId');
|
||||
});
|
||||
|
@ -10,6 +10,8 @@ import { EventSchemaType, type FeatureSearchResponseSchema } from 'openapi';
|
||||
import type { ProjectSchema } from 'openapi';
|
||||
import { useEventCreators } from 'hooks/api/getters/useEventCreators/useEventCreators';
|
||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { FilterItemParam } from 'utils/serializeQueryParams';
|
||||
|
||||
export const useEventLogFilters = (
|
||||
projects: ProjectSchema[],
|
||||
@ -17,9 +19,14 @@ export const useEventLogFilters = (
|
||||
) => {
|
||||
const { environments } = useEnvironments();
|
||||
const { eventCreators } = useEventCreators();
|
||||
const location = useLocation();
|
||||
const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const hasGroupId = searchParams.has('groupId');
|
||||
const groupIdValue = searchParams.get('groupId');
|
||||
|
||||
const projectOptions =
|
||||
projects?.map((project: ProjectSchema) => ({
|
||||
label: project.name,
|
||||
@ -50,6 +57,22 @@ export const useEventLogFilters = (
|
||||
value: env.name,
|
||||
})) ?? [];
|
||||
|
||||
const groupIdOptions =
|
||||
hasGroupId && groupIdValue
|
||||
? (() => {
|
||||
const parsedGroupId =
|
||||
FilterItemParam.decode(groupIdValue);
|
||||
return parsedGroupId
|
||||
? [
|
||||
{
|
||||
label: parsedGroupId.values[0],
|
||||
value: parsedGroupId.values[0],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
})()
|
||||
: [];
|
||||
|
||||
const availableFilters: IFilterItem[] = [
|
||||
{
|
||||
label: 'Date From',
|
||||
@ -87,6 +110,19 @@ export const useEventLogFilters = (
|
||||
singularOperators: ['IS'],
|
||||
pluralOperators: ['IS_ANY_OF'],
|
||||
},
|
||||
...(hasGroupId
|
||||
? ([
|
||||
{
|
||||
label: 'Group ID',
|
||||
icon: 'group',
|
||||
options: groupIdOptions,
|
||||
filterKey: 'groupId',
|
||||
singularOperators: ['IS'],
|
||||
pluralOperators: ['IS_ANY_OF'],
|
||||
persistent: false,
|
||||
},
|
||||
] as IFilterItem[])
|
||||
: []),
|
||||
...(projectOptions.length > 1
|
||||
? ([
|
||||
{
|
||||
@ -131,6 +167,7 @@ export const useEventLogFilters = (
|
||||
JSON.stringify(projects),
|
||||
JSON.stringify(eventCreators),
|
||||
JSON.stringify(environments),
|
||||
location.search,
|
||||
]);
|
||||
|
||||
return availableFilters;
|
||||
|
@ -73,7 +73,7 @@ export const useEventLogSearch = (
|
||||
type: FilterItemParam,
|
||||
environment: FilterItemParam,
|
||||
id: StringParam,
|
||||
groupId: StringParam,
|
||||
groupId: FilterItemParam,
|
||||
...extraParameters(logType),
|
||||
};
|
||||
|
||||
|
@ -46,6 +46,10 @@ export interface EventSchema {
|
||||
* @nullable
|
||||
*/
|
||||
ip?: string | null;
|
||||
/**
|
||||
* The event group ID.
|
||||
*/
|
||||
groupId?: string;
|
||||
/**
|
||||
* The concise, human-readable name of the event.
|
||||
* @nullable
|
||||
|
Loading…
Reference in New Issue
Block a user