diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b800b6..8a0ffa0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Build Test on: pull_request: - branches: [develop] + branches: [main] jobs: build: diff --git a/CHANGELOG.md b/CHANGELOG.md index 30c2824..2820605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,33 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Prefix the change with one of these keywords: -- _Added_: for new features. -- _Changed_: for changes in existing functionality. -- _Deprecated_: for soon-to-be removed features. -- _Removed_: for now removed features. -- _Fixed_: for any bug fixes. -- _Security_: in case of vulnerabilities. +- _Added_: for new features. +- _Changed_: for changes in existing functionality. +- _Deprecated_: for soon-to-be removed features. +- _Removed_: for now removed features. +- _Fixed_: for any bug fixes. +- _Security_: in case of vulnerabilities. ## [Unreleased] ### Added -- Script helpers for block invalidations, checks and getting blocks in editor -- Higher order component to wrap invalid blocks -- Styles for invalid block wrapper -- Check core heading block to prevent publishing with an H1 in the content -- Check core table block to prevent publishing when a header row is not being used -- Check core image for alternative text -- Add attritbute to image block to confirm decorative image to bypass a11y error -- Instructions on setting up to run and build the plugin -- Getting involved instructions in the README -- Husky pre-commit check -- Commit lint and rules -- Git action to run build on PR to develop -- Git issue and PR templates to log features and tasks +- Add check for core/button text and link ## [0.1.0] ### Added -- Base files for initial plugin setup \ No newline at end of file +- Initial release diff --git a/Functions/BlockConfig.php b/Functions/BlockConfig.php index 79dbc4a..af753d2 100644 --- a/Functions/BlockConfig.php +++ b/Functions/BlockConfig.php @@ -25,6 +25,11 @@ private function __construct() { // Initialize the block configuration once $this->blockConfig = [ + [ + 'function_name' => 'renderCoreButtonOptions', + 'option_name' => 'coreButtonBlockCheck', + 'block_label' => esc_html__('Button', 'block-accessibility-checks'), + ], [ 'function_name' => 'renderCoreHeadingOptions', 'option_name' => 'coreHeadingBlockCheck', diff --git a/Functions/SettingsPage.php b/Functions/SettingsPage.php index 8c1c1f4..bcc491a 100644 --- a/Functions/SettingsPage.php +++ b/Functions/SettingsPage.php @@ -151,6 +151,19 @@ private function renderBlockOptions($blockOptionName, $description) echo ''; } + /** + * Renders the core button options on the settings page. + * + * This method is responsible for rendering the core button options on the settings page. + * It calls the `renderBlockOptions` method with the appropriate parameters to display the options. + * + * @return void + */ + public function renderCoreButtonOptions() + { + $this->renderBlockOptions('coreButtonBlockCheck', 'How strict do you want to be with the core/button block?'); + } + /** * Renders the core heading options on the settings page. * diff --git a/README.md b/README.md index 88e6aa8..7b6f62e 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ The following is a list of checks that are happening on core blocks. | Block | Description | | ------------ | ------------------------------------------------------------------------------- | +| core/button | Checks for text and link on each button | | core/heading | Prevents the usage of an level one heading in the content | | core/image | Checks for alternative text on an image | | core/image | Adds a toggle to confirm image use as decorative allowing for bypass a11y check | diff --git a/build/block-checks-rtl.css b/build/block-checks-rtl.css index a6a6595..c88db9f 100644 --- a/build/block-checks-rtl.css +++ b/build/block-checks-rtl.css @@ -1 +1 @@ -.a11y-block-error{background-color:#f7edec;border:1px dashed #8b3122;border-radius:5px;display:flex;flex-direction:column;gap:18px;margin-right:-50px;overflow:hidden;padding:50px;width:100%}.a11y-error-msg{background-color:#8b3122;border-radius:5px;color:#fff;font-size:16px;font-weight:600;line-height:24px;margin:-40px -40px 30px;padding:15px 20px;text-align:center}.a11y-error-msg+*{margin-top:0}.a11y-block-error>:last-child{margin-bottom:0}.a11y-block-error.wp-block-heading{padding-bottom:0;padding-top:0}.a11y-block-warning{background-color:#f7f6ec;border-radius:5px;display:flex;flex-direction:column;gap:18px;margin-right:-50px;overflow:hidden;padding:50px 50px 30px;width:100%}.a11y-warning-msg{border-bottom:2px solid #fff;color:#8b6d22;font-size:16px;font-weight:600;line-height:24px;margin:-40px -50px 10px;padding:10px 10px 20px;text-align:center}.a11y-warning-msg+*{margin-top:0}.a11y-block-warning>:last-child{margin-bottom:0}.a11y-block-warning.wp-block-heading{padding-bottom:0;padding-top:0} +.a11y-block-error{background-color:#f7edec;border:1px dashed #8b3122;border-radius:5px;padding:15px}.a11y-error-msg{background-color:#8b3122;border-radius:5px;color:#fff;margin-top:0;padding:5px 12px}.a11y-error-msg+*{margin-top:0}.a11y-block-error>:last-child{margin-bottom:0}.a11y-block-error.wp-block-heading{padding-bottom:0;padding-top:0}.a11y-block-warning{background-color:#f7f6ec;border-radius:5px;padding:15px}.a11y-warning-msg{border-bottom:2px solid #fff;color:#8b6d22;margin-top:0;padding:10px 10px 20px}.a11y-warning-msg+*{margin-top:0}.a11y-block-warning>:last-child{margin-bottom:0}.a11y-block-warning.wp-block-heading{padding-bottom:0;padding-top:0} diff --git a/build/block-checks.asset.php b/build/block-checks.asset.php index f87dd30..d1b764e 100644 --- a/build/block-checks.asset.php +++ b/build/block-checks.asset.php @@ -1 +1 @@ - array('wp-block-editor', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => '12f443fb42d1eb70e4dd'); + array('wp-block-editor', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => '84a825068ffd07f9034c'); diff --git a/build/block-checks.css b/build/block-checks.css index efbcdf0..c88db9f 100644 --- a/build/block-checks.css +++ b/build/block-checks.css @@ -1 +1 @@ -.a11y-block-error{background-color:#f7edec;border:1px dashed #8b3122;border-radius:5px;display:flex;flex-direction:column;gap:18px;margin-left:-50px;overflow:hidden;padding:50px;width:100%}.a11y-error-msg{background-color:#8b3122;border-radius:5px;color:#fff;font-size:16px;font-weight:600;line-height:24px;margin:-40px -40px 30px;padding:15px 20px;text-align:center}.a11y-error-msg+*{margin-top:0}.a11y-block-error>:last-child{margin-bottom:0}.a11y-block-error.wp-block-heading{padding-bottom:0;padding-top:0}.a11y-block-warning{background-color:#f7f6ec;border-radius:5px;display:flex;flex-direction:column;gap:18px;margin-left:-50px;overflow:hidden;padding:50px 50px 30px;width:100%}.a11y-warning-msg{border-bottom:2px solid #fff;color:#8b6d22;font-size:16px;font-weight:600;line-height:24px;margin:-40px -50px 10px;padding:10px 10px 20px;text-align:center}.a11y-warning-msg+*{margin-top:0}.a11y-block-warning>:last-child{margin-bottom:0}.a11y-block-warning.wp-block-heading{padding-bottom:0;padding-top:0} +.a11y-block-error{background-color:#f7edec;border:1px dashed #8b3122;border-radius:5px;padding:15px}.a11y-error-msg{background-color:#8b3122;border-radius:5px;color:#fff;margin-top:0;padding:5px 12px}.a11y-error-msg+*{margin-top:0}.a11y-block-error>:last-child{margin-bottom:0}.a11y-block-error.wp-block-heading{padding-bottom:0;padding-top:0}.a11y-block-warning{background-color:#f7f6ec;border-radius:5px;padding:15px}.a11y-warning-msg{border-bottom:2px solid #fff;color:#8b6d22;margin-top:0;padding:10px 10px 20px}.a11y-warning-msg+*{margin-top:0}.a11y-block-warning>:last-child{margin-bottom:0}.a11y-block-warning.wp-block-heading{padding-bottom:0;padding-top:0} diff --git a/build/block-checks.js b/build/block-checks.js index cc5b3ae..1829448 100644 --- a/build/block-checks.js +++ b/build/block-checks.js @@ -1 +1 @@ -(()=>{"use strict";const e=window.wp.i18n,i=window.wp.hooks,t=window.wp.compose,a=window.wp.blockEditor,c=window.wp.components;(0,i.addFilter)("blocks.registerBlockType","block-accessibility-checks/add-image-attribute",(function(e){return"core/image"!==e.name||(e.attributes=Object.assign(e.attributes,{isDecorative:{type:"boolean",default:!1}})),e}));var r=(0,t.createHigherOrderComponent)((function(i){return function(t){if("core/image"!==t.name)return React.createElement(i,t);var r=t.attributes,s=t.setAttributes,n=r.isDecorative;return React.createElement(React.Fragment,null,React.createElement(a.InspectorControls,null,React.createElement(c.PanelBody,{title:(0,e.__)("Accessibility Settings","block-accessibility-checks"),initialOpen:!0},React.createElement(c.ToggleControl,{label:(0,e.__)("Please confirm this image is decorative","block-accessibility-checks"),checked:n,onChange:function(e){return s({isDecorative:e})}}))),React.createElement(i,t))}}),"addImageInspectorControls");(0,i.addFilter)("editor.BlockEdit","block-accessibility-checks/add-inspector-control",r);const s=window.wp.plugins,n=window.wp.data,o=window.wp.element;var l=BlockAccessibilityChecks.blockChecksOptions.coreHeadingBlockCheck;function d(i){if("core/heading"===i.name&&1===i.attributes.level){var t={isValid:!0,message:"",clientId:i.clientId,mode:l};switch(l){case"error":t.isValid=!1,t.message=(0,e.__)("Accessibility Error: Level 1 headings are not allowed in your content area.","block-accessibility-checks");break;case"warning":t.isValid=!1,t.message=(0,e.__)("Warning: Level 1 headings are discouraged in your content area.","block-accessibility-checks");break;default:t.isValid=!0}return t}return{isValid:!0,mode:"none"}}var b=BlockAccessibilityChecks.blockChecksOptions.coreImageBlockCheck;function u(i){if("core/image"===i.name&&!i.attributes.alt&&!i.attributes.isDecorative){var t={isValid:!0,message:"",clientId:i.clientId,mode:b};switch(b){case"error":t.isValid=!1,t.message=(0,e.__)("Accessibility Error: Images are required to have alternative text.","block-accessibility-checks");break;case"warning":t.isValid=!1,t.message=(0,e.__)("Accessibility Warning: Images without alternative text are discouraged in your content area.","block-accessibility-checks");break;default:t.isValid=!0}return t}return{isValid:!0,mode:"none"}}var k=BlockAccessibilityChecks.blockChecksOptions.coreTableBlockCheck;function m(i){if("core/table"===i.name&&0!==i.attributes.body.length&&0===i.attributes.head.length){var t={isValid:!0,message:"",clientId:i.clientId,mode:k};switch(k){case"error":t.isValid=!1,t.message=(0,e.__)("Accessibility Error: Tables are required to have a header row.","block-accessibility-checks");break;case"warning":t.isValid=!1,t.message=(0,e.__)("Warning: It is recommended that tables have a header row.","block-accessibility-checks");break;default:t.isValid=!0}return t}return{isValid:!0,mode:"none"}}var g=(0,t.createHigherOrderComponent)((function(i){return function(t){var a=t.name,c=t.attributes,r=t.clientId,s={isValid:!0,mode:"none",message:""};switch(a){case"core/heading":s=d({name:a,attributes:c,clientId:r});break;case"core/image":s=u({name:a,attributes:c,clientId:r});break;case"core/table":s=m({name:a,attributes:c,clientId:r});break;default:s={isValid:!0,mode:"none",message:""}}if("none"===s.mode||s.isValid)return React.createElement(i,t);var n="error"===s.mode?"a11y-block-error":"a11y-block-warning",o=s.message||("error"===s.mode?(0,e.__)("Accessibility Error: This block does not meet accessibility standards.","block-accessibility-checks"):(0,e.__)("Accessibility Warning: This block may have accessibility issues.","block-accessibility-checks"));return React.createElement("div",{className:n},React.createElement("p",{className:"error"===s.mode?"a11y-error-msg":"a11y-warning-msg"},o),React.createElement(i,t))}}),"withErrorHandling");(0,i.addFilter)("editor.BlockEdit","block-accessibility-checks/with-error-handling",g);var h=[d,u,m];(0,s.registerPlugin)("block-validation",{render:function(){var e=(0,n.useSelect)((function(e){return e("core/block-editor").getBlocks()}),[]).flatMap((function(e){return h.map((function(i){return i(e)}))})).filter((function(e){return!e.isValid})),i=(0,n.useDispatch)("core/editor"),t=i.lockPostSaving,a=i.unlockPostSaving,c=i.lockPostAutosaving,r=i.unlockPostAutosaving,s=i.disablePublishSidebar,l=i.enablePublishSidebar;return(0,o.useEffect)((function(){e.some((function(e){return"error"===e.mode}))?(t(),c(),s()):(a(),r(),l())}),[e,s,l,c,t,r,a]),null}})})(); \ No newline at end of file +(()=>{"use strict";const e=window.wp.i18n,t=window.wp.hooks,i=window.wp.compose,r=window.wp.blockEditor,a=window.wp.components;(0,t.addFilter)("blocks.registerBlockType","block-accessibility-checks/add-image-attribute",(function(e){return"core/image"!==e.name||(e.attributes=Object.assign(e.attributes,{isDecorative:{type:"boolean",default:!1}})),e}));var c=(0,i.createHigherOrderComponent)((function(t){return function(i){if("core/image"!==i.name)return React.createElement(t,i);var c=i.attributes,n=i.setAttributes,s=c.isDecorative;return React.createElement(React.Fragment,null,React.createElement(r.InspectorControls,null,React.createElement(a.PanelBody,{title:(0,e.__)("Accessibility Settings","block-accessibility-checks"),initialOpen:!0},React.createElement(a.ToggleControl,{label:(0,e.__)("Please confirm this image is decorative","block-accessibility-checks"),checked:s,onChange:function(e){return n({isDecorative:e})}}))),React.createElement(t,i))}}),"addImageInspectorControls");(0,t.addFilter)("editor.BlockEdit","block-accessibility-checks/add-inspector-control",c);const n=window.wp.plugins,s=window.wp.data;function o(e){return function(e){if(Array.isArray(e))return l(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(e){if("string"==typeof e)return l(e,t);var i={}.toString.call(e).slice(8,-1);return"Object"===i&&e.constructor&&(i=e.constructor.name),"Map"===i||"Set"===i?Array.from(e):"Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?l(e,t):void 0}}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var i=0,r=Array(t);i0?[].concat(o(t),o(d(e.innerBlocks))):t}))}function u(){return d((0,s.useSelect)((function(e){return e("core/block-editor").getBlocks()}),[])).filter((function(e){return!e.isValid}))}const b=window.wp.element;var k=BlockAccessibilityChecks.blockChecksOptions.coreButtonBlockCheck;function m(t){if("core/button"===t.name&&!t.attributes.url&&!t.attributes.text.originalHTML){var i={isValid:!0,message:"",clientId:t.clientId,mode:k};switch(k){case"error":i.isValid=!1,i.message=(0,e.__)("Error: Buttons must have text and a link","block-accessibility-checks");break;case"warning":i.isValid=!1,i.message=(0,e.__)("Warning: Buttons must have text and a link","block-accessibility-checks");break;default:i.isValid=!0}return i}return{isValid:!0,mode:"none"}}var g=BlockAccessibilityChecks.blockChecksOptions.coreHeadingBlockCheck;function h(t){if("core/heading"===t.name&&1===t.attributes.level){var i={isValid:!0,message:"",clientId:t.clientId,mode:g};switch(g){case"error":i.isValid=!1,i.message=(0,e.__)("Error: Level 1 headings should only be used for page titles","block-accessibility-checks");break;case"warning":i.isValid=!1,i.message=(0,e.__)("Warning: Level 1 headings should only be used for page titles","block-accessibility-checks");break;default:i.isValid=!0}return i}return{isValid:!0,mode:"none"}}var f=BlockAccessibilityChecks.blockChecksOptions.coreImageBlockCheck;function y(t){if("core/image"===t.name&&!t.attributes.alt&&!t.attributes.isDecorative){var i={isValid:!0,message:"",clientId:t.clientId,mode:f};switch(f){case"error":i.isValid=!1,i.message=(0,e.__)("Error: Images are required to have alternative text","block-accessibility-checks");break;case"warning":i.isValid=!1,i.message=(0,e.__)("Warning: Images are required to have alternative text","block-accessibility-checks");break;default:i.isValid=!0}return i}return{isValid:!0,mode:"none"}}var v=BlockAccessibilityChecks.blockChecksOptions.coreTableBlockCheck;function w(t){if("core/table"===t.name&&0!==t.attributes.body.length&&0===t.attributes.head.length){var i={isValid:!0,message:"",clientId:t.clientId,mode:v};switch(v){case"error":i.isValid=!1,i.message=(0,e.__)("Error: Tables are required to have a header row","block-accessibility-checks");break;case"warning":i.isValid=!1,i.message=(0,e.__)("Warning: Tables are required to have a header row","block-accessibility-checks");break;default:i.isValid=!0}return i}return{isValid:!0,mode:"none"}}var p=(0,i.createHigherOrderComponent)((function(t){return function(i){var r=i.name,a=i.attributes,c=i.clientId,n={isValid:!0,mode:"none",message:""};switch(r){case"core/button":n=m({name:r,attributes:a,clientId:c});break;case"core/heading":n=h({name:r,attributes:a,clientId:c});break;case"core/image":n=y({name:r,attributes:a,clientId:c});break;case"core/table":n=w({name:r,attributes:a,clientId:c});break;default:n={isValid:!0,mode:"none",message:""}}if("none"===n.mode||n.isValid)return React.createElement(t,i);var s="error"===n.mode?"a11y-block-error":"a11y-block-warning",o=n.message||("error"===n.mode?(0,e.__)("Accessibility Error: This block does not meet accessibility standards.","block-accessibility-checks"):(0,e.__)("Accessibility Warning: This block may have accessibility issues.","block-accessibility-checks"));return React.createElement("div",{className:s},React.createElement("p",{className:"error"===n.mode?"a11y-error-msg":"a11y-warning-msg"},o),React.createElement(t,i))}}),"withErrorHandling");(0,t.addFilter)("editor.BlockEdit","block-accessibility-checks/with-error-handling",p);var V=[m,h,y,w];(0,n.registerPlugin)("block-validation",{render:function(){var e=u(),t=(0,s.useDispatch)("core/editor"),i=t.lockPostSaving,r=t.unlockPostSaving,a=t.lockPostAutosaving,c=t.unlockPostAutosaving,n=t.disablePublishSidebar,o=t.enablePublishSidebar;return(0,b.useEffect)((function(){e.some((function(e){return"error"===e.mode}))?(i(),a(),n()):(r(),c(),o())}),[e,n,o,a,i,c,r]),null}})})(); \ No newline at end of file diff --git a/readme.txt b/readme.txt index 73c2403..24772bf 100644 --- a/readme.txt +++ b/readme.txt @@ -16,6 +16,7 @@ Accessibility is a crucial aspect of web content creation. Many organizations mu The following is a list of checks that are configurable for core blocks. Each check can be set to error (default), warning or none. +* core/button: Checks for text and link on each button * core/heading: Prevents the usage of an level one heading in the content * core/image: Checks for alternative text on an image * core/image: Adds a toggle to confirm image use as decorative allowing for bypass accessibility check diff --git a/src/scripts/blockChecks/checkButton.js b/src/scripts/blockChecks/checkButton.js new file mode 100644 index 0000000..5b440a1 --- /dev/null +++ b/src/scripts/blockChecks/checkButton.js @@ -0,0 +1,49 @@ +/* global BlockAccessibilityChecks */ +import { __ } from '@wordpress/i18n'; +const validationMode = + BlockAccessibilityChecks.blockChecksOptions.coreButtonBlockCheck; + +/** + * Checks the heading level of a block. + * + * @param {Object} block - The block object to be checked. + * @return {Object} - The response object containing the validation result. + */ +export function checkButtonAttributes(block) { + if ( + block.name === 'core/button' && + !block.attributes.url && + !block.attributes.text.originalHTML + ) { + const response = { + isValid: true, + message: '', + clientId: block.clientId, + mode: validationMode, + }; + + switch (validationMode) { + case 'error': + response.isValid = false; + response.message = __( + 'Error: Buttons must have text and a link', + 'block-accessibility-checks' + ); + break; + case 'warning': + response.isValid = false; + response.message = __( + 'Warning: Buttons must have text and a link', + 'block-accessibility-checks' + ); + break; + case 'none': + default: + response.isValid = true; + } + + return response; + } + + return { isValid: true, mode: 'none' }; +} diff --git a/src/scripts/blockChecks/checkHeading.js b/src/scripts/blockChecks/checkHeading.js index 3e23bb5..154ccec 100644 --- a/src/scripts/blockChecks/checkHeading.js +++ b/src/scripts/blockChecks/checkHeading.js @@ -22,14 +22,14 @@ export function checkHeadingLevel(block) { case 'error': response.isValid = false; response.message = __( - 'Accessibility Error: Level 1 headings are not allowed in your content area.', + 'Error: Level 1 headings should only be used for page titles', 'block-accessibility-checks' ); break; case 'warning': response.isValid = false; response.message = __( - 'Warning: Level 1 headings are discouraged in your content area.', + 'Warning: Level 1 headings should only be used for page titles', 'block-accessibility-checks' ); break; diff --git a/src/scripts/blockChecks/checkImage.js b/src/scripts/blockChecks/checkImage.js index ded48e0..e942937 100644 --- a/src/scripts/blockChecks/checkImage.js +++ b/src/scripts/blockChecks/checkImage.js @@ -26,14 +26,14 @@ export function checkImageAlt(block) { case 'error': response.isValid = false; response.message = __( - 'Accessibility Error: Images are required to have alternative text.', + 'Error: Images are required to have alternative text', 'block-accessibility-checks' ); break; case 'warning': response.isValid = false; response.message = __( - 'Accessibility Warning: Images without alternative text are discouraged in your content area.', + 'Warning: Images are required to have alternative text', 'block-accessibility-checks' ); break; diff --git a/src/scripts/blockChecks/checkTable.js b/src/scripts/blockChecks/checkTable.js index 0446125..e414c5d 100644 --- a/src/scripts/blockChecks/checkTable.js +++ b/src/scripts/blockChecks/checkTable.js @@ -26,14 +26,14 @@ export function checkTableHeaderRow(block) { case 'error': response.isValid = false; response.message = __( - 'Accessibility Error: Tables are required to have a header row.', + 'Error: Tables are required to have a header row', 'block-accessibility-checks' ); break; case 'warning': response.isValid = false; response.message = __( - 'Warning: It is recommended that tables have a header row.', + 'Warning: Tables are required to have a header row', 'block-accessibility-checks' ); break; diff --git a/src/scripts/helpers/blockErrorComponent.js b/src/scripts/helpers/blockErrorComponent.js index 7768476..c2f8e9b 100644 --- a/src/scripts/helpers/blockErrorComponent.js +++ b/src/scripts/helpers/blockErrorComponent.js @@ -2,6 +2,7 @@ import { createHigherOrderComponent } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import { __ } from '@wordpress/i18n'; +import { checkButtonAttributes } from '../blockChecks/checkButton'; import { checkHeadingLevel } from '../blockChecks/checkHeading'; import { checkImageAlt } from '../blockChecks/checkImage'; import { checkTableHeaderRow } from '../blockChecks/checkTable'; @@ -23,6 +24,13 @@ const withErrorHandling = createHigherOrderComponent((BlockEdit) => { }; switch (name) { + case 'core/button': + validationResult = checkButtonAttributes({ + name, + attributes, + clientId, + }); + break; case 'core/heading': validationResult = checkHeadingLevel({ name, diff --git a/src/scripts/helpers/getInvalidBlocks.js b/src/scripts/helpers/getInvalidBlocks.js index 4e646d5..6b0067a 100644 --- a/src/scripts/helpers/getInvalidBlocks.js +++ b/src/scripts/helpers/getInvalidBlocks.js @@ -1,17 +1,45 @@ import { useSelect } from '@wordpress/data'; import { blockChecksArray } from '../registerPlugin'; +/** + * Recursively retrieves invalid blocks from a list of blocks. + * + * @param {Array} blocks - Array of blocks to check. + * @return {Array} An array of invalid blocks. + */ +function getInvalidBlocksRecursive(blocks) { + // Recursive function to check each block and its inner blocks + return blocks.flatMap((block) => { + // Run checks on the current block + const results = blockChecksArray.map((check) => check(block)); + + // If the block has inner blocks, recursively check them + if (block.innerBlocks && block.innerBlocks.length > 0) { + return [ + ...results, + ...getInvalidBlocksRecursive(block.innerBlocks), + ]; + } + + return results; + }); +} + /** * Retrieves the invalid blocks from the block editor. * * @return {Array} An array of invalid blocks. */ export function GetInvalidBlocks() { + // Hook to get all blocks once, at the top level const allBlocks = useSelect( (select) => select('core/block-editor').getBlocks(), [] ); - return allBlocks - .flatMap((block) => blockChecksArray.map((check) => check(block))) - .filter((result) => !result.isValid); + + // Now, use the recursive function to check all blocks and their inner blocks + const invalidBlocks = getInvalidBlocksRecursive(allBlocks); + + // Filter out valid blocks and return only invalid ones + return invalidBlocks.filter((result) => !result.isValid); } diff --git a/src/scripts/registerPlugin.js b/src/scripts/registerPlugin.js index 28f38e8..63e2728 100644 --- a/src/scripts/registerPlugin.js +++ b/src/scripts/registerPlugin.js @@ -3,11 +3,13 @@ import { BlockInvalidation } from './helpers/blockInvalidation'; import './helpers/blockErrorComponent'; // Import block check functions +import { checkButtonAttributes } from './blockChecks/checkButton'; import { checkHeadingLevel } from './blockChecks/checkHeading'; import { checkImageAlt } from './blockChecks/checkImage'; import { checkTableHeaderRow } from './blockChecks/checkTable'; export const blockChecksArray = [ + checkButtonAttributes, checkHeadingLevel, checkImageAlt, checkTableHeaderRow, diff --git a/src/styles/error.scss b/src/styles/error.scss index b9f5318..b328d64 100644 --- a/src/styles/error.scss +++ b/src/styles/error.scss @@ -1,28 +1,18 @@ /* ERROR STYLES */ .a11y-block-error { - display: flex; - flex-direction: column; - gap: 18px; - width: 100%; - padding: 50px; - margin-left: -50px; + padding: 15px; background-color: #f7edec; border: 1px dashed #8b3122; border-radius: 5px; - overflow: hidden; } .a11y-error-msg { - font-size: 16px; - line-height: 24px; - font-weight: 600; - text-align: center; + margin-top: 0; color: white; background-color: #8b3122; border-radius: 5px; - padding: 15px 20px; - margin: -40px -40px 30px -40px; + padding: 5px 12px; } /* Remove top spacing from block being wrapped */ diff --git a/src/styles/warning.scss b/src/styles/warning.scss index 71a765f..becd0ee 100644 --- a/src/styles/warning.scss +++ b/src/styles/warning.scss @@ -1,24 +1,14 @@ .a11y-block-warning { - display: flex; - flex-direction: column; - gap: 18px; - width: 100%; - padding: 50px 50px 30px 50px; - margin-left: -50px; + padding: 15px; background-color: #f7f6ec; border-radius: 5px; - overflow: hidden; } .a11y-warning-msg { - font-size: 16px; - line-height: 24px; - font-weight: 600; - text-align: center; + margin-top: 0; color: #8b6d22; padding: 10px 10px 20px 10px; border-bottom: 2px solid #ffffff; - margin: -40px -50px 10px -50px; } /* Remove top spacing from block being wrapped */