diff --git a/CHANGELOG.md b/CHANGELOG.md index cf638d4d155..5d70cffa87b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## [`master`](https://github.com/elastic/eui/tree/master) -No public interface changes since `38.1.0`. +**Bug fixes** + +- Fixed logo icons with static SVG IDs causing accessibility errors when multiples of the same logo were present ([#5204](https://github.com/elastic/eui/pull/5204)) ## [`38.1.0`](https://github.com/elastic/eui/tree/v38.1.0) diff --git a/scripts/compile-icons.js b/scripts/compile-icons.js index a42ef619eb9..f1f84046439 100644 --- a/scripts/compile-icons.js +++ b/scripts/compile-icons.js @@ -16,21 +16,24 @@ function pascalCase(x) { const iconFiles = glob.sync('**/*.svg', { cwd: iconsDir, realpath: true }); iconFiles.forEach(async (filePath) => { + const fileName = path.basename(filePath, '.svg'); const svgSource = fs.readFileSync(filePath); + const svgString = svgSource.toString(); try { - const viewBoxPosition = svgSource.toString().indexOf('viewBox'); - if (viewBoxPosition === -1) { + if (!svgString.includes('viewBox')) { throw new Error(`${filePath} is missing a 'viewBox' attribute`); } - const jsxSource = await svgr( + const hasIds = svgString.includes('id="'); + + let jsxSource = await svgr( svgSource, { plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'], svgoConfig: { plugins: [ - { cleanupIDs: false }, + { cleanupIDs: true }, { prefixIds: false }, { removeViewBox: false }, ], @@ -43,17 +46,38 @@ iconFiles.forEach(async (filePath) => { { template }, opts, { imports, componentName, props, jsx } - ) => template.ast` + ) => + hasIds + ? template.ast` +${imports} +import { htmlIdGenerator } from '../../../services'; +const ${componentName} = (${props}) => { + const generateId = htmlIdGenerator('${fileName}'); + return ( + ${jsx} + ); +}; +export const icon = ${componentName}; +` + : template.ast` ${imports} const ${componentName} = (${props}) => ${jsx} export const icon = ${componentName}; - `, +`, }, { - componentName: `EuiIcon${pascalCase(path.basename(filePath, '.svg'))}`, + componentName: `EuiIcon${pascalCase(fileName)}`, } ); + // Replace static SVGs IDs with dynamic JSX that uses the htmlIdGenerator + if (hasIds) { + jsxSource = jsxSource + .replace(/id="(\S+)"/gi, "id={generateId('$1')}") + .replace(/"url\(#(\S+)\)"/gi, "{`url(#${generateId('$1')})`}") + .replace(/xlinkHref="#(\S+)"/gi, "xlinkHref={`#${generateId('$1')}`}"); + } + const outputFilePath = filePath.replace(/\.svg$/, '.js'); const comment = '// THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY\n\n'; fs.writeFileSync(outputFilePath, comment + jsxSource); diff --git a/src/components/icon/__snapshots__/icon.test.tsx.snap b/src/components/icon/__snapshots__/icon.test.tsx.snap index da324d45aee..4533372f50d 100644 --- a/src/components/icon/__snapshots__/icon.test.tsx.snap +++ b/src/components/icon/__snapshots__/icon.test.tsx.snap @@ -3761,7 +3761,7 @@ exports[`EuiIcon props type logoApache is rendered 1`] = ` > @@ -4255,11 +4255,11 @@ exports[`EuiIcon props type logoDropwizard is rendered 1`] = ` > @@ -4911,7 +4911,7 @@ exports[`EuiIcon props type logoHAproxy is rendered 1`] = ` xmlns="http://www.w3.org/2000/svg" > @@ -5321,7 +5321,7 @@ exports[`EuiIcon props type logoMemcached is rendered 1`] = ` cy="42.708%" fx="41.406%" fy="42.708%" - id="logo_memcached-c" + id="logo_memcached_generated-id_c" r="0%" > `; diff --git a/src/components/icon/assets/logo_apache.js b/src/components/icon/assets/logo_apache.js index 746c8c74ce5..dc155aa00b3 100644 --- a/src/components/icon/assets/logo_apache.js +++ b/src/components/icon/assets/logo_apache.js @@ -1,140 +1,144 @@ // THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY import * as React from 'react'; +import { htmlIdGenerator } from '../../../services'; -const EuiIconLogoApache = ({ title, titleId, ...props }) => ( - - {title ? {title} : null} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -); +const EuiIconLogoApache = ({ title, titleId, ...props }) => { + const generateId = htmlIdGenerator('logo_apache'); + return ( + + {title ? {title} : null} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; export const icon = EuiIconLogoApache; diff --git a/src/components/icon/assets/logo_dropwizard.js b/src/components/icon/assets/logo_dropwizard.js index 472e28bf7fe..7c9bbbcfad3 100644 --- a/src/components/icon/assets/logo_dropwizard.js +++ b/src/components/icon/assets/logo_dropwizard.js @@ -1,67 +1,71 @@ // THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY import * as React from 'react'; +import { htmlIdGenerator } from '../../../services'; -const EuiIconLogoDropwizard = ({ title, titleId, ...props }) => ( - - {title ? {title} : null} - - - - - - - - - - - - - - - - - - - - -); +const EuiIconLogoDropwizard = ({ title, titleId, ...props }) => { + const generateId = htmlIdGenerator('logo_dropwizard'); + return ( + + {title ? {title} : null} + + + + + + + + + + + + + + + + + + + + + ); +}; export const icon = EuiIconLogoDropwizard; diff --git a/src/components/icon/assets/logo_dropwizard.svg b/src/components/icon/assets/logo_dropwizard.svg index c62372fa8e8..5bd368ec9bf 100644 --- a/src/components/icon/assets/logo_dropwizard.svg +++ b/src/components/icon/assets/logo_dropwizard.svg @@ -1,17 +1,17 @@ - - + + - + - + diff --git a/src/components/icon/assets/logo_gcp.js b/src/components/icon/assets/logo_gcp.js index ba76bf6f2f9..9ecd296a4c5 100644 --- a/src/components/icon/assets/logo_gcp.js +++ b/src/components/icon/assets/logo_gcp.js @@ -1,58 +1,62 @@ // THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY import * as React from 'react'; +import { htmlIdGenerator } from '../../../services'; -const EuiIconLogoGcp = ({ title, titleId, ...props }) => ( - - {title ? {title} : null} - - - - - - - - - - - - +const EuiIconLogoGcp = ({ title, titleId, ...props }) => { + const generateId = htmlIdGenerator('logo_gcp'); + return ( + + {title ? {title} : null} + + + + + + + + + + + + + + - - - -); + + ); +}; export const icon = EuiIconLogoGcp; diff --git a/src/components/icon/assets/logo_google_g.js b/src/components/icon/assets/logo_google_g.js index 17cb77e1909..495c7dcda54 100644 --- a/src/components/icon/assets/logo_google_g.js +++ b/src/components/icon/assets/logo_google_g.js @@ -1,82 +1,86 @@ // THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY import * as React from 'react'; +import { htmlIdGenerator } from '../../../services'; -const EuiIconLogoGoogleG = ({ title, titleId, ...props }) => ( - - {title ? {title} : null} - - - - - - - - - - - +const EuiIconLogoGoogleG = ({ title, titleId, ...props }) => { + const generateId = htmlIdGenerator('logo_google_g'); + return ( + + {title ? {title} : null} + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - -); + + ); +}; export const icon = EuiIconLogoGoogleG; diff --git a/src/components/icon/assets/logo_haproxy.js b/src/components/icon/assets/logo_haproxy.js index d960a45fa92..36370a837dc 100644 --- a/src/components/icon/assets/logo_haproxy.js +++ b/src/components/icon/assets/logo_haproxy.js @@ -1,84 +1,88 @@ // THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY import * as React from 'react'; +import { htmlIdGenerator } from '../../../services'; -const EuiIconLogoHaproxy = ({ title, titleId, ...props }) => ( - - {title ? {title} : null} - - - - - - - - - - - - - - - - - - - - -); +const EuiIconLogoHaproxy = ({ title, titleId, ...props }) => { + const generateId = htmlIdGenerator('logo_haproxy'); + return ( + + {title ? {title} : null} + + + + + + + + + + + + + + + + + + + + + ); +}; export const icon = EuiIconLogoHaproxy; diff --git a/src/components/icon/assets/logo_ibm.js b/src/components/icon/assets/logo_ibm.js index 6923b5c8f8f..634d865974c 100644 --- a/src/components/icon/assets/logo_ibm.js +++ b/src/components/icon/assets/logo_ibm.js @@ -1,106 +1,110 @@ // THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY import * as React from 'react'; +import { htmlIdGenerator } from '../../../services'; -const EuiIconLogoIbm = ({ title, titleId, ...props }) => ( - - {title ? {title} : null} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -); +const EuiIconLogoIbm = ({ title, titleId, ...props }) => { + const generateId = htmlIdGenerator('logo_ibm'); + return ( + + {title ? {title} : null} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; export const icon = EuiIconLogoIbm; diff --git a/src/components/icon/assets/logo_memcached.js b/src/components/icon/assets/logo_memcached.js index 5c3f5138d75..3d7fd7aef4b 100644 --- a/src/components/icon/assets/logo_memcached.js +++ b/src/components/icon/assets/logo_memcached.js @@ -1,84 +1,94 @@ // THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY import * as React from 'react'; +import { htmlIdGenerator } from '../../../services'; -const EuiIconLogoMemcached = ({ title, titleId, ...props }) => ( - - {title ? {title} : null} - - - - - - - - - - - - - - - - - - - - - - - - - - - -); +const EuiIconLogoMemcached = ({ title, titleId, ...props }) => { + const generateId = htmlIdGenerator('logo_memcached'); + return ( + + {title ? {title} : null} + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; export const icon = EuiIconLogoMemcached; diff --git a/src/components/icon/assets/logo_php.js b/src/components/icon/assets/logo_php.js index b2ed53c6b2a..9b52bec56ea 100644 --- a/src/components/icon/assets/logo_php.js +++ b/src/components/icon/assets/logo_php.js @@ -1,89 +1,101 @@ // THIS IS A GENERATED FILE. DO NOT MODIFY MANUALLY import * as React from 'react'; +import { htmlIdGenerator } from '../../../services'; -const EuiIconLogoPhp = ({ title, titleId, ...props }) => ( - - {title ? {title} : null} - - - - - { + const generateId = htmlIdGenerator('logo_php'); + return ( + + {title ? {title} : null} + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - -); + + ); +}; export const icon = EuiIconLogoPhp; diff --git a/src/components/icon/assets/stop_slash.js b/src/components/icon/assets/stop_slash.js index 13d8e89d65e..4352ef68c57 100644 --- a/src/components/icon/assets/stop_slash.js +++ b/src/components/icon/assets/stop_slash.js @@ -12,10 +12,7 @@ const EuiIconStopSlash = ({ title, titleId, ...props }) => ( {...props} > {title ? {title} : null} - + ); diff --git a/src/components/icon/assets/stop_slash.svg b/src/components/icon/assets/stop_slash.svg index 31d24729f97..7ef4b0cf4c7 100644 --- a/src/components/icon/assets/stop_slash.svg +++ b/src/components/icon/assets/stop_slash.svg @@ -1,3 +1,3 @@ - +