Rules
The Rules pattern allows users to build up a conditional logic statement in a visual way.
Anatomy of a rule
A complete rule is made up of blocks, which define specific conditions, and links, which allow additional blocks to be added.
── Rule container
└── rule block
└── rule block header
└── rule block controls
└── rule block control
└── rule links
└── rule link
For a rule which is structured in the IF/ELSE/AND/OR/RETURN format, the blocks and links are typically arranged within the following example heirarchy.
── IF block
| | └── OR link
| |── AND block
| | └── OR link
| |─── AND link
| └── RETURN block
|── ELSE IF link
└── ELSE RETURN block
Rule blocks
A rule block represents a single logical condition that must be matched for the rule to take this route. The controls within the rule block will be specific to the context of the parent user interface.
The rule classes define how the block is visually styled, the block must use the parent .rule-block
class.
.rule-block--if
.rule-block--and
.rule-block--or
.rule-block--else
.rule-block--else-if
.rule-block--return
.rule-block--else-return
<div id="rule-form-container-1" class="rules-container rules-container--v2">
<fieldset class="rule-block rule-block--if">
<legend class="hide">If rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">if</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
<div class="rule-block__control">
{{
form.select({
'class': 'form__group--small',
'id': 'guid-' ~ random(),
'label': 'Choose logic',
'show-label': false,
'options': [
{
'label': 'is',
'value': 'value1'
},
{
'label': 'is not',
'value': 'value2'
}
]
})
}}
</div>
<div class="rule-block__control">
{{
form.select({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose value',
'show-label': false,
'placeholder': 'Choose value',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
</div>
Removable blocks
Any blocks that a user has added by using the rule links should be removeable, this is achieved by adding the remove control as the last child of the rule block. These controls should also be present if a saved rule is loaded.
The first IF block, as well as all RETURN and ELSE RETURN blocks should not be removeable.
<div id="rule-form-container-2" class="rules-container rules-container--v2">
<fieldset class="rule-block rule-block--and">
<legend class="hide">And rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">And</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'placeholder': 'Choose condition',
'show-label': false,
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
<button data-action="remove" data-tippy-content="Remove this item" data-tippy-placement="left" class="btn remove-button js-remove-condition"><i class="icon-remove-sign"><span class="hide">Remove this item</span></i></button>
</fieldset>
</div>
Indented blocks
When an OR block immediately follows an IF, AND or ELSE-IF rule, it should be indented using a rule-block--indent
wrapper. Any rule links relating to this indented block should also be contained within the indent wrapper to maintain the layout of the ruleset.
If an OR block follows another OR block, it does not need to be further indented.
...
<div id="rule-form-container-3" class="rules-container rules-container--v2">
<fieldset class="rule-block rule-block--if">
<legend class="hide">If rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">if</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'placeholder': 'Choose condition',
'show-label': false,
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
<div class="rule-block--indent">
<fieldset class="rule-block rule-block--or">
<legend class="hide">Or rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">or</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'placeholder': 'Choose condition',
'show-label': false,
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
<button data-action="remove" data-tippy-content="Remove this item" data-tippy-placement="left" class="btn remove-button js-remove-trigger"><i class="icon-remove-sign"><span class="hide">Remove this item</span></i></button>
</fieldset>
<fieldset class="rule-block rule-block--or">
<legend class="hide">Or rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">or</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'placeholder': 'Choose condition',
'show-label': false,
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
<button data-action="remove" data-tippy-content="Remove this item" data-tippy-placement="left" class="btn remove-button js-remove-condition"><i class="icon-remove-sign"><span class="hide">Remove this item</span></i></button>
</fieldset>
<div class="rule-links">
<div class="rule-link rule-link--or">
<button class="btn" aria-label="Add a new OR condition">or</button>
</div>
</div>
</div><!-- /.ruleblock--indent -->
...
</div><!-- /.rules-container -->
Ruleset examples
Basic
This example demonstrates the starting point of an IF/ELSEIF/AND/OR/RETURN type ruleset.
<div id="rule-form-container-4" class="rules-container rules-container--v2">
<fieldset class="rule-block rule-block--if">
<legend class="hide">If rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">if</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
<div class="rule-links">
<div class="rule-link rule-link--or">
<button class="btn" aria-label="Add a new OR condition">or</button>
</div>
<div class="rule-link rule-link--and">
<button class="btn" aria-label="Add a new AND condition">and</button>
</div>
</div>
<fieldset class="rule-block rule-block--return">
<legend class="hide">Return</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">return</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
<div class="rule-links">
<div class="rule-link rule-link--else-if">
<button class="btn">else if</button>
</div>
</div>
<fieldset class="rule-block rule-block--else-return">
<legend class="hide">Else return</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">else return</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
</div><!-- /.rules-container -->
Full
This example shows a complex rule with various conditions visible, this is here for illustration purposes only.
<div id="rule-form-container-5" class="rules-container rules-container--v2">
<fieldset class="rule-block rule-block--if">
<legend class="hide">If rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">if</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
<div class="rule-block--indent">
<fieldset class="rule-block rule-block--or">
<legend class="hide">Or rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">or</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
<button data-action="remove" data-tippy-content="Remove this item" data-tippy-placement="left" class="btn remove-button js-remove-condition"><i class="icon-remove-sign"><span class="hide">Remove this item</span></i></button>
</fieldset>
<fieldset class="rule-block rule-block--or">
<legend class="hide">Or rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">or</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
<button data-action="remove" data-tippy-content="Remove this item" data-tippy-placement="left" class="btn remove-button js-remove-condition"><i class="icon-remove-sign"><span class="hide">Remove this item</span></i></button>
</fieldset>
<div class="rule-links">
<div class="rule-link rule-link--or">
<button class="btn" aria-label="Add a new OR condition">or</button>
</div>
</div>
</div><!-- /.ruleblock--indent -->
<fieldset class="rule-block rule-block--and">
<legend class="hide">And rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">and</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
<button data-action="remove" data-tippy-content="Remove this item" data-tippy-placement="left" class="btn remove-button js-remove-condition"><i class="icon-remove-sign"><span class="hide">Remove this item</span></i></button>
</fieldset>
<div class="rule-block--indent">
<fieldset class="rule-block rule-block--or">
<legend class="hide">Or rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">or</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
<button data-action="remove" data-tippy-content="Remove this item" data-tippy-placement="left" class="btn remove-button js-remove-condition"><i class="icon-remove-sign"><span class="hide">Remove this item</span></i></button>
</fieldset>
<div class="rule-links">
<div class="rule-link rule-link--or">
<button class="btn" aria-label="Add a new OR condition">or</button>
</div>
</div>
</div><!-- /.ruleblock--indent -->
<div class="rule-links">
<div class="rule-link rule-link--and">
<button class="btn" aria-label="Add a new AND condition">and</button>
</div>
</div>
<fieldset class="rule-block rule-block--return">
<legend class="hide">Return</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">return</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose return value',
'show-label': false,
'placeholder': 'Choose return value',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
<div class="rule-links">
<div class="rule-link rule-link--else-if">
<button class="btn">else if</button>
</div>
</div>
<fieldset class="rule-block rule-block--else-if">
<legend class="hide">Else If rule condition</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">else if</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose condition',
'show-label': false,
'placeholder': 'Choose condition',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
<button data-action="remove" data-tippy-content="Remove this item" data-tippy-placement="left" class="btn remove-button js-remove-condition"><i class="icon-remove-sign"><span class="hide">Remove this item</span></i></button>
</fieldset>
<div class="rule-links">
<div class="rule-link rule-link--or">
<button class="btn" aria-label="Add a new OR condition">or</button>
</div>
<div class="rule-link rule-link--and">
<button class="btn" aria-label="Add a new AND condition">and</button>
</div>
</div>
<fieldset class="rule-block rule-block--return">
<legend class="hide">Return</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">return</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose return value',
'show-label': false,
'placeholder': 'Choose return value',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
<div class="rule-links">
<div class="rule-link rule-link--else-if">
<button class="btn">else if</button>
</div>
</div>
<fieldset class="rule-block rule-block--else-return">
<legend class="hide">Else return</legend>
<div class="rule-block__header">
<h2 class="rule-block__heading">else return</h2>
</div>
<div class="rule-block__controls">
<div class="rule-block__control">
{{
form.select2({
'class': 'form__group--medium',
'id': 'guid-' ~ random(),
'label': 'Choose return value',
'show-label': false,
'placeholder': 'Choose return value',
'options': [
{
'label': 'Value one',
'value': 'value1'
},
{
'label': 'Value two',
'value': 'value2'
}
]
})
}}
</div>
</div>
</fieldset>
</div><!-- /.rules-container -->
Interactions
Currently, there is no associated JavaScript component to enable interactions for the rules user interface.