mirror of https://github.com/grafana/grafana
PlayList: add delete functionality to remove playlist (#38120)
* WIP: add delete functionality to playlist * fixes deleted item to be removed instantly without manual refresh * update confirmModal to reference playlist name * refactor confirmModal message to be clear enough * WIP: some unit tests for the playlistPage * added more tests and did some cleanup * some code refactoring * adds ability for user roles to control playlist delete * some abstraction to cleanup code * modified alert message for delete button to correspond with action * tried a better approach to modify the alert message * fixes playlist lookup on each render * update handlers to not use anonymous function * exposed getBackendSrv().get api to fetch all playlist * used better naming convention * removes unecessary async/await construct * some code refactoring * used the correct param structurepull/38787/head
parent
326455a9b8
commit
4e8ab0512c
@ -0,0 +1,83 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { render, waitFor } from '@testing-library/react'; |
||||||
|
import { PlaylistPage, PlaylistPageProps } from './PlaylistPage'; |
||||||
|
import { locationService } from '../../../../packages/grafana-runtime/src'; |
||||||
|
|
||||||
|
const fnMock = jest.fn(); |
||||||
|
|
||||||
|
jest.mock('@grafana/runtime', () => ({ |
||||||
|
...((jest.requireActual('@grafana/runtime') as unknown) as object), |
||||||
|
getBackendSrv: () => ({ |
||||||
|
get: fnMock, |
||||||
|
}), |
||||||
|
})); |
||||||
|
|
||||||
|
jest.mock('app/core/services/context_srv', () => ({ |
||||||
|
contextSrv: { |
||||||
|
isEditor: true, |
||||||
|
}, |
||||||
|
})); |
||||||
|
|
||||||
|
function getTestContext(propOverrides?: object) { |
||||||
|
const props: PlaylistPageProps = { |
||||||
|
navModel: { |
||||||
|
main: { |
||||||
|
text: 'Playlist', |
||||||
|
}, |
||||||
|
node: { |
||||||
|
text: 'playlist', |
||||||
|
}, |
||||||
|
}, |
||||||
|
route: { |
||||||
|
path: '/playlists', |
||||||
|
component: jest.fn(), |
||||||
|
}, |
||||||
|
queryParams: { state: 'ok' }, |
||||||
|
match: { params: { name: 'playlist', sourceName: 'test playlist' }, isExact: false, url: 'asdf', path: '' }, |
||||||
|
history: locationService.getHistory(), |
||||||
|
location: { pathname: '', hash: '', search: '', state: '' }, |
||||||
|
}; |
||||||
|
|
||||||
|
Object.assign(props, propOverrides); |
||||||
|
|
||||||
|
return render(<PlaylistPage {...props} />); |
||||||
|
} |
||||||
|
|
||||||
|
describe('PlaylistPage', () => { |
||||||
|
describe('when mounted without a playlist', () => { |
||||||
|
it('page should load', () => { |
||||||
|
fnMock.mockResolvedValue([]); |
||||||
|
const { getByText } = getTestContext(); |
||||||
|
expect(getByText(/loading/i)).toBeInTheDocument(); |
||||||
|
}); |
||||||
|
it('then show empty list', async () => { |
||||||
|
const { getByText } = getTestContext(); |
||||||
|
await waitFor(() => getByText('There are no playlists created yet')); |
||||||
|
}); |
||||||
|
}); |
||||||
|
describe('when mounted with a playlist', () => { |
||||||
|
it('page should load', () => { |
||||||
|
fnMock.mockResolvedValue([ |
||||||
|
{ |
||||||
|
id: 0, |
||||||
|
name: 'A test playlist', |
||||||
|
interval: '10m', |
||||||
|
items: [ |
||||||
|
{ title: 'First item', type: 'dashboard_by_id', order: 1, value: '1' }, |
||||||
|
{ title: 'Middle item', type: 'dashboard_by_id', order: 2, value: '2' }, |
||||||
|
{ title: 'Last item', type: 'dashboard_by_tag', order: 2, value: 'Last item' }, |
||||||
|
], |
||||||
|
}, |
||||||
|
]); |
||||||
|
const { getByText } = getTestContext(); |
||||||
|
expect(getByText(/loading/i)).toBeInTheDocument(); |
||||||
|
}); |
||||||
|
it('then playlist title and buttons should appear on the page', async () => { |
||||||
|
const { getByRole, getByText } = getTestContext(); |
||||||
|
await waitFor(() => getByText('A test playlist')); |
||||||
|
expect(getByRole('button', { name: /Start playlist/i })).toBeInTheDocument(); |
||||||
|
expect(getByRole('link', { name: /Edit playlist/i })).toBeInTheDocument(); |
||||||
|
expect(getByRole('button', { name: /Delete playlist/i })).toBeInTheDocument(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
import React, { FC, useState } from 'react'; |
||||||
|
import { SelectableValue, urlUtil } from '@grafana/data'; |
||||||
|
import { locationService } from '@grafana/runtime'; |
||||||
|
import { PlaylistDTO } from './types'; |
||||||
|
import { Button, Checkbox, Field, Modal, RadioButtonGroup, VerticalGroup } from '@grafana/ui'; |
||||||
|
|
||||||
|
export interface StartModalProps { |
||||||
|
playlist: PlaylistDTO; |
||||||
|
onDismiss: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const StartModal: FC<StartModalProps> = ({ playlist, onDismiss }) => { |
||||||
|
const [mode, setMode] = useState<any>(false); |
||||||
|
const [autoFit, setAutofit] = useState(false); |
||||||
|
|
||||||
|
const modes: Array<SelectableValue<any>> = [ |
||||||
|
{ label: 'Normal', value: false }, |
||||||
|
{ label: 'TV', value: 'tv' }, |
||||||
|
{ label: 'Kiosk', value: true }, |
||||||
|
]; |
||||||
|
|
||||||
|
const onStart = () => { |
||||||
|
const params: any = {}; |
||||||
|
if (mode) { |
||||||
|
params.kiosk = mode; |
||||||
|
} |
||||||
|
if (autoFit) { |
||||||
|
params.autofitpanels = true; |
||||||
|
} |
||||||
|
locationService.push(urlUtil.renderUrl(`/playlists/play/${playlist.id}`, params)); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<Modal isOpen={true} icon="play" title="Start playlist" onDismiss={onDismiss}> |
||||||
|
<VerticalGroup> |
||||||
|
<Field label="Mode"> |
||||||
|
<RadioButtonGroup value={mode} options={modes} onChange={setMode} /> |
||||||
|
</Field> |
||||||
|
<Checkbox |
||||||
|
label="Autofit" |
||||||
|
description="Panel heights will be adjusted to fit screen size" |
||||||
|
name="autofix" |
||||||
|
value={autoFit} |
||||||
|
onChange={(e) => setAutofit(e.currentTarget.checked)} |
||||||
|
/> |
||||||
|
</VerticalGroup> |
||||||
|
<Modal.ButtonRow> |
||||||
|
<Button variant="primary" onClick={onStart}> |
||||||
|
Start {playlist.name} |
||||||
|
</Button> |
||||||
|
</Modal.ButtonRow> |
||||||
|
</Modal> |
||||||
|
); |
||||||
|
}; |
||||||
Loading…
Reference in new issue