Skip to content

Commit

Permalink
feat(react-core): base version
Browse files Browse the repository at this point in the history
  • Loading branch information
Aysnine committed Dec 14, 2021
1 parent ba79931 commit 62db91c
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 82 deletions.
44 changes: 44 additions & 0 deletions packages/react-core/src/__snapshots__/index.spec.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`React Core should render successfully 1`] = `
<body>
<div>
<div
class="craft-root"
>
<div
class="component-a"
>
<b>
Component A:
1
</b>
<div
class="component-a-children"
>
<div
class="component-b"
>
<b>
Component B:
2
</b>
<div
class="component-b-children"
>
<div
class="component-c"
>
<b>
Component C:
3
</b>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
`;
126 changes: 126 additions & 0 deletions packages/react-core/src/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { render } from '@testing-library/react';
import { ExUnused, NodeMeta, NodeMetaBase, RootMeta } from '@d2-craft/typed';
import { CraftProvider, CraftRoot, useCraftNode, makeComponentMap, CraftNode, useCraftProvider } from './index'
import React from 'react';

describe('React Core', () => {
it('should render successfully', () => {

// Example component A
type ComponentMetaA = ExMeta<'A', { a: string }>;

const ComponentA: React.FC = () => {
const { meta } = useCraftNode<ComponentMetaA>()
const { componentMap } = useCraftProvider()

return (
<div className="component-a">
<b>Component A: { meta.config?.a }</b>
<div className="component-a-children">
{
meta.children?.map((child, key) => {
const Component = componentMap.get(child.component)
return Component && (
<CraftNode meta={child} key={key}>
<Component />
</CraftNode>
)
})
}
</div>
</div>
)
}

// Example component B
type ComponentMetaB = ExMeta<'B', { b: string }>

const ComponentB: React.FC = () => {
const { meta } = useCraftNode<ComponentMetaB>()
const { componentMap } = useCraftProvider()

return (
<div className="component-b">
<b>Component B: { meta.config?.b }</b>
<div className="component-b-children">
{
meta.children?.map((child, key) => {
const Component = componentMap.get(child.component)
return Component && (
<CraftNode meta={child} key={key}>
<Component />
</CraftNode>
)
})
}
</div>
</div>
)
}

// Example component C
type ComponentMetaC = ExMeta<'C', { c: string }>

const ComponentC: React.FC = () => {
const { meta } = useCraftNode<ComponentMetaC>()

return (
<div className="component-c">
<b>Component C: { meta.config?.c }</b>
</div>
)
}

// Recursive components meta
type ExMeta<N, C> = NodeMetaBase<N, C, NodeMeta<ExMetaTree>>
type ExMetaTree<Ex = ExUnused> = NodeMeta< Ex
| ComponentMetaA
| ComponentMetaB
| ComponentMetaC
>

// Components map
const componentMap = makeComponentMap()
.append<ComponentMetaA>('A', ComponentA)
.append<ComponentMetaB>('B', ComponentB)
.append<ComponentMetaC>('C', ComponentC)
.value()

// Example data
const rootMeta: RootMeta<ExMetaTree> = {
children: [{
component: 'A',
config: { a: '1' },
children: [{
component: 'B',
config: { b: '2' },
children: [{
component: 'C',
config: { c: '3' }
}]
}]
}]
}

// Three layer construct
const { baseElement } = render(
<CraftProvider componentMap={componentMap}>
<CraftRoot<ExMetaTree> meta={rootMeta}>
<div className="craft-root">
{rootMeta.children.map((child, key) => {
const Component = componentMap.get(child.component)
return Component && (
<CraftNode meta={child} key={key}>
<Component />
</CraftNode>
)
})}
</div>
</CraftRoot>
</CraftProvider>
);

expect(baseElement).toBeTruthy();
expect(baseElement).toMatchSnapshot()
});
});
10 changes: 0 additions & 10 deletions packages/react-core/src/lib/craft-node/craft-node.spec.tsx

This file was deleted.

23 changes: 12 additions & 11 deletions packages/react-core/src/lib/craft-node/craft-node.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { createContext, useContext } from "react";
import { NodeMeta } from "@d2-craft/typed";
import { NodeMeta, exUnusedMeta } from "@d2-craft/typed";

export interface CraftNodeProps<ExtendableNodeMeta> {
meta: NodeMeta<ExtendableNodeMeta>
export interface CraftNodeProps {
meta: unknown
}

export function CraftNode<ExtendableNodeMeta>(props: CraftNodeProps<ExtendableNodeMeta>) {
export const CraftNode: React.FC<CraftNodeProps> = (props) => {
return (
<div>
<h1>Welcome to CraftNode!</h1>
</div>
<CraftNodeContext.Provider value={{ __id: 'TODO', meta: props.meta }}>
{props.children}
</CraftNodeContext.Provider>
);
}

Expand All @@ -19,12 +19,13 @@ export default CraftNode;

export const CraftNodeContext = createContext<{
__id: string;
meta: NodeMeta
meta: unknown
}>({
__id: '',
meta: null
meta: exUnusedMeta as unknown
})

export const useCraftNodeContext = () => {
return useContext(CraftNodeContext)
export const useCraftNode = <MetaType,>() => {
const { meta, __id } = useContext(CraftNodeContext)
return { meta: meta as unknown as MetaType, __id }
}
10 changes: 0 additions & 10 deletions packages/react-core/src/lib/craft-provider/craft-provider.spec.tsx

This file was deleted.

33 changes: 27 additions & 6 deletions packages/react-core/src/lib/craft-provider/craft-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext, useCallback, useContext, useMemo } from "react";
import React, { createContext, useCallback, useContext, useMemo } from "react";

let uidIndex = 0
const uid = () => String(uidIndex++)
Expand All @@ -7,16 +7,21 @@ const uid = () => String(uidIndex++)

const CraftContext = createContext<{
uid: () => string
}>({ uid })
componentMap: Map<string, React.ComponentType>
}>({ uid, componentMap: new Map() })

export const useCraftContext = () => useContext(CraftContext)
export const useCraftProvider = () => useContext(CraftContext)

/* eslint-disable-next-line */
export interface CraftProviderProps {}
export interface CraftProviderProps {
componentMap: Map<string, React.ComponentType>
}

export const CraftProvider: React.FC<CraftProviderProps> = (props) => {

const value = useMemo(() => ({ uid }), [])
const value = useMemo(() => ({
uid,
componentMap: props.componentMap
}), [props.componentMap])

return (
<CraftContext.Provider value={value}>
Expand All @@ -26,3 +31,19 @@ export const CraftProvider: React.FC<CraftProviderProps> = (props) => {
}

export default CraftProvider;

/* Utils */

export const makeComponentMap = () => {
const componentMap = new Map<string, React.ComponentType>()

const self = {
value: () => componentMap,
append: <M extends { component: string },>(name: M['component'], fc: React.ComponentType) => {
componentMap.set(name, fc)
return self
}
}

return self
}
34 changes: 0 additions & 34 deletions packages/react-core/src/lib/craft-root/craft-root.spec.tsx

This file was deleted.

15 changes: 8 additions & 7 deletions packages/react-core/src/lib/craft-root/craft-root.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { createContext, useContext } from "react";
import { createContext, PropsWithChildren, ReactNode, ReactPropTypes, useContext } from "react";
import { RootMeta } from '@d2-craft/typed'

export interface CraftRootProps<ExtendableNodeMeta> {
meta: RootMeta<ExtendableNodeMeta>
meta: RootMeta<ExtendableNodeMeta>;
children?: ReactNode | undefined;
}

export function CraftRoot<ExtendableNodeMeta>(props: CraftRootProps<ExtendableNodeMeta>) {
return (
<div>
<h1>Welcome to CraftRoot!</h1>
</div>
<CraftRootContext.Provider value={{ meta: props.meta }}>
{props.children}
</CraftRootContext.Provider>
);
}

Expand All @@ -18,13 +19,13 @@ export default CraftRoot;
/* Context */

export const CraftRootContext = createContext<{
meta: RootMeta
meta: unknown
}>({
meta: {
children: []
}
})

export const useCraftRootContext = () => {
export const useCraftRoot = () => {
return useContext(CraftRootContext)
}
4 changes: 2 additions & 2 deletions packages/typed/src/lib/typed.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NodeMeta, NodeMetaBase, RootMeta } from './typed';
import { ExUnused, NodeMeta, NodeMetaBase, RootMeta } from './typed';

describe('typed', () => {
it('should work', () => {
type ExItem<N, C> = NodeMetaBase<N, C, NodeMeta<ExNodeMeta>>
type ExNodeMeta<Ex = null> = NodeMeta< Ex
type ExNodeMeta<Ex = ExUnused> = NodeMeta< Ex
| ExItem<'A', { a: string }>
| ExItem<'B', { b: string }>
>
Expand Down
10 changes: 8 additions & 2 deletions packages/typed/src/lib/typed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ export interface NodeMetaBase<Name, Config, _NodeMeta> {
children?: _NodeMeta[]
}

export type NodeMeta<Ex = null> = Ex
/**
* Don't use it
*/
export type ExUnused = { component: '_' }
export const exUnusedMeta: ExUnused = { component: '_' }

export type NodeMeta<Ex = ExUnused> = Ex

/**
* @example
*
* type ExItem<N, C> = NodeMetaBase<N, C, NodeMeta<ExNodeMeta>>
* type ExNodeMeta<Ex = null> = NodeMeta< Ex
* type ExNodeMeta<Ex = ExUnused> = NodeMeta< Ex
* | ExItem<'A', { a: string }>
* | ExItem<'B', { b: string }>
* >
Expand Down

0 comments on commit 62db91c

Please sign in to comment.