diff --git a/src/components/__tests__/__snapshots__/checker.js.snap b/src/components/__tests__/__snapshots__/checker.js.snap
index 1d3253c..fa6ee37 100644
--- a/src/components/__tests__/__snapshots__/checker.js.snap
+++ b/src/components/__tests__/__snapshots__/checker.js.snap
@@ -235,7 +235,6 @@ exports[`render matches snapshot with errors 1`] = `
>
{
describe("fixIssue", () => {
let ev, error
- beforeEach(async () => {
- await promisify(instance.check.bind(instance))()
- component.update()
- error = instance.state.errors[0]
- ev = { preventDefault: jest.fn() }
- jest.spyOn(instance, "check")
- })
+ describe("_fixSingleError", () => {
+ beforeEach(async () => {
+ await promisify(instance.check.bind(instance))()
+ component.update()
+ error = instance.state.errors[0]
+ ev = { preventDefault: jest.fn() }
+ jest.spyOn(instance, "check")
+ })
- test("updates the real node", () => {
- instance.fixIssue(ev)
- const formState = instance.state.formState
- expect(error.rule.update).toHaveBeenCalledWith(error.node, formState)
- })
+ test("updates the real node", () => {
+ instance.fixIssue(ev)
+ const formState = instance.state.formState
+ expect(error.rule.update).toHaveBeenCalledWith(error.node, formState)
+ })
- test("checks everything after applying a fix", () => {
- instance.fixIssue(ev)
- expect(instance.check).toHaveBeenCalled()
- })
+ test("checks everything after applying a fix", () => {
+ instance.fixIssue(ev)
+ expect(instance.check).toHaveBeenCalled()
+ })
+
+ test("does nothing if there are no errors", () => {
+ instance.state.errors = []
+ instance.fixIssue(ev)
+ expect(instance.check).not.toHaveBeenCalled()
+ })
- test("does nothing if there are no errors", () => {
- instance.state.errors = []
- instance.fixIssue(ev)
- expect(instance.check).not.toHaveBeenCalled()
+ test("focuses the close button", () => {
+ instance._closeButtonRef = { focus: jest.fn() }
+ instance.fixIssue(ev)
+ expect(instance._closeButtonRef.focus).toHaveBeenCalled()
+ })
})
- test("focuses the close button", () => {
- instance._closeButtonRef = { focus: jest.fn() }
- instance.fixIssue(ev)
- expect(instance._closeButtonRef.focus).toHaveBeenCalled()
+ describe("_fixBulkError", () => {
+ beforeEach(async () => {
+ node.appendChild(document.createElement("div"))
+
+ await promisify(instance.check.bind(instance))()
+ component.update()
+ error = instance.state.errors[0]
+ ev = { preventDefault: jest.fn() }
+ jest.spyOn(instance, "check")
+
+ const newErrors = instance.state.errors
+ newErrors.forEach(err => (err.rule.bulkUpdateSupported = true))
+ component.setState({ errors: newErrors, formStateBulkUpdateEnabled: true })
+ })
+
+ test("updates the real nodes", () => {
+ const [error1, error2, error3] = instance.state.errors
+ instance.fixIssue(ev)
+ const formState = instance.state.formState
+ expect(error1.rule.update).toHaveBeenCalledWith(error1.node, formState)
+ expect(error2.rule.update).toHaveBeenCalledWith(error2.node, formState)
+ expect(error3.rule.update).toHaveBeenCalledWith(error3.node, formState)
+ })
+
+ test("does not update the other error node", async () => {
+ const newErrors = instance.state.errors
+ newErrors[2].rule = {...newErrors[0].rule, id: 'another-error', update: jest.fn()}
+ component.setState({ errors: newErrors, formStateBulkUpdateEnabled: true })
+ const [error1, error2, error3] = instance.state.errors
+ instance.fixIssue(ev)
+ const formState = instance.state.formState
+ expect(error1.rule.update).toHaveBeenCalledWith(error1.node, formState)
+ expect(error2.rule.update).toHaveBeenCalledWith(error2.node, formState)
+ expect(error3.rule.update).not.toHaveBeenCalled()
+ })
+
+ test("checks everything after applying a fix", () => {
+ instance.fixIssue(ev)
+ expect(instance.check).toHaveBeenCalled()
+ })
+
+ test("does nothing if there are no errors", () => {
+ instance.state.errors = []
+ instance.fixIssue(ev)
+ expect(instance.check).not.toHaveBeenCalled()
+ })
+
+ test("focuses the close button", () => {
+ instance._closeButtonRef = { focus: jest.fn() }
+ instance.fixIssue(ev)
+ expect(instance._closeButtonRef.focus).toHaveBeenCalled()
+ })
})
})
diff --git a/src/components/checker.js b/src/components/checker.js
index 5152857..805a166 100644
--- a/src/components/checker.js
+++ b/src/components/checker.js
@@ -42,6 +42,7 @@ export default class Checker extends React.Component {
errors: [],
formState: {},
formStateValid: false,
+ formStateBulkUpdateEnabled: false,
errorIndex: 0,
config: {},
showWhyPopover: false,
@@ -206,6 +207,7 @@ export default class Checker extends React.Component {
this.setState({
formState,
formStateValid: this.formStateValid(formState),
+ formStateBulkUpdateEnabled: false,
})
}
@@ -224,7 +226,7 @@ export default class Checker extends React.Component {
return rule.test(node)
}
- fixIssue() {
+ _fixNode(doneCallback) {
const rule = this.errorRule()
let node = this.errorNode()
if (rule && node) {
@@ -234,8 +236,44 @@ export default class Checker extends React.Component {
if (this._closeButtonRef) {
this._closeButtonRef.focus()
}
- const errorIndex = this.state.errorIndex
- this.check(() => this.setErrorIndex(errorIndex))
+ this.check(doneCallback)
+ }
+ }
+
+ _findMatchErrorIndexes(ruleId) {
+ return this.state.errors.reduce((result, {rule}, index) => {
+ if (rule.id === ruleId) {
+ result.push(index)
+ }
+ return result
+ }, [])
+ }
+
+ _fixSingleError() {
+ const errorIndex = this.state.errorIndex
+ this._fixNode(() => this.setErrorIndex(errorIndex))
+ }
+
+ _fixBulkError() {
+ const fixMatchedErrors = (ruleId) => {
+ const indexes = this._findMatchErrorIndexes(ruleId)
+ if (indexes.length === 0) {
+ return
+ }
+ const errorIndex = indexes[0]
+ this.setErrorIndex(errorIndex)
+ this._fixNode(fixMatchedErrors)
+ }
+ const rule = this.errorRule()
+ this._fixNode(() => fixMatchedErrors(rule.id))
+ }
+
+ fixIssue() {
+ const rule = this.errorRule()
+ if (this.state.formStateBulkUpdateEnabled && rule?.bulkUpdateSupported) {
+ this._fixBulkError()
+ } else {
+ this._fixSingleError()
}
}
@@ -295,6 +333,8 @@ export default class Checker extends React.Component {
num: this.state.errorIndex + 1,
total: this.state.errors.length,
})
+ const showBulkUpdate = rule && rule.bulkUpdateSupported && this._findMatchErrorIndexes(rule.id).length > 1 &&
+ this.state.formStateValid
return (
@@ -410,6 +450,17 @@ export default class Checker extends React.Component {
{this.renderField(f)}
))}
+ {
+ showBulkUpdate &&
+
+ this.setState({formStateBulkUpdateEnabled: e.target.checked})}
+ />
+
+ }
{
export default {
id: "headings-sequence",
+ bulkUpdateSupported: true,
test: elem => {
const testTags = {
H2: true,