diff --git a/docs/configuration.md b/docs/configuration.md
index 6c50d41f3..7f4a88cb4 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -49,6 +49,17 @@ Editors support the following options, configurable using presets and element at
- `markdown`: Pass `false` to disable Markdown support.
- `multiLine`: Pass `false` to force single line editing.
- `richText`: Pass `false` to disable rich text editing.
+- `headings`: Pass an array of heading tags to configure which heading levels are available. The toolbar heading button will cycle through the configured levels. Defaults to `["h2", "h3", "h4"]`. Pass an empty array to disable headings entirely.
+
+ ```js
+ // Via preset
+ Lexxy.configure({
+ default: { headings: ["h1", "h2", "h3"] }
+ })
+
+ // Via element attribute
+
+ ```
The toolbar is considered part of the editor for `lexxy:focus` and `lexxy:blur` events. If the toolbar registers event or lexical handlers, it should expose a `dispose()` function which will be called on editor disconnect.
diff --git a/src/config/lexxy.js b/src/config/lexxy.js
index e26c591c1..82e5dd983 100644
--- a/src/config/lexxy.js
+++ b/src/config/lexxy.js
@@ -17,6 +17,7 @@ const presets = new Configuration({
toolbar: {
upload: "both"
},
+ headings: [ "h2", "h3", "h4" ],
highlight: {
buttons: {
color: range(1, 9).map(n => `var(--highlight-${n})`),
diff --git a/src/elements/toolbar.js b/src/elements/toolbar.js
index d532eac8f..a5e8454e9 100644
--- a/src/elements/toolbar.js
+++ b/src/elements/toolbar.js
@@ -306,10 +306,10 @@ export class LexicalToolbarElement extends HTMLElement {
}
#closeDropdowns() {
- this.#dropdowns.forEach((details) => {
- details.open = false
- })
- }
+ this.#dropdowns.forEach((details) => {
+ details.open = false
+ })
+ }
get #dropdowns() {
return this.querySelectorAll("details")
diff --git a/test/javascript/unit/editor/headings_configuration.test.js b/test/javascript/unit/editor/headings_configuration.test.js
new file mode 100644
index 000000000..a88443efb
--- /dev/null
+++ b/test/javascript/unit/editor/headings_configuration.test.js
@@ -0,0 +1,54 @@
+import { expect, test } from "vitest"
+import { createElement } from "../helpers/dom_helper"
+import EditorConfiguration from "src/editor/configuration"
+import { configure } from "src/index"
+
+configure({
+ default: {
+ headings: ["h2", "h3", "h4"]
+ },
+ minimal: {
+ headings: ["h2"],
+ },
+ noHeadings: {
+ headings: [],
+ },
+})
+
+test("uses default headings", () => {
+ const element = createElement("")
+ const config = new EditorConfiguration(element)
+ expect(config.get("headings")).toEqual(["h2", "h3", "h4"])
+})
+
+test("overrides headings with attribute", () => {
+ const element = createElement(
+ ''
+ )
+ const config = new EditorConfiguration(element)
+ expect(config.get("headings")).toEqual(["h1", "h2", "h3", "h4", "h5", "h6"])
+})
+
+test("overrides headings with attribute to include h1 and h5", () => {
+ const element = createElement(
+ ''
+ )
+ const config = new EditorConfiguration(element)
+ expect(config.get("headings")).toEqual(["h1", "h2", "h5"])
+})
+
+test("restricts headings to a subset", () => {
+ const element = createElement(
+ ""
+ )
+ const config = new EditorConfiguration(element)
+ expect(config.get("headings")).toEqual(["h2"])
+})
+
+test("handles empty headings array", () => {
+ const element = createElement(
+ ""
+ )
+ const config = new EditorConfiguration(element)
+ expect(config.get("headings")).toEqual([])
+})