skip to Main Content

I can’t seem to find an answer to this that I can understand

I have tabs and a tab block, which is a clone of this basic tab block plugin. I’m trying to add the ability to select an "icon" for each tab block.

I’m close. Using the MediaUpload component, I’m able to see the file I’ve selected under the activeTab object, but it doesn’t update the parent block attribute, so I can’t reference the icon_url attribute.

tab/edit.js

const Edit = ({ attributes, setAttributes, clientId }) => {
    const { uid, activeTab } = attributes;

    useEffect(() => {
        if (!uid) {
            setAttributes({ uid: clientId });
        }
    }, []);

    const display = activeTab === uid ? "block" : "none";
    const ALLOWED_MEDIA_TYPES = ["image", "svg"];

    const setTabIcon = (icon_url) => {
        const parentBlock = select("core/block-editor").getBlock(clientId);
        dispatch("core/block-editor").updateBlockAttributes(
            parentBlock.clientId,
            {
                ...attributes,
                icon_url,
            }
        );
    };

    return (
        <div {...useBlockProps()}>
            <InspectorControls>
                <div>
                    <MediaUpload
                        allowedTypes={ALLOWED_MEDIA_TYPES}
                        onSelect={(media) => setTabIcon(media.url)}
                        render={({ open }) => (
                            <button onClick={open}>Open Media Library</button>
                        )}
                    />
                </div>
            </InspectorControls>
            <div className={"guten-tab-panel"} style={{ display }}>
                <InnerBlocks
                    allowedBlocks={["core/heading", "core/paragraph"]}
                    renderAppender={() => <InnerBlocks.ButtonBlockAppender />}
                />
            </div>
        </div>
    );
};

export default Edit;

I would first think that using setAttributes here would also update the parent, but this only updates setActive in the child block. It doesn’t keep the change.

In tabs.js, I’m trying to reference tab.icon_url. icon_url doesn’t exist, only uid and title

tabs/tabs.js

const Edit = ({ attributes, setAttributes, clientId }) => {
    const { tabs, activeTab } = attributes;

    const blockProps = useBlockProps({
        className: `${useBlockProps().className} guten-tab-wrapper`,
    });

    const setActiveTab = (uid) => {
        setAttributes({ activeTab: uid });
        const parentBlock = select("core/block-editor").getBlock(clientId);
        parentBlock.innerBlocks.forEach((innerBlock) => {
            dispatch("core/block-editor").updateBlockAttributes(
                innerBlock.clientId,
                {
                    activeTab: uid,
                }
            );
        });
    };

    const addNewTab = () => {
        const tab = createBlock("ahsan03/tab");
        const position = tabs.length;
        dispatch("core/block-editor").insertBlock(tab, position, clientId);
        setAttributes({
            tabs: [
                ...tabs,
                {
                    uid: tab.clientId,
                    title: `Tab ${tabs.length + 1}`,
                    icon_url: "",
                },
            ],
        });
        setActiveTab(tab.clientId);
    };

    const tabTitleChange = (newValue) => {
        setAttributes({
            tabs: [
                ...tabs.map((tab) => {
                    return tab.uid === activeTab
                        ? {
                                ...tab,
                                title: newValue,
                          }
                        : tab;
                }),
            ],
        });
    };

    useEffect(() => {
        if (tabs.length && !activeTab) {
            setActiveTab(tabs[0].uid);
        }
    }, [tabs]);

    return (
        <>
            <div {...blockProps}>
                <div className={"guten-tabs-nav"}>
                    {tabs.map((tab) => {
                        return (
                            <div
                                key={tab.uid}
                                className={"guten-tab-item"}
                                role="tab"
                                tabIndex="0"
                                onClick={() => setActiveTab(tab.uid)}
                            >
                                <div
                                    className={`guten-tab-link${
                                        tab.uid === activeTab
                                            ? " is-active"
                                            : ""
                                    }`}
                                >
                                    <img src={tab.icon_url} alt="" />
                                    {console.log("tabs tab", {
                                        tab,
                                    })}
                                    <RichText
                                        tagName="div"
                                        value={tab.title}
                                        onChange={tabTitleChange}
                                    />
                                </div>
                            </div>
                        );
                    })}
                    <Button
                        variant={"primary"}
                        icon={"plus"}
                        onClick={addNewTab}
                    >
                        {__("", "gtt")}
                    </Button>
                </div>
                <div className={"guten-tab-content"}>
                    <InnerBlocks
                        allowedBlocks={["ahsan03/tab"]}
                        renderAppender={false}
                    />
                </div>
            </div>
        </>
    );
};

export default Edit;

How can I fix this so uploading an image is in the parent block attributes?

Here’s an updated setTabIcon function that I think is closer to what I need, I’m just not sure what to do after fetching the parentBlock.

const setTabIcon = (icon_url) => {
    const parentBlockIds =
        select("core/block-editor").getBlockParents(clientId);

    parentBlockIds.forEach((parentBlockId) => {
        const parentBlock = select("core/block-editor").getBlock(parentBlockId);
        console.log({ parentBlock });
    });
};

2

Answers


  1. Chosen as BEST ANSWER

    I was able to fix this by moving setTabIcon and the MediaFile component to the parent block.

    Would love suggestions on how to improve this code.


  2. Make a state variable in the parent component, and then pass that state and setState to the child as props, and then from the child component, you can update the state in the parent component.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search