Skip to content

Commit

Permalink
Design endringer brevredigering & keyboard shortcuts for tekststil (#952
Browse files Browse the repository at this point in the history
)

* gjør brevredigering mer responsiv

* fjern dokumenttado fra sakspart

* keyboard shortcuts for endring av tekst-style

* editor får focus ved manuell endring av tekststil & clean up

* fiks test

* fiks stylelint error
  • Loading branch information
RamziAbuQassim authored Oct 7, 2024
1 parent e7f8617 commit c204082
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,18 @@ describe("<LetterEditor />", () => {
cy.mount(<EditorWithState initial={exampleLetter1} />);

cy.get(".TITLE1").contains("Tittel over punktliste").click();
cy.get('[data-cy="TITLE1-BUTTON"]').should("be.disabled");
cy.get('[data-cy="TITLE2-BUTTON"]').should("be.enabled");
cy.get('[data-cy="PARAGRAPH-BUTTON"]').should("be.enabled").click();
cy.getDataCy("typography-select").contains("Overskrift (alt+2)").should("be.selected");

cy.getDataCy("typography-select").select("Normal (alt+1)");
cy.get(".PARAGRAPH").contains("Tittel over punktliste");
cy.get('[data-cy="PARAGRAPH-BUTTON"]').should("be.disabled");
cy.get('[data-cy="TITLE1-BUTTON"]').should("be.enabled");
cy.get('[data-cy="TITLE2-BUTTON"]').should("be.enabled").click();
cy.getDataCy("typography-select").contains("Normal (alt+1)").should("be.selected");

cy.getDataCy("typography-select").select("Underoverskrift (alt+3)");
cy.get(".TITLE2").contains("Tittel over punktliste");
cy.get('[data-cy="TITLE2-BUTTON"]').should("be.disabled");
cy.get('[data-cy="PARAGRAPH-BUTTON"]').should("be.enabled");
cy.get('[data-cy="TITLE1-BUTTON"]').should("be.enabled");
cy.getDataCy("typography-select").contains("Underoverskrift (alt+3)").should("be.selected");
});
});

describe("Navigation", () => {
it("ArrowUp works within sibling contenteditables", () => {
cy.mount(<EditorWithState initial={exampleLetter1} />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import type { Dispatch, SetStateAction } from "react";
import { createContext, useContext } from "react";

import { DebugPanel } from "~/Brevredigering/LetterEditor/components/DebugPanel";
import type { CallbackReceiver } from "~/Brevredigering/LetterEditor/lib/actions";
import { type CallbackReceiver } from "~/Brevredigering/LetterEditor/lib/actions";

import { ContentGroup } from "./components/ContentGroup";
import { EditorMenu } from "./components/EditorMenu";
import { SakspartView } from "./components/SakspartView";
import { SignaturView } from "./components/SignaturView";
import type { LetterEditorState } from "./model/state";
import { useEditorKeyboardShortcuts } from "./utils";

export const LetterEditor = ({
freeze,
Expand All @@ -31,6 +32,7 @@ export const LetterEditor = ({
}) => {
const letter = editorState.redigertBrev;
const blocks = letter.blocks;
const editorKeyboardShortcuts = useEditorKeyboardShortcuts(editorState, setEditorState);

return (
<div
Expand Down Expand Up @@ -64,7 +66,7 @@ export const LetterEditor = ({
>
{letter.title}
</Heading>
<div>
<div onKeyDown={editorKeyboardShortcuts}>
{blocks.map((block, blockIndex) => (
<div className={block.type} key={blockIndex}>
<ContentGroup literalIndex={{ blockIndex, contentIndex: 0 }} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,77 @@
import { css } from "@emotion/react";
import { CheckmarkCircleFillIcon, ExclamationmarkTriangleFillIcon } from "@navikt/aksel-icons";
import { Button, HStack, Loader } from "@navikt/ds-react";
import { HStack, Loader, Select } from "@navikt/ds-react";
import { format, isToday } from "date-fns";
import { memo, type ReactNode, useEffect, useRef, useState } from "react";
import { memo, useEffect, useRef, useState } from "react";

import Actions from "~/Brevredigering/LetterEditor/actions";
import { useEditor } from "~/Brevredigering/LetterEditor/LetterEditor";
import { isTextContent } from "~/Brevredigering/LetterEditor/model/utils";
import { formatTime } from "~/utils/dateUtils";

import type { CallbackReceiver } from "../lib/actions";
import { applyAction } from "../lib/actions";
import type { LetterEditorState } from "../model/state";
import { getCursorOffset } from "../services/caretUtils";
import type { Typography } from "../utils";
import { TypographyToText } from "../utils";

const SelectTypography = (props: {
editorState: LetterEditorState;
setEditorState: CallbackReceiver<LetterEditorState>;
}) => {
const changeableContent = isTextContent(
props.editorState.redigertBrev.blocks[props.editorState.focus.blockIndex]?.content?.[
props.editorState.focus.contentIndex
],
);

return (
<Select
data-cy="typography-select"
hideLabel
label="Tekst stil"
onChange={(e) => {
applyAction(
Actions.switchTypography,
props.setEditorState,
props.editorState.focus,
e.target.value as Typography,
);
//setter fokuset tilbake til editor etter valgt tekststil
applyAction(Actions.cursorPosition, props.setEditorState, getCursorOffset());
}}
readOnly={!changeableContent}
size="small"
value={props.editorState.redigertBrev.blocks[props.editorState.focus.blockIndex]?.type}
>
{Object.entries(TypographyToText).map(([key, value]) => (
<option key={key} value={key}>
{value}
</option>
))}
</Select>
);
};

export const EditorMenu = () => {
const { freeze, error, editorState, setEditorState } = useEditor();
const activeTypography = editorState.redigertBrev.blocks[editorState.focus.blockIndex]?.type;
const changeableContent = isTextContent(
editorState.redigertBrev.blocks[editorState.focus.blockIndex]?.content?.[editorState.focus.contentIndex],
);

return (
<div
css={css`
border-bottom: 1px solid var(--a-gray-200);
background: var(--a-blue-50);
padding: var(--a-spacing-3) var(--a-spacing-4);
background: var(--a-white);
padding: var(--a-spacing-2) var(--a-spacing-4);
display: flex;
gap: var(--a-spacing-2);
align-self: stretch;
align-items: center;
justify-content: space-between;
`}
>
<SelectTypographyButton
dataCy="TITLE1-BUTTON"
enabled={activeTypography !== "TITLE1" && changeableContent}
onClick={() => applyAction(Actions.switchTypography, setEditorState, editorState.focus, "TITLE1")}
>
Overskrift 1
</SelectTypographyButton>
<SelectTypographyButton
dataCy="TITLE2-BUTTON"
enabled={activeTypography !== "TITLE2" && changeableContent}
onClick={() => applyAction(Actions.switchTypography, setEditorState, editorState.focus, "TITLE2")}
>
Overskrift 2
</SelectTypographyButton>
<SelectTypographyButton
dataCy="PARAGRAPH-BUTTON"
enabled={activeTypography !== "PARAGRAPH" && changeableContent}
onClick={() => applyAction(Actions.switchTypography, setEditorState, editorState.focus, "PARAGRAPH")}
>
Normal
</SelectTypographyButton>
<SelectTypography editorState={editorState} setEditorState={setEditorState} />

<LagretTidspunkt
datetime={editorState.info.sistredigert}
error={error}
Expand Down Expand Up @@ -132,39 +154,3 @@ const LagretTidspunkt = memo(
}
},
);

function SelectTypographyButton({
dataCy,
enabled,
children,
onClick,
}: {
dataCy: string;
enabled: boolean;
children: ReactNode;
onClick: () => void;
}) {
return (
<Button
css={
!enabled &&
css`
color: var(--a-text-on-action);
background-color: var(--a-surface-action-active);
`
}
data-cy={dataCy}
disabled={!enabled}
// Use mouseDown instead of onClick to prevent the cursor from losing focus
onMouseDown={(event) => {
event.preventDefault();
onClick();
}}
size="xsmall"
type="button"
variant="secondary-neutral"
>
{children}
</Button>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@ export const SakspartView = ({ sakspart }: { sakspart: Sakspart }) => (
<span />
<span>Saksnummer:</span>
<span>{sakspart.saksnummer}</span>
<span>{sakspart.dokumentDato}</span>
</div>
);
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
.editor {
margin-top: var(--a-spacing-6);
width: 758px;
/*clamp: minimum, ideal, max */
padding: 0 clamp(1rem, 2vw, 4rem);
caret-color: green;

[contenteditable] {

&:focus-within {
Expand All @@ -18,14 +20,14 @@
font-size: var(--a-font-size-heading-medium);
letter-spacing: -0.002em;
line-height: var(--a-font-line-height-heading-medium);
font-weight: var(--a-font-weight-bold);
margin-bottom: var(--a-spacing-2);
font-weight: var(--a-font-weight-bold);
margin-bottom: var(--a-spacing-2);
}

.TITLE2 {
font-size: var(--a-font-size-heading-small);
letter-spacing: -0.001em;
line-height: var(--a-font-line-height-heading-small);
font-weight: var(--a-font-weight-bold);
margin-bottom: var(--a-spacing-2);
}
font-weight: var(--a-font-weight-bold);
margin-bottom: var(--a-spacing-2);
}
35 changes: 35 additions & 0 deletions skribenten-web/frontend/src/Brevredigering/LetterEditor/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { Dispatch, SetStateAction } from "react";

import Actions from "./actions";
import { applyAction } from "./lib/actions";
import type { LetterEditorState } from "./model/state";

export enum Typography {
PARAGRAPH = "PARAGRAPH",
TITLE1 = "TITLE1",
TITLE2 = "TITLE2",
}

export const TypographyToText = {
[Typography.PARAGRAPH]: "Normal (alt+1)",
[Typography.TITLE1]: "Overskrift (alt+2)",
[Typography.TITLE2]: "Underoverskrift (alt+3)",
} as const;

export const useEditorKeyboardShortcuts = (
editorState: LetterEditorState,
setEditorState: Dispatch<SetStateAction<LetterEditorState>>,
) => {
return (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.altKey && event.code === "Digit1") {
event.preventDefault();
applyAction(Actions.switchTypography, setEditorState, editorState.focus, Typography.PARAGRAPH);
} else if (event.altKey && event.code === "Digit2") {
event.preventDefault();
applyAction(Actions.switchTypography, setEditorState, editorState.focus, Typography.TITLE1);
} else if (event.altKey && event.code === "Digit3") {
event.preventDefault();
applyAction(Actions.switchTypography, setEditorState, editorState.focus, Typography.TITLE2);
}
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -222,40 +222,49 @@ function RedigerBrev({
<div
css={css`
background: var(--a-white);
display: grid;
grid-template:
"modelEditor letterEditor" 1fr
"footer footer" var(--nav-bar-height) / 30% 70%;
display: flex;
flex-direction: column;
border-left: 1px solid var(--a-gray-200);
border-right: 1px solid var(--a-gray-200);
> form:first-of-type {
padding: var(--a-spacing-6);
border-right: 1px solid var(--a-gray-200);
}
`}
>
<ModelEditor
brevId={brev.info.id}
brevkode={brev.info.brevkode}
defaultValues={defaultValuesModelEditor}
disableSubmit={saksbehandlerValgMutation.isPending}
onSubmit={onSubmit}
saksId={saksId}
vedtaksId={vedtaksId}
/>
<LetterEditor
editorHeight={"var(--main-page-content-height)"}
editorState={editorState}
error={redigertBrevMutation.isError || saksbehandlerValgMutation.isError || signaturMutation.isError}
freeze={redigertBrevMutation.isPending || saksbehandlerValgMutation.isPending || signaturMutation.isPending}
setEditorState={setEditorState}
showDebug={showDebug}
/>
<div
css={css`
display: grid;
grid-template-columns: 25% 75%;
> form:first-of-type {
padding: var(--a-spacing-6);
border-right: 1px solid var(--a-gray-200);
}
`}
>
<ModelEditor
brevId={brev.info.id}
brevkode={brev.info.brevkode}
defaultValues={defaultValuesModelEditor}
disableSubmit={saksbehandlerValgMutation.isPending}
onSubmit={onSubmit}
saksId={saksId}
vedtaksId={vedtaksId}
/>
<LetterEditor
editorHeight={"var(--main-page-content-height)"}
editorState={editorState}
error={redigertBrevMutation.isError || saksbehandlerValgMutation.isError || signaturMutation.isError}
freeze={redigertBrevMutation.isPending || saksbehandlerValgMutation.isPending || signaturMutation.isPending}
setEditorState={setEditorState}
showDebug={showDebug}
/>
</div>
<HStack
css={css`
grid-area: footer;
position: sticky;
bottom: 0;
left: 0;
width: 100%;
background: var(--a-white);
border-top: 1px solid var(--a-gray-200);
padding: 0.5rem 1rem;
`}
Expand Down

0 comments on commit c204082

Please sign in to comment.