mirror of
https://github.com/ansible/awx.git
synced 2026-03-20 02:17:37 -02:30
Fixes several things about Survey List
Aligns Select All with other select buttons Add required asterisk to those items that are required Adds label for the Default and Question Type column Adds chips for multiselect items. Adds RBAC to add and edit survey. Also fixes a bug where the survey was not reloading properly after edit
This commit is contained in:
@@ -5,7 +5,7 @@ import { Button, Tooltip } from '@patternfly/react-core';
|
|||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
function ToolbarAddButton({ linkTo, onClick, i18n }) {
|
function ToolbarAddButton({ linkTo, onClick, i18n, isDisabled }) {
|
||||||
if (!linkTo && !onClick) {
|
if (!linkTo && !onClick) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'ToolbarAddButton requires either `linkTo` or `onClick` prop'
|
'ToolbarAddButton requires either `linkTo` or `onClick` prop'
|
||||||
@@ -15,6 +15,7 @@ function ToolbarAddButton({ linkTo, onClick, i18n }) {
|
|||||||
return (
|
return (
|
||||||
<Tooltip content={i18n._(t`Add`)} position="top">
|
<Tooltip content={i18n._(t`Add`)} position="top">
|
||||||
<Button
|
<Button
|
||||||
|
isDisabled={isDisabled}
|
||||||
component={Link}
|
component={Link}
|
||||||
to={linkTo}
|
to={linkTo}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ function SurveyList({
|
|||||||
toggleSurvey,
|
toggleSurvey,
|
||||||
updateSurvey,
|
updateSurvey,
|
||||||
deleteSurvey,
|
deleteSurvey,
|
||||||
|
canAddAndEditSurvey,
|
||||||
i18n,
|
i18n,
|
||||||
}) {
|
}) {
|
||||||
const questions = survey?.spec || [];
|
const questions = survey?.spec || [];
|
||||||
@@ -97,6 +98,7 @@ function SurveyList({
|
|||||||
onSelect={() => handleSelect(question)}
|
onSelect={() => handleSelect(question)}
|
||||||
onMoveUp={moveUp}
|
onMoveUp={moveUp}
|
||||||
onMoveDown={moveDown}
|
onMoveDown={moveDown}
|
||||||
|
canAddAndEditSurvey={canAddAndEditSurvey}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{isPreviewModalOpen && (
|
{isPreviewModalOpen && (
|
||||||
@@ -169,6 +171,7 @@ function SurveyList({
|
|||||||
surveyEnabled={surveyEnabled}
|
surveyEnabled={surveyEnabled}
|
||||||
onToggleSurvey={toggleSurvey}
|
onToggleSurvey={toggleSurvey}
|
||||||
isDeleteDisabled={selected?.length === 0}
|
isDeleteDisabled={selected?.length === 0}
|
||||||
|
canAddAndEditSurvey={canAddAndEditSurvey}
|
||||||
onToggleDeleteModal={() => setIsDeleteModalOpen(true)}
|
onToggleDeleteModal={() => setIsDeleteModalOpen(true)}
|
||||||
/>
|
/>
|
||||||
{content}
|
{content}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ describe('<SurveyList />', () => {
|
|||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('Switch').invoke('onChange')(true);
|
wrapper.find('Switch').invoke('onChange')(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
expect(toggleSurvey).toHaveBeenCalled();
|
expect(toggleSurvey).toHaveBeenCalled();
|
||||||
@@ -53,14 +54,18 @@ describe('<SurveyList />', () => {
|
|||||||
let wrapper;
|
let wrapper;
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<SurveyList survey={surveyData} deleteSurvey={deleteSurvey} />
|
<SurveyList
|
||||||
|
survey={surveyData}
|
||||||
|
deleteSurvey={deleteSurvey}
|
||||||
|
canAddAndEditSurvey
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
expect(wrapper.find('Button[variant="danger"]').prop('isDisabled')).toBe(
|
expect(wrapper.find('Button[variant="danger"]').prop('isDisabled')).toBe(
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('Checkbox[aria-label="Select all"]').prop('isChecked')
|
wrapper.find('Checkbox[aria-label="Select all"]').prop('isChecked')
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
@@ -81,6 +86,7 @@ describe('<SurveyList />', () => {
|
|||||||
act(() => {
|
act(() => {
|
||||||
wrapper.find('Button[variant="danger"]').invoke('onClick')();
|
wrapper.find('Button[variant="danger"]').invoke('onClick')();
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
await act(() =>
|
await act(() =>
|
||||||
@@ -88,6 +94,7 @@ describe('<SurveyList />', () => {
|
|||||||
);
|
);
|
||||||
expect(deleteSurvey).toHaveBeenCalled();
|
expect(deleteSurvey).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render Preview button ', async () => {
|
test('should render Preview button ', async () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
@@ -97,6 +104,7 @@ describe('<SurveyList />', () => {
|
|||||||
|
|
||||||
expect(wrapper.find('Button[aria-label="Preview"]').length).toBe(1);
|
expect(wrapper.find('Button[aria-label="Preview"]').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Preview button should render Modal', async () => {
|
test('Preview button should render Modal', async () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
@@ -108,6 +116,7 @@ describe('<SurveyList />', () => {
|
|||||||
|
|
||||||
expect(wrapper.find('SurveyPreviewModal').length).toBe(1);
|
expect(wrapper.find('SurveyPreviewModal').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Modal close button should close modal', async () => {
|
test('Modal close button should close modal', async () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
@@ -119,11 +128,34 @@ describe('<SurveyList />', () => {
|
|||||||
|
|
||||||
expect(wrapper.find('SurveyPreviewModal').length).toBe(1);
|
expect(wrapper.find('SurveyPreviewModal').length).toBe(1);
|
||||||
|
|
||||||
wrapper.find('Modal').prop('onClose')();
|
act(() => wrapper.find('Modal').prop('onClose')());
|
||||||
|
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
expect(wrapper.find('SurveyPreviewModal').length).toBe(0);
|
expect(wrapper.find('SurveyPreviewModal').length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('user without edit/delete permission cannot delete', async () => {
|
||||||
|
const deleteSurvey = jest.fn();
|
||||||
|
let wrapper;
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SurveyList survey={surveyData} deleteSurvey={deleteSurvey} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.find('DataToolbar')
|
||||||
|
.find('Checkbox')
|
||||||
|
.prop('isDisabled')
|
||||||
|
).toBe(true);
|
||||||
|
expect(wrapper.find('Switch').prop('isDisabled')).toBe(true);
|
||||||
|
expect(wrapper.find('ToolbarAddButton').prop('isDisabled')).toBe(true);
|
||||||
|
expect(wrapper.find('Button[variant="danger"]').prop('isDisabled')).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Survey with no questions', () => {
|
describe('Survey with no questions', () => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { withI18n } from '@lingui/react';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Button as _Button,
|
Button as _Button,
|
||||||
|
Chip as _Chip,
|
||||||
DataListAction as _DataListAction,
|
DataListAction as _DataListAction,
|
||||||
DataListCheck,
|
DataListCheck,
|
||||||
DataListItemCells,
|
DataListItemCells,
|
||||||
@@ -27,8 +28,20 @@ const Button = styled(_Button)`
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
`;
|
`;
|
||||||
|
const Required = styled.span`
|
||||||
|
color: red;
|
||||||
|
margin-left: 5px;
|
||||||
|
`;
|
||||||
|
const Chip = styled(_Chip)`
|
||||||
|
margin-right: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Label = styled.b`
|
||||||
|
margin-right: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
function SurveyListItem({
|
function SurveyListItem({
|
||||||
|
canAddAndEditSurvey,
|
||||||
question,
|
question,
|
||||||
i18n,
|
i18n,
|
||||||
isLast,
|
isLast,
|
||||||
@@ -54,7 +67,7 @@ function SurveyListItem({
|
|||||||
<Button
|
<Button
|
||||||
variant="plain"
|
variant="plain"
|
||||||
aria-label={i18n._(t`move up`)}
|
aria-label={i18n._(t`move up`)}
|
||||||
isDisabled={isFirst}
|
isDisabled={isFirst || !canAddAndEditSurvey}
|
||||||
onClick={() => onMoveUp(question)}
|
onClick={() => onMoveUp(question)}
|
||||||
>
|
>
|
||||||
<CaretUpIcon />
|
<CaretUpIcon />
|
||||||
@@ -64,7 +77,7 @@ function SurveyListItem({
|
|||||||
<Button
|
<Button
|
||||||
variant="plain"
|
variant="plain"
|
||||||
aria-label={i18n._(t`move down`)}
|
aria-label={i18n._(t`move down`)}
|
||||||
isDisabled={isLast}
|
isDisabled={isLast || !canAddAndEditSurvey}
|
||||||
onClick={() => onMoveDown(question)}
|
onClick={() => onMoveDown(question)}
|
||||||
>
|
>
|
||||||
<CaretDownIcon />
|
<CaretDownIcon />
|
||||||
@@ -73,6 +86,7 @@ function SurveyListItem({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</DataListAction>
|
</DataListAction>
|
||||||
<DataListCheck
|
<DataListCheck
|
||||||
|
isDisabled={!canAddAndEditSurvey}
|
||||||
checked={isChecked}
|
checked={isChecked}
|
||||||
onChange={onSelect}
|
onChange={onSelect}
|
||||||
aria-labelledby="survey check"
|
aria-labelledby="survey check"
|
||||||
@@ -80,12 +94,43 @@ function SurveyListItem({
|
|||||||
<DataListItemCells
|
<DataListItemCells
|
||||||
dataListCells={[
|
dataListCells={[
|
||||||
<DataListCell key="name">
|
<DataListCell key="name">
|
||||||
<Link to={`survey/edit/${question.variable}`}>
|
<>
|
||||||
{question.question_name}
|
<Link to={`survey/edit/${question.variable}`}>
|
||||||
</Link>
|
{question.question_name}
|
||||||
|
</Link>
|
||||||
|
{question.required && (
|
||||||
|
<Required
|
||||||
|
aria-label={i18n._(t`Required`)}
|
||||||
|
className="pf-c-form__label-required"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
*
|
||||||
|
</Required>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
</DataListCell>,
|
||||||
|
|
||||||
|
<DataListCell key="type">
|
||||||
|
<Label>{i18n._(t`Type:`)}</Label>
|
||||||
|
{question.type}
|
||||||
|
</DataListCell>,
|
||||||
|
<DataListCell key="default">
|
||||||
|
<Label>{i18n._(t`Default:`)}</Label>
|
||||||
|
{[question.type].includes('password') && (
|
||||||
|
<span>{i18n._(t`encrypted`).toUpperCase()}</span>
|
||||||
|
)}
|
||||||
|
{[question.type].includes('multiselect') &&
|
||||||
|
question.default.length > 0 &&
|
||||||
|
question.default.split('\n').map(chip => (
|
||||||
|
<Chip key={chip} isReadOnly>
|
||||||
|
{chip}
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
|
{![question.type].includes('password') &&
|
||||||
|
![question.type].includes('multiselect') && (
|
||||||
|
<span>{question.default}</span>
|
||||||
|
)}
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
<DataListCell key="type">{question.type}</DataListCell>,
|
|
||||||
<DataListCell key="default">{question.default}</DataListCell>,
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</DataListItemRow>
|
</DataListItemRow>
|
||||||
|
|||||||
@@ -25,6 +25,18 @@ describe('<SurveyListItem />', () => {
|
|||||||
const moveDown = wrapper.find('Button[aria-label="move down"]');
|
const moveDown = wrapper.find('Button[aria-label="move down"]');
|
||||||
expect(moveUp.length).toBe(1);
|
expect(moveUp.length).toBe(1);
|
||||||
expect(moveDown.length).toBe(1);
|
expect(moveDown.length).toBe(1);
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.find('b')
|
||||||
|
.at(0)
|
||||||
|
.text()
|
||||||
|
).toBe('Type:');
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.find('b')
|
||||||
|
.at(1)
|
||||||
|
.text()
|
||||||
|
).toBe('Default:');
|
||||||
expect(wrapper.find('DataListCheck').length).toBe(1);
|
expect(wrapper.find('DataListCheck').length).toBe(1);
|
||||||
expect(wrapper.find('DataListCell').length).toBe(3);
|
expect(wrapper.find('DataListCell').length).toBe(3);
|
||||||
});
|
});
|
||||||
@@ -44,4 +56,85 @@ describe('<SurveyListItem />', () => {
|
|||||||
expect(moveUp).toBe(true);
|
expect(moveUp).toBe(true);
|
||||||
expect(moveDown).toBe(true);
|
expect(moveDown).toBe(true);
|
||||||
});
|
});
|
||||||
|
test('required item has required asterisk', () => {
|
||||||
|
const newItem = {
|
||||||
|
question_name: 'Foo',
|
||||||
|
default: 'Bar',
|
||||||
|
type: 'text',
|
||||||
|
id: 1,
|
||||||
|
required: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wrapper;
|
||||||
|
act(() => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SurveyListItem question={newItem} isChecked={false} isFirst isLast />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(wrapper.find('span[aria-label="Required"]').length).toBe(1);
|
||||||
|
});
|
||||||
|
test('items that are not required should not have an asterisk', () => {
|
||||||
|
let wrapper;
|
||||||
|
act(() => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SurveyListItem question={item} isChecked={false} isFirst isLast />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(wrapper.find('span[aria-label="Required"]').length).toBe(0);
|
||||||
|
});
|
||||||
|
test('required item has required asterisk', () => {
|
||||||
|
const newItem = {
|
||||||
|
question_name: 'Foo',
|
||||||
|
default: 'a\nd\nb\ne\nf\ng\nh\ni\nk',
|
||||||
|
type: 'multiselect',
|
||||||
|
id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wrapper;
|
||||||
|
act(() => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SurveyListItem question={newItem} isChecked={false} isFirst isLast />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(wrapper.find('Chip').length).toBe(9);
|
||||||
|
wrapper
|
||||||
|
.find('Chip')
|
||||||
|
.map(chip => expect(chip.prop('isReadOnly')).toBe(true));
|
||||||
|
});
|
||||||
|
test('items that are no required should have no an asterisk', () => {
|
||||||
|
const newItem = {
|
||||||
|
question_name: 'Foo',
|
||||||
|
default: '$encrypted$',
|
||||||
|
type: 'password',
|
||||||
|
id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wrapper;
|
||||||
|
act(() => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SurveyListItem question={newItem} isChecked={false} isFirst isLast />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(wrapper.find('span').text()).toBe('ENCRYPTED');
|
||||||
|
});
|
||||||
|
test('users without edit/delete permissions are unable to reorder the questions', () => {
|
||||||
|
let wrapper;
|
||||||
|
act(() => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SurveyListItem
|
||||||
|
question={item}
|
||||||
|
canAddAndDelete={false}
|
||||||
|
isChecked={false}
|
||||||
|
isFirst
|
||||||
|
isLast
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(wrapper.find('button[aria-label="move up"]').prop('disabled')).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
wrapper.find('button[aria-label="move down"]').prop('disabled')
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -183,6 +183,9 @@ function SurveyQuestionForm({
|
|||||||
type="textarea"
|
type="textarea"
|
||||||
label={i18n._(t`Multiple Choice Options`)}
|
label={i18n._(t`Multiple Choice Options`)}
|
||||||
validate={required(null, i18n)}
|
validate={required(null, i18n)}
|
||||||
|
tooltip={i18n._(
|
||||||
|
t`Each answer choice must be on a separate line.`
|
||||||
|
)}
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import React from 'react';
|
|||||||
import { useRouteMatch } from 'react-router-dom';
|
import { useRouteMatch } from 'react-router-dom';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DataToolbar,
|
DataToolbar as _DataToolbar,
|
||||||
DataToolbarContent,
|
DataToolbarContent,
|
||||||
DataToolbarGroup,
|
DataToolbarGroup,
|
||||||
DataToolbarItem,
|
DataToolbarItem,
|
||||||
@@ -12,7 +13,12 @@ import {
|
|||||||
import { Switch, Checkbox, Button } from '@patternfly/react-core';
|
import { Switch, Checkbox, Button } from '@patternfly/react-core';
|
||||||
import { ToolbarAddButton } from '@components/PaginatedDataList';
|
import { ToolbarAddButton } from '@components/PaginatedDataList';
|
||||||
|
|
||||||
|
const DataToolbar = styled(_DataToolbar)`
|
||||||
|
margin-left: 52px;
|
||||||
|
`;
|
||||||
|
|
||||||
function SurveyToolbar({
|
function SurveyToolbar({
|
||||||
|
canAddAndEditSurvey,
|
||||||
isAllSelected,
|
isAllSelected,
|
||||||
onSelectAll,
|
onSelectAll,
|
||||||
i18n,
|
i18n,
|
||||||
@@ -21,12 +27,14 @@ function SurveyToolbar({
|
|||||||
isDeleteDisabled,
|
isDeleteDisabled,
|
||||||
onToggleDeleteModal,
|
onToggleDeleteModal,
|
||||||
}) {
|
}) {
|
||||||
|
isDeleteDisabled = !canAddAndEditSurvey || isDeleteDisabled;
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
return (
|
return (
|
||||||
<DataToolbar id="survey-toolbar">
|
<DataToolbar id="survey-toolbar">
|
||||||
<DataToolbarContent>
|
<DataToolbarContent>
|
||||||
<DataToolbarItem>
|
<DataToolbarItem>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
isDisabled={!canAddAndEditSurvey}
|
||||||
isChecked={isAllSelected}
|
isChecked={isAllSelected}
|
||||||
onChange={isChecked => {
|
onChange={isChecked => {
|
||||||
onSelectAll(isChecked);
|
onSelectAll(isChecked);
|
||||||
@@ -42,12 +50,16 @@ function SurveyToolbar({
|
|||||||
label={i18n._(t`On`)}
|
label={i18n._(t`On`)}
|
||||||
labelOff={i18n._(t`Off`)}
|
labelOff={i18n._(t`Off`)}
|
||||||
isChecked={surveyEnabled}
|
isChecked={surveyEnabled}
|
||||||
|
isDisabled={!canAddAndEditSurvey}
|
||||||
onChange={() => onToggleSurvey(!surveyEnabled)}
|
onChange={() => onToggleSurvey(!surveyEnabled)}
|
||||||
/>
|
/>
|
||||||
</DataToolbarItem>
|
</DataToolbarItem>
|
||||||
<DataToolbarGroup>
|
<DataToolbarGroup>
|
||||||
<DataToolbarItem>
|
<DataToolbarItem>
|
||||||
<ToolbarAddButton linkTo={`${match.url}/add`} />
|
<ToolbarAddButton
|
||||||
|
isDisabled={!canAddAndEditSurvey}
|
||||||
|
linkTo={`${match.url}/add`}
|
||||||
|
/>
|
||||||
</DataToolbarItem>
|
</DataToolbarItem>
|
||||||
<DataToolbarItem>
|
<DataToolbarItem>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ describe('<SurveyToolbar />', () => {
|
|||||||
isAllSelected
|
isAllSelected
|
||||||
onToggleDeleteModal={jest.fn()}
|
onToggleDeleteModal={jest.fn()}
|
||||||
onToggleSurvey={jest.fn()}
|
onToggleSurvey={jest.fn()}
|
||||||
|
canAddAndEditSurvey
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -84,4 +85,31 @@ describe('<SurveyToolbar />', () => {
|
|||||||
expect(wrapper.find('Switch').length).toBe(1);
|
expect(wrapper.find('Switch').length).toBe(1);
|
||||||
expect(wrapper.find('Switch').prop('isChecked')).toBe(true);
|
expect(wrapper.find('Switch').prop('isChecked')).toBe(true);
|
||||||
});
|
});
|
||||||
|
test('all action buttons in toolbar are disabled', () => {
|
||||||
|
let wrapper;
|
||||||
|
act(() => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<SurveyToolbar
|
||||||
|
surveyEnabled
|
||||||
|
isDeleteDisabled={false}
|
||||||
|
onSelectAll={jest.fn()}
|
||||||
|
isAllSelected
|
||||||
|
onToggleDelete={jest.fn()}
|
||||||
|
onToggleSurvey={jest.fn()}
|
||||||
|
canAddAndEditSurvey={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
wrapper
|
||||||
|
.find('DataToolbar')
|
||||||
|
.find('Checkbox')
|
||||||
|
.prop('isDisabled')
|
||||||
|
).toBe(true);
|
||||||
|
expect(wrapper.find('Switch').prop('isDisabled')).toBe(true);
|
||||||
|
expect(wrapper.find('ToolbarAddButton').prop('isDisabled')).toBe(true);
|
||||||
|
expect(wrapper.find('Button[variant="danger"]').prop('isDisabled')).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ function Template({ i18n, me, setBreadcrumb }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const canSeeNotificationsTab = me.is_system_auditor || isNotifAdmin;
|
const canSeeNotificationsTab = me.is_system_auditor || isNotifAdmin;
|
||||||
|
const canAddAndEditSurvey =
|
||||||
|
template?.summary_fields?.user_capabilities.edit ||
|
||||||
|
template?.summary_fields?.user_capabilities.delete;
|
||||||
|
|
||||||
const tabsArray = [
|
const tabsArray = [
|
||||||
{ name: i18n._(t`Details`), link: `${match.url}/details` },
|
{ name: i18n._(t`Details`), link: `${match.url}/details` },
|
||||||
@@ -97,7 +100,7 @@ function Template({ i18n, me, setBreadcrumb }) {
|
|||||||
link: `${match.url}/completed_jobs`,
|
link: `${match.url}/completed_jobs`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Survey`),
|
name: canAddAndEditSurvey ? i18n._(t`Survey`) : i18n._(t`View Survey`),
|
||||||
link: `${match.url}/survey`,
|
link: `${match.url}/survey`,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -200,7 +203,10 @@ function Template({ i18n, me, setBreadcrumb }) {
|
|||||||
)}
|
)}
|
||||||
{template && (
|
{template && (
|
||||||
<Route path="/templates/:templateType/:id/survey">
|
<Route path="/templates/:templateType/:id/survey">
|
||||||
<TemplateSurvey template={template} />
|
<TemplateSurvey
|
||||||
|
template={template}
|
||||||
|
canAddAndEditSurvey={canAddAndEditSurvey}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
)}
|
)}
|
||||||
{!hasRolesandTemplateLoading && (
|
{!hasRolesandTemplateLoading && (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { Switch, Route, useParams } from 'react-router-dom';
|
import { Switch, Route, useParams, useLocation } from 'react-router-dom';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { JobTemplatesAPI, WorkflowJobTemplatesAPI } from '@api';
|
import { JobTemplatesAPI, WorkflowJobTemplatesAPI } from '@api';
|
||||||
@@ -9,10 +9,12 @@ import ErrorDetail from '@components/ErrorDetail';
|
|||||||
import useRequest, { useDismissableError } from '@util/useRequest';
|
import useRequest, { useDismissableError } from '@util/useRequest';
|
||||||
import { SurveyList, SurveyQuestionAdd, SurveyQuestionEdit } from './Survey';
|
import { SurveyList, SurveyQuestionAdd, SurveyQuestionEdit } from './Survey';
|
||||||
|
|
||||||
function TemplateSurvey({ template, i18n }) {
|
function TemplateSurvey({ template, canAddAndEditSurvey, i18n }) {
|
||||||
const [surveyEnabled, setSurveyEnabled] = useState(template.survey_enabled);
|
const [surveyEnabled, setSurveyEnabled] = useState(template.survey_enabled);
|
||||||
|
|
||||||
const { templateType } = useParams();
|
const { templateType } = useParams();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
result: survey,
|
result: survey,
|
||||||
request: fetchSurvey,
|
request: fetchSurvey,
|
||||||
@@ -28,9 +30,10 @@ function TemplateSurvey({ template, i18n }) {
|
|||||||
return data;
|
return data;
|
||||||
}, [template.id, templateType])
|
}, [template.id, templateType])
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSurvey();
|
fetchSurvey();
|
||||||
}, [fetchSurvey]);
|
}, [fetchSurvey, location]);
|
||||||
|
|
||||||
const { request: updateSurvey, error: updateError } = useRequest(
|
const { request: updateSurvey, error: updateError } = useRequest(
|
||||||
useCallback(
|
useCallback(
|
||||||
@@ -82,12 +85,22 @@ function TemplateSurvey({ template, i18n }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/templates/:templateType/:id/survey/add">
|
{canAddAndEditSurvey && (
|
||||||
<SurveyQuestionAdd survey={survey} updateSurvey={updateSurveySpec} />
|
<Route path="/templates/:templateType/:id/survey/add">
|
||||||
</Route>
|
<SurveyQuestionAdd
|
||||||
<Route path="/templates/:templateType/:id/survey/edit/:variable">
|
survey={survey}
|
||||||
<SurveyQuestionEdit survey={survey} updateSurvey={updateSurveySpec} />
|
updateSurvey={updateSurveySpec}
|
||||||
</Route>
|
/>
|
||||||
|
</Route>
|
||||||
|
)}
|
||||||
|
{canAddAndEditSurvey && (
|
||||||
|
<Route path="/templates/:templateType/:id/survey/edit/:variable">
|
||||||
|
<SurveyQuestionEdit
|
||||||
|
survey={survey}
|
||||||
|
updateSurvey={updateSurveySpec}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
)}
|
||||||
<Route path="/templates/:templateType/:id/survey" exact>
|
<Route path="/templates/:templateType/:id/survey" exact>
|
||||||
<SurveyList
|
<SurveyList
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
@@ -96,6 +109,7 @@ function TemplateSurvey({ template, i18n }) {
|
|||||||
toggleSurvey={toggleSurvey}
|
toggleSurvey={toggleSurvey}
|
||||||
updateSurvey={updateSurveySpec}
|
updateSurvey={updateSurveySpec}
|
||||||
deleteSurvey={deleteSurvey}
|
deleteSurvey={deleteSurvey}
|
||||||
|
canAddAndEditSurvey={canAddAndEditSurvey}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@@ -116,6 +116,9 @@ class WorkflowJobTemplate extends Component {
|
|||||||
|
|
||||||
const canSeeNotificationsTab = me.is_system_auditor || isNotifAdmin;
|
const canSeeNotificationsTab = me.is_system_auditor || isNotifAdmin;
|
||||||
const canToggleNotifications = isNotifAdmin;
|
const canToggleNotifications = isNotifAdmin;
|
||||||
|
const canAddAndEditSurvey =
|
||||||
|
template?.summary_fields?.user_capabilities.edit ||
|
||||||
|
template?.summary_fields?.user_capabilities.delete;
|
||||||
|
|
||||||
const tabsArray = [
|
const tabsArray = [
|
||||||
{ name: i18n._(t`Details`), link: `${match.url}/details` },
|
{ name: i18n._(t`Details`), link: `${match.url}/details` },
|
||||||
@@ -145,7 +148,7 @@ class WorkflowJobTemplate extends Component {
|
|||||||
link: `${match.url}/completed_jobs`,
|
link: `${match.url}/completed_jobs`,
|
||||||
});
|
});
|
||||||
tabsArray.push({
|
tabsArray.push({
|
||||||
name: i18n._(t`Survey`),
|
name: canAddAndEditSurvey ? i18n._(t`Survey`) : i18n._(t`View Survey`),
|
||||||
link: `${match.url}/survey`,
|
link: `${match.url}/survey`,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -269,7 +272,10 @@ class WorkflowJobTemplate extends Component {
|
|||||||
)}
|
)}
|
||||||
{template && (
|
{template && (
|
||||||
<Route path="/templates/:templateType/:id/survey">
|
<Route path="/templates/:templateType/:id/survey">
|
||||||
<TemplateSurvey template={template} />
|
<TemplateSurvey
|
||||||
|
template={template}
|
||||||
|
canAddAndEditSurvey={canAddAndEditSurvey}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
)}
|
)}
|
||||||
<Route key="not-found" path="*">
|
<Route key="not-found" path="*">
|
||||||
|
|||||||
Reference in New Issue
Block a user