-
Notifications
You must be signed in to change notification settings - Fork 148
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
) * Re-implement with standalone compute select status * Clean up code * Fix switching from exclude to include * Add parents check for include mode * Simplify toggle facet value * Simplify compute status and fix tests * Simplify facet select mode switching * Add compute select test coverage * Make the behavior consistent with web-ui * Enhance code and add tests * Improve event filters test * Fix filter facets in include mode * Reset when unchecking children in Only mode * Polish UI and code * Polish and add testsfor event filter * Restore changes to computeFacetState
- Loading branch information
Showing
8 changed files
with
458 additions
and
54 deletions.
There are no files selected for viewing
219 changes: 219 additions & 0 deletions
219
developer-extension/src/panel/components/tabs/eventsTab/computeFacetState.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
import type { RumActionEvent, RumResourceEvent } from '@datadog/browser-rum' | ||
import { FacetRegistry } from '../../../hooks/useEvents' | ||
import type { FacetValuesFilter } from '../../../hooks/useEvents' | ||
import { FACET_ROOT } from '../../../facets.constants' | ||
import type { Facet } from '../../../facets.constants' | ||
import { computeSelectionState } from './computeFacetState' | ||
|
||
const rumResourceXHREvent = { | ||
type: 'resource', | ||
resource: { | ||
type: 'xhr', | ||
url: 'http://example.com', | ||
}, | ||
} as RumResourceEvent | ||
|
||
const rumResourceBeaconEvent = { | ||
type: 'resource', | ||
resource: { | ||
type: 'beacon', | ||
url: 'http://example.com', | ||
}, | ||
} as RumResourceEvent | ||
|
||
const rumCustomActionEvent = { | ||
type: 'action', | ||
action: { | ||
type: 'custom', | ||
}, | ||
} as RumActionEvent | ||
|
||
// test that computeSelectionState returns the correct state | ||
describe('computeSelectionState', () => { | ||
describe('include mode', () => { | ||
it('returns "unselected" when the filter is empty', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'include', | ||
facetValues: {}, | ||
} | ||
const facet = { | ||
path: 'resource.type', | ||
label: 'Resource Type', | ||
} | ||
|
||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
const facetValue = 'xhr' | ||
expect( | ||
computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'resource', 'xhr']) | ||
).toBe('unselected') | ||
}) | ||
it('returns "selected" when the facet is in the filter', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'include', | ||
facetValues: { | ||
'resource.type': ['xhr'], | ||
}, | ||
} | ||
const facet = { | ||
path: 'resource.type', | ||
label: 'Resource Type', | ||
} | ||
|
||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
const facetValue = 'xhr' | ||
expect( | ||
computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'resource', 'xhr']) | ||
).toBe('selected') | ||
}) | ||
|
||
it('returns "partial-selected" when some children are in the filter', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'include', | ||
facetValues: { | ||
'resource.type': ['xhr'], | ||
}, | ||
} | ||
const facet = FACET_ROOT.values!.rum?.facets![0] as Facet | ||
const facetValue = 'resource' | ||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
facetRegistry.addEvent(rumResourceBeaconEvent) | ||
expect(computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'resource'])).toBe( | ||
'partial-selected' | ||
) | ||
}) | ||
|
||
it('returns "selected" when all children are in the filter', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'include', | ||
facetValues: { | ||
'resource.type': ['xhr', 'beacon'], | ||
}, | ||
} | ||
const facet = FACET_ROOT.values!.rum?.facets![0] as Facet | ||
const facetValue = 'resource' | ||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
facetRegistry.addEvent(rumResourceBeaconEvent) | ||
expect(computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'resource'])).toBe( | ||
'selected' | ||
) | ||
}) | ||
|
||
it('returns "unselected" when the facet or children are not in the filter', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'include', | ||
facetValues: { | ||
'resource.type': ['xhr'], | ||
}, | ||
} | ||
const facet = { | ||
path: 'action.type', | ||
label: 'Action Type', | ||
} | ||
|
||
const facetValue = 'action' | ||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
facetRegistry.addEvent(rumCustomActionEvent) | ||
expect(computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'action'])).toBe( | ||
'unselected' | ||
) | ||
}) | ||
}) | ||
|
||
describe('exclude mode', () => { | ||
it('returns "selected" when the filter is empty', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'exclude', | ||
facetValues: {}, | ||
} | ||
const facet = { | ||
path: 'resource.type', | ||
label: 'Resource Type', | ||
} | ||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
const facetValue = 'xhr' | ||
expect( | ||
computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'resource', 'xhr']) | ||
).toBe('selected') | ||
}) | ||
|
||
it('returns "unselected" when the facet is in the filter', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'exclude', | ||
facetValues: { | ||
'resource.type': ['xhr'], | ||
}, | ||
} | ||
const facet = { | ||
path: 'resource.type', | ||
label: 'Resource Type', | ||
} | ||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
const facetValue = 'xhr' | ||
expect( | ||
computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'resource', 'xhr']) | ||
).toBe('unselected') | ||
}) | ||
it('returns "partial-selected" when some children are in the filter', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'exclude', | ||
facetValues: { | ||
'resource.type': ['xhr'], | ||
}, | ||
} | ||
const facet = FACET_ROOT.values!.rum?.facets![0] as Facet | ||
|
||
const facetValue = 'resource' | ||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
facetRegistry.addEvent(rumResourceBeaconEvent) | ||
expect(computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'resource'])).toBe( | ||
'partial-selected' | ||
) | ||
}) | ||
|
||
it('returns "unelected" when all children are in the filter', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'exclude', | ||
facetValues: { | ||
'resource.type': ['xhr', 'beacon'], | ||
}, | ||
} | ||
const facet = FACET_ROOT.values!.rum?.facets![0] as Facet | ||
const facetValue = 'resource' | ||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
facetRegistry.addEvent(rumResourceBeaconEvent) | ||
expect(computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'resource'])).toBe( | ||
'unselected' | ||
) | ||
}) | ||
|
||
it('returns "selected" when the facet or children are not in the filter', () => { | ||
const facetValuesFilter: FacetValuesFilter = { | ||
type: 'exclude', | ||
facetValues: { | ||
'resource.type': ['xhr'], | ||
}, | ||
} | ||
const facet = { | ||
path: 'action.type', | ||
label: 'Action Type', | ||
} | ||
|
||
const facetValue = 'action' | ||
const facetRegistry = new FacetRegistry() | ||
facetRegistry.addEvent(rumResourceXHREvent) | ||
facetRegistry.addEvent(rumCustomActionEvent) | ||
expect(computeSelectionState(facetValuesFilter, facetRegistry, facet, facetValue, ['rum', 'action'])).toBe( | ||
'selected' | ||
) | ||
}) | ||
}) | ||
}) |
68 changes: 68 additions & 0 deletions
68
developer-extension/src/panel/components/tabs/eventsTab/computeFacetState.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import type { FacetRegistry, FacetValuesFilter } from '../../../hooks/useEvents' | ||
import type { Facet, FacetValue } from '../../../facets.constants' | ||
type SelectionState = 'selected' | 'unselected' | 'partial-selected' | ||
|
||
function isAllChildrenFiltered(children: string[], filteredFacetValues: string[]) { | ||
return children.every((child: FacetValue) => filteredFacetValues.includes(child)) | ||
} | ||
|
||
function isAnyChildrenFiltered(children: string[], filteredFacetValues: string[]) { | ||
return children.some((child: FacetValue) => filteredFacetValues.includes(child)) | ||
} | ||
|
||
// limitation: only populate direct parents | ||
export function computeSelectionState( | ||
facetValuesFilter: FacetValuesFilter, | ||
facetRegistry: FacetRegistry, | ||
facet: Facet, | ||
facetValue: FacetValue, | ||
parentList: string[] | ||
): SelectionState { | ||
const childrenFacets = facet.values?.[facetValue]?.facets | ||
|
||
// we cannot know how many children in total there are, so we need to have facetRegistry | ||
const children = | ||
childrenFacets && childrenFacets.flatMap((child: Facet) => facetRegistry.getFacetChildrenValues(child.path)) | ||
const filteredFacetValues = Object.values(facetValuesFilter.facetValues).flat() | ||
const isFiltering = !!Object.keys(facetValuesFilter.facetValues) | ||
|
||
if (facetValuesFilter.type === 'include') { | ||
if (!isFiltering) { | ||
return 'unselected' | ||
} | ||
|
||
for (const parent of parentList) { | ||
if (filteredFacetValues.includes(parent)) { | ||
return 'selected' | ||
} | ||
} | ||
|
||
// if all children are in the filter, then it should be selected' | ||
if (children && isAllChildrenFiltered(children, filteredFacetValues)) { | ||
return 'selected' | ||
} | ||
// if any of the direct children of the facet is in the filter, then it should be partial-selected | ||
if (children && isAnyChildrenFiltered(children, filteredFacetValues)) { | ||
return 'partial-selected' | ||
} | ||
} else if (facetValuesFilter.type === 'exclude') { | ||
if (!isFiltering) { | ||
return 'selected' | ||
} | ||
// if facet.value is in facetValueFilter, then it should be unselected | ||
if (filteredFacetValues.includes(facetValue)) { | ||
return 'unselected' | ||
} | ||
// if all children are in the filter, then it should be unselected | ||
if (children && isAllChildrenFiltered(children, filteredFacetValues)) { | ||
return 'unselected' | ||
} | ||
// if any of the children of the facet is in the filter, then it should be partial-selected | ||
if (children && isAnyChildrenFiltered(children, filteredFacetValues)) { | ||
return 'partial-selected' | ||
} | ||
return 'selected' | ||
} | ||
|
||
return 'unselected' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.