user3472810
user3472810

Reputation: 459

Error when downloading SVG files from React application

I have a page of iconCard components that consume icon components. I have added a button to each card to allow downloading the icon for each card on click of a button contained on that card with the 'downloadSVG' function. I see the download happen, and the images are named appropriately (svgFile.svg) and saved into my Downloads folder, but when I open my downloads folder on my machine and click on the svg file, I get this error:

Namespace prefix xlink for href on use is not defined

I have read about the deprecated xlink namespace for href attributes, but when I remove 'xlink' and just use 'href' it doesn't resolve the issue. The path to my icons is as follows:

project/public/assets/icons/[icon-name]/[icon-name.svg]

Here is my code.

import React, { useCallback, useRef } from 'react';
import PropTypes from 'prop-types';

import { CbdsButton, CbdsIcon } from '@cbds/cbds-components-react';
import ButtonInlineCode from '../ButtonInlineCode';


const IconCard = ({ iconName, size, color, library, a11yRole, a11yLabel }) => {

const svgRef = useRef();

const downloadSVG = useCallback(() => {
    const svg = svgRef.current.innerHTML;
    const blob = new Blob([svg], { type: "image/svg+xml" });
    downloadBlob(blob, `${iconName}.svg`);
}, []);

return (
    <div className="cbdsws-c-iconCard">

    <div className="cbdsws-c-iconCard__media" ref={svgRef}>
    <CbdsIcon iconName={iconName} size={size} color={color} library={library} a11yRole={a11yRole} a11yLabel={a11yLabel ? a11yLabel : iconName} />
  </div>

  <div className="cbdsws-c-iconCard__body">
    <div className="cbdsws-c-iconCard__copyBtn cbdsws-c-iconCard__longName">
      <ButtonInlineCode codeSnippet={iconName} type={library === "ui" ? "icon" : "iconBrand"} />
      <br />
      <CbdsButton size="sm" onClick={downloadSVG} variant="ghost" label="Download" />
    </div>
  </div>

</div>
);

function downloadBlob(blob, filename) {
    const objectUrl = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = objectUrl
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    setTimeout(() => URL.revokeObjectURL(objectUrl), 5000);
    }

};

IconCard.propTypes = {
iconName: PropTypes.string.isRequired,
size: PropTypes.oneOf([
    "sm",
    "md",
    "12",
    "16",
    "20",
    "24",
    "28",
    "60",
    "80",
    "120",
    ]),
    color: PropTypes.oneOf(["primary", "light", "dark"]),
    library: PropTypes.oneOf(["ui", "brand"]),
    a11yRole: PropTypes.string,
    a11yLabel: PropTypes.string,
};

IconCard.defaultProps = {
    iconName: "close",
    size: null,
    color: null,
    library: "ui",
    a11yRole: "img",
};

export default IconCard;

And here is an example of one of the SVG files. They're all like this one. I suppose it's possible I may need to add Namespacing to each svg. There are hundreds of them. The ones on the cards are being pulled from 2 sprites, but to download them, I had to save each individual one to a folder.

<svg width="48" height="48" viewBox="0 0 48 48">
    <path fill-rule="evenodd" d="M24 4c10.985 0 20 9.015 20 20s-9.015 20-20 20S4 34.985 4 24 13.015 4 24 4zm0 10c-1.003 0-2 1-2 2v6h-6c-.999 0-2 1-2 2s1 2 2 2h6v6c0 1 1 2 2 2s2-1 2-2v-6h6c1 0 2-1 2-2s-1-2-2-2h-6v-6c0-1.001-.997-2-2-2z"></path>
</svg>

Can anyone please shed some light? Thank you.

Upvotes: 0

Views: 67

Answers (1)

user3472810
user3472810

Reputation: 459

I reworked my code and came up with a much more streamlined solution using the simple HTML 'Download' attribute. This example also includes logic to pull icons from 2 possible libraries.

<ButtonInlineCode 
    codeSnippet={iconName} 
    type={library === "ui" ? "icon" : "iconBrand"} 
/>
        
<br />

<CbdsButton 
    as="a" 
    size="sm" 
    variant="ghost" 
    label="Download" 
    a11yLabel={iconName + "(Download)"} 
    url={`assets/icons/${library === "ui" ? "svg-ui" : "svg-brand"}/${iconName}/${iconName}.svg`} 
    download={`${iconName}.svg`} 
    />

Upvotes: 0

Related Questions