@procore/text-editor 0.0.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +50 -24
  3. package/codemod/__fixtures__/src/components/MultilineFormRichText.tsx +2 -1
  4. package/codemod/text-editor-migrate.js +54 -0
  5. package/codemod/text-editor-migrate.test.js +9 -0
  6. package/dist/DebouncedTextEditor/DebouncedTextEditor.d.ts +35 -0
  7. package/dist/DebouncedTextEditor/DebouncedTextEditor.js +71 -0
  8. package/dist/DebouncedTextEditor/DebouncedTextEditor.js.map +1 -0
  9. package/dist/DebouncedTextEditor/index.d.ts +1 -0
  10. package/dist/DebouncedTextEditor/index.js +2 -0
  11. package/dist/DebouncedTextEditor/index.js.map +1 -0
  12. package/dist/TextEditor/TextEditor.d.ts +1 -1
  13. package/dist/TextEditor/TextEditor.js +90 -36
  14. package/dist/TextEditor/TextEditor.js.map +1 -1
  15. package/dist/TextEditor/TextEditor.styles.js +1 -1
  16. package/dist/TextEditor/TextEditor.types.d.ts +38 -30
  17. package/dist/TextEditor/TextEditor.types.js.map +1 -1
  18. package/dist/TextEditor/textEditorTheming/textEditorTheming.styles.js +2 -1
  19. package/dist/TextEditor/textEditorTheming/textEditorTheming.styles.js.map +1 -1
  20. package/dist/TextEditor/utils/config.js +3 -3
  21. package/dist/TextEditor/utils/config.js.map +1 -1
  22. package/dist/TextEditor/utils/index.d.ts +0 -1
  23. package/dist/TextEditor/utils/index.js +0 -1
  24. package/dist/TextEditor/utils/index.js.map +1 -1
  25. package/dist/TextEditorOutput/TextEditorOutput.styles.js +1 -1
  26. package/dist/_storyHelpers/constants.d.ts +1 -0
  27. package/dist/_storyHelpers/constants.js +2 -1
  28. package/dist/_storyHelpers/constants.js.map +1 -1
  29. package/dist/_typedoc/TextEditor/TextEditor.types.json +57 -47
  30. package/dist/_typedoc/TextEditor/TextEditorProvider.types.json +2 -2
  31. package/dist/_typedoc/TextEditorOutput/TextEditorOutput.types.json +3 -3
  32. package/dist/_typedoc/deprecations.json +1 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +1 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/utils/debounce.d.ts +1 -0
  37. package/dist/utils/debounce.js +24 -0
  38. package/dist/utils/debounce.js.map +1 -0
  39. package/package.json +13 -12
  40. package/dist/TextEditor/utils/plugins.d.ts +0 -7
  41. package/dist/TextEditor/utils/plugins.js +0 -184
  42. package/dist/TextEditor/utils/plugins.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @procore/text-editor
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ffdc97f: - Add optional `readonly` prop to display content in read-only mode.
8
+ - Remove Accessibility icons from the toolbar.
9
+ - Expose `DebouncedTextEditor` component for debounced value updates.
10
+
11
+ ## 0.1.0
12
+
13
+ ### Minor Changes
14
+
15
+ - New toolbar defaults. Remove `plugins` and `config` props to rely on the TextEditor package's default internal configuration.
16
+ - Add Enter key `stopPropagation` to prevent form submission when pressing Enter in the editor.
17
+
3
18
  ## 0.0.3
4
19
 
5
20
  ### Patch Changes
package/README.md CHANGED
@@ -10,7 +10,7 @@ yarn add @procore/text-editor
10
10
 
11
11
  ## Usage
12
12
 
13
- ### Basic Usage
13
+ ### Standalone Usage
14
14
 
15
15
  ```tsx
16
16
  import { TextEditor } from '@procore/text-editor'
@@ -27,47 +27,39 @@ function MyComponent() {
27
27
  }
28
28
  ```
29
29
 
30
- ### With Custom Configuration
30
+ ### With @procore/core-react Forms
31
31
 
32
32
  ```tsx
33
+ import { Form } from '@procore/core-react'
33
34
  import { TextEditor } from '@procore/text-editor'
34
35
 
35
36
  function MyComponent() {
36
- const [value, setValue] = React.useState('')
37
-
38
37
  return (
39
- <TextEditor
40
- value={value}
41
- onChange={(newValue) => setValue(newValue)}
42
- plugins={['link', 'table']}
43
- config={(defaultConfig) => ({
44
- ...defaultConfig,
45
- // Your custom CKEditor configuration
46
- })}
38
+ <Form.RichText
39
+ name="richtext"
40
+ label="Richtext"
41
+ textEditorComponent={TextEditor}
47
42
  />
48
43
  )
49
44
  }
50
45
  ```
51
46
 
52
- ### TextEditorProvider
53
47
 
54
- Use `TextEditorProvider` to enable advanced features:
48
+ ### Displaying Read-Only Content
49
+
50
+ You can display formatted text in two ways:
51
+
52
+ **Option 1:** Use `TextEditor` with `readonly` prop:
55
53
 
56
54
  ```tsx
57
- import { TextEditor, TextEditorProvider } from '@procore/text-editor'
55
+ import { TextEditor } from '@procore/text-editor'
58
56
 
59
- function MyComponent() {
60
- return (
61
- <TextEditorProvider features={{ stickyToolbar: true, tabAsNavigation: true }}>
62
- <TextEditor value={value} onChange={setValue} />
63
- </TextEditorProvider>
64
- )
57
+ function MyComponent({ htmlContent }) {
58
+ return <TextEditor value={htmlContent} readonly />
65
59
  }
66
60
  ```
67
61
 
68
- ### TextEditorOutput
69
-
70
- Display formatted text from the editor:
62
+ **Option 2:** Use the `TextEditorOutput` component:
71
63
 
72
64
  ```tsx
73
65
  import { TextEditorOutput } from '@procore/text-editor'
@@ -77,6 +69,40 @@ function MyComponent({ htmlContent }) {
77
69
  }
78
70
  ```
79
71
 
72
+ ## Features
73
+
74
+
75
+ ### Toolbar Features
76
+
77
+ The editor includes a comprehensive toolbar with the following capabilities:
78
+
79
+ | Feature | Description |
80
+ |---------|-------------|
81
+ | **Bold** | Apply bold formatting |
82
+ | **Italic** | Apply italic formatting |
83
+ | **Underline** | Apply underline formatting |
84
+ | **Strikethrough** | Apply strikethrough formatting |
85
+ | **Alignment** | Left, center, and right text alignment |
86
+ | **Bulleted List** | Create unordered lists |
87
+ | **Numbered List** | Create ordered lists with start index and reversed options |
88
+ | **Indent/Outdent** | Increase or decrease text indentation |
89
+ | **Cut/Paste** | Cut and paste content with clipboard support |
90
+ | **Paste as Text** | Paste content as plain text without formatting |
91
+ | **Font Size** | Choose from 8pt, 10pt, 12pt, 14pt, 18pt, 24pt, 36pt |
92
+ | **Font Color** | Apply text color from a 22-color palette |
93
+ | **Background Color** | Apply background highlight from a 22-color palette |
94
+ | **Insert Table** | Create and edit tables with cell properties |
95
+ | **Link** | Insert and edit hyperlinks with auto-link detection |
96
+ | **Block Quote** | Create block quotations |
97
+ | **Heading** | Apply heading styles |
98
+ | **Horizontal Line** | Insert horizontal dividers |
99
+ | **Remove Format** | Clear all formatting from selected text |
100
+ | **Special Characters** | Insert special characters and symbols |
101
+ | **Subscript** | Apply subscript formatting |
102
+ | **Superscript** | Apply superscript formatting |
103
+ | **Code Block** | Insert formatted code blocks |
104
+ | **Undo/Redo** | Undo and redo editing actions |
105
+
80
106
  ## Jest Configuration
81
107
 
82
108
  To ensure your Jest tests work with CKEditor, wrap your Jest configuration with `textEditorJestConfig`:
@@ -1,4 +1,4 @@
1
- import { Form } from '@procore/core-react'
1
+ import { Form, TextEditor } from '@procore/core-react'
2
2
  import React from 'react'
3
3
 
4
4
  export const MultilineFormRichText = () => {
@@ -9,6 +9,7 @@ export const MultilineFormRichText = () => {
9
9
  label="Description"
10
10
  plugins={['table']}
11
11
  />
12
+ <TextEditor value="notes" plugins={['links']} />
12
13
  </Form>
13
14
  )
14
15
  }
@@ -9,6 +9,7 @@
9
9
  * 2. Updates Form.RichText usage to include textEditorComponent prop
10
10
  * 3. Updates Jest configuration to use textEditorJestConfig
11
11
  * 4. Adds ckeditor5 singleton to module federation shared config
12
+ * 5. Remove deprecated plugins prop from TextEditor and Form.RichText components
12
13
  */
13
14
 
14
15
  const fs = require('fs')
@@ -168,6 +169,52 @@ function transformImports(fileContent) {
168
169
  return { content: newContent, modified, needsTextEditorImport }
169
170
  }
170
171
 
172
+ /**
173
+ * Remove deprecated plugins prop from TextEditor and Form.RichText components
174
+ */
175
+ function removeDeprecatedProps(fileContent) {
176
+ let modified = false
177
+ let newContent = fileContent
178
+
179
+ // Pattern to match TextEditor components with plugins prop
180
+ const textEditorRegex = /(<TextEditor)([\s\S]*?)(\/>|>)/g
181
+ newContent = newContent.replace(
182
+ textEditorRegex,
183
+ (match, opening, props, closing) => {
184
+ let updatedProps = props
185
+ const hasPlugins = props.includes('plugins=')
186
+
187
+ if (hasPlugins) {
188
+ // Remove plugins prop
189
+ updatedProps = updatedProps.replace(/\s*plugins\s*=\s*\{[^}]*\}/g, '')
190
+ modified = true
191
+ }
192
+
193
+ return opening + updatedProps + closing
194
+ }
195
+ )
196
+
197
+ // Pattern to match Form.RichText components with plugins prop
198
+ const formRichTextRegex = /(<Form\.RichText(?:Field)?)([\s\S]*?)(\/>|>)/g
199
+ newContent = newContent.replace(
200
+ formRichTextRegex,
201
+ (match, opening, props, closing) => {
202
+ let updatedProps = props
203
+ const hasPlugins = props.includes('plugins=')
204
+
205
+ if (hasPlugins) {
206
+ // Remove plugins prop
207
+ updatedProps = updatedProps.replace(/\s*plugins\s*=\s*\{[^}]*\}/g, '')
208
+ modified = true
209
+ }
210
+
211
+ return opening + updatedProps + closing
212
+ }
213
+ )
214
+
215
+ return { content: newContent, modified }
216
+ }
217
+
171
218
  /**
172
219
  * Transform Form.RichText usage
173
220
  */
@@ -692,6 +739,13 @@ function processFile(filePath) {
692
739
  newContent = formResult.content
693
740
  wasModified = true
694
741
  }
742
+
743
+ // Remove deprecated plugins prop
744
+ const deprecatedPropsResult = removeDeprecatedProps(newContent)
745
+ if (deprecatedPropsResult.modified) {
746
+ newContent = deprecatedPropsResult.content
747
+ wasModified = true
748
+ }
695
749
  }
696
750
 
697
751
  // Write back if modified
@@ -129,6 +129,15 @@ describe('text-editor-migrate codemod', () => {
129
129
  expect(content).not.toContain('textEditorComponent={TextEditor}')
130
130
  expect(content).not.toContain('@procore/text-editor')
131
131
  })
132
+
133
+ it('should remove deprecated plugins prop from Form.RichText', () => {
134
+ execFileSync('node', [codemodPath, testDir], { stdio: 'ignore' })
135
+
136
+ const content = readFile('src/components/MultilineFormRichText.tsx')
137
+
138
+ expect(content).not.toContain('plugins=')
139
+ expect(content).toContain('textEditorComponent={TextEditor}')
140
+ })
132
141
  })
133
142
 
134
143
  describe('Jest configuration transformations', () => {
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import type { TextEditorProps } from '../TextEditor';
3
+ /**
4
+ * A performance-optimized version of `TextEditor` with built-in debounce on the `onChange` callback.
5
+ *
6
+ * ## When to use `DebouncedTextEditor`
7
+ *
8
+ * Use this component when:
9
+ * - The `onChange` handler triggers expensive operations (API calls, complex state updates, re-renders)
10
+ * - You want to reduce the frequency of updates while the user is actively typing
11
+ * - Performance is a concern in forms with multiple rich text editors
12
+ *
13
+ * ## How it works
14
+ *
15
+ * The component waits 300ms after the user stops typing before calling `onChange`.
16
+ * If the user continues typing, the timer resets. This reduces unnecessary updates
17
+ * while maintaining a responsive editing experience.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * import { DebouncedTextEditor } from '@procore/text-editor'
22
+ *
23
+ * function MyComponent() {
24
+ * const [value, setValue] = React.useState('')
25
+ *
26
+ * return (
27
+ * <DebouncedTextEditor
28
+ * value={value}
29
+ * onChange={(newValue) => setValue(newValue)}
30
+ * />
31
+ * )
32
+ * }
33
+ * ```
34
+ */
35
+ export declare const DebouncedTextEditor: ({ onChange, value, ...rest }: TextEditorProps) => React.JSX.Element;
@@ -0,0 +1,71 @@
1
+ var _excluded = ["onChange", "value"];
2
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
3
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
4
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
5
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
7
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
8
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
9
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
10
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
11
+ import React, { useCallback, useEffect, useState } from 'react';
12
+ import { TextEditor } from '../TextEditor';
13
+ import { debounce } from '../utils/debounce';
14
+ var DEBOUNCE_MS = 300;
15
+
16
+ /**
17
+ * A performance-optimized version of `TextEditor` with built-in debounce on the `onChange` callback.
18
+ *
19
+ * ## When to use `DebouncedTextEditor`
20
+ *
21
+ * Use this component when:
22
+ * - The `onChange` handler triggers expensive operations (API calls, complex state updates, re-renders)
23
+ * - You want to reduce the frequency of updates while the user is actively typing
24
+ * - Performance is a concern in forms with multiple rich text editors
25
+ *
26
+ * ## How it works
27
+ *
28
+ * The component waits 300ms after the user stops typing before calling `onChange`.
29
+ * If the user continues typing, the timer resets. This reduces unnecessary updates
30
+ * while maintaining a responsive editing experience.
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * import { DebouncedTextEditor } from '@procore/text-editor'
35
+ *
36
+ * function MyComponent() {
37
+ * const [value, setValue] = React.useState('')
38
+ *
39
+ * return (
40
+ * <DebouncedTextEditor
41
+ * value={value}
42
+ * onChange={(newValue) => setValue(newValue)}
43
+ * />
44
+ * )
45
+ * }
46
+ * ```
47
+ */
48
+ export var DebouncedTextEditor = function DebouncedTextEditor(_ref) {
49
+ var onChange = _ref.onChange,
50
+ value = _ref.value,
51
+ rest = _objectWithoutProperties(_ref, _excluded);
52
+ var _useState = useState(value !== null && value !== void 0 ? value : ''),
53
+ _useState2 = _slicedToArray(_useState, 2),
54
+ internalValue = _useState2[0],
55
+ setInternalValue = _useState2[1];
56
+ var debouncedOnChange = useCallback(debounce(function (newValue) {
57
+ onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
58
+ }, DEBOUNCE_MS), []);
59
+ function handleChange(newValue) {
60
+ setInternalValue(newValue);
61
+ debouncedOnChange(newValue);
62
+ }
63
+ useEffect(function () {
64
+ setInternalValue(value !== null && value !== void 0 ? value : '');
65
+ }, [value]);
66
+ return /*#__PURE__*/React.createElement(TextEditor, _extends({}, rest, {
67
+ value: internalValue,
68
+ onChange: handleChange
69
+ }));
70
+ };
71
+ //# sourceMappingURL=DebouncedTextEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DebouncedTextEditor.js","names":["React","useCallback","useEffect","useState","TextEditor","debounce","DEBOUNCE_MS","DebouncedTextEditor","_ref","onChange","value","rest","_objectWithoutProperties","_excluded","_useState","_useState2","_slicedToArray","internalValue","setInternalValue","debouncedOnChange","newValue","handleChange","createElement","_extends"],"sources":["../../src/DebouncedTextEditor/DebouncedTextEditor.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useState } from 'react'\nimport type { TextEditorProps } from '../TextEditor'\nimport { TextEditor } from '../TextEditor'\nimport { debounce } from '../utils/debounce'\n\nconst DEBOUNCE_MS = 300\n\n/**\n * A performance-optimized version of `TextEditor` with built-in debounce on the `onChange` callback.\n *\n * ## When to use `DebouncedTextEditor`\n *\n * Use this component when:\n * - The `onChange` handler triggers expensive operations (API calls, complex state updates, re-renders)\n * - You want to reduce the frequency of updates while the user is actively typing\n * - Performance is a concern in forms with multiple rich text editors\n *\n * ## How it works\n *\n * The component waits 300ms after the user stops typing before calling `onChange`.\n * If the user continues typing, the timer resets. This reduces unnecessary updates\n * while maintaining a responsive editing experience.\n *\n * @example\n * ```tsx\n * import { DebouncedTextEditor } from '@procore/text-editor'\n *\n * function MyComponent() {\n * const [value, setValue] = React.useState('')\n *\n * return (\n * <DebouncedTextEditor\n * value={value}\n * onChange={(newValue) => setValue(newValue)}\n * />\n * )\n * }\n * ```\n */\nexport const DebouncedTextEditor = ({\n onChange,\n value,\n ...rest\n}: TextEditorProps) => {\n const [internalValue, setInternalValue] = useState(value ?? '')\n\n const debouncedOnChange = useCallback(\n debounce((newValue: string) => {\n onChange?.(newValue)\n }, DEBOUNCE_MS),\n []\n )\n\n function handleChange(newValue: string) {\n setInternalValue(newValue)\n debouncedOnChange(newValue)\n }\n\n useEffect(() => {\n setInternalValue(value ?? '')\n }, [value])\n\n return <TextEditor {...rest} value={internalValue} onChange={handleChange} />\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAOA,KAAK,IAAIC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AAE/D,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,QAAQ,QAAQ,mBAAmB;AAE5C,IAAMC,WAAW,GAAG,GAAG;;AAEvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,IAAMC,mBAAmB,GAAG,SAAtBA,mBAAmBA,CAAAC,IAAA,EAIT;EAAA,IAHrBC,QAAQ,GAAAD,IAAA,CAARC,QAAQ;IACRC,KAAK,GAAAF,IAAA,CAALE,KAAK;IACFC,IAAI,GAAAC,wBAAA,CAAAJ,IAAA,EAAAK,SAAA;EAEP,IAAAC,SAAA,GAA0CX,QAAQ,CAACO,KAAK,aAALA,KAAK,cAALA,KAAK,GAAI,EAAE,CAAC;IAAAK,UAAA,GAAAC,cAAA,CAAAF,SAAA;IAAxDG,aAAa,GAAAF,UAAA;IAAEG,gBAAgB,GAAAH,UAAA;EAEtC,IAAMI,iBAAiB,GAAGlB,WAAW,CACnCI,QAAQ,CAAC,UAACe,QAAgB,EAAK;IAC7BX,QAAQ,aAARA,QAAQ,uBAARA,QAAQ,CAAGW,QAAQ,CAAC;EACtB,CAAC,EAAEd,WAAW,CAAC,EACf,EACF,CAAC;EAED,SAASe,YAAYA,CAACD,QAAgB,EAAE;IACtCF,gBAAgB,CAACE,QAAQ,CAAC;IAC1BD,iBAAiB,CAACC,QAAQ,CAAC;EAC7B;EAEAlB,SAAS,CAAC,YAAM;IACdgB,gBAAgB,CAACR,KAAK,aAALA,KAAK,cAALA,KAAK,GAAI,EAAE,CAAC;EAC/B,CAAC,EAAE,CAACA,KAAK,CAAC,CAAC;EAEX,oBAAOV,KAAA,CAAAsB,aAAA,CAAClB,UAAU,EAAAmB,QAAA,KAAKZ,IAAI;IAAED,KAAK,EAAEO,aAAc;IAACR,QAAQ,EAAEY;EAAa,EAAE,CAAC;AAC/E,CAAC"}
@@ -0,0 +1 @@
1
+ export { DebouncedTextEditor } from './DebouncedTextEditor';
@@ -0,0 +1,2 @@
1
+ export { DebouncedTextEditor } from './DebouncedTextEditor';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["DebouncedTextEditor"],"sources":["../../src/DebouncedTextEditor/index.ts"],"sourcesContent":["export { DebouncedTextEditor } from './DebouncedTextEditor'\n"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,uBAAuB"}
@@ -41,4 +41,4 @@ import type { TextEditorProps } from './TextEditor.types';
41
41
  * }),
42
42
  * })
43
43
  */
44
- export declare function TextEditor({ disabled, error, value, initialValue, onChange, onInit, onBlur, onKeyDown, config: externalConfig, plugins: stringPlugins, locale: propLocale, onDirty, ...restProps }: TextEditorProps): React.JSX.Element | null;
44
+ export declare function TextEditor(props: Readonly<TextEditorProps>): React.JSX.Element | null;
@@ -1,11 +1,5 @@
1
- function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
- var _excluded = ["disabled", "error", "value", "initialValue", "onChange", "onInit", "onBlur", "onKeyDown", "config", "plugins", "locale", "onDirty"];
1
+ var _excluded = ["disabled", "error", "readonly", "value", "initialValue", "onChange", "onInit", "onBlur", "onKeyDown", "locale", "onDirty"];
3
2
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
4
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
5
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
7
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
8
- function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
9
3
  function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
10
4
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
11
5
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
@@ -18,13 +12,14 @@ import { CKEditor } from '@ckeditor/ckeditor5-react';
18
12
  import { ClassicEditor } from 'ckeditor5';
19
13
  import React from 'react';
20
14
  import { useI18nContext, useZIndexContext } from '@procore/core-react';
15
+ import { TextEditorOutput } from '../TextEditorOutput';
21
16
  import { useStickyToolbar } from './StickyToolbar';
22
17
  import { GlobalEditorStyles, StyledTextEditor } from './TextEditor.styles';
23
18
  import { TextEditorContext } from './TextEditorProvider';
24
19
  import { TextEditorTheme } from './textEditorTheming';
25
20
  import { useCKEditorCss } from './useCKEditorCss';
26
21
  import { useTabAsNavigation } from './useTabAsNavigation';
27
- import { addButtonDataAttributes, addPluginsFromStringArray, getDefaultConfig } from './utils';
22
+ import { addButtonDataAttributes, getDefaultConfig } from './utils';
28
23
 
29
24
  /**
30
25
  * To ensure your Jest tests work with the CKEditor implementation, wrap your Jest configuration with `textEditorJestConfig`:
@@ -67,20 +62,21 @@ import { addButtonDataAttributes, addPluginsFromStringArray, getDefaultConfig }
67
62
  * }),
68
63
  * })
69
64
  */
70
- export function TextEditor(_ref) {
71
- var disabled = _ref.disabled,
72
- error = _ref.error,
73
- value = _ref.value,
74
- initialValue = _ref.initialValue,
75
- onChange = _ref.onChange,
76
- onInit = _ref.onInit,
77
- onBlur = _ref.onBlur,
78
- onKeyDown = _ref.onKeyDown,
79
- externalConfig = _ref.config,
80
- stringPlugins = _ref.plugins,
81
- propLocale = _ref.locale,
82
- onDirty = _ref.onDirty,
83
- restProps = _objectWithoutProperties(_ref, _excluded);
65
+ export function TextEditor(props) {
66
+ var disabled = props.disabled,
67
+ error = props.error,
68
+ readonly = props.readonly,
69
+ value = props.value,
70
+ initialValue = props.initialValue,
71
+ onChange = props.onChange,
72
+ onInit = props.onInit,
73
+ onBlur = props.onBlur,
74
+ onKeyDown = props.onKeyDown,
75
+ propLocale = props.locale,
76
+ onDirty = props.onDirty,
77
+ restProps = _objectWithoutProperties(props, _excluded);
78
+ var ariaLabel = props['aria-label'];
79
+ var ariaDescription = props['aria-description'];
84
80
  var _useCKEditorCss = useCKEditorCss(),
85
81
  cssLoading = _useCKEditorCss.isLoading;
86
82
  var editorRef = React.useRef(null);
@@ -113,6 +109,42 @@ export function TextEditor(_ref) {
113
109
  }
114
110
  };
115
111
  }, []);
112
+
113
+ // Update aria-label when ariaLabel prop changes
114
+ React.useEffect(function () {
115
+ if (!isEditorReady || !editorInstanceRef.current) {
116
+ return;
117
+ }
118
+ var editor = editorInstanceRef.current;
119
+ editor.editing.view.change(function (writer) {
120
+ var viewEditableRoot = editor.editing.view.document.getRoot();
121
+ if (viewEditableRoot) {
122
+ if (ariaLabel) {
123
+ writer.setAttribute('aria-label', ariaLabel, viewEditableRoot);
124
+ } else {
125
+ writer.removeAttribute('aria-label', viewEditableRoot);
126
+ }
127
+ }
128
+ });
129
+ }, [ariaLabel, isEditorReady]);
130
+
131
+ // Update aria-description when ariaDescription prop changes
132
+ React.useEffect(function () {
133
+ if (!isEditorReady || !editorInstanceRef.current) {
134
+ return;
135
+ }
136
+ var editor = editorInstanceRef.current;
137
+ editor.editing.view.change(function (writer) {
138
+ var viewEditableRoot = editor.editing.view.document.getRoot();
139
+ if (viewEditableRoot) {
140
+ if (ariaDescription) {
141
+ writer.setAttribute('aria-description', ariaDescription, viewEditableRoot);
142
+ } else {
143
+ writer.removeAttribute('aria-description', viewEditableRoot);
144
+ }
145
+ }
146
+ });
147
+ }, [ariaDescription, isEditorReady]);
116
148
  var _useZIndexContext = useZIndexContext(),
117
149
  zIndex = _useZIndexContext.value;
118
150
  var _React$useContext = React.useContext(TextEditorContext),
@@ -122,19 +154,14 @@ export function TextEditor(_ref) {
122
154
  var mergedConfig = React.useMemo(function () {
123
155
  var defaultConfig = getDefaultConfig(locale);
124
156
 
125
- // Handle string plugins for backward compatibility
126
- var configWithPlugins = stringPlugins !== null && stringPlugins !== void 0 && stringPlugins.length ? addPluginsFromStringArray(defaultConfig, stringPlugins) : defaultConfig;
127
-
128
- // Apply any external CKEditor config overrides provided by the consumer
129
- var externalResult = typeof externalConfig === 'function' ? externalConfig(configWithPlugins) : undefined;
130
- // Merge default config (with plugins) and consumer overrides
131
- var finalConfig = _objectSpread(_objectSpread({}, configWithPlugins), externalResult !== null && externalResult !== void 0 ? externalResult : {});
132
- return finalConfig;
133
- }, [externalConfig, locale, stringPlugins]);
134
- var bindKeyDownHandler = React.useCallback(function (editor) {
135
- if (!onKeyDown) {
136
- return;
157
+ // Set accessible label for the editable area if provided
158
+ // This is used by screen readers to announce the editor's purpose
159
+ if (ariaLabel) {
160
+ defaultConfig.label = ariaLabel;
137
161
  }
162
+ return defaultConfig;
163
+ }, [ariaLabel, locale]);
164
+ var bindKeyDownHandler = React.useCallback(function (editor) {
138
165
  var editorElement = editor.editing.view.document.getRoot();
139
166
  if (!editorElement) {
140
167
  return;
@@ -148,7 +175,12 @@ export function TextEditor(_ref) {
148
175
  // Create and store the new listener
149
176
  var keyDownListener = function keyDownListener(_event, data) {
150
177
  var domEvent = data.domEvent;
151
- onKeyDown(domEvent, editor);
178
+ // Prevent Enter from propagating to parent forms so the
179
+ // editor can handle newlines without accidentally submitting.
180
+ if (domEvent.key === 'Enter') {
181
+ domEvent.stopPropagation();
182
+ }
183
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(domEvent, editor);
152
184
  };
153
185
  keyDownListenerRef.current = keyDownListener;
154
186
 
@@ -174,6 +206,20 @@ export function TextEditor(_ref) {
174
206
  editor.setData(initialValue);
175
207
  }
176
208
 
209
+ // Set aria-label and aria-description on the editable area if provided
210
+ // These provide accessible context to screen reader users
211
+ editor.editing.view.change(function (writer) {
212
+ var viewEditableRoot = editor.editing.view.document.getRoot();
213
+ if (viewEditableRoot) {
214
+ if (ariaLabel) {
215
+ writer.setAttribute('aria-label', ariaLabel, viewEditableRoot);
216
+ }
217
+ if (ariaDescription) {
218
+ writer.setAttribute('aria-description', ariaDescription, viewEditableRoot);
219
+ }
220
+ }
221
+ });
222
+
177
223
  // Bind keydown handler when editor is ready
178
224
  bindKeyDownHandler(editor);
179
225
  onInit === null || onInit === void 0 ? void 0 : onInit(editor);
@@ -197,10 +243,18 @@ export function TextEditor(_ref) {
197
243
  if (cssLoading) {
198
244
  return null;
199
245
  }
246
+ if (readonly) {
247
+ return /*#__PURE__*/React.createElement(TextEditorOutput, {
248
+ value: value,
249
+ "aria-label": ariaLabel,
250
+ "aria-description": ariaDescription
251
+ });
252
+ }
200
253
  return /*#__PURE__*/React.createElement(StyledTextEditor, {
201
254
  ref: editorRef,
202
255
  key: editorKey,
203
- error: error
256
+ error: error,
257
+ "aria-invalid": error ? 'true' : 'false'
204
258
  }, /*#__PURE__*/React.createElement(GlobalEditorStyles, {
205
259
  zIndex: zIndex + 1
206
260
  }), /*#__PURE__*/React.createElement(TextEditorTheme, null), /*#__PURE__*/React.createElement(CKEditor, _extends({}, restProps, {
@@ -1 +1 @@
1
- {"version":3,"file":"TextEditor.js","names":["CKEditor","ClassicEditor","React","useI18nContext","useZIndexContext","useStickyToolbar","GlobalEditorStyles","StyledTextEditor","TextEditorContext","TextEditorTheme","useCKEditorCss","useTabAsNavigation","addButtonDataAttributes","addPluginsFromStringArray","getDefaultConfig","TextEditor","_ref","disabled","error","value","initialValue","onChange","onInit","onBlur","onKeyDown","externalConfig","config","stringPlugins","plugins","propLocale","locale","onDirty","restProps","_objectWithoutProperties","_excluded","_useCKEditorCss","cssLoading","isLoading","editorRef","useRef","editorInstanceRef","initialValueRef","keyDownListenerRef","_React$useState","useState","_React$useState2","_slicedToArray","isDirtyState","setIsDirtyState","_React$useState3","_React$useState4","isEditorReady","setIsEditorReady","_useI18nContext","contextLocale","useEffect","undefined","current","editing","view","document","off","_useZIndexContext","zIndex","_React$useContext","useContext","features","enableStickyToolbar","stickyToolbar","enableTabAsNavigation","tabAsNavigation","mergedConfig","useMemo","defaultConfig","configWithPlugins","length","externalResult","finalConfig","_objectSpread","bindKeyDownHandler","useCallback","editor","editorElement","getRoot","keyDownListener","_event","data","domEvent","on","handleChange","getData","isDirty","handleReady","setData","handleBlur","event","enabled","_useTabAsNavigation","editorKey","concat","createElement","ref","key","_extends","onReady"],"sources":["../../src/TextEditor/TextEditor.tsx"],"sourcesContent":["import { CKEditor } from '@ckeditor/ckeditor5-react'\nimport type { EditorConfig } from 'ckeditor5'\nimport { ClassicEditor, type EventInfo } from 'ckeditor5'\nimport React from 'react'\n\nimport { useI18nContext, useZIndexContext } from '@procore/core-react'\nimport { useStickyToolbar } from './StickyToolbar'\nimport { GlobalEditorStyles, StyledTextEditor } from './TextEditor.styles'\nimport type { KeyDownListener, TextEditorProps } from './TextEditor.types'\nimport { TextEditorContext } from './TextEditorProvider'\nimport { TextEditorTheme } from './textEditorTheming'\nimport { useCKEditorCss } from './useCKEditorCss'\nimport { useTabAsNavigation } from './useTabAsNavigation'\nimport {\n addButtonDataAttributes,\n addPluginsFromStringArray,\n getDefaultConfig,\n} from './utils'\n\n/**\n * To ensure your Jest tests work with the CKEditor implementation, wrap your Jest configuration with `textEditorJestConfig`:\n *\n * @example\n *\n * // Using Jest config file\n *\n * const { textEditorJestConfig } = require('@procore/text-editor/jestConfig')\n *\n * module.exports = textEditorJestConfig({\n * // Your existing Jest config\n * })\n *\n * @example\n *\n * // Using Hammer\n *\n * import { textEditorJestConfig } from '@procore/text-editor/jestConfig'\n *\n * export default {\n * testJest: (defaultConfig) =>\n * textEditorJestConfig({\n * ...defaultConfig,\n * // Your existing Jest config\n * }),\n * }\n *\n * @example\n *\n * // Using Core Scripts\n *\n * const { textEditorJestConfig } = require('@procore/text-editor/jestConfig')\n *\n * module.exports = () => ({\n * jestOverride: (defaultConfig) =>\n * textEditorJestConfig({\n * ...defaultConfig,\n * // Your existing Jest config\n * }),\n * })\n */\nexport function TextEditor({\n disabled,\n error,\n value,\n initialValue,\n onChange,\n onInit,\n onBlur,\n onKeyDown,\n config: externalConfig,\n plugins: stringPlugins,\n locale: propLocale,\n onDirty,\n ...restProps\n}: TextEditorProps) {\n const { isLoading: cssLoading } = useCKEditorCss()\n\n const editorRef = React.useRef<HTMLDivElement>(null)\n const editorInstanceRef = React.useRef<ClassicEditor | null>(null)\n const initialValueRef = React.useRef<string>(value || initialValue || '')\n const keyDownListenerRef = React.useRef<KeyDownListener | null>(null)\n const [isDirtyState, setIsDirtyState] = React.useState(false)\n const [isEditorReady, setIsEditorReady] = React.useState(false)\n const { locale: contextLocale } = useI18nContext()\n const locale = propLocale || contextLocale\n\n React.useEffect(() => {\n if (value !== undefined) {\n initialValueRef.current = value\n }\n }, [value])\n\n // Cleanup keydown listener on unmount\n React.useEffect(() => {\n return () => {\n if (keyDownListenerRef.current && editorInstanceRef.current) {\n editorInstanceRef.current.editing.view.document.off(\n 'keydown',\n keyDownListenerRef.current\n )\n keyDownListenerRef.current = null\n }\n }\n }, [])\n\n const { value: zIndex } = useZIndexContext()\n\n const { features } = React.useContext(TextEditorContext)\n const enableStickyToolbar = !!features?.stickyToolbar\n const enableTabAsNavigation = !!features?.tabAsNavigation\n\n const mergedConfig = React.useMemo(() => {\n const defaultConfig = getDefaultConfig(locale)\n\n // Handle string plugins for backward compatibility\n const configWithPlugins = stringPlugins?.length\n ? addPluginsFromStringArray(defaultConfig, stringPlugins)\n : defaultConfig\n\n // Apply any external CKEditor config overrides provided by the consumer\n const externalResult =\n typeof externalConfig === 'function'\n ? externalConfig(configWithPlugins)\n : undefined\n // Merge default config (with plugins) and consumer overrides\n let finalConfig: EditorConfig = {\n ...configWithPlugins,\n ...(externalResult ?? {}),\n }\n\n return finalConfig\n }, [externalConfig, locale, stringPlugins])\n\n const bindKeyDownHandler = React.useCallback(\n (editor: ClassicEditor) => {\n if (!onKeyDown) {\n return\n }\n\n const editorElement = editor.editing.view.document.getRoot()\n if (!editorElement) {\n return\n }\n\n // Remove existing keydown listener\n if (keyDownListenerRef.current) {\n editor.editing.view.document.off('keydown', keyDownListenerRef.current)\n }\n\n // Create and store the new listener\n const keyDownListener = (\n _event: EventInfo,\n data: { domEvent: Event }\n ) => {\n const domEvent = data.domEvent as KeyboardEvent\n onKeyDown(domEvent, editor)\n }\n\n keyDownListenerRef.current = keyDownListener\n\n // Add the keydown listener\n editor.editing.view.document.on('keydown', keyDownListener)\n },\n [onKeyDown]\n )\n\n const handleChange = (\n _event: EventInfo<string, unknown>,\n editor: ClassicEditor\n ) => {\n const data = editor.getData()\n const isDirty = data !== initialValueRef.current\n\n // Call onDirty only on first content modification\n if (isDirty && !isDirtyState) {\n setIsDirtyState(true)\n onDirty?.()\n }\n\n onChange?.(data, isDirty)\n }\n\n const handleReady = (editor: ClassicEditor) => {\n addButtonDataAttributes(editor)\n editorInstanceRef.current = editor\n setIsEditorReady(true)\n\n if (initialValue) {\n editor.setData(initialValue)\n }\n\n // Bind keydown handler when editor is ready\n bindKeyDownHandler(editor)\n\n onInit?.(editor)\n }\n\n const handleBlur = (\n event: EventInfo<string, unknown>,\n editor: ClassicEditor\n ) => {\n onBlur?.(event, editor)\n }\n\n useStickyToolbar({\n enabled: enableStickyToolbar,\n editor: isEditorReady ? editorInstanceRef.current : null,\n editorRef,\n cssLoading,\n })\n\n const { config } = useTabAsNavigation({\n config: mergedConfig,\n enabled: enableTabAsNavigation,\n editor: isEditorReady ? editorInstanceRef.current : null,\n })\n\n const editorKey = `${locale}-${enableTabAsNavigation}`\n\n if (cssLoading) {\n return null\n }\n\n return (\n <StyledTextEditor ref={editorRef} key={editorKey} error={error}>\n <GlobalEditorStyles zIndex={zIndex + 1} />\n <TextEditorTheme />\n <CKEditor\n {...restProps}\n editor={ClassicEditor}\n config={config}\n disabled={disabled}\n data={value}\n onChange={handleChange}\n onReady={handleReady}\n onBlur={handleBlur}\n />\n </StyledTextEditor>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TextEditor.js","names":["CKEditor","ClassicEditor","React","useI18nContext","useZIndexContext","TextEditorOutput","useStickyToolbar","GlobalEditorStyles","StyledTextEditor","TextEditorContext","TextEditorTheme","useCKEditorCss","useTabAsNavigation","addButtonDataAttributes","getDefaultConfig","TextEditor","props","disabled","error","readonly","value","initialValue","onChange","onInit","onBlur","onKeyDown","propLocale","locale","onDirty","restProps","_objectWithoutProperties","_excluded","ariaLabel","ariaDescription","_useCKEditorCss","cssLoading","isLoading","editorRef","useRef","editorInstanceRef","initialValueRef","keyDownListenerRef","_React$useState","useState","_React$useState2","_slicedToArray","isDirtyState","setIsDirtyState","_React$useState3","_React$useState4","isEditorReady","setIsEditorReady","_useI18nContext","contextLocale","useEffect","undefined","current","editing","view","document","off","editor","change","writer","viewEditableRoot","getRoot","setAttribute","removeAttribute","_useZIndexContext","zIndex","_React$useContext","useContext","features","enableStickyToolbar","stickyToolbar","enableTabAsNavigation","tabAsNavigation","mergedConfig","useMemo","defaultConfig","label","bindKeyDownHandler","useCallback","editorElement","keyDownListener","_event","data","domEvent","key","stopPropagation","on","handleChange","getData","isDirty","handleReady","setData","handleBlur","event","enabled","_useTabAsNavigation","config","editorKey","concat","createElement","ref","_extends","onReady"],"sources":["../../src/TextEditor/TextEditor.tsx"],"sourcesContent":["import { CKEditor } from '@ckeditor/ckeditor5-react'\nimport { ClassicEditor, type EventInfo } from 'ckeditor5'\nimport React from 'react'\n\nimport { useI18nContext, useZIndexContext } from '@procore/core-react'\nimport { TextEditorOutput } from '../TextEditorOutput'\nimport { useStickyToolbar } from './StickyToolbar'\nimport { GlobalEditorStyles, StyledTextEditor } from './TextEditor.styles'\nimport type { KeyDownListener, TextEditorProps } from './TextEditor.types'\nimport { TextEditorContext } from './TextEditorProvider'\nimport { TextEditorTheme } from './textEditorTheming'\nimport { useCKEditorCss } from './useCKEditorCss'\nimport { useTabAsNavigation } from './useTabAsNavigation'\nimport { addButtonDataAttributes, getDefaultConfig } from './utils'\n\n/**\n * To ensure your Jest tests work with the CKEditor implementation, wrap your Jest configuration with `textEditorJestConfig`:\n *\n * @example\n *\n * // Using Jest config file\n *\n * const { textEditorJestConfig } = require('@procore/text-editor/jestConfig')\n *\n * module.exports = textEditorJestConfig({\n * // Your existing Jest config\n * })\n *\n * @example\n *\n * // Using Hammer\n *\n * import { textEditorJestConfig } from '@procore/text-editor/jestConfig'\n *\n * export default {\n * testJest: (defaultConfig) =>\n * textEditorJestConfig({\n * ...defaultConfig,\n * // Your existing Jest config\n * }),\n * }\n *\n * @example\n *\n * // Using Core Scripts\n *\n * const { textEditorJestConfig } = require('@procore/text-editor/jestConfig')\n *\n * module.exports = () => ({\n * jestOverride: (defaultConfig) =>\n * textEditorJestConfig({\n * ...defaultConfig,\n * // Your existing Jest config\n * }),\n * })\n */\nexport function TextEditor(props: Readonly<TextEditorProps>) {\n const {\n disabled,\n error,\n readonly,\n value,\n initialValue,\n onChange,\n onInit,\n onBlur,\n onKeyDown,\n locale: propLocale,\n onDirty,\n ...restProps\n } = props\n const ariaLabel = props['aria-label']\n const ariaDescription = props['aria-description']\n const { isLoading: cssLoading } = useCKEditorCss()\n\n const editorRef = React.useRef<HTMLDivElement>(null)\n const editorInstanceRef = React.useRef<ClassicEditor | null>(null)\n const initialValueRef = React.useRef<string>(value || initialValue || '')\n const keyDownListenerRef = React.useRef<KeyDownListener | null>(null)\n const [isDirtyState, setIsDirtyState] = React.useState(false)\n const [isEditorReady, setIsEditorReady] = React.useState(false)\n const { locale: contextLocale } = useI18nContext()\n const locale = propLocale || contextLocale\n\n React.useEffect(() => {\n if (value !== undefined) {\n initialValueRef.current = value\n }\n }, [value])\n\n // Cleanup keydown listener on unmount\n React.useEffect(() => {\n return () => {\n if (keyDownListenerRef.current && editorInstanceRef.current) {\n editorInstanceRef.current.editing.view.document.off(\n 'keydown',\n keyDownListenerRef.current\n )\n keyDownListenerRef.current = null\n }\n }\n }, [])\n\n // Update aria-label when ariaLabel prop changes\n React.useEffect(() => {\n if (!isEditorReady || !editorInstanceRef.current) {\n return\n }\n\n const editor = editorInstanceRef.current\n editor.editing.view.change((writer) => {\n const viewEditableRoot = editor.editing.view.document.getRoot()\n if (viewEditableRoot) {\n if (ariaLabel) {\n writer.setAttribute('aria-label', ariaLabel, viewEditableRoot)\n } else {\n writer.removeAttribute('aria-label', viewEditableRoot)\n }\n }\n })\n }, [ariaLabel, isEditorReady])\n\n // Update aria-description when ariaDescription prop changes\n React.useEffect(() => {\n if (!isEditorReady || !editorInstanceRef.current) {\n return\n }\n\n const editor = editorInstanceRef.current\n editor.editing.view.change((writer) => {\n const viewEditableRoot = editor.editing.view.document.getRoot()\n if (viewEditableRoot) {\n if (ariaDescription) {\n writer.setAttribute(\n 'aria-description',\n ariaDescription,\n viewEditableRoot\n )\n } else {\n writer.removeAttribute('aria-description', viewEditableRoot)\n }\n }\n })\n }, [ariaDescription, isEditorReady])\n\n const { value: zIndex } = useZIndexContext()\n\n const { features } = React.useContext(TextEditorContext)\n const enableStickyToolbar = !!features?.stickyToolbar\n const enableTabAsNavigation = !!features?.tabAsNavigation\n\n const mergedConfig = React.useMemo(() => {\n const defaultConfig = getDefaultConfig(locale)\n\n // Set accessible label for the editable area if provided\n // This is used by screen readers to announce the editor's purpose\n if (ariaLabel) {\n defaultConfig.label = ariaLabel\n }\n\n return defaultConfig\n }, [ariaLabel, locale])\n\n const bindKeyDownHandler = React.useCallback(\n (editor: ClassicEditor) => {\n const editorElement = editor.editing.view.document.getRoot()\n if (!editorElement) {\n return\n }\n\n // Remove existing keydown listener\n if (keyDownListenerRef.current) {\n editor.editing.view.document.off('keydown', keyDownListenerRef.current)\n }\n\n // Create and store the new listener\n const keyDownListener = (\n _event: EventInfo,\n data: { domEvent: Event }\n ) => {\n const domEvent = data.domEvent as KeyboardEvent\n // Prevent Enter from propagating to parent forms so the\n // editor can handle newlines without accidentally submitting.\n if (domEvent.key === 'Enter') {\n domEvent.stopPropagation()\n }\n\n onKeyDown?.(domEvent, editor)\n }\n\n keyDownListenerRef.current = keyDownListener\n\n // Add the keydown listener\n editor.editing.view.document.on('keydown', keyDownListener)\n },\n [onKeyDown]\n )\n\n const handleChange = (\n _event: EventInfo<string, unknown>,\n editor: ClassicEditor\n ) => {\n const data = editor.getData()\n const isDirty = data !== initialValueRef.current\n\n // Call onDirty only on first content modification\n if (isDirty && !isDirtyState) {\n setIsDirtyState(true)\n onDirty?.()\n }\n\n onChange?.(data, isDirty)\n }\n\n const handleReady = (editor: ClassicEditor) => {\n addButtonDataAttributes(editor)\n editorInstanceRef.current = editor\n setIsEditorReady(true)\n\n if (initialValue) {\n editor.setData(initialValue)\n }\n\n // Set aria-label and aria-description on the editable area if provided\n // These provide accessible context to screen reader users\n editor.editing.view.change((writer) => {\n const viewEditableRoot = editor.editing.view.document.getRoot()\n if (viewEditableRoot) {\n if (ariaLabel) {\n writer.setAttribute('aria-label', ariaLabel, viewEditableRoot)\n }\n if (ariaDescription) {\n writer.setAttribute(\n 'aria-description',\n ariaDescription,\n viewEditableRoot\n )\n }\n }\n })\n\n // Bind keydown handler when editor is ready\n bindKeyDownHandler(editor)\n\n onInit?.(editor)\n }\n\n const handleBlur = (\n event: EventInfo<string, unknown>,\n editor: ClassicEditor\n ) => {\n onBlur?.(event, editor)\n }\n\n useStickyToolbar({\n enabled: enableStickyToolbar,\n editor: isEditorReady ? editorInstanceRef.current : null,\n editorRef,\n cssLoading,\n })\n\n const { config } = useTabAsNavigation({\n config: mergedConfig,\n enabled: enableTabAsNavigation,\n editor: isEditorReady ? editorInstanceRef.current : null,\n })\n\n const editorKey = `${locale}-${enableTabAsNavigation}`\n\n if (cssLoading) {\n return null\n }\n\n if (readonly) {\n return (\n <TextEditorOutput\n value={value}\n aria-label={ariaLabel}\n aria-description={ariaDescription}\n />\n )\n }\n\n return (\n <StyledTextEditor\n ref={editorRef}\n key={editorKey}\n error={error}\n aria-invalid={error ? 'true' : 'false'}\n >\n <GlobalEditorStyles zIndex={zIndex + 1} />\n <TextEditorTheme />\n <CKEditor\n {...restProps}\n editor={ClassicEditor}\n config={config}\n disabled={disabled}\n data={value}\n onChange={handleChange}\n onReady={handleReady}\n onBlur={handleBlur}\n />\n </StyledTextEditor>\n )\n}\n"],"mappings":"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"}