Skip to content

Commit

Permalink
Merge pull request #2218 from siner308/required-children-prop
Browse files Browse the repository at this point in the history
required children prop
  • Loading branch information
alexcjohnson authored Sep 12, 2022
2 parents d02017d + d956adb commit 6481306
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { RequiredChildrenComponentProps } from "../props";


const RequiredChildrenComponent = (props: RequiredChildrenComponentProps) => {
const {children} = props;
return (
<div>
{children}
</div>
)
}

export default RequiredChildrenComponent;
2 changes: 2 additions & 0 deletions @plotly/dash-generator-test-component-typescript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import WrappedHTML from './components/WrappedHTML';
import FCComponent from './components/FCComponent';
import EmptyComponent from './components/EmptyComponent';
import MixedComponent from './components/MixedComponent';
import RequiredChildrenComponent from './components/RequiredChildrenComponent';

export {
TypeScriptComponent,
Expand All @@ -16,4 +17,5 @@ export {
FCComponent,
EmptyComponent,
MixedComponent,
RequiredChildrenComponent,
};
4 changes: 4 additions & 0 deletions @plotly/dash-generator-test-component-typescript/src/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ export type WrappedHTMLProps = {
children?: React.ReactNode;
id?: string;
} & Pick<React.ButtonHTMLAttributes<any>, 'autoFocus'>

export type RequiredChildrenComponentProps = {
children: React.ReactNode;
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).

### Fixed

- [#2218](https://github.com/plotly/dash/pull/2218) Fix bug [#1348](https://github.com/plotly/dash/issues/1348) Validate children prop (required or not).
- [#2223](https://github.com/plotly/dash/pull/2223) Exclude hidden folders when building `dash.page_registry`.
- [#2182](https://github.com/plotly/dash/pull/2182) Fix [#2172](https://github.com/plotly/dash/issues/2172) Make it so that when using pages, if `suppress_callback_exceptions=True` the `validation_layout` is not set.
- [#2152](https://github.com/plotly/dash/pull/2152) Fix bug [#2128](https://github.com/plotly/dash/issues/2128) preventing rendering of multiple components inside a dictionary.
Expand Down
62 changes: 42 additions & 20 deletions dash/development/_py_components_generation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from collections import OrderedDict
import copy
import os
from textwrap import fill
from textwrap import fill, dedent

from dash.development.base_component import _explicitize_args
from dash.exceptions import NonExistentEventException
Expand Down Expand Up @@ -65,11 +65,8 @@ def __init__(self, {default_argtext}):
_explicit_args = kwargs.pop('_explicit_args')
_locals = locals()
_locals.update(kwargs) # For wildcard attrs and excess named props
args = {{k: _locals[k] for k in _explicit_args if k != 'children'}}
for k in {required_props}:
if k not in args:
raise TypeError(
'Required argument `' + k + '` was not specified.')
args = {args}
{required_validation}
super({typename}, self).__init__({argtext})
'''

Expand All @@ -87,18 +84,40 @@ def __init__(self, {default_argtext}):
description=description,
prop_reorder_exceptions=prop_reorder_exceptions,
).replace("\r\n", "\n")
required_args = required_props(filtered_props)
is_children_required = 'children' in required_args
required_args = [arg for arg in required_args if arg != "children"]

prohibit_events(props)

# pylint: disable=unused-variable
prop_keys = list(props.keys())
if "children" in props:
if "children" in props and "children" in list_of_valid_keys:
prop_keys.remove("children")
default_argtext = "children=None, "
args = "{k: _locals[k] for k in _explicit_args if k != 'children'}"
argtext = "children=children, **args"
else:
default_argtext = ""
args = "{k: _locals[k] for k in _explicit_args}"
argtext = "**args"

if len(required_args) == 0:
required_validation = ""
else:
required_validation = f"""
for k in {required_args}:
if k not in args:
raise TypeError(
'Required argument `' + k + '` was not specified.')
"""

if is_children_required:
required_validation += """
if 'children' not in _explicit_args:
raise TypeError('Required argument children was not specified.')
"""

default_arglist = [
(
f"{p:s}=Component.REQUIRED"
Expand All @@ -121,20 +140,23 @@ def __init__(self, {default_argtext}):
)

default_argtext += ", ".join(default_arglist + ["**kwargs"])
required_args = required_props(filtered_props)
nodes = collect_nodes({k: v for k, v in props.items() if k != "children"})
return c.format(
typename=typename,
namespace=namespace,
filtered_props=filtered_props,
list_of_valid_wildcard_attr_prefixes=wildcard_prefixes,
list_of_valid_keys=list_of_valid_keys,
docstring=docstring,
default_argtext=default_argtext,
argtext=argtext,
required_props=required_args,
children_props=nodes,
base_nodes=filter_base_nodes(nodes) + ["children"],

return dedent(
c.format(
typename=typename,
namespace=namespace,
filtered_props=filtered_props,
list_of_valid_wildcard_attr_prefixes=wildcard_prefixes,
list_of_valid_keys=list_of_valid_keys,
docstring=docstring,
default_argtext=default_argtext,
args=args,
argtext=argtext,
required_validation=required_validation,
children_props=nodes,
base_nodes=filter_base_nodes(nodes) + ["children"],
)
)


Expand Down
8 changes: 8 additions & 0 deletions tests/integration/test_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
TypeScriptComponent,
TypeScriptClassComponent,
StandardComponent,
RequiredChildrenComponent,
)
from dash_test_components import StyledComponent
from dash.html import Button, Div
Expand Down Expand Up @@ -99,3 +100,10 @@ def test_gene003_max_props():

with pytest.raises(TypeError):
MyNestedComponent(valuey="nor this")


def test_gene004_required_children_prop():
with pytest.raises(TypeError):
RequiredChildrenComponent()

RequiredChildrenComponent(children='worked')
5 changes: 1 addition & 4 deletions tests/unit/development/metadata_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,5 @@ def __init__(self, children=None, optionalArray=Component.UNDEFINED, optionalBoo
_locals = locals()
_locals.update(kwargs) # For wildcard attrs and excess named props
args = {k: _locals[k] for k in _explicit_args if k != 'children'}
for k in []:
if k not in args:
raise TypeError(
'Required argument `' + k + '` was not specified.')

super(Table, self).__init__(children=children, **args)
5 changes: 5 additions & 0 deletions tests/unit/development/test_base_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,3 +534,8 @@ def update(v):
@app.callback(Output(output1, "children"), Input(input1, "value"))
def update2(v):
return f"Input 1 {v}"


def test_debc030_invalid_children_args():
with pytest.raises(TypeError):
dcc.Input(children='invalid children')

0 comments on commit 6481306

Please sign in to comment.