bumbeishvili / org-chart

Highly customizable org chart. Integrations available for Angular, React, Vue
https://stackblitz.com/edit/web-platform-o5t1ha
MIT License
890 stars 317 forks source link

Add Node function is not working properly #125

Closed BekaBaydullaev closed 1 year ago

BekaBaydullaev commented 2 years ago

Hi! In the "React Integration" example there is a button to add a new node with "hard-coded " values, so I assumed that if I pass values of the node like: "id", "name" and "parentNodeId" from the user input it will create a new node, but it throws an error "Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'filter')" which leads to the line 492 of "d3-org-chart" file where: const nodeFound = attrs.allNodes.filter(({ data }) => attrs.nodeId(data) === attrs.nodeId(obj))[0];.

Another thing is even if I call the "addNode" function from other places, like a button that is in the mui drawer or dialog box it also show same error, although I'm not passing new values, its still using same "hard-coded" values from the example.

After some playing around, I decided to put all the code into one file, guessing that this error could be caused due to wrong declaration of the OrgChart or wrong data parsing from the json file, but still the same error.

So the question is how to properly use addNode function to create a new node using values provided by a user?

I will add the whole code for better picture of what I'm trying to do.

P.S. I'm a newbie to react development, so please don't mind my bad coding 😅

Thanks in advance

=================Code that I'm trying to make work==================

import React, {useState, useEffect, useRef} from 'react';
import {jsonData} from './data.js'
import {
    Button,
    Dialog,
    Grid,
    LinearProgress,
    Slide,
    Typography,
    useMediaQuery
} from '@mui/material';
import {gridSpacing} from 'store/constant';
import MainCard from "../../ui-component/cards/MainCard";
import {OrgChart} from 'd3-org-chart';
import {Field, Form, Formik} from "formik";
import {TextField} from "formik-mui";
import Card from "@mui/material/Card";
import CardHeader from "@mui/material/CardHeader";
import Avatar from "@mui/material/Avatar";
import GavelIcon from "@mui/icons-material/Gavel";
import ReactDOMServer from "react-dom/server";
import {useTheme} from "@mui/material/styles";

const Obs = ((props, ref) => {
    const d3Container = useRef(null);

    const [data, setData] = useState(null);
    let chart = new OrgChart();

    useEffect(() => {
        setData(jsonData);
        if (data && d3Container.current) {
            if (!chart) {
                chart = new OrgChart();
            }
            chart
                .container(d3Container.current)
                .data(data)
                .nodeWidth(d => 225)
                .nodeHeight(d => 110)
                .initialZoom(0.7)
                .siblingsMargin(d => 50)
                .childrenMargin(d => 75)
                .neightbourMargin((n1, n2) => 100)
                .childrenMargin((d) => 60)
                .compactMarginBetween((d) => 35)
                .compactMarginPair((d) => 80)
                .onNodeClick((d, i, arr) => {
                    console.log(d, 'Id of clicked node ');
                    setOpen(true)
                    // props.onNodeClick(d);
                })
                .nodeContent(function (d, i, arr, state) {
                    return (
                        ReactDOMServer.renderToString(Cards(d))
                    )
                })
                .render();
        }
    }, [data, d3Container.current]);

    const Cards = (d) => {
        console.log(d)
        return (
            <Card sx={{minHeight: 125, minWidth: 225,}}
            >

                <Grid
                    container
                    direction="row"
                    justifyContent="space-between"
                    alignItems="center"
                    spacing={0}
                >

                    <Grid item>
                        <CardHeader
                            sx={{
                                maxHeight: 50,
                                minWidth: 225
                            }}

                            avatar={
                                <Avatar
                                    sx={{width: 34, height: 34}}>
                                    <GavelIcon color="error"/>
                                </Avatar>
                            }

                            title={<Typography color="#111672" fontWeight="bold"> {d.data.name} </Typography>}

                        />
                    </Grid>

                    <Grid item xs align="center">
                        <Typography align="center"> Managers: </Typography>
                        <Typography align="center" variant="button"> {d.data._directSubordinates} </Typography>
                    </Grid>
                    <Grid item xs align="center">
                        <Typography align="center"> Oversees: </Typography>
                        <Typography align="center" variant="button"> {d.data._totalSubordinates} </Typography>
                    </Grid>
                </Grid>
            </Card>)
    }

    function addNodeWithData(nodeData) {
        console.log("index -> addNodeWithData -> nodeData")
        console.log(nodeData)
        chart.addNode(nodeData);

    }

    function addNode() {
        const node = {
            name: 'New Node',
            nodeId: '6060',
            parentNodeId: '6066'
        };

        chart.addNode(node);
    }

    function deleteNode(){
        chart.removeNode(6068)
    }

    // props.setClick(addNode);

    const [open, setOpen] = React.useState(false);

    const handleClose = () => {
        setOpen(false);
    };
    const Transition = React.forwardRef(function Transition(props, ref) {
        return <Slide direction="up" ref={ref} {...props} />;
    });

    const theme = useTheme();
    const fullScreen = useMediaQuery(theme.breakpoints.down("md"));

    return (

        <Grid container spacing={gridSpacing}>
            <Grid item xs={12}>
                <MainCard>
                    <button id={"addButton"} onClick={addNode}>add node as root's child</button>
                    <button onClick={deleteNode}>remove node</button>
                    <div ref={d3Container}/>

                    <Dialog
                        fullWidth={true}
                        maxWidth={"sm"}
                        fullScreen={fullScreen}
                        open={open}
                        TransitionComponent={Transition}
                        keepMounted
                        onClose={handleClose}
                        aria-labelledby="alert-dialog-slide-title"
                        aria-describedby="alert-dialog-slide-description"

                    >
                        <Formik
                            enableReinitialize={true}
                            initialValues={{
                                id: "7946",
                                name: "Dav Nan",
                                parentNodeId: "6066"
                            }}

                            onSubmit={(values, {resetForm, setSubmitting}) => {
                                console.log("add button clicked")
                                addNodeWithData(values)
                                resetForm()
                                setSubmitting(false)
                                // document.getElementById("addButton").click()
                            }}
                        >
                            {({submitForm, isSubmitting}) => (
                                <Form>
                                    <Grid>
                                        <Grid item xs="auto">
                                            <Field
                                                component={TextField}
                                                label="Name"
                                                name='name'
                                                fullWidth
                                            />
                                        </Grid>
                                        <Grid item xs="auto">

                                            {isSubmitting && <LinearProgress/>}
                                        </Grid>
                                        <Grid item xs="auto">

                                        </Grid>
                                    </Grid>
                                    <Grid>
                                        <Button variant="contained" disabled={isSubmitting}
                                                onClick={submitForm}>
                                            Add new child
                                        </Button>

                                    </Grid>
                                </Form>
                            )}

                        </Formik>
                    </Dialog>

                </MainCard>
            </Grid>
        </Grid>

    )
});
export default Obs
bumbeishvili commented 2 years ago

I am not exactly sure what is happening, if you can provide an online reproducible example by forking the react integration sample, I may be able to check it

BekaBaydullaev commented 2 years ago

Here is a reproduced code on the sandbox

BekaBaydullaev commented 2 years ago

I am not exactly sure what is happening, if you can provide an online reproducible example by forking the react integration sample, I may be able to check it

Any update regarding this issues ? 😅

bumbeishvili commented 2 years ago

Not expecting updates in the coming days (maybe weeks or even months?)

I'll look into it once I will have more free time from my current projects

bumbeishvili commented 1 year ago

Hmm:/ I checked this example and it was working for me