must-match/must-not-match
--warhol-must-match
and --warhol-must-not-match
are two directives that
require certain elements to match (or not match) CSS selectors of your choice.
This is useful to constrain the ordering of component children. Consider the
following example component definition:
<!-- "Icon button" component defined in pattern library -->
<button class="iconButton">
<img class="iconButton__icon" src="checkmark.png" />
<span class="iconButton__text">OK</span>
</button>
Warhol's default laissez-faire approach to component children
means that it allows accepts number and any combination of child elements that
are present in the pattern library. The directives --warhol-must-match
and
--warhol-must-not-match
are one way to tighten up the requirements for the
component child elements. They allow you to provide one or more selectors that
the element in question must (--warhol-must-match
) or must not
(--warhol-must-not-match
) match.
If we wanted to make sure that there can at most be one icon and to ensure
that it is always the first child element inside an .iconButton
component,
we could change the component definition in the pattern library as follows:
<!-- "Icon button" component defined in pattern library -->
<button class="iconButton">
<img style="--warhol-must-match: ':first-child'" class="iconButton__icon" src="checkmark.png" />
<span class="iconButton__text">OK</span>
</button>
The use of the style
attribute is optional - the could just as well be defined
in a CSS file together with all of the other style directives. The quotes around
the selector are required for all but the simplest CSS selectors (like div
).
The property --warhol-must-match
does not count as style property but
instructs Warhol to complain if it encounters an .iconButton
component where
the icon is not the first child element:
<!-- "Icon button" components implemented in a production web site -->
<button class="iconButton">
<img class="icon" src="checkmark.png" /> <!-- no problem -->
<span class="text">OK</span>
</button>
<button class="iconButton">
<img class="icon" src="info.png" /> <!-- no problem -->
</button>
<button class="iconButton">
<span class="text">Cancel</span>
<img class="icon" src="cross.png" /> <!-- causes a "must match violation" error -->
</button>
Unsurprisingly, --warhol-must-match
requires an element to not match a
certain selector, in this case :first-child
. We could use this directive to
require the icon in an .iconButton
component to be followed up by some other
element:
<!-- "Icon button" component defined in pattern library -->
<button class="iconButton">
<img
style="--warhol-must-match: ':first-child'; --warhol-must-not-match: ':only-child'"
class="iconButton__icon"
src="checkmark.png" />
<span class="iconButton__text">OK</span>
</button>
This effectively forces every .iconButton
component to contain an icon and
a text element in exactly this order:
<!-- "Icon button" components implemented in a production web site -->
<button class="iconButton">
<img class="icon" src="checkmark.png" /> <!-- no problem -->
<span class="text">OK</span>
</button>
<button class="iconButton">
<img class="icon" src="info.png" /> <!-- causes a "must match violation" error -->
</button>
<button class="iconButton">
<span class="text">Cancel</span>
<img class="icon" src="cross.png" /> <!-- causes a "must match violation" error -->
</button>
The directives --warhol-must-match
and --warhol-must-not-match
are at the
moment the only way to restrict the order or force the presence of component
children. Other features like this will be added in the future.
The properties --warhol-must-match
and --warhol-must-not-match
are not
inherited by nested components.
Tips for debugging #
- Read the error message for any must-match violation carefully, as it
explains precisely which selector does not (or, in violation of
--warhol-must-not-match
, does) match the element that is being reported. - Check the snapshot error log in browser extension or the web app to make sure that Warhol was able to parse the selectors in your directives.
- Keep it simple!
--warhol-must-match
and--warhol-must-not-match
are a somewhat clunky way to express complex constraints. Better features for this exact purpose are on their way!