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

Get minimum ProseMirror editor working #1

Merged
merged 23 commits into from
Oct 25, 2023
Merged

Conversation

rhystmills
Copy link
Collaborator

@rhystmills rhystmills commented Oct 19, 2023

Co-authored with @samanthagottlieb

What does this change?

This gets a basic ProseMirror editor working. The aim is to create a general purpose editor that satisfied the requirements of:

media-atom-maker is a lower priority because it's already been moved to ProseMirror from Scribe (so there's no Scribe-based vulnerability), but we want to make this package compatible with media-atom-maker's requirements so it can be ported over in the future, so that the editors in the four repositories can all be managed via this one package.

This branch has been tested locally in tagmanager via yalc. The package has not yet been deployed to npm.

Things this PR doesn't have, that we will want to add in future PRs:

  • A means of testing changes from this package itself, for example a demo-app that includes the React editor component. Currently this package only builds a component to be used by other packages.
  • A means of deploying to an npm package.

Currently, vanilla css styles are included in the built dist and need to be imported in the target package. Is this a good setup? (A google chat thread led to recommendations against emotion and I wanted to keep this package as un-opinionated as possible).

How to test

  1. Publish this package locally using yarn yalc from the root of the project
  2. Use this branch of tagmanager locally.
  3. Add that package to tagmanager using yalc add @guardian/prosemirror-editor
  4. Run tagmanager
  5. In tagmanager, go to 'Create a new tag' and pick an option from the dropdown.
  6. Click on an existing tag from the home page table and inspect the rich text editor element of the Description/Profile field. Do the classes include 'prosemirror'? (They should).
  7. Open the network tab, clear the requests. Then edit the tag's rich text, and save the changes. A POST request should go to the url similar to https://tagmanager.local.dev-gutools.co.uk/api/tag/71301. Does the description sent in that request look valid? Is the bold text serialised as <b> tags and the italic text as <i> tags? (It should). Are new paragraphs serialised as new <p> tags? (They should).

Copy link
Member

@davidfurey davidfurey left a comment

Choose a reason for hiding this comment

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

Epic PR! I've made a few suggestions, and haven't yet run through your test steps.

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright 2023 Guardian News and Media
Copy link
Member

Choose a reason for hiding this comment

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

Where did this license come from?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Great. Surprised that it isn't normal for us to state that, but happy for us to stick with our convention

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Looks like we do in some repos, I'll mention it for the convenience of others viewing the license.

README.md Outdated
It is also designed to meet the features required by the rich text editor in [media-atom-maker](https://github.com/guardian/media-atom-maker/blob/3391bfd82ad27848dc9e06a08628320ce481006e/public/video-ui/src/components/FormFields/RichTextEditor.tsx#L2).

It aims to provide:
- A `RichTextEditor` serialising to one-to-many <p> tags containing the markip below - supporting only the requirements of the above editors, at least until we have further use cases. Specifically, from the first three editors:
Copy link
Member

Choose a reason for hiding this comment

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

I think you might need to escape <p> to make this render correctly. Also, do you really mean <p> here?

Typo:markip

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Whoops - yes it should be <p> but not be markip.

README.md Outdated
- unlinking
- strikethrough
- superscript and subscript
`media-atom-maker` adds some more requirements:
Copy link
Member

Choose a reason for hiding this comment

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

Probably need a - here

package.json Outdated
{
"name": "@guardian/prosemirror-editor",
"version": "0.1.0",
"description": "Providing implementations of ProseMirror for use in our tools.",
Copy link
Member

Choose a reason for hiding this comment

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

our -> the Guardian ?

shouldAcceptCopiedText: boolean;
}

export const RichTextEditor = ({ value, onUpdate, config, label, shouldAcceptCopiedText = false }: RichTextEditorProps) => {
Copy link
Member

Choose a reason for hiding this comment

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

Why is a default value required for shouldAcceptCopiedText, which is a required field?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Wise, optional now

allowedMarks: string[];
};

type FlatTextMark = "strong" | "strike" | "em";
Copy link
Member

Choose a reason for hiding this comment

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

You could avoid having to write these out again on line 27 with:

const flatTextMarks = ["strong", "strike", "em"] as const
type FlatTextMark = typeof flatTextMarks

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I did start with this - but I had trouble getting Typescript happy with partial variants of the flatTextMarks - it should be valid to have a customFlatTextConfig that only supports 'strong', for example. A greater Typescripter might find better way to do that than what I've ended up with - possibly similar to the above.

allowedNodes: string[]
): OrderedMap<NodeSpec> => {
let map = OrderedMap.from<NodeSpec>({});
nodes.forEach((key, node) => {
Copy link
Member

Choose a reason for hiding this comment

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

Isn't it a shame that OrderedMap doesn't have filter?

You could write your own and avoid the duplicated lines filterNodes and filterMarks. But for 2 uses, possibly not worth the complexity

import { isInNode, toggleBulletListCommand } from "./utils/listsHelpers";
import { linkItemCommand, unlinkItemCommand } from "./utils/command-helpers";

export const cmdItem = (cmd: Command, options: Partial<MenuItemSpec>) => {
Copy link
Member

Choose a reason for hiding this comment

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

To avoid having to cast options.title as string:

Suggested change
export const cmdItem = (cmd: Command, options: Partial<MenuItemSpec>) => {
export const cmdItem = (cmd: Command, options: Partial<MenuItemSpec> & { title: string }) => {

(Applies multiple times in this file)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Delightful, implemented now.

// to format the outputHtml as an html string rather than a document fragment, we are creating a temporary div, adding it as a child, then using innerHTML which returns an html string
const tmp = document.createElement('div');
tmp.appendChild(outputHtml);
if (onChange) {
Copy link
Member

Choose a reason for hiding this comment

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

Is any of the work on line 45-49 valuable if onChange is undefined? Also, the types imply that onChange cannot be undefined?

Copy link
Collaborator Author

@rhystmills rhystmills Oct 25, 2023

Choose a reason for hiding this comment

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

Yes, good point. Not sure how that made it's way in - think the code may be borrowed from my prosemirror implementation in media-atom-maker and can't remember why past me wrote it. I'll remove the if condition.

};

export const isTooLong = (value: string, maxWordLength: number): boolean => {
const wordLength = getWords(value).reduce((length, word) => {
Copy link
Member

Choose a reason for hiding this comment

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

I appreciate that this is copy and pasted from elsewhere, but as far as I can see this calculates the total number of characters in all of the words in value rather than just a count of the number of words in value

Copy link
Collaborator Author

@rhystmills rhystmills Oct 25, 2023

Choose a reason for hiding this comment

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

Yes, looks like it - it's a bit of validation from media-atom-maker most recently updated in guardian/media-atom-maker#1127 . Naming could be better - perhaps would be something like tooManyCharactersInAllWords would be an improvement.

It looks like there's an assumption based into the original logic that spaces aren't including in Youtube's video title character limit, which might be wrong. Seems like it would be an odd restriction.

Copy link
Collaborator Author

@rhystmills rhystmills Oct 25, 2023

Choose a reason for hiding this comment

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

No explanation of the odd logic in the original PR. I'd be inclined to go for a simple .length on the string value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm removing these functions for now as they're currently unused -when length validation is added I'll make it simpler.

Copy link
Member

Choose a reason for hiding this comment

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

Great. I suspect there is an error in the original PR rather than the logic being intentionally odd. I'll open a PR in media-atom-maker to fix

Copy link
Member

@davidfurey davidfurey left a comment

Choose a reason for hiding this comment

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

lgtm

@rhystmills rhystmills merged commit 6ba81cf into main Oct 25, 2023
1 check passed
@jonathonherbert jonathonherbert deleted the rm/initialise-repo branch November 14, 2023 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants