diff --git a/.wordpress-org/blueprints/blueprint.json b/.wordpress-org/blueprints/blueprint.json index 75d3abf..d68be14 100644 --- a/.wordpress-org/blueprints/blueprint.json +++ b/.wordpress-org/blueprints/blueprint.json @@ -11,8 +11,8 @@ "pluginData": { "resource": "git:directory", "url": "https://github.com/BeAPI/blockparty-button-icon", - "ref": "playground/blueprint-preview", - "refType": "branch" + "ref": "1.0.0", + "refType": "tag" }, "options": { "activate": true, diff --git a/README.md b/README.md index 40215e0..faaa860 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Be API Github Banner](.github/banner-github.png)](https://beapi.fr) -[![Test with WordPress Playground](https://img.shields.io/badge/Test%20with-WordPress%20Playground-0073aa?style=for-the-badge&logo=wordpress&logoColor=white)](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/BeAPI/blockparty-button-icon/refs/heads/playground/blueprint-preview/.wordpress-org/blueprints/blueprint.json) +[![Test with WordPress Playground](https://img.shields.io/badge/Test%20with-WordPress%20Playground-0073aa?style=for-the-badge&logo=wordpress&logoColor=white)](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/BeAPI/blockparty-button-icon/refs/heads/main/.wordpress-org/blueprints/blueprint.json) # Blockparty Button Icon diff --git a/build/index-rtl.css b/build/index-rtl.css deleted file mode 100644 index 5b61c41..0000000 --- a/build/index-rtl.css +++ /dev/null @@ -1 +0,0 @@ -.wp-block-button.blockparty-button-icon .wp-block-button__link{align-items:center;display:inline-flex;gap:.5em}.wp-block-button.blockparty-button-icon .wp-block-button__link .block-editor-block-list__layout,.wp-block-button.blockparty-button-icon .wp-block-button__link>.block-editor-inner-blocks{display:contents}.wp-block-button.blockparty-button-icon .wp-block-button__link .block-editor-block-list__block{display:inline-flex;line-height:0;margin:0;max-width:none;min-height:0;vertical-align:middle}.wp-block-button.blockparty-button-icon .wp-block-button__link .block-editor-block-list__block>.wp-block-icon{height:1.25em;width:1.25em}.wp-block-button.blockparty-button-icon.blockparty-button-icon--right .wp-block-button__link{flex-direction:row-reverse}.wp-block-button.blockparty-button-icon .wp-block-icon{align-items:center;display:inline-flex;flex-shrink:0;justify-content:center;line-height:0}.wp-block-button.blockparty-button-icon .wp-block-icon svg{display:block} diff --git a/build/index.asset.php b/build/index.asset.php deleted file mode 100644 index 3c68811..0000000 --- a/build/index.asset.php +++ /dev/null @@ -1,16 +0,0 @@ - array( - 'react-jsx-runtime', - 'wp-block-editor', - 'wp-blocks', - 'wp-components', - 'wp-data', - 'wp-element', - 'wp-hooks', - 'wp-i18n', - 'wp-keycodes', - 'wp-primitives', - 'wp-url' - ), - 'version' => 'ff595fb21b7020ff3bb6' -); diff --git a/build/index.css b/build/index.css deleted file mode 100644 index 5b61c41..0000000 --- a/build/index.css +++ /dev/null @@ -1 +0,0 @@ -.wp-block-button.blockparty-button-icon .wp-block-button__link{align-items:center;display:inline-flex;gap:.5em}.wp-block-button.blockparty-button-icon .wp-block-button__link .block-editor-block-list__layout,.wp-block-button.blockparty-button-icon .wp-block-button__link>.block-editor-inner-blocks{display:contents}.wp-block-button.blockparty-button-icon .wp-block-button__link .block-editor-block-list__block{display:inline-flex;line-height:0;margin:0;max-width:none;min-height:0;vertical-align:middle}.wp-block-button.blockparty-button-icon .wp-block-button__link .block-editor-block-list__block>.wp-block-icon{height:1.25em;width:1.25em}.wp-block-button.blockparty-button-icon.blockparty-button-icon--right .wp-block-button__link{flex-direction:row-reverse}.wp-block-button.blockparty-button-icon .wp-block-icon{align-items:center;display:inline-flex;flex-shrink:0;justify-content:center;line-height:0}.wp-block-button.blockparty-button-icon .wp-block-icon svg{display:block} diff --git a/build/index.js b/build/index.js deleted file mode 100644 index 41e891e..0000000 --- a/build/index.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var t={6(){const t=window.wp.hooks,e="core/button",o="wp-block-button__label",n={hasIcon:{type:"boolean",default:!1},iconPosition:{type:"string",default:"left"}},l="noopener",s="nofollow";function r(t){var e,o,n="";if("string"==typeof t||"number"==typeof t)n+=t;else if("object"==typeof t)if(Array.isArray(t)){var l=t.length;for(e=0;e{const t=window?.blockpartyButtonIconSettings?.allowedIconBlocks;return(Array.isArray(t)&&t.length>0?t:k).filter(t=>void 0!==(0,g.getBlockType)(t))};function _({hasIcon:t,iconPosition:e,hasIconBlock:o,clientId:n,setAttributes:l}){const s=(0,f.useSelect)(t=>function(t,e){const{getBlocks:o}=t(p.store),n=y(),l=o(e);return l.find(t=>n.includes(t.name))?.clientId}(t,n),[n]);return(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(p.BlockControls,{children:(0,h.jsx)(u.ToolbarGroup,{children:t?(0,h.jsx)(u.ToolbarButton,{onClick:()=>{l({hasIcon:!1})},children:(0,a.__)("Remove icon","blockparty-button-icon")}):(0,h.jsx)(u.ToolbarButton,{icon:(0,h.jsx)(x,{}),label:(0,a.__)("Icon","blockparty-button-icon"),isDisabled:!o,onClick:()=>{l({hasIcon:!0})}})})},"toolbar"),(0,h.jsx)(p.InspectorControls,{group:"styles",children:(0,h.jsxs)(u.PanelBody,{title:(0,a.__)("Icon","blockparty-button-icon"),initialOpen:t,children:[(0,h.jsx)(u.ToggleControl,{label:(0,a.__)("Show icon","blockparty-button-icon"),checked:t,disabled:!o,onChange:t=>{l({hasIcon:t})}}),t&&(0,h.jsxs)(h.Fragment,{children:[(0,h.jsxs)(u.__experimentalToggleGroupControl,{label:(0,a.__)("Icon position","blockparty-button-icon"),value:e,isBlock:!0,__next40pxDefaultSize:!0,onChange:t=>{l({iconPosition:t})},children:[(0,h.jsx)(u.__experimentalToggleGroupControlOption,{value:"left",label:(0,a.__)("Left","blockparty-button-icon")}),(0,h.jsx)(u.__experimentalToggleGroupControlOption,{value:"right",label:(0,a.__)("Right","blockparty-button-icon")})]}),(0,h.jsx)(u.Button,{variant:"secondary",__next40pxDefaultSize:!0,disabled:!s,style:{width:"100%",justifyContent:"center"},onClick:()=>{!function(t){if(!t)return;const{selectBlock:e}=(0,f.dispatch)(p.store);e(t);const o=t=>{(function(){const t=document.querySelectorAll(".block-editor-block-contextual-toolbar, .block-editor-block-toolbar");for(const e of t){const t=e.querySelectorAll("button.components-toolbar-button, button.components-button");for(const e of t){if(e.disabled||"true"===e.getAttribute("aria-disabled"))continue;const t=e.textContent?.replace(/\s+/g," ").trim(),o=Boolean(e.querySelector("svg"));if(t&&t.length>=2&&!o)return e.click(),!0}}return!1})()||t<8&&window.setTimeout(()=>o(t+1),50)};window.requestAnimationFrame(()=>o(0))}(s)},children:(0,a.__)("Replace Icon","blockparty-button-icon")})]})]})})]})}const w=window.wp.url;function v(t){if(!t?.hasIcon)return{};const e=(0,p.__experimentalGetGapCSSValue)(t.style?.spacing?.blockGap);return e?{gap:e}:{}}const j=o.replace(/-/g,"\\-");function I(t){const e=t.trim(),o=new RegExp(`^]*\\bclass="[^"]*\\b${j}\\b[^"]*"[^>]*>([\\s\\S]*)<\\/span>$`,"i"),n=e.match(o);return n?n[1].trim():e}function C(t){if(!t)return t;let e=t.trim(),o=!0;for(;o;){const t=I(e);o=t!==e,e=t}if(e.includes("]*>/gi,"");t.length&&(e=t)}return e}const S=p.__experimentalUseBackgroundProps||(()=>({className:void 0,style:{}})),N=[...p.LinkControl.DEFAULT_LINK_SETTINGS,{id:"nofollow",title:(0,a.__)("Mark as nofollow","blockparty-button-icon")}];function B(t){const{attributes:e,setAttributes:n,clientId:r,isSelected:d,className:f}=t,{tagName:x,linkTarget:g,placeholder:k,rel:j,style:I,text:B,url:T,hasIcon:A,iconPosition:G}=e,P=x||"a",E="a"===P,R=y(),L=R.length>0,M=R[0];(0,c.useEffect)(()=>{if(!A)return;const t=C(B);t!==B&&n({text:t})},[A,B,n]);const[F,O]=(0,c.useState)(!1),[z,D]=(0,c.useState)(null),$=(0,c.useRef)(),H=(0,p.__experimentalUseBorderProps)(e),Z=(0,p.__experimentalUseColorProps)(e),V=(0,p.__experimentalGetSpacingClassesAndStyles)(e),U=(0,p.__experimentalGetShadowClassesAndStyles)(e),q=S(e),K=(0,p.getTypographyClassesAndStyles)(e),J=(0,p.useBlockProps)({ref:D,className:i({"blockparty-button-icon":A,"blockparty-button-icon--left":A&&"right"!==G,"blockparty-button-icon--right":A&&"right"===G}),onKeyDown(t){b.isKeyboardEvent.primary(t,"k")&&(t.preventDefault(),O(!0))}}),W=!!T,X="_blank"===g,Q=!!j?.includes(s),Y=(0,c.useMemo)(()=>({url:T,opensInNewTab:X,nofollow:Q}),[T,X,Q]);(0,c.useEffect)(()=>{d||O(!1)},[d]);const tt=i(f,"wp-block-button__link",Z.className,q.className,H.className,K.className,{"no-border-radius":0===I?.border?.radius,"has-custom-font-size":J.style?.fontSize},(0,p.__experimentalGetElementClassName)("button")),et={...H.style,...Z.style,...q.style,...V.style,...U.style,...K.style,...v(e),writingMode:void 0},ot=A&&L&&(0,h.jsx)(p.InnerBlocks,{allowedBlocks:R,templateLock:"insert",template:[[M,{style:{dimensions:{width:"1.25em",height:"1.25em"}},lock:{move:!0,remove:!0}}]],templateInsertUpdatesSelection:!1,directInsert:!1,renderAppender:!1});let nt;return nt=A?E?(0,h.jsxs)("a",{className:tt,style:et,href:T||"#",target:g,rel:j,onClick:t=>{t.preventDefault()},children:[ot,(0,h.jsx)(p.RichText,{ref:$,tagName:"span",className:o,"aria-label":(0,a.__)("Button text","blockparty-button-icon"),placeholder:k||(0,a.__)("Add text…","blockparty-button-icon"),value:B,onChange:t=>{n({text:t})},withoutInteractiveFormatting:!0,identifier:"text"})]}):(0,h.jsxs)("button",{type:e.type||"button",className:tt,style:et,children:[ot,(0,h.jsx)(p.RichText,{ref:$,tagName:"span",className:o,"aria-label":(0,a.__)("Button text","blockparty-button-icon"),placeholder:k||(0,a.__)("Add text…","blockparty-button-icon"),value:B,onChange:t=>{n({text:t})},withoutInteractiveFormatting:!0,identifier:"text"})]}):(0,h.jsx)(p.RichText,{ref:$,"aria-label":(0,a.__)("Button text","blockparty-button-icon"),placeholder:k||(0,a.__)("Add text…","blockparty-button-icon"),value:B,onChange:t=>{n({text:t})},withoutInteractiveFormatting:!0,tagName:P,type:"button"===P?e.type||"button":null,className:tt,style:et,href:E?T:null,target:E?g:null,rel:E?j:null,identifier:"text"}),(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(_,{hasIcon:A,iconPosition:G,hasIconBlock:L,clientId:r,setAttributes:n}),(0,h.jsx)("div",{...J,children:nt}),E&&(0,h.jsx)(p.BlockControls,{group:"block",children:(0,h.jsx)(u.ToolbarButton,{name:"link",icon:m,title:(0,a.__)("Link","blockparty-button-icon"),shortcut:b.displayShortcut.primary("k"),onClick:()=>{O(!F)},isActive:W})}),E&&d&&F&&(0,h.jsx)(u.Popover,{placement:"bottom",onClose:()=>{O(!1),$.current?.focus()},anchor:z,focusOnMount:!!F&&"firstElement",__unstableSlotName:"__unstable-block-tools-after",shift:!0,children:(0,h.jsx)(p.LinkControl,{value:Y,onChange:({url:t,opensInNewTab:e,nofollow:o})=>n((({rel:t="",url:e="",opensInNewTab:o,nofollow:n})=>{let r,i=t;if(o)r="_blank",i=i?.includes(l)?i:i+` ${l}`;else{const t=new RegExp(`\\b${l}\\s*`,"g");i=i?.replace(t,"").trim()}if(n)i=i?.includes(s)?i:(i+` ${s}`).trim();else{const t=new RegExp(`\\b${s}\\s*`,"g");i=i?.replace(t,"").trim()}return{url:(0,w.prependHTTPS)(e),linkTarget:r,rel:i||void 0}})({rel:j,url:t,opensInNewTab:e,nofollow:o})),onRemove:()=>{n({url:void 0,linkTarget:void 0,rel:void 0}),O(!1),$.current?.focus()},forceIsEditingLink:!W,settings:N})})]})}function T({clientId:t,hasIcon:e}){const o=(0,f.useSelect)(e=>e(p.store).getBlocks(t),[t]),{removeBlocks:n}=(0,f.useDispatch)(p.store);return(0,c.useEffect)(()=>{e||0===o.length||n(o.map(t=>t.clientId),!1)},[e,o,n]),null}const A=`.${o}`,G=`a.wp-block-button__link:not(:has(> ${A})), button.wp-element-button:not(:has(> ${A})), ${A}`;(0,t.addFilter)("blocks.registerBlockType","blockparty/button-icon/extend-core-button",(t,l)=>{if(l!==e)return t;const s=t.edit,r=t.save,a=t.supports?.spacing??{},c=t.attributes?.text??{};return{...t,supports:{...t.supports,spacing:{...a,blockGap:{sides:["horizontal"]},__experimentalDefaultControls:{...a.__experimentalDefaultControls,blockGap:{sides:["horizontal"]}}}},attributes:{...t.attributes,...n,text:{...c,selector:G}},edit:t=>function(t,e){const{attributes:o,setAttributes:n,clientId:l}=t,{hasIcon:s,iconPosition:r}=o,i=y().length>0;return(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(T,{clientId:l,hasIcon:s}),s?(0,h.jsx)(B,{...t}):(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(_,{hasIcon:s,iconPosition:r,hasIconBlock:i,clientId:l,setAttributes:n}),(0,h.jsx)(e,{...t})]})]})}(t,s),save:t=>t.attributes.hasIcon?function({attributes:t}){const{tagName:e,type:n,fontSize:l,linkTarget:s,rel:r,style:a,text:c,title:u,url:b,hasIcon:d,iconPosition:m}=t,f=e||"a",x="button"===f,g=n||"button",k=(0,p.__experimentalGetBorderClassesAndStyles)(t),y=(0,p.__experimentalGetColorClassesAndStyles)(t),_=(0,p.__experimentalGetSpacingClassesAndStyles)(t),w=(0,p.__experimentalGetShadowClassesAndStyles)(t),j=(0,p.getTypographyClassesAndStyles)(t),I=i("wp-block-button__link",y.className,k.className,j.className,{"no-border-radius":0===a?.border?.radius,"has-custom-font-size":l||a?.typography?.fontSize},(0,p.__experimentalGetElementClassName)("button")),S={...k.style,...y.style,..._.style,...w.style,...j.style,...v(t),writingMode:void 0},N=p.useBlockProps.save({className:i({"blockparty-button-icon":d,"blockparty-button-icon--left":d&&"right"!==m,"blockparty-button-icon--right":d&&"right"===m})}),B={className:I,style:S,title:u};return x?B.type=g:(B.href=b,B.target=s,B.rel=r),d?(0,h.jsx)("div",{...N,children:(0,h.jsxs)(f,{...B,children:[(0,h.jsx)(p.InnerBlocks.Content,{}),(0,h.jsx)(p.RichText.Content,{tagName:"span",className:o,value:C(c)})]})}):(0,h.jsx)("div",{...N,children:(0,h.jsx)(p.RichText.Content,{tagName:f,value:c,...B})})}(t):r(t)}}),(0,t.addFilter)("blocks.getBlockAttributes","blockparty/button-icon/normalize-button-text",(t,o)=>{if(o.name!==e||!t.hasIcon)return t;const n=C(t.text);return n===t.text?t:{...t,text:n}})}};const e={};function o(n){const l=e[n];if(void 0!==l)return l.exports;const s=e[n]={exports:{}};return t[n](s,s.exports,o),s.exports}o.m=t,(()=>{const t=[];o.O=(e,n,l)=>{if(!n){for(var s=0;sObject.hasOwn(t,e),(()=>{const t={57:0,350:0};o.O.j=e=>0===t[e];const e=(e,n)=>{let[l,s,r]=n;var i,a,c=0;if(l.some(e=>0!==t[e])){for(i in s)o.o(s,i)&&(o.m[i]=s[i]);if(r)var u=r(o)}for(e&&e(n);co(6));n=o.O(n)})(); \ No newline at end of file diff --git a/build/style-index-rtl.css b/build/style-index-rtl.css deleted file mode 100644 index b789f92..0000000 --- a/build/style-index-rtl.css +++ /dev/null @@ -1 +0,0 @@ -.wp-block-button.blockparty-button-icon .wp-block-button__link{align-items:center;display:inline-flex;gap:.5em}.wp-block-button.blockparty-button-icon.blockparty-button-icon--right .wp-block-button__link{flex-direction:row-reverse}.wp-block-button.blockparty-button-icon .wp-block-icon{align-items:center;display:inline-flex;flex-shrink:0;justify-content:center;line-height:0}.wp-block-button.blockparty-button-icon .wp-block-icon svg{display:block} diff --git a/build/style-index.css b/build/style-index.css deleted file mode 100644 index b789f92..0000000 --- a/build/style-index.css +++ /dev/null @@ -1 +0,0 @@ -.wp-block-button.blockparty-button-icon .wp-block-button__link{align-items:center;display:inline-flex;gap:.5em}.wp-block-button.blockparty-button-icon.blockparty-button-icon--right .wp-block-button__link{flex-direction:row-reverse}.wp-block-button.blockparty-button-icon .wp-block-icon{align-items:center;display:inline-flex;flex-shrink:0;justify-content:center;line-height:0}.wp-block-button.blockparty-button-icon .wp-block-icon svg{display:block} diff --git a/src/editor.scss b/src/editor.scss index 4ea3abb..de7f69f 100644 --- a/src/editor.scss +++ b/src/editor.scss @@ -1,5 +1,3 @@ -$wp-block-button-icon-size: 1.25em; - .wp-block-button.blockparty-button-icon { .wp-block-button__link { display: inline-flex; @@ -22,11 +20,6 @@ $wp-block-button-icon-size: 1.25em; line-height: 0; vertical-align: middle; } - - .block-editor-block-list__block > .wp-block-icon { - width: $wp-block-button-icon-size; - height: $wp-block-button-icon-size; - } } &.blockparty-button-icon--right .wp-block-button__link { diff --git a/src/extend-core-button/open-icon-inserter.js b/src/extend-core-button/open-icon-inserter.js index c93c9d4..1181fc5 100644 --- a/src/extend-core-button/open-icon-inserter.js +++ b/src/extend-core-button/open-icon-inserter.js @@ -1,9 +1,62 @@ +import { __ } from '@wordpress/i18n'; import { dispatch } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; const MAX_ATTEMPTS = 8; const RETRY_DELAY_MS = 50; +/** + * Normalize toolbar button label text for comparison. + * + * @param {string} text Raw label text. + * @return {string} Normalized label. + */ +function normalizeLabel( text ) { + return text.replace( /\s+/g, ' ' ).trim().toLowerCase(); +} + +/** + * Labels used by core/icon to open the icon library modal. + * + * @return {Set} Normalized labels. + */ +function getIconLibraryOpenLabels() { + // Match core/icon toolbar labels (default text domain). + return new Set( + [ __( 'Choose icon' ), __( 'Replace' ) ].map( normalizeLabel ) + ); +} + +/** + * Whether a toolbar button opens the core/icon library. + * + * Must not match the parent button's text-only "Remove icon" control, which + * can still be in the DOM right after selectBlock switches to core/icon. + * + * @param {HTMLElement} button Toolbar button element. + * @return {boolean} True when the control opens the icon library. + */ +export function isIconLibraryToolbarButton( button ) { + if ( + button.disabled || + button.getAttribute( 'aria-disabled' ) === 'true' + ) { + return false; + } + + const text = button.textContent?.replace( /\s+/g, ' ' ).trim(); + if ( ! text ) { + return false; + } + + // core/icon uses a text-only control; icon-only buttons are unrelated. + if ( button.querySelector( 'svg' ) ) { + return false; + } + + return getIconLibraryOpenLabels().has( normalizeLabel( text ) ); +} + /** * Clicks the core/icon toolbar control that opens the icon library modal. * @@ -20,18 +73,7 @@ function clickIconLibraryToolbarButton() { ); for ( const button of buttons ) { - if ( - button.disabled || - button.getAttribute( 'aria-disabled' ) === 'true' - ) { - continue; - } - - const text = button.textContent?.replace( /\s+/g, ' ' ).trim(); - const hasSvg = Boolean( button.querySelector( 'svg' ) ); - - // core/icon uses a text-only toolbar button for Choose icon / Replace. - if ( text && text.length >= 2 && ! hasSvg ) { + if ( isIconLibraryToolbarButton( button ) ) { button.click(); return true; } diff --git a/tests/bin/check-release-version.sh b/tests/bin/check-release-version.sh index ccafcf4..9dbf7be 100755 --- a/tests/bin/check-release-version.sh +++ b/tests/bin/check-release-version.sh @@ -137,6 +137,12 @@ if [ -f .wordpress-org/blueprints/blueprint.json ]; then echo "::error::.wordpress-org/blueprints/blueprint.json pluginData.ref must be '${NEW_VER}' (got '${BP_REF}')" fail=1 fi + + BP_REF_TYPE=$(jq -r '.steps[] | select(.pluginData) | .pluginData.refType' .wordpress-org/blueprints/blueprint.json | head -1) + if [ "$BP_REF_TYPE" != "tag" ]; then + echo "::error::.wordpress-org/blueprints/blueprint.json pluginData.refType must be 'tag' (got '${BP_REF_TYPE}')" + fail=1 + fi fi if [ -f blockparty-button-icon.php ]; then diff --git a/tests/js/open-icon-inserter.test.js b/tests/js/open-icon-inserter.test.js new file mode 100644 index 0000000..412943e --- /dev/null +++ b/tests/js/open-icon-inserter.test.js @@ -0,0 +1,93 @@ +/** + * @jest-environment jsdom + */ + +/** + * Internal dependencies + */ +import { isIconLibraryToolbarButton } from '../../src/extend-core-button/open-icon-inserter'; + +jest.mock( '@wordpress/i18n', () => ( { + __: ( text ) => text, +} ) ); + +jest.mock( '@wordpress/data', () => ( { + dispatch: jest.fn(), +} ) ); + +jest.mock( '@wordpress/block-editor', () => ( { + store: 'core/block-editor', +} ) ); + +/** + * Build a toolbar button stub for label matching. + * + * @param {Object} options Options. + * @param {string} options.text Visible button text. + * @param {boolean} [options.hasSvg] Whether the button contains an SVG. + * @param {boolean} [options.disabled] Whether the button is disabled. + * @param {string} [options.ariaDisabled] aria-disabled attribute value. + * @return {HTMLButtonElement} Button element. + */ +const createButton = ( { + text, + hasSvg = false, + disabled = false, + ariaDisabled, +} ) => { + const button = document.createElement( 'button' ); + button.className = 'components-toolbar-button'; + button.textContent = text; + button.disabled = disabled; + + if ( ariaDisabled !== undefined ) { + button.setAttribute( 'aria-disabled', ariaDisabled ); + } + + if ( hasSvg ) { + button.appendChild( + document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ) + ); + } + + return button; +}; + +describe( 'isIconLibraryToolbarButton', () => { + it( 'matches core/icon Choose icon and Replace controls', () => { + expect( + isIconLibraryToolbarButton( + createButton( { text: 'Choose icon' } ) + ) + ).toBe( true ); + expect( + isIconLibraryToolbarButton( createButton( { text: 'Replace' } ) ) + ).toBe( true ); + } ); + + it( 'does not match the parent Remove icon control', () => { + expect( + isIconLibraryToolbarButton( + createButton( { text: 'Remove icon' } ) + ) + ).toBe( false ); + } ); + + it( 'ignores icon-only and disabled controls', () => { + expect( + isIconLibraryToolbarButton( + createButton( { text: 'Replace', hasSvg: true } ) + ) + ).toBe( false ); + expect( + isIconLibraryToolbarButton( + createButton( { text: 'Replace', disabled: true } ) + ) + ).toBe( false ); + expect( + isIconLibraryToolbarButton( + createButton( { text: 'Replace', ariaDisabled: 'true' } ) + ) + ).toBe( false ); + } ); +} );