Conditionally show/hide fields #668
Replies: 13 comments
-
Could you provide some mockups showing the desired rendering? It sounds like something I've suggested a solution to before but I'm not certain. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Let me know if this sandbox does what you want: https://codesandbox.io/p/devbox/x86zgp Screenshot: Here's the code for reference: styles.scssThis right-justifies the "Remove group" button, which wasn't in your requirement but I thought it looked nicer. .ruleGroup .ruleGroup .ruleGroup-header {
justify-content: space-between;
} App.tsximport { teal } from '@mui/material/colors';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import {
MaterialActionElement,
MaterialValueSelector,
QueryBuilderMaterial,
} from '@react-querybuilder/material';
import { useState } from 'react';
import type {
ActionWithRulesAndAddersProps,
Field,
Path,
RuleGroupType,
VersatileSelectorProps,
} from 'react-querybuilder';
import { formatQuery, QueryBuilder } from 'react-querybuilder';
import './styles.scss';
import { ChannelRuleGroup } from './ChannelRuleGroup.js';
const muiTheme = createTheme({
palette: {
secondary: {
main: teal[500],
},
},
});
const fields: Field[] = [
{ name: 'recipients', label: 'Recipients' },
{ name: 'recipientsList', label: 'Recipients List' },
{ name: 'recipientsListKey', label: 'Recipients List Key' },
{ name: 'subject', label: 'Subject' },
{ name: 'htmlTemplate', label: 'HTML Template' },
];
const initialQuery: RuleGroupType = {
combinator: 'and',
rules: [],
};
/**
* Return a group with the default rules and a `channel` property
* equivalent to the `context` passed by the action element.
*/
const onAddGroup = (
rg: RuleGroupType,
_path: Path,
_query: RuleGroupType,
context: any
) => {
console.log(context);
return {
...rg,
channel: context,
rules: [
{ field: 'recipients', operator: '=', value: '' },
{ field: 'recipientsList', operator: '=', value: '' },
{ field: 'recipientsListKey', operator: '=', value: '' },
{ field: 'subject', operator: '=', value: '' },
{ field: 'htmlTemplate', operator: '=', value: '' },
],
};
};
/**
* Render four buttons instead of one, each passing a different context.
*/
const AddGroup = (props: ActionWithRulesAndAddersProps) =>
props.path.length === 0 ? (
<>
<MaterialActionElement
{...props}
label="Email"
handleOnClick={e => props.handleOnClick(e!, 'Email')}
/>
<MaterialActionElement
{...props}
label="Notification"
handleOnClick={e => props.handleOnClick(e!, 'Notification')}
/>
<MaterialActionElement
{...props}
label="Desktop Alert"
handleOnClick={e => props.handleOnClick(e!, 'Desktop Alert')}
/>
<MaterialActionElement
{...props}
label="Push Notification"
handleOnClick={e => props.handleOnClick(e!, 'Push Notification')}
/>
</>
) : null;
/** Used to hide any elements */
const HiddenElement = () => null;
/** Used to disabled any selectors */
const DisabledSelector = (props: VersatileSelectorProps) => (
<MaterialValueSelector {...props} disabled />
);
export const App = () => {
const [query, setQuery] = useState(initialQuery);
return (
<div>
<ThemeProvider theme={muiTheme}>
<QueryBuilderMaterial>
<QueryBuilder
fields={fields}
query={query}
onQueryChange={setQuery}
controlClassnames={{ queryBuilder: 'queryBuilder-branches' }}
controlElements={{
addGroupAction: AddGroup,
addRuleAction: HiddenElement,
removeRuleAction: HiddenElement,
combinatorSelector: HiddenElement,
fieldSelector: DisabledSelector,
operatorSelector: DisabledSelector,
ruleGroup: ChannelRuleGroup,
}}
onAddGroup={onAddGroup}
/>
</QueryBuilderMaterial>
</ThemeProvider>
<h4>Query</h4>
<pre>
<code>{formatQuery(query, 'json')}</code>
</pre>
</div>
);
}; ChannelRuleGroup.tsxThis is a very slightly modified version of the standard import Typography from '@mui/material/Typography';
import * as React from 'react';
import type { RuleGroupProps } from 'react-querybuilder';
import {
TestID,
useRuleGroup,
useStopEventPropagation,
RuleGroupHeaderComponents,
RuleGroupBodyComponents,
} from 'react-querybuilder';
export const ChannelRuleGroup = React.memo((props: RuleGroupProps) => {
const rg = useRuleGroup(props);
rg.addRule = useStopEventPropagation(rg.addRule);
rg.addGroup = useStopEventPropagation(rg.addGroup);
rg.cloneGroup = useStopEventPropagation(rg.cloneGroup);
rg.toggleLockGroup = useStopEventPropagation(rg.toggleLockGroup);
rg.removeGroup = useStopEventPropagation(rg.removeGroup);
rg.shiftGroupUp = useStopEventPropagation(rg.shiftGroupUp);
rg.shiftGroupDown = useStopEventPropagation(rg.shiftGroupDown);
return (
<div
ref={rg.previewRef}
title={rg.accessibleDescription}
className={rg.outerClassName}
data-testid={TestID.ruleGroup}
data-dragmonitorid={rg.dragMonitorId}
data-dropmonitorid={rg.dropMonitorId}
data-rule-group-id={rg.id}
data-level={rg.path.length}
data-path={JSON.stringify(rg.path)}>
<div ref={rg.dropRef} className={rg.classNames.header}>
{rg.path.length > 0 && (
<Typography variant="body1">
Output Channel:{' '}
<strong>{(rg.ruleGroup as any).channel ?? null}</strong>
</Typography>
)}
<RuleGroupHeaderComponents
{...(rg as Parameters<typeof RuleGroupHeaderComponents>[0])}
/>
</div>
<div className={rg.classNames.body}>
<RuleGroupBodyComponents
{...(rg as Parameters<typeof RuleGroupBodyComponents>[0])}
/>
</div>
</div>
);
}); |
Beta Was this translation helpful? Give feedback.
-
Hi Jake, Really appreciate you taking the time to post this up. The overall idea is what I have just without the custom ruleGroup which seems like a more rubust way to go and I was looking at how to do that so could add a title as you have so thanks. Yes the delete button is better right aligned, hadn't quite got that far so thanks for the extra attention to detail. I can't quite seem to get it to work inside our app though. Firstly not using typescript so in converting it I've probably missed something. These 2 functions don't appear on rg so these lines are thowing cannot convert undefined to null no probelm we're not using them so I've commented them out, but would be good to know what I'm missing
rg.path doesn't seem to be defined either so .lemngth is failing. Again I have added rg.path?.length and that's ok
Now my main problem is these 2 lines
I have tried to just spread rg as the props,
but this then complains that schema isn't present. And when I debug rg inside the component, indeed, there is no schema property. The schema property does exist on the 'props' being passed in beofre you passed it to useRuleGroup so instead I have tried
Which then loads the component and the buttons. But when I click a button I get props.handleOnClick is not a function. Would be great to get this custom group working as it looks much better, but I think the main issue is how to add this group from it being selected as a value rather than having to have a button for each type of output. Is this something that would be possible through fieldSelector some how? |
Beta Was this translation helpful? Give feedback.
-
my channelgroup.js
|
Beta Was this translation helpful? Give feedback.
-
Oh, I'm sorry about that. I was using the v7 release candidate template. If you upgrade to version If you can't upgrade to the v7 RC or wait on the proper v7, I can modify the example to use v6. |
Beta Was this translation helpful? Give feedback.
-
oh no that's no problem, thanks, I can just go to 7. Only just started using the library so nothing in production yet. Sorry just to confirm though, spreading rg[0] is correct for js implementation?
|
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
yeah no worries. Sorry might need that v6 example. Just realised can only use v7 with react 18 and we haven't quite got there yet. Next on the list, but we need to to the muiv5 upgrade first :-( |
Beta Was this translation helpful? Give feedback.
-
I'll see if I can convert the example from v7 to v6 (and from TS to JS) tonight. |
Beta Was this translation helpful? Give feedback.
-
thanks really appreciate your time |
Beta Was this translation helpful? Give feedback.
-
Updated sandbox: https://codesandbox.io/p/devbox/xq7lpp?file=%2Fsrc%2FChannelRuleGroup.jsx%3A7%2C29 JS code for the custom rule group, based on RuleGroup.tsx from v6.5.5: import Typography from '@mui/material/Typography';
import * as React from 'react';
import {
TestID,
useRuleGroup,
useStopEventPropagation,
RuleGroupHeaderComponents,
RuleGroupBodyComponents,
} from 'react-querybuilder';
export const ChannelRuleGroup = props => {
const rg = { ...props, ...useRuleGroup(props) };
const { addRule, addGroup, cloneGroup, toggleLockGroup, removeGroup } = rg;
const methodsWithoutEventPropagation = useStopEventPropagation({
addRule,
addGroup,
cloneGroup,
toggleLockGroup,
removeGroup,
});
const subComponentProps = { ...rg, ...methodsWithoutEventPropagation };
return (
<div
ref={rg.previewRef}
className={rg.outerClassName}
data-testid={TestID.ruleGroup}
data-dragmonitorid={rg.dragMonitorId}
data-dropmonitorid={rg.dropMonitorId}
data-rule-group-id={rg.id}
data-level={rg.path.length}
data-path={JSON.stringify(rg.path)}>
<div ref={rg.dropRef} className={rg.classNames.header}>
{rg.path.length > 0 && (
<Typography variant="body1">
Output Channel: <strong>{rg.ruleGroup.channel ?? null}</strong>
</Typography>
)}
<RuleGroupHeaderComponents {...subComponentProps} />
</div>
<div className={rg.classNames.body}>
<RuleGroupBodyComponents {...subComponentProps} />
</div>
</div>
);
}; Refer to my previous comment for the RQB v7 code when you get around to upgrading. |
Beta Was this translation helpful? Give feedback.
-
Hi,
Great library thanks for the time and effort you have put into it. Totally appreciate it's such a chicken and egg complicated type problem so well done.
I've got quite far with it and during all the reading could have sworn I read somewhere about conditionally hiding fields, but can't seem to find reference to it now, so I could have just mistaken it for conditionally altering some other aspect of the rules.
What I am trying to do is to allow users to add output channels for their notification rules. Myt approach was going to be have a top level group with a 'channel' field allowing the selection of eg 'email, web, push' etc
When selecting the channel, then display the different fields related to that particular channel.
Any hints on how to do this (or maybe a better approach) would be greatly appreciated.
Cheers
Paul
Beta Was this translation helpful? Give feedback.
All reactions