Skip to content

Commit

Permalink
Add support for multiple StackSwitch on one StackPage (#1049)
Browse files Browse the repository at this point in the history
* Add support for multiple StackSwitch on one StackPage

This allows having a Composite block with two sub blocks where each has it's own subroutes (eg a list)

Add a SubRoute wrapper for this case that needs to be added in front of the tested StckSwitch and do that
for all composite blocks

* Add changeset
  • Loading branch information
nsams authored May 11, 2023
1 parent 71103a2 commit a711678
Show file tree
Hide file tree
Showing 15 changed files with 637 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/twenty-parents-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@comet/blocks-admin": minor
---

Allow Composite block with multiple sub blocks that have their own subroutes (eg a list)
8 changes: 8 additions & 0 deletions .changeset/warm-feet-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@comet/admin": minor
---

Add support for multiple StackSwitch on one StackPage

Add a SubRoute wrapper for this case that needs to be added in front of the tested StckSwitch and do that
for all composite blocks
2 changes: 2 additions & 0 deletions demo/admin/src/pages/PageContentBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ColumnsBlock } from "./blocks/ColumnsBlock";
import { FullWidthImageBlock } from "./blocks/FullWidthImageBlock";
import { HeadlineBlock } from "./blocks/HeadlineBlock";
import { TextImageBlock } from "./blocks/TextImageBlock";
import { TwoListsBlock } from "./blocks/TwoListsBlock";

export const PageContentBlock = createBlocksBlock({
name: "PageContent",
Expand All @@ -26,6 +27,7 @@ export const PageContentBlock = createBlocksBlock({
fullWidthImage: FullWidthImageBlock,
columns: ColumnsBlock,
anchor: AnchorBlock,
twoLists: TwoListsBlock,
},
additionalItemFields: {
...userGroupAdditionalItemFields,
Expand Down
23 changes: 23 additions & 0 deletions demo/admin/src/pages/blocks/TwoListsBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createCompositeBlock, createListBlock } from "@comet/blocks-admin";

import { HeadlineBlock } from "./HeadlineBlock";

const TwoListsListBlock = createListBlock({
name: "TwoListsList",
block: HeadlineBlock,
});

export const TwoListsBlock = createCompositeBlock({
name: "TwoLists",
displayName: "Two Lists",
blocks: {
list1: {
block: TwoListsListBlock,
title: "List 1",
},
list2: {
block: TwoListsListBlock,
title: "List 2",
},
},
});
91 changes: 90 additions & 1 deletion demo/api/block-meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,8 @@
"LinkList",
"FullWidthImage",
"Columns",
"Anchor"
"Anchor",
"TwoLists"
],
"nullable": false
},
Expand Down Expand Up @@ -1587,6 +1588,94 @@
}
]
},
{
"name": "TwoLists",
"fields": [
{
"name": "list1",
"kind": "Block",
"block": "TwoListsList",
"nullable": false
},
{
"name": "list2",
"kind": "Block",
"block": "TwoListsList",
"nullable": false
}
],
"inputFields": [
{
"name": "list1",
"kind": "Block",
"block": "TwoListsList",
"nullable": false
},
{
"name": "list2",
"kind": "Block",
"block": "TwoListsList",
"nullable": false
}
]
},
{
"name": "TwoListsList",
"fields": [
{
"name": "blocks",
"kind": "NestedObjectList",
"object": {
"fields": [
{
"name": "key",
"kind": "String",
"nullable": false
},
{
"name": "visible",
"kind": "Boolean",
"nullable": false
},
{
"name": "props",
"kind": "Block",
"block": "Headline",
"nullable": false
}
]
},
"nullable": false
}
],
"inputFields": [
{
"name": "blocks",
"kind": "NestedObjectList",
"object": {
"fields": [
{
"name": "key",
"kind": "String",
"nullable": false
},
{
"name": "visible",
"kind": "Boolean",
"nullable": false
},
{
"name": "props",
"kind": "Block",
"block": "Headline",
"nullable": false
}
]
},
"nullable": false
}
]
},
{
"name": "YouTubeVideo",
"fields": [
Expand Down
2 changes: 2 additions & 0 deletions demo/api/src/pages/blocks/PageContentBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ColumnsBlock } from "./columns.block";
import { FullWidthImageBlock } from "./full-width-image.block";
import { HeadlineBlock } from "./headline.block";
import { TextImageBlock } from "./TextImageBlock";
import { TwoListsBlock } from "./two-lists.block";

const supportedBlocks = {
space: SpaceBlock,
Expand All @@ -22,6 +23,7 @@ const supportedBlocks = {
fullWidthImage: FullWidthImageBlock,
columns: ColumnsBlock,
anchor: AnchorBlock,
twoLists: TwoListsBlock,
};

class BlocksBlockItemData extends BaseBlocksBlockItemData(supportedBlocks) {
Expand Down
40 changes: 40 additions & 0 deletions demo/api/src/pages/blocks/two-lists.block.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
BlockData,
BlockDataInterface,
BlockInput,
ChildBlock,
ChildBlockInput,
createBlock,
createListBlock,
ExtractBlockData,
inputToData,
} from "@comet/blocks-api";
import { ValidateNested } from "class-validator";

import { HeadlineBlock } from "./headline.block";

const TwoListsList = createListBlock({ block: HeadlineBlock }, "TwoListsList");

class TwoListsBlockData extends BlockData {
@ChildBlock(TwoListsList)
list1: ExtractBlockData<typeof TwoListsList>;

@ChildBlock(TwoListsList)
list2: ExtractBlockData<typeof TwoListsList>;
}

class TwoListsBlockInput extends BlockInput {
@ChildBlockInput(TwoListsList)
@ValidateNested()
list1: ExtractBlockData<typeof TwoListsList>;

@ChildBlockInput(TwoListsList)
@ValidateNested()
list2: ExtractBlockData<typeof TwoListsList>;

transformToBlockData(): BlockDataInterface {
return inputToData(TwoListsBlockData, this);
}
}

export const TwoListsBlock = createBlock(TwoListsBlockData, TwoListsBlockInput, "TwoLists");
105 changes: 105 additions & 0 deletions packages/admin/admin-stories/src/admin/router/SubRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { SubRoute, SubRouteIndexRoute, useSubRoutePrefix } from "@comet/admin";
import { storiesOf } from "@storybook/react";
import * as React from "react";
import { Redirect, Route, Switch, useLocation, useRouteMatch } from "react-router";
import { Link } from "react-router-dom";

import { storyRouterDecorator } from "../../story-router.decorator";

function Cmp1() {
const urlPrefix = useSubRoutePrefix();
return (
<>
<Switch>
<Route path={`${urlPrefix}/sub`}>
<div>Cmp1 Sub</div>
</Route>
<SubRouteIndexRoute>
<div>
<Link to={`${urlPrefix}/sub`}>Cmp1 SubLink</Link>
</div>
</SubRouteIndexRoute>
</Switch>
</>
);
}

function Cmp2() {
const urlPrefix = useSubRoutePrefix();
return (
<>
<Switch>
<Route path={`${urlPrefix}/sub`}>
<div>
<Link to={`${urlPrefix}/sub`}>Sub</Link>
<br />
<Link to={`${urlPrefix}/sub/sub2`}>Sub2</Link>
</div>
<Switch>
<Route path={`${urlPrefix}/sub/sub2`}>
<div>Cmp2 Sub2</div>
</Route>
<Route>
<div>Cmp2 Sub</div>
</Route>
</Switch>
</Route>
<SubRouteIndexRoute>
<div>
<Link to={`${urlPrefix}/sub`}>Cmp2 SubLink</Link>
</div>
</SubRouteIndexRoute>
</Switch>
</>
);
}

function Story() {
const match = useRouteMatch();
return (
<div>
<SubRoute path={`${match.url}/cmp1`}>
<div>
<Cmp1 />
</div>
</SubRoute>
<SubRoute path={`${match.url}/cmp2`}>
<div>
<Cmp2 />
</div>
</SubRoute>
</div>
);
}

function Path() {
const location = useLocation();
const [, rerender] = React.useState(0);
React.useEffect(() => {
const timer = setTimeout(() => {
rerender(new Date().getTime());
}, 1000);
return () => clearTimeout(timer);
}, []);
return <div>{location.pathname}</div>;
}

function App() {
return (
<>
<Path />
<Switch>
<Route exact path="/">
<Redirect to="/foo" />
</Route>
<Route path="/foo">
<Story />
</Route>
</Switch>
</>
);
}

storiesOf("@comet/admin/stack", module)
.addDecorator(storyRouterDecorator())
.add("Subroute", () => <App />);
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Stack, StackBreadcrumbs, StackLink, StackPage, StackSwitch, SubRoute, useSubRoutePrefix } from "@comet/admin";
import { storiesOf } from "@storybook/react";
import * as React from "react";
import { Redirect, Route, Switch, useLocation } from "react-router";

import { storyRouterDecorator } from "../../story-router.decorator";

function Story() {
const urlPrefix = useSubRoutePrefix();
return (
<Stack topLevelTitle="Stack">
<StackBreadcrumbs />
<SubRoute path={`${urlPrefix}/first`}>
<StackSwitch>
<StackPage name="page1">
<p>First Page1</p>
<StackLink pageName="page2" payload="test">
activate page2
</StackLink>
</StackPage>
<StackPage name="page2">First Page2</StackPage>
</StackSwitch>
</SubRoute>
<SubRoute path={`${urlPrefix}/second`}>
<StackSwitch>
<StackPage name="page1">
<p>Second Page1</p>
<StackLink pageName="page2" payload="test">
activate page2
</StackLink>
</StackPage>
<StackPage name="page2">Second Page2</StackPage>
</StackSwitch>
</SubRoute>
</Stack>
);
}

function Path() {
const location = useLocation();
const [, rerender] = React.useState(0);
React.useEffect(() => {
const timer = setTimeout(() => {
rerender(new Date().getTime());
}, 100);
return () => clearTimeout(timer);
}, []);
return <div>{location.pathname}</div>;
}

function App() {
return (
<>
<Path />
<Switch>
<Route exact path="/">
<Redirect to="/foo" />
</Route>
<Route path="/foo">
<Story />
</Route>
</Switch>
</>
);
}

storiesOf("@comet/admin/stack", module)
.addDecorator(storyRouterDecorator())
.add("Stack Nested one Stack", () => <App />);
Loading

0 comments on commit a711678

Please sign in to comment.