How to style dynamically added nested elements in react - css

I was dynamically adding nested categories and subacatogories in react. I want to hide and show the sub-categories on clicking or hovering main-category. I wanted to show/hide subcatogories(mobiles, tv, laptops) on clicking Accessories and wanna show/hide Noodles on clicking groceries. Please help
My Code:
import React from "react";
function App(props) {
const categoriesList = [
{
id: "1",
name: "Accessories",
slug: "accessories",
children: [
{
id: "2",
name: "Mobiles",
slug: "mobiles",
},
{
id: "3",
name: "TV",
slug: "tv",
},
{
id: "4",
name: "Laptops",
slug: "laptops",
children: [
{
id: 5,
name: "Dell",
slug: "dell",
},
],
},
],
},
{
id: "6",
name: "Books",
slug: "books",
// children: undefined,
},
{
id: "7",
name: "Groceries",
slug: "groceries",
children: [
{
id: "8",
name: "Noodles",
slug: "noodles",
},
],
},
];
const renderCategories = (categories) => {
let myCategories = [];
for (const category of categories) {
myCategories.push(
<li className="category-list" key={category.name}>
{category.name}
{category?.children?.length > 0 ? (
<ul className="nested-category">
{renderCategories(category.children)}
</ul>
) : null}
</li>
);
}
return myCategories;
};
return (
<div className="app">
<ul className="main">{renderCategories(categoriesList)}</ul>
</div>
);
}

You need a state like categoryToBeDisplayed and then just have an onClick on the category li tag
for (const category of categories) {
myCategories.push(
<li className="category-list" key={category.name} onClick={() => setCategory(category.name)}>
{category.name}
{category?.children?.length > 0 && categoryToBeDisplayed === category.name ? (
<ul className="nested-category">
{renderCategories(category.children)}
</ul>
) : null}
</li>
);
}
And if you want multiple to be open at once just have the state accept an array and check if categoryToBeDisplayed.includes(category.name)

Related

Can't display retrieved data from Strapi in the UI, but can see them in the console Using Next.Js

I am trying to retrieve data from strapi using the NextJs as the front-end. The problem is that I can't display them on the UI, but I can see them in the console.
I used Graphql for querying the data using this commend:
query {
menus(filters: {language: {code: {eq: "en"}}}) {
data {
attributes {
Entry {
title
link
}
}
}
}
}
, I got this:
{
"data": {
"menus": {
"data": [
{
"attributes": {
"Entry": [
{
"title": "home",
"link": "/home.html"
},
{
"title": "classes",
"link": "./calss.html"
},
{
"title": "events",
"link": "/events.html"
},
{
"title": "news",
"link": "/news.html"
},
{
"title": "reosurces",
"link": "./resources.html"
},
{
"title": "links",
"link": "./links.html"
}
]
}
}
]
}
}
}
and this is how the UI handled:
const mainMenuAdapter = (menuData) => {
return menuData.map(md => {
const entry = md['attributes']['Entry']
console.log('title', entry)
return {
link: entry.link,
title: entry.title,
}
})
}
I use useContext;
const { menuData } = useContext(GlobalContext)
const menus = mainMenuAdapter(menuData)
here is how the mapping is handled.
<div className="bg-green-400 relative filosofia_regular bg-grayDark flex-wrap md:flex flex-row items-end md:justify-around p-2 lg:justify-evenly text-center mx-auto w-full">
{!!menus && menus.map((menu, i) => {
return (
<div key={i}>
<Link href="/">
<a className="text-gray-100 px-2 text-sm lg:text-2xl xl:text-4xl whitespace-nowrap">
{t(menu.title)}
</a>
</Link>
{i < menus.length - 1 && (
<a className="text-white">|</a>
)}
</div>
);
})}
</div>
and here is the console.
(6) [{…}, {…}, {…}, {…}, {…}, {…}]
0:
link: "/home.html"
title: "home"
__typename: "ComponentEntryComEntry"
[[Prototype]]: Object
1: {title: 'classes', link: './calss.html', __typename: 'ComponentEntryComEntry'}
2: {title: 'events', link: '/events.html', __typename: 'ComponentEntryComEntry'}
3: {title: 'news', link: '/news.html', __typename: 'ComponentEntryComEntry'}
4: {title: 'reosurces', link: './resources.html', __typename: 'ComponentEntryComEntry'}
5: {title: 'links', link: './links.html', __typename: 'ComponentEntryComEntry'}
length: 6
[[Prototype]]: Array(0)
Can anyone please tell me what I am missing?
Thanks

ReactJS 7 - How to conditionally change the background color of table cell only (not the row) based on its value?

I am trying to change the background color of the cell based on its value inside. I am currently using this library: react-data-table-component
Logic:
If value is greater than 0, then the background color of the cell will be red.
Otherwise there won't be a background color.
Here's my current snippet code for the Data Table:
const CountryTable = ({items}) => {
return(
<DataTable
title="Covid-19 Stats"
defaultSortAsc="false"
responsive
defaultSortField="cases"
defaultSortAsc={false}
striped
highlightOnHover
data={items}
columns={
[
{
name: '#',
selector: (row, index) => index+1,
disableSortBy: true,
},
{
name: 'Country',
selector: 'country',
sortable: true,
},
{
name: 'Total Cases',
selector: 'cases',
sortable: true,
},
{
getProps: (state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
style: {
background:
parseInt(rowInfo.row.todayCases) > 0 ? "red" : null
}
};
} else {
return {};
}
},
name: 'Additional New Cases',
selector: 'todayCases',
sortable: true,
},
{
name: 'Current Active Cases',
selector: 'active',
sortable: true,
},
{
name: 'Total Deaths',
selector: 'deaths',
sortable: true,
},
{
name: 'Additional New Deaths',
selector: 'todayDeaths',
sortable: true,
},
{
name: 'Total Recoveries',
selector: 'recovered',
sortable: true,
},
{
name: 'Additional New Recoveries',
selector: 'todayRecovered',
sortable: true,
},
]
}
/>
);
}
And here's the illustration of what it should be look like:
The docs say you can set conditional styles like this: https://www.npmjs.com/package/react-data-table-component#conditional-style-object
const conditionalRowStyles = [
{
when: row => row.calories < 300,
style: {
backgroundColor: 'green',
color: 'white',
'&:hover': {
cursor: 'pointer',
},
},
},
// You can also pass a callback to style for additional customization
{
when: row => row.calories < 300,
style: row => ({
backgroundColor: row.isSpecia ? 'pink' : 'inerit',
}),
},
];
const MyTable = () => (
<DataTable
title="Desserts"
columns={columns}
data={data}
conditionalRowStyles={conditionalRowStyles}
/>
);
I quickly put together an example usings cell and styled components:
https://codesandbox.io/s/upbeat-galileo-r7p34?file=/src/App.js
import "./styles.css";
import DataTable from "react-data-table-component";
import styled from "styled-components";
const StyledCell = styled.div`
&.low {
background: green !important;
}
&.medium {
background: orange;
}
&.high {
background: red !important;
}
`;
export default function App() {
let items = [
{
Country: "Canada",
AdditionalNewCases: 500
},
{
Country: "England",
AdditionalNewCases: 5000
},
{
Country: "USA",
AdditionalNewCases: 500000
}
];
function getCssClass(value) {
if (value > 5000) return "high";
else if (value > 500) return "medium";
return "low";
}
return (
<DataTable
title="Covid-19 Stats"
defaultSortAsc="false"
responsive
defaultSortAsc={false}
striped
highlightOnHover
data={items}
columns={[
{
name: "number",
selector: (row, index) => index + 1,
disableSortBy: true
},
{
name: "Country",
selector: "Country",
sortable: true
},
{
name: "New Cases",
selector: "AdditionalNewCases",
sortable: true,
cell: (row) => (
<StyledCell className={getCssClass(row.AdditionalNewCases)}>
{row.AdditionalNewCases}
</StyledCell>
)
}
]}
/>
);
}

How to add block content in the cutsom gutenberg block

I was trying to add a custom shortcode in the admin panel when my custom block added to the page.
( function( blocks, element, editor, components) {
var el = element.createElement;
const { RichText, InspectorControls, BlockEdit } = editor;
const { Fragment } = element;
const { TextControl, ToggleControl, Panel, PanelBody, PanelRow, SelectControl } = components;
blocks.registerBlockType( 'gutenberg-custom/show-information', {
title: 'Show information',
icon: 'universal-access-alt',
category: 'layout',
example: {},
attributes: {
exhibitor_id:{
type: "string"
}
},
edit: function(props) {
console.log('props', props)
return el(
Fragment,
{},
el(
InspectorControls,
{},
el(
PanelBody,
{ title: "Show Settings", initialOpen: true },
/* Text Field */
el(
PanelRow,
{},
el(SelectControl, {
label: "Select Exhibitor",
options: [
{ label: 'Big', value: '100%' },
{ label: 'Medium', value: '50%' },
{ label: 'Small', value: '25%' },
] ,
onChange: value => {
props.setAttributes({ exhibitor_id: value });
},
value: props.attributes.exhibitor_id
})
)
),
)
);
},
save: function(props){
return 'Test output'
}
} );
}(
window.wp.blocks,
window.wp.element,
window.wp.editor,
window.wp.components
) );
The above is my js code. I could add the settings for the block but I couldn't figure out how can I add my custom content that is '[custom-shortcode]' in the block itself (like in the picture).
How can I do that?
edit: function(props) {
console.log("props", props);
return el(
Fragment,
{},
el(
InspectorControls,
{},
el(
PanelBody,
{ title: "Show Settings", initialOpen: true },
el(
PanelRow,
{},
el(SelectControl, {
label: "Select Exhibitor",
options: [
{ label: "Big", value: "100%" },
{ label: "Medium", value: "50%" },
{ label: "Small", value: "25%" }
],
onChange: value => {
props.setAttributes({ exhibitor_id: value });
},
value: props.attributes.exhibitor_id
})
)
)
),
el(
"div",
{},
"[show-information]"
)
);
},

How to add array of ids to entity that doesn't have them

I have my schema set up almost exactly how I want it in redux state, except I want to add an array of form ids to the formTemplate object.
It would look like this:
// Normalized Form Templates
{
1: {
id: '1',
isGlobal: true,
name: 'Form Template Name',
forms: [1, 2], // This is the line I want to add...but how?
},
}
// Normalized Forms
{
1: {
id: '1',
createdAt: '2016-12-28T23:30:13.547Z',
name: 'Form 1',
parentTemplate: '1',
pdfs: [1, 2],
},
2: {
id: '2',
createdAt: '2016-12-28T23:30:13.547Z',
name: 'Form 2',
parentTemplate: '1',
pdfs: [],
},
}
Here is my schema
import { schema } from 'normalizr'
const formTemplate = new schema.Entity('formTemplates', {}, {
processStrategy: value => ({
id: value.id,
name: value.attributes.title,
isGlobal: value.attributes.is_global,
}),
})
const form = new schema.Entity('forms', {
pdfs: [pdf],
}, {
processStrategy: value => ({
id: value.id,
createdAt: value.attributes.created_at,
name: value.attributes.title,
parentTemplate: value.attributes.form_template_id,
pdfs: [...value.relationships.documents.data],
}),
})
const pdf = new schema.Entity('pdfs')
export default {
data: [form],
included: [formTemplate],
}
This is an example of the API response that I'm normalizing
{
"data": [
{
"id": "5",
"type": "provider_forms",
"attributes": {
"title": "Form 1",
"created_at": "2017-01-02T06:00:42.518Z",
"form_template_id": 1
},
"relationships": {
"form_template": {
"data": {
"id": "1",
"type": "form_templates"
}
},
"documents": {
"data": [ // some pdf data here ]
}
}
}
],
"included": [
{
"id": "1",
"type": "form_templates",
"attributes": {
"title": "Form Template",
"created_at": "2016-12-29T22:24:36.201Z",
"updated_at": "2017-01-02T06:00:20.205Z",
"is_global": true
},
}
]
}
You won't be able to do this with Normalizr because the form template entity has no context back to the forms entities. Your actions/reducer need to handle this.
Okay I figured out a way to do this. I changed my formTemplate entity to map them in manually like so:
const formTemplate = new schema.Entity('formTemplates', {}, {
processStrategy: (value, parent) => {
// eslint-disable-next-line eqeqeq
const childrenForms = parent.data.filter(form => form.attributes.form_template_id == value.id)
return {
id: value.id,
name: value.attributes.title,
isGlobal: value.attributes.is_global,
forms: childrenForms.map(form => form.id),
}
},
})

Dynamic Country-State-City using AutoForm and Collection2 package in Meteor

I am using autoform and collection2 package and making a form in meteor. As of now i put some hard-coded option for country-state-city dropdown and insert-update is working fine. Now I want for the first time only country dropdown is enable other two are disable. Based on Country selection the states dropdown will populate and enable. Then based on State selection City Should Populate.
I don't want to do this manually. Is there any way to do this using autoform / collection2 features??? My code sample is as follows:
Collection2 Schema:
country:{
type: String,
label : "Country",
autoform: {
afFieldInput: {
type: "select"
},
options: function () {
return [
{label: 'Country1', value: 'Country1'},
{label: 'Country2', value: 'Country2'},
{label: 'Country3', value: 'Country3'},
{label: 'Country4', value: 'Country4'}
];
}
}
},
state:{
type: String,
label : "State",
autoform: {
afFieldInput: {
type: "select"
},
options: function () {
return [
{label: 'State1', value: 'State1'},
{label: 'State2', value: 'State2'},
{label: 'State3', value: 'State3'},
{label: 'State4', value: 'State4'}
];
}
}
},
city:{
type: String,
label : "City",
autoform: {
afFieldInput: {
type: "select"
},
options: function () {
return [
{label: 'City1', value: 'City1'},
{label: 'City2', value: 'City2'},
{label: 'City3', value: 'City3'},
{label: 'City4', value: 'City4'}
];
}
}
},
HTML ::
{{> afQuickField name='country' template="bootstrap3-horizontal" label-class="col-sm-4" input-col-class="col-sm-8"}}
{{> afQuickField name='state' template="bootstrap3-horizontal" label-class="col-sm-4" input-col-class="col-sm-8"}}
{{> afQuickField name='city' template="bootstrap3-horizontal" label-class="col-sm-4" input-col-class="col-sm-8"}}
Any Help??
I think this is somewhat the idea you have, https://jsfiddle.net/bdhacker/eRv2W/
// Countries
var country_arr = new Array("Afghanistan", "Albania", "Algeria", "American Samoa", "Angola", "Anguilla", "Antartica"...
// States
var s_a = new Array();
s_a[0] = "";
s_a[1] = "Badakhshan|Badghis|Baghlan|Balkh|Bamian|Farah|Faryab|Ghazni|Ghowr|Helmand|Herat|Jowzjan|Kabol|Kandahar|Kapisa|Konar|Kondoz|Laghman|Lowgar|Nangarhar|Nimruz|Oruzgan|Paktia|Paktika|Parvan|Samangan|Sar-e Pol|Takhar|Vardak|Zabol";...
you can extract the data from this and adjust to your app. Hope it helps
i think you can set the inputs of state and city to be disabled
country:{
type: String,
label : "Country",
autoform: {
afFieldInput: {
type: "select"
},
options: function () {
return [
{label: 'Country1', value: 'Country1'},
{label: 'Country2', value: 'Country2'},
{label: 'Country3', value: 'Country3'},
{label: 'Country4', value: 'Country4'}
];
}
}
},
state:{
type: String,
label : "State",
autoform: {
afFieldInput: {
type: "select",
disabled:true
},
options: function () {
return [
{label: 'State1', value: 'State1'},
{label: 'State2', value: 'State2'},
{label: 'State3', value: 'State3'},
{label: 'State4', value: 'State4'}
];
}
}
},
city:{
type: String,
label : "City",
autoform: {
afFieldInput: {
type: "select",
disabled:true
},
options: function () {
return [
{label: 'City1', value: 'City1'},
{label: 'City2', value: 'City2'},
{label: 'City3', value: 'City3'},
{label: 'City4', value: 'City4'}
];
}
}
},
and use Template event to enable the options
Template.YOURTEMPLATENAME.events({
'change input[name="country"]' :function(){
if ($('input[name="country"]').value() != ''){
$('input[name="state"]').attr('disabled','');
}else {
$('input[name="state"]').attr('disabled','disabled');
}
},
'change input[name="state"]' :function(){
if ($('input[name="state"]').value() != ''){
$('input[name="city"]').attr('disabled','');
}else {
$('input[name="city"]').attr('disabled','disabled');
}
}
});

Resources