Merge pull request #3825 from LLK/release/2020-04-23

[Master] Release/2020 04 23
This commit is contained in:
Benjamin Wheeler 2020-04-23 16:04:13 -04:00 committed by GitHub
commit 67f4abf832
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 791 additions and 663 deletions

950
package-lock.json generated
View file

@ -4,475 +4,6 @@
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@babel/cli": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.8.4.tgz",
"integrity": "sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==",
"dev": true,
"requires": {
"chokidar": "^2.1.8",
"commander": "^4.0.1",
"convert-source-map": "^1.1.0",
"fs-readdir-recursive": "^1.1.0",
"glob": "^7.0.0",
"lodash": "^4.17.13",
"make-dir": "^2.1.0",
"slash": "^2.0.0",
"source-map": "^0.5.0"
},
"dependencies": {
"anymatch": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
"dev": true,
"optional": true,
"requires": {
"micromatch": "^3.1.4",
"normalize-path": "^2.1.1"
},
"dependencies": {
"normalize-path": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"dev": true,
"optional": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
}
}
},
"arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
"integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
"dev": true,
"optional": true
},
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true,
"optional": true
},
"braces": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
"dev": true,
"optional": true,
"requires": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
"extend-shallow": "^2.0.1",
"fill-range": "^4.0.0",
"isobject": "^3.0.1",
"repeat-element": "^1.1.2",
"snapdragon": "^0.8.1",
"snapdragon-node": "^2.0.1",
"split-string": "^3.0.2",
"to-regex": "^3.0.1"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"chokidar": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
"dev": true,
"optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
}
},
"commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"optional": true,
"requires": {
"ms": "2.0.0"
}
},
"expand-brackets": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
"integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
"dev": true,
"optional": true,
"requires": {
"debug": "^2.3.3",
"define-property": "^0.2.5",
"extend-shallow": "^2.0.1",
"posix-character-classes": "^0.1.0",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.1"
},
"dependencies": {
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
"integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
"dev": true,
"optional": true,
"requires": {
"is-descriptor": "^0.1.0"
}
},
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
},
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
"integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"is-data-descriptor": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
"integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"is-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
"integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
"dev": true,
"optional": true,
"requires": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
"kind-of": "^5.0.0"
}
},
"kind-of": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
"dev": true,
"optional": true
}
}
},
"extglob": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
"integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
"dev": true,
"optional": true,
"requires": {
"array-unique": "^0.3.2",
"define-property": "^1.0.0",
"expand-brackets": "^2.1.4",
"extend-shallow": "^2.0.1",
"fragment-cache": "^0.2.1",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.1"
},
"dependencies": {
"define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
"integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
"dev": true,
"optional": true,
"requires": {
"is-descriptor": "^1.0.0"
}
},
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
"integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
"dev": true,
"optional": true,
"requires": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
"repeat-string": "^1.6.1",
"to-regex-range": "^2.1.0"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
"dev": true,
"optional": true,
"requires": {
"is-glob": "^3.1.0",
"path-dirname": "^1.0.0"
},
"dependencies": {
"is-glob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
"dev": true,
"optional": true,
"requires": {
"is-extglob": "^2.1.0"
}
}
}
},
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^6.0.0"
}
},
"is-data-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^6.0.0"
}
},
"is-descriptor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
"dev": true,
"optional": true,
"requires": {
"is-accessor-descriptor": "^1.0.0",
"is-data-descriptor": "^1.0.0",
"kind-of": "^6.0.2"
}
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true,
"optional": true
},
"is-glob": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"dev": true,
"optional": true,
"requires": {
"is-extglob": "^2.1.1"
}
},
"is-number": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
"integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"isobject": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true,
"optional": true
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true,
"optional": true
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
"integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
"dev": true,
"optional": true,
"requires": {
"arr-diff": "^4.0.0",
"array-unique": "^0.3.2",
"braces": "^2.3.1",
"define-property": "^2.0.2",
"extend-shallow": "^3.0.2",
"extglob": "^2.0.4",
"fragment-cache": "^0.2.1",
"kind-of": "^6.0.2",
"nanomatch": "^1.2.9",
"object.pick": "^1.3.0",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.2"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true,
"optional": true
},
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"optional": true
},
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
}
}
},
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
@ -16257,21 +15788,490 @@
} }
}, },
"scratch-gui": { "scratch-gui": {
"version": "0.1.0-prerelease.20200415195503", "version": "0.1.0-prerelease.20200423001702",
"resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20200415195503.tgz", "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20200423001702.tgz",
"integrity": "sha512-QYZGxVmQj8QDbmgNvVtF7zAXi+QB+skK/wCnG/9o+ivFiZBAGpMh8hSqVvJe5lAen3ptVQ7yjJ1dEm1urjk7kw==", "integrity": "sha512-xLqqRRhdQCHj2fQ4OjmUeHcMzztgiVz6zbSYR9be7Bl9Gk+7yuo5XWOiqKqfsloAb0dco1og15zVQKVmLQ/A9A==",
"dev": true "dev": true
}, },
"scratch-l10n": { "scratch-l10n": {
"version": "3.8.20200414213430", "version": "3.8.20200421213407",
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.8.20200414213430.tgz", "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.8.20200421213407.tgz",
"integrity": "sha512-+oLlx3wIWWubx1yGh4nMWSUzP9y3nArGXrMZf9x1oCO2b3k835oMfuhUZSZ7vi8gsruhw1iNQp+H1HkM9cKi+A==", "integrity": "sha512-cuFbyCX4v5oId7HJ5Ojm5dQwqW5sfZ7Bmo6xkNIOCwOMcIeo0JJpBbbZ8XtdodLjEDg37/weBWfYAxYIUphGIA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/cli": "^7.1.2", "@babel/cli": "^7.1.2",
"@babel/core": "^7.1.2", "@babel/core": "^7.1.2",
"babel-plugin-react-intl": "^3.0.1", "babel-plugin-react-intl": "^3.0.1",
"transifex": "1.6.6" "transifex": "1.6.6"
},
"dependencies": {
"@babel/cli": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.8.4.tgz",
"integrity": "sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==",
"dev": true,
"requires": {
"chokidar": "^2.1.8",
"commander": "^4.0.1",
"convert-source-map": "^1.1.0",
"fs-readdir-recursive": "^1.1.0",
"glob": "^7.0.0",
"lodash": "^4.17.13",
"make-dir": "^2.1.0",
"slash": "^2.0.0",
"source-map": "^0.5.0"
}
},
"anymatch": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
"dev": true,
"optional": true,
"requires": {
"micromatch": "^3.1.4",
"normalize-path": "^2.1.1"
},
"dependencies": {
"normalize-path": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"dev": true,
"optional": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
}
}
},
"arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
"integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
"dev": true,
"optional": true
},
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true,
"optional": true
},
"braces": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
"dev": true,
"optional": true,
"requires": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
"extend-shallow": "^2.0.1",
"fill-range": "^4.0.0",
"isobject": "^3.0.1",
"repeat-element": "^1.1.2",
"snapdragon": "^0.8.1",
"snapdragon-node": "^2.0.1",
"split-string": "^3.0.2",
"to-regex": "^3.0.1"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"chokidar": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
"dev": true,
"optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
}
},
"commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"optional": true,
"requires": {
"ms": "2.0.0"
}
},
"expand-brackets": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
"integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
"dev": true,
"optional": true,
"requires": {
"debug": "^2.3.3",
"define-property": "^0.2.5",
"extend-shallow": "^2.0.1",
"posix-character-classes": "^0.1.0",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.1"
},
"dependencies": {
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
"integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
"dev": true,
"optional": true,
"requires": {
"is-descriptor": "^0.1.0"
}
},
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
},
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
"integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"is-data-descriptor": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
"integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"is-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
"integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
"dev": true,
"optional": true,
"requires": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
"kind-of": "^5.0.0"
}
},
"kind-of": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
"dev": true,
"optional": true
}
}
},
"extglob": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
"integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
"dev": true,
"optional": true,
"requires": {
"array-unique": "^0.3.2",
"define-property": "^1.0.0",
"expand-brackets": "^2.1.4",
"extend-shallow": "^2.0.1",
"fragment-cache": "^0.2.1",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.1"
},
"dependencies": {
"define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
"integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
"dev": true,
"optional": true,
"requires": {
"is-descriptor": "^1.0.0"
}
},
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
"integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
"dev": true,
"optional": true,
"requires": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
"repeat-string": "^1.6.1",
"to-regex-range": "^2.1.0"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
"dev": true,
"optional": true,
"requires": {
"is-glob": "^3.1.0",
"path-dirname": "^1.0.0"
},
"dependencies": {
"is-glob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
"dev": true,
"optional": true,
"requires": {
"is-extglob": "^2.1.0"
}
}
}
},
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^6.0.0"
}
},
"is-data-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^6.0.0"
}
},
"is-descriptor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
"dev": true,
"optional": true,
"requires": {
"is-accessor-descriptor": "^1.0.0",
"is-data-descriptor": "^1.0.0",
"kind-of": "^6.0.2"
}
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true,
"optional": true
},
"is-glob": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"dev": true,
"optional": true,
"requires": {
"is-extglob": "^2.1.1"
}
},
"is-number": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
"integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
"dev": true,
"optional": true,
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"isobject": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true,
"optional": true
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true,
"optional": true
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
"integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
"dev": true,
"optional": true,
"requires": {
"arr-diff": "^4.0.0",
"array-unique": "^0.3.2",
"braces": "^2.3.1",
"define-property": "^2.0.2",
"extend-shallow": "^3.0.2",
"extglob": "^2.0.4",
"fragment-cache": "^0.2.1",
"kind-of": "^6.0.2",
"nanomatch": "^1.2.9",
"object.pick": "^1.3.0",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.2"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true,
"optional": true
},
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"optional": true
},
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
}
} }
}, },
"scratch-parser": { "scratch-parser": {

View file

@ -128,7 +128,7 @@
"redux-mock-store": "^1.2.3", "redux-mock-store": "^1.2.3",
"redux-thunk": "2.0.1", "redux-thunk": "2.0.1",
"sass-loader": "6.0.6", "sass-loader": "6.0.6",
"scratch-gui": "0.1.0-prerelease.20200415195503", "scratch-gui": "0.1.0-prerelease.20200423001702",
"scratch-l10n": "latest", "scratch-l10n": "latest",
"selenium-webdriver": "3.6.0", "selenium-webdriver": "3.6.0",
"slick-carousel": "1.6.0", "slick-carousel": "1.6.0",

View file

@ -4,8 +4,6 @@ const React = require('react');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');
const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js');
require('./extension-landing.scss'); require('./extension-landing.scss');
const ExtensionRequirements = props => ( const ExtensionRequirements = props => (
@ -30,7 +28,6 @@ const ExtensionRequirements = props => (
/> />
macOS 10.13+ macOS 10.13+
</span> </span>
{CHROME_APP_RELEASED && (
<React.Fragment> <React.Fragment>
<span> <span>
<img <img
@ -47,7 +44,6 @@ const ExtensionRequirements = props => (
Android 6.0+ Android 6.0+
</span> </span>
</React.Fragment> </React.Fragment>
)}
<span> <span>
<img src="/svgs/extensions/bluetooth.svg" /> <img src="/svgs/extensions/bluetooth.svg" />
Bluetooth Bluetooth

View file

@ -11,19 +11,45 @@ class InfoButton extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
bindAll(this, [ bindAll(this, [
'handleHideMessage', 'handleClick',
'handleShowMessage' 'handleMouseLeave',
'handleShowMessage',
'setButtonRef'
]); ]);
this.state = { this.state = {
requireClickToClose: false, // default to closing on mouseout
visible: false visible: false
}; };
} }
handleHideMessage () { componentWillMount () {
window.addEventListener('mousedown', this.handleClick, false);
}
componentWillUnmount () {
window.removeEventListener('mousedown', this.handleClick, false);
}
handleClick (e) {
if (this.buttonRef) { // only handle click if we can tell whether it happened in this component
let newVisibleState = false; // for most clicks, hide the info message
if (this.buttonRef.contains(e.target)) { // if the click was inside the info icon...
newVisibleState = !this.state.requireClickToClose; // toggle it
}
this.setState({
requireClickToClose: newVisibleState,
visible: newVisibleState
});
}
}
handleMouseLeave () {
if (this.state.visible && !this.state.requireClickToClose) {
this.setState({visible: false}); this.setState({visible: false});
} }
}
handleShowMessage () { handleShowMessage () {
this.setState({visible: true}); this.setState({visible: true});
} }
setButtonRef (element) {
this.buttonRef = element;
}
render () { render () {
const messageJsx = this.state.visible && ( const messageJsx = this.state.visible && (
<div className="info-button-message"> <div className="info-button-message">
@ -34,8 +60,8 @@ class InfoButton extends React.Component {
<React.Fragment> <React.Fragment>
<div <div
className="info-button" className="info-button"
onClick={this.handleShowMessage} ref={this.setButtonRef}
onMouseOut={this.handleHideMessage} onMouseLeave={this.handleMouseLeave}
onMouseOver={this.handleShowMessage} onMouseOver={this.handleShowMessage}
> >
<MediaQuery minWidth={frameless.desktop}> <MediaQuery minWidth={frameless.desktop}>

View file

@ -4,23 +4,21 @@
.info-button { .info-button {
position: relative; position: relative;
display: inline-block; display: inline-block;
width: 1rem; width: 2rem;
height: 1rem; height: 2rem;
margin-left: .375rem; margin-left: -.125rem;
margin-top: -.25rem; margin-top: -.75rem;
border-radius: 50%;
background-color: $type-gray-60percent;
background-image: url("/svgs/info-button/info-button.svg"); background-image: url("/svgs/info-button/info-button.svg");
background-size: cover; background-size: cover;
top: .1875rem; top: .6875rem;
} }
.info-button-message { .info-button-message {
$arrow-border-width: 1rem; $arrow-border-width: 1rem;
display: block; display: block;
position: absolute; position: absolute;
top: 0; top: .375rem;
left: 0; left: .5rem;
transform: translate(1rem, -1rem); transform: translate(1rem, -1rem);
width: 16.5rem; width: 16.5rem;
min-height: 1rem; min-height: 1rem;
@ -66,6 +64,7 @@
we need to center this element within its width. */ we need to center this element within its width. */
margin: 0 calc((100% - 16.5rem) / 2);; margin: 0 calc((100% - 16.5rem) / 2);;
top: .125rem; top: .125rem;
left: 0;
&:before { &:before {
display: none; display: none;

View file

@ -3,7 +3,6 @@ const FormattedMessage = require('react-intl').FormattedMessage;
const React = require('react'); const React = require('react');
const OS_ENUM = require('../../lib/os-enum.js'); const OS_ENUM = require('../../lib/os-enum.js');
const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js');
const {isDownloaded, isFromGooglePlay} = require('./install-util.js'); const {isDownloaded, isFromGooglePlay} = require('./install-util.js');
@ -27,24 +26,10 @@ const InstallScratch = ({
<div className="blue install-scratch"> <div className="blue install-scratch">
<FlexRow className="inner column"> <FlexRow className="inner column">
<h2 className="title"> <h2 className="title">
{CHROME_APP_RELEASED ? (
<FormattedMessage <FormattedMessage
id="installScratch.appHeaderTitle" id="installScratch.appHeaderTitle"
values={{operatingsystem: currentOS}} values={{operatingsystem: currentOS}}
/> />
) : (
<React.Fragment>
{isDownloaded(currentOS) && (
<FormattedMessage id="installScratch.desktopHeaderTitle" />
)}
{isFromGooglePlay(currentOS) && (
<FormattedMessage
id="installScratch.appHeaderTitle"
values={{operatingsystem: currentOS}}
/>
)}
</React.Fragment>
)}
</h2> </h2>
<Steps> <Steps>
<div className="step"> <div className="step">
@ -56,14 +41,12 @@ const InstallScratch = ({
<React.Fragment> <React.Fragment>
{currentOS === OS_ENUM.WINDOWS && ( {currentOS === OS_ENUM.WINDOWS && (
<FormattedMessage <FormattedMessage
id={CHROME_APP_RELEASED ? 'installScratch.getScratchAppWindows' : id="installScratch.getScratchAppWindows"
'installScratch.downloadScratchDesktop'}
/> />
)} )}
{currentOS === OS_ENUM.MACOS && ( {currentOS === OS_ENUM.MACOS && (
<FormattedMessage <FormattedMessage
id={CHROME_APP_RELEASED ? 'installScratch.getScratchAppMacOs' : id="installScratch.getScratchAppMacOs"
'installScratch.downloadScratchDesktop'}
/> />
)} )}
{isFromGooglePlay(currentOS) && ( {isFromGooglePlay(currentOS) && (

View file

@ -127,10 +127,11 @@
} }
.join-flow-privacy-message { .join-flow-privacy-message {
margin: 1rem auto; margin: .5rem auto 1rem;
font-size: .75rem; font-size: .75rem;
font-weight: 500; font-weight: 500;
color: $type-gray-60percent; color: $type-gray-60percent;
text-align: center;
} }
.join-flow-inner-username-step { .join-flow-inner-username-step {

View file

@ -4,8 +4,6 @@ const FormattedMessage = require('react-intl').FormattedMessage;
const PropTypes = require('prop-types'); const PropTypes = require('prop-types');
const React = require('react'); const React = require('react');
const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js');
const FlexRow = require('../../components/flex-row/flex-row.jsx'); const FlexRow = require('../../components/flex-row/flex-row.jsx');
const Button = require('../../components/forms/button.jsx'); const Button = require('../../components/forms/button.jsx');
@ -35,8 +33,6 @@ const OSChooser = props => (
<img src="/svgs/extensions/mac.svg" /> <img src="/svgs/extensions/mac.svg" />
macOS macOS
</Button> </Button>
{CHROME_APP_RELEASED && (
<React.Fragment>
<Button <Button
className={classNames({active: props.currentOS === OS_ENUM.CHROMEOS})} className={classNames({active: props.currentOS === OS_ENUM.CHROMEOS})}
onClick={() => // eslint-disable-line react/jsx-no-bind onClick={() => // eslint-disable-line react/jsx-no-bind
@ -55,8 +51,6 @@ const OSChooser = props => (
<img src="/svgs/extensions/android.svg" /> <img src="/svgs/extensions/android.svg" />
Android Android
</Button> </Button>
</React.Fragment>
)}
</FlexRow> </FlexRow>
</div> </div>
); );

View file

@ -28,6 +28,7 @@ const Spinner = require('../../components/spinner/spinner.jsx');
const StepNavigation = require('../../components/stepnavigation/stepnavigation.jsx'); const StepNavigation = require('../../components/stepnavigation/stepnavigation.jsx');
const TextArea = require('../../components/forms/textarea.jsx'); const TextArea = require('../../components/forms/textarea.jsx');
const Tooltip = require('../../components/tooltip/tooltip.jsx'); const Tooltip = require('../../components/tooltip/tooltip.jsx');
const ValidationMessage = require('../../components/forms/validation-message.jsx');
require('./steps.scss'); require('./steps.scss');
@ -84,11 +85,13 @@ class UsernameStep extends React.Component {
'handleChangeShowPassword', 'handleChangeShowPassword',
'handleUsernameBlur', 'handleUsernameBlur',
'handleValidSubmit', 'handleValidSubmit',
'validateUsername' 'validateUsername',
'handleFocus'
]); ]);
this.state = { this.state = {
showPassword: props.showPassword, showPassword: props.showPassword,
waiting: false, waiting: false,
showUsernameTip: true,
validUsername: '' validUsername: ''
}; };
} }
@ -159,6 +162,9 @@ class UsernameStep extends React.Component {
if (isValid) return this.props.onNextStep(formData); if (isValid) return this.props.onNextStep(formData);
}); });
} }
handleFocus () {
this.setState({showUsernameTip: false});
}
render () { render () {
return ( return (
<Slide className="registration-step username-step"> <Slide className="registration-step username-step">
@ -205,6 +211,13 @@ class UsernameStep extends React.Component {
null null
)} )}
</label> </label>
{ this.state.showUsernameTip &&
<ValidationMessage
className={'validation-full-width-input'}
message={this.props.intl.formatMessage({id: 'registration.usernameAdviceShort'})}
mode="info"
/>
}
<Input <Input
required required
className={this.state.validUsername} className={this.state.validUsername}
@ -227,6 +240,7 @@ class UsernameStep extends React.Component {
maxLength: 20 maxLength: 20
}} }}
onBlur={this.handleUsernameBlur} onBlur={this.handleUsernameBlur}
onFocus={this.handleFocus}
/> />
</div> </div>
<Input <Input
@ -253,6 +267,7 @@ class UsernameStep extends React.Component {
notEqualsField: 'user.username' notEqualsField: 'user.username'
}} }}
/> />
<Checkbox <Checkbox
help={null} help={null}
name="showPassword" name="showPassword"

View file

@ -57,6 +57,10 @@
.username-label { .username-label {
margin-bottom: .75rem; margin-bottom: .75rem;
} }
.validation-info {
top:auto;
left:auto;
}
} }
&.demographics-step { &.demographics-step {

View file

@ -141,10 +141,7 @@
"installScratch.or": "or", "installScratch.or": "or",
"installScratch.directDownload": "Direct download", "installScratch.directDownload": "Direct download",
"installScratch.desktopHeaderTitle": "Install Scratch Desktop",
"installScratch.appHeaderTitle": "Install the Scratch app for {operatingsystem}", "installScratch.appHeaderTitle": "Install the Scratch app for {operatingsystem}",
"installScratch.downloadScratchDesktop": "Download Scratch Desktop",
"installScratch.downloadScratchAppGeneric": "Download Scratch for {operatingsystem}",
"installScratch.getScratchAppPlay": "Get the Scratch app on the Google Play Store", "installScratch.getScratchAppPlay": "Get the Scratch app on the Google Play Store",
"installScratch.getScratchAppMacOs": "Get the Scratch app on the Mac App Store", "installScratch.getScratchAppMacOs": "Get the Scratch app on the Mac App Store",
"installScratch.getScratchAppWindows": "Get the Scratch app on the Microsoft Store", "installScratch.getScratchAppWindows": "Get the Scratch app on the Microsoft Store",
@ -218,7 +215,7 @@
"registration.studentUsernameStepDescription": "You can make games, animations, and stories using Scratch. Setting up an account is easy and it's free. Fill in the form below to get started.", "registration.studentUsernameStepDescription": "You can make games, animations, and stories using Scratch. Setting up an account is easy and it's free. Fill in the form below to get started.",
"registration.studentUsernameStepHelpText": "Already have a Scratch account?", "registration.studentUsernameStepHelpText": "Already have a Scratch account?",
"registration.studentUsernameStepTooltip": "You'll need to create a new Scratch account to join this class.", "registration.studentUsernameStepTooltip": "You'll need to create a new Scratch account to join this class.",
"registration.studentUsernameFieldHelpText": "For safety, don't use your real name!", "registration.studentUsernameSuggestion": "Try your favorite food, hobby, or animal along with some numbers",
"registration.acceptTermsOfUse": "By creating an account, you acknowledge the {privacyPolicyLink} and you accept and agree to the {touLink}.", "registration.acceptTermsOfUse": "By creating an account, you acknowledge the {privacyPolicyLink} and you accept and agree to the {touLink}.",
"registration.usernameStepTitle": "Request a Teacher Account", "registration.usernameStepTitle": "Request a Teacher Account",
"registration.usernameStepTitleScratcher": "Create a Scratch Account", "registration.usernameStepTitleScratcher": "Create a Scratch Account",

View file

@ -1,6 +1,5 @@
import bowser from 'bowser'; import bowser from 'bowser';
import OS_ENUM from './os-enum.js'; import OS_ENUM from './os-enum.js';
import {CHROME_APP_RELEASED} from './feature-flags.js';
/** /**
* Helper function to the current Operating System. * Helper function to the current Operating System.
@ -9,10 +8,8 @@ import {CHROME_APP_RELEASED} from './feature-flags.js';
export default function () { export default function () {
// matching OS strings from https://github.com/lancedikson/bowser/blob/master/src/constants.js // matching OS strings from https://github.com/lancedikson/bowser/blob/master/src/constants.js
if (bowser.osname === 'macOS') return OS_ENUM.MACOS; if (bowser.osname === 'macOS') return OS_ENUM.MACOS;
if (CHROME_APP_RELEASED) {
if (bowser.osname === 'Chrome OS') return OS_ENUM.CHROMEOS; if (bowser.osname === 'Chrome OS') return OS_ENUM.CHROMEOS;
if (bowser.osname === 'Android') return OS_ENUM.ANDROID; if (bowser.osname === 'Android') return OS_ENUM.ANDROID;
}
// if (bowser.osname === 'iOS') return OS_ENUM.IOS; // @todo // if (bowser.osname === 'iOS') return OS_ENUM.IOS; // @todo
return OS_ENUM.WINDOWS; return OS_ENUM.WINDOWS;
} }

View file

@ -8,6 +8,5 @@ const flagInUrl = flag => {
}; };
module.exports = { module.exports = {
CHROME_APP_RELEASED: true,
CONTACT_US_POPUP: isStaging() && flagInUrl('CONTACT_US_POPUP') CONTACT_US_POPUP: isStaging() && flagInUrl('CONTACT_US_POPUP')
}; };

View file

@ -10,7 +10,6 @@ const Page = require('../../components/page/www/page.jsx');
const render = require('../../lib/render.jsx'); const render = require('../../lib/render.jsx');
const detectOS = require('../../lib/detect-os.js').default; const detectOS = require('../../lib/detect-os.js').default;
const OS_ENUM = require('../../lib/os-enum.js'); const OS_ENUM = require('../../lib/os-enum.js');
const {CHROME_APP_RELEASED} = require('../../lib/feature-flags.js');
const OSChooser = require('../../components/os-chooser/os-chooser.jsx'); const OSChooser = require('../../components/os-chooser/os-chooser.jsx');
const InstallScratch = require('../../components/install-scratch/install-scratch.jsx'); const InstallScratch = require('../../components/install-scratch/install-scratch.jsx');
const {isDownloaded, isFromGooglePlay} = require('../../components/install-scratch/install-util.js'); const {isDownloaded, isFromGooglePlay} = require('../../components/install-scratch/install-util.js');
@ -52,14 +51,12 @@ class Download extends React.Component {
width="40" width="40"
/> />
<FormattedMessage <FormattedMessage
id={CHROME_APP_RELEASED ? 'download.appTitle' : id="download.appTitle"
'download.title'}
/> />
</h1> </h1>
<span className="download-description"> <span className="download-description">
<FormattedMessage <FormattedMessage
id={CHROME_APP_RELEASED ? 'download.appIntro' : id="download.appIntro"
'download.intro'}
/> />
</span> </span>
</FlexRow> </FlexRow>
@ -82,8 +79,6 @@ class Download extends React.Component {
/> />
macOS 10.13+ macOS 10.13+
</span> </span>
{CHROME_APP_RELEASED && (
<React.Fragment>
<span> <span>
<img <img
alt="" alt=""
@ -98,8 +93,6 @@ class Download extends React.Component {
/> />
Android 6.0+ Android 6.0+
</span> </span>
</React.Fragment>
)}
</FlexRow> </FlexRow>
</FlexRow> </FlexRow>
</FlexRow> </FlexRow>

View file

@ -1,6 +1,4 @@
{ {
"download.title": "Scratch Desktop",
"download.intro": "You can install the Scratch Desktop editor to work on projects without an internet connection. This version will work on Windows and MacOS.",
"download.appTitle": "Download the Scratch App", "download.appTitle": "Download the Scratch App",
"download.appIntro": "Would you like to create and save Scratch projects without an internet connection? Download the free Scratch app.", "download.appIntro": "Would you like to create and save Scratch projects without an internet connection? Download the free Scratch app.",
"download.requirements": "Requirements", "download.requirements": "Requirements",
@ -23,7 +21,7 @@
"download.macMoveToApplications" : "Open the .dmg file. Move Scratch Desktop into Applications.", "download.macMoveToApplications" : "Open the .dmg file. Move Scratch Desktop into Applications.",
"download.winMoveToApplications" : "Run the .exe file.", "download.winMoveToApplications" : "Run the .exe file.",
"download.doIHaveToDownload" : "Do I have to download an app to use Scratch?", "download.doIHaveToDownload" : "Do I have to download an app to use Scratch?",
"download.doIHaveToDownloadAnswer" : "No. You can also use the Scratch project editor in any web browser on any device by going to scratch.mit.edu and clicking \"Create\".", "download.doIHaveToDownloadAnswer" : "No. You can also use the Scratch project editor in most web browsers on most devices by going to scratch.mit.edu and clicking \"Create\".",
"download.canIUseScratchLink" : "Can I use Scratch Link to connect to extensions?", "download.canIUseScratchLink" : "Can I use Scratch Link to connect to extensions?",
"download.canIUseScratchLinkAnswer" : "Yes. However, you will need an Internet connection to use Scratch Link.", "download.canIUseScratchLinkAnswer" : "Yes. However, you will need an Internet connection to use Scratch Link.",
"download.canIUseExtensions" : "Can I connect to hardware extensions?", "download.canIUseExtensions" : "Can I connect to hardware extensions?",

View file

@ -0,0 +1,43 @@
const classNames = require('classnames');
const FormattedMessage = require('react-intl').FormattedMessage;
const PropTypes = require('prop-types');
const React = require('react');
const SocialMessage = require('../../../components/social-message/social-message.jsx');
const FollowStudioMessage = props => (
<SocialMessage
as="div"
className={classNames(
'mod-follow-studio',
props.className
)}
datetime={props.followDateTime}
>
<FormattedMessage
id="messages.followStudioText"
values={{
profileLink: (
<a href={`/users/${props.followerUsername}/`}>
{props.followerUsername}
</a>
),
studioLink: (
<a href={`/studios/${props.studioId}`}>
{props.studioTitle}
</a>
)
}}
/>
</SocialMessage>
);
FollowStudioMessage.propTypes = {
className: PropTypes.string,
followDateTime: PropTypes.string.isRequired,
followerUsername: PropTypes.string.isRequired,
studioId: PropTypes.string.isRequired,
studioTitle: PropTypes.string.isRequired
};
module.exports = FollowStudioMessage;

View file

@ -0,0 +1,42 @@
const classNames = require('classnames');
const FormattedMessage = require('react-intl').FormattedMessage;
const PropTypes = require('prop-types');
const React = require('react');
const SocialMessage = require('../../../components/social-message/social-message.jsx');
const FollowUserMessage = props => (
<SocialMessage
as="div"
className={classNames(
'mod-follow-user',
props.className
)}
datetime={props.followDateTime}
>
<FormattedMessage
id="messages.followProfileText"
values={{
profileLink: (
<a href={`/users/${props.followerUsername}/`}>
{props.followerUsername}
</a>
),
followeeLink: (
<a href={`/users/${props.followeeId}/`}>
{props.followeeId}
</a>
)
}}
/>
</SocialMessage>
);
FollowUserMessage.propTypes = {
className: PropTypes.string,
followDateTime: PropTypes.string.isRequired,
followeeId: PropTypes.string.isRequired,
followerUsername: PropTypes.string.isRequired
};
module.exports = FollowUserMessage;

View file

@ -1,55 +0,0 @@
const classNames = require('classnames');
const FormattedMessage = require('react-intl').FormattedMessage;
const PropTypes = require('prop-types');
const React = require('react');
const SocialMessage = require('../../../components/social-message/social-message.jsx');
const FollowMessage = props => {
let followeeLink = '';
let followeeTitle = '';
if (typeof props.followeeTitle === 'undefined') {
followeeLink = `/users/${props.followeeId}`;
followeeTitle = props.followeeId;
} else {
followeeLink = `/studios/${props.followeeId}`;
followeeTitle = props.followeeTitle;
}
return (
<SocialMessage
as="div"
className={classNames(
'mod-follow-user',
props.className
)}
datetime={props.followDateTime}
>
<FormattedMessage
id="messages.followText"
values={{
profileLink: (
<a href={`/users/${props.followerUsername}/`}>
{props.followerUsername}
</a>
),
followeeLink: (
<a href={followeeLink}>
{followeeTitle}
</a>
)
}}
/>
</SocialMessage>
);
};
FollowMessage.propTypes = {
className: PropTypes.string,
followDateTime: PropTypes.string.isRequired,
followeeId: PropTypes.string.isRequired,
followeeTitle: PropTypes.string,
followerUsername: PropTypes.string.isRequired
};
module.exports = FollowMessage;

View file

@ -13,7 +13,8 @@
"messages.becomeCuratorText": "{username} became a curator of {studio}", "messages.becomeCuratorText": "{username} became a curator of {studio}",
"messages.becomeManagerText": "{username} was promoted to manager of {studio}", "messages.becomeManagerText": "{username} was promoted to manager of {studio}",
"messages.favoriteText": "{profileLink} favorited {projectLink}", "messages.favoriteText": "{profileLink} favorited {projectLink}",
"messages.followText": "{profileLink} is now following {followeeLink}", "messages.followProfileText": "{profileLink} is now following {followeeLink}",
"messages.followStudioText": "{profileLink} is now following {studioLink}",
"messages.loveText": "{profileLink} loved {projectLink}", "messages.loveText": "{profileLink} loved {projectLink}",
"messages.remixText": "{profileLink} remixed {remixedProjectLink} as {projectLink}", "messages.remixText": "{profileLink} remixed {remixedProjectLink} as {projectLink}",
"messages.shareText": "{profileLink} shared the project {projectLink}", "messages.shareText": "{profileLink} shared the project {projectLink}",

View file

@ -26,7 +26,8 @@ const Welcome = require('../../components/welcome/welcome.jsx');
const BecomeCuratorMessage = require('./activity-rows/become-curator.jsx'); const BecomeCuratorMessage = require('./activity-rows/become-curator.jsx');
const BecomeManagerMessage = require('./activity-rows/become-manager.jsx'); const BecomeManagerMessage = require('./activity-rows/become-manager.jsx');
const FavoriteProjectMessage = require('./activity-rows/favorite-project.jsx'); const FavoriteProjectMessage = require('./activity-rows/favorite-project.jsx');
const FollowMessage = require('./activity-rows/follow.jsx'); const FollowUserMessage = require('./activity-rows/follow-user.jsx');
const FollowStudioMessage = require('./activity-rows/follow-studio.jsx');
const LoveProjectMessage = require('./activity-rows/love-project.jsx'); const LoveProjectMessage = require('./activity-rows/love-project.jsx');
const RemixProjectMessage = require('./activity-rows/remix-project.jsx'); const RemixProjectMessage = require('./activity-rows/remix-project.jsx');
const ShareProjectMessage = require('./activity-rows/share-project.jsx'); const ShareProjectMessage = require('./activity-rows/share-project.jsx');
@ -53,7 +54,7 @@ class ActivityList extends React.Component {
switch (message.type) { switch (message.type) {
case 'followuser': case 'followuser':
return ( return (
<FollowMessage <FollowUserMessage
followDateTime={message.datetime_created} followDateTime={message.datetime_created}
followeeId={message.followed_username} followeeId={message.followed_username}
followerUsername={message.actor_username} followerUsername={message.actor_username}
@ -62,12 +63,12 @@ class ActivityList extends React.Component {
); );
case 'followstudio': case 'followstudio':
return ( return (
<FollowMessage <FollowStudioMessage
followDateTime={message.datetime_created} followDateTime={message.datetime_created}
followeeId={message.gallery_id} followeeId={message.gallery_id}
followeeTitle={message.title}
followerUsername={message.actor_username}
key={key} key={key}
studioTitle={message.title}
studioUsername={message.actor_username}
/> />
); );
case 'loveproject': case 'loveproject':

View file

@ -126,7 +126,7 @@ class StudentRegistration extends React.Component {
id: 'registration.studentUsernameStepTooltip' id: 'registration.studentUsernameStepTooltip'
})} })}
usernameHelp={this.props.intl.formatMessage({ usernameHelp={this.props.intl.formatMessage({
id: 'registration.studentUsernameFieldHelpText' id: 'registration.studentUsernameSuggestion'
})} })}
waiting={this.state.waiting} waiting={this.state.waiting}
onNextStep={this.handleAdvanceStep} onNextStep={this.handleAdvanceStep}

View file

@ -1 +1 @@
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><g fill-rule="evenodd"><path d="M10 0a10 10 0 1 0 10 10A10 10 0 0 0 10 0" fill="#9a9eac"/><path d="M10 13.39a1.33 1.33 0 1 1-1.33 1.33A1.33 1.33 0 0 1 10 13.39zm2.68-8.77a3 3 0 0 1 1.42 2.31 3.15 3.15 0 0 1-.95 2.56 8.37 8.37 0 0 1-1.59 1.1c-.7.39-.7.41-.7.77 0 .55 0 1-1 1s-1-.45-1-1a2.65 2.65 0 0 1 1.72-2.52A6.61 6.61 0 0 0 11.79 8a1.22 1.22 0 0 0 .3-.91 1 1 0 0 0-.5-.79 2.8 2.8 0 0 0-2.87.2c-.98.64-.81 1.55-1.72 1.3s-.92-1-.53-1.7a3.94 3.94 0 0 1 1.9-1.66 4.67 4.67 0 0 1 4.3.18z" fill="#fff"/></g></svg> <svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="40" height="40"><g fill-rule="evenodd"><path d="M20 10a10 10 0 1 0 10 10 10 10 0 0 0-10-10" fill="#9a9eac"/><path d="M20 23.39a1.33 1.33 0 1 1-1.33 1.33A1.34 1.34 0 0 1 20 23.39zm2.68-8.77a3 3 0 0 1 1.42 2.31 3.14 3.14 0 0 1-1 2.56 8.2 8.2 0 0 1-1.59 1.1c-.7.39-.7.41-.7.77 0 .55 0 1-1 1s-1-.45-1-1a2.64 2.64 0 0 1 1.72-2.52 6.71 6.71 0 0 0 1.26-.84 1.22 1.22 0 0 0 .3-.91 1 1 0 0 0-.5-.79 2.8 2.8 0 0 0-2.87.2c-1 .64-.81 1.55-1.72 1.3s-.92-1-.53-1.7a3.9 3.9 0 0 1 1.9-1.66 4.67 4.67 0 0 1 4.3.18z" fill="#fff"/></g></svg>

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 589 B

View file

@ -3,6 +3,15 @@ import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
import InfoButton from '../../../src/components/info-button/info-button'; import InfoButton from '../../../src/components/info-button/info-button';
describe('InfoButton', () => { describe('InfoButton', () => {
// mock window.addEventListener
// for more on this technique, see discussion at https://github.com/airbnb/enzyme/issues/426#issuecomment-253515886
const mockedAddEventListener = {};
/* eslint-disable no-undef */
window.addEventListener = jest.fn((event, cb) => {
mockedAddEventListener[event] = cb;
});
/* eslint-enable no-undef */
test('Info button defaults to not visible', () => { test('Info button defaults to not visible', () => {
const component = mountWithIntl( const component = mountWithIntl(
<InfoButton <InfoButton
@ -11,33 +20,118 @@ describe('InfoButton', () => {
); );
expect(component.find('div.info-button-message').exists()).toEqual(false); expect(component.find('div.info-button-message').exists()).toEqual(false);
}); });
test('mouseOver on info button makes info message visible', () => {
test('mouseOver on info button makes info message visible', done => {
const component = mountWithIntl( const component = mountWithIntl(
<InfoButton <InfoButton
message="Here is some info about something!" message="Here is some info about something!"
/> />
); );
// mouseOver info button
component.find('div.info-button').simulate('mouseOver'); component.find('div.info-button').simulate('mouseOver');
setTimeout(function () { // necessary because mouseover uses debounce
// crucial: if we don't call update(), then find() below looks through an OLD
// version of the DOM! see https://github.com/airbnb/enzyme/issues/1233#issuecomment-358915200
component.update();
expect(component.find('div.info-button-message').exists()).toEqual(true); expect(component.find('div.info-button-message').exists()).toEqual(true);
done();
}, 500);
}); });
test('clicking on info button makes info message visible', () => { test('clicking on info button makes info message visible', () => {
const component = mountWithIntl( const component = mountWithIntl(
<InfoButton <InfoButton
message="Here is some info about something!" message="Here is some info about something!"
/> />
); );
component.find('div.info-button').simulate('click'); const buttonRef = component.instance().buttonRef;
// click on info button
mockedAddEventListener.mousedown({target: buttonRef});
component.update();
expect(component.find('div.info-button').exists()).toEqual(true);
expect(component.find('div.info-button-message').exists()).toEqual(true); expect(component.find('div.info-button-message').exists()).toEqual(true);
}); });
test('after message is visible, mouseOut makes it vanish', () => {
test('clicking on info button, then mousing out makes info message still appear', done => {
const component = mountWithIntl( const component = mountWithIntl(
<InfoButton <InfoButton
message="Here is some info about something!" message="Here is some info about something!"
/> />
); );
component.find('div.info-button').simulate('mouseOver'); const buttonRef = component.instance().buttonRef;
// click on info button
mockedAddEventListener.mousedown({target: buttonRef});
component.update();
expect(component.find('div.info-button').exists()).toEqual(true);
expect(component.find('div.info-button-message').exists()).toEqual(true); expect(component.find('div.info-button-message').exists()).toEqual(true);
component.find('div.info-button').simulate('mouseOut');
// mouseLeave from info button
component.find('div.info-button').simulate('mouseLeave');
setTimeout(function () { // necessary because mouseover uses debounce
component.update();
expect(component.find('div.info-button-message').exists()).toEqual(true);
done();
}, 500);
});
test('clicking on info button, then clicking on it again makes info message go away', () => {
const component = mountWithIntl(
<InfoButton
message="Here is some info about something!"
/>
);
const buttonRef = component.instance().buttonRef;
// click on info button
mockedAddEventListener.mousedown({target: buttonRef});
component.update();
expect(component.find('div.info-button').exists()).toEqual(true);
expect(component.find('div.info-button-message').exists()).toEqual(true);
// click on info button again
mockedAddEventListener.mousedown({target: buttonRef});
component.update();
expect(component.find('div.info-button-message').exists()).toEqual(false);
});
test('clicking on info button, then clicking somewhere else', () => {
const component = mountWithIntl(
<InfoButton
message="Here is some info about something!"
/>
);
const buttonRef = component.instance().buttonRef;
// click on info button
mockedAddEventListener.mousedown({target: buttonRef});
component.update();
expect(component.find('div.info-button').exists()).toEqual(true);
expect(component.find('div.info-button-message').exists()).toEqual(true);
// click on some other target
mockedAddEventListener.mousedown({target: null});
component.update();
expect(component.find('div.info-button-message').exists()).toEqual(false);
});
test('after message is visible, mouseLeave makes it vanish', () => {
const component = mountWithIntl(
<InfoButton
message="Here is some info about something!"
/>
);
// mouseOver info button
component.find('div.info-button').simulate('mouseOver');
component.update();
expect(component.find('div.info-button-message').exists()).toEqual(true);
// mouseLeave away from info button
component.find('div.info-button').simulate('mouseLeave');
component.update();
expect(component.find('div.info-button-message').exists()).toEqual(false); expect(component.find('div.info-button-message').exists()).toEqual(false);
}); });
}); });