Skip to content

Commit

Permalink
feat: remove onDelete for Placeholder tag for non Platform admiins (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
RODO94 authored Nov 20, 2024
1 parent 186ff00 commit 6647d3a
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 41 deletions.
35 changes: 17 additions & 18 deletions editor.planx.uk/src/@planx/components/Checklist/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Delete from "@mui/icons-material/Delete";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import FormControlLabel from "@mui/material/FormControlLabel";
import IconButton from "@mui/material/IconButton";
import { ComponentType as TYPES } from "@opensystemslab/planx-core/types";
import { FormikErrors, FormikValues, useFormik } from "formik";
Expand Down Expand Up @@ -378,19 +377,19 @@ export const ChecklistComponent: React.FC<ChecklistProps> = (props) => {
/>
</InputRow>
<InputRow>
<Switch
checked={!!formik.values.groupedOptions}
onChange={() =>
formik.setValues({
...formik.values,
...toggleExpandableChecklist({
options: formik.values.options,
groupedOptions: formik.values.groupedOptions,
}),
})
}
label="Expandable"
/>
<Switch
checked={!!formik.values.groupedOptions}
onChange={() =>
formik.setValues({
...formik.values,
...toggleExpandableChecklist({
options: formik.values.options,
groupedOptions: formik.values.groupedOptions,
}),
})
}
label="Expandable"
/>
</InputRow>
<InputRow>
<Switch
Expand All @@ -401,16 +400,16 @@ export const ChecklistComponent: React.FC<ChecklistProps> = (props) => {
!formik.values.allRequired,
)
}
label="All required"
/>
label="All required"
/>
</InputRow>
<InputRow>
<Switch
checked={formik.values.neverAutoAnswer}
onChange={() =>
onChange={() =>
formik.setFieldValue(
"neverAutoAnswer",
!formik.values.neverAutoAnswer
!formik.values.neverAutoAnswer,
)
}
label="Always put to user (forgo automation)"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import Box from "@mui/material/Box";
import { Palette, useTheme } from "@mui/material/styles";
import { NodeTag } from "@opensystemslab/planx-core/types";
import { NodeTag, Role } from "@opensystemslab/planx-core/types";
import { useStore } from "pages/FlowEditor/lib/store";
import React from "react";
import { getContrastTextColor } from "styleUtils";
import { FONT_WEIGHT_SEMI_BOLD } from "theme";

export const TAG_DISPLAY_VALUES: Record<
NodeTag,
{ color: keyof Palette["nodeTag"]; displayName: string }
{ color: keyof Palette["nodeTag"]; displayName: string; editableBy?: Role[] }
> = {
placeholder: {
color: "blocking",
displayName: "Placeholder",
editableBy: ["platformAdmin"],
},
toReview: {
color: "nonBlocking",
Expand Down
119 changes: 119 additions & 0 deletions editor.planx.uk/src/ui/editor/ComponentTagSelect.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import ChecklistComponent from "@planx/components/Checklist/Editor";
import { within } from "@testing-library/react";
import { TAG_DISPLAY_VALUES } from "pages/FlowEditor/components/Flow/components/Tag";
import { useStore } from "pages/FlowEditor/lib/store";
import React from "react";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { act } from "react-dom/test-utils";
import { setup } from "testUtils";
import { it } from "vitest";

const { setState } = useStore;

const mockUser = {
id: 200,
firstName: "Testy",
lastName: "McTester",
email: "[email protected]",
teams: [],
};

describe("Checklist Component for a Platform Admin", () => {
beforeEach(() =>
act(() =>
setState({
user: {
...mockUser,
isPlatformAdmin: true,
},
teamSlug: "team",
}),
),
);

it("renders all tags with none selected", async () => {
const { getByRole, user } = setup(
<DndProvider backend={HTML5Backend}>
<ChecklistComponent text="" />
</DndProvider>,
);
const tagSelect = getByRole("combobox", { name: /tag this component/i });

await user.click(tagSelect);

const optionsList = getByRole("listbox", { name: /tag this component/i });
const options = within(optionsList).getAllByRole("option");

const tagDisplayNames = Object.values(TAG_DISPLAY_VALUES).map(
(tag) => tag.displayName,
);
const optionTexts = options.map((option) => option.textContent);

expect(optionTexts).toEqual(expect.arrayContaining(tagDisplayNames));
});

it("renders all tags with Placeholder selected as a button", async () => {
const { queryByTestId, queryByRole } = setup(
<DndProvider backend={HTML5Backend}>
<ChecklistComponent
text=""
node={{ data: { text: "", tags: ["placeholder"] } }}
/>
</DndProvider>,
);

const placeholderChip = queryByTestId("placeholder-chip");
const placeholderButton = queryByRole("button", { name: /placeholder/i });

expect(placeholderChip).toBeInTheDocument();
expect(placeholderButton).toBeInTheDocument();
});
});

describe("Checklist Component for a non Platform Admin", () => {
beforeEach(() =>
act(() =>
setState({
user: {
...mockUser,
isPlatformAdmin: false,
},
}),
),
);

it("renders all tags except Placeholder with none selected", async () => {
const { getByRole, user } = setup(
<DndProvider backend={HTML5Backend}>
<ChecklistComponent text="" />
</DndProvider>,
);
const tagSelect = getByRole("combobox", { name: /tag this component/i });

await user.click(tagSelect);

const optionsList = getByRole("listbox", { name: /tag this component/i });
const options = within(optionsList).getAllByRole("option");
const optionTexts = options.map((option) => option.textContent);

expect(optionTexts).not.toContain(/placeholder/i);
});

it("renders all tags with static Placeholder selected", async () => {
const { getByTestId, queryByRole } = setup(
<DndProvider backend={HTML5Backend}>
<ChecklistComponent
text=""
node={{ data: { text: "", tags: ["placeholder"] } }}
/>
</DndProvider>,
);

const placeholderChip = getByTestId("placeholder-chip");
const placeholderButton = queryByRole("button", { name: /placeholder/i });

expect(placeholderChip).toBeInTheDocument();
expect(placeholderButton).not.toBeInTheDocument();
});
});
64 changes: 43 additions & 21 deletions editor.planx.uk/src/ui/editor/ComponentTagSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import BookmarksIcon from "@mui/icons-material/Bookmarks";
import { AutocompleteProps } from "@mui/material/Autocomplete";
import Chip from "@mui/material/Chip";
import ListItem from "@mui/material/ListItem";
import { NODE_TAGS, NodeTag } from "@opensystemslab/planx-core/types";
import { NODE_TAGS, NodeTag, Role } from "@opensystemslab/planx-core/types";
import { TAG_DISPLAY_VALUES } from "pages/FlowEditor/components/Flow/components/Tag";
import { useStore } from "pages/FlowEditor/lib/store";
import React from "react";
import { getContrastTextColor } from "styleUtils";
import ModalSection from "ui/editor/ModalSection";
Expand All @@ -16,18 +17,29 @@ interface Props {
onChange: (values: NodeTag[]) => void;
}

const skipTag = (role?: Role) => {
const userRole = useStore.getState().getUserRoleForCurrentTeam();
return role === userRole ? false : true;
};

const renderOption: AutocompleteProps<
NodeTag,
true,
true,
false,
"div"
>["renderOption"] = (props, tag, { selected }) => (
<ListItem {...props}>
<CustomCheckbox aria-hidden="true" className={selected ? "selected" : ""} />
{TAG_DISPLAY_VALUES[tag].displayName}
</ListItem>
);
>["renderOption"] = (props, tag, { selected }) => {
if (TAG_DISPLAY_VALUES[tag].editableBy?.some(skipTag)) return null;
return (
<ListItem {...props}>
<CustomCheckbox
aria-hidden="true"
className={selected ? "selected" : ""}
/>
{TAG_DISPLAY_VALUES[tag].displayName}
</ListItem>
);
};

const renderTags: AutocompleteProps<
NodeTag,
Expand All @@ -36,20 +48,30 @@ const renderTags: AutocompleteProps<
false,
"div"
>["renderTags"] = (value, getTagProps) =>
value.map((tag, index) => (
<Chip
{...getTagProps({ index })}
key={tag}
label={TAG_DISPLAY_VALUES[tag].displayName}
sx={(theme) => ({
backgroundColor: theme.palette.nodeTag[TAG_DISPLAY_VALUES[tag].color],
color: getContrastTextColor(
theme.palette.nodeTag[TAG_DISPLAY_VALUES[tag].color],
"#FFF",
),
})}
/>
));
value.map((tag, index) => {
return (
<Chip
{...getTagProps({ index })}
data-testid={
TAG_DISPLAY_VALUES[tag].displayName.toLowerCase() + "-chip"
}
key={tag}
label={TAG_DISPLAY_VALUES[tag].displayName}
sx={(theme) => ({
backgroundColor: theme.palette.nodeTag[TAG_DISPLAY_VALUES[tag].color],
color: getContrastTextColor(
theme.palette.nodeTag[TAG_DISPLAY_VALUES[tag].color],
"#FFF",
),
})}
onDelete={
TAG_DISPLAY_VALUES[tag].editableBy?.some(skipTag)
? undefined
: getTagProps({ index }).onDelete
}
/>
);
});

export const ComponentTagSelect: React.FC<Props> = ({ value, onChange }) => {
return (
Expand Down

0 comments on commit 6647d3a

Please sign in to comment.