Skip to content

Commit acf3f5c

Browse files
committed
build a static version in React
1 parent 0a347dd commit acf3f5c

File tree

5 files changed

+170
-139
lines changed

5 files changed

+170
-139
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
body {
2+
padding: 5px;
3+
}
4+
label {
5+
display: block;
6+
margin-top: 5px;
7+
margin-bottom: 5px;
8+
}
9+
th {
10+
padding-top: 10px;
11+
}
12+
td {
13+
padding: 2px;
14+
padding-right: 40px;
15+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
{
3+
"category": "Fruits",
4+
"price": "$1",
5+
"stocked": true,
6+
"name": "Apple"
7+
},
8+
{
9+
"category": "Fruits",
10+
"price": "$1",
11+
"stocked": true,
12+
"name": "Dragonfruit"
13+
},
14+
{
15+
"category": "Fruits",
16+
"price": "$2",
17+
"stocked": false,
18+
"name": "Passionfruit"
19+
},
20+
{
21+
"category": "Vegetables",
22+
"price": "$2",
23+
"stocked": true,
24+
"name": "Spinach"
25+
},
26+
{
27+
"category": "Vegetables",
28+
"price": "$4",
29+
"stocked": false,
30+
"name": "Pumpkin"
31+
},
32+
{
33+
"category": "Vegetables",
34+
"price": "$1",
35+
"stocked": true,
36+
"name": "Peas"
37+
}
38+
]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from reactpy import component, html
2+
3+
4+
# start
5+
@component
6+
def product_category_row(category):
7+
return html.tr(html.th({"colSpan": "2"}, category))
8+
9+
10+
@component
11+
def product_row(product):
12+
if product["stocked"]:
13+
name = product["name"]
14+
else:
15+
name = html.span({"style": {"color": "red"}}, product["name"])
16+
return html.tr(html.td(name), html.td(product["price"]))
17+
18+
19+
@component
20+
def product_table(products):
21+
rows = []
22+
last_category = None
23+
for product in products:
24+
if product["category"] != last_category:
25+
rows.append(
26+
product_category_row(product["category"], key=product["category"])
27+
)
28+
rows.append(product_row(product, key=product["name"]))
29+
last_category = product["category"]
30+
31+
return html.table(
32+
html.thead(html.tr(html.th("Name"), html.th("Price"))), html.tbody(rows)
33+
)
34+
35+
36+
@component
37+
def search_bar():
38+
return html.form(
39+
html.input({"type": "text", "placeholder": "Search..."}),
40+
html.label(html.input({"type": "checkbox"}), "Only show products in stock"),
41+
)
42+
43+
44+
@component
45+
def filterable_product_table(products):
46+
return html.div(search_bar(), product_table(products))
47+
48+
49+
PRODUCTS = [
50+
{"category": "Fruits", "price": "$1", "stocked": True, "name": "Apple"},
51+
{"category": "Fruits", "price": "$1", "stocked": True, "name": "Dragonfruit"},
52+
{"category": "Fruits", "price": "$2", "stocked": False, "name": "Passionfruit"},
53+
{"category": "Vegetables", "price": "$2", "stocked": True, "name": "Spinach"},
54+
{"category": "Vegetables", "price": "$4", "stocked": False, "name": "Pumpkin"},
55+
{"category": "Vegetables", "price": "$1", "stocked": True, "name": "Peas"},
56+
]
57+
58+
59+
@component
60+
def app():
61+
return filterable_product_table(PRODUCTS)
62+
63+
64+
# end
65+
if __name__ == "__main__":
66+
from reactpy import run
67+
from reactpy.utils import _read_docs_css
68+
69+
@component
70+
def styled_app():
71+
return html._(html.style(_read_docs_css()), app())
72+
73+
run(styled_app)

docs/src/learn/thinking-in-react.md

Lines changed: 19 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,8 @@ Imagine that you already have a JSON API and a mockup from a designer.
1616

1717
The JSON API returns some data that looks like this:
1818

19-
```json
20-
[
21-
{ "category": "Fruits", "price": "$1", "stocked": true, "name": "Apple" },
22-
{
23-
"category": "Fruits",
24-
"price": "$1",
25-
"stocked": true,
26-
"name": "Dragonfruit"
27-
},
28-
{
29-
"category": "Fruits",
30-
"price": "$2",
31-
"stocked": false,
32-
"name": "Passionfruit"
33-
},
34-
{
35-
"category": "Vegetables",
36-
"price": "$2",
37-
"stocked": true,
38-
"name": "Spinach"
39-
},
40-
{
41-
"category": "Vegetables",
42-
"price": "$4",
43-
"stocked": false,
44-
"name": "Pumpkin"
45-
},
46-
{ "category": "Vegetables", "price": "$1", "stocked": true, "name": "Peas" }
47-
]
19+
```json linenums="0"
20+
{% include "../../examples/json/thinking_in_react/start_with_the_mockup.json" %}
4821
```
4922

5023
The mockup looks like this:
@@ -91,125 +64,35 @@ Now that you've identified the components in the mockup, arrange them into a hie
9164

9265
Now that you have your component hierarchy, it's time to implement your app. The most straightforward approach is to build a version that renders the UI from your data model without adding any interactivity... yet! It's often easier to build the static version first and add interactivity later. Building a static version requires a lot of typing and no thinking, but adding interactivity requires a lot of thinking and not a lot of typing.
9366

94-
To build a static version of your app that renders your data model, you'll want to build [components](your-first-component.md) that reuse other components and pass data using [props.](/learn/passing-props-to-a-component) Props are a way of passing data from parent to child. (If you're familiar with the concept of [state](/learn/state-a-components-memory), don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.)
67+
To build a static version of your app that renders your data model, you'll want to build [components](your-first-component.md) that reuse other components and pass data using [props.](../learn/passing-props-to-a-component.md) Props are a way of passing data from parent to child. (If you're familiar with the concept of [state](../learn/state-a-components-memory.md), don't use state at all to build this static version. State is reserved only for interactivity, that is, data that changes over time. Since this is a static version of the app, you don't need it.)
9568

9669
You can either build "top down" by starting with building the components higher up in the hierarchy (like `filterable_product_table`) or "bottom up" by working from components lower down (like `product_row`). In simpler examples, it’s usually easier to go top-down, and on larger projects, it’s easier to go bottom-up.
9770

98-
```jsx
99-
function product_category_row({ category }) {
100-
return (
101-
<tr>
102-
<th colSpan="2">{category}</th>
103-
</tr>
104-
);
105-
}
71+
=== "app.py"
10672

107-
function product_row({ product }) {
108-
const name = product.stocked ? (
109-
product.name
110-
) : (
111-
<span style={{ color: "red" }}>{product.name}</span>
112-
);
73+
```python
74+
{% include "../../examples/python/thinking_in_react/build_a_static_version_in_react.py" start="# start" end="# end" %}
75+
```
11376

114-
return (
115-
<tr>
116-
<td>{name}</td>
117-
<td>{product.price}</td>
118-
</tr>
119-
);
120-
}
77+
=== "styles.css"
12178

122-
function product_table({ products }) {
123-
const rows = [];
124-
let lastCategory = null;
79+
```css
80+
{% include "../../examples/css/thinking_in_react/build_a_static_version_in_react.css" %}
81+
```
12582

126-
products.forEach((product) => {
127-
if (product.category !== lastCategory) {
128-
rows.push(
129-
<product_category_row
130-
category={product.category}
131-
key={product.category}
132-
/>
133-
);
134-
}
135-
rows.push(<product_row product={product} key={product.name} />);
136-
lastCategory = product.category;
137-
});
138-
139-
return (
140-
<table>
141-
<thead>
142-
<tr>
143-
<th>Name</th>
144-
<th>Price</th>
145-
</tr>
146-
</thead>
147-
<tbody>{rows}</tbody>
148-
</table>
149-
);
150-
}
151-
152-
function search_bar() {
153-
return (
154-
<form>
155-
<input type="text" placeholder="Search..." />
156-
<label>
157-
<input type="checkbox" /> Only show products in stock
158-
</label>
159-
</form>
160-
);
161-
}
162-
163-
function filterable_product_table({ products }) {
164-
return (
165-
<div>
166-
<search_bar />
167-
<product_table products={products} />
168-
</div>
169-
);
170-
}
171-
172-
const PRODUCTS = [
173-
{ category: "Fruits", price: "$1", stocked: true, name: "Apple" },
174-
{ category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" },
175-
{ category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" },
176-
{ category: "Vegetables", price: "$2", stocked: true, name: "Spinach" },
177-
{ category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin" },
178-
{ category: "Vegetables", price: "$1", stocked: true, name: "Peas" },
179-
];
180-
181-
export default function App() {
182-
return <filterable_product_table products={PRODUCTS} />;
183-
}
184-
```
185-
186-
```css
187-
body {
188-
padding: 5px;
189-
}
190-
label {
191-
display: block;
192-
margin-top: 5px;
193-
margin-bottom: 5px;
194-
}
195-
th {
196-
padding-top: 10px;
197-
}
198-
td {
199-
padding: 2px;
200-
padding-right: 40px;
201-
}
202-
```
83+
=== ":material-play: Run"
20384

204-
(If this code looks intimidating, go through the [Quick Start](/learn/) first!)
85+
```python
86+
# TODO
87+
```
20588

206-
After building your components, you'll have a library of reusable components that render your data model. Because this is a static app, the components will only return JSX. The component at the top of the hierarchy (`filterable_product_table`) will take your data model as a prop. This is called _one-way data flow_ because the data flows down from the top-level component to the ones at the bottom of the tree.
89+
(If this code looks intimidating, go through the [Quick Start](../learn/get-started.md) first!)
20790

208-
<Pitfall>
91+
After building your components, you'll have a library of reusable components that render your data model. Because this is a static app, the components will only return non-interactive HTML. The component at the top of the hierarchy (`filterable_product_table`) will take your data model as a prop. This is called _one-way data flow_ because the data flows down from the top-level component to the ones at the bottom of the tree.
20992

210-
At this point, you should not be using any state values. That’s for the next step!
93+
!!! warning "Pitfall"
21194

212-
</Pitfall>
95+
At this point, you should not be using any state values. That’s for the next step!
21396

21497
## Step 3: Find the minimal but complete representation of UI state
21598

docs/src/static/css/extra.css

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,31 @@
105105
background-color: rgb(68 172 153);
106106
}
107107

108-
.md-typeset .note>.admonition-title:before,.md-typeset .note>summary:before {
109-
-webkit-mask-image: var(--md-admonition-icon--abstract);
110-
mask-image: var(--md-admonition-icon--abstract)
108+
.md-typeset .note > .admonition-title:before,
109+
.md-typeset .note > summary:before {
110+
-webkit-mask-image: var(--md-admonition-icon--abstract);
111+
mask-image: var(--md-admonition-icon--abstract);
112+
}
113+
114+
/* Colors for "warning" admonition */
115+
[data-md-color-scheme="slate"] .md-typeset .admonition.warning,
116+
[data-md-color-scheme="slate"] .md-typeset details.warning {
117+
background: rgb(182 87 0 / 0.2);
118+
padding: 0.8rem 1.4rem;
119+
border-radius: 0.8rem;
120+
}
121+
122+
[data-md-color-scheme="slate"] .md-typeset .warning .admonition-title,
123+
[data-md-color-scheme="slate"] .md-typeset .warning summary {
124+
font-size: 1rem;
125+
background: transparent;
126+
padding-bottom: 0;
127+
color: rgb(219 125 39);
128+
}
129+
130+
[data-md-color-scheme="slate"] .md-typeset .warning .admonition-title:before {
131+
font-size: 1.1rem;
132+
background-color: rgb(219 125 39);
111133
}
112134

113135
/* Move the sidebars to the edges of the page */

0 commit comments

Comments
 (0)