Search code examples
wordpresswoocommercegutenberg-blocks

Woocommerce blocks: Show custom field based of selected shipping method


Maybe someone can help with showing a custom field depending on the selected shipping method in the new block-based WP WooCommerce Checkout page?

Example

Do I have to add something in the block.js file or do it need to be done via plain javascript? I can't find any examples or explanations in the documentation or in google.

block.js

/**
 * External dependencies
 */
import { useEffect, useState, useCallback } from '@wordpress/element';
import { SelectControl, TextareaControl } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { debounce } from 'lodash';

/**
 * Internal dependencies
 */
import { options } from './options';
import { txt } from './text';

export const Block = ({ checkoutExtensionData, extensions }) => {
    const { setExtensionData } = checkoutExtensionData;

    const debouncedSetExtensionData = useCallback(
        debounce((namespace, key, value) => {
            setExtensionData(namespace, key, value);
        }, 1000),
        [setExtensionData]
    );

    const terminalValidationErrorId = 'terminal';

    const { setValidationErrors, clearValidationError } = useDispatch(
        'wc/store/validation'
    );

    const validationError = useSelect((select) => {
        const store = select('wc/store/validation');

        return store.getValidationError(terminalValidationErrorId);
    });

    const [
        selectedTerminal,
        setSelectedTerminal,
    ] = useState('');

    /* Handle changing the select's value */
    useEffect(() => {
        setExtensionData(
            'terminals',
            'alternateShippingInstruction',
            selectedTerminal
        );

        if ( selectedTerminal !== '' ) {
            clearValidationError(terminalValidationErrorId);
            return;
        }

        if ( selectedTerminal === '' ) {
            setValidationErrors({
                [terminalValidationErrorId]: {
                    message: txt.error_terminal,
                    hidden: false
                }
            });
        }
    }, [
        setExtensionData,
        selectedTerminal,
        setValidationErrors,
        clearValidationError,
    ]);

    return (
        <div className="terminal_select_container">
            <SelectControl
                label={txt.title_terminal}
                value={selectedTerminal}
                options={options}
                onChange={setSelectedTerminal}
            />
            {(validationError?.hidden || selectedTerminal !== '') ? null : (
                <div className="wc-block-components-validation-error terminal-error">
                    <span>{validationError?.message}</span>
                </div>
            )}
        </div>
    );
};

Solution

  • I found a solution myself.

    export const Block = ({ checkoutExtensionData, extensions }) => {
        const [showBlock, setShowBlock] = useState(false);
    
        const shippingRates = useSelect((select) => {
            const store = select('wc/store/cart');
            return store.getCartData().shippingRates;
        });
    
        const getActiveShippingRates = (shippingRates) => {
            if ( ! shippingRates.length ) {
                return [];
            }
    
            let activeRates = [];
            for ( let i = 0; i < shippingRates.length; i++ ) {
                if ( ! shippingRates[i].shipping_rates ) {
                    continue;
                }
                for ( let j = 0; j < shippingRates[i].shipping_rates.length; j++ ) {
                    activeRates.push(shippingRates[i].shipping_rates[j]);
                }
            }
            
            return activeRates;
        };
    
        const getMyPluginData = () => {
            if ( ! wcSettings || ! wcSettings["myplugin-blocks_data"] ) {
                return [];
            }
    
            return wcSettings["myplugin-blocks_data"];
        };
    
        const isMyShippingRate = (methodKey) => {
            for ( let [key, value] of Object.entries(getMyPluginData().methods) ) {
                if ( methodKey == value ) {
                    return true;
                }
            }
            return false;
        };
    
        useEffect(() => {
            setShowBlock(false);
            if ( shippingRates.length ) {
                const activeRates = getActiveShippingRates(shippingRates);
                for ( let i = 0; i < activeRates.length; i++ ) {
                    if ( ! activeRates[i].rate_id ) {
                        continue;
                    }
                    if ( isMyShippingRate(activeRates[i].rate_id) && activeRates[i].selected ) {
                        setShowBlock(true);
                    }
                }
            }
        }, [
            shippingRates
        ]);
    
        if ( ! showBlock ) {
            return <></>
        }
    
        return (
            <div>
                ...
            </div>
        );
    };