Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[v9.3.x] TimeRangePicker: Fix recent ranges not showing all items (#6…
…0085) TimeRangePicker: Fix recent ranges not showing all items (#59836) * Fix not all recently used time ranges showing * Refactor time picker history to store simpler json objects * Don't store duplicate items * update todo tests: * Add tests for TimePickerWithHistory * better fix for focus scope issues in test (cherry picked from commit 6280780) Co-authored-by: Josh Hunt <joshhunt@users.noreply.github.com>
- Loading branch information
1 parent
4bdebf3
commit b12ca99
Showing
5 changed files
with
236 additions
and
22 deletions.
There are no files selected for viewing
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
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
56 changes: 56 additions & 0 deletions
56
packages/grafana-ui/src/components/Dropdown/ButtonSelect.test.tsx
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,56 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import React from 'react'; | ||
|
||
import { SelectableValue } from '@grafana/data'; | ||
|
||
import { ButtonSelect } from './ButtonSelect'; | ||
|
||
const OPTIONS: SelectableValue[] = [ | ||
{ | ||
label: 'Hello', | ||
value: 'a', | ||
}, | ||
{ | ||
label: 'World', | ||
value: 'b', | ||
}, | ||
]; | ||
|
||
describe('ButtonSelect', () => { | ||
it('initially renders the selected value with the menu closed', () => { | ||
const selected = OPTIONS[0]; | ||
render(<ButtonSelect value={selected} options={OPTIONS} onChange={() => {}} />); | ||
|
||
expect(screen.getByText('Hello')).toBeInTheDocument(); | ||
expect(screen.queryAllByRole('menuitemradio')).toHaveLength(0); | ||
}); | ||
|
||
it('opens the menu when clicking the button', async () => { | ||
const selected = OPTIONS[0]; | ||
render(<ButtonSelect value={selected} options={OPTIONS} onChange={() => {}} />); | ||
|
||
const button = screen.getByText('Hello'); | ||
await userEvent.click(button); | ||
|
||
expect(screen.queryAllByRole('menuitemradio')).toHaveLength(2); | ||
}); | ||
|
||
it('closes the menu when clicking an option', async () => { | ||
const selected = OPTIONS[0]; | ||
const onChange = jest.fn(); | ||
render(<ButtonSelect value={selected} options={OPTIONS} onChange={onChange} />); | ||
|
||
const button = screen.getByText('Hello'); | ||
await userEvent.click(button); | ||
|
||
const option = screen.getByText('World'); | ||
await userEvent.click(option); | ||
|
||
expect(screen.queryAllByRole('menuitemradio')).toHaveLength(0); | ||
expect(onChange).toHaveBeenCalledWith({ | ||
label: 'World', | ||
value: 'b', | ||
}); | ||
}); | ||
}); |
132 changes: 132 additions & 0 deletions
132
public/app/core/components/TimePicker/TimePickerWithHistory.test.tsx
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,132 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import React from 'react'; | ||
|
||
import { getDefaultTimeRange } from '@grafana/data'; | ||
|
||
import { TimePickerWithHistory } from './TimePickerWithHistory'; | ||
|
||
describe('TimePickerWithHistory', () => { | ||
// In some of the tests we close and re-open the picker. When we do that we must re-find these inputs | ||
// as new elements will have been mounted | ||
const getFromField = () => screen.getByLabelText('Time Range from field'); | ||
const getToField = () => screen.getByLabelText('Time Range to field'); | ||
const getApplyButton = () => screen.getByRole('button', { name: 'Apply time range' }); | ||
|
||
const LOCAL_STORAGE_KEY = 'grafana.dashboard.timepicker.history'; | ||
const OLD_LOCAL_STORAGE = [ | ||
{ | ||
from: '2022-12-03T00:00:00.000Z', | ||
to: '2022-12-03T23:59:59.000Z', | ||
raw: { from: '2022-12-03T00:00:00.000Z', to: '2022-12-03T23:59:59.000Z' }, | ||
}, | ||
{ | ||
from: '2022-12-02T00:00:00.000Z', | ||
to: '2022-12-02T23:59:59.000Z', | ||
raw: { from: '2022-12-02T00:00:00.000Z', to: '2022-12-02T23:59:59.000Z' }, | ||
}, | ||
]; | ||
|
||
const NEW_LOCAL_STORAGE = [ | ||
{ from: '2022-12-03T00:00:00.000Z', to: '2022-12-03T23:59:59.000Z' }, | ||
{ from: '2022-12-02T00:00:00.000Z', to: '2022-12-02T23:59:59.000Z' }, | ||
]; | ||
|
||
const props = { | ||
timeZone: 'utc', | ||
onChange: () => {}, | ||
onChangeTimeZone: () => {}, | ||
onMoveBackward: () => {}, | ||
onMoveForward: () => {}, | ||
onZoom: () => {}, | ||
}; | ||
|
||
afterEach(() => window.localStorage.clear()); | ||
|
||
it('Should load with no history', async () => { | ||
const timeRange = getDefaultTimeRange(); | ||
render(<TimePickerWithHistory value={timeRange} {...props} />); | ||
await userEvent.click(screen.getByLabelText(/Time range selected/)); | ||
|
||
expect(screen.getByText(/It looks like you haven't used this time picker before/i)).toBeInTheDocument(); | ||
}); | ||
|
||
it('Should load with old TimeRange history', async () => { | ||
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(OLD_LOCAL_STORAGE)); | ||
|
||
const timeRange = getDefaultTimeRange(); | ||
render(<TimePickerWithHistory value={timeRange} {...props} />); | ||
await userEvent.click(screen.getByLabelText(/Time range selected/)); | ||
|
||
expect(screen.getByText(/2022-12-03 00:00:00 to 2022-12-03 23:59:59/i)).toBeInTheDocument(); | ||
expect(screen.queryByText(/2022-12-02 00:00:00 to 2022-12-02 23:59:59/i)).toBeInTheDocument(); | ||
}); | ||
|
||
it('Should load with new TimePickerHistoryItem history', async () => { | ||
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(NEW_LOCAL_STORAGE)); | ||
|
||
const timeRange = getDefaultTimeRange(); | ||
render(<TimePickerWithHistory value={timeRange} {...props} />); | ||
await userEvent.click(screen.getByLabelText(/Time range selected/)); | ||
|
||
expect(screen.queryByText(/2022-12-03 00:00:00 to 2022-12-03 23:59:59/i)).toBeInTheDocument(); | ||
expect(screen.queryByText(/2022-12-02 00:00:00 to 2022-12-02 23:59:59/i)).toBeInTheDocument(); | ||
}); | ||
|
||
it('Saves changes into local storage without duplicates', async () => { | ||
const timeRange = getDefaultTimeRange(); | ||
render(<TimePickerWithHistory value={timeRange} {...props} />); | ||
await userEvent.click(screen.getByLabelText(/Time range selected/)); | ||
|
||
await clearAndType(getFromField(), '2022-12-03 00:00:00'); | ||
await clearAndType(getToField(), '2022-12-03 23:59:59'); | ||
await userEvent.click(getApplyButton()); | ||
|
||
await userEvent.click(screen.getByLabelText(/Time range selected/)); | ||
|
||
// Same range again! | ||
await clearAndType(getFromField(), '2022-12-03 00:00:00'); | ||
await clearAndType(getToField(), '2022-12-03 23:59:59'); | ||
await userEvent.click(getApplyButton()); | ||
|
||
const newLsValue = JSON.parse(window.localStorage.getItem(LOCAL_STORAGE_KEY) ?? '[]'); | ||
expect(newLsValue).toEqual([{ from: '2022-12-03T00:00:00.000Z', to: '2022-12-03T23:59:59.000Z' }]); | ||
}); | ||
|
||
it('Should show 4 most recently used time ranges', async () => { | ||
const inputRanges: Array<[string, string]> = [ | ||
['2022-12-10 00:00:00', '2022-12-10 23:59:59'], | ||
['2022-12-11 00:00:00', '2022-12-11 23:59:59'], | ||
['2022-12-12 00:00:00', '2022-12-12 23:59:59'], | ||
['2022-12-13 00:00:00', '2022-12-13 23:59:59'], | ||
['2022-12-14 00:00:00', '2022-12-14 23:59:59'], | ||
]; | ||
|
||
const expectedLocalStorage = [ | ||
{ from: '2022-12-14T00:00:00.000Z', to: '2022-12-14T23:59:59.000Z' }, | ||
{ from: '2022-12-13T00:00:00.000Z', to: '2022-12-13T23:59:59.000Z' }, | ||
{ from: '2022-12-12T00:00:00.000Z', to: '2022-12-12T23:59:59.000Z' }, | ||
{ from: '2022-12-11T00:00:00.000Z', to: '2022-12-11T23:59:59.000Z' }, | ||
]; | ||
|
||
const timeRange = getDefaultTimeRange(); | ||
render(<TimePickerWithHistory value={timeRange} {...props} />); | ||
await userEvent.click(screen.getByLabelText(/Time range selected/)); | ||
|
||
for (const [inputFrom, inputTo] of inputRanges) { | ||
await userEvent.click(screen.getByLabelText(/Time range selected/)); | ||
await clearAndType(getFromField(), inputFrom); | ||
await clearAndType(getToField(), inputTo); | ||
|
||
await userEvent.click(getApplyButton()); | ||
} | ||
|
||
const newLsValue = JSON.parse(window.localStorage.getItem(LOCAL_STORAGE_KEY) ?? '[]'); | ||
expect(newLsValue).toEqual(expectedLocalStorage); | ||
}); | ||
}); | ||
|
||
async function clearAndType(field: HTMLElement, text: string) { | ||
await userEvent.clear(field); | ||
return await userEvent.type(field, text); | ||
} |
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