Coder Social home page Coder Social logo

concrete-utopia / utopia Goto Github PK

View Code? Open in Web Editor NEW
3.7K 30.0 164.0 203 MB

Design ❤️ Code

Home Page: https://utopia.app

License: MIT License

JavaScript 0.98% CSS 0.33% HTML 0.04% TypeScript 96.24% Nix 0.16% Haskell 2.23% Shell 0.02% Dockerfile 0.01%
utopia future now

utopia's Introduction

RelativeCI All Contributors

Welcome to Utopia 🏝

Utopia is an integrated design and development environment for React. It uses React code as the source of truth, and lets you make real time changes to components by editing it and using a suite of design tools. It's early software, but you can try it today, look at an example project, or read about it on our blog!

Try Utopia Now!

screenshot of utopia

Start the editor

For contributors only: Installing Utopia on your machine

Please note: to use Utopia, visit utopia.app. Installing it locally is for feature development of Utopia itself: it's slower - sometimes very significantly so. It still needs connection to our servers. And you won't be able to edit projects in the file system on your machine if you install it locally.

To contribute to Utopia, you'll need to clone the repo, install the prerequisites, and then run the editor for the first time.

Quick Start for contemporary MacOS

  1. Clone the repo
  2. Install Nix-Shell nix-shell sh <(curl -L https://nixos.org/nix/install)
  3. Close terminal, start new terminal session, navigate to repo folder, run nix-shell. first run will take a while it will look like nothing will happen.
  4. Generate and copy a Github Token here (fine-grained or classic, longest expiration. No need to tick any of the permission checkboxes for classic)
  5. First run and build: open terminal GITHUB_TOKEN=<token from above> start-full.
  6. Wait - this step will take a while and start with (harmless) errors. Once you see the scratchpad, check the server tab - it'll take the longest (many minutes).
  7. Load the editor at http://localhost:8000/p.
  8. Subsequent runs: start-minimal (this won't rebuild everything) except for server and infra-level changes
  9. Editor hot-reloads and rebuilds as needed; refresh browser for asset changes
  10. stop-dev in the Scratchpad stops the server.
  11. Install direnv to start nix-shell automatically brew install direnv

Install the Prerequisites

  • nix-shell. If you are on macOS Catalina or later, you will be prompted to include an extra flag in the install script. If using Windows follow this guide. If you don't want to use nix, we have instructions here
  • direnv. If you don't have direnv installed, you'll need to run nix-shell before any of the start commands, and switching to nix will be a bit slower.

NB: If you're on Windows, you'll first need to set up the Windows Subsystem for Linux (wsl).

You need to generate a GITHUB_TOKEN

A Github token is required to build VS Code, as there are some dependencies downloaded via the Github API which will be rate limited without one, causing the build to sporadically fail with a 403 error. To prevent that, generate a new Github token here (if you have access to fine-grained tokens, use one of those, but if not then a "classic" token will be fine).

Give it the longest expiration time possible, readonly public access, and don't give it any permissions for anything else. Then copy that token somewhere so that you can use it in the next step.

Run the Editor for the first time

The first time running the editor, run the following script:

GITHUB_TOKEN=<token from above> start-full

(You'll only need to do this once, and it will take quite some time to download and build various dependencies. After that, you can usually use start-minimal.)

Working in the dev environment

Both of these scripts result in a tmux session with all of the various servers running and watching for changes. You can see all of the active sessions in the bar along the bottom, prefixed by the "window" number that they are running in. You should be able to click on each of those to switch to viewing that session, or if that doesn't work you can use the key combo cmd+b (macOS) or ctrl+b (Linux or Windows), followed by the number for that session. (see here for the relevant tmux docs)

To shut them down, in the "Scratchpad" tab of the session run the following command:

stop-dev

Finally, loading the running application

Now the editor should load on http://localhost:8000/p, or http://localhost:8000 when developing the website itself.

Pull request bundle support.

When a series of environment variables are set (see Branches.hs), the editor supports the ability to get a bundle of editor code from S3 that was created from a PR, and load that instead of the code currently held locally. Which means that changes can be tested without spinning up multiple environments.

To use this if the URL currently is https://utopia.pizza/p/e976df60-phase-rutabaga/, the branch name would be added on in a query parameter like so: https://utopia.pizza/p/e976df60-phase-rutabaga/?branch_name=my-test-branch.

Limitations:

  • Doesn't currently support Monaco because of the way that builds the workers in a special webpack plugin, so changes to the version of Monaco in that branch may fail in unusual ways.
  • Anything that isn't editor code will not be changed by this, such as the website code or the server endpoints.

Troubleshooting

I'm on macOS and Nix has suddenly stopped working

Part of the nix installation will add a hook into /etc/bashrc, which can be wiped by a macOS update. There is an open bug ticket for that here. If this has happened to you, you'll need to manually add that hook back in, or alternatively add it to your own ~/.zshrc (where it won't be overwritten), copying and pasting the hook exactly as follows:

# Nix
if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
  . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
fi
# End Nix

fsevents

If you notice that 1 or more CPU cores are running 100% because of node processes, it is probably webpack-dev-server having trouble with fsevents on MacOS. To fix it, run pnpm install fsevents in the utopia/editor directory. see webpack/webpack#701 (comment)

Installation error on utopia-vscode-extensions step

If you see an error to the effect of

Usage Error: The nearest package directory (/path/to/utopia/utopia-vscode-extension) doesn't seem to be part of the project declared in /path/to/utopia.

- If /path/to/utopia isn't intended to be a project, remove any yarn.lock and/or package.json file there.
- If /path/to/utopia is intended to be a project, it might be that you forgot to list utopia-vscode-extension in its workspace configuration.
- Finally, if /path/to/utopia is fine and you intend utopia-vscode-extension to be treated as a completely separate project (not even a workspace), create an empty yarn.lock file in it.

It could be caused by a .yarnrc or .yarnrc.yml file located in a parent directory. .yarnrc files are used to configure yarn setting and will effect projects in all child directories below them. This can change the yarn version of utopia to one that will produce the above error. Simply removing the .yarnrc or .yarnrc.yml file will fix this error.

Running this without Nix

We highly recommend using Nix to make life easier, but if you're having trouble with that or would prefer not to, then there is always the option to run each of the various scripts individually.

You'll first need to build VS Code and install a few dependencies:

cd [root-directory]/utopia-vscode-extension
pnpm install
pnpm run build

cd [root-directory]/vscode-build
yarn
yarn run build

cd [root-directory]/editor
pnpm install

Then, to run the application, you'll need multiple things running concurrently... The server:

cd [root-directory]/server
hpack
cabal new-run -j --disable-optimization --disable-profiling --disable-documentation --disable-library-coverage --disable-benchmarks utopia-web -- +RTS -N -c"

PostgreSQL:

cd [root-directory]
PGLOCK_DIR="`pwd`/.pglock/"
mkdir -p $PGLOCK_DIR
pg_ctl -D utopia-db stop
initdb -D utopia-db
pg_ctl -D utopia-db -l pglog.txt -o "--unix_socket_directories='$PGLOCK_DIR' -c log_statement=none" start
psql -o /dev/null -h "$PGLOCK_DIR" -d utopia -tc "SELECT 1 FROM pg_database WHERE datname = 'utopia'" || create-db
tail -f pglog.txt

tsc to compile the editor:

cd [root-directory]/editor
pnpm tsc --watch && NODE_OPTIONS=--max_old_space_size=4096

Vite to run the editor:

cd [root-directory]/editor
pnpm run dev-fast

The utopia-vscode-common module:

cd [root-directory]/utopia-vscode-common
pnpm install
pnpm run watch-dev

The utopia-vscode-extension module:

cd [root-directory]/utopia-vscode-extension
pnpm install
pnpm run watch-dev

A script for pulling changes to the above extension into VS Code:

cd [root-directory]/vscode-build
yarn run pull-utopia-extension

The website project (if developing that)

cd [root-directory]/website-next
pnpm install
BROWSER=none pnpm run dev

Using direnv to make your life easier

Since a lot of this requires using nix-shell everywhere, you can just use direnv to make that a lot simpler. Not only will this automatically use a nix shell whenever you cd into the project folder, but it also adds caching to vastly speed up the opening of that shell. You can install direnv by using brew:

brew install direnv

To actually run direnv you need to hook it into your shell by following the instructions here.

Then to configure it, in your $HOME directory add a file .direnvrc with the following contents (copied from https://github.com/direnv/direnv/wiki/Nix#using-a-global-use_nix-with-garbage-collection-prevention):

use_nix() {
  local path="$(nix-instantiate --find-file nixpkgs)"

  if [ -f "${path}/.version-suffix" ]; then
    local version="$(< $path/.version-suffix)"
  elif [ -f "${path}/.git" ]; then
    local version="$(< $(< ${path}/.git/HEAD))"
  fi

  local cache=".direnv/cache-${version:-unknown}"

  local update_drv=0
  if [[ ! -e "$cache" ]] || \
    [[ "$HOME/.direnvrc" -nt "$cache" ]] || \
    [[ .envrc -nt "$cache" ]] || \
    [[ default.nix -nt "$cache" ]] || \
    [[ shell.nix -nt "$cache" ]];
  then
    [ -d .direnv ] || mkdir .direnv
    nix-shell --show-trace --pure "$@" --run "\"$direnv\" dump bash" > "$cache"
    update_drv=1
  else
    log_status using cached derivation
  fi
  local term_backup=$TERM path_backup=$PATH
  if [ -n ${TMPDIR+x} ]; then
    local tmp_backup=$TMPDIR
  fi

  eval "$(< $cache)"
  export PATH=$PATH:$path_backup TERM=$term_backup TMPDIR=$tmp_backup
  if [ -n ${tmp_backup+x} ]; then
    export TMPDIR=${tmp_backup}
  else
    unset TMPDIR
  fi

  # `nix-shell --pure` sets invalid ssl certificate paths
  if [ "${SSL_CERT_FILE:-}" = /no-cert-file.crt ]; then
    unset SSL_CERT_FILE
  fi
  if [ "${NIX_SSL_CERT_FILE:-}" = /no-cert-file.crt ]; then
    unset NIX_SSL_CERT_FILE
  fi

  # This part is based on https://discourse.nixos.org/t/what-is-the-best-dev-workflow-around-nix-shell/418/4
  if [ "$out" ] && (( $update_drv )); then
    local drv_link=".direnv/drv"
    local drv="$(nix --experimental-features nix-command show-derivation $out | grep -E -o -m1 '/nix/store/.*.drv')"
    local stripped_pwd=${PWD/\//}
    local escaped_pwd=${stripped_pwd//-/--}
    local escaped_pwd=${escaped_pwd//\//-}
    ln -fs "$drv" "$drv_link"
    ln -fs "$PWD/$drv_link" "/nix/var/nix/gcroots/per-user/$LOGNAME/$escaped_pwd"
    log_status renewed cache and derivation link
  fi

  if [[ $# = 0 ]]; then
    watch_file default.nix
    watch_file shell.nix
  fi
}

And add a .envrc file to the root folder of the project with the following contents:

use nix

(This file is deliberately contained in the .gitignore because it is supposed to be personal to you - it allows you to add custom environment variables that will always be in scope whenever you're in this directory)

Please update your .zshrc (or .bashrc) to hook it into the shell, for example for zsh add this line:

eval "$(direnv hook zsh)"

After this step open a new shell window and enter the utopia directory. Direnv should be activated as soon as you enter, you can use the start and start-performance scripts without manually running nix-shell.

Unit Tests with Jest

Use the following nix scripts:

test-editor

Continuous mode:

test-editor-watch

On macOS, when trying to watch, you might run into an error message about number of open files. In that case, install watchman:

brew install watchman

Browser tests with Karma

Tip: If you want to focus on a specific test suite you can change describe(... to describe.only(... and if you want to run a specific test case, you can do the same with it.only(... .

Use the following nix scripts:

One-off run with a headless Chrome (same as on the CI):

test-editor-browser

Debugging (bring your own Chrome):

test-editor-browser-debug

In debugging mode, Karma will be run in watch mode, and will not use a managed browser. Once the Karma webpack server is ready, it will print something like this to the terminal:

01 03 2022 11:20:17.412:WARN [karma]: No captured browser, open http://localhost:9876/

Use your own Chrome browser to navigate to the printed http address. The website will have a Debug button, or you can just append /debug.html to the url like so: http://localhost:9876/debug.html (your port may vary).

In debug mode, you can see all console logs in Chrome's own DevTools, you can use debugger; statements, etc. Behind the scenes, Karma runs a webpack dev server in watch mode, which means if you change the source code, webpack will recompile and karma will attempt to re-run the tests. If the tests do not rerun for some reason, just navigate to http://localhost:9876/debug.html again.

(I recommend opening DevTools before navigating to /debug.html to make sure you don't miss debugger; statements and see the console output even if a test hangs the browser or enters an infinite loop.)

Browser tests failing to run with Error: spawn Unknown system error -86

This is most likely because the browser tests are trying to run a headless version of Chrome built for Intel chips on an Mac with an ARM chip. If that's the case, Rosetta 2 should solve your problem:

softwareupdate --install-rosetta

VSCode linting, formatting with prettier etc

To enable format-on-save, you should install the VSCode plugin esbenp.prettier-vscode, and dbaeumer.vscode-eslint and then in your workspace settings, enable format on save, and tell prettier to use the eslint integration mode:

  "eslint.workingDirectories": ["./editor", "./utopia-api"],
  "editor.formatOnSave": true,
  "prettier.useEditorConfig": false,
  "prettier.requireConfig": true,
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }

Select prettier as the default formatter in your settings; VSCode may prompt you to do so. The last four line items, starting with [typescript] reflect this. You should restart VSCode after this.

Deploying

All pushes to the master branch will immediately trigger this workflow that runs the tests and then triggers a deploy to our Staging environment.

To deploy to the Production environment, somebody needs to manually trigger our tag-release.yml workflow, giving either a specific commit hash or branch name (defaulting to master), and optionally a tag name (the default behaviour is to increment the patch version). This can be triggered via the "Run Workflow" button here
Note: in the "Use workflow from" dropdown you have to select "Branch: master" - this is specifying which workflow to run, not which branch to cut the release from.

utopia's People

Contributors

alecmolloy avatar balazsbajorics avatar benwolfram avatar bkrmendy avatar charkour avatar cscheffauer avatar enidemi avatar gbalint avatar github-actions[bot] avatar lankaukk avatar liady avatar maltenuhn avatar omardasilva avatar rheeseyb avatar ruggi avatar seanmclem avatar seanparsons avatar thetylerwolf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

utopia's Issues

We're showing a number of properties as controlled when they are not

STR

  1. Open below project
  2. select 'register'
  3. Look at inspector

Expected: Height: 30 isn't shown as controlled, since it's not (no css class nor javascript is affecting it)

STR2

  1. Open new project
  2. Look at default view
    Expected: dimension properties aren't controlled. Self-size property isn't controlled.
/** @jsx jsx */
import * as React from 'react'
import { Rectangle, Scene, Storyboard, Text, View, img, jsx } from 'utopia-api'
export const cardData = [
  {
    title: 'Monument Valley',
    photoURL:
      'https://images.unsplash.com/photo-1509316785289-025f5b846b35?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3310&q=80',
  },

  {
    title: 'Raja Ampat',
    photoURL:
      'https://images.unsplash.com/photo-1586517659479-eea1f837aaf8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2090&q=80',
  },

  {
    title: 'Jal Mahal',
    photoURL:
      'https://images.unsplash.com/photo-1477586957327-847a0f3f4fe3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2100&q=80',
  },

  {
    title: 'Tokyo',
    photoURL:
      'https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2271&q=80',
  },
]
export var Card = (props) => {
  console.log(props)
  return (
    <div
      style={{
        ...(props.style || {}),
        minWidth: 335,
        minHeight: 184,
        paddingTop: 0,
        backgroundImage: `url(${props.photoURL})`,
        backgroundSize: 'cover',
        borderRadius: '0px',
        height: 255,
        top: -4,
        position: 'relative',
      }}
      className={props.className}
    >
      <div
        style={{
          height: 42,
          bottom: 0,
          display: 'flex',
          color: '#080808BA',
          fontFamily: 'Arvo, sans-serif',
          left: 0,
          backgroundColor: '#FFFFFFB8',
          opacity: 1,
          borderRadius: '0px',
          paddingLeft: 8,
          right: 0,
          alignItems: 'center',
          paddingRight: 8,
          position: 'absolute',
        }}
      >
        <span style={{ color: '#0A0A0A', fontFamily: 'Inter', border: 'none', flexGrow: 1 }}>
          {props.title}
        </span>
        <div
          layout={{ crossBasis: 30, flexBasis: 100 }}
          css={{
            justifyContent: 'center',
            transition: 'all .03s linear',
            display: 'flex',
            '&:hover': { color: 'white', backgroundColor: 'black', fontWeight: 500 },
            fontSize: 12,
            border: '1px solid black',
            '&:active': { transform: 'translateY(2px)' },
            alignItems: 'center',
          }}
        >
          Register
        </div>
      </div>
      <View
        style={{
          height: 30,
          backgroundColor: '#DDDDDDBD',
          width: 33,
          opacity: 1,
          borderRadius: '12.5px',
          right: 11,
          top: 12,
          position: 'absolute',
        }}
        layout={{ layoutSystem: 'pinSystem' }}
      >
        <img
          style={{
            height: 22,
            left: 6,
            width: 21,
            mixBlendMode: 'multiply',
            opacity: 0.25,
            top: 4,
          }}
          src={'./assets/heart-icon-transparent-icon-symbol-love-black-11553480202wbkavsmiom.png'}
          data-aspect-ratio-locked
          alt={''}
        />
      </View>
    </div>
  )
}
Card.propertyControls = {
  title: {
    type: 'string',
    title: 'Greetings',
    defaultValue: 'Hello World',
  },

  photoURL: {
    type: 'string',
    title: 'Photo URL',
    defaultValue:
      'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
  },
}
export var App = (props) => {
  return (
    <View style={{ ...(props.style || {}), backgroundColor: '#FFFFFF', width: 433 }} layout={{}}>
      <div
        style={{
          bottom: 0,
          flexDirection: 'column',
          display: 'flex',
          left: 0,
          right: 0,
          top: 0,
          alignItems: 'stretch',
          position: 'absolute',
        }}
        layout={{ gapMain: 27 }}
        data-label={'List Container'}
      >
        {cardData.map((item) => (
          <div
            onMouseDown={() => console.log(`clicked ${item.title}`)}
            css={{ transition: 'all .1s ease-in-out', '&:hover': { transform: 'scale(1.02)' } }}
            data-label={'Item'}
          >
            <Card
              photoURL={item.photoURL}
              layout={{ crossBasis: 184, flexBasis: 335 }}
              title={item.title}
            />
          </div>
        ))}
      </div>
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ height: 812, left: 555, width: 434, top: -123 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
      data-label={'List Example'}
    />
    <Scene
      style={{ height: 223, left: 555, width: 335, top: -434 }}
      component={Card}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{
        photoURL:
          'https://images.unsplash.com/photo-1465805139202-a644e217f00e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80',
        title: 'The Trip of a Lifetime',
      }}
      data-label={'Card'}
    />
  </Storyboard>
)

bundler: fix resolving if the package.json points to a directory

This should be a small bugfix

problem: import swap from 'lodash-move' breaks. import swap from 'lodash-move/lib/index' works.

the reason is probably that the package.json's contains this entry:
"main": "lib"
which we probably don't resolve correctly to /node_modules/lodash-move/lib/index.js

Dragging a view with margin applied causes weird jumping

STR

  1. Insert a view
  2. give it a margin of 32px by inserting marginLeft: 32 into style
  3. Drag it up and down

Observed: it 'jumps' while dragging

Expected: it stays still

(NB we have canvas controls for margin that we only apply for elements with flexbox layouts. you can switch them on for all elements if that helps)

Copying and pasting code triggers strange "autocomplete" code rewrites

Repro: Use the sample project below.

  1. Select the <Global>...</Global> tags
  2. Paste this code, so as to replace it
 <Global
      styles={css`
        .some-class {
          color: hotpink !important;
        }
      `}
    />

Result:
image

Project:

/** @jsx jsx */
import * as React from 'react'
import Global from 'emotion'
import { Scene, Storyboard, Text, UtopiaUtils, View, jsx } from 'utopia-api'
export var App = (props) => {
  return (
<div>
      <Global>
        
    </Global>

    <View style={{ ...(props.style || {}) }} layout={{ layoutSystem: 'pinSystem' }}>
      asdf
    </View>
  </div>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ height: 812, left: 0, width: 375, top: 0 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
    />
  </Storyboard>
)

You'll also need emotion support in your package.json:

{
  "name": "Utopia Project",
  "version": "0.1.0",
  "utopia": {
    "main-ui": "src/app.ui.js",
    "html": "preview.html",
    "js": "preview.jsx"
  },
  "dependencies": {
    "react": "16.13.1",
    "@types/react": "16.8.17",
    "csstype": "2.6.7",
    "react-dom": "16.13.1",
    "@types/react-dom": "16.8.4",
    "utopia-api": "0.4.1",
    "react-spring": "8.0.27",
    "@emotion/core": "10.0.28",
    "emotion": "10.0.27"
  }
}

Editor crashes irregularly when opening context menu

STR

  1. New project
  2. set opacity by clicking on the slider
  3. right click on the "Opacity" label
  4. set an overflow value by clicking on either hidden or visible
  5. right click on the "Overflow" label

expected: context menu opens
actual: editor crashes

n.b. it seems to be an issue with clicking on opacity and then overflow? or the other way around. but clicking on either first doesn't crash.

Perceived bug: inactive / unset rows look disabled

Symptom: inactive row didn't give feedback that it could be set, even while typing into an input it still looked. Resulted in confusion whether "this would work".

Underlying problems:
1 - our unset style looks disabled
2 - unset properties don't give any kind of feedback that they are in fact settable
3 - while in an input field, it still feels like the input is disabled.

Proposed prototype:

  • Approach this like other "formless" inspectors: use default text colour for all controls, but only show control borders and backgrounds for set properties
  • Unless a control is disabled, use hover state to show control borders so it's clear you can set
  • Controlled state remains as is, multiselect remains as is and does NOT need to be tackled before evaluating the prototype

Possible parts of a solution:
1 - tweak the unset style so it's still less prominent. Possibly adjust the disabled style downwards further
2 - Use hover states to indicate settability of fields. This will require some experimentation
3 - input fields that could be set received an active (buttons) or focused (inputs) field that matches the resulting state - in our case I believe that's always going to be set

I can drag the outermost div inside a scene and the inspector doesn't change layout type or show me left / right

Root cause: we are showing an incorrect inspector layout segment here that doesn't have inputs for left and right, hence you can't see them.

Proposed solution:

  • Whenever at least one property of left, right, top, bottom is set or observed, or position is set to absolute, change the computed layout section to absolute
  • Rename 'absolute' to 'constraints'
  • In the 'constraints' section, introduce a control for 'absolute' (toggle)
  • When clicking the constraints segment of the segment control and it is not already computed to be the current segment, insert position: absolute

Inserting a component seems to enter a 'partial insertion' mode

STR

Use the below code and then click “Card” in the insert menu


/** @jsx jsx */
import * as React from 'react'
import { Scene, Storyboard, View, jsx } from 'utopia-api'
export var Card = ({ style, title = 'blah' }) => {
  return <div style={style}>{title}</div>
}
export var App = (props) => {
  return (
    <div
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF' }}
      layout={{ layoutSystem: 'pinSystem' }}
    />
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      component={App}
      props={{ style: { top: 0, left: 0, bottom: 0, right: 0 } }}
      style={{ left: 0, top: 0, width: 375, height: 812 }}
      layout={{ layoutSystem: 'pinSystem' }}
    />
  </Storyboard>
)

Dragging in the Navigator often opens the File Browser

Describe the bug
For whatever bizarre reason, dragging an element in the Navigator triggers the pane to change to display the File Browser instead.

To Reproduce

  1. Select an element in the Navigator
  2. Start dragging

Result: Navigator pane is replaced with the file browser (this might take a few attempts but I've managed to reproduce it repeatedly with little trouble)

Expected behaviour
File browser doesn't show (MN: when dragging things on the canvas, not external files; see comment below)

Spike: Improve our CSS support on the canvas by trialling shadow root _for Utopia components_

Problem

Using external CSS files can mess with the canvas, inspector, and overall the application. This is particularly challenging when dealing with components that are intended to be root components

Observed problems surrounding this

  • CSS files seem to apply regardless of whether they are imported or otherwise referenced
  • CSS files setting styles on body, div etc can mess up the editor
  • Similarly for those using the same class names as the editor

Approach
In the past we trialled using the shadow root approach for the rendered output. This caused problems with screenshotting.

This spike aims to solve the problems above by using shadow root in for our editor / controls.

Improve our asset upload notifications and error handling

Problem

  1. When not logged in, we don't upload assets but it seems like we do. In particular, we fire 'replaced asset' notices
  2. When logged in, we also fire the replaced asset notice

Proposed Solution

  • Don't upload assets when logged in. Display an error toast saying "You're not logged in. Create an account or log in to upload images and assets"
  • Only fire the 'replaced asset' toast if an asset has actually been replaced

Improve the 'Insert to Flex', 'Convert to Flex' and 'reparent to flex' outcomes

Problem

Converting a view to flex, or conversely reparenting an element into a flex environment leads to unexpected outcomes that are not obvious from looking at the inspector.

This is typically because of one of these reasons

  1. the presence of width, height, left etcand position properties.
  2. the presence of those properties because of css classes or spread operators
  3. the presence of invisible or uneditable style attributes in a React component that cannot be overridden.

Proposal
To improve this experience:

Remove these properties where they exist

  • width and height that are explicitly set are transferred to base. Implicitly set ones are ignored
  • left, right, top, bottom are cleared
  • position is cleared

On insertion, we need to be particularly careful: inserting an element into a flex context should never set positional properties (left, right, top, bottom), and should only set flexBase and crossBase.

Warn of objects that cannot be well positioned

  • For elements and components where this fails, warn via toast
  • For elements that end up with 0 height, width, or both - warn via toast.

Other approaches considered and not in scope or rejected

  • Inserting 'neutralizing' overrides into style that would override css classes (clutter, outside of 80/20)
  • inserting 'neutralizing' css classes (same)

Moving an element with arrow keys does not update the code

STR

  1. Select any element in a new project
  2. Use the arrow keys to move it, either inindividually or by holding them down

Observed: code never updates until you make another change
Expected: code updates at least at some point, even if we put a delay in the middle

Saving a project reloads all external images

In this example project, making any changes to the code and saving causes these images to reload. On a slow connection, that can take up to 20 seconds.

/** @jsx jsx */
import * as React from 'react'
import { Rectangle, Scene, Storyboard, Text, View, img, jsx } from 'utopia-api'
export const cardData = [
  {
    title: 'Monument Valley',
    photoURL:
      'https://images.unsplash.com/photo-1509316785289-025f5b846b35?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3310&q=80',
  },

  {
    title: 'Raja Ampat',
    photoURL:
      'https://images.unsplash.com/photo-1586517659479-eea1f837aaf8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2090&q=80',
  },

  {
    title: 'Jal Mahal',
    photoURL:
      'https://images.unsplash.com/photo-1477586957327-847a0f3f4fe3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2100&q=80',
  },

  {
    title: 'Tokyo',
    photoURL:
      'https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2271&q=80',
  },
]
export var Card = (props) => {
  console.log(props)
  return (
    <div
      style={{
        ...(props.style || {}),
        minWidth: 335,
        minHeight: 184,
        paddingTop: 0,
        backgroundImage: `url(${props.photoURL})`,
        backgroundSize: 'cover',
        borderRadius: '0px',
        height: 255,
        position: 'relative',
        left: 0,
        top: 0,
      }}
      className={props.className}
    >
      <div
        style={{
          height: 42,
          bottom: 0,
          display: 'flex',
          color: '#080808BA',
          fontFamily: 'Arvo, sans-serif',
          left: 0,
          backgroundColor: '#FFFFFFB8',
          opacity: 1,
          borderRadius: '0px',
          paddingLeft: 8,
          right: 0,
          alignItems: 'center',
          paddingRight: 8,
          position: 'absolute',
        }}
      >
        <span style={{ color: '#0A0A0A', fontFamily: 'Inter', border: 'none', flexGrow: 1 }}>
          {props.title}
        </span>
        <div
          layout={{ crossBasis: 30, flexBasis: 100 }}
          css={{
            justifyContent: 'center',
            transition: 'all .03s linear',
            display: 'flex',
            '&:hover': { color: 'white', backgroundColor: 'black', fontWeight: 500 },
            fontSize: 12,
            border: '1px solid black',
            '&:active': { transform: 'translateY(2px)' },
            alignItems: 'center',
          }}
        >
          Register
        </div>
      </div>
      <View
        style={{
          height: 30,
          backgroundColor: '#DDDDDDBD',
          width: 33,
          opacity: 1,
          borderRadius: '12.5px',
          right: 11,
          top: 12,
          position: 'absolute',
        }}
        layout={{ layoutSystem: 'pinSystem' }}
      >
        <img
          style={{
            height: 22,
            left: 6,
            width: 21,
            mixBlendMode: 'multiply',
            opacity: 0.25,
            top: 4,
          }}
          src={'./assets/heart-icon-transparent-icon-symbol-love-black-11553480202wbkavsmiom.png'}
          data-aspect-ratio-locked
          alt={''}
          data-label={'heart icon'}
        />
      </View>
    </div>
  )
}
Card.propertyControls = {
  title: {
    type: 'string',
    title: 'Greetings',
    defaultValue: 'Hello World',
  },

  photoURL: {
    type: 'string',
    title: 'Photo URL',
    defaultValue:
      'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
  },
}
export var App = (props) => {
  return (
    <View style={{ ...(props.style || {}), backgroundColor: '#FFFFFF', width: 433 }} layout={{}}>
      <div
        style={{
          bottom: 0,
          flexDirection: 'column',
          display: 'flex',
          left: 0,
          right: 0,
          top: 0,
          alignItems: 'stretch',
          position: 'absolute',
        }}
        layout={{ gapMain: 27 }}
        data-label={'List Container'}
      >
        {cardData.map((item) => (
          <div
            onMouseDown={() => console.log(`clicked ${item.title}`)}
            css={{ transition: 'all .1s ease-in-out', '&:hover': { transform: 'scale(1.02)' } }}
            data-label={'Item'}
          >
            <Card
              photoURL={item.photoURL}
              layout={{ crossBasis: 184, flexBasis: 335 }}
              title={item.title}
            />
          </div>
        ))}
      </div>
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ left: 555, top: -391, width: 434, height: 812 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
      data-label={'List Example'}
    />
    <Scene
      style={{ left: 1140, top: -391, width: 335, height: 223 }}
      component={Card}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{
        photoURL:
          'https://images.unsplash.com/photo-1465805139202-a644e217f00e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80',
        title: 'The Trip of a Lifetime',
      }}
      data-label={'Card'}
    />
  </Storyboard>
)

Importing from inside the project doesn't work

STR

  1. Load default project
  2. create cards.js in src
  3. Add code export const cardData = [1,2,3.]
  4. Import cardData from cards.js in app.ui.js

Observed: fails with Dependency Not Found error, including all variants: ‘cards’, ‘cards.js’, ‘./cards.js’, ‘/src/cards’

image

Diagonal resizing is broken

Describe the bug
A manually inserted div or span cannot be resized diagonally.

To Reproduce

  1. In code, add a div (or View) into the main view, and fill it withhello world, so that the result is <span>Hello World</span>
  2. Try to resize it diagonally

Result: 9 block shows, but can't resize diagonally
Expected: either 9-block doesn't show (bleurgh) or we can actually resize

Show shadows in the inspector that don't use the utility fn

STR

Example 1

  1. Insert a view
  2. give it a shadow. Note the utility function
  3. remove that shadow and replace it with a custom shadow, eg boxShadow: '0px 3px 5px 1px rgba(0,0,0,.25)'

Expected: shows up in in the inspector. Observed: it does not. Also applies to multiple shadows

Note: we originally introduced the utility fn because border would affect the layout and 'move' the box. Same with margin and why we didn't support it. This is no longer an issue, so I believe we could have border set real border, boxShadow a real box shadow, and retire the utility fn.

Reparenting should allow reparenting into everything you can see

Problem: In legacy, we could reparent into any visible element. This is no longer possible.

You can see this by playing the Pyramid game: try to reparent the top of the pyramid (seen from above) into the top block of another pyramid.

/** @jsx jsx */
import * as React from 'react'
import { Scene, Storyboard, Text, UtopiaUtils, View, jsx } from 'utopia-api'
export var App = (props) => {
  return (
    <View
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF', left: 0, top: 0 }}
      layout={{ layoutSystem: 'pinSystem' }}
    >
      <View
        style={{ backgroundColor: '#DDDDDD', left: 123, width: 135, height: 118, top: 240 }}
        layout={{ layoutSystem: 'pinSystem' }}
      >
        <View
          style={{ left: 19, top: 17, width: 100, height: 87, backgroundColor: '#636363DB' }}
          layout={{ layoutSystem: 'pinSystem' }}
        >
          <View
            style={{ backgroundColor: '#636363DB', width: 50, height: 43, left: 24, top: 19 }}
            layout={{ layoutSystem: 'pinSystem' }}
          />
        </View>
      </View>
      <View
        style={{ backgroundColor: '#DDDDDD', left: 123, top: 67, width: 135, height: 118 }}
        layout={{ layoutSystem: 'pinSystem' }}
      >
        <View
          style={{ left: 19, top: 17, width: 100, height: 87, backgroundColor: '#636363DB' }}
          layout={{ layoutSystem: 'pinSystem' }}
        >
          <View
            style={{ backgroundColor: '#636363DB', width: 50, height: 43, left: 23, top: 22 }}
            layout={{ layoutSystem: 'pinSystem' }}
          />
        </View>
      </View>
      <Text
        style={{ fontSize: 16, width: 226, height: 103, left: 78, top: 419 }}
        text={'Try to reparenting the top of pyramid 2 into pyramid 1'}
        textSizing={'fixed'}
      />
    </View>
  )
}
export var FlexRow = (props) => {
  return <div style={{ display: 'flex' }}>{props.children}</div>
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ height: 812, left: 0, width: 375, top: 0 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
    />
  </Storyboard>
)

The inspector should show the observed value

Problem
At the moment, the inspector controls in their unset state variously do not show the actual value, but the default value we hardcoded. In many cases this works out ok, but frequently it does not:

  • Font family and size are set by the browser or (often) in a stylesheet.
  • using css classes to eg apply flex flex-column items-center (from our utopions, or tailwinds.css) cause the inspector to show flex controls (great!), but do not update the other controls.

Proposed Solution

  • Show the observed / measured value for all properties

Related issues: #38, #6, #5

Set up the repo

  • Set up deployments
  • Fix caching in deployments
  • Set up Slack integrations
  • Set up Discord integrations
  • Discuss and write up developer guidelines
  • Check and update readme
  • Transfer tickets
  • Archive old project

QA for programmatic / scriptable canvas

Main test cases

  1. generatively create and position scenes, e.g. create permutations of a set of components with various props
  2. link some props to javascript variables, preserving others as user-configurable in the UI. We already had this issue where we wanted to eg set a default style prop as coming from an imported theme object, or use an array of JS values as options, etc
  3. create arrays of things as options for eg dropdowns
// for 1 + 2: 
const userNames= ['James', 'Εἰρήνη', '愛美']
const const styles = [getPremiumUserStyle(), getStandardUserStyle()]

// pseudocode to generate 3 x 2 scenes of this `UserName` component
const UserName = props => <div style={padding: '4px 2px', border: '2px solid orange', {...props.style}>{props.userName}</div>
  1. use javascript expressions / function results as default values, eg pseudocode
const theme = { background: 'white', defaultColor: 'rgb(0,0,15)' }
...
<Canvas>
  <Scene defaultProps={style: {{ backgroundColor: theme.backgroundColor }} }>
   <MyLittleComponent />
  </Scene>
</Canvas>

  1. be able to use JSX as a default value, including for children prop. Pseudocode:
    defaultProps: { children: {<Button>Click</Button>} }

  2. auto-sizing scenes (?)

Canvas can show an incorrect Reference Error

Error: "ReferenceError: divHeight is not defined":

STR: Edit the code in the following sequence:

Step 1

/** @jsx jsx */
import * as React from 'react'
import { Scene, Storyboard, View, jsx } from 'utopia-api'
export var App = (props) => {
  const words = ['Hello', 'There', 'General', 'Kenobi']
  return (
    <div
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF' }}
      layout={{ layoutSystem: 'pinSystem' }}
    >
      {words.map((word, i) => {
        return <div style={{ top: 10, height: 100 }}>{word}</div>
      })}
    </div>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      component={App}
      props={{ style: { top: 0, left: 0, bottom: 0, right: 0 } }}
      style={{ left: 0, top: 0, width: 375, height: 812 }}
      layout={{ layoutSystem: 'pinSystem' }}
    />
  </Storyboard>
)

Step 2


/** @jsx jsx */
import * as React from 'react'
import { Scene, Storyboard, View, jsx } from 'utopia-api'
export var App = (props) => {
  const words = ['Hello', 'There', 'General', 'Kenobi']
  const divHeight = 100
  return (
    <div
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF' }}
      layout={{ layoutSystem: 'pinSystem' }}
    >
      {words.map((word, i) => {
        return <div style={{ top: 10, height: 100 }}>{word}</div>
      })}
    </div>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      component={App}
      props={{ style: { top: 0, left: 0, bottom: 0, right: 0 } }}
      style={{ left: 0, top: 0, width: 375, height: 812 }}
      layout={{ layoutSystem: 'pinSystem' }}
    />
  </Storyboard>
)

Step 3


/** @jsx jsx */
import * as React from 'react'
import { Scene, Storyboard, View, jsx } from 'utopia-api'
export var App = (props) => {
  const words = ['Hello', 'There', 'General', 'Kenobi']
  const divHeight = 100
  return (
    <div
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF' }}
      layout={{ layoutSystem: 'pinSystem' }}
    >
      {words.map((word, i) => {
        return <div style={{ top: 10, height: divHeight }}>{word}</div>
      })}
    </div>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      component={App}
      props={{ style: { top: 0, left: 0, bottom: 0, right: 0 } }}
      style={{ left: 0, top: 0, width: 375, height: 812 }}
      layout={{ layoutSystem: 'pinSystem' }}
    />
  </Storyboard>
)

You can remove the error through these changes

/** @jsx jsx */
import * as React from 'react'
import { Scene, Storyboard, View, jsx } from 'utopia-api'
export var App = (props) => {
  const words = ['Hello', 'There', 'General', 'Kenobi']
  const divHeight = 100
  return (
    <div
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF' }}
      layout={{ layoutSystem: 'pinSystem' }}
    >
      {words.map((word, i) => {
        return <div style={{ top: 10 + (divHeight + 10)*i, height: divHeight }}>{word}</div>
      })}
    </div>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      component={App}
      props={{ style: { top: 0, left: 0, bottom: 0, right: 0 } }}
      style={{ left: 0, top: 0, width: 375, height: 812 }}
      layout={{ layoutSystem: 'pinSystem' }}
    />
  </Storyboard>
)

Bug: switching to Flow doesn't clear out non-flow settings; non-flow settings don't prevent flow from showing up.

  1. When switching to flow, we should clean out non-flow positioning styles, since they are invisible in the inspector and this causes no end of confusion. Notably all pins, as well as left, right, top, bottom and position

  2. Obversely, if there are non-flow positioning properties present, it isn’t flow and shouldn't be shown as such. These specifically include pins, and any settings of left, right, top, or bottom, and position: sticky

Repro:
1 - pin a view to all edges
2 - change its layout to flow via the inspector

result: it ends up who-knows-where for whatever reason.

Rendering error: trying to spread non-object type undefined

STR

  1. Open up the below project
  2. Behold the error

Expected: this works, or gives me a clearer error message
Unexpected: this doesn't work

/** @jsx jsx */
import * as React from 'react'
import { Rectangle, Scene, Storyboard, View, img, jsx } from 'utopia-api'

export const cardData = [
  {
    title: 'Monument Valley',
    photoURL:
      'https://images.unsplash.com/photo-1509316785289-025f5b846b35?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3310&q=80',
  },
  {
    title: 'Raja Ampat',
    photoURL:
      'https://images.unsplash.com/photo-1586517659479-eea1f837aaf8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2090&q=80',
  },
  {
    title: 'Jal Mahal',
    photoURL:
      'https://images.unsplash.com/photo-1477586957327-847a0f3f4fe3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2100&q=80',
  },
]
export var Card = (props) => {
  console.log(props)
  return (
    <div
      style={{
        ...props.style,
        position: 'relative',
        minWidth: 335,
        minHeight: 184,
        paddingTop: 0,
        backgroundImage: `url(${props.photoURL})`,
        backgroundSize: 'cover',
        borderRadius: '0px',
      }}
    >
      <div
        style={{
          height: 32,
          display: 'flex',
          color: '#080808BA',
          fontFamily: 'Lora, sans-serif',
          left: 0,
          backgroundColor: '#FFFFFFB8',
          paddingLeft: 4,
          top: 152,
          alignItems: 'center',
          position: 'absolute',
          right: 0,
        }}
      >
        {props.title}
      </div>
      <Rectangle style={{ height: 0, left: 305, backgroundColor: '#DDDDDD', width: 1, top: 13 }} />
      <View
        style={{
          position: 'absolute',
          top: 12,
          width: 33,
          height: 30,
          left: 291,
          opacity: 1,
          backgroundColor: '#DDDDDDBD',
          borderRadius: '12.5px',
        }}
        layout={{ layoutSystem: 'pinSystem' }}
      >
        <img
          style={{
            width: 21,
            height: 22,
            mixBlendMode: 'multiply',
            opacity: 0.25,
            left: 6,
            top: 4,
          }}
          src={'./assets/heart-icon-transparent-icon-symbol-love-black-11553480202wbkavsmiom.png'}
          data-aspect-ratio-locked
          alt={''}
        />
      </View>
    </div>
  )
}
Card.propertyControls = {
  title: {
    type: 'string',
    title: 'Greetings',
    defaultValue: 'Hello World',
  },

  photoURL: {
    type: 'string',
    title: 'Photo URL',
    defaultValue:
      'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
  },
}
export var App = (props) => {
  return (
    <View
      style={{
        ...(props.style || {}),
        backgroundColor: '#FFFFFF',
        position: 'absolute',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'stretch',
        left: 30,
        width: 304,
      }}
      layout={{ gapMain: 16 }}
    >
      <div>
        {cardData.map((item) => (
          <Card title={item.title} photoURL={item.photoURL} />
        ))}
      </div>
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ height: 812, left: 555, width: 375, top: -21 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
    />
    <Scene
      style={{ left: 1131, top: -46, width: 335, height: 184 }}
      component={Card}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{
        title: 'The Trip of a Lifetime',
        photoURL:
          'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
      }}
      data-label={'Card'}
    />
  </Storyboard>
)

Components that have pinned children but don't specifically include `position: relative` lose their children when you place the component into another one

Problem: The card component in this sample project contains a number of children. These children are laid out correctly inside the card component.

When using the card component to generate a list view, the child views instead end up elsewhere.

This appears to happen because the top-level view / div of the component does not contain position: relative or position: absolute. This should be called out as a warning in the navigator and inspector, since it creates an entirely unexpected behaviour. There may be other solutions to this problem.

Possibly related: #3956

Sample code (look at the Card component and then the instances of it)

/** @jsx jsx */
import * as React from 'react'
import { Rectangle, Scene, Storyboard, View, img, jsx } from 'utopia-api'
const myList = ['Hello', 'World', 'Maybe']
export const cardData = [
  {
    title: '',
    photoURL:
      'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
  },
]
export var Card = (props) => {
  return (
    <div
      style={{
        ...props.style,
        width: 335,
        height: 184,
        paddingTop: 0,
        backgroundImage:
          'url(https://images.unsplash.com/photo-1591564272079-03679e3bfc3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80), linear-gradient(90deg, #D3D3D3 0%, #e1e1e1 100%)',
        backgroundSize: 'cover',
        borderRadius: '0px',
      }}
    >
      <div
        style={{
          height: 32,
          display: 'flex',
          color: '#080808BA',
          fontFamily: 'Lora, sans-serif',
          left: 0,
          backgroundColor: '#FFFFFFB8',
          width: 335,
          paddingLeft: 4,
          top: 152,
          alignItems: 'center',
          position: 'absolute',
        }}
      >
        {props.title}
      </div>
      <Rectangle style={{ height: 0, left: 305, backgroundColor: '#DDDDDD', width: 1, top: 13 }} />
      <View
        style={{
          position: 'absolute',
          top: 12,
          width: 33,
          height: 30,
          left: 291,
          opacity: 1,
          backgroundColor: '#DDDDDDBD',
          borderRadius: '12.5px',
        }}
        layout={{ layoutSystem: 'pinSystem' }}
      >
        <img
          style={{
            width: 21,
            height: 22,
            mixBlendMode: 'multiply',
            opacity: 0.25,
            left: 6,
            top: 4,
          }}
          src={'./assets/heart-icon-transparent-icon-symbol-love-black-11553480202wbkavsmiom.png'}
          data-aspect-ratio-locked
          alt={''}
        />
      </View>
    </div>
  )
}
Card.propertyControls = {
  title: {
    type: 'string',
    title: 'Greetings',
    defaultValue: 'Hello World',
  },

  photoURL: {
    type: 'string',
    title: 'Photo URL',
    defaultValue:
      'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
  },
}
export var App = (props) => {
  return (
    <View
      style={{
        ...(props.style || {}),
        backgroundColor: '#FFFFFF',
        width: 375,
        position: 'absolute',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'stretch',
      }}
    >
      {myList.map((x) => (
        <Card title={x} />
      ))}
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ height: 812, left: 555, width: 375, top: -21 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
    />
    <Scene
      style={{ left: 1065, top: 31, width: 335, height: 184 }}
      component={Card}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{
        title: 'The Trip of a Lifetime',
        photoURL:
          'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
      }}
      data-label={'Card'}
    />
  </Storyboard>
)

Clicking into input field - but not making a change - changes controlled to set status

STR

  1. Open below project
  2. select one of the components inside the map function
  3. Click on one of these controls inside the inspector

Expected: shows current value, doesn't override value when not making a change to the input
Observed: doesn't show anything, clicking into it overrides the value upon exit even though no input happened

image

/** @jsx jsx */
import * as React from 'react'
import { Rectangle, Scene, Storyboard, Text, View, img, jsx } from 'utopia-api'
export const cardData = [
  {
    title: 'Monument Valley',
    photoURL:
      'https://images.unsplash.com/photo-1509316785289-025f5b846b35?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3310&q=80',
  },

  {
    title: 'Raja Ampat',
    photoURL:
      'https://images.unsplash.com/photo-1586517659479-eea1f837aaf8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2090&q=80',
  },

  {
    title: 'Jal Mahal',
    photoURL:
      'https://images.unsplash.com/photo-1477586957327-847a0f3f4fe3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2100&q=80',
  },

  {
    title: 'Tokyo',
    photoURL:
      'https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2271&q=80',
  },
]
export var Card = (props) => {
  console.log(props)
  return (
    <div
      style={{
        ...(props.style || {}),
        minWidth: 335,
        minHeight: 184,
        paddingTop: 0,
        backgroundImage: `url(${props.photoURL})`,
        backgroundSize: 'cover',
        borderRadius: '0px',
        height: 255,
        top: -4,
        position: 'relative',
      }}
      className={props.className}
    >
      <div
        style={{
          height: 42,
          bottom: 0,
          display: 'flex',
          color: '#080808BA',
          fontFamily: 'Arvo, sans-serif',
          left: 0,
          backgroundColor: '#FFFFFFB8',
          opacity: 1,
          borderRadius: '0px',
          paddingLeft: 8,
          right: 0,
          alignItems: 'center',
          paddingRight: 8,
          position: 'absolute',
        }}
      >
        <span style={{ color: '#0A0A0A', fontFamily: 'Inter', border: 'none', flexGrow: 1 }}>
          {props.title}
        </span>
        <div
          layout={{ crossBasis: 30, flexBasis: 100 }}
          css={{
            justifyContent: 'center',
            transition: 'all .03s linear',
            display: 'flex',
            '&:hover': { color: 'white', backgroundColor: 'black', fontWeight: 500 },
            fontSize: 12,
            border: '1px solid black',
            '&:active': { transform: 'translateY(2px)' },
            alignItems: 'center',
          }}
        >
          Register
        </div>
      </div>
      <View
        style={{
          height: 30,
          backgroundColor: '#DDDDDDBD',
          width: 33,
          opacity: 1,
          borderRadius: '12.5px',
          right: 11,
          top: 12,
          position: 'absolute',
        }}
        layout={{ layoutSystem: 'pinSystem' }}
      >
        <img
          style={{
            height: 22,
            left: 6,
            width: 21,
            mixBlendMode: 'multiply',
            opacity: 0.25,
            top: 4,
          }}
          src={'./assets/heart-icon-transparent-icon-symbol-love-black-11553480202wbkavsmiom.png'}
          data-aspect-ratio-locked
          alt={''}
        />
      </View>
    </div>
  )
}
Card.propertyControls = {
  title: {
    type: 'string',
    title: 'Greetings',
    defaultValue: 'Hello World',
  },

  photoURL: {
    type: 'string',
    title: 'Photo URL',
    defaultValue:
      'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
  },
}
export var App = (props) => {
  return (
    <View style={{ ...(props.style || {}), backgroundColor: '#FFFFFF', width: 433 }} layout={{}}>
      <div
        style={{
          bottom: 0,
          flexDirection: 'column',
          display: 'flex',
          left: 0,
          right: 0,
          top: 0,
          alignItems: 'stretch',
          position: 'absolute',
        }}
        layout={{ gapMain: 27 }}
        data-label={'List Container'}
      >
        {cardData.map((item) => (
          <div
            onMouseDown={() => console.log(`clicked ${item.title}`)}
            css={{ transition: 'all .1s ease-in-out', '&:hover': { transform: 'scale(1.02)' } }}
            data-label={'Item'}
          >
            <Card
              photoURL={item.photoURL}
              layout={{ crossBasis: 184, flexBasis: 335 }}
              title={''}
            />
          </div>
        ))}
      </div>
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ height: 812, left: 555, width: 434, top: -123 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
      data-label={'List Example'}
    />
    <Scene
      style={{ height: 223, left: 555, width: 335, top: -434 }}
      component={Card}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{
        photoURL:
          'https://images.unsplash.com/photo-1465805139202-a644e217f00e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2250&q=80',
        title: 'The Trip of a Lifetime',
      }}
      data-label={'Card'}
    />
  </Storyboard>
)

"Layout may be affected by external css" is affected by Emotion

"Layout may be affected by external css" should really only show up if there's external CSS. Emotion doesn't count.

If we can't do that because it's hard to distinguish etc, we should bring down the level of emphasis, this makes me think I've committed a grave mistake :)

image

When converting a view to Flex, things end up in odd / unpredicted positions and dimensions

STR

  1. Use the sample code below
  2. Convert the outer view to flex via the inspector

Expected: things resize and reposition nicely, eg in a row

Observed: things end up in super strange places, as if someone manually redistributed them. Without the code editor open, it's hard to know how and why.

Investigation shows that the children are not changed, and still carry the same information (top, left, width, height) as before.

Suggested fix: when changing the layout system, strip the children of that information.

  • Width and height should (used to?) go into flexBase and crossBase.
  • positional information (left, top, bottom, right, margin, position) should be removed.
  • This should also happen after reparenting or pasting an element into a flex container
  • Fire one Info toast if at least one element had to be changed: "One or more elements had position information that was removed"
/** @jsx jsx */
import * as React from 'react'
import { Scene, Storyboard, View, jsx } from 'utopia-api'
export var App = (props) => {
  return (
    <View
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF' }}
      layout={{ layoutSystem: 'pinSystem' }}
    >
      <View
        style={{ backgroundColor: '#DDDDDD', left: 0, width: 375, height: 38, top: 774 }}
        layout={{ layoutSystem: 'pinSystem' }}
      />
      <View
        style={{ backgroundColor: '#DDDDDD', left: 0, width: 375, height: 674, top: 69 }}
        layout={{ layoutSystem: 'pinSystem' }}
      />
      <View
        style={{ backgroundColor: '#DDDDDD', left: 0, top: 0, width: 375, height: 38 }}
        layout={{ layoutSystem: 'pinSystem' }}
      />
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      component={App}
      props={{ style: { top: 0, left: 0, bottom: 0, right: 0 } }}
      style={{ left: 0, top: 0, width: 375, height: 812 }}
      layout={{ layoutSystem: 'pinSystem' }}
    />
  </Storyboard>
)

bundler: fix esmodules support in the module resolution

lodash-es is a special esmodules-only version (this is kind of a rarity) of lodash, and it breaks on Utopia.

The reason is that we don't yet support esmodules resolution.

I'm not sure about the size of the fix, because esmodules don't use require() so it will take some investigation how best to solve this for modern Chrome.

Since the number of modules that only support esmodules and not the node/require style are very small (for now, but growing), I think this ticket is not urgent.

Remove View, and update our keyboard shortcuts accordingly

Problem

Our default project, and editor, create the impression that they depend on special UI components like View. This is confirmed when inserting them. We already have addressed this problem partially (with images), but have more work to do here.

Proposed Solution

  • Retire the ‘View’ component, since it no longer does things we need
  • Remove it from the sample project and replace it with div
  • Insert View <Subdued>div</Subdued>
  • Replace the default icon[ ]

While nearby:

  • Keyboard shortcut ‘A’ to insert a scene, mimicking ‘Artboard’.

Need to distinguish between a single child and an array of children

Unfortunately there exists a function React.Children.only which has been identified as problematic (see here, here, here etc.), which acts as a downcast when the child of an element is an element itself, or throws otherwise. Because this function throws if the children are an array (even if the array only contains one element), it means that we have to maintain that distinction from the initial parse of the jsx file up until the rendering of the canvas, meaning we also need to special case all uses of React.Children.map that we apply.

We have this hack in place until then.

Bug: you can accidentally trigger insert mode and not get out

Problem
Accidentally entered insert mode for a text element without realising because I thought I had a different window focused (a regular occurence).

Changed to the other window to type what I needed to type, switched back to Utopia and now I can't exit Insert mode (via pressing esc or trying to complete an insertion - had to reload the editor!) because:

    Error: Unable to use insert drag state in select mode.
        at https://utopia.app/editor/1.7e88d1d90dfe917165b7.js:1:213797
        at Object.s [as foldEither] (https://utopia.app/editor/1.7e88d1d90dfe917165b7.js:1:82441)
        at Object.z [as produceCanvasTransientState] (https://utopia.app/editor/1.7e88d1d90dfe917165b7.js:1:211778)
        at https://utopia.app/editor/editor.40d79b3703ee256b79c4.js:1:1033446
        at t.reduce.reduce (https://utopia.app/editor/editor.40d79b3703ee256b79c4.js:1:1035902)
        at Array.reduce (<anonymous>)
        at Object.CEmK.n.editorDispatch (https://utopia.app/editor/editor.40d79b3703ee256b79c4.js:1:1032947)
        at n (https://utopia.app/editor/editor.40d79b3703ee256b79c4.js:1:1660135)
        at n.Editor.dispatch (https://utopia.app/editor/editor.40d79b3703ee256b79c4.js:1:1660269)

Inspector does not show the current layout system if it comes from css classes

Describe the bug
The inspector does not show the correct layout system if it comes from css. (Possibly related: #5 )

image

To Reproduce
Use the below code. Ideally, do this before we've re-inserted the shadow dom

/** @jsx jsx */
import * as React from 'react'
import { Scene, Storyboard, View, jsx } from 'utopia-api'
export var App = (props) => {
  return (
    <View
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF' }}
      layout={{ layoutSystem: 'pinSystem' }}
    >
      <div className={'flex flex-column'}>I show up with the correct controls showing up in layout system, but with the wrong things</div>
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      component={App}
      props={{ style: { top: 0, left: 0, bottom: 0, right: 0 } }}
      style={{ left: 0, top: 0, width: 375, height: 812 }}
      layout={{ layoutSystem: 'pinSystem' }}
    />
  </Storyboard>
)

cmd+dragging a view / element / component causes unpredictable behaviours based on parent layout mode

Problem
"I tried moving this div with CMD like you said, but it's not moving"

Root cause
The div in question was in another div, which didn't have a layout type enabled. As a result, it behaved like a flow layout. In a flow layout, at most you can reorder (and not even that if there's a generated element elsewhere in the flow). Otherwise the element stays put until you've moved it out fully. If you're moving it into another flow (or flex) layout, it can give the impression it's still "stuck"

Analysis
In almost any design tool, cmd+drag gives you free-form control over the element you're dragging. We used to do that as well. This meant that you were getting the feedback "I'm moving this thing", with what happens to it (because of layout system etc becoming a separate thing). However, this either regressed or broke. As a consequence, a view inside a flow layout, flex layout, or pinned absolutely, will behave radically differently but with little to nothing on the canvas to tell you that. It also means that during a reparent you get weird or unpredictable behaviours. But most importantly, it gives the impression the editor is "broken", "laggy", "not behaving well".

Proposed solution

  • In legacy we solved this well, through suffering, by not actually reparenting until the element was dropped, and instead showing a placeholder control.
  • If that's not possible, another option is to clone or snapshot the element and move that "wherever the mouse goes", to give the illusion of control. However, that will probably fail on drop
  • To solve that, there is the option of creating a placeholder view (not a control) that is actually being reparented-in-realtime, possibly with similar top level settings

(Please discuss in #feature-layout before implementing a solution)

bundler: manage peer dependencies of a certain package

problem: if a package has peer dependencies today, it will fail with a silent error. the user has to know to install the peer dependencies manually.

proposed solution: the dependency interface should propose adding missing peer dependencies.

Fix the flex-gap slider so it works on first interaction even when unset

The slider doesn't work right now, seemingly until the value is set.

STR:

  1. insert a couple of views into main view
  2. make main view layout-system flex
  3. try to set the flex-gap property with the slider

Expected: it works
Observed: it does not, until you enter a number into the input control

"Missing default props" for list items that wrap components in a div

image

STR

  1. copypaste below code into project
  2. go to line 118

Expected:

  • Navigator shows these as components not ghost divs
  • Inspector doesn't complain about missing default props when they're set
/** @jsx jsx */
import * as React from 'react'
import { Rectangle, Scene, Storyboard, Text, View, img, jsx } from 'utopia-api'
export const cardData = [
  {
    title: 'Monument Valley',
    photoURL:
      'https://images.unsplash.com/photo-1509316785289-025f5b846b35?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3310&q=80',
  },

  {
    title: 'Raja Ampat',
    photoURL:
      'https://images.unsplash.com/photo-1586517659479-eea1f837aaf8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2090&q=80',
  },

  {
    title: 'Jal Mahal',
    photoURL:
      'https://images.unsplash.com/photo-1477586957327-847a0f3f4fe3?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2100&q=80',
  },
]
export var Card = (props) => {
  console.log(props)
  return (
    <div
      className={props.className}
      style={{
        ...(props.style || {}),
        position: 'relative',
        minWidth: 335,
        minHeight: 184,
        paddingTop: 0,
        backgroundImage: `url(${props.photoURL})`,
        backgroundSize: 'cover',
        borderRadius: '0px',
      }}
    >
      <div
        style={{
          display: 'flex',
          color: '#080808BA',
          fontFamily: 'Lora, sans-serif',
          left: 0,
          backgroundColor: '#FFFFFFB8',
          paddingLeft: 4,
          alignItems: 'center',
          position: 'absolute',
          right: 0,
          bottom: 0,
          height: 32,
        }}
      >
        {props.title}
      </div>
      <Rectangle style={{ height: 0, left: 305, backgroundColor: '#DDDDDD', width: 1, top: 13 }} />
      <View
        style={{
          position: 'absolute',
          top: 12,
          height: 30,
          opacity: 1,
          backgroundColor: '#DDDDDDBD',
          borderRadius: '12.5px',
          right: 11,
          width: 33,
        }}
        layout={{ layoutSystem: 'pinSystem' }}
      >
        <img
          style={{
            width: 21,
            height: 22,
            mixBlendMode: 'multiply',
            opacity: 0.25,
            left: 6,
            top: 4,
          }}
          src={'./assets/heart-icon-transparent-icon-symbol-love-black-11553480202wbkavsmiom.png'}
          data-aspect-ratio-locked
          alt={''}
        />
      </View>
    </div>
  )
}
Card.propertyControls = {
  title: {
    type: 'string',
    title: 'Greetings',
    defaultValue: 'Hello World',
  },

  photoURL: {
    type: 'string',
    title: 'Photo URL',
    defaultValue:
      'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
  },
}
export var App = (props) => {
  return (
    <View style={{ ...(props.style || {}), backgroundColor: '#FFFFFF', width: 433 }} layout={{}}>
      <div
        data-label={'List Container'}
        style={{
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'stretch',
          position: 'absolute',
          top: 0,
          left: 0,
          bottom: 0,
          right: 0,
        }}
        layout={{ gapMain: 27 }}
      >
        {cardData.map((item) => (
          <div css={{ transition: 'all .3s ease-in-out', '&:hover': { transform: 'scale(1.02)' } }}>
            <Card
              title={item.title}
              photoURL={item.photoURL}
              layout={{ flexBasis: 335, crossBasis: 184 }}
            />
          </div>
        ))}
      </div>
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ left: 555, top: -21, width: 434, height: 812 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
      data-label={'List Example'}
    />
    <Scene
      style={{ left: 555, top: -332, width: 335, height: 184 }}
      component={Card}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{
        title: 'The Trip of a Lifetime',
        photoURL:
          'https://images.unsplash.com/photo-1591587761216-4188baf81c68?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80',
      }}
      data-label={'Card'}
    />
    <Text
      style={{
        height: 14,
        fontSize: '11px',
        fontFamily: 'inter',
        fontWeight: 500,
        width: 71,
        color: '#1100FF',
        left: 1112,
        top: -190,
      }}
      text={'Try live mode'}
      textSizing={'auto'}
    />
  </Storyboard>
)

Add event handler section to the inspector

Problem Can't see elements or components with built-in event handlers (onClick etc), which makes it very easy to accidentally delete them (since we let you do that even if there's code

Proposed solution

  • Create new subsection in inspector
  • Show array of event handler name, name of the function it's calling in standard grid format (label on the left, input or even just label on the right (depending on what we could put into the input)
  • support right-click to delete

Assess how good / bad / indifferent this is.

Improve and regularize the file and folders of the default project

Problem
Our file / folder structure is misaligned with the 'default' react project structure - i.e. the one you'd find in CRA projects or Codesandbox ones, etc

This will cause more pain down the line, makes us look unnecessarily non-standard on first impression, and makes the editor unnecessarily dependent on file names

Proposed solution

  • Rename preview.html to index.html and preview.jsx to app.jsx
  • Use app.jsx as the default uijs file
  • move app.jsx out of the default folder into src
  • move and rename public/preview.jsx to src/index.js
  • Add the entry point for the canvas to package.json, hardcode app.jsx as a fallback, and give a meaningful error message on the canvas if that file does not exist.

Unable to select the dropdown element via the Canvas in this code

Describe the bug
There is no way to select the dropdown element in this code

To Reproduce
Requires the feature/local-bundling-2 branch until that is merged

  1. Add antd as a dependency to your project
  2. Use the below code
  3. Try to select the element via the Canvas

Code:

/** @jsx jsx */
import { Dropdown, Menu } from 'antd'
import 'antd/dist/antd.css'
import * as React from 'react'
import { Scene, Storyboard, UtopiaUtils, View, jsx } from 'utopia-api'
export var MyMenu = (props) => {
  return (
    <Menu>
      <Menu.Item>
        1st menu item
      </Menu.Item>
      <Menu.Item>
        2nd menu item
      </Menu.Item>
      <Menu.Item>
        3rd menu item
      </Menu.Item>
      <Menu.Item danger>a danger item</Menu.Item>
    </Menu>
  )
}
export var App = (props) => {
  const menu = <MyMenu />
  return (
    <View
      style={{ ...(props.style || {}), backgroundColor: '#FFFFFF' }}
      layout={{ layoutSystem: 'pinSystem' }}
    >
      <Dropdown overlay={menu} className={'hi'}>
        <a onClick={(e) => e.preventDefault()} className={'ant-dropdown-link'}>
          Hover me
        </a>
      </Dropdown>
      ,
    </View>
  )
}
export var storyboard = (
  <Storyboard layout={{ layoutSystem: 'pinSystem' }}>
    <Scene
      style={{ height: 812, left: 0, width: 375, top: 0 }}
      component={App}
      layout={{ layoutSystem: 'pinSystem' }}
      props={{ style: { bottom: 0, left: 0, right: 0, top: 0 } }}
    />
  </Storyboard>
)

Result: Unable to select it

Expected behaviour
Able to select it

Flex coming from variable isn't being indicated in inspector, but shows up correctly on canvas

Repro:

  1. Copy + paste code
  2. Select FLEX... in the navigator

Observed:

  • inspector shows padding unset and at value 8.
  • canvas shows padding (correctly) set to 8 all-round

Expected:

  • they match up
  • inspector shows padding prop as controlled

image

Code:

/** @jsx jsx */
import * as React from 'react'
import { View, jsx } from 'utopia-api'
export var canvasMetadata = {
  scenes: [
    {
      component: 'App',
      props: { layout: { bottom: 0, left: 0, right: 0, top: 0 } },
      frame: { left: 0, top: 0, width: 247, height: 314 },
      container: { layoutSystem: 'pinSystem' },
    },
  ],
  elementMetadata: {},
}
const padded = { padding: 8 }
const paddedH = { paddingLeft: 8, paddingRight: 8 }
const height = { height: 31 }
export var App = (props) => {
  return (
    <div
      style={{
        ...(props.style || {}),
        backgroundColor: '#FFFFFF',
        height: 314,
        left: 0,
        top: 0,
        flexDirection: 'column',
        alignItems: 'stretch',
        right: 0,
      }}
      layout={{ layoutSystem: 'flex' }}
      css={{ fontFamily: 'Inter', fontSize: 11 }}
      data-uid={'aaa'}
    >
      <div
        style={{ display: 'flex', ...padded, ...height, justifyContent: 'space-between' }}
        data-uid={'54f'}
        layout={{ layoutSystem: 'flex', flexBasis: 31, crossBasis: 375 }}
        data-label={"FLEX BUT YOU WOULDN'T KNOW"}
      >
        <span
          style={{ ...paddedH }}
          data-uid={'6b9'}
          layout={{ flexBasis: 51.859375, crossBasis: 15 }}
        >
          Output
        </span>
        <span
          style={{ fontStyle: 'normal', fontWeight: 500 }}
          data-uid={'715'}
          layout={{ flexBasis: 36.5, crossBasis: 15 }}
        >
          Output
        </span>
        <span style={{}} data-uid={'109'} layout={{ flexBasis: 35.859375, crossBasis: 15 }}>
          Output
        </span>
      </div>
    </div>
  )
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.