How to get Material UI Icon SVG path? React Icon Picker - reactjs

I have a react application using Material UI's Icons. The main objective is to have an Icon Picker, where a user can select a MUI Icon and that selected icon's SVG path is saved within the state.
This svg path will then be saved out to an API where it can be displayed in various places. etc.
I've searched through documentation on MUI's site regarding icons, but it's all about implementation, which I can do just fine. I've looked for an npm package, without much luck.
I did come across this package (https://github.com/DMDc0de/material-ui-icon-picker), which is essentially what I'd like the picker to be - but it outputs the icon's name for an icon component <i />. Not what I want. I need the source of the SVG path.
Any direction towards this would be super helpful.

Go to the icon site: https://material.io/tools/icons/
Click on an icon
Click on "Selected icon" button (bottom left)
Click on the "SVG" button to download the SVG version
Alternatively, go to the GitHub repo and download the SVGs there.

One way of doing that programmatically is to load the component and to render it in a string. Then to extract the path from the string.
To do so, we can use the renderToString or renderToMarkupString method of ReactDomServer.
Than we can extract the path from the generated string. We can either parse the svg XML with the DOMParser or with a regexp.
Here's an example in TypeScript:
import EditIcon from '#material-ui/icons/Edit';
import ReactDOMServer from 'react-dom/server';
export function getEditIconPath(): string {
const iconString = ReactDOMServer.renderToString(<EditIcon />);
const parser = new DOMParser();
const svgDoc = parser.parseFromString(iconString, 'image/svg+xml');
const iconPath = svgDoc.querySelector('path')?.getAttribute('d') as string;
return iconPath;
}
Another way to achieve that would be to use the React Test Renderer, thus we can get directly a json including the different properties (including the path). However, it looks like this method is around 10 times slower than the previous method.
Here's an example with the second method:
import EditIcon from '#material-ui/icons/Edit';
import TestRenderer from 'react-test-renderer'; // ES6
export function getEditIconPath(): string {
const iconComponent = TestRenderer.create(<EditIcon />);
const iconJson = iconComponent.toJSON();
const path = iconJson.children[0].props.d;
return path;
}

Related

How to insert JSX in Storybook control

Running Storybook I'd like to navigate to my component and play with Docs tab and check its behavior as I change control value for each property. I implemented a component Footer that could receive string | JSX.Element | React.FunctionComponent types (I'm using TypeScript along ReactJS).
Unfortunately, when I type <div>my jsx</div> inside control field, a red border comes up pointing error and doesn't update the component in the preview as expected.
This is the screen I'm trying to insert into control field:
In the .stories.js file I have at the end:
export const Default = Template.bind({});
Default.args = {
subscribe: "Replace me by the Subscribe component of Design System.",
brand: <figure>Put branding logo here!</figure>,
links: <div><div>first column of links</div><div>second column of links</div></div>,
bottom: "All rights reserved."
}
The Links text is the default value for link property (from the actual React component file). As the code and image above can show us, it seems the JSX argument passed on Default.args in .stories.js file is completely ignored.
I'd like to insert a JSX into control field of storybook playground and then get Footer component live updated with JSX component rendered in the preview. How can I achieve that?

How can i write a test using React Testing Library to check if hover changes the components style?

I am testing my application, and encountered a problem. When trying to test whether a row in my Dropdown component applies an effect on hover, I noticed I was not able to check elements for their background color, which I find odd.
Trying to use the jest-dom matcher "toHaveStyle()", the following is an example where I cannot for the life of me get it to work.
dropdown.test.tsx
test('Should contain clickable elements that change style when hovered', () => {
const dropElement1 = screen.getByLabelText('testLabel1');
expect(dropElement1).toHaveStyle('background: white');
});
Error
I have also tried this by using 'background-color', by using the hex value (another interesting bug is that PrettyDom converts hex to RGB), or by adding ; to the declaration in toHaveStyle().
I am certain that the element is indeed white, and I can't understand what is going wrong. If my approach is bad practice and you have a better idea of how to check this, or you have a solution to my problem, please, let me know!
Your testing case can't find the dropElement1 styles because it's a drop-down menu and not opened since you just render the Dropdonw component.
You need to simulate a mouse hover or clicking action on the DropDown menu and then expect to have styles property for it.
import React from "react";
import { render, screen, fireEvent, waitFor } from "#testing-library/react";
import { Dropdown } from "./Dropdown";
test('Should contain clickable elements that change style when hovered', async () => {
render(<Dropdown />);
fireEvent.mouseEnter(screen.getByText('drop-down-btn'));
await waitFor(() => screen.getByTestId('dropdown-menu'))
expect(screen.getByLabelText('testLabel1')).toHaveStyle('background: white');
});
Note: as you have not posted the Dropdown component, I put some sample names for getting your toggles and drop-down menu. also, you can read about the mouse events on the react-testing-library. you can also use mouseOver but it depends on your drop-down menu implementation.

React Tabulator - How to display table horizontally

I'm using react-tabulator for a component: http://tabulator.info/docs/4.0/frameworks
I have put the component on the page in my app but am struggling to do anything with the styling. Right now, the component just displays everything vertically and looks really bad:
I want to display this horizontally in something that looks like a normal tabular format. I would also like to change column width. I've found limited documentation examples. Someone did ask a similar question and in this StackOverflow thread: How to style react-tabulator table? but I've not been able to edit the styles.css stylesheet to do anything useful.
Here is my component code:
import React from 'react'
import { ReactTabulator } from 'react-tabulator'
import 'react-tabulator/lib/styles.css';
const TabularData = (props) => {
const dataArray = []
//gets just first streelights record
for (const [i, v] of props.streetlights.features.entries()) {
if (i < 1) {
dataArray.push(v.properties); // properties of each item is what contains the info about each streetlight
}
}
let columns = [
{title:"WORKLOCATI", field:"WORKLOCATI"},
{title:"WORKREQUES", field:"WORKREQUES"},
{title:"WORK_EFFEC", field:"WORK_EFFEC"},
{title:"WORK_REQUE", field:"WORK_REQUE"},
]
return (
<ReactTabulator
columns={columns}
layout={"fitData"}
data={dataArray}
/>
)
}
export default TabularData
The css in react-tabulator/lib/styles.css is just the most base-level css.
Try importing one of the pre-built themes:
import "react-tabulator/css/bootstrap/tabulator_bootstrap.min.css";
There are a whole bunch of them in the css folder, and you can use them as a basis for creating your own.
Minimum working example here.
To get the right styling you will also have to import tabulator.min.css in your module, which is the theme, according to here.
Your imports should look like this:
import { ReactTabulator } from 'react-tabulator'
import 'react-tabulator/lib/styles.css';
import 'react-tabulator/lib/css/tabulator.min.css'; // theme
Without it, it looks like the image you posted:
With it, it looks like this:
In the folder node_modules/react-tabulator/css you can find more themes.

how do I interpolate a Link component in Next-i18next / React i18next that changes position in the text

Currently I'm using Next.js with Next-i18next for I18N, but I understand that the React/i18next implementation is basically the same.
The problem I'm having is that I need to interpolate a next Link component inside some translation text, but depending on the language (English vs German), the order of the text and the link would change.
For instance the text I'm struggling with is: 'Accept data policy' vs 'Datenschutzerklärung akzeptieren'
As of the moment I have a quick fix by creating two values in the translation JSON files for the text and the link and then swapping the position based on the current language. Obviously this is not a sustainable solution. I have tried to utilise the 'Trans' component but this is showing some unexpected behaviour where the translation only kicks in after the page is refreshed, otherwise you see the text inside the Trans component.
example:
function LinkText({ href, children}) {
return <Link to={href || ''}>{children}</Link>;
}
return (
<Trans i18nKey="sentence">
text before link
<LinkText href="/data-policy">{t("dataPolicy")}</LinkText>
text after link
</Trans>
);
and the JSON in question:
{
"sentence": "agree to our <1><0/></1>",
"dataPolicy": "data policy"
}
Here's a link to CodeSandbox I made to replicate the problem with in React: link
(P.S The implementation of i18next doesn't seem to effectively swap out the languages in Codesandbox at the moment, but I included it as the code is there for a MWE)
Thanks in advance for your help, this has been driving me insane.
You had few missing parts,
Your i18next config was lack of a way to fetch the locale files, I've added i18next-http-backend.
You should use Trans component to inject the link to the sentence.
Your locale json should look like this:
{
"sentence": "Accept <0>data policy</0>"
}
// TranslatedLink.js
import React from 'react';
import { useTranslation, Trans } from 'react-i18next';
import { Link } from 'react-router-dom';
function LinkText({ href, children }) {
return <Link to={href || ''}>{children}</Link>;
}
export default function TranslatedLink() {
const { t } = useTranslation(['common']);
return (
<div style={{ padding: 50 }}>
<Trans i18nKey="sentence" t={t} components={[<LinkText href="/data-policy" />]} />
</div>
);
}
A working example: https://codesandbox.io/s/react-i18n-interpolation-issue-forked-ck8l4

React color ChromePicker alpha slider does not work. Is required use Alpha individual API?

Im trying to implement react-color from https://casesandberg.github.io/react-color/#examples
I'm using the ChromePicker component, but the Alpha slider is not working, as expected.
The slider bar is working, but the slider bullet is not moving on.
Here is the https://codesandbox.io/s/react-color-nkh7w example. Is there any component missing?
The problem is that react-color components (like the ChromePicker you are using) do not accept properties for alpha/opacity directly. You have two options:
Using the color.rgb value only, and don't separate the alpha. If you pass the rgb object as the color property), it should work as expected.
You can take the color.rgb.a value, convert it to hex, and append it to the color.hex string.
color.hex doesn't contain the alpha value. you can you color.rgba instead, and also you can convert it to hex with rgb-hex package to make it displayable:
import { ChromePicker } from "react-color";
import rgbHex from "rgb-hex";
const App = () => {
const [color, setColor] = useState("#fff");
return <>
<ChromePicker
color={color}
onChange={c =>
setColor("#" + rgbHex(c.rgb.r, c.rgb.g, c.rgb.b, c.rgb.a))
}
/>
<p>You picked {color}</p>
</>
}

Resources