You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Makes the template patterns more self-documenting without the need for comments
Speeds debugging (I think the validation should only run when in HARD_MODE - i.e. not in production)
Makes it possible to do reflection on component props, enabling Storybook-style auto-generated UI for manipulating prop values in a future Craft living styleguide generator (imagine a UI with 2 frames: a rendered Twig component + autogenerated form UI for changing prop values, change the props and the template re-renders)
1 & 2 are immediate term wins, 3 is long term value
Implementation:
I’m already going to re-work defineProps() to use Yii’s DynamicModel (which I should have used in the first place TBH). That allows us to pass a Yii Model rules() definition as a second argument
Then we need a prop type syntax that is:
Straightforward to translate to Model rules
Fluent / not overly verbose
Can be gradually adopted / opt-in while supporting the current literal syntax
Much as it pains me to say anything positive about React, the propTypes fluent API is quite nice, and more readable/less verbose than a Vue style object definition. It also should be fairly easy to convert to Yii Model rules, I think something like this could work well.
We can / should directly support Craft elements as types, as this will be a common use case. We can either directly describe the shape of the element that we're actually using (via withShape()),or provide refinements in ElementQuery syntax: entry.section('news') or category.group('news')
Aside: maybe we could have a shorter alias of propType() (such as pt() / prop ) for brevity?
{% do defineProps(data, {
'entry': propType.element('craft\\elements\\Entry'),
}) %}
So supporting custom element types would be an easy lift
Craft Field types
Similarly, we should support Craft custom field types that we use frequently out of the box:
{% do defineProps(data, {
'action': propType.fields.linkIt,
'richText': propType.fields.redactor,
}) %}
And instances of other classes could either be supported dynamically with a isInstanceOf() method:
{% do defineProps(data, {
'map': propType.isInstanceOf('doublesecretagency\\googlemaps\\records\\Address'),
}) %}
Or declared in config:
<?php// config/conventions.phpreturn [
'propTypes' => [
'types' = [
'mapAddress' => [
'instanceOf' => 'doublesecretagency\\googlemaps\\records\\Address',
'validationStrategy' => 'Model', // instances are models so we can call validate() on them
],
],
],
];
Impersonating a class-typed prop
Often, we need pass data to a component that expects a class instance of say Entry, but what we actually have is a collection of dynamically assembled properties.
This is where asType() comes in. asType() is aware of the same types, fields and elements as defineProps() and will attempt to coerce the data into the type specified.
defineProps validation will then work in exactly the same way as if a "real" instance of the Type had been passed
e.g.
{# _components/card.twig #}
{% do defineProps(data, {
'entry': propType.entry.isRequired.withShape({
'title': propType.string.isRequired,
'description': propType.fields.redactor.isRequired,
'action': propType.fields.linkIt,
'image': propType.asset.isRequired,
}),
}) %}
{# index.twig #}
{% include'_components/card'with {
entry: asType.entry({
title: 'Fake card title',
image: assetFromSomewhere,
description: asType.fields.redactor('<p>Lorem ipsum dolor sit amet</p>'),
url: asType.fields.linkIt({
url: '/',
text: 'Take me to the river',
}),
})
} %}
Note: asType.elements.entry is probably more consistent, but it is more verbose, so I wonder if having the shortcut for "core" element types is worth it...
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Why this is useful
HARD_MODE
- i.e. not in production)1 & 2 are immediate term wins, 3 is long term value
Implementation:
I’m already going to re-work
defineProps()
to use Yii’s DynamicModel (which I should have used in the first place TBH). That allows us to pass a Yii Modelrules()
definition as a second argumentThen we need a prop type syntax that is:
Much as it pains me to say anything positive about React, the
propTypes
fluent API is quite nice, and more readable/less verbose than a Vue style object definition. It also should be fairly easy to convert to Yii Model rules, I think something like this could work well.Examples
Basic usage
Advanced usage
Nested objects:
optionalObjectWithShape: PropTypes.shape({
optionalProperty: PropTypes.string,
requiredProperty: PropTypes.number.isRequired
}),
We could abstract values to config for reuse/consistency across components
Craft Element types
We can / should directly support Craft elements as types, as this will be a common use case. We can either directly describe the shape of the element that we're actually using (via
withShape()
),or provide refinements inElementQuery
syntax:entry.section('news')
orcategory.group('news')
Aside: maybe we could have a shorter alias of
propType()
(such aspt()
/prop
) for brevity?collectionOf()
would work liketake()
, i.e. it would accept any of:Custom Element types
The above would be equivalent to:
So supporting custom element types would be an easy lift
Craft Field types
Similarly, we should support Craft custom field types that we use frequently out of the box:
And instances of other classes could either be supported dynamically with a
isInstanceOf()
method:Or declared in config:
Impersonating a class-typed prop
Often, we need pass data to a component that expects a class instance of say
Entry
, but what we actually have is a collection of dynamically assembled properties.This is where
asType()
comes in.asType()
is aware of the same types, fields and elements asdefineProps()
and will attempt to coerce the data into the type specified.defineProps
validation will then work in exactly the same way as if a "real" instance of the Type had been passede.g.
Note:
asType.elements.entry
is probably more consistent, but it is more verbose, so I wonder if having the shortcut for "core" element types is worth it...Beta Was this translation helpful? Give feedback.
All reactions