arwes / arwes

Futuristic Sci-Fi UI Web Framework.

Home Page:https://arwes.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Nesting BleepsAnimator, Animator, and Grid components causes mangled HTML

VerdeVistaVoyager opened this issue · comments

Describe the bug

Nesting BleepsAnimator, Animator, and Grid components creates HTML nesting issues.

To reproduce

Steps to reproduce the behavior:

Combine BleepsAnimator, Animator, and Grid components.
Run the app.
Open the app in your browser and view browser dev tools.
See the error messages.

Expected behavior

See no error messages (nesting these Awres components should not create mangled HTML).

Screenshots

// ERROR 1 of 2
Warning: Expected server HTML to contain a matching <h1> in <span>.
    at h1
    at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
    at span
    at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at p
    at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Text (webpack-internal:///(app-client)/./node_modules/@arwes/react-text/build/esm/Text/Text.js:22:17)
    at div
    at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Grid (webpack-internal:///(app-client)/./node_modules/@mui/material/Grid/Grid.js:392:87)
    at div
    at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Grid (webpack-internal:///(app-client)/./node_modules/@mui/material/Grid/Grid.js:392:87)
    at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
    at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
    at BleepsProvider (webpack-internal:///(app-client)/./node_modules/@arwes/react-bleeps/build/esm/BleepsProvider/BleepsProvider.js:12:13)
    at AnimatorGeneralProvider (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/AnimatorGeneralProvider/AnimatorGeneralProvider.js:10:13)
    at Sandbox (webpack-internal:///(app-client)/./app/page.tsx:542:110)
    at InnerLayoutRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:227:11)
    at RedirectErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
    at RedirectBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
    at NotFoundBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:59:11)
    at LoadingBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:324:11)
    at ErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:104:11)
    at InnerScrollAndFocusHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:139:9)
    at ScrollAndFocusHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:213:11)
    at RenderFromTemplateContext (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/render-from-template-context.js:15:44)
    at OuterLayoutRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:334:11)
    at body
    at html
    at RedirectErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
    at RedirectBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
    at ReactDevOverlay (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:70:9)
    at NotFoundErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:51:9)
    at NotFoundBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:59:11)
    at HotReload (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:318:11)
    at Router (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/app-router.js:150:11)
    at ErrorBoundaryHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:77:9)
    at ErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:104:11)
    at AppRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/app-router.js:395:13)
    at ServerRoot (webpack-internal:///(app-client)/./node_modules/next/dist/client/app-index.js:166:11)
    at RSCComponent
    at Root (webpack-internal:///(app-client)/./node_modules/next/dist/client/app-index.js:182:11)


// ERROR 2 of 2
Warning: validateDOMNesting(...): <h1> cannot appear as a descendant of <p>.
    at h1
    at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
    at span
    at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at p
    at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Text (webpack-internal:///(app-client)/./node_modules/@arwes/react-text/build/esm/Text/Text.js:22:17)
    at div
    at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Grid (webpack-internal:///(app-client)/./node_modules/@mui/material/Grid/Grid.js:392:87)
    at div
    at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
    at Grid (webpack-internal:///(app-client)/./node_modules/@mui/material/Grid/Grid.js:392:87)
    at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
    at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
    at BleepsProvider (webpack-internal:///(app-client)/./node_modules/@arwes/react-bleeps/build/esm/BleepsProvider/BleepsProvider.js:12:13)
    at AnimatorGeneralProvider (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/AnimatorGeneralProvider/AnimatorGeneralProvider.js:10:13)
    at Sandbox (webpack-internal:///(app-client)/./app/page.tsx:542:110)
    at InnerLayoutRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:227:11)
    at RedirectErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
    at RedirectBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
    at NotFoundBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:59:11)
    at LoadingBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:324:11)
    at ErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:104:11)
    at InnerScrollAndFocusHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:139:9)
    at ScrollAndFocusHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:213:11)
    at RenderFromTemplateContext (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/render-from-template-context.js:15:44)
    at OuterLayoutRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:334:11)
    at body
    at html
    at RedirectErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
    at RedirectBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
    at ReactDevOverlay (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:70:9)
    at NotFoundErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:51:9)
    at NotFoundBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:59:11)
    at HotReload (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:318:11)
    at Router (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/app-router.js:150:11)
    at ErrorBoundaryHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:77:9)
    at ErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:104:11)
    at AppRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/app-router.js:395:13)
    at ServerRoot (webpack-internal:///(app-client)/./node_modules/next/dist/client/app-index.js:166:11)
    at RSCComponent
    at Root (webpack-internal:///(app-client)/./node_modules/next/dist/client/app-index.js:182:11)

Development environment

// package.json
  "dependencies": {
    "@arwes/react": "^1.0.0-alpha.23",
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/icons-material": "^5.14.7",
    "@mui/material": "^5.14.0",
    "@types/node": "20.4.2",
    "@types/react": "18.2.15",
    "@types/react-dom": "18.2.7",
    "autoprefixer": "10.4.14",
    "eslint": "8.45.0",
    "eslint-config-next": "13.4.10",
    "next": "13.4.10",
    "postcss": "8.4.26",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "tailwindcss": "3.3.3",
    "typescript": "5.1.6"
  }

// Node version
v18.16.0

// OS
Ubuntu 20.04

## Testing environment

// Browser
Chrome

Hello @VerdeVistaVoyager!

I see you are using Material UI but I think the problem is only related to the Arwes components. Can you please create a sandbox example in https://arwes.dev/play? Select any existing sandbox and click the option "Customize" you so you can share the URL with the example code.

Thanks for reporting!

@romelperez Yup! Since the playground is client-only environment, there is no hydration issue to troubleshoot within the Awres playground. This is going to be specific to @Arwes integration with Next.js.

Code snippet below includes the following changes:

  • Material UI removed
  • Material UI components, such as <Grid>, replaced with placeholder <div>'s

Behavior:

  • no hydration error nor nested HTML errors in Arwes playground
  • hydration errors and nested HTML errors when using Next.js and 'use client'
import React, { useEffect, type ReactElement, useState, ReactNode } from 'react';
import { createRoot } from 'react-dom/client';

import { type CSSObject, Global } from '@emotion/react';

import {
  createAppTheme,
  createAppStylesBaseline,
  type AnimatorGeneralProviderSettings,
  AnimatorGeneralProvider,
  Animator,
  Animated,
  aaVisibility,
  aa,
  type BleepsProviderSettings,
  BleepsProvider,
  useBleeps,
  BleepsOnAnimator,
  FrameSVGCorners,
  GridLines,
  Dots,
  MovingLines,
  Text,
  FrameSVGNefrex,
  AnimatorInterface,
  AnimatorNode,
  useAnimator,
  AnimatorProps,
} from '@arwes/react';

interface ExperimentItemProps extends AnimatorProps {
  children?: ReactNode
}

const ExperimentItem = ({ children, ...animator }: ExperimentItemProps): ReactElement => {
  return (
    <Animator {...animator}>
      <Animated
        style={{ margin: 5, width: 40, height: 15, backgroundColor: '#777' }}
        animated={{
          transitions: {
            entering: {
              x: [0, 50],
              backgroundColor: ['#0ff', '#ff0'],
              options: { easing: 'linear' }
            },
            exiting: {
              x: [50, 0],
              backgroundColor: ['#ff0', '#0ff'],
              options: { easing: 'linear' }
            }
          }
        }}
        hideOnExited={false}
      />
      <div style={{ marginLeft: 20 }}>
        {children}
      </div>
    </Animator>
  );
};

const Experiment = () => {
  return (
    <AnimatorGeneralProvider duration={{ enter: 0.4, stagger: 0.1 }}>
      <ExperimentItem manager='sequence' combine>

        <ExperimentItem manager='parallel' combine>
          {Array(5).fill(0).map((_, i) => <ExperimentItem key={i} />)}
        </ExperimentItem>

        <ExperimentItem manager='stagger' combine>
          {Array(5).fill(0).map((_, i) => <ExperimentItem key={i} />)}
        </ExperimentItem>

        <ExperimentItem manager='sequence' combine>
          {Array(5).fill(0).map((_, i) => <ExperimentItem key={i} />)}
        </ExperimentItem>

      </ExperimentItem>
    </AnimatorGeneralProvider>
  )
}

const theme = createAppTheme();
const stylesBaseline = createAppStylesBaseline(theme);

const Background = (): ReactElement => {
  return (
    <div
      style={{
        position: 'fixed',
        inset: 0,
        backgroundColor: theme.colors.primary.bg(1),
        backgroundImage: 'linear-gradient(rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.9)), url("bg1.jpeg")',
        backgroundSize: 'cover',
      }}
    >
      <GridLines lineColor={theme.colors.primary.deco(0)} />
      <Dots color={theme.colors.primary.deco(1)} />
      <MovingLines lineColor={theme.colors.primary.deco(2)} />
    </div>
  );
};

const TagLines = () => {
  const childrenList = [
    'TEAM PLAYER',
    'SOFTWARE ENGINEER',
    'JAVASCRIPT DEVELOPER',
    'NODE.JS DEVELOPER',
    'FRONT END DEVELOPER',
    'API DEVELOPER',
    'CLOUD ENGINEER',
    'PEOPLE FIRST DEVELOPER',
    'LIFE LONG LEARNER'
  ];

  const [childrenIndex, setChildrenIndex] = useState(0);

  useEffect(() => {
    const timeout = setTimeout(() => {
      const isLastIndex = childrenIndex === childrenList.length - 1;
      const nextIndex = isLastIndex ? 0 : childrenIndex + 1;
      setChildrenIndex(nextIndex);
    }, 2000);
    return () => clearTimeout(timeout);
  }, [childrenIndex, childrenList.length]);

  return (
    <h4>
      <Animator duration={{ enter: 1, exit: 1 }}>
        <Text fixed>
          {childrenList[childrenIndex]}
        </Text>
      </Animator>
    </h4>
  );
}

const Sandbox = () => {
  // ignoring the hydration errors
  // TODO: check if awres library has recommendations for <AnimatorGeneralProvider ...> usage
  const [floatingButtonVisible, setFloatingButtonVisible] = useState(false);
  return (
    <>
      <Global styles={stylesBaseline as Record<string, CSSObject>} />
      <AnimatorGeneralProvider duration={{
        enter: 0.2,
        exit: 0.2,
        stagger: 0.04
      }}>
        <BleepsProvider
          master={{ volume: 0.9 }}
          bleeps={{
            intro: {
              sources: [{ src: 'https://next.arwes.dev/assets/sounds/intro.mp3', type: 'audio/mpeg' }]
            },
            click: {
              sources: [{ src: 'https://next.arwes.dev/assets/sounds/click.mp3', type: 'audio/mpeg' }]
            }
          }}>
          <Animator active={true} combine manager='stagger'>
            <Animator>
              <Background />
            </Animator>
            <Animator>
              <div>
                <div>
                  <Text
                    style={{ color: '#ddd', fontFamily: 'monospace' }}
                    manager='decipher'
                    easing='outSine'
                    fixed
                  >
                    <Animator active={true} duration={{ enter: 1.5, exit: 1.5 }}>
                      <h1
                        style={{
                          width: '320px',
                          textAlign: 'center',
                          fontSize: '48px'
                        }}>
                        <span></span>
                      </h1>
                    </Animator>
                  </Text>
                </div>
                <div>
                  <TagLines />
                </div>
                <div>
                  <div
                    style={{ marginTop: '50px' }}>
                    <button
                      id="about-btn"
                      // href="#about"
                      // variant="outlined"
                      onClick={() => {
                        console.log('clicked')
                        setFloatingButtonVisible(!floatingButtonVisible);
                      }}
                    >About</button>
                    <button
                      id="experiment-btn"
                      // href="#experiment"
                      // variant="outlined"
                      style={{
                        margin: '5px'
                      }}
                      onClick={() => {
                        setFloatingButtonVisible(!floatingButtonVisible);
                      }}
                    >Experiment</button>
                  </div>
                </div>
              </div>
            </Animator>
            <Animator>
              <div>
                <div>
                  <h1 style={{ width: '220px', fontSize: '16px', marginBottom: '10%' }} id="about">
                    <Text style={{ color: 'yellow' }}>
Hello                    </Text>
                  </h1>
                </div>
                <div>
                  <h1 style={{ width: '220px', fontSize: '16px', marginBottom: '10%' }}>
                    <Text style={{ color: 'yellow' }}>
From year to year I had op
                    </Text>
                  </h1>
                </div>
                <div>

                  <h1 style={{ width: '220px', fontSize: '16px', marginBottom: '10%' }}>
                    <Text style={{ color: 'yellow' }}>
                    I am
                    </Text>
                  </h1>

                </div>
                <div>
                  <h1 style={{ width: '220px', fontSize: '16px', marginBottom: '10%' }}>
                    <Text style={{ color: 'yellow' }}>
                      Noodles available upon request.
                    </Text>
                  </h1>
                </div>
              </div>
            </Animator>
          </Animator>
        </BleepsProvider>
      </AnimatorGeneralProvider >
      <div>
        <div>
          <div id="experiment">
            <Experiment />
          </div>
        </div>
      </div>
      <div
        onClick={() => {
          setFloatingButtonVisible(!floatingButtonVisible);
        }}
        style={{
          display: floatingButtonVisible ? 'block' : 'none'
        }}>
        <div>
          <div>
            <div />
          </div>
        </div>
      </div >
    </>
  );
};

createRoot(document.querySelector('#root') as HTMLElement).render(<Sandbox />);

Opportunity for me to better understand hydration behavior with Nextjs and Reactjs.

Can't promise dedicated time to look into this, but the outcome would look like a barebones BleepsAnimator and Animator example on a Next.js page such that it (hydrates correctly OR has no hydration) AND (has no nested HTML errors).

If I ever come back to this I'll leave a comment