Fork the repository into your own account first. See the GitHub Help article for instructions.
After you have successfully forked the repository, clone it locally.
git clone https://github.com/transloadit/uppy.git
cd uppy
We are using Corepack to manage version of Yarn. Corepack comes pre-installed with Node.js >=16.x, or can be installed through npm
. You can run corepack enable
to install a yarn
executable in your $PATH
, or prefix all yarn commands with corepack yarn
.
corepack -v || npm i -g corepack
yarn -v || corepack enable
yarn install || corepack yarn install
Our website’s examples section is also our playground, please read the Local Previews section to get up and running.
- If we are
require()
ing a file from the same subpackage, we can freely use relative imports as long as the required file is under thesrc
directory (for example to import@uppy/dashboard/src/utils/hi.js
from@uppy/dashboard/src/index.js
, userequire('./utils/hi.js')
). - But if we want to
require()
some file from another subpackage - we should use global @uppy requires, and they should always be in the form of@uppy/:packageName/(lib instead of src)/(same path).js
Unit tests are using Jest and can be run with:
yarn run test:unit
For end-to-end tests, we use Webdriverio. For it to run locally, you need to install a Selenium standalone server. Follow the Webdriverio guide to do so. You can also install a Selenium standalone server from NPM:
npm install selenium-standalone -g
selenium-standalone install
And then launch it:
selenium-standalone start
After you have installed and launched the selenium standalone server, run:
yarn run test:endtoend:local
By default, test:endtoend:local
uses Firefox. You can use a different browser, like Chrome, by passing the -b
flag:
yarn run test:endtoend:local -- -b chrome
Note: The
--
is important, it tells yarn that the remaining arguments should be interpreted by the script itself, not by yarn.
You can run in several browsers by passing several -b
flags:
yarn run test:endtoend:local -- -b chrome -b firefox
When trying to get a specific integration test to pass, it’s not that helpful to continuously run all tests. You can use the --suite
flag to run tests from a single ./test/endtoend
folder. For example, --suite thumbnails
will only run the tests from ./test/endtoend/thumbnails
. It can also be used in conjunction with one or more -b
flags.
yarn run test:endtoend:local -- -b chrome --suite thumbnails
These tests are also run automatically on Travis builds with SauceLabs cloud service using different OSes.
Even though facebook allows using http://localhost in dev mode, Instagram doesn’t seem to support that, and seems to need a publically available domain name with HTTPS.
Make sure that you are using a development facebook app at https://developers.facebook.com/apps
Go to “Instagram Basic Display” and find Instagram App ID
and Instagram App Secret
. Put them in a file called env.sh
in the repo root:
export COMPANION_INSTAGRAM_KEY="Instagram App ID"
export COMPANION_INSTAGRAM_SECRET="Instagram App Secret"
Run
ngrok http 3020
Note the ngrok https base URL, for example https://e0c7de09808d.ngrok.io
and
append /instagram/redirect
to it, such as:
https://e0c7de09808d.ngrok.io/instagram/redirect
Add this full ngrok URL to Valid OAuth Redirect URIs
under Instagram Basic Display
.
Edit bin/companion
and change to your ngrok URI:
COMPANION_DOMAIN="e0c7de09808d.ngrok.io"
COMPANION_PROTOCOL="https"
Edit examples/dev/Dashboard.js
:
const COMPANION_URL = 'https://e0c7de09808d.ngrok.io'
Go to: Roles -> Roles -> Add Instagram testers -> Add your instagram account
Go to your instagram account at https://www.instagram.com/accounts/manage_access/
Tester invites -> Accept
Now you should be able to test the Instagram integration.
Before doing a release, check that the examples on the website work:
yarn start
open http://localhost:4000/examples/dashboard
Also check the other examples:
cd examples/EXAMPLENAME
yarn install
yarn start
Releases are managed by Lerna. We do some cleanup and compile work around releases too. Use the npm release script:
yarn run release
If you have two-factor authentication enabled on your account, Lerna will ask for a one-time password. You may stumble upon a known issue with the CLI where the OTP prompt may be obscured by a publishing progress bar. If Lerna appears to freeze as it starts publishing, chances are it’s waiting for the password. Try typing in your OTP and hitting enter.
Other things to keep in mind during release:
- When doing a major release >= 1.0, of the
@uppy/core
package, thepeerDependency
of the plugin packages needs to be updated first. Eg when updating from 1.y.z to 2.0.0, the peerDependency of each should be"@uppy/core": "^2.0.0"
before doingnpm run release
. - When adding a new package, add the following key to its package.json:
Else, the release script will try and fail to publish a private package, because the
"publishConfig": { "access": "public" }
@uppy
scope on npm does not support that.
After a release, the demos on transloadit.com should also be updated. After updating, check that some things work locally:
- the demos in the demo section work (try one that uses an import robot, and one that you need to upload to)
- the demos on the homepage work and can import from Google Drive, Instagram, Dropbox, etc.
If you don’t have access to the transloadit.com source code ping @arturi or @goto-bus-stop and we’ll pick it up. ✨
We keep the uppy.io website in ./website
to keep docs and code in sync as we are still iterating at high velocity.
The site is built with Hexo, and Travis automatically deploys this onto GitHub Pages (it overwrites the gh-pages
branch with Hexo’s build at every change to main
). The content is written in Markdown and located in ./website/src
. Feel free to fork & hack!
Even though bundled in this repo, the website is regarded as a separate project. As such, it has its own package.json
and we aim to keep the surface where the two projects interface as small as possible. ./website/update.js
is called during website builds to inject the Uppy knowledge into the site.
yarn install
yarn start
- Go to http://localhost:4000. Your changes in
/website
and/packages/@uppy
will be watched, your browser will refresh as files change.
Then, to work on, for instance, the XHRUpload example, you would edit the following files:
${EDITOR} packages/@uppy/core/src/index.js \
packages/@uppy/core/src/Plugin.js \
packages/@uppy/xhr-upload/src/index.js \
website/src/examples/xhrupload/app.es6
And open http://localhost:4000/examples/xhrupload/ in your web browser.
The CSS standards followed in this project closely resemble those from Medium’s CSS Guidelines. If something is not mentioned here, follow their guidelines.
This project uses naming conventions adopted from the SUIT CSS framework. Read about them here.
To quickly summarize:
Syntax: u-[sm-|md-|lg-]<utilityName>
.u-utilityName
.u-floatLeft
.u-lg-col6
Syntax: [<namespace>-]<ComponentName>[-descendentName][--modifierName]
.twt-Button /* Namespaced component */
.MyComponent /* Components pascal cased */
.Button--default /* Modified button style */
.Button--large
.Tweet
.Tweet-header /* Descendents */
.Tweet-bodyText
.Accordion.is-collapsed /* State of component */
.Accordion.is-expanded
This project uses SASS, with some limitations on nesting. One-level-deep nesting is allowed, but nesting may not extend a selector by using the &
operator. For example:
/* BAD */
.Button {
&--disabled {
...
}
}
/* GOOD */
.Button {
...
}
.Button--disabled {
...
}
Style to the mobile breakpoint with your selectors, then use min-width
media queries to add any styles to the tablet or desktop breakpoints.
- All selectors are sorted alphabetically and by type.
- HTML elements go above classes and IDs in a file.
- Rules are sorted alphabetically.
/* BAD */
.wrapper {
width: 940px;
margin: auto;
}
h1 {
color: red;
}
.article {
width: 100%;
padding: 32px;
}
/* GOOD */
h1 {
color: red;
}
.article {
padding: 32px;
width: 100%;
}
.wrapper {
margin: auto;
width: 940px;
}
Before opening a pull request for the new integration, open an issue to discuss said integration with the Uppy team. After discussing the integration, you can get started on it. First off, you need to construct the basic components for your integration. The following components are the current standard:
Dashboard
: Inline Dashboard (inline: true
)DashboardModal
: Dashboard as a modalDragDrop
ProgressBar
StatusBar
All these components should function as references to the normal component. Depending on how the framework you’re using handles references to the DOM, your approach to creating these may be different. For example, in React, you can assign a property of the component to the reference of a component (see here). This may differ in your framework, but from what we’ve found, the concepts are generally pretty similar.
If you’re familiar with React, Vue or soon Svelte, it might be useful to read through the code of those integrations, as they lay out a pretty good structure. After the basic components have been built, here are a few more important tasks to get done:
- Add TypeScript support in some capacity (if possible)
- Write documentation
- Add an example
- Configuring the build system
Before going into these tasks, here are a few common gotchas that you should be aware of.
Your package.json
should resemble something like this:
{
"name": "@uppy/framework",
"dependencies": {
"@uppy/dashboard": "workspace:*",
"@uppy/drag-drop": "workspace:*",
"@uppy/progress-bar": "workspace:*",
"@uppy/status-bar": "workspace:*",
"@uppy/utils": "workspace:*",
"prop-types": "^15.6.1"
},
"peerDependencies": {
"@uppy/core": "^2.0.0"
},
"publishConfig": {
"access": "public"
}
}
The most important part about this is that @uppy/core
is a peer dependency. If your framework complains about @uppy/core
not being resolved, you can also add it as a dev dependency
This section won’t be too in-depth, because TypeScript depends on your framework. As general advice, prefer using d.ts
files and vanilla JavaScript over TypeScript files. This is circumstantial, but it makes handling the build system a lot easier when TypeScript doesn’t have to transpiled. The version of typescript in the monorepo is 4.1
.
Generally, documentation for integrations can be broken down into a few pieces that apply to every component, and then documentation for each component. The structure should look something like this:
- Installation
- Initializing Uppy (may vary depending on how the framework handles reactivity)
- Usage
- For each component
- Loading CSS
- Props
It may be easier to copy the documentation of earlier integrations and change the parts that need to be changed rather than writing this from scratch. Preferably, keep the documentation to one page. For the front-matter, write something like:
title: Framework Name
type: docs
module: "@uppy/framework"
order: 0
category: "Other Integrations"
This data is used to generate Uppy’s website. Refer to the section about running the website locally if you’d like to see how the docs look on the website.
You can likely use whatever code generation tool for your framework (ex. create-react-app
) to create this example. Make sure you add the same version of @uppy/core
to this as your peer dependency required, or you may run into strange issues. Try to include all the components are some of their functionality. The React example is a great... well example of how to do this well.
The biggest part of this is understanding Uppy’s build system. The high level description is that babel
goes through almost all the packages and transpiles all the Javascript files in the src
directory to more compatible JavaScript in the lib
folder. If you’re using vanilla JavaScript for your integration (like React and Vue do), then you can use this build system and use the files generated as your entry points.
If you’re using some kind of more abstract file format (like Svelte), then you probably want do to a few things: add the directory name to this IGNORE
regex; add all your build dependencies to the root package.json
(try to keep this small); add a new build:framework
script to the root package.json
. This script usually looks something like this:
{
"scripts": {
"build:framework": "cd framework && yarn run build"
}
}
Then, add this script to the build:js
script. Try running the build:js
script and make sure it does not error. It may also be of use to make sure that global dependencies aren’t being used (ex. not having rollup locally and relying on a global install), as these dependencies won’t be present on the machine’s handling building.