SVG Knockout text styling issue: unexplained cropped bottom - javascript

I've tried to build a react component for knockout text and I run into a styling issue I can't explain to myself.
Here is my attempt.
const styles = {
container: {
backgroundSize: "cover",
backgroundImage: "url(http://brokensquare.com/Code/assets/landscape.jpg)",
padding: "20% 20%"
},
knockout: {
borderRadius: 200,
overflow: "hidden"
}
};
const Knockout = ({ text, style }) => {
const s = style || {};
return (
<div style={styles.knockout}>
<svg viewBox="0 0 200 25">
<rect
fill={s.backgroundColor || "rgba(0,0,0,0.6)"}
x="0"
y="0"
width="100%"
height="100%"
mask="url(#knockout-text)"
/>
<mask id="knockout-text">
<rect fill="#fff" x="0" y="0" width="100%" height="100%" />
<text y="70%" fill="#000" textAnchor="middle" x="50%">
{text}
</text>
</mask>
</svg>
</div>
);
};
const App = Radium(() => (
<div>
<div style={styles.container}>
<Knockout style={{}} text={"VERY INSPIRATION"} />
</div>
</div>
));
As you can see the bottom is cropped for some reason, instead of having the side completely rounded, drawing half a circle. Can anyone here see why and how I could fix this ? Thanks.

add this css, it will work
svg {
display: block
}
you can check below codesandbox
https://codesandbox.io/s/71qxyx6m86
I added styles as below
render(
<div>
<App />
<Style
rules={{
"*": {
margin: 0,
padding: 0,
boxSizing: "border-box"
},
svg: {
display: "block"
}
}}
/>
</div>,
document.getElementById("root")

Related

How can I simulate a click on Highchart

This is my DashboardPage component:
export const DashboardPage = (props) => {
const [mounted, setMounted] = useState(false);
const { index } = props;
let navigate = useNavigate();
useEffect(() => {
if (mounted === false) {
setMounted(true);
index();
}
}, [mounted, index]);
const optionNUsersPerCompany = {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Utenti per azienda'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
},
accessibility: {
point: {
valueSuffix: '%'
}
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.1f} %'
},
}
},
series: [{
name: 'Utenti',
colorByPoint: true,
data: getDataUsersPerCompany(props.items),
point: {
events: {
click: (e) => {
console.log(e);
props.setCompanyIdInUsersStore(e.target.point.idCompany);
return navigate(PATH_USERS);
}
}
}
}]
}
return (
<Chart options={optionNUsersPerCompany} />
)
}
const mapDispatchToProps = (dispatch) => {
return {
index: () => dispatch(statsActions.index()),
setCompanyIdInUsersStore: (id) => dispatch(companiesActions.setCompanyIdInUsersStore(id)),
};
}
const mapStateToProps = (state) => {
return {
items: state.stats.items,
}
};
export default connect(mapStateToProps, mapDispatchToProps)(DashboardPage);
And this is the simple Highchart Component:
import React, { useState } from "react";
import HighchartsReact from "highcharts-react-official";
import Highcharts from 'highcharts';
export default React.memo(function Chart(props) {
const [currentOptions, setCurrentOptions] = useState({});
/**
* Questo controllo serve a non re-draware per due volte di seguito i containers
*/
if (JSON.stringify(props.options) !== JSON.stringify(currentOptions)) {
setCurrentOptions(props.options);
}
return (
<HighchartsReact
highcharts={Highcharts}
options={currentOptions}
immutable={true}
/>
);
});
I want to test the click on plotOptions with Jest in React Highcharts. This is the resulting HTML page from Jest:
<div>
<div
data-highcharts-chart="1"
style="overflow: hidden;"
>
<div
class="highcharts-container "
dir="ltr"
id="highcharts-r3a94y8-149"
style="position: relative; overflow: hidden; width: 600px; height: 400px; text-align: left; line-height: normal; z-index: 0; user-select: none; outline: none;"
>
<svg
class="highcharts-root"
height="400"
style="font-family: \"Lucida Grande\", \"Lucida Sans Unicode\", Arial, Helvetica, sans-serif; font-size: 12px;"
version="1.1"
viewBox="0 0 600 400"
width="600"
xmlns="http://www.w3.org/2000/svg"
>
<desc>
Created with Highcharts 10.1.0
</desc>
<defs>
<clippath
id="highcharts-r3a94y8-151-"
>
<rect
fill="none"
height="375"
width="580"
x="0"
y="0"
/>
</clippath>
</defs>
<rect
class="highcharts-background"
fill="#ffffff"
height="400"
rx="0"
ry="0"
width="600"
x="0"
y="0"
/>
<rect
class="highcharts-plot-background"
fill="none"
height="375"
width="580"
x="10"
y="10"
/>
<rect
class="highcharts-plot-border"
data-z-index="1"
fill="none"
height="375"
width="580"
x="10"
y="10"
/>
<g
class="highcharts-series-group"
data-z-index="3"
>
<g
class="highcharts-series highcharts-series-0 highcharts-pie-series highcharts-tracker"
data-z-index="0.1"
opacity="1"
style="cursor: pointer;"
transform="translate(10,10) scale(1 1)"
>
<path
class="highcharts-point highcharts-color-0"
d="M 290 175 A 0 0 0 0 1 290 175 L 290 175 A 0 0 0 0 0 290 175 Z"
fill="#7cb5ec"
opacity="1"
stroke="#ffffff"
stroke-linejoin="round"
stroke-width="1"
transform="translate(0,0)"
/>
</g>
<g
class="highcharts-markers highcharts-series-0 highcharts-pie-series"
data-z-index="0.1"
opacity="1"
transform="translate(10,10) scale(1 1)"
/>
</g>
<text
class="highcharts-title"
data-z-index="4"
style="color: rgb(51, 51, 51); font-size: 18px; fill: #333333;"
text-anchor="middle"
x="300"
y="24"
>
Utenti per azienda
</text>
<text
class="highcharts-subtitle"
data-z-index="4"
style="color: rgb(102, 102, 102); fill: #666666;"
text-anchor="middle"
x="300"
y="10"
/>
<text
class="highcharts-caption"
data-z-index="4"
style="color: rgb(102, 102, 102); fill: #666666;"
text-anchor="start"
x="10"
y="397"
/>
<g
class="highcharts-data-labels highcharts-series-0 highcharts-pie-series highcharts-tracker"
data-z-index="6"
opacity="0"
style="cursor: pointer;"
transform="translate(10,10) scale(1 1)"
>
<path
class="highcharts-data-label-connector highcharts-color-0"
d="M 295 370 C 290 370 290 352 290 346 L 290 340"
fill="none"
stroke="#7cb5ec"
stroke-width="1"
/>
<g
class="highcharts-label highcharts-data-label highcharts-data-label-color-0"
data-z-index="1"
style="cursor: pointer;"
transform="translate(300,360)"
>
<text
data-z-index="1"
style="color: rgb(0, 0, 0); font-size: 11px; font-weight: bold; fill: #000000;"
x="5"
y="16"
>
<tspan
style="font-weight: bold;"
>
acme
</tspan>
: 100.0 %
</text>
</g>
</g>
<g
class="highcharts-legend highcharts-no-tooltip"
data-z-index="7"
>
<rect
class="highcharts-legend-box"
fill="none"
height="8"
rx="0"
ry="0"
visibility="hidden"
width="8"
x="0"
y="0"
/>
<g
data-z-index="1"
>
<g />
</g>
</g>
<text
class="highcharts-credits"
data-z-index="8"
style="cursor: pointer; color: rgb(153, 153, 153); font-size: 9px; fill: #999999;"
text-anchor="end"
x="590"
y="395"
>
Highcharts.com
</text>
</svg>
</div>
</div>
</div>
I'm thinking of get the g element and click on it
render(<BrowserRouter><DashboardPage store={store} /></BrowserRouter>);
const company = screen.getAllByRole('g');
but I got:
TestingLibraryElementError: Unable to find an accessible element with the role "g"
There are no accessible roles. But there might be some inaccessible roles. If you wish to access them, then set the `hidden` option to `true`. Learn more about this here: https://testing-library.com/docs/dom-testing-library/api-queries#byrole
I'm trying to get element by class name:
it('Can handle handleOnUserClick on Chart', async () => {
let initialState = {
stats: {
items: {
nUsersPerCompany: [
{
company: "demolitori",
qty: 5,
id: 16
}
]
}
},
setCompanyIdInUsersStore: () => {},
};
let store = mockStore(initialState);
const { container } = render(<BrowserRouter><DashboardPage store={store} /></BrowserRouter>);
// This console.log outputs the previous HTML code
console.log(prettyDOM(container));
const company = await container.getElementsByClassName("highcharts-label");
console.log(company);
fireEvent.click(company);
});
But with last console.log(company) I get an empty HTML collection:
HTMLCollection {}
I tried also to remove the async/await mode without luck.
I tried to use:
const {container} = render(<BrowserRouter><DashboardPage store={store} /></BrowserRouter>);
console.log(prettyDOM(container))
const path = container.getElementsByClassName("highcharts-color-0");
console.log(prettyDOM(path[0]))
fireEvent.click(path[0]);
The test itself works, but coverage complain with missing the arrow function in click e
point: {
events: {
click: (e) => {
props.setCompanyIdInUsersStore(e.target.point.idCompany);
return navigate(PATH_USERS);
}
}
}
The coverage statistic is correct, as Highcharts render the elements using SVG and other components (path, rect etc.) that do not support click events simulation by DOM. If you replace click with mouseOver, your test coverage would hit 100%, as it is supported.
Highcharts team might be doing some custom implementation to achieve click handlers and we need to utilize that to solve your use case. #ppotaczek pointed out the correct approach.
Code modifications:
Assume DashboardPage component accepts an optional prop, onAfterChartCreated.
It passes that prop over to Chart component as value of callback.
import { useState, useEffect, useRef } from "react";
import {useNavigate} from 'react-router-dom';
import Chart from "./Chart";
import {connect} from 'react-redux';
export const DashboardPage = (props) => {
// previous code
return (
<Chart options={optionNUsersPerCompany} callback={props.onAfterChartCreated} />
)
}
// follow up code
export default connect(mapStateToProps, mapDispatchToProps)(DashboardPage);
Chart component should be modified to:
import React, { useState } from "react";
import HighchartsReact from "highcharts-react-official";
import Highcharts from 'highcharts';
export default React.memo(function Chart(props) {
const [currentOptions, setCurrentOptions] = useState({});
/**
* Questo controllo serve a non re-draware per due volte di seguito i containers
*/
if (JSON.stringify(props.options) !== JSON.stringify(currentOptions)) {
setCurrentOptions(props.options);
}
return (
<HighchartsReact
highcharts={Highcharts}
options={currentOptions}
immutable={true}
callback={props.callback}
/>
);
});
callback is a prop on HighCharts react wrapper, that executes when chart is rendered and returns an instance of the chart. You could refer to their docs for more info or check out How do i access highcharts api after component renders?
Now we modify the test case for DashboardPage component as follows:
const afterChartCreatedCallback = (chart) => {
// We can now trigger click on any data point using Highcharts API
chart.series[0].data[0].firePointEvent('click');
}
render(<DashboardPage onAfterChartCreated={afterChartCreatedCallback} />);
So, we only use the callback in the test, to improve the coverage. This should increase your DashboardPage component coverage to 100%.

React Native SVG Image not loading

I created an svg. And want to re-create it in React-Native. I use this npm package for it (https://www.npmjs.com/package/react-native-svg#image).
Here is the SVG
What i created is this
<TestApp
imageWidth={2300}
imageHeight={1438}
width={width}
url={'./url/to/picture'}
scale1={0.00100794}
scale2={0.000666667}
/>
const TestApp = ({
width,
overlayColor = 'black',
overlayOpacity = '0.3',
translate1,
translate2,
scale1,
scale2,
imageWidth,
imageHeight,
url,
}) => {
const height = (254 / 202) * width;
const translate = translate1
? `translate(${translate1} ${translate2 ? translate2 : ''})`
: '';
const scale = scale1 ? `scale(${scale1} ${scale2 ? scale2 : ''})` : '';
return (
<Svg
width={width}
height={height}
viewBox={`0 0 ${202} ${254}`}
fill="none"
>
<Mask
id="breathMask"
mask-type="alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width={width}
height={height}
>
<Path
d="M0 0H201.374V228.761C201.374 228.761 158.683 254 100.687 254C42.6913 254 0 228.761 0 228.761V0Z"
fill="#C4C4C4"
/>
</Mask>
<G mask="url(#breathMask)">
<Rect
x="1"
width={width}
height={(303 / 254) * height}
fill="url(#pattern0)"
/>
<Path
d="M0 0H201.374V228.761C201.374 228.761 158.683 254 100.687 254C42.6913 254 0 228.761 0 228.761V0Z"
fill={overlayColor}
fillOpacity={overlayOpacity}
/>
</G>
<Defs>
<Pattern
id="pattern0"
patternContentUnits="objectBoundingBox"
width="1"
height="1"
>
<Use href="#image0" transform={`${translate} ${scale}`} />
</Pattern>
<Image
id="image0"
widht={imageWidth}
height={imageHeight}
href="../path/to/url"
/>
</Defs>
</Svg>
);
};
Basically I copied the svg as it is.
The problem, the picture is not loading, even If I put a public url, such as this in it.
The svg with a grey background is not rendering. The picture will render in webview, though but not in ios or android.
You got any ideas how to fix it? Or a complete other way to achieve something like this?
Probably easiest way would be to just create a curved picture in photoshop, but thats not really dynamic.
Have you tried the using other parameters?
<Image
style={{ width: imageWidth, height: imageHeight }}
source={{
uri: '../path/to/url',
}}
/>

React SVG style element on hover

I have an array of squares inside SVG-based React component and I need to apply custom style (fill color set to purple) to square under cursor on hover.
I tried that with both CSS :hover and onMouseOver events, neither did work.
Any clues are much appreciated
const { render } = ReactDOM,
rootNode = document.getElementById('root')
const Matrix = ({m, n}) => (
<svg
viewBox={`0 0 ${m*10+10} ${n*10+10}`} xmlns="http://www.w3.org/2000/svg"
>
<defs>
<rect
id="cell"
width="5"
height="5"
className="cell"
onMouseOver={({target}) => target.style.cssText="fill:purple"}
/>
</defs>
<g>
{
Array(n).fill().map((row, rowIdx) => (
<g key={rowIdx}>
{
Array(m).fill().map((col, colIdx) => (
<g key={colIdx}>
<use
x={5+colIdx*5}
y={5+rowIdx*5}
xlinkHref="#cell"
fill="lightgray"
stroke="white"
strokeWidth=".4"
/>
</g>
))
}
</g>
))
}
</g>
</svg>
)
render (
<Matrix m={10} n={10} />,
rootNode
)
#cell:hover {
fill: purple;
}
.cell:hover {
fill: purple;
}
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script></script><div id="root"></div>
If you have to color each square you must have to use your class .cell inside the <use /> of each cell:
<use
className="cell"
x={5+colIdx*5}
y={5+rowIdx*5}
xlinkHref="#cell"
fill="grey"
stroke="white"
strokeWidth=".4"
/>
And now you can use your css style:
.cell:hover {
fill: purple;
}
Snippet:
const { render } = ReactDOM,
rootNode = document.getElementById('root')
const Matrix = ({m, n}) => (
<svg
viewBox={`0 0 ${m*10+10} ${n*10+10}`} xmlns="http://www.w3.org/2000/svg"
>
<defs>
<rect
id="cell"
width="5"
height="5"
/>
</defs>
<g>
{
Array(n).fill().map((row, rowIdx) => (
<g key={rowIdx}>
{
Array(m).fill().map((col, colIdx) => (
<g key={colIdx}>
<use
className="cell"
x={5+colIdx*5}
y={5+rowIdx*5}
xlinkHref="#cell"
fill="grey"
stroke="white"
strokeWidth=".4"
/>
</g>
))
}
</g>
))
}
</g>
</svg>
)
render (
<Matrix m={10} n={10} />,
rootNode
)
.cell:hover {
fill: purple;
}
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script></script><div id="root"></div>

What should I use to show live icons in React OR Javascript? Like how much coffee is filled in a mug?

I want to create dashboard with lots of live icons. Live icons means icons content will change dynamically.
For example:- Let say i have an icon of a coffee cup, based upon number of coffee sold the coffee level in cup goes up.
I tried solving it using SVG & CSS. Below is the live code-sandbox DEMO example. (same code I am posting below)
https://codesandbox.io/s/fill-coffee-to-svg-1sfb0
what exactly I am doing is on top of SVG component i using div node and applying css on the div tag.
Issue is that, It is very difficult to manage path/points dynamically in SVG, also for different screen version it will be difficult.
Can anyone suggest any better approach for this.
import ReactDOM from "react-dom";
import React, { Component } from "react";
class Todo extends Component {
state = { height: 4 };
setHeight = (e, fillType) => {
e.preventDefault();
console.log(this.state.height);
if (this.state.height < 100) {
if (fillType === "fill") {
this.setState({
height: this.state.height + 2
});
}
}
if (this.state.height >= 2) {
if (fillType === "drink") {
this.setState({
height: this.state.height - 2
});
}
}
};
render() {
return (
<div>
<svg width="255" height="224" xmlns="http://www.w3.org/2000/svg">
<g>
<rect
fill="#fff"
id="canvas_background"
height="226"
width="257"
y="-1"
x="-1"
/>
<g
display="none"
overflow="visible"
y="0"
x="0"
height="100%"
width="100%"
id="canvasGrid"
>
<rect
fill="url(#gridpattern)"
stroke-width="0"
y="0"
x="0"
height="100%"
width="100%"
/>
</g>
</g>
<g>
<title>Layer 1</title>
<path
stroke="#000"
id="svg_1"
d="m78.82963,176.75921l97.93778,0c14.11708,-0.03733 23.74788,-17.00704 23.70086,-34.46505l0,-11.73873c27.94999,-0.03136 48.22814,-30.02253 48.21769,-64.99381c0.01045,-35.59398 -19.86965,-64.83701 -43.00946,-64.81162l-150.95938,0l0,141.54714c0.02194,19.22158 11.60543,34.42772 24.1125,34.46207zm121.63551,-149.38391l0,0l5.20823,0c19.14875,-0.04331 25.29102,25.83983 25.19908,38.38045c0.01881,20.24897 -10.47393,39.66916 -30.40731,37.78463l0,-76.16508zm-199.71514,158.00316c0.01776,26.16387 13.9729,38.29683 25.20535,38.37149l202.59351,0c13.39827,-0.07466 25.14161,-15.13147 25.20117,-38.37149l-253.00002,0z"
stroke-width="1.5"
fill="#fff"
/>
</g>
</svg>
<div
style={{
position: "absolute",
left: "63px",
top: "9px",
height: "175px",
width: "145px",
borderBottomLeftRadius: "25px",
borderBottomRightRadius: "25px",
overflow: "auto"
}}
>
<div
style={{
height: this.state.height + "%",
width: "100%",
position: "absolute",
bottom: "0",
zIndex: 1000,
background: "green",
transition: "all .4s"
}}
/>
</div>
<button onClick={e => this.setHeight(e, "fill")}>
fill more cofee +
</button>
<button onClick={e => this.setHeight(e, "drink")}>drink cofee -</button>
</div>
);
}
}
ReactDOM.render(<Todo />, document.getElementById("root"));
Have you tried using the react-spring library?
In the basics section of react-spring documentation (can be found here - https://www.react-spring.io/docs/hooks/basics) this code snippet is given:
const props = useSpring({ x: 100, from: { x: 0 } })
return (
<animated.svg strokeDashoffset={props.x}>
<path d="..." />
</animated.svg>
)
Using your example of your coffee mug, if you would render your mug separate to the coffee filling the mug, with the coffee stroke being upwards, then you could use this technique to animate the coffee height.
Edit:
As you are using a class component you will want to use the render props API for this - https://www.react-spring.io/docs/props/spring
<Spring
from={{ x: 100 }}
to={{ x: 0 }}>
{props => (
<svg strokeDashoffset={props.x}>
<path d="..." />
</svg>
)}
</Spring>

How to mask user image in SVG hexagon shape on react native

I am struggling with hexagon shape on react native can anyone have some idea about how does it work using svg in react native or any alternate way?
creating shapes using js
image cutout of hexagon
masking image[enter image description here][1]
I'm trying this below image.
https://i.stack.imgur.com/MLDFl.jpg
This is My code:
render() {
return (
<Svg
height="300"
width="300"
viewBox="0 0 860 860"
>
<Defs>
<ClipPath id="clip">
<Polygon
strokeWidth="2"
stroke="#979797"
strokeDasharray='8,8'
strokeLinecap='butt'
fill="rgba(0, 0, 0, 0.5)"
points="258.5,223.999 130.5,298 2.5,224 2.5,76 130.5,2 258.5,76 " />
</ClipPath>
</Defs>
<Image
x="0%"
y="0%"
width="100%"
height="100%"
preserveAspectRatio="xMidYMid slice"
opacity="0.5"
href={require('./assets/Image.jpg')}
clipPath="url(#clip)"
/>
</Svg>
);
}
This kind of image can be created with my react-native-image-filter-kit module:
import { Image } from 'react-native'
import { DstATopComposition } from 'react-native-image-filter-kit'
const style = { width: 320, height: 320 }
const masked = (
<DstATopComposition
dstImage={
<Image
style={style}
source={{ uri: 'https://i.stack.imgur.com/MLDFl.jpg' }}
/>
}
srcImage={
<Image
style={style}
source={{ uri: 'http://www.myiconfinder.com/uploads/iconsets/256-256-53d5151ca4f467ed9951f85024881c85.png' }}
/>
}
/>
)
Note that shape generation is not supported currently, so you need to use another image for masking.

Categories

Resources