chore: delete ui-components package (#23950)

* chore: delete ui-components package

* add dependency to frontend-shared that was previously provided by ui-components

* whoops

* fix deps
This commit is contained in:
Emily Rohrbough
2022-09-26 19:19:10 -05:00
committed by GitHub
parent bf590eba3f
commit 63b1a9560d
59 changed files with 78 additions and 2054 deletions

View File

@@ -1732,26 +1732,6 @@ jobs:
path: /tmp/artifacts
- store-npm-logs
ui-components-integration-tests:
<<: *defaults
steps:
- restore_cached_workspace
- run:
command: yarn build-for-tests
working_directory: packages/ui-components
- run:
command: |
CYPRESS_KONFIG_ENV=production \
CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY \
yarn cypress:run --record --parallel --group ui-components
working_directory: packages/ui-components
- verify-mocha-results
- store_test_results:
path: /tmp/cypress
- store_artifacts:
path: /tmp/artifacts
- store-npm-logs
npm-webpack-preprocessor:
<<: *defaults
steps:
@@ -2502,10 +2482,6 @@ linux-x64-workflow: &linux-x64-workflow
context: [test-runner:cypress-record-key, test-runner:percy]
requires:
- build
- ui-components-integration-tests:
context: test-runner:cypress-record-key
requires:
- build
- npm-webpack-dev-server:
requires:
- system-tests-node-modules-install
@@ -2577,7 +2553,6 @@ linux-x64-workflow: &linux-x64-workflow
- server-integration-tests
- server-unit-tests
- test-kitchensink
- ui-components-integration-tests
- unit-tests
- unit-tests-release
- cli-visual-tests

View File

@@ -14,5 +14,5 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
import '@packages/frontend-shared/cypress/e2e/support/e2eSupport'
import '@packages/frontend-shared/cypress/support/e2e'
import './commands'

View File

@@ -14,5 +14,5 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
import '@packages/frontend-shared/cypress/e2e/support/e2eSupport'
import '@packages/frontend-shared/cypress/support/e2e'
import './commands'

View File

@@ -59,7 +59,7 @@
"test-scripts": "mocha -r packages/ts/register --reporter spec 'scripts/unit/**/*spec.js'",
"test-scripts-watch": "yarn test-scripts --watch --watch-extensions 'ts,js'",
"pretest-unit": "yarn ensure-deps",
"test-unit": "lerna exec yarn test-unit --ignore \"'{@packages/{driver,root,static,web-config,net-stubbing,rewriter,ui-components},@cypress/{webpack-dev-server,eslint-plugin-dev}}'\"",
"test-unit": "lerna exec yarn test-unit --ignore \"'{@packages/{driver,root,static,web-config,net-stubbing,rewriter},@cypress/{webpack-dev-server,eslint-plugin-dev}}'\"",
"pretest-watch": "yarn ensure-deps",
"test-watch": "lerna exec yarn test-watch --ignore \"'@packages/{driver,root,static,web-config}'\"",
"type-check": "yarn lerna exec yarn type-check --scope @tooling/system-tests && node scripts/type_check",
@@ -90,7 +90,6 @@
"@octokit/auth-app": "3.6.1",
"@octokit/core": "3.6.0",
"@percy/cli": "1.2.0",
"@percy/cypress": "^3.1.1",
"@semantic-release/changelog": "5.0.1",
"@semantic-release/git": "9.0.0",
"@types/bluebird": "3.5.29",

View File

@@ -30,7 +30,7 @@ export default defineConfig({
'just-my-luck',
'combine-properties',
'faker',
'@packages/ui-components/cypress/support/customPercyCommand',
'@packages/frontend-shared/cypress/support/customPercyCommand',
],
},
},

View File

@@ -27,7 +27,7 @@ import { setActivePinia } from 'pinia'
import type { Pinia } from 'pinia'
import 'cypress-real-events/support'
import { installCustomPercyCommand } from '@packages/ui-components/cypress/support/customPercyCommand'
import { installCustomPercyCommand } from '@packages/frontend-shared/cypress/support/customPercyCommand'
let pinia: Pinia

View File

@@ -1,4 +1,4 @@
import '@packages/frontend-shared/cypress/e2e/support/e2eSupport'
import '@packages/frontend-shared/cypress/support/e2e'
import 'cypress-real-events/support'
import './execute-spec'

View File

@@ -20,7 +20,7 @@ export default defineConfig({
viteConfig: {
optimizeDeps: {
include: [
'@packages/ui-components/cypress/support/customPercyCommand',
'cypress/support/customPercyCommand',
],
},
},
@@ -28,6 +28,5 @@ export default defineConfig({
},
e2e: {
baseUrl: 'http://localhost:5555',
supportFile: 'cypress/e2e/support/e2eSupport.ts',
},
})

View File

@@ -1,7 +1,7 @@
import path from 'path'
import execa from 'execa'
import type { CyTaskResult, OpenGlobalModeOptions, RemoteGraphQLInterceptor, ResetOptionsResult, WithCtxInjected, WithCtxOptions } from './support/e2eSupport'
import type { CyTaskResult, OpenGlobalModeOptions, RemoteGraphQLInterceptor, ResetOptionsResult, WithCtxInjected, WithCtxOptions } from '../support/e2e'
import { fixtureDirs } from '@tooling/system-tests'
// import type { CloudExecuteRemote } from '@packages/data-context/src/sources'
import { makeGraphQLServer } from '@packages/graphql/src/makeGraphQLServer'

View File

@@ -213,7 +213,7 @@
"field": "scrollBehavior"
},
{
"value": "cypress/e2e/support/e2eSupport.ts",
"value": "cypress/support/e2e.ts",
"from": "config",
"field": "supportFile"
},
@@ -290,7 +290,7 @@
"just-my-luck",
"combine-properties",
"faker",
"@packages/ui-components/cypress/support/customPercyCommand"
"cypress/support/customPercyCommand"
]
}
}

View File

@@ -3,7 +3,7 @@ import { registerMountFn, addVueCommand } from './common'
import '../../src/styles/shared.scss'
import 'virtual:windi.css'
import 'cypress-real-events/support'
import { installCustomPercyCommand } from '@packages/ui-components/cypress/support/customPercyCommand'
import { installCustomPercyCommand } from './customPercyCommand'
import { addNetworkCommands } from './onlineNetwork'
import { GQLStubRegistry } from './mock-graphql/stubgql-Registry'

View File

@@ -1,22 +1,23 @@
import '@testing-library/cypress/add-commands'
import { browsers } from '@packages/launcher/lib/browsers'
import { installCustomPercyCommand } from '@packages/ui-components/cypress/support/customPercyCommand'
import { configure } from '@testing-library/cypress'
import i18n from '../../../src/locales/en-US.json'
import { addNetworkCommands } from '../../support/onlineNetwork'
import { fixtureDirs, ProjectFixtureDir } from '@tooling/system-tests'
import type { DataContext } from '@packages/data-context'
import type { AuthenticatedUserShape } from '@packages/data-context/src/data'
import type { DocumentNode, ExecutionResult } from 'graphql'
import type { Browser, FoundBrowser, OpenModeOptions } from '@packages/types'
import type { E2ETaskMap } from '../e2ePluginSetup'
import type { SinonStub } from 'sinon'
import type sinon from 'sinon'
import type pDefer from 'p-defer'
import 'cypress-plugin-tab'
import type { Response } from 'cross-fetch'
import type { E2ETaskMap } from '../e2e/e2ePluginSetup'
import { installCustomPercyCommand } from './customPercyCommand'
import i18n from '../../src/locales/en-US.json'
import { addNetworkCommands } from './onlineNetwork'
configure({ testIdAttribute: 'data-cy' })
const NO_TIMEOUT = 1000 * 1000

View File

@@ -25,6 +25,7 @@
"@iconify/json": "1.1.368",
"@iconify/vue": "3.0.0-beta.1",
"@intlify/vite-plugin-vue-i18n": "2.4.0",
"@percy/core": "^1.0.0-beta.48",
"@percy/cypress": "^3.1.0",
"@testing-library/cypress": "8.0.0",
"@toycode/markdown-it-class": "1.2.3",
@@ -40,6 +41,7 @@
"@vue/compiler-sfc": "3.2.31",
"@vueuse/core": "7.2.2",
"axe-core": "4.4.1",
"browser-logos": "github:alrra/browser-logos",
"combine-properties": "0.1.0",
"cross-env": "6.0.3",
"cypress-axe": "0.14.0",

View File

@@ -1,16 +1,16 @@
import chromeIcon from '../../../../node_modules/browser-logos/src/chrome/chrome.svg?url'
import firefoxIcon from '../../../../node_modules/browser-logos/src/firefox/firefox.svg?url'
import edgeIcon from '../../../../node_modules/browser-logos/src/edge/edge.svg?url'
import electronIcon from '../../../../node_modules/browser-logos/src/electron/electron.svg?url'
import canaryIcon from '../../../../node_modules/browser-logos/src/chrome-canary/chrome-canary.svg?url'
import chromeBetaIcon from '../../../../node_modules/browser-logos/src/chrome-beta/chrome-beta.svg?url'
import chromiumIcon from '../../../../node_modules/browser-logos/src/chromium/chromium.svg?url'
import edgeBetaIcon from '../../../../node_modules/browser-logos/src/edge-beta/edge-beta.png'
import edgeCanaryIcon from '../../../../node_modules/browser-logos/src/edge-canary/edge-canary.png'
import edgeDevIcon from '../../../../node_modules/browser-logos/src/edge-dev/edge-dev.png'
import firefoxNightlyIcon from '../../../../node_modules/browser-logos/src/firefox-nightly/firefox-nightly.svg?url'
import firefoxDeveloperEditionIcon from '../../../../node_modules/browser-logos/src/firefox-developer-edition/firefox-developer-edition.svg?url'
import webKitIcon from '../../../../node_modules/browser-logos/src/webkit/webkit.svg?url'
import chromeIcon from 'browser-logos/src/chrome/chrome.svg?url'
import firefoxIcon from 'browser-logos/src/firefox/firefox.svg?url'
import edgeIcon from 'browser-logos/src/edge/edge.svg?url'
import electronIcon from 'browser-logos/src/electron/electron.svg?url'
import canaryIcon from 'browser-logos/src/chrome-canary/chrome-canary.svg?url'
import chromeBetaIcon from 'browser-logos/src/chrome-beta/chrome-beta.svg?url'
import chromiumIcon from 'browser-logos/src/chromium/chromium.svg?url'
import edgeBetaIcon from 'browser-logos/src/edge-beta/edge-beta.png'
import edgeCanaryIcon from 'browser-logos/src/edge-canary/edge-canary.png'
import edgeDevIcon from 'browser-logos/src/edge-dev/edge-dev.png'
import firefoxNightlyIcon from 'browser-logos/src/firefox-nightly/firefox-nightly.svg?url'
import firefoxDeveloperEditionIcon from 'browser-logos/src/firefox-developer-edition/firefox-developer-edition.svg?url'
import webKitIcon from 'browser-logos/src/webkit/webkit.svg?url'
import genericBrowserLogo from '@packages/frontend-shared/src/assets/logos/generic-browser.svg?url'
export const allBrowsersIcons = {

View File

@@ -24,7 +24,7 @@ export default defineConfig({
viteConfig: {
optimizeDeps: {
include: [
'@packages/ui-components/cypress/support/customPercyCommand',
'@packages/frontend-shared/cypress/support/customPercyCommand',
],
},
},

View File

@@ -24,7 +24,7 @@ import 'cypress-real-events/support'
import './commands'
import './attachFileWithPath'
import { installCustomPercyCommand } from '@packages/ui-components/cypress/support/customPercyCommand'
import { installCustomPercyCommand } from '@packages/frontend-shared/cypress/support/customPercyCommand'
installCustomPercyCommand({
before: () => {},

View File

@@ -1,5 +1,5 @@
/// <reference path="../../../../frontend-shared/cypress/e2e/support/e2eSupport.ts" />
require('../../../../frontend-shared/cypress/e2e/support/e2eSupport')
/// <reference path="../../../../frontend-shared/cypress/support/e2e.ts" />
require('../../../../frontend-shared/cypress/support/e2e')
require('./dropFileWithPath')
require('./containsPath')

View File

@@ -1,6 +1,6 @@
import { mount } from 'cypress/react'
import 'cypress-real-events/support'
import { installCustomPercyCommand } from '@packages/ui-components/cypress/support/customPercyCommand'
import { installCustomPercyCommand } from '@packages/frontend-shared/cypress/support/customPercyCommand'
import '../../src/main.scss'

View File

@@ -1,6 +1,6 @@
import 'cypress-real-events/support'
// @ts-ignore
import { installCustomPercyCommand } from '@packages/ui-components/cypress/support/customPercyCommand'
import { installCustomPercyCommand } from '@packages/frontend-shared/cypress/support/customPercyCommand'
installCustomPercyCommand({
before () {

View File

@@ -7,7 +7,6 @@
@import 'lib/tag';
@import 'lib/tooltip';
@import '~@reach/dialog/styles.css';
@import '../../ui-components/src/file-opener/file-opener';
// import all other scss files in src except if they are in lib
// or their file name is `selector-playground` or `main`
// NOTE: no need to import scss files in their components

View File

@@ -8,7 +8,6 @@
@import 'lib/tag';
@import 'lib/tooltip';
@import '~@reach/dialog/styles.css';
@import '../../ui-components/src/file-opener/file-opener';
// import all other scss files in src except if they are in lib
// or their file name is `selecor-playground` or `main`
// NOTA: no need to import scss files in their components

View File

@@ -9,5 +9,4 @@ $cy-tooltip-class: 'cy-tooltip';
// NOTA: no need to import scss files in their components
@import '../../runner/src/!(lib)*/**/!(assertions-menu|main).scss';
@import '../../ui-components/src/dropdown';
@import '../../reporter/src/main-runner';

View File

@@ -1,2 +0,0 @@
cypress/screenshots
cypress/videos

View File

@@ -1,34 +0,0 @@
# UI Components
This packages contains React components that are shared between two or more of the UI packages (`desktop-gui`, `reporter`, & `runner`).
## Installing
Dependencies can be installed with:
```bash
cd packages/ui-components
npm install
```
## Developing & Testing
These components are best developed by using their Cypress tests.
Run this in one terminal tab to watch the source files
```bash
npm run watch
```
Run this in another terminal tab to run Cypress
```bash
npm run cypress:open
```
To run the tests once you can run:
```bash
npm run build && npm run cypress:run
```

View File

@@ -1,73 +0,0 @@
const wp = require('@cypress/webpack-preprocessor')
const webpackOptions = {
mode: 'none',
resolve: {
extensions: ['.ts', '.js', '.jsx', '.tsx', '.png'],
},
module: {
rules: [
{
test: /\.(ts|js|jsx|tsx)$/,
exclude: /node_modules/,
use: {
loader: require.resolve('babel-loader'),
options: {
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
],
presets: [
require.resolve('@babel/preset-env'),
require.resolve('@babel/preset-react'),
require.resolve('@babel/preset-typescript'),
],
babelrc: false,
},
},
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
use: [
{
loader: require.resolve('file-loader'),
options: {
name: './fonts/[name].[ext]',
},
},
],
},
{
test: /\.(png)$/,
use: [
{
loader: require.resolve('file-loader'),
options: {
name: './img/[name].[ext]',
esModule: false,
},
},
],
},
],
},
}
module.exports = {
'fixturesFolder': false,
'projectId': 'ypt4pf',
'reporter': '../../node_modules/cypress-multi-reporters/index.js',
'reporterOptions': {
'configFile': '../../mocha-reporter-config.json',
},
'retries': {
'runMode': 2,
'openMode': 0,
},
'e2e': {
setupNodeEvents (on, config) {
on('file:preprocessor', wp({ webpackOptions }))
return config
},
},
}

View File

@@ -1,7 +0,0 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:cypress/recommended",
"plugin:@cypress/dev/react"
]
}

View File

@@ -1,75 +0,0 @@
import { render } from 'react-dom'
import React from 'react'
import { BrowserIcon } from '../../src'
describe('<BrowserIcon />', () => {
const _ = Cypress._
const browsers = [
'Canary',
'Chrome',
'Chromium',
'Edge',
'Edge Beta',
'Edge Canary',
'Edge Dev',
'Electron',
'Firefox',
'Firefox Developer Edition',
'Firefox Nightly',
]
beforeEach(() => {
cy.visit('dist/index.html')
cy.viewport(200, 200)
})
it('displays correct logo for supported browsers', () => {
cy.render(render, <>
{_.map(browsers, (browser) => (
<BrowserIcon browserName={browser} key={browser} />
))}
</>)
_.each(browsers, (browser, i) => {
cy.get('.browser-icon').eq(i)
.should('have.attr', 'src')
.and('include', _.kebabCase(browser))
})
})
it('displays family logo for other variants', () => {
cy.render(render, <>
<BrowserIcon browserName='Chrome Custom' />
<BrowserIcon browserName='Edge Custom' />
<BrowserIcon browserName='Electron Custom' />
<BrowserIcon browserName='Firefox Custom' />
<BrowserIcon browserName='Chromium Custom' />
</>)
cy.get('.browser-icon').eq(0)
.should('have.attr', 'src')
.and('include', 'chrome')
cy.get('.browser-icon').eq(1)
.should('have.attr', 'src')
.and('include', 'edge')
cy.get('.browser-icon').eq(2)
.should('have.attr', 'src')
.and('include', 'electron')
cy.get('.browser-icon').eq(3)
.should('have.attr', 'src')
.and('include', 'firefox')
cy.get('.browser-icon').eq(4)
.should('have.attr', 'src')
.and('include', 'chromium')
})
it('displays generic logo for unsupported browsers', () => {
cy.render(render, <BrowserIcon browserName='custom' />)
cy.get('.browser-icon').should('have.class', 'fa-globe')
})
})

View File

@@ -1,92 +0,0 @@
import React from 'react'
import { render } from 'react-dom'
import { Dropdown } from '../../'
describe('<Dropdown />', () => {
let defaultProps
beforeEach(() => {
defaultProps = {
chosen: { name: 'First' },
others: [{ name: 'Second' }, { name: 'Third' }],
keyProperty: 'name',
renderItem: ({ name }) => name,
onSelect: () => {},
}
cy.visit('dist/index.html')
cy.viewport(400, 600)
})
it('displays chosen option and hides others', () => {
cy.render(render, <Dropdown {...defaultProps} />)
cy.contains('First').should('be.visible')
cy.contains('Second').should('not.be.visible')
cy.contains('Third').should('not.be.visible')
})
it('shows others after clicking chosen option', () => {
cy.render(render, <Dropdown {...defaultProps} />)
cy.contains('First').click()
cy.contains('Second').should('be.visible')
cy.contains('Third').should('be.visible')
})
it('calls onSelect after clicking option', () => {
const onSelect = cy.stub()
cy.render(render, <Dropdown {...defaultProps} onSelect={onSelect} />)
cy.contains('First').click()
cy.contains('Second').click().then(() => {
expect(onSelect).to.be.calledWith({ name: 'Second' })
})
})
it('applies className to container', () => {
cy.render(render, <Dropdown {...defaultProps} className="custom-class" />)
cy.get('.dropdown').should('have.class', 'custom-class')
})
it('renders item as specified by renderItem prop', () => {
cy.render(render, <Dropdown {...defaultProps} renderItem={({ name }) => <span>{name}</span>} />)
cy.contains('First').children().should('match', 'span')
cy.contains('Second').should('match', 'span')
cy.contains('Third').should('match', 'span')
})
it('renders caret if there are items', () => {
cy.render(render, <Dropdown {...defaultProps} />)
cy.get('.dropdown-caret')
})
it('does not render caret if there are no items', () => {
cy.render(render, <Dropdown {...defaultProps} others={[]}/>)
cy.get('.dropdown-caret').should('not.exist')
})
it('disables if disabled specified and does not render options or caret', () => {
cy.render(render, <Dropdown {...defaultProps} disabled/>)
cy.contains('First').should('have.class', 'disabled').click({ force: true })
cy.get('.dropdown li').should('not.exist')
cy.get('.dropdown-caret').should('not.exist')
})
it('closes dropdown when clicking outside of it', () => {
cy.document().then((doc) => {
cy.render(render, <Dropdown {...defaultProps} document={doc} />)
})
cy.contains('First').click()
cy.get('body').click()
cy.contains('Second').should('not.be.visible')
})
})

View File

@@ -1,157 +0,0 @@
import chaiSubset from 'chai-subset'
import { action } from 'mobx'
import { observer, useLocalStore } from 'mobx-react'
import { render } from 'react-dom'
import React from 'react'
import { EditorPicker } from '../../'
chai.use(chaiSubset)
const _ = Cypress._
describe('<EditorPicker />', () => {
let defaultProps
beforeEach(() => {
defaultProps = {
chosen: { id: 'vscode', name: 'VS Code', binary: 'vscode', isOther: false },
editors: [
{ id: 'computer', name: 'On Computer', binary: 'computer', isOther: false, description: 'Opens on computer etc etc' },
{ id: 'atom', name: 'Atom', binary: 'atom', isOther: false },
{ id: 'sublime', name: 'Sublime Text', binary: 'sublime', isOther: false },
{ id: 'vscode', name: 'VS Code', binary: 'vscode', isOther: false },
{ id: 'other', name: 'Other', binary: '', isOther: true, description: 'Enter the full path etc etc' },
],
onSelect: () => {},
}
cy.visit('dist/index.html')
cy.viewport(400, 600)
})
it('renders each specified editor', () => {
cy.render(render, <EditorPicker {...defaultProps} />)
cy.contains('On Computer')
cy.contains('Atom')
cy.contains('Sublime Text')
cy.contains('VS Code')
cy.contains('Other')
})
it('has chosen editor selected', () => {
cy.render(render, <EditorPicker {...defaultProps} />)
cy.contains('VS Code').find('input').should('be.checked')
})
it('has none chosen if not specified', () => {
cy.render(render, <EditorPicker {...defaultProps} chosen={undefined} />)
cy.get('input[type="radio"]').should('not.be.checked')
})
// NOTE: this doesn't work currently because the tooltip renders in the spec
// iframe and not the AUT iframe. need to switch to @cypress/react
// or something similar to get this to work
it.skip('shows info circle with desciption tooltip when specified', () => {
cy.render(render, <EditorPicker {...defaultProps} />)
cy.get('.fa-info-circle').trigger('mouseover')
cy.get('.cy-tooltip')
.should('be.visible')
.should('have.text', 'Opens on computer etc etc')
})
it('calls onSelect when option is chosen', () => {
const onSelect = cy.stub()
cy.render(render, <EditorPicker {...defaultProps} onSelect={onSelect}/>)
cy.contains('Sublime Text').click().then(() => {
expect(onSelect).to.be.calledWith(defaultProps.editors[2])
})
})
describe('"Other" handling', () => {
it('shows description when chosen', () => {
cy.render(render, <EditorPicker {...defaultProps} chosen={defaultProps.editors[4]}/>)
cy.contains('Enter the full path').should('be.visible')
})
it('selects item when focusing text input', () => {
cy.render(render, <EditorPicker {...defaultProps} chosen={defaultProps.editors[4]}/>)
cy.contains('Other').find('input[type="text"]').focus()
cy.contains('Other').find('input[type="radio"]').should('be.checked')
})
it('populates path if specified', () => {
defaultProps.editors[4].binary = '/path/to/my/editor'
cy.render(render, <EditorPicker {...defaultProps} chosen={defaultProps.editors[4]}/>)
cy.contains('Other').find('input[type="text"]').should('have.value', '/path/to/my/editor')
})
describe('typing path', () => {
const path = '/path/to/my/editor'
const Wrapper = observer(({ onSelectSpy }) => {
const state = useLocalStore(() => ({
editors: defaultProps.editors,
setChosenEditor: action((editor) => {
state.chosenEditor = editor
}),
setOtherPath: action((otherPath) => {
const otherOption = _.find(state.editors, { isOther: true })
otherOption.binary = otherPath
}),
}))
const onSelect = (chosen) => {
state.setChosenEditor(chosen)
onSelectSpy(chosen)
}
return (
<EditorPicker
{...defaultProps}
editors={state.editors}
chosen={state.chosen}
onSelect={onSelect}
onUpdateOtherPath={state.setOtherPath}
/>
)
})
it('trims the path', () => {
const onSelect = cy.stub()
cy.render(render, <Wrapper onSelectSpy={onSelect} />)
cy.contains('Other').find('input[type="text"]').type(` ${path} `, { delay: 0 })
.should(() => {
expect(onSelect.lastCall.args[0].binary).to.equal(path)
})
})
it('calls onSelect for every character typed', () => {
const onSelect = cy.stub()
cy.render(render, <Wrapper onSelectSpy={onSelect} />)
_.each(path.split(''), (letter, i) => {
const pathSoFar = path.substring(0, i + 1)
cy.contains('Other').find('input[type="text"]').type(letter, { delay: 0 })
.should(() => {
expect(onSelect.lastCall.args[0].id).to.equal('other')
expect(onSelect.lastCall.args[0].binary).to.equal(pathSoFar)
})
})
})
})
})
})

View File

@@ -1,179 +0,0 @@
import React from 'react'
import { render } from 'react-dom'
import { FileOpener } from '../../'
const _ = Cypress._
const fileDetails = {
absoluteFile: '/absolute/path/to/file_spec.js',
column: 0,
line: 0,
originalFile: 'path/to/file_spec.js',
relativeFile: 'path/to/file_spec.js',
}
const preferredOpener = {
id: 'vscode',
name: 'VS Code',
binary: 'vscode',
isOther: false,
}
const availableEditors = [
{ id: 'computer', name: 'On Computer', binary: 'computer', isOther: false, description: 'Opens on computer etc etc' },
{ id: 'atom', name: 'Atom', binary: 'atom', isOther: false },
{ id: 'sublime', name: 'Sublime Text', binary: 'sublime', isOther: false },
{ id: 'vscode', name: 'VS Code', binary: 'vscode', isOther: false },
{ id: 'other', name: 'Other', binary: '', isOther: true, description: 'Enter the full path etc etc' },
]
describe('<FileOpener />', () => {
let defaultProps
beforeEach(() => {
defaultProps = {
fileDetails,
openFile: () => {},
getUserEditor: (callback) => {
callback({
preferredOpener,
availableEditors,
})
},
setUserEditor: () => {},
className: 'file-opener',
}
cy.visit('dist/index.html')
cy.viewport(600, 600)
})
it('renders link text', () => {
cy.render(render, <FileOpener {...defaultProps}>Open in IDE</FileOpener>)
cy.get('.file-opener').should('have.text', 'Open in IDE')
})
context('when user has already set opener and opens file', () => {
it('opens in preferred opener', () => {
const openFile = cy.stub()
cy.render(render, <FileOpener {...defaultProps} openFile={openFile}>Open in IDE</FileOpener>)
cy.get('.file-opener').click().then(() => {
expect(openFile).to.be.calledWith(preferredOpener, fileDetails)
})
})
})
context('when user has not already set opener and opens file', () => {
let defaultPropsModal
beforeEach(() => {
defaultPropsModal = {
...defaultProps,
getUserEditor: (callback) => {
callback({
availableEditors,
})
},
}
})
it('opens modal with available editors', () => {
cy.render(render, <FileOpener {...defaultPropsModal}>Open in IDE</FileOpener>)
cy.get('.file-opener').click()
_.each(availableEditors, ({ name }) => {
cy.contains(name)
})
cy.contains('Set preference and open file')
})
it('closes modal when cancel is clicked', () => {
cy.render(render, <FileOpener {...defaultPropsModal}>Open in IDE</FileOpener>)
cy.get('.file-opener').click()
cy.contains('Sublime Text').click()
cy.contains('Cancel').click()
cy.contains('Set preference and open file').should('not.exist')
})
it('initially has no editors chosen', () => {
cy.render(render, <FileOpener {...defaultPropsModal}>Open in IDE</FileOpener>)
cy.get('.file-opener').click()
cy.get('input[type="radio"]').should('not.be.checked')
cy.get('.submit').should('have.class', 'is-disabled')
})
it('should not open without editor selected', () => {
const setEditor = cy.stub()
const openFile = cy.stub()
cy.render(render, <FileOpener {...defaultPropsModal} setEditor={setEditor} openFile={openFile}>Open in IDE</FileOpener>)
cy.get('.file-opener').click()
cy.get('.submit')
.should('have.class', 'is-disabled')
.click()
.then(() => {
expect(setEditor).not.to.be.called
expect(openFile).not.to.be.called
})
})
it('disables submit when Other is selected but path not entered', () => {
const setEditor = cy.stub()
const openFile = cy.stub()
cy.render(render, <FileOpener {...defaultPropsModal} setEditor={setEditor} openFile={openFile}>Open in IDE</FileOpener>)
cy.get('.file-opener').click()
cy.contains('Other').click()
cy.get('.submit')
.should('have.class', 'is-disabled')
.click()
.then(() => {
expect(setEditor).not.to.be.called
expect(openFile).not.to.be.called
})
})
it('sets user editor when selected', () => {
const setEditor = cy.stub()
cy.render(render, <FileOpener {...defaultPropsModal} setUserEditor={setEditor}>Open in IDE</FileOpener>)
cy.get('.file-opener').click()
cy.contains('Sublime Text').click()
cy.get('.submit').click().then(() => {
expect(setEditor).to.be.calledWith(availableEditors[2])
})
})
it('opens in correct editor when selected', () => {
const openFile = cy.stub()
cy.render(render, <FileOpener {...defaultPropsModal} openFile={openFile}>Open in IDE</FileOpener>)
cy.get('.file-opener').click()
cy.contains('Sublime Text').click()
cy.get('.submit').click().then(() => {
expect(openFile).to.be.calledWith(availableEditors[2], fileDetails)
})
})
it('closes modal after selection', () => {
cy.render(render, <FileOpener {...defaultPropsModal}>Open in IDE</FileOpener>)
cy.get('.file-opener').click()
cy.contains('Sublime Text').click()
cy.get('.submit').click()
cy.contains('Set preference and open file').should('not.exist')
})
})
})

View File

@@ -1,259 +0,0 @@
import React, { useState } from 'react'
import { render } from 'react-dom'
import { Select, SelectItem } from '../../src'
const _ = Cypress._
describe('<Select />', () => {
beforeEach(() => {
cy.visit('dist/index.html')
cy.viewport(400, 600)
})
it('optionally provide a name', () => {
cy.render(render, (
<>
<Select value="v2" name="myName">
<SelectItem value="v1" data-test="v1" />
<SelectItem value="v2" data-test="v2" />
<SelectItem value="v3" data-test="v3" />
</Select>
<br/>
<Select value="v1">
<SelectItem value="v1" data-test="v1" />
<SelectItem value="v2" data-test="v2" />
<SelectItem value="v3" data-test="v3" />
</Select>
</>
))
cy.get('input[name="myName"]').then((el) => expect(el.length).to.eql(3))
cy.get('input').then((els) => {
expect(els.length).to.eql(6)
expect(_.filter(els, (el) => el.name !== 'myName').length).to.eql(3)
})
})
it('can render multiple groups on the same page', () => {
cy.render(render, (
<>
<Select value="v2" name="myName">
<SelectItem value="v1" data-test="v1" />
<SelectItem value="v2" data-test="v2" />
<SelectItem value="v3" data-test="v3" />
</Select>
<br/>
<Select value="v1">
<SelectItem value="v1" data-test="v1" />
<SelectItem value="v2" data-test="v2" />
<SelectItem value="v3" data-test="v3" />
</Select>
<br/>
<Select value="v3">
<SelectItem value="v1" data-test="v1" />
<SelectItem value="v2" data-test="v2" />
<SelectItem value="v3" data-test="v3" />
</Select>
</>
))
cy.get('input[name="myName"]').then((el) => expect(el.length).to.eql(3))
cy.get('input').then((els) => {
expect(els.length).to.eql(9)
expect(_.filter(els, (el) => el.name !== 'myName').length).to.eql(6)
expect(els[1].checked).to.be.true
expect(els[3].checked).to.be.true
expect(els[8].checked).to.be.true
})
})
it('only a single SelectItem is checked at a time', () => {
const onChange = cy.stub()
cy.render(render, (
<Select value="v2" onChange={onChange}>
<SelectItem value="v1" />
<SelectItem value="v2" />
<SelectItem value="v3" />
</Select>
))
cy.get('[data-value="v1"]').click()
.then(() => expect(onChange).to.be.calledWith('v1'))
cy.get('[data-value="v3"]').click()
.then(() => expect(onChange).to.be.calledWith('v3'))
})
it('other components are permitted as children for a <Select />', () => {
cy.render(render, (
<div width="100%">
<Select value="oranges">
<h3>Fruits</h3>
<label><SelectItem value="apples" data-test="apples" /> apples</label>
<label><SelectItem value="oranges" data-test="oranges" /> oranges</label>
<h3>Vegetables</h3>
<label><SelectItem value="apples" data-test="apples" /> carrot</label>
<label><SelectItem value="oranges" data-test="oranges" /> potato</label>
</Select>
</div>
))
cy.get('h3').first().contains('Fruits')
cy.get('h3').last().contains('Vegetables')
})
describe('single value selection', () => {
it('focus-able', () => {
cy.render(render, (
<Select value="v2">
<SelectItem value="v1" />
<SelectItem value="v2" />
<SelectItem value="v3" />
</Select>
))
cy.get('[value="v1"]').as('focused').first().focus()
cy.window().then((win) => {
cy.get('@focused').then((el) => {
expect(win.document.activeElement).to.eql(el[0])
})
})
})
// NOTE: requires native event tab support
// cypress-plugin-tab does not mimic the behavior of tabbing in this context exactly as the browser does
it.skip('tabbing moves focus to the next element with a tabIndex', () => {
cy.render(render, (
<Select>
<SelectItem value="v1" data-test={`v1`} />
<input type="text"/>
<SelectItem value="v2" data-test={`v2`} />
<SelectItem value="v3" data-test={`v3`} />
</Select>
))
cy.get('[data-test="v1"]').focus().tab()
cy.window().then((win) => {
cy.get('input[type="text"]').then((el) => {
expect(win.document.activeElement).to.eql(el[0])
})
})
cy.get('[data-test="v3"]').focus().tab({ shift: true })
cy.window().then((win) => {
cy.get('input[type="text"]').then((el) => {
expect(win.document.activeElement).to.eql(el[0])
})
})
})
describe('arrow keys', () => {
const left = 13
const up = 38
const right = 39
const down = 40
let onChange
beforeEach(() => {
const Wrapper = ({ onChangeSpy }) => {
const [selected, setSelected] = useState()
const onChange = (value) => {
onChangeSpy(value)
setSelected(value)
}
return (
<Select value={selected} onChange={onChange}>
<SelectItem value="v1" />
<input type="text"/>
<SelectItem value="v2" />
<SelectItem value="v3" />
<SelectItem value="v4">
<input data-cy="input" type="text" />
</SelectItem>
<SelectItem value="v5" />
</Select>
)
}
onChange = cy.spy()
cy.render(render, <Wrapper onChangeSpy={onChange} />)
cy.get('li').as('items')
cy.get('[value]').as('values')
})
it('left on first goes to last', () => {
cy.get('@items').first().click().trigger('keydown', { keyCode: left, which: left })
cy.get('@values').last().should('be.checked')
cy.wrap(onChange).should('be.calledWith', 'v5')
})
it('up on first goes to last', () => {
cy.get('@items').first().click().trigger('keydown', { keyCode: up, which: up })
cy.get('@values').last().should('be.checked')
cy.wrap(onChange).should('be.calledWith', 'v5')
})
it('right on last goes to first', () => {
cy.get('@items').last().click().trigger('keydown', { keyCode: right, which: right })
cy.get('@values').first().should('be.checked')
cy.wrap(onChange).should('be.calledWith', 'v1')
})
it('down on last goes to first', () => {
cy.get('@items').last().click().trigger('keydown', { keyCode: down, which: down })
cy.get('@values').first().should('be.checked')
cy.wrap(onChange).should('be.calledWith', 'v1')
})
it('left on last goes to penultimate', () => {
cy.get('@items').last().click().trigger('keydown', { keyCode: left, which: left })
cy.get('@values').eq(3).should('be.checked')
cy.wrap(onChange).should('be.calledWith', 'v4')
})
it('up on last goes to penultimate', () => {
cy.get('@items').last().click().trigger('keydown', { keyCode: up, which: up })
cy.get('@values').eq(3).should('be.checked')
cy.wrap(onChange).should('be.calledWith', 'v4')
})
it('right on first goes to second', () => {
cy.get('@items').first().click().trigger('keydown', { keyCode: right, which: right })
cy.get('@values').eq(1).should('be.checked')
cy.wrap(onChange).should('be.calledWith', 'v2')
})
it('down on first goes to second', () => {
cy.get('@items').first().click().trigger('keydown', { keyCode: down, which: down })
cy.get('@values').eq(1).should('be.checked')
cy.wrap(onChange).should('be.calledWith', 'v2')
})
describe('when keydown comes from inner element', () => {
it('does not move on left', () => {
cy.get('[data-cy="input"]').type('{leftarrow}')
cy.get('@values').eq(3).should('be.checked')
})
it('does not move on right', () => {
cy.get('[data-cy="input"]').type('{rightarrow}')
cy.get('@values').eq(3).should('be.checked')
})
it('does not move on up', () => {
cy.get('[data-cy="input"]').type('{uparrow}')
cy.get('@values').eq(3).should('be.checked')
})
it('does not move on down', () => {
cy.get('[data-cy="input"]').type('{downarrow}')
cy.get('@values').eq(3).should('be.checked')
})
})
})
})
})

View File

@@ -1,3 +0,0 @@
Cypress.Commands.add('render', (r, component) => {
cy.window().invoke('renderComponent', (el) => r(component, el))
})

View File

@@ -1 +0,0 @@
<div id="app"></div>

View File

@@ -1,4 +0,0 @@
import './test-entry.scss'
import '../../src/browser-icon' // ensures browser icon images load
window.renderComponent = (r) => r(document.getElementById('app'))

View File

@@ -1,8 +0,0 @@
@import "~@fortawesome/fontawesome-free/scss/regular.scss";
@import "~@fortawesome/fontawesome-free/scss/solid.scss";
@import "~@fortawesome/fontawesome-free/scss/brands.scss";
@import "~@fortawesome/fontawesome-free/scss/fontawesome.scss";
@import '~@reach/dialog/styles.css';
@import '../../src/dropdown';
@import '../../src/file-opener/file-opener';

View File

@@ -1,7 +0,0 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -1,7 +0,0 @@
/// <reference path="../../cli/types/cy-blob-util.d.ts" />
/// <reference path="../../cli/types/cy-bluebird.d.ts" />
/// <reference path="../../cli/types/cy-minimatch.d.ts" />
/// <reference path="../../cli/types/cypress.d.ts" />
/// <reference path="../../cli/types/cypress-global-vars.d.ts" />
/// <reference path="../../cli/types/cypress-type-helpers.d.ts" />

View File

@@ -1,44 +0,0 @@
{
"name": "@packages/ui-components",
"version": "0.0.0-development",
"private": true,
"main": "src/index.tsx",
"scripts": {
"build-for-tests": "node ../../scripts/run-webpack",
"check-deps": "node ../../scripts/check-deps.js --verbose",
"check-deps-pre": "npm run check-deps -- --prescript",
"clean-deps": "rimraf node_modules",
"cypress:open": "node ../../scripts/cypress open --project .",
"cypress:run": "node ../../scripts/cypress run --project .",
"pretest": "npm run check-deps-pre",
"watch": "npm run build-for-tests -- --watch --progress"
},
"devDependencies": {
"@babel/core": "7.9.0",
"@babel/plugin-proposal-class-properties": "7.8.3",
"@babel/plugin-proposal-decorators": "7.8.3",
"@babel/preset-env": "7.9.0",
"@babel/preset-react": "7.9.4",
"@cypress/react-tooltip": "^0.5.3",
"@cypress/webpack-preprocessor": "0.0.0-development",
"@fortawesome/fontawesome-free": "6.0.0",
"@percy/core": "^1.0.0-beta.48",
"@percy/cypress": "^3.1.0",
"@reach/dialog": "0.10.5",
"@reach/visually-hidden": "0.10.4",
"babel-loader": "8.1.0",
"browser-logos": "github:alrra/browser-logos",
"chai-subset": "1.6.0",
"classnames": "2.3.1",
"file-loader": "4.3.0",
"lodash": "^4.17.19",
"mobx": "5.15.4",
"mobx-react": "6.1.7",
"prop-types": "15.7.2",
"react": "16.8.6",
"react-dom": "16.8.6",
"rimraf": "3.0.2",
"webpack": "^4.44.2",
"webpack-cli": "3.3.2"
}
}

View File

@@ -1,26 +0,0 @@
{
"extends": [
"plugin:@cypress/dev/react",
"plugin:@cypress/dev/tests"
],
"parser": "@typescript-eslint/parser",
"settings": {
"react": {
"version": "16.12"
}
},
"rules": {
"arrow-body-style": "off",
"no-unused-vars": "off",
"react/jsx-filename-extension": [
"warn",
{
"extensions": [
".js",
".jsx",
".tsx"
]
}
]
}
}

View File

@@ -1,64 +0,0 @@
import _ from 'lodash'
import React from 'react'
interface FamilyOptions {
[key: string]: RegExp
}
const families: FamilyOptions = {
chrome: /^chrome/i,
chromium: /^chromium/i,
edge: /^edge/i,
electron: /^electron/i,
firefox: /^firefox/i,
}
interface LogoOptions {
[key: string]: string
}
const logoPaths: LogoOptions = {
canary: require('browser-logos/src/chrome-canary/chrome-canary_32x32.png'),
chrome: require('browser-logos/src/chrome/chrome_32x32.png'),
chromeBeta: require('browser-logos/src/chrome-beta/chrome-beta_32x32.png'),
chromium: require('browser-logos/src/chromium/chromium_32x32.png'),
edge: require('browser-logos/src/edge/edge_32x32.png'),
edgeBeta: require('browser-logos/src/edge-beta/edge-beta_32x32.png'),
edgeCanary: require('browser-logos/src/edge-canary/edge-canary_32x32.png'),
edgeDev: require('browser-logos/src/edge-dev/edge-dev_32x32.png'),
electron: require('browser-logos/src/electron/electron_32x32.png'),
firefox: require('browser-logos/src/firefox/firefox_32x32.png'),
firefoxDeveloperEdition: require('browser-logos/src/firefox-developer-edition/firefox-developer-edition_32x32.png'),
firefoxNightly: require('browser-logos/src/firefox-nightly/firefox-nightly_32x32.png'),
}
const familyFallback = (browserKey: string) => {
return _.reduce(families, (found, regex, family) => {
if (found !== '') return found
if (regex.test(browserKey)) return family
return ''
}, '')
}
const logoPath = (browserName: string) => {
const browserKey = _.camelCase(browserName)
return logoPaths[browserKey] || logoPaths[familyFallback(browserKey)]
}
interface Props {
browserName: string
}
// browserName should be the browser's display name
const BrowserIcon = ({ browserName }: Props) => {
if (logoPath(browserName)) {
return <img className='browser-icon' src={logoPath(browserName)} alt={browserName} />
}
return <i className='browser-icon fas fa-fw fa-globe' />
}
export default BrowserIcon

View File

@@ -1,65 +0,0 @@
.dropdown {
display: inline-block;
position: relative;
vertical-align: middle;
}
.dropdown-chosen {
background: none;
border: none;
outline: none;
&.disabled {
pointer-events: none;
opacity: 0.65;
}
}
.dropdown-caret {
border-top: 4px dashed;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
border-width: 5px 5px 0;
display: inline-block;
height: 0;
vertical-align: middle;
width: 0;
}
.dropdown-menu {
background-clip: padding-box;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 2px;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
font-size: 14px;
display: none;
float: left;
left: 0;
margin: 0;
min-width: 160px;
padding: 0;
position: absolute;
top: 100%;
list-style: none;
text-align: left;
z-index: 2000;
.open & {
display: block;
}
li {
&:last-of-type {
border-bottom: 0;
}
&:hover,
&:focus,
&:active {
cursor: pointer;
background-color: #f5f5f5;
color: #262626;
}
}
}

View File

@@ -1,118 +0,0 @@
import cs from 'classnames'
import _ from 'lodash'
import React, { Component, ReactNode } from 'react'
import { findDOMNode } from 'react-dom'
interface Indexable {
[key: string]: any
}
interface Props {
className?: string
chosen: Indexable
others: Indexable[]
onSelect: (item: Indexable) => any
renderItem: (item: Indexable) => ReactNode
keyProperty: string
disabled?: boolean
document: Document
}
class Dropdown extends Component<Props> {
static defaultProps = {
className: '',
document,
}
state = { open: false }
outsideClickHandler: (e: Event) => void = () => {}
componentDidMount () {
this.outsideClickHandler = (e: Event) => {
if (!findDOMNode(this)?.contains(e.target as Node)) {
this.setState({ open: false })
}
}
this.props.document.body.addEventListener('click', this.outsideClickHandler)
}
componentWillUnmount () {
this.props.document.body.removeEventListener('click', this.outsideClickHandler)
}
render () {
return (
<li className={cs('dropdown', this.props.className, { open: this.state.open })}>
{this._button()}
{this._items()}
</li>
)
}
_button () {
if (this.props.others.length) {
return (
<button onClick={this._toggleOpen} className={cs('dropdown-chosen', { disabled: this.props.disabled })}>
{this._buttonContent()}
</button>
)
}
return (
<span>
{this._buttonContent()}
</span>
)
}
_buttonContent () {
return (
<>
{this.props.renderItem(this.props.chosen)}{' '}
{this._caret()}
</>
)
}
_caret () {
if (!this.props.others.length || this.props.disabled) return null
return (
<span className='dropdown-toggle'>
<span className='dropdown-caret' />
<span className='sr-only'>Toggle Dropdown</span>
</span>
)
}
_toggleOpen = () => {
this.setState({ open: !this.state.open })
}
_items () {
if (!this.props.others.length || this.props.disabled) return null
return (
<ul className='dropdown-menu'>
{_.map(this.props.others, (item) => (
<li
key={item[this.props.keyProperty]}
tabIndex={0}
onClick={() => this._onSelect(item)}
>{this.props.renderItem(item)}</li>
))}
</ul>
)
}
_onSelect (item: object) {
const retval = this.props.onSelect(item)
const open = _.isBoolean(retval) ? retval : false
this.setState({ open })
}
}
export default Dropdown

View File

@@ -1,96 +0,0 @@
import _ from 'lodash'
import { Dialog } from '@reach/dialog'
import { action } from 'mobx'
import { observer, useLocalStore } from 'mobx-react'
// @ts-ignore
import Tooltip from '@cypress/react-tooltip'
import cs from 'classnames'
import React from 'react'
import VisuallyHidden from '@reach/visually-hidden'
import EditorPicker from './editor-picker'
import { Editor } from './file-model'
interface Props {
chosenEditor: Editor
editors: Editor[]
isOpen: boolean
onClose: (() => void)
onSetChosenEditor: ((editor: Editor) => void)
onSetEditor: ((editor: Editor) => void)
}
const validate = (chosenEditor: Editor) => {
let isValid = !!chosenEditor && !!chosenEditor.id
let validationMessage = 'Please select a preference'
if (isValid && chosenEditor.isOther && !chosenEditor.binary) {
isValid = false
validationMessage = 'Please enter the path for the "Other" editor'
}
return {
isValid,
validationMessage,
}
}
const EditorPickerModal = observer(({ chosenEditor, editors, isOpen, onClose, onSetChosenEditor, onSetEditor }: Props) => {
const state = useLocalStore((external) => ({
setOtherPath: action((otherPath: string) => {
const otherOption = _.find(external.editors, { isOther: true })
if (otherOption) {
otherOption.binary = otherPath
}
}),
}), { editors })
const setEditor = () => {
const { isValid } = validate(chosenEditor)
if (!isValid) return
onSetEditor(chosenEditor)
}
if (!editors.length) return null
const { isValid, validationMessage } = validate(chosenEditor)
return (
<Dialog
className='editor-picker-modal'
aria-label="Explanation of choosing an editor"
isOpen={isOpen}
onDismiss={onClose}
>
<div className='content'>
<h1>File Opener Preference</h1>
<p>Please select your preference for opening files on your system.</p>
<EditorPicker
chosen={chosenEditor}
editors={editors}
onSelect={onSetChosenEditor}
onUpdateOtherPath={state.setOtherPath}
/>
<p>We will use your selected preference to open files in the future. You can change your preference in the <b>Settings</b> tab of the Cypress Test Runner.</p>
</div>
<div className='controls'>
<Tooltip title={validationMessage} visible={isValid ? false : undefined} className='cy-tooltip'>
<button className={cs('submit', { 'is-disabled': !isValid })} onClick={setEditor}>Set preference and open file</button>
</Tooltip>
<button className='cancel' onClick={onClose}>Cancel</button>
</div>
<button className='close-button' onClick={onClose}>
<VisuallyHidden>Close</VisuallyHidden>
<span aria-hidden>
<i className='fas fa-times' />
</span>
</button>
</Dialog>
)
})
export default EditorPickerModal

View File

@@ -1,56 +0,0 @@
import _ from 'lodash'
import { observer } from 'mobx-react'
import React, { ChangeEvent } from 'react'
import { Editor } from './file-model'
import { Select, SelectItem } from '../select'
interface Props {
chosen?: Partial<Editor>
editors: Editor[]
onSelect: (editor: Editor) => any
onUpdateOtherPath: (path: string) => any
}
const EditorPicker = observer(({ chosen = {}, editors, onSelect, onUpdateOtherPath }: Props) => {
const editorOptions = _.reject(editors, { isOther: true })
const otherOption = _.find(editors, { isOther: true })
const onChange = (id: string) => {
const editor = _.find(editors, { id })
editor && onSelect(editor)
}
const updateOtherPath = (event: ChangeEvent<HTMLInputElement>) => {
onUpdateOtherPath(_.trim(event.target.value || ''))
}
const otherInput = (
<input
type='text'
className='other-input'
value={otherOption?.binary || ''}
onFocus={_.partial(onChange, 'other')}
onChange={updateOtherPath}
/>
)
return (
<Select value={chosen.id || ''} className='editor-picker' name='editor-picker' onChange={onChange}>
{_.map(editorOptions, (editor) => (
<SelectItem key={editor.id} value={editor.id}>
{editor.name}
</SelectItem>
))}
{otherOption && (
<SelectItem value={otherOption.id}>
{otherOption.name}: {otherInput}
{chosen.isOther && <span className='description'>Enter the full path to your editor's executable</span>}
</SelectItem>
)}
</Select>
)
})
export default EditorPicker

View File

@@ -1,12 +0,0 @@
export interface Editor {
id: string
name: string
binary: string
isOther: boolean
description?: string
}
export interface GetUserEditorResult {
preferredOpener?: Editor
availableEditors?: Editor[]
}

View File

@@ -1,158 +0,0 @@
@import '../select/select';
$pass: #08c18d !default;
$font-sans: 'Fira Mono', 'Helvetica Neue', 'Arial', sans-serif;
.editor-picker {
label {
align-items: flex-start;
display: flex;
flex-wrap: wrap;
}
.other-input {
border: solid 1px #7e7e7e;
border-radius: 3px;
margin-left: 0.3em;
padding: 0.2em 0.4em;
}
.is-selected .other-input {
flex-grow: 2;
}
.description {
color: #7e7e7e;
font-size: 0.9em;
}
i.description {
margin-left: 0.4em;
margin-top: 0.1em;
}
span.description {
display: block;
padding-left: 5.2em;
width: 100%;
}
}
.editor-picker-modal {
max-width: 40em;
.editor-picker {
margin-bottom: 1em;
}
.controls {
> span:first-child {
order: 1;
}
button.is-disabled,
button.is-disabled:hover,
button.is-disabled:focus {
background: $pass !important;
cursor: default !important;
opacity: 0.5;
}
padding: 1em 1em 1em;
}
}
[data-reach-dialog-overlay] {
display: flex;
padding: 2em 0;
z-index: 2;
}
[data-reach-dialog-content] {
align-items: center;
background: #f8f8f8;
border-radius: 10px;
font-size: 0.9em;
justify-content: center;
margin: auto;
min-width: 30em;
padding: 2em 0 0;
position: relative;
@if variable-exists(open-sans) {
font-family: $open-sans;
} @else {
font-family: $font-sans;
}
h1 {
font-size: 1.5em;
padding-bottom: 0.5em;
}
button {
background: none;
border: none;
cursor: pointer;
font-size: 1em;
padding: 0.6em 1em;
line-height: 1em;
}
p {
line-height: 1.5;
margin-bottom: 1em;
color: #444;
}
.content {
padding: 0 1.2em 0.6em;
}
.controls {
display: flex;
justify-content: flex-end;
padding: 0.6em;
button {
border-radius: 3px;
&:hover,
&:focus {
outline: none;
}
}
.submit {
background: $pass;
color: #fff;
margin-left: 0.6em;
order: 1;
&:hover,
&:focus {
background: darken($pass, 10%);
}
}
.cancel {
background: #e3e3e3;
&:hover,
&:focus {
background: darken(#e3e3e3, 10%);
}
}
}
.close-button {
position: absolute;
right: 0.1em;
top: 0.5em;
&:focus {
outline: none;
}
}
}

View File

@@ -1,82 +0,0 @@
import _ from 'lodash'
import { action } from 'mobx'
import { observer, useLocalStore } from 'mobx-react'
import React, { MouseEvent, ReactNode } from 'react'
import EditorPickerModal from './editor-picker-modal'
import { GetUserEditorResult, Editor } from './file-model'
import type { FileDetails } from '@packages/types'
interface Props {
children: ReactNode
fileDetails: FileDetails
openFile: (where: Editor, absoluteFile: FileDetails) => any
getUserEditor: (callback: (result: GetUserEditorResult) => void) => any
setUserEditor: (editor: Editor) => any
className?: string
}
const FileOpener = observer(({ children, fileDetails, openFile, getUserEditor, setUserEditor, className }: Props) => {
const state = useLocalStore(() => ({
editors: [] as Editor[],
chosenEditor: {} as Editor,
isLoadingEditor: false,
isModalOpen: false,
setChosenEditor: action((editor: Editor) => {
state.chosenEditor = editor
}),
setEditors: action((editors: Editor[]) => {
state.editors = editors
}),
setIsLoadingEditor: action((isLoading: boolean) => {
state.isLoadingEditor = isLoading
}),
setIsModalOpen: action((isOpen: boolean) => {
state.isModalOpen = isOpen
}),
}))
const attemptOpenFile = (e: MouseEvent) => {
e.preventDefault()
if (state.isLoadingEditor) return
state.setIsLoadingEditor(true)
// TODO: instead of the back-n-forth, send 'open:file' or similar, and if the
// user editor isn't set, it should send back the available editors
getUserEditor((result: GetUserEditorResult) => {
state.setIsLoadingEditor(false)
if (result.preferredOpener) {
return openFile(result.preferredOpener, fileDetails)
}
state.setEditors(result.availableEditors || [])
state.setIsModalOpen(true)
})
}
const setEditor = (editor: Editor) => {
setUserEditor(editor)
state.setIsModalOpen(false)
state.setChosenEditor({} as Editor)
openFile(editor, fileDetails)
}
return (
<a className={className} onClick={attemptOpenFile} href='#'>
{children}
<EditorPickerModal
chosenEditor={state.chosenEditor}
editors={state.editors}
isOpen={state.isModalOpen}
onSetEditor={setEditor}
onSetChosenEditor={state.setChosenEditor}
onClose={_.partial(state.setIsModalOpen, false)}
/>
</a>
)
})
export default FileOpener

View File

@@ -1,13 +0,0 @@
export { default as BrowserIcon } from './browser-icon'
export { default as Dropdown } from './dropdown'
export { default as EditorPicker } from './file-opener/editor-picker'
export { default as EditorPickerModal } from './file-opener/editor-picker-modal'
export { default as FileOpener } from './file-opener/file-opener'
export * from './file-opener/file-model'
export * from './select'

View File

@@ -1,17 +0,0 @@
import _ from 'lodash'
import { createContext } from 'react'
import type { KeyboardEvent } from 'react'
interface ContextValue {
handleChange: (value: string) => any
handleKeyDown: (event: KeyboardEvent) => any
isSelected: (value: string) => boolean
name: string
}
export default createContext<ContextValue>({
handleChange: _.noop,
handleKeyDown: _.noop,
isSelected: () => false,
name: '',
})

View File

@@ -1,5 +0,0 @@
export { default as SelectItem } from './select-item'
export { default as Select } from './select-group'
export { default as useSelect } from './use-select'

View File

@@ -1,78 +0,0 @@
import cs from 'classnames'
import React, { Children, KeyboardEvent, ReactElement, ReactNode, useCallback, useMemo } from 'react'
import _ from 'lodash'
import Context from './context'
const generateGroupName = (name?: string) => {
if (name) {
return name
}
return _.uniqueId('Select-')
}
const toValues = (children: ReactNode[]) => {
const withSelectItem = _.filter(children, (child: ReactElement) => {
return child.props.selectItem
})
return _.map<any, string>(withSelectItem, (child: ReactElement) => child.props.value)
}
const left = 13
const up = 38
const right = 39
const down = 40
interface Props {
children: ReactNode
className?: string
name?: string
onChange?: (value: string) => any
value: string
}
const Select = ({ children, className, name, onChange = _.noop, value }: Props) => {
const allValues = useMemo(() => toValues(Children.toArray(children)), [children])
const handleKeyDown = useCallback(({ keyCode }: KeyboardEvent) => {
if (![left, up, right, down].includes(keyCode)) {
return
}
const currentIndex = _.findIndex(allValues, (v) => v === value)
if (currentIndex === -1) {
onChange(allValues[0])
return
}
if ([left, up].includes(keyCode)) {
const incrementedIndex = currentIndex - 1
const newIndex = incrementedIndex < 0 ? (Math.abs(incrementedIndex) + allValues.length - 2) : (incrementedIndex) % (allValues.length)
onChange(allValues[newIndex])
} else if ([right, down].includes(keyCode)) {
const newIndex = (currentIndex + 1) % (allValues.length)
onChange(allValues[newIndex])
}
}, [allValues, value])
return (
<Context.Provider value={{
handleChange: onChange,
handleKeyDown,
isSelected: (v) => v === value,
name: generateGroupName(name),
}}>
<ul className={cs('select', className)}>
{children}
</ul>
</Context.Provider>
)
}
export default Select

View File

@@ -1,63 +0,0 @@
import cs from 'classnames'
import React, { KeyboardEvent, ReactNode, useRef } from 'react'
import { partial, uniqueId } from 'lodash'
import VisuallyHidden from '@reach/visually-hidden'
import useSelect from './use-select'
interface Props {
value: string
children?: ReactNode
selectItem?: boolean
}
const SelectItem = ({ value, children, ...rest }: Props) => {
const { name, handleChange, handleKeyDown, isSelected } = useSelect()
const liRef = useRef(null)
const inputRef = useRef(null)
const id = uniqueId('select-item-')
const onKeyDown = (e: KeyboardEvent) => {
// ensure it's not an element in children that's being keyed down
if (e.target && e.target !== liRef.current && e.target !== inputRef.current) {
return
}
handleKeyDown(e)
}
return (<li
className={cs('select-item', { 'is-selected': isSelected(value) })}
ref={liRef}
onClick={partial(handleChange, value)}
onKeyDown={onKeyDown}
data-value={value}
>
<label htmlFor={id}>
<i className='select-item-indicator fa' />
<VisuallyHidden>
<input
{...rest}
id={id}
ref={inputRef}
checked={isSelected(value)}
onChange={partial(handleChange, value)}
onKeyDown={onKeyDown}
name={name}
type='radio'
value={value}
// style required to prevent an error from being thrown
// when used inside of a dialog (ex. editor picker modal)
style={{ margin: 0 }}
/>
</VisuallyHidden>
{children}
</label>
</li>)
}
SelectItem.defaultProps = {
selectItem: true,
}
export default SelectItem

View File

@@ -1,42 +0,0 @@
.select-item {
border-radius: 3px;
border: solid 2px transparent;
cursor: pointer;
margin: 0.2em 0;
padding: 0.2em 0.4em;
label {
cursor: pointer;
}
.select-item-indicator {
@extend .far;
@extend .#{$fa-css-prefix}-circle;
color: #999;
margin-right: 0.5em;
position: relative;
top: 3px;
}
&:hover,
&:focus {
background: #fff;
.select-item-indicator {
@extend .fas;
@extend .#{$fa-css-prefix}-circle;
color: #999;
}
}
&.is-selected {
background: #fff;
border-color: #08c18d;
.select-item-indicator {
@extend .fas;
@extend .#{$fa-css-prefix}-circle;
color: #08c18d;
}
}
}

View File

@@ -1,4 +0,0 @@
import { useContext } from 'react'
import context from './context'
export default () => useContext(context)

View File

@@ -1,57 +0,0 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es2015",
"module": "commonjs",
/*
* Allow javascript files to be compiled.
* Override this in modules that need JS
*/
"allowJs": true,
"jsx": "react",
"noImplicitAny": false,
"noImplicitThis": false,
"preserveWatchOutput": true,
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
/* Generates corresponding '.d.ts' file. */
// "declaration": true,
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
/* Generates corresponding '.map' file. */
"sourceMap": true,
/* Import emit helpers from 'tslib'. */
"importHelpers": true,
"strictNullChecks": true,
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
// "traceResolution": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
/**
* Skip type checking of all declaration files (*.d.ts).
* TODO: Look into changing this in the future
*/
/* Additional Checks */
/* Report errors on unused locals. */
// "noEmit": true,
"noUnusedLocals": false,
// "noUnusedParameters": true, /* Report errors on unused parameters. */
/* Report error when not all code paths in function return a value. */
"noImplicitReturns": true,
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "../", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": ["../driver/src"], /* List of root folders whose combined content represents the structure of the project at runtime. */
"types": [], /* Type declaration files to be included in compilation. */
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"noErrorTruncation": true,
"experimentalDecorators": true,
},
"exclude": [
"dist"
]
}

View File

@@ -1,26 +0,0 @@
import { getCommonConfig, HtmlWebpackPlugin } from '@packages/web-config/webpack.config.base'
import path from 'path'
import webpack from 'webpack'
// @ts-ignore
const config: webpack.Configuration = {
...getCommonConfig(),
entry: {
components: [path.resolve(__dirname, 'cypress', 'support', 'test-entry.jsx')],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
devtoolModuleFilenameTemplate: 'cypress://[namespace]/[resource-path]',
},
}
config.plugins = [
// @ts-ignore
...config.plugins,
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'cypress/support/test-entry.html'),
}),
]
export default config

View File

@@ -32,6 +32,5 @@ export const monorepoPaths = {
pkgSocket: path.join(__dirname, '../../packages/socket'),
pkgTs: path.join(__dirname, '../../packages/ts'),
pkgTypes: path.join(__dirname, '../../packages/types'),
pkgUiComponents: path.join(__dirname, '../../packages/ui-components'),
pkgWebConfig: path.join(__dirname, '../../packages/web-config')
} as const

View File

@@ -2406,7 +2406,7 @@
check-more-types "2.24.0"
lazy-ass "1.6.0"
"@cypress/react-tooltip@0.5.3", "@cypress/react-tooltip@^0.5.3":
"@cypress/react-tooltip@0.5.3":
version "0.5.3"
resolved "https://registry.yarnpkg.com/@cypress/react-tooltip/-/react-tooltip-0.5.3.tgz#3e0635304b2bf7dab5b7c251eb1ad23048b05dac"
integrity sha512-5/CkKmEtirHAsMh8B6vdzcRHVAYsspviQhv8aw4S+DM/RjAdxALiof8nw941GfmWzgu8qDXGkxgKvv3oxZrfNw==
@@ -5418,6 +5418,14 @@
"@percy/client" "1.2.0"
"@percy/logger" "1.2.0"
"@percy/client@1.10.4":
version "1.10.4"
resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.10.4.tgz#558ec16d8780d6513881da8550d453e390571d63"
integrity sha512-TQq4TOL86cXZUoLhz4mje0OAvQtxjNZIpYLvhJ5ekOdFrBuU5xXVegXjAQRTN90SokPT80/lPfRVwQgsaBaXSw==
dependencies:
"@percy/env" "1.10.4"
"@percy/logger" "1.10.4"
"@percy/client@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.2.0.tgz#d7d7f06c98fbd1d7c6ecf2a3200080d9fdae6595"
@@ -5426,13 +5434,15 @@
"@percy/env" "1.2.0"
"@percy/logger" "1.2.0"
"@percy/client@1.8.1":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.8.1.tgz#bbc81c2a41ab44e5f79c93aac6afa68c17a90af8"
integrity sha512-2T0SdIUFwMCsnaUTM0hNQx5lrkg2qPHPKBL/MRrbbTPJVJe3J9PzeR84RJtKviyy+ciEkkm6UaQsWBngbyXXsQ==
"@percy/config@1.10.4":
version "1.10.4"
resolved "https://registry.yarnpkg.com/@percy/config/-/config-1.10.4.tgz#8df1d07f718e5ba377cd4acc6da6df5c5933ce2f"
integrity sha512-K0p4fKE77jsXWaNJIOP61IbGaA4KHbGXuqchHrFAsxh8HsdzadntFsTkXxtyS6eu6v4kfeLo0j25Mq6xkgQ5gQ==
dependencies:
"@percy/env" "1.8.1"
"@percy/logger" "1.8.1"
"@percy/logger" "1.10.4"
ajv "^8.6.2"
cosmiconfig "^7.0.0"
yaml "^2.0.0"
"@percy/config@1.2.0":
version "1.2.0"
@@ -5444,16 +5454,6 @@
cosmiconfig "^7.0.0"
yaml "^2.0.0"
"@percy/config@1.8.1":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@percy/config/-/config-1.8.1.tgz#46d73b8cdb0e1d7400e8d91fe12e9f6ea08d2b31"
integrity sha512-yKdKx0kh5xyVxBdNVExXsoNuSmKcmpma31DkqqacDUu2nYSjuCGxr3j3y8BRAKURfj59fdhwpFNmjVVB1xiVWA==
dependencies:
"@percy/logger" "1.8.1"
ajv "^8.6.2"
cosmiconfig "^7.0.0"
yaml "^2.0.0"
"@percy/core@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.2.0.tgz#aee388447faf3dac0f829d450cb2db24b7fa5f47"
@@ -5474,14 +5474,14 @@
ws "^8.0.0"
"@percy/core@^1.0.0-beta.48":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.8.1.tgz#b1e419da98251b370cdcea436bb702ee3461d203"
integrity sha512-wwTSJWoKK8tr8AbORlJGJ7uz55v8oUdOrfZtCnRPssWiceaWFXecRbjLXcNCCz0/2nT6Y1ilDsEOjlExYnKhww==
version "1.10.4"
resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.10.4.tgz#65fd447e19f2cb870880ab97575bbcb4012b9d50"
integrity sha512-7Fu9h6XjMNjJF0RDft0GQ6A3uo1SQip+x8yp1oTF3K4qoKywc28EnfPyGeQ83Jju40cu1z6VzjnvnyIWK3/B6Q==
dependencies:
"@percy/client" "1.8.1"
"@percy/config" "1.8.1"
"@percy/dom" "1.8.1"
"@percy/logger" "1.8.1"
"@percy/client" "1.10.4"
"@percy/config" "1.10.4"
"@percy/dom" "1.10.4"
"@percy/logger" "1.10.4"
content-disposition "^0.5.4"
cross-spawn "^7.0.3"
extract-zip "^2.0.1"
@@ -5492,43 +5492,43 @@
rimraf "^3.0.2"
ws "^8.0.0"
"@percy/cypress@^3.1.0", "@percy/cypress@^3.1.1":
"@percy/cypress@^3.1.0":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@percy/cypress/-/cypress-3.1.1.tgz#4e7c5bdeccf1240b2150fc9d608df72c2f213d4b"
integrity sha512-khvWmCOJW7pxwDZPB5ovvbSe11FfNtH8Iyq8PHRYLD9ibAkiAWHZVs07bLK5wju1Q9X8s7zg5uj2yWxIlB1yjA==
dependencies:
"@percy/sdk-utils" "^1.0.0-beta.44"
"@percy/dom@1.10.4":
version "1.10.4"
resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.10.4.tgz#c8c6227d6e074547e309da0563fb485ca5d2fb3a"
integrity sha512-EevExMWUKvBFe2UvXuskJCoj8Xc28PeX60ktSRvc7Z68wSQZmE2hlu8mfnkQ6KSDyO96duBPrKWJn9EeYFvIWg==
"@percy/dom@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.2.0.tgz#c59aae392539cc493145daa928c8d43cfbfdd819"
integrity sha512-OThOEuEyczxToc/+Df/aHDFY0O5balTG1w0k91wyZCNEIjD2nhwiAj09m28SmybNpq6TC6j0qnuPTD9rk2vgDw==
"@percy/dom@1.8.1":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.8.1.tgz#b3daa0f6f8d95a5b72df4edfdca615ed04f14f93"
integrity sha512-h9XJV+VcVHrMkfIgJ6sJLujtYLzkvRy3aBvUY/iS7yh88qM16pSuTgEHG9fzbOp8ZNZpu6zZdxy4QmeACqzGzw==
"@percy/env@1.10.4":
version "1.10.4"
resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.10.4.tgz#1ba30add5920703e44314d680d469671390d8acd"
integrity sha512-11xPV2/yNga+2RZnTkleIdcpqqb4WGNUBhdjMds/45YQJXX1ZbtzGi8eU/UPEHYCeY7L6IZlatIyaE50wZg/Jw==
"@percy/env@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.2.0.tgz#22ec45884f2e7c6d962237c9350ab16625e4e196"
integrity sha512-+In1gN8ArNMwyFj8tZMEpqI8CAoLeuZpa99DPP4QNqyNW1+mUEzYobqBJ8VrJDBWqCayWtuxiofQO+ihReBo0Q==
"@percy/env@1.8.1":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.8.1.tgz#338d87479dc48fc2ca3e0a66c98f0b5235f322c2"
integrity sha512-noxlV3fesivvXxnWEODUzdoToIurOolvwlEk+ETmpB933zKTpo55UFD5leV5LWP5Oxwrv4uen2i7ZdHRXPD47Q==
"@percy/logger@1.10.4", "@percy/logger@^1.0.0-beta.48":
version "1.10.4"
resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.10.4.tgz#a95532c558bc6ea73c0dd99778c1963871733369"
integrity sha512-8rUE5hhwIRoPAdA3Osh4+dkVbXE6q4Pn7xyt63NLoFHt9JR2H/iFowsaetkCCHa6VKKfGMjXm04hmrP2o0vUWw==
"@percy/logger@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.2.0.tgz#4739e57df77db1377a67be1d2a34575a68718f4a"
integrity sha512-UfCJA2jRBwBqGdCNKlNK2Y5QYf0h5dsI56GdYO97hCv1GOj29X+1xQsfYFTbocTR5EmoBsS41sErBZ1aVBtDng==
"@percy/logger@1.8.1", "@percy/logger@^1.0.0-beta.48":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.8.1.tgz#91e1fb3ec2952dfd170201ab101aa1a7f505c778"
integrity sha512-O1GpuuN6pzBk/dso57LS90APbRNfc4y0qsGEIph+8f1zK6dBZmalwKLzhgrvb267rLe3pduYM5fCiYel0pqh7Q==
"@percy/sdk-utils@^1.0.0-beta.44":
version "1.0.0-beta.48"
resolved "https://registry.yarnpkg.com/@percy/sdk-utils/-/sdk-utils-1.0.0-beta.48.tgz#47e59d92c7df19fe13faf5458702fd2b9a0e0b76"
@@ -5582,13 +5582,6 @@
tslib "^2.0.0"
warning "^4.0.3"
"@reach/visually-hidden@0.10.4":
version "0.10.4"
resolved "https://registry.yarnpkg.com/@reach/visually-hidden/-/visually-hidden-0.10.4.tgz#ab390db0adf759393af4d856f84375468b1df676"
integrity sha512-GnuPuTRCf+Ih47BoKvGyB+jP8EVWLb04GfbGa5neOrjdp90qrb4zr7pMSL4ZvTsrxt9MRooJA2BhSxs5DbyqCQ==
dependencies:
tslib "^2.0.0"
"@rollup/plugin-commonjs@^17.1.0":
version "17.1.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz#757ec88737dffa8aa913eb392fade2e45aef2a2d"
@@ -10859,8 +10852,8 @@ brorand@^1.0.1, brorand@^1.1.0:
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
"browser-logos@github:alrra/browser-logos":
version "69.0.4"
resolved "https://codeload.github.com/alrra/browser-logos/tar.gz/90fdf03c58819fd31892869712feb7d614d79a96"
version "72.0.0"
resolved "https://codeload.github.com/alrra/browser-logos/tar.gz/6e3e6a8da0dc8ec9851a6987fd9bd3523fe1876c"
browser-process-hrtime@^1.0.0:
version "1.0.0"
@@ -23830,13 +23823,6 @@ mobx-react-lite@^1.4.2:
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-1.5.2.tgz#c4395b0568b9cb16f07669d8869cc4efa1b8656d"
integrity sha512-PyZmARqqWtpuQaAoHF5pKX7h6TKNLwq6vtovm4zZvG6sEbMRHHSqioGXSeQbpRmG8Kw8uln3q/W1yMO5IfL5Sg==
mobx-react@6.1.7:
version "6.1.7"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.1.7.tgz#2fe620851ef33df38e50645101321830a07fc4e2"
integrity sha512-TXwqweY7yEbMl+0F5jOO5bHWUbgeNJr8o1or+1NdhWWg6duIqsGW1D6ojNmn2RO27vRJHkZrp4kjv6919nHS0A==
dependencies:
mobx-react-lite "^1.4.2"
mobx-react@6.1.8:
version "6.1.8"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.1.8.tgz#5b9a3950463d58c154dda2c94d16d48ed8fcb02e"