Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions apps/web-e2e/src/modules/component/component.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,21 @@ export const componentInstanceChild = {
}),
},
}

export const fragment1: ICreateElementSeedData = {
atom: IAtomType.ReactFragment,
name: 'Fragment1',
parentElement: 'New Component Root',
}

export const fragment2: ICreateElementSeedData = {
atom: IAtomType.ReactFragment,
name: 'Fragment2',
parentElement: 'New Component Root',
}

export const fragment3: ICreateElementSeedData = {
atom: IAtomType.ReactFragment,
name: 'Fragment3',
parentElement: 'New Component Root',
}
44 changes: 21 additions & 23 deletions apps/web-e2e/src/modules/component/components.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { expect } from '@playwright/test'

import { globalBeforeAll } from '../../setup/before-all'
import { seedAppData } from '../app/app.data'
import { elementColA, elementColB, elementColC } from '../builder/builder.data'
import {
COMPONENT_PROP_VALUE,
componentInstance,
componentInstanceChild,
componentTextElement,
fragment1,
fragment2,
fragment3,
} from './component.data'
import { test } from './component.fixture'

Expand All @@ -21,7 +23,7 @@ globalBeforeAll()

test.beforeAll(async ({ request }) => {
const seedData = await seedAppData(request, {
atomTypes: [IAtomType.AntDesignTypographyText, IAtomType.AntDesignGridCol],
atomTypes: [IAtomType.AntDesignTypographyText],
componentTypes: [],
})
app = seedData.app
Expand Down Expand Up @@ -85,27 +87,23 @@ test('should be able to delete component elements', async ({
await page.openComponentBuilder()
await page.openPropsTab()
await page.checkRootElementExists()
await page.createElementTree([
{ ...elementColA, parentElement: 'New Component Root' },
{ ...elementColB, parentElement: 'New Component Root' },
{ ...elementColC, parentElement: 'New Component Root' },
])

const colAElement = page.getTreeElement(elementColA.name, elementColA.atom)
const colBElement = page.getTreeElement(elementColB.name, elementColB.atom)
const colCElement = page.getTreeElement(elementColC.name, elementColC.atom)

await expect(colAElement).toBeVisible()
await expect(colBElement).toBeVisible()
await expect(colCElement).toBeVisible()

await page.deleteElementByContextMenu(elementColA)
await page.deleteElementFromUpdateForm(elementColB)
await page.deleteElementFromOverlay(elementColC)

await expect(colAElement).toBeHidden()
await expect(colBElement).toBeHidden()
await expect(colCElement).toBeHidden()
await page.createElementTree([fragment1, fragment2, fragment3])

const fragment1Element = page.getTreeElement(fragment1.name, fragment1.atom)
const fragment2Element = page.getTreeElement(fragment2.name, fragment2.atom)
const fragment3Element = page.getTreeElement(fragment3.name, fragment3.atom)

await expect(fragment1Element).toBeVisible()
await expect(fragment2Element).toBeVisible()
await expect(fragment3Element).toBeVisible()

await page.deleteElementByContextMenu(fragment1)
await page.deleteElementFromUpdateForm(fragment2)
await page.deleteElementFromOverlay(fragment3)

await expect(fragment1Element).toBeHidden()
await expect(fragment2Element).toBeHidden()
await expect(fragment3Element).toBeHidden()
await expect(page.getNotification()).toBeHidden()
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
'use client'

import type { IDomTreeProps } from '@codelab/frontend-abstract-application'
import type { ComponentType, JSXElementConstructor, ReactNode } from 'react'

import { DOM_TREE_CONTAINER_ID } from '@codelab/frontend-abstract-domain'
import { ComponentName } from '@cui/registry'
import { useProfiler } from '@sentry/react'
import { Alert } from 'antd'
import { Tag } from 'antd'
import { pipe } from 'fp-ts/function'
import { observer } from 'mobx-react-lite'
import { forwardRef } from 'react'

// https://github.com/ant-design/ant-design/issues/52213
const ErrorBoundary = Alert.ErrorBoundary as JSXElementConstructor<{
children: ReactNode
}>
import { ErrorBoundary } from 'react-error-boundary'

/**
* DomTree is the pure rendering component for the element tree.
Expand All @@ -36,7 +31,18 @@ const DomTreeComponent = forwardRef<HTMLDivElement, IDomTreeProps>(
useProfiler(ComponentName.DomTreeComponent)

return (
<ErrorBoundary>
<ErrorBoundary
fallbackRender={({ error }) => {
return renderer.isBuilder ? (
<Tag color="#cd201f">
Encountered unrecoverable error in application: {error?.message}
</Tag>
) : null
}}
onError={(error, info) =>
console.error('RootRenderer failed with error', error, info)
}
>
<div
id={DOM_TREE_CONTAINER_ID}
ref={ref}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import type { ElementWrapperProps } from '@codelab/frontend-abstract-application'
import type { IAtomType } from '@codelab/shared-abstract-modules'

import { type IComponentType } from '@codelab/frontend-abstract-domain'
import QuestionCircleOutlined from '@ant-design/icons/QuestionCircleOutlined'
import { mergeProps } from '@codelab/frontend-domain-prop/utils'
import { useDomainStore } from '@codelab/frontend-infra-mobx-context'
import { ComponentName } from '@cui/registry'
import { Tag, Tooltip } from 'antd'
import { getSnapshot } from 'mobx-keystone'
import { observer } from 'mobx-react-lite'
import { Fragment, useEffect } from 'react'
Expand Down Expand Up @@ -50,7 +51,7 @@ export const ElementWrapper = observer<ElementWrapperProps>(
getAtom(atomType) ||
Fragment

const ReactComponent: IComponentType = renderOutput.atomType
const ReactComponent = renderOutput.atomType
? getReactComponent(renderOutput.atomType)
: Fragment

Expand Down Expand Up @@ -87,17 +88,25 @@ export const ElementWrapper = observer<ElementWrapperProps>(
* - a sub tree of elements
* - children prop value
*/

const children =
runtimeElement.renderedChildren ??
runtimeElement.runtimeProps.renderedChildrenProp

return (
<ErrorBoundary
fallbackRender={() => null}
fallbackRender={({ error }) => {
return renderer.isBuilder ? (
<Tag color="#cd201f">
Encountered error rendering {runtimeElement.element.current.name}{' '}
element{' '}
<Tooltip title={error.message}>
<QuestionCircleOutlined />
</Tooltip>
</Tag>
) : null
}}
onError={onError}
onReset={onReset}
resetKeys={[renderOutput]}
>
<StyledComponent
ReactComponent={ReactComponent}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import type { ObjectLike } from '@codelab/shared-abstract-types'

import { clone, set } from 'radash'
import { set } from 'radash'
import { connectField, joinName, useForm } from 'uniforms'

import type { WithExpressionFieldProps } from './ToggleExpression'
Expand Down Expand Up @@ -37,7 +37,8 @@ const WrappedUnionField = (props: WrappedUnionFieldProps) => {
)

// Clone form.model before modification to prevent "Cannot assign to read only property" errors when switching union field types. The form.model is frozen/read-only, so we need a mutable copy. Fixes: https://github.com/codelab-app/platform/issues/3814
const model = set(clone(form.model), path, value)
// Need to use structuredClone (deep clone) to prevent same issue when attempting to set inner form values
const model = set(structuredClone(form.model), path, value)
const bridge = refershBridge(model)
form.onChange(props.name, bridge.getInitialValue(props.name))
} else {
Expand Down
24 changes: 4 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"serve": "NX_TUI=true nx run-many --target=serve --projects=api,web --parallel",
"serve:sites": "NX_TUI=true nx run-many --target=serve --projects=api,web,sites --parallel",
"serve:test": "NX_TUI=false nx run-many --target=serve --projects=api,web --parallel -c test",
"serve:prod": "NX_TUI=false nx run-many --target=build --projects=api,web -c prod && NX_TUI=true nx run-many --target=serve --projects=api,web --parallel -c prod",
"serve:sites:test": "NX_TUI=false nx run-many --target=serve --projects=api,web,sites --parallel -c test",
"caddy": "caddy run --config scripts/caddy/Caddyfile",
"e2e": "nx e2e-ui web-e2e -c test",
Expand Down Expand Up @@ -94,7 +95,6 @@
"@lexical/react": "0.37.0",
"@lexical/utils": "0.37.0",
"@mailchimp/mailchimp_marketing": "3.0.80",
"@mjackson/headers": "0.11.1",
"@modelcontextprotocol/sdk": "^1.18.2",
"@neo4j/graphql": "6.3.1",
"@nestjs/apollo": "13.0.2",
Expand Down Expand Up @@ -149,7 +149,6 @@
"@sentry/profiling-node": "10.19.0",
"@sentry/react": "10.19.0",
"@sinclair/typebox": "0.34.33",
"@sotaproject/strikethrough": "1.0.1",
"@storybook/react": "9.1.1",
"@supabase/supabase-js": "2.1.1",
"@svgr/webpack": "8.1.0",
Expand All @@ -160,7 +159,6 @@
"@uiw/react-codemirror": "4.23.12",
"acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
"adm-zip": "0.5.10",
"ajv": "8.17.1",
"animate.css": "4.1.1",
"antd": "5.25.3",
Expand All @@ -187,8 +185,6 @@
"dataloader": "^2.2.3",
"date-fns": "^4.1.0",
"deep-object-diff": "1.1.9",
"deepmerge": "^4.3.1",
"deepmerge-ts": "^7.1.5",
"diff": "5.1.0",
"dir-compare": "4.2.0",
"dots-wrapper": "3.11.8",
Expand All @@ -198,9 +194,7 @@
"env-var": "7.4.1",
"eslint-plugin-barrel-files": "3.0.1",
"express": "4.18.3",
"fast-safe-stringify": "2.1.1",
"fp-ts": "^2.16.11",
"framer-motion": "6.3.3",
"fs-extra": "11.2.0",
"glob": "10.3.12",
"graphql": "16.9.0",
Expand All @@ -219,7 +213,6 @@
"kebab-case": "^2.0.2",
"lexical": "0.37.0",
"lucide-react": "^0.544.0",
"mem0ai": "^2.1.33",
"mobx": "6.13.7",
"mobx-keystone": "1.11.4",
"mobx-react-lite": "4.1.0",
Expand Down Expand Up @@ -247,16 +240,15 @@
"radash": "12.1.0",
"rc-select": "14.13.0",
"rc-upload": "4.5.2",
"react": "19.1.0",
"react": "19.2.0",
"react-day-picker": "9.11.0",
"react-dom": "19.1.0",
"react-error-boundary": "4.0.13",
"react-dom": "19.2.0",
"react-error-boundary": "6.0.0",
"react-grid-layout": "1.4.2",
"react-hook-form": "7.54.2",
"react-hook-form-antd": "1.1.3",
"react-hotjar": "5.3.0",
"react-hotkeys-hook": "3.4.4",
"react-is": "19.1.0",
"react-quill": "2.0.0",
"react-resizable": "3.0.4",
"react-resizable-panels": "3.0.6",
Expand Down Expand Up @@ -300,7 +292,6 @@
"yargs": "17.7.2",
"zen-observable-ts": "1.1.0",
"zod": "3.23.8",
"zustand": "4.5.2",
"zx": "8.2.4"
},
"devDependencies": {
Expand Down Expand Up @@ -371,16 +362,13 @@
"@testing-library/jest-dom": "6.7.0",
"@testing-library/react": "16.2.0",
"@testing-library/user-event": "14.6.1",
"@types/adm-zip": "0.5.0",
"@types/auth0": "3.3.10",
"@types/aws-lambda": "8.10.116",
"@types/babel__parser": "^7.1.5",
"@types/babel__traverse": "^7.28.0",
"@types/buble": "0.20.1",
"@types/chance": "1.1.4",
"@types/concurrently": "6.4.0",
"@types/cookie": "0.5.1",
"@types/deepmerge": "^2.2.3",
"@types/diff": "5.0.3",
"@types/express": "4.17.21",
"@types/flat": "5.0.2",
Expand All @@ -400,7 +388,6 @@
"@types/react": "19.0.12",
"@types/react-dom": "19.0.4",
"@types/react-grid-layout": "1.3.4",
"@types/react-is": "18.3.0",
"@types/react-resizable": "1.7.4",
"@types/react-responsive": "8.0.5",
"@types/react-stickynode": "4.0.0",
Expand All @@ -426,7 +413,6 @@
"chance": "1.1.11",
"change-case-all": "2.1.0",
"commitlint-plugin-function-rules": "4.0.1",
"concurrently": "7.0.0",
"cookie": "0.5.0",
"cross-env": "7.0.3",
"cz-customizable": "7.4.0",
Expand Down Expand Up @@ -474,15 +460,13 @@
"json-schema": "0.4.0",
"jsonc-eslint-parser": "2.4.0",
"junit-viewer": "^4.11.1",
"knip": "5.29.2",
"lint-staged": "16.1.4",
"minimatch": "9.0.4",
"msw": "2.7.0",
"msw-storybook-addon": "2.0.4",
"nx": "21.6.4",
"pino-pretty": "13.0.0",
"postcss": "8.4.45",
"semver": "^7.7.2",
"styled-components": "6.1.15",
"swc-loader": "0.2.6",
"tailwindcss": "3.4.17",
Expand Down
Loading