feat(launchpad): Directory Upload in Global Mode (#18165)

* small html tweaks

* add basic implementation of vue3-file-selector

* add test for file upload

* update readme to reflect latest

* Update packages/launchpad/src/global/GlobalEmpty.vue

Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>

* HTML tweaks

* add cypress-file-upload types

Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
This commit is contained in:
Mark Noonan
2021-09-21 21:19:37 -04:00
committed by GitHub
parent 2b57a2600c
commit 3b8828907d
9 changed files with 72 additions and 30 deletions
@@ -8,6 +8,7 @@ import { Component, computed, watch, defineComponent, h } from 'vue'
import { ClientTestContext } from '../../src/graphql/ClientTestContext'
import type { TestSourceTypeLookup } from '@packages/graphql/src/testing/testUnionType'
import { createI18n } from '@packages/launchpad/src/locales/i18n'
import 'cypress-file-upload'
/**
* This variable is mimicing ipc provided by electron.
+1 -1
View File
@@ -48,7 +48,7 @@
// "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. */
// "typeRoots": [] /* List of folders to include type definitions from. */
"types": ["chrome", "./vue-shims", "./vite-env", "vite-plugin-icons", "@intlify/vite-plugin-vue-i18n/client", "@testing-library/cypress"], /* Type declaration files to be included in compilation. */
"types": ["chrome", "./vue-shims", "./vite-env", "vite-plugin-icons", "@intlify/vite-plugin-vue-i18n/client", "@testing-library/cypress", "cypress-file-upload"], /* Type declaration files to be included in compilation. */
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"noErrorTruncation": true,
+2 -10
View File
@@ -36,21 +36,13 @@ yarn workspace @packages/launchpad build
For the best development experience, you will want to use VS Code with the [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) extension. This will give you type completion inside `vue` files.
You probably want to start Vite in watch mode:
```bash
## from repo root
yarn workspace @packages/launchpad watch
yarn dev
```
The start the application:
```bash
## from repo root
yarn workspace @packages/launchpad dev
```
This also starts the GraphQL Server. You can access it on `http://localhost:52159/graphql`.
This starts Vite in watch mode. It also starts the GraphQL Server. You can access it on `http://localhost:52159/graphql`.
![graphql](../graphql/gql.png)
-1
View File
@@ -3,7 +3,6 @@
"baseUrl": "http://localhost:5555",
"viewportWidth": 800,
"viewportHeight": 850,
"fixturesFolder": false,
"video": false,
"retries": {
"runMode": 2,
@@ -0,0 +1,3 @@
{
"purpose": "This is a file to test a file input."
}
+2
View File
@@ -36,6 +36,7 @@
"classnames": "2.3.1",
"concurrently": "^6.2.0",
"cross-env": "6.0.3",
"cypress-file-upload": "^5.0.8",
"graphql": "^15.5.1",
"graphql-tag": "^2.12.5",
"javascript-time-ago": "2.3.8",
@@ -52,6 +53,7 @@
"vue-prism-component": "2.0.0",
"vue-prism-editor": "^2.0.0-alpha.2",
"vue-tsc": "^0.3.0",
"vue3-file-selector": "^1.0.1",
"windicss": "3.1.4",
"wonka": "^4.0.15"
},
@@ -4,19 +4,27 @@ import GlobalEmpty from './GlobalEmpty.vue'
const emptyText = defaultMessages.globalPage.empty
describe('<GlobalEmpty />', () => {
it('renders the empty state', () => {
beforeEach(() => {
cy.mount(() => (<div
class="p-12 min-w-280px max-w-650px overflow-auto resize-x">
<GlobalEmpty />
</div>))
})
it('renders the empty state', () => {
cy.contains(emptyText.title)
cy.contains(emptyText.helper)
const parts = emptyText.dropText.split('{0}')
cy.contains(parts[0])
cy.contains(emptyText.browseManually)
cy.get('input[type=file]').should('have.length', 1)
})
it('handles a file upload', () => {
cy.get('input[type=file]').attachFile('project-upload.json')
cy.get('[data-testid=upload-name]').should('have.text', 'project-upload.json')
})
})
+43 -16
View File
@@ -1,29 +1,56 @@
<template>
<main class="text-center">
<main class="text-center" ref="projectUpload">
<h1 class="text-2rem mb-2">{{ t('globalPage.empty.title') }}</h1>
<p class="text-lg font-light text-gray-600 mb-6">{{ t('globalPage.empty.helper') }}</p>
<button
type="button"
@click="selectProject"
class="min-w-220px relative block w-full border-2 bg-gray-50 border-gray-300 border-dashed rounded-lg p-12 text-center hover:border-gray-400 text-center"
>
<IconPlaceholder
class="-mb-8px mx-auto max-w-65px h-full relative justify-center w-full text-indigo-600"
/>
<span class="mt-13px block text-lg font-medium text-gray-700 font-light">
<i18n-t keypath="globalPage.empty.dropText">
<a class="font-normal" @click="selectProject">{{ t('globalPage.empty.browseManually') }}</a>
</i18n-t>
</span>
</button>
<FileSelector v-model="files" v-slot="{ openDialog }" allow-multiple >
<Dropzone v-slot="{ hovered }" @click="openDialog">
<div
class="min-w-220px relative block w-full border-2 bg-gray-50 border-gray-300 border-dashed rounded-lg p-12 text-center hover:border-gray-400 text-center"
:class="{ 'border-blue-200': hovered }"
>
<IconPlaceholder
class="mx-auto max-w-65px h-full relative justify-center w-full text-indigo-600"
/>
<i18n-t keypath="globalPage.empty.dropText">
<button
class="text-indigo-600 hover:underline"
>
<!--
This button allows keyboard users to fire a click event with the Enter or Space keys,
which will be handled by the dropzone's existing click handler.
-->
{{ t('globalPage.empty.browseManually') }}</button>
</i18n-t>
</div>
</Dropzone>
</FileSelector>
<div data-testid="upload-name" class="hidden">{{uploadName}}</div>
</main>
</template>
<script lang="ts" setup>
import { useI18n } from "../composables"
import IconPlaceholder from 'virtual:vite-icons/icons8/circle-thin'
import { FileSelector, Dropzone } from 'vue3-file-selector'
import { ref, watch, onMounted } from 'vue'
const { t } = useI18n()
const files = ref<File[]>([])
const uploadName = ref('')
const projectUpload = ref<HTMLDivElement>()
const selectProject = (file: File) => { uploadName.value = file.name}
watch(files, (newVal) => {
const uploadLength = newVal.length;
const latestUpload = newVal[uploadLength - 1]
selectProject(latestUpload)
})
onMounted(() => {
// TODO: remove this when vue3-file-selector supports setting this attribute
projectUpload.value?.querySelector('input[type=file]')?.setAttribute('webkitdirectory', 'webkitdirectory')
})
const selectProject = () => { }
</script>
+10
View File
@@ -16747,6 +16747,11 @@ cypress-expect@2.0.0:
arg "4.1.3"
debug "4.2.0"
cypress-file-upload@^5.0.8:
version "5.0.8"
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1"
integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==
cypress-image-snapshot@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/cypress-image-snapshot/-/cypress-image-snapshot-3.1.1.tgz#cb7242d8086e0d4cbb7f333f927f71cdb5ff9971"
@@ -41372,6 +41377,11 @@ vue-tsc@^0.3.0:
dependencies:
vscode-vue-languageservice "^0.27.0"
vue3-file-selector@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vue3-file-selector/-/vue3-file-selector-1.0.1.tgz#bcae2f5ab44c406c1d72a60885990883051b688b"
integrity sha512-popFgEvLrkRFo9MWs8mzlb4HH+Mg2+5DhJF7MzKmUrE9179rtVt4Wf7/w+0FvhDRVELQ6f8Z9BhF+SDSUSpRVw==
vue@3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.6.tgz#c71445078751f458648fd8fb3a2da975507d03d2"