Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Editor Menu #3028

Merged
merged 16 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions editor.planx.uk/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ const EditorToolbar: React.FC<{
</InnerContainer>
</Container>
</StyledToolbar>
<TestEnvironmentBanner />
{user && (
<StyledPopover
open={open}
Expand Down
54 changes: 25 additions & 29 deletions editor.planx.uk/src/components/TestEnvironmentBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import Container from "@mui/material/Container";
import { styled } from "@mui/material/styles";
import Typography from "@mui/material/Typography";
import { visuallyHidden } from "@mui/utils";
import { useStore } from "pages/FlowEditor/lib/store";
import React, { useState } from "react";

const TestEnvironmentWarning = styled(Box)(({ theme }) => ({
display: "flex",
backgroundColor: theme.palette.background.paper,
color: theme.palette.text.primary,
justifyContent: "space-between",
Expand All @@ -17,37 +17,33 @@ const TestEnvironmentWarning = styled(Box)(({ theme }) => ({
}));

const TestEnvironmentBanner: React.FC = () => {
const [showWarning, setShowWarning] = useState(true);
const [isTestEnvBannerVisible, hideTestEnvBanner] = useStore(state => [state.isTestEnvBannerVisible, state.hideTestEnvBanner]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now controlled at a store level as it was being re-rendered (and therefore going back to visible) on each route change.

I think this is the same issue we're seeing with everything re-rendering when we got to /nodes routes. See comment below on this 👍


const isTestEnvironment = () => !window.location.href.includes(".uk");
if (!isTestEnvBannerVisible) return null;

return (
<>
{isTestEnvironment() && showWarning && (
<TestEnvironmentWarning>
<Container
maxWidth={false}
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<Box display="flex" alignItems="center">
<ReportIcon color="error" />
<Typography variant="body2" ml={1}>
This is a <strong>testing environment</strong> for new features.
Do not use it to make permanent content changes.
</Typography>
</Box>
<Button size="small" onClick={() => setShowWarning(false)}>
Hide
<Box sx={visuallyHidden} component="span">the test environment banner</Box>
</Button>
</Container>
</TestEnvironmentWarning>
)}
</>
<TestEnvironmentWarning>
<Container
maxWidth={false}
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<Box display="flex" alignItems="center">
<ReportIcon color="error" />
<Typography variant="body2" ml={1}>
This is a <strong>testing environment</strong> for new features.
Do not use it to make permanent content changes.
</Typography>
</Box>
<Button size="small" onClick={hideTestEnvBanner}>
Hide
<Box sx={visuallyHidden} component="span">the test environment banner</Box>
</Button>
</Container>
</TestEnvironmentWarning>
);
};

Expand Down
2 changes: 1 addition & 1 deletion editor.planx.uk/src/lib/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// add/edit/remove feature flags in array below
const AVAILABLE_FEATURE_FLAGS = ["UNDO"] as const;
const AVAILABLE_FEATURE_FLAGS = ["UNDO", "EDITOR_NAVIGATION"] as const;

type FeatureFlag = (typeof AVAILABLE_FEATURE_FLAGS)[number];

Expand Down
113 changes: 113 additions & 0 deletions editor.planx.uk/src/pages/FlowEditor/components/EditorMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import FactCheckIcon from "@mui/icons-material/FactCheck";
import TuneIcon from "@mui/icons-material/Tune";
import Box from "@mui/material/Box";
import IconButton from "@mui/material/IconButton";
import { styled } from "@mui/material/styles";
import Tooltip, { tooltipClasses, TooltipProps } from "@mui/material/Tooltip";
import React from "react";
import { useCurrentRoute, useNavigation } from "react-navi";
import { rootFlowPath } from "routes/utils";
import { FONT_WEIGHT_SEMI_BOLD } from "theme";
import EditorIcon from "ui/icons/Editor";

const MENU_WIDTH = "46px";

const Root = styled(Box)(({ theme }) => ({
width: MENU_WIDTH,
flexShrink: 0,
background: theme.palette.background.paper,
borderRight: `1px solid ${theme.palette.border.main}`,
}));

const MenuWrap = styled("ul")(({ theme }) => ({
listStyle: "none",
margin: 0,
padding: theme.spacing(4, 0, 0, 0),
}));

const MenuItem = styled("li")(({ theme }) => ({
margin: theme.spacing(0.75, 0),
padding: 0,
}));

const TooltipWrap = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} arrow placement="right" classes={{ popper: className }} />
))(() => ({
[`& .${tooltipClasses.arrow}`]: {
color: "#2c2c2c",
},
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: "#2c2c2c",
left: "-5px",
fontSize: "0.8em",
borderRadius: 0,
fontWeight: FONT_WEIGHT_SEMI_BOLD,
},
}));

const MenuButton = styled(IconButton, {
shouldForwardProp: (prop) => prop !== "isActive",
})<{ isActive: boolean }>(({ theme, isActive }) => ({
color: theme.palette.primary.main,
width: MENU_WIDTH,
height: MENU_WIDTH,
border: "1px solid transparent",
borderRightColor: theme.palette.border.main,
"&:hover": {
background: "white",
borderTopColor: theme.palette.border.light,
borderBottomColor: theme.palette.border.light,
},
...(isActive && {
background: theme.palette.common.white,
color: theme.palette.text.primary,
border: `1px solid ${theme.palette.border.main}`,
borderRightColor: "transparent",
}),
}));

function EditorMenu() {
const { navigate } = useNavigation();
const { lastChunk } = useCurrentRoute();
const rootPath = rootFlowPath();

const isActive = (route: string) => lastChunk.url.pathname.endsWith(route);
const handleClick = (route: string) =>
!isActive(route) && navigate(rootPath + route);

const routes = [
Copy link
Contributor

@DafyddLlyr DafyddLlyr Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ianjon3s This pattern of splitting up "content" and "code" is a good one to be aware of.

We now have a list of items we're iterating over with the .map() below.

It's now a bit simpler to add or remove a menu item, or control the visual appearance of all of them consistently 👍

{
title: "Editor",
Icon: EditorIcon,
route: "/",
},
{
title: "Service settings",
Icon: TuneIcon,
route: "/service",
},
{
title: "Submissions log",
Icon: FactCheckIcon,
route: "/submissions-log",
},
];

return (
<Root>
<MenuWrap>
{routes.map(({ title, Icon, route }) => (
<MenuItem onClick={() => handleClick(route)} key={title}>
<TooltipWrap title={title}>
<MenuButton isActive={isActive(route)} disableRipple>
<Icon />
</MenuButton>
</TooltipWrap>
</MenuItem>
))}
</MenuWrap>
</Root>
);
}

export default EditorMenu;
6 changes: 0 additions & 6 deletions editor.planx.uk/src/pages/FlowEditor/floweditor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ $fontMonospace: "Source Code Pro", monospace;

// ------------------------------------------------

#editor-container {
flex: 1;
display: flex;
overflow: hidden;
}

#editor {
flex: 1;
overflow: auto;
Expand Down
24 changes: 19 additions & 5 deletions editor.planx.uk/src/pages/FlowEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import "./floweditor.scss";
import { gql, useSubscription } from "@apollo/client";
import UndoOutlined from "@mui/icons-material/UndoOutlined";
import Box from "@mui/material/Box";
import Link from "@mui/material/Link";
import { styled } from "@mui/material/styles";
import Typography from "@mui/material/Typography";
import { formatOps } from "@planx/graph";
import { OT } from "@planx/graph/types";
Expand All @@ -16,6 +18,7 @@ import Flow from "./components/Flow";
import PreviewBrowser from "./components/PreviewBrowser";
import { useStore } from "./lib/store";
import useScrollControlsAndRememberPosition from "./lib/useScrollControlsAndRememberPosition";
import EditNoteIcon from '@mui/icons-material/EditNote';

interface Operation {
id: string;
Expand All @@ -33,6 +36,12 @@ const formatLastEditDate = (date: string): string => {
addSuffix: true,
});
};
const EditorContainer = styled(Box)(() => ({
display: "flex",
alignItems: "stretch",
overflow: "hidden",
flexGrow: 1,
}));

const formatLastEditMessage = (
date: string,
Expand Down Expand Up @@ -94,16 +103,21 @@ export const LastEdited = () => {
return (
<Box
sx={(theme) => ({
padding: theme.spacing(1),
backgroundColor: theme.palette.grey[200],
borderBottom: `1px solid ${theme.palette.border.main}`,
padding: theme.spacing(0.5, 1),
paddingLeft: theme.spacing(2),
display: "flex",
alignItems: "center",
[theme.breakpoints.up("md")]: {
paddingLeft: theme.spacing(3),
paddingLeft: theme.spacing(2),
},
})}
>
<Typography variant="body2" fontSize="small">
{message}
</Typography>
<Link variant="body2" fontSize="small" fontWeight="600" ml={2} sx={{ display: "flex", alignItems: "center", }}><EditNoteIcon /> View edit history</Link>
</Box>
);
};
Expand Down Expand Up @@ -197,7 +211,7 @@ const FlowEditor: React.FC<any> = ({ flow, breadcrumbs }) => {
const showPreview = useStore((state) => state.showPreview);

return (
<Box id="editor-container">
<EditorContainer id="editor-container">
<Box
sx={{
display: "flex",
Expand All @@ -216,8 +230,8 @@ const FlowEditor: React.FC<any> = ({ flow, breadcrumbs }) => {
url={`${window.location.origin}${rootFlowPath(false)}/published`}
/>
)}
</Box>
</EditorContainer>
);
};

export default FlowEditor;
export default FlowEditor;
6 changes: 6 additions & 0 deletions editor.planx.uk/src/pages/FlowEditor/lib/store/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export interface EditorUIStore {
flowLayout: FlowLayout;
showPreview: boolean;
togglePreview: () => void;
isTestEnvBannerVisible: boolean;
hideTestEnvBanner: () => void;
}

export const editorUIStore: StateCreator<
Expand All @@ -52,6 +54,10 @@ export const editorUIStore: StateCreator<
togglePreview: () => {
set({ showPreview: !get().showPreview });
},

isTestEnvBannerVisible: true,

hideTestEnvBanner: () => set({ isTestEnvBannerVisible: false }),
});

interface PublishFlowResponse {
Expand Down
27 changes: 27 additions & 0 deletions editor.planx.uk/src/pages/layout/FlowEditorLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { PropsWithChildren } from "react";
import EditorMenu from "pages/FlowEditor/components/EditorMenu";
import Box from "@mui/material/Box";
import { styled } from "@mui/material/styles";
import { ErrorBoundary } from "react-error-boundary";
import ErrorFallback from "components/ErrorFallback";
import { hasFeatureFlag } from "lib/featureFlags";

const Root = styled(Box)(() => ({
display: "flex",
alignItems: "stretch",
overflow: "hidden",
flexGrow: 1,
}))

const FlowEditorLayout: React.FC<PropsWithChildren> = ({ children }) => (
<Root>
{ hasFeatureFlag("EDITOR_NAVIGATION") && <EditorMenu /> }
<ErrorBoundary FallbackComponent={ErrorFallback}>
{children}
</ErrorBoundary>
</Root>
);

export default FlowEditorLayout;


Loading
Loading