Skip to content

Commit 285ca31

Browse files
Enhance user experience in forms (#460)
1 parent 0e313fe commit 285ca31

File tree

12 files changed

+162
-37
lines changed

12 files changed

+162
-37
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ yarn-debug.log*
2424
yarn-error.log*
2525

2626
editor-content.js
27+
index.js
28+
formik.js

formik.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { EditorProps } from "./index";
2+
3+
interface FormikEditorProps extends EditorProps {
4+
name: string;
5+
}
6+
7+
function FormikEditor(props: FormikEditorProps): JSX.Element;
8+
9+
export default FormikEditor;

types.d.ts renamed to index.d.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ interface Config {
6262
placeholder?: Partial<PlaceholderOptions>;
6363
}
6464

65-
export function Editor(props: {
65+
export interface EditorProps {
6666
initialValue?: string;
6767
menuType?: "fixed" | "bubble" | "none";
68+
label?: string;
69+
required?: boolean;
6870
autoFocus?: boolean;
6971
hideSlashCommands?: boolean;
7072
defaults?: string[];
@@ -88,10 +90,14 @@ export function Editor(props: {
8890
error?: string;
8991
config?: Config;
9092
[otherProps: string]: any;
91-
}): JSX.Element;
93+
}
94+
95+
export function Editor(props: EditorProps): JSX.Element;
9296

9397
export function EditorContent(props: {
9498
content?: string;
9599
className?: string;
96100
[otherProps: string]: any;
97101
}): JSX.Element;
102+
103+
export function isEditorEmpty(htmlContent: string | null | undefined): boolean;

lib/components/Common/ErrorWrapper.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const ErrorWrapper = ({ error, isFixedMenuActive, children }) => {
2828
return message;
2929
};
3030

31-
if (!error) return children;
31+
if (isNilOrEmpty(error)) return children;
3232

3333
return (
3434
<>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React, { forwardRef } from "react";
2+
3+
import { Field } from "formik";
4+
5+
import Editor from ".";
6+
7+
import "../../index.scss";
8+
9+
const FormikEditor = ({ name, ...otherProps }, ref) => (
10+
<Field name={name}>
11+
{({ field, form, meta }) => (
12+
<Editor
13+
{...field}
14+
error={meta.touched ? meta.error : ""}
15+
initialValue={field.value}
16+
ref={ref}
17+
onBlur={() => form.setFieldTouched(name, true)}
18+
onChange={value => form.setFieldValue(name, value)}
19+
{...otherProps}
20+
/>
21+
)}
22+
</Field>
23+
);
24+
25+
export default forwardRef(FormikEditor);

lib/components/Editor/index.jsx

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import { EditorView } from "prosemirror-view";
66

77
import { DIRECT_UPLOAD_ENDPOINT } from "common/constants";
88
import ErrorWrapper from "components/Common/ErrorWrapper";
9+
import Label from "components/Common/Label";
910
import useEditorWarnings from "hooks/useEditorWarnings";
10-
import { noop } from "utils/common";
11+
import { noop, slugify } from "utils/common";
1112

1213
import { DEFAULT_EDITOR_OPTIONS } from "./constants";
1314
import CharacterCountWrapper from "./CustomExtensions/CharacterCount";
@@ -24,6 +25,8 @@ const Editor = (
2425
{
2526
initialValue = "",
2627
menuType = "fixed",
28+
label = "",
29+
required = false,
2730
autoFocus = false,
2831
hideSlashCommands = false,
2932
defaults = DEFAULT_EDITOR_OPTIONS,
@@ -109,31 +112,41 @@ const Editor = (
109112
};
110113

111114
return (
112-
<ErrorWrapper error={error} isFixedMenuActive={isFixedMenuActive}>
113-
<CharacterCountWrapper editor={editor} isActive={isCharacterCountActive}>
114-
<Menu
115-
addons={addons}
116-
defaults={defaults}
115+
<div>
116+
{label && (
117+
<Label data-cy={`${slugify(label)}-editor-label`} required={required}>
118+
{label}
119+
</Label>
120+
)}
121+
<ErrorWrapper error={error} isFixedMenuActive={isFixedMenuActive}>
122+
<CharacterCountWrapper
117123
editor={editor}
118-
editorSecrets={editorSecrets}
119-
isIndependant={false}
120-
mentions={mentions}
121-
menuType={menuType}
122-
uploadConfig={uploadConfig}
123-
uploadEndpoint={uploadEndpoint}
124-
variables={variables}
125-
/>
126-
<EditorContent editor={editor} {...otherProps} />
127-
<ImageUploader
128-
editor={editor}
129-
imageUploadUrl={uploadEndpoint}
130-
isOpen={isImageUploaderOpen}
131-
unsplashApiKey={editorSecrets.unsplash}
132-
uploadConfig={uploadConfig}
133-
onClose={() => setIsImageUploaderOpen(false)}
134-
/>
135-
</CharacterCountWrapper>
136-
</ErrorWrapper>
124+
isActive={isCharacterCountActive}
125+
>
126+
<Menu
127+
addons={addons}
128+
defaults={defaults}
129+
editor={editor}
130+
editorSecrets={editorSecrets}
131+
isIndependant={false}
132+
mentions={mentions}
133+
menuType={menuType}
134+
uploadConfig={uploadConfig}
135+
uploadEndpoint={uploadEndpoint}
136+
variables={variables}
137+
/>
138+
<EditorContent editor={editor} {...otherProps} />
139+
<ImageUploader
140+
editor={editor}
141+
imageUploadUrl={uploadEndpoint}
142+
isOpen={isImageUploaderOpen}
143+
unsplashApiKey={editorSecrets.unsplash}
144+
uploadConfig={uploadConfig}
145+
onClose={() => setIsImageUploaderOpen(false)}
146+
/>
147+
</CharacterCountWrapper>
148+
</ErrorWrapper>
149+
</div>
137150
);
138151
};
139152

lib/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { isEditorEmpty } from "utils/common";
2+
13
import Editor from "./components/Editor";
24
import Menu from "./components/Editor/Menu";
35
import EditorContent from "./components/EditorContent";
46
import "./index.scss";
57

6-
export { Editor, EditorContent, Menu };
8+
export { Editor, EditorContent, Menu, isEditorEmpty };

lib/styles/components/_input.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,6 @@
240240
.ne-label {
241241
display: flex;
242242
align-items: center;
243+
color: rgb(var(--neeto-ui-gray-700));
244+
margin-bottom: 8px;
243245
}

lib/utils/common.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,14 @@ export const humanize = string => {
2727
export const isNilOrEmpty = object => isNil(object) || isEmpty(object);
2828

2929
export const noop = () => {};
30+
31+
export const isEditorEmpty = htmlContent => {
32+
if (isNilOrEmpty(htmlContent)) return true;
33+
34+
const element = document.createElement("div");
35+
element.innerHTML = htmlContent;
36+
const editorIsEmpty = isNilOrEmpty(element.textContent);
37+
element.remove();
38+
39+
return editorIsEmpty;
40+
};

package.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "@bigbinary/neeto-editor",
33
"version": "1.0.8",
4-
"main": "./build/index.js",
5-
"types": "./types.d.ts",
4+
"main": "./index.js",
5+
"module": "./index.js",
6+
"types": "./index.d.ts",
67
"description": "neetoEditor is the library that drives the rich text experience in all neeto products built at BigBinary",
78
"keywords": [
89
"ui",
@@ -11,9 +12,10 @@
1112
"react"
1213
],
1314
"files": [
14-
"dist",
15-
"build",
16-
"types.d.ts"
15+
"index.js",
16+
"formik.js",
17+
"index.d.ts",
18+
"formik.d.ts"
1719
],
1820
"author": "BigBinary",
1921
"license": "MIT",
@@ -85,6 +87,7 @@
8587
"eslint-plugin-react-hooks": "4.6.0",
8688
"eslint-plugin-storybook": "0.6.6",
8789
"eslint-plugin-unused-imports": "2.0.0",
90+
"formik": "2.2.9",
8891
"husky": "8.0.1",
8992
"lint-staged": "13.0.3",
9093
"lodash.isplainobject": "4.0.6",
@@ -116,6 +119,7 @@
116119
"webpack-dev-server": "4.9.3"
117120
},
118121
"peerDependencies": {
122+
"formik": "2.2.9",
119123
"react": "17.0.2",
120124
"react-dom": "17.0.2"
121125
},

rollup.config.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,18 @@ export default [
4545
{
4646
input: "./lib/index.js",
4747
output: {
48-
file: "build/index.js",
48+
file: "./index.js",
49+
format: "esm",
50+
sourcemap: false,
51+
name: "neetoEditor",
52+
assetFileNames: "[name][extname]",
53+
},
54+
plugins,
55+
},
56+
{
57+
input: "./lib/components/Editor/FormikEditor.jsx",
58+
output: {
59+
file: "./formik.js",
4960
format: "esm",
5061
sourcemap: false,
5162
name: "neetoEditor",

yarn.lock

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5658,6 +5658,11 @@ deep-is@^0.1.3, deep-is@~0.1.3:
56585658
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
56595659
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
56605660

5661+
deepmerge@^2.1.1:
5662+
version "2.2.1"
5663+
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
5664+
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
5665+
56615666
deepmerge@^4.2.2:
56625667
version "4.2.2"
56635668
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@@ -6889,6 +6894,19 @@ format@^0.2.0:
68896894
resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
68906895
integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==
68916896

6897+
formik@2.2.9:
6898+
version "2.2.9"
6899+
resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0"
6900+
integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==
6901+
dependencies:
6902+
deepmerge "^2.1.1"
6903+
hoist-non-react-statics "^3.3.0"
6904+
lodash "^4.17.21"
6905+
lodash-es "^4.17.21"
6906+
react-fast-compare "^2.0.1"
6907+
tiny-warning "^1.0.2"
6908+
tslib "^1.10.0"
6909+
68926910
forwarded@0.2.0:
68936911
version "0.2.0"
68946912
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -7391,6 +7409,13 @@ hmac-drbg@^1.0.1:
73917409
minimalistic-assert "^1.0.0"
73927410
minimalistic-crypto-utils "^1.0.1"
73937411

7412+
hoist-non-react-statics@^3.3.0:
7413+
version "3.3.2"
7414+
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
7415+
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
7416+
dependencies:
7417+
react-is "^16.7.0"
7418+
73947419
hosted-git-info@^2.1.4:
73957420
version "2.8.9"
73967421
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
@@ -8549,6 +8574,11 @@ locate-path@^6.0.0:
85498574
dependencies:
85508575
p-locate "^5.0.0"
85518576

8577+
lodash-es@^4.17.21:
8578+
version "4.17.21"
8579+
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
8580+
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
8581+
85528582
lodash.debounce@^4.0.8:
85538583
version "4.0.8"
85548584
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@@ -10860,6 +10890,11 @@ react-element-to-jsx-string@^14.3.4:
1086010890
is-plain-object "5.0.0"
1086110891
react-is "17.0.2"
1086210892

10893+
react-fast-compare@^2.0.1:
10894+
version "2.0.4"
10895+
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
10896+
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
10897+
1086310898
react-fast-compare@^3.0.1:
1086410899
version "3.2.0"
1086510900
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
@@ -10893,7 +10928,7 @@ react-is@17.0.2, react-is@^17.0.1:
1089310928
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
1089410929
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
1089510930

10896-
react-is@^16.13.1:
10931+
react-is@^16.13.1, react-is@^16.7.0:
1089710932
version "16.13.1"
1089810933
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
1089910934
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -12409,6 +12444,11 @@ timers-browserify@^2.0.4:
1240912444
dependencies:
1241012445
setimmediate "^1.0.4"
1241112446

12447+
tiny-warning@^1.0.2:
12448+
version "1.0.3"
12449+
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
12450+
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
12451+
1241212452
tippy.js@6.3.7, tippy.js@^6.3.1, tippy.js@^6.3.7:
1241312453
version "6.3.7"
1241412454
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
@@ -12513,7 +12553,7 @@ tsconfig-paths@^3.14.1:
1251312553
minimist "^1.2.6"
1251412554
strip-bom "^3.0.0"
1251512555

12516-
tslib@^1.8.1, tslib@^1.9.3:
12556+
tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.3:
1251712557
version "1.14.1"
1251812558
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
1251912559
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==

0 commit comments

Comments
 (0)