Switch Widget in Subheader puts first Button in Popup

redacted redacted redacted

Hi @alicia-secure-haze,

Before diving deeper into the topic, could you help me clarify these points:

  1. customWidgetMap, of which Engine are you using? WidgetMap is avaiable in both TE, OE, FE and each engine have slighly different implementation for its subheader
  2. Then, if possible, could you attach your code here, which shows us how you replace the Button into a Switch so it will be easier to indentify whether it is a Widget or Engine problem.

Thanks,

redacted redacted redacted

Hi @alicia-secure-haze,

The Switch component you are trying to replace is actually a Button inside a ButtonGroupContainer. This container is specifically built for dynamically adjust the buttons into a popup menu depending on the screen size. That explains why wrapping the Switch into a Button will keep the UI as-is. To actually having a switch here, I would recommend these 2 TLDR options:

  1. Via Widgets, which is to wrap your switch into a “button” tag element then remove unneeded styles
  2. Via Form Engine, which is to customize the ActionContentBox component then process to add your switch outside the ButtonGroupContainer

Then, let’s get deeper into the customization, for the Widgets approach you can simply use CSS styling to omit/unset several styling from the usual Button, e.g.:

export const StyledSwitchWrapper = styled.div(({ theme }) => {
	return css`
		display: flex;
		flex-direction: column;
		width: auto;

		${StyledButton} {
			background: unset;

			:hover,
			:focus {
				border: unset;
				outline: unset;
				background: unset;
			}
		}
	`;
});

// Your switch component
return (
	<StyledSwitchWrapper>
		<div>Hello</div>
		<Button>
			<Switch checked={true} onChange={noop}></Switch>
		</Button>
	</StyledSwitchWrapper>
)

On the other hand, for the Form Engine option, you can override the default ActionContentBox then provide your list of buttons like below:

const formEngineWidgetMap = {
	...DefaultWidgetMap,
	ActionContentbox: (props) => {
		const buttons = React.useMemo(() => {
			if (React.isValidElement(props.buttons)) { // This will be a ButtonGroupContainer instance
				return (
					<div className="-u-flex">
						<div className="-u-width-full">{props.buttons}</div>
						<Switch className="-u-width-auto" checked={true} onChange={noop}></Switch>
					</div>
				);
			}
			// This will be the other cases which will be applied on "small screen" or 
			// when the action bar is hidden via certain FE logics like:
			// - Dependent Control/Field/Group
			// - Enablements
			// In such cases, you would need other alternative display of the buttons as they will be controlled/displayed differently
			// compared to the usual button group container
			return props.buttons;
		}, [props.buttons]);

		return <DefaultWidgetMap.ActionContentbox {...props} buttons={buttons} />;
	}
};


// Your FormEngine component
return <FormEngineViews.FormEngine {...props} widgetMap={formEngineWidgetMap} />

Both approaches has its own quirks and might not work in certains use case where we require the FormEngine/Widgets to be “responsive”, for instance, rendering on smaller screen size. Or, a dependent control from your form model could potentially hide you sub action bar, which could result in your switch component not displayed.

Therefore, my recommendation here is, instead of a Switch, you could use the usual Button but with different icon depending on the “checked” state.

const formEngineWidgetMap = {
	...DefaultWidgetMap,
	Button: (props) => {
		if (thisIsMyCustomizedButton) {
			return <DefaultWidgetMap.Button {...props} icon={isChecked ? <Icon>checked</Icon> : <Icon>unchecked</Icon>} />;
		}
		return <DefaultWidgetMap.Button {...props} />;
	}
};

redacted redacted redacted