Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d5aeef1
WEB-4654 abstract out CGM Use filter
henry-tp Jul 15, 2026
3911e1f
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 15, 2026
75e0462
WEB-4654 change directory for FilterByCGMUse
henry-tp Jul 15, 2026
7257c07
WEB-4654 clear unneeded props
henry-tp Jul 16, 2026
e0d8a32
WEB-4654 add tests for dropdown
henry-tp Jul 17, 2026
53c73a7
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 17, 2026
ef670d2
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 17, 2026
a3dc964
WEB-4654 fix metrics tracking
henry-tp Jul 17, 2026
0901ffe
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 17, 2026
65123c3
WEB-4654 fix tests
henry-tp Jul 17, 2026
d4d3821
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 17, 2026
fc2614e
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 19, 2026
e0edb0c
WEB-4654 use MemoryRouter instead of mocking out location hook
henry-tp Jul 19, 2026
17ef2fb
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 20, 2026
33961fe
WEB-4654 remove unused vars
henry-tp Jul 20, 2026
ff50288
WEB-4654 remove pendingFilters dead state
henry-tp Jul 21, 2026
279c4f0
Merge branch 'WEB-4654-filter-bar' into WEB-4654-cgm-use
henry-tp Jul 21, 2026
552162d
Merge branch 'WEB-4654-tags' into WEB-4654-cgm-use
henry-tp Jul 21, 2026
8582105
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 24, 2026
6ac7ce4
WEB-4654 fix missing metric update
henry-tp Jul 26, 2026
94c7db8
WEB-4654 update pageName
henry-tp Jul 26, 2026
10c09bf
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 26, 2026
79841f6
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 26, 2026
5235683
Merge branch 'WEB-4654-time-in-range' into WEB-4654-cgm-use
henry-tp Jul 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions __tests__/unit/app/pages/clinicworkspace/ClinicPatients.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,28 @@ describe('ClinicPatients', () => {
expect.any(Function),
);
}, TEST_TIMEOUT_MS);

it('maps an applied cgm use filter into the getPatientsForClinic query', async () => {
render(
<MockedProviderWrappers>
<ClinicPatients {...defaultProps} />
</MockedProviderWrappers>
);

// Open the % CGM Use filter dropdown, select a range, and apply.
await userEvent.click(screen.getByRole('button', { name: /CGM Use/ }));
await userEvent.click(screen.getByRole('radio', { name: /Less than 70%/ }));
await userEvent.click(screen.getByRole('button', { name: /Apply/ }));

expect(defaultProps.api.clinics.getPatientsForClinic).toHaveBeenLastCalledWith(
'clinicID123',
expect.objectContaining({
'cgm.timeCGMUsePercent': '<0.7',
limit: 50, offset: 0, period: '14d', sortType: 'cgm', sort: '-lastData',
}),
expect.any(Function),
);
}, TEST_TIMEOUT_MS);
});

describe('managing sites', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import { thunk } from 'redux-thunk';
import { MemoryRouter } from 'react-router-dom';

import FilterByCGMUse from '@app/pages/clinicworkspace/clinicPatientsFilters/FilterByCGMUse';

const mockStore = configureStore([thunk]);

describe('FilterByCGMUse', () => {
let store;

const selectedClinicId = 'clinic123';

const setActiveFilters = jest.fn();

const ui = (props = {}) => (
<Provider store={store}>
<MemoryRouter initialEntries={['/clinic-workspace']}>
<FilterByCGMUse
setActiveFilters={setActiveFilters}
{...props}
/>
</MemoryRouter>
</Provider>
);

const renderComponent = (props = {}) => render(ui(props));

beforeEach(() => {
store = mockStore({
blip: {
selectedClinicId,
clinics: { [selectedClinicId]: { id: selectedClinicId } },
},
});

setActiveFilters.mockClear();
});

describe('handleChange', () => {
it('calls setActiveFilters with the applied cgm use merged into the existing activeFilters', async () => {
renderComponent({ activeFilters: { timeCGMUsePercent: null, patientTags: ['tag1'] } });

await userEvent.click(screen.getByRole('button', { name: /CGM Use/ }));
await screen.findByTestId('cgm-use-filter-dropdown');

await userEvent.click(screen.getByRole('radio', { name: /Less than 70%/ }));
await userEvent.click(screen.getByRole('button', { name: 'Apply' }));

expect(setActiveFilters).toHaveBeenCalledTimes(1);
expect(setActiveFilters).toHaveBeenCalledWith({
timeCGMUsePercent: '<0.7',
patientTags: ['tag1'],
});
});
});

describe('activeFilters passthrough', () => {
it('reflects the active cgm use in the pre-selected radio', async () => {
renderComponent({ activeFilters: { timeCGMUsePercent: '>=0.7' } });

await userEvent.click(screen.getByRole('button', { name: /CGM Use/ }));
await screen.findByTestId('cgm-use-filter-dropdown');

expect(screen.getByRole('radio', { name: /70% or more/ })).toBeChecked();
expect(screen.getByRole('radio', { name: /Less than 70%/ })).not.toBeChecked();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import { thunk } from 'redux-thunk';
import { MemoryRouter } from 'react-router-dom';

import CGMUseFilterDropdown from '@app/pages/clinicworkspace/components/CGMUseFilterDropdown';
import { trackMetric as mockTrackMetric } from '../../../../../app/core/metricUtils';

const mockStore = configureStore([thunk]);

describe('CGMUseFilterDropdown', () => {
let store;

const selectedClinicId = 'clinic123';

let onChange = jest.fn();

const ui = (props = {}) => (
<Provider store={store}>
<MemoryRouter initialEntries={['/clinic-workspace']}>
<CGMUseFilterDropdown
onChange={onChange}
timeCGMUsePercent={null}
{...props}
/>
</MemoryRouter>
</Provider>
);

const renderComponent = (props = {}) => render(ui(props));

beforeEach(() => {
store = mockStore({
blip: {
selectedClinicId,
clinics: { [selectedClinicId]: { id: selectedClinicId } },
},
});

onChange.mockClear();
mockTrackMetric.mockClear();
});

describe('filtering for cgm use', () => {
it('applies the cgm use filter based on the radio selected', async () => {
renderComponent({ timeCGMUsePercent: null });

// Dropdown closed initially
expect(screen.queryByTestId('cgm-use-filter-dropdown')).not.toBeInTheDocument();

// Open the dropdown
await userEvent.click(screen.getByRole('button', { name: /CGM Use/ }));
expect(screen.getByTestId('cgm-use-filter-dropdown')).toBeInTheDocument();

// Nothing selected initially
expect(screen.getByRole('radio', { name: /Less than 70%/ })).not.toBeChecked();
expect(screen.getByRole('radio', { name: /70% or more/ })).not.toBeChecked();

// Selecting an option and applying sets the filter
await userEvent.click(screen.getByRole('radio', { name: /Less than 70%/ }));
await userEvent.click(screen.getByRole('button', { name: /Apply/ }));
expect(onChange).toHaveBeenCalledWith('<0.7');

// Dropdown should automatically close
expect(screen.queryByTestId('cgm-use-filter-dropdown')).not.toBeInTheDocument();
});

it('disables the Apply button until an option is selected', async () => {
renderComponent({ timeCGMUsePercent: null });

await userEvent.click(screen.getByRole('button', { name: /CGM Use/ }));

// Disabled with nothing selected
expect(screen.getByRole('button', { name: /Apply/ })).toBeDisabled();

// Enabled once an option is selected
await userEvent.click(screen.getByRole('radio', { name: /70% or more/ }));
expect(screen.getByRole('button', { name: /Apply/ })).toBeEnabled();
});

it('pre-selects the radio matching the active filter', async () => {
renderComponent({ timeCGMUsePercent: '>=0.7' });

await userEvent.click(screen.getByRole('button', { name: /CGM Use/ }));

expect(screen.getByRole('radio', { name: /70% or more/ })).toBeChecked();
expect(screen.getByRole('radio', { name: /Less than 70%/ })).not.toBeChecked();
});

it('clears the filter', async () => {
renderComponent({ timeCGMUsePercent: '<0.7' });

await userEvent.click(screen.getByRole('button', { name: /CGM Use/ }));
expect(screen.getByRole('radio', { name: /Less than 70%/ })).toBeChecked();

await userEvent.click(screen.getByRole('button', { name: /Clear/ }));
expect(onChange).toHaveBeenCalledWith(null);
expect(screen.queryByTestId('cgm-use-filter-dropdown')).not.toBeInTheDocument();
});
});
});
Loading