Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nesting BleepsAnimator, Animator, and Grid components causes mangled HTML #213

Closed
VerdeVistaVoyager opened this issue Sep 14, 2023 · 3 comments
Labels
status: blocked Dependency tasks are still unresolved. type: bug

Comments

@VerdeVistaVoyager
Copy link

VerdeVistaVoyager commented Sep 14, 2023

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

@romelperez
Copy link
Member

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 romelperez added type: bug status: blocked Dependency tasks are still unresolved. labels Sep 14, 2023
@VerdeVistaVoyager
Copy link
Author

VerdeVistaVoyager commented Sep 14, 2023

@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 />);

@VerdeVistaVoyager
Copy link
Author

VerdeVistaVoyager commented Sep 14, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: blocked Dependency tasks are still unresolved. type: bug
Projects
None yet
Development

No branches or pull requests

2 participants