ReactJs - Display components around a component - css

I want to display a specific number of components around a component. How can I do that? :)
With this implementation, I draw 8 Patrat component
{Array.from(Array(8)).map((item, index) =>
(<Patrat key={index}/>)
)}
I want to display these 8 Patrat around a circle which is also a component.

After Understanding the issue and what you want here is the final solution
You can also create function which can dynamically create position and pass it to the Child components
Here is complete code resource link (Click Here)
Also your can experiment with some comment line in code link given above

You could create a recursive loop where you create a new Patrat component with the recursive call as children to it.
Example
function Patrat({ children }) {
return (
<div
style={{
paddingLeft: 10,
backgroundColor: "#" + Math.floor(Math.random() * 16777215).toString(16)
}}
>
{children}
</div>
);
}
function App() {
const content = (function patratLoop(num) {
if (num === 0) {
return <div> Foo </div>;
}
return <Patrat>{patratLoop(--num)}</Patrat>;
})(8);
return content;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

You could make the circle responsible for positioning the elements, then have Patrats as children of circle (Circle > div.circle_element_position > Patrat) or if Patrats will change depending on the parent component, you could use same logic but use renderProps for Patrats (Circle > div.circle_element_position > props.renderPatrat(index))
It would look more or less like this:
function Partat() {
return <div>1</div>
}
function calculatePositions(numberOfPatrats) {
//your implementation
}
function Circle ({numberOfPatrats}) {
let positions = calculatePositions(numberOfPatrats);
return <div className="circle">
{positions.map(
(position, index) => <div style={position} key={index}><Partat /></div>
)}
</div>
}
To place Parats on the positions you want you just need to implement calculatePositions, which will be similar to what you had in your jsfiddle example.

Related

How to call a function inside a child component in `Vue3` created through a `v-for` loop?

I am currently building a form builder with vue3 composition API. The user can add in different types of inputs like text, radio buttons etc into the form before saving the form. The saved form will then render with the appropriate HTML inputs. The user can edit the name of the question, eg Company Name <HTML textInput.
Currently, when the user adds an input type eg,text, the type is saved into an ordered array. I run a v-for through the ordered array and creating a custom component formComponent, passing in the type.
My formComponent renders out a basic text input for the user to edit the name of the question, and a place holder string for where the text input will be displayed. My issue is in trying to save the question text from the parent.
<div v-if="type=='text'">
<input type="text" placeholder="Key in title"/>
<span>Input field here</span>
</div>
I have an exportForm button in the parent file that when pressed should ideally return an ordered array of toString representations of all child components. I have tried playing with $emit but I have issue triggering the $emit on all child components from the parent; if I understand, $emit was designed for a parent component to listen to child events.
I have also tried using $refs in the forLoop. However, when I log the $refs they give me the div elements.
<div v-for="item in formItems" ref="formComponents">
<FormComponent :type="item" />
</div>
The ideal solution would be to define a method toString() inside each of the child components and have a forLoop running through the array of components to call toString() and append it to a string but I am unable to do that.
Any suggestions will be greatly appreciated!
At first:
You don't really need to access the child components, to get their values. You can bind them dynamically on your data. I would prefer this way, since it is more Vue conform way to work with reactive data.
But I have also implemented the other way you wanted to achieve, with accessing the child component's methods getValue().
I would not suggest to use toString() since it can be confused with internal JS toString() function.
In short:
the wrapping <div> is not necessary
the refs should be applied to the <FormComponents> (see Refs inside v-for)
this.$refs.formComponents returns the Array of your components
FormComponent is used here as <form-components> (see DOM Template Parsing Caveats)
The values are two-way bound with Component v-model
Here is the working playground with the both ways of achieving your goal.
Pay attention how the values are automatically changing in the FormItems data array.
const { createApp } = Vue;
const FormComponent = {
props: ['type', 'modelValue'],
emits: ['update:modelValue'],
template: '#form-component',
data() {
return { value: this.modelValue }
},
methods: {
getValue() {
return this.value;
}
}
}
const App = {
components: { FormComponent },
data() {
return {
formItems: [
{ type: 'text', value: null },
{ type: 'checkbox', value: false }
]
}
},
methods: {
getAllValues() {
let components = this.$refs.formComponents;
let values = [];
for(var i = 0; i < components.length; i++) {
values.push(components[i].getValue())
}
console.log(`values: ${values}`);
}
}
}
const app = createApp(App)
app.mount('#app')
#app { line-height: 2; }
[v-cloak] { display: none; }
label { font-weight: bold; }
th, td { padding: 0px 8px 0px 8px; }
<div id="app">
<label>FormItems:</label><br/>
<table border=1>
<thead><tr><th>#</th><th>Item Type:</th><th>Item Value</th></tr></thead>
<tbody><tr v-for="(item, index) in formItems" :key="index">
<td>{{index}}</td><td>{{item.type}}</td><td>{{item.value}}</td>
</tr></tbody>
</table>
<hr/>
<label>FormComponents:</label>
<form-component
v-for="(item, index) in formItems"
:type="item.type" v-model="item.value" :key="index" ref="formComponents">
</form-component>
<button type="button" #click="getAllValues">Get all values</button>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<script type="text/x-template" id="form-component">
<div>
<label>type:</label> {{type}},
<label>value:</label> <input :type='type' v-model="value" #input="$emit('update:modelValue', this.type=='checkbox' ? $event.target.checked : $event.target.value)" />
</div>
</script>

How can I get a custom css variable from any element (cypress)

I want to create some tests checking the styles of elements. We use these custom CSS vars. Is there any way to get these in cypress instead of checking for e.g. RGB(0,0,0)?
Thx in advance!
If you use cy.should() alongside have.css, you can specify which CSS property to check, and the value.
Using a simple example from your image, it would look something like this:
cy.get('foo')
.should('have.css', 'min-width', '211px');
If there are more complex checks going on, you can always run the .should() as a callback.
cy.get('foo').should(($el) => {
const minHeight = +($el.css('min-height').split('px')[0]);
expect(minHeight).to.eql(40);
});
I found myself checking a lot of CSS values on elements, and opted to have a custom command that allowed me to pass in an expected object and check for all of those values.
Cypress.Commands.add('validateCss', { prevSubject: 'element' }, (subject, expected: { [key: string]: any }) => {
Object.entries(expected).forEach(([key, value]) => {
cy.wrap(subject).should('have.css', key, value);
});
});
const expected = { 'min-width': '211px', 'min-height': '40px' };
cy.get('foo').validateCss(expected);
Interacting with browser element or Dynamic CSS can be achieved in may ways ,
most use-full is cy.get() with the help of .should()
you can find here ( However i know you already checked this :) )
https://docs.cypress.io/api/commands/get#Get-vs-Find
for Example
cy.get('#comparison')
.get('div')
// finds the div.test-title outside the #comparison
// and the div.feature inside
.should('have.class', 'test-title')
.and('have.class', 'feature')
It is possible to evaluate a css variable fairly simply using getComputedStyle()
Cypress.Commands.add('cssVar', (cssVarName) => {
return cy.document().then(doc => {
return window.getComputedStyle(doc.body).getPropertyValue(cssVarName).trim()
})
})
cy.cssVar('--mycolor')
.should('eq', 'yellow')
where, for example
<html>
<head>
<style>
body {
--mycolor: yellow;
}
p {
background-color: var(--mycolor);
}
</style>
</head>
But asserting that <p> has --mycolor requires a dummy element to evaluate yellow to rgb(255, 255, 0).
Cypress.Commands.add('hasCssVar', {prevSubject:true}, (subject, styleName, cssVarName) => {
cy.document().then(doc => {
const dummy = doc.createElement('span')
dummy.style.setProperty(styleName, `var(${cssVarName})`)
doc.body.appendChild(dummy)
const evaluatedStyle = window.getComputedStyle(dummy).getPropertyValue(styleName).trim()
dummy.remove()
cy.wrap(subject)
.then($el => window.getComputedStyle($el[0]).getPropertyValue(styleName).trim())
.should('eq', evaluatedStyle)
})
})
it('compares element property to CSS variable', () => {
cy.cssVar('--mycolor').should('eq', 'yellow')
cy.get('p').hasCssVar('background-color', '--mycolor') // passes
cy.get('button').click() // change the css var color
cy.cssVar('--mycolor').should('eq', 'red')
cy.get('p').hasCssVar('background-color', '--mycolor') // passes
})
The complication is not really because of the CSS var, but because we are dealing with color names that are automatically translated by the browser CSS engine.
Full test page
<html>
<head>
<style>
body {
--mycolor: yellow;
}
p {
background-color: var(--mycolor);
}
</style>
</head>
<body>
<p>Some text, change the background from yellow to red.</p>
<button onclick="changeColor()">Change color</button>
<script>
function changeColor() {
document.body.style.setProperty('--mycolor', 'red')
};
</script>
</body>
</html>
Test log

Grid only displaying one Column

I've recently started trying to use react-virtualized, but I've ran into an issue from the get-go. I've been trying to build a very simple grid, at first I loaded my data in and it wasn't working properly, but I've changed it to a simple 4x4 Grid and it's still giving me issues. Right now all 16 cells are being loading in a single column, and I've tried logging the rowIndex and the columnIndex, and those are giving me the correct output.
I'm not sure if I'm doing something wrong when I call the Grid, or if I'm doing something wrong with the cellRenderer, I would really appreciate some help with this. I have parts of my code down below.
_cellRenderer({columnIndex, key, rowIndex, styles}){
return(
<div>
{columnIndex}
</div>);
}
render(){
return(
<div>
<Autosizer>
{({width}) => (
<Grid
cellRenderer={this._cellRenderer}
columnCount={4}
columnWidth={30}
rowCount={4}
rowHeight={30}
width={400}
height={400}
/>
)}
</Autosizer>
</div>
);
}
You aren't using the parameters passed to your renderer. For example, style and key are both passed for a reason and you must use them. (The documentation should make this pretty clear.)
Put another way:
// Wrong:
function cellRenderer ({ columnIndex, key, rowIndex, style }) {
return (
<div>
{columnIndex}
</div>
)
}
​
// Right:
function cellRenderer ({ columnIndex, key, rowIndex, style }) {
return (
<div
key={key}
style={style}
>
{columnIndex}
</div>
)
}
Also, in case you didn't notice, the parameter is style and not styles like your code snippet shows.

How do I generate an internally-celled border between Cards in Semantic-UI

I'm using the Card component from Semantic-UI. I won't know in advance how many cards will be rendered. I'd like to put a border between inward facing sides of the card, similar to the way its done in Grid. What's an elegant way to do this?
Here's my bit of code:
renderCategoryCards(){
return this.props.grid.map((grid_cat) => {
return ( <CategoryCard key={grid_cat._id} category={grid_cat}/> )
})
}
render() {
return (
<div className="categories-wrapper">
<Card.Group itemsPerRow={3} stackable>
{this.renderCategoryCards()}
</Card.Group>
</div>
);
}
render() { return ( <Grid className="categories-wrapper" celled='internally' container stackable centered columns='equal'> {this.generateRows()} </Grid> ); } }

Issue with onClick in React component

I have a search result component that contains a div that, when clicked, is supposed to open a modal. Here's the relevant code below:
const createClickableDiv = function(props){
if(props.searchType === 'option1'){
return (
<div onClick={this.onDivClick}>
<div>Some content</div>
</div>
);
}else if(props.searchType === 'option2'){
return (
<div onClick={this.onDivClick}>
<div>Some other content</div>
</div>
);
}
return (<div className="result-empty"></div>);
};
class SearchResultItem extends Component{
constructor(props){
super(props);
}
onDivClick(){
AppUIActions.openModal((<Modal />));
}
render(){
const clickableDiv = createClickableDiv(this.props);
return (
<div>
{clickableDiv}
</div>
}
}
When I load the page, nothing shows up. However, when I remove onClick={this.onDivClick} from the divs, everything renders correctly, just without the desired click functionality. I put a try-catch around the code in the createClickableDiv function and it's saying this is undefined. I'm not really sure where to go from here.
Thanks!
this. sometimes behaves strangely. When your function createClickableDiv is called, this no longer refers to your SearchResultItem but to the createClickableDiv, which does not have the function onDivClick defined.
Try changing the function call to createClickableDiv(this), to pass pointer to your complete SearchResultItem,
and change the function itself to:
const createClickableDiv = function(caller){
if(caller.props.searchType === 'option1'){
return (
<div onClick={caller.onDivClick}>
<div>Some content</div>
</div>
// etc..
Maybe that would work.

Resources