Skip to content

Commit 2ed60ff

Browse files
authored
Clarify data not uploaded (#1179)
- Move lock icon around and make it more visible - Remove as many visual elements as possible to give focus to the padlock - Add a folder icon for local file selection - List files when uploaded
1 parent bdd3a05 commit 2ed60ff

13 files changed

Lines changed: 108 additions & 96 deletions

File tree

docs/CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ pnpm -F webapp test
104104

105105
The webapp tests rely on `cypress` and the test suite is located in the `webapp/cypress` folder.
106106

107-
Note that you can also run test interactively in the browser of your choice. To do so, run
107+
Note that you can also run test interactively in the browser of your choice. To do so, run from the `webapp` folder
108108

109109
```
110-
VITE_SERVER_URL=http://server npx -w webapp start-server-and-test start http://localhost:8081 'cypress open --e2e'
110+
pnpm run test:e2e-interactive
111111
```
112112

113113
which should open the Cypress UI and let you choose the browser you wand to use and which tests to run. More information on [the Cypress docs](https://docs.cypress.io/app/get-started/open-the-app).

webapp/cypress/e2e/datasetInput.cy.ts

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,14 @@ describe("image dataset input by group", () => {
3939

4040
goToDatasetInputStep();
4141
cy.get("button").contains("group").click();
42-
cy.contains("select images").selectFile([
43-
{ fileName: "first.png", contents: new Uint8Array() },
44-
{ fileName: "second.png", contents: new Uint8Array() },
45-
{ fileName: "third.png", contents: new Uint8Array() },
46-
]);
42+
cy.contains("Drop images");
43+
cy.get('[data-testid="select-image-button"]')
44+
.first()
45+
.selectFile([
46+
{ fileName: "first.png", contents: new Uint8Array() },
47+
{ fileName: "second.png", contents: new Uint8Array() },
48+
{ fileName: "third.png", contents: new Uint8Array() },
49+
]);
4750

4851
cy.contains("Number of selected files: 3").should("exist");
4952
});
@@ -61,21 +64,27 @@ describe("image dataset input by csv", () => {
6164

6265
goToDatasetInputStep();
6366
cy.get("button").contains("csv").click();
64-
cy.contains("select CSV").selectFile({
65-
fileName: "csv",
66-
contents: new TextEncoder().encode(
67-
"filename,label\n" +
68-
"first,first\n" +
69-
"second,second\n" +
70-
"third,third\n",
71-
),
72-
});
67+
cy.contains("Drop CSV");
68+
cy.get('[data-testid="select-tabular-button"]')
69+
.first()
70+
.selectFile({
71+
fileName: "csv",
72+
contents: new TextEncoder().encode(
73+
"filename,label\n" +
74+
"first,first\n" +
75+
"second,second\n" +
76+
"third,third\n",
77+
),
78+
});
7379

74-
cy.contains("select images").selectFile([
75-
{ fileName: "first.png", contents: new Uint8Array() },
76-
{ fileName: "second.png", contents: new Uint8Array() },
77-
{ fileName: "third.png", contents: new Uint8Array() },
78-
]);
80+
cy.contains("Drop images");
81+
cy.get('[data-testid="select-image-button"]')
82+
.first()
83+
.selectFile([
84+
{ fileName: "first.png", contents: new Uint8Array() },
85+
{ fileName: "second.png", contents: new Uint8Array() },
86+
{ fileName: "third.png", contents: new Uint8Array() },
87+
]);
7988

8089
cy.contains("Number of selected files: 3").should("exist");
8190
});
@@ -91,10 +100,13 @@ describe("tabular dataset input", () => {
91100
);
92101

93102
goToDatasetInputStep();
94-
cy.contains("select CSV").selectFile({
95-
fileName: "filename",
96-
contents: new TextEncoder().encode("a,b,c\n1,2,3\n"),
97-
});
103+
cy.contains("Drop CSV");
104+
cy.get('[data-testid="select-tabular-button"]')
105+
.first()
106+
.selectFile({
107+
fileName: "filename",
108+
contents: new TextEncoder().encode("a,b,c\n1,2,3\n"),
109+
});
98110

99111
cy.contains("filename").should("exist");
100112
});

webapp/cypress/e2e/testing.cy.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ it("can test titanic", () => {
99
cy.contains("button", "download").click();
1010
cy.contains("button", "test").click();
1111

12-
cy.contains("label", "select CSV").selectFile(
13-
"../datasets/titanic_train.csv",
14-
);
12+
cy.contains("Drop CSV");
13+
cy.get('[data-testid="select-tabular-button"]')
14+
.first()
15+
.selectFile("../datasets/titanic_train.csv");
1516
cy.contains("button", "next").click();
1617

1718
cy.contains("Validate your model")
18-
.parent()
19-
.parent()
19+
.parents()
20+
.eq(1)
2021
.contains("button", "test")
2122
.click();
2223

@@ -31,13 +32,13 @@ it("can test lus_covid", () => {
3132
cy.contains("button", "test").click();
3233

3334
cy.task<string[]>("readdir", "../datasets/lus_covid/COVID+/").then((files) =>
34-
cy.contains("label", "select images").selectFile(files),
35+
cy.get('[data-testid="select-image-button"]').first().selectFile(files),
3536
);
3637
cy.contains("button", "next").click();
3738

3839
cy.contains("Validate your model")
39-
.parent()
40-
.parent()
40+
.parents()
41+
.eq(1)
4142
.contains("button", "test")
4243
.click();
4344

@@ -51,14 +52,14 @@ it("can start and stop testing of wikitext", () => {
5152
cy.contains("button", "download").click();
5253
cy.contains("button", "test").click();
5354

54-
cy.contains("label", "select text").selectFile(
55-
"../datasets/wikitext/wiki.test.tokens",
56-
);
55+
cy.get('[data-testid="select-text-button"]')
56+
.first()
57+
.selectFile("../datasets/wikitext/wiki.test.tokens");
5758
cy.contains("button", "next").click();
5859

5960
cy.contains("Validate your model")
60-
.parent()
61-
.parent()
61+
.parents()
62+
.eq(1)
6263
.contains("button", "test")
6364
.click();
6465
cy.contains("button", "stop testing").click();

webapp/cypress/e2e/training.cy.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ describe("training page", () => {
3030
goToTaskOverview();
3131
cy.contains("button", "next").click();
3232

33-
cy.contains("label", "select CSV").selectFile(
34-
"../datasets/titanic_train.csv",
35-
);
33+
cy.contains("Drop CSV");
34+
cy.get('[data-testid="select-tabular-button"]')
35+
.first()
36+
.selectFile("../datasets/titanic_train.csv");
3637
cy.contains("button", "next").click();
3738

3839
cy.contains("button", "locally").click();
@@ -61,15 +62,17 @@ describe("training page", () => {
6162
cy
6263
.contains("h4", "COVID-Positive")
6364
.parents()
64-
.contains("select images")
65+
.eq(1)
66+
.find('[data-testid="select-image-button"]')
6567
.selectFile(files),
6668
);
6769
cy.task<string[]>("readdir", "../datasets/lus_covid/COVID-/").then(
6870
(files) =>
6971
cy
7072
.contains("h4", "COVID-Negative")
7173
.parents()
72-
.contains("select images")
74+
.eq(1)
75+
.find('[data-testid="select-image-button"]')
7376
.selectFile(files),
7477
);
7578
cy.contains("button", "next").click();

webapp/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"build": "vue-tsc --build && vite build",
88
"test": "pnpm run test:unit && pnpm run test:e2e",
99
"test:unit": "vitest --run",
10-
"test:e2e": "VITE_SERVER_URL=http://server start-server-and-test \"pnpm start\" http://localhost:1351 'pnpm exec cypress run --e2e'"
10+
"test:e2e": "VITE_SERVER_URL=http://server start-server-and-test \"pnpm start\" http://localhost:1351 'pnpm exec cypress run --e2e'",
11+
"test:e2e-interactive": "VITE_SERVER_URL=http://server start-server-and-test \"pnpm start\" http://localhost:1351 'pnpm exec cypress open --e2e'"
1112
},
1213
"dependencies": {
1314
"@epfml/discojs": "workspace:",

webapp/src/components/dataset_input/DatasetInput.vue

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,6 @@
33
<template #title> Connect your data </template>
44
<template #icon> <PlugIcon /> </template>
55

6-
<div class="mb-5 text-left">
7-
Disco needs to know where your data is located on your device in order to
8-
read it (<b>not</b> upload it!) and train models. Models are trained on
9-
your local data and are periodically aggregated with other users' models
10-
if any.
11-
<br />
12-
<br />
13-
<b>
14-
Your data stays on your device and data is never uploaded anywhere.
15-
</b>
16-
</div>
176
<slot />
187
</IconCard>
198
</template>

webapp/src/components/dataset_input/FileSelection.vue

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@
88
>
99
<section>
1010
<!-- Hide the file input field when already submitted-->
11+
<div v-if="noUpload" class="flex justify-center items-center mb-4">
12+
<span
13+
v-tippy="{
14+
content:
15+
'Your files are read locally in your browser, who can only read them if you manually select them first. They are never sent to our servers or anyone else\'s. To convince yourself, you can start a training session (without collaboration) while turning off your machine\'s wifi !',
16+
}"
17+
class="bg-green-200 text-green-800 dark:text-green-200 dark:bg-green-800 rounded-full hover:cursor-pointer px-2 py-1"
18+
>
19+
<i class="fa fa-lock mr-1" />
20+
<span> Stays on your device </span>
21+
</span>
22+
</div>
1123
<div
1224
v-if="!hideConnectField"
1325
class="border-dashed rounded-xl border-disco-cyan flex flex-col justify-center items-center min-h-48"
@@ -19,15 +31,16 @@
1931
@drop="(e: DragEvent) => dragFiles(e)"
2032
>
2133
<p
22-
class="p-4 text-lg text-disco-blue dark:text-disco-light-blue flex-wrap justify-center"
34+
class="p-4 text-lg text-disco-blue dark:text-white flex-wrap justify-center"
2335
>
24-
<span>Drag and drop the {{ fileType }} or</span>
36+
<span>Drop {{ fileType }} here or</span>
2537
</p>
26-
<label class="mb-6">
38+
<label class="mb-6" :data-testid="`select-${props.type}-button`">
2739
<span
28-
class="px-4 py-2 min-w-32 text-lg uppercase text-white bg-disco-cyan rounded-sm duration-200 hover:bg-white dark:hover:bg-slate-950 hover:outline-solid hover:outline-disco-cyan hover:outline-2 hover:text-disco-cyan hover:cursor-pointer"
40+
class="px-4 py-2 min-w-32 text-lg capitalize text-white bg-disco-cyan font-disco rounded-full duration-200 hover:bg-transparent dark:hover:bg-transparent hover:outline-solid hover:outline-2 hover:outline-disco-cyan dark:hover:outline-disco-light-cyan hover:text-disco-cyan dark:hover:text-disco-light-cyan"
2941
>
30-
select {{ fileType }}
42+
<i v-if="noUpload" class="fas fa-folder-open mr-2" />
43+
<span>select</span>
3144
</span>
3245
<input
3346
ref="inputFileElement"
@@ -40,20 +53,6 @@
4053
/>
4154
</label>
4255
</div>
43-
<!-- Display what has been connected -->
44-
<div
45-
v-if="files === undefined && lockIcon"
46-
class="flex justify-end items-center mt-1"
47-
>
48-
<span
49-
v-tippy="{
50-
content: 'Data always stays on your device and is never shared.',
51-
}"
52-
class="hover:cursor-pointer"
53-
>
54-
<i class="fa fa-lock mr-1" />
55-
</span>
56-
</div>
5756

5857
<!-- Display some text if specified -->
5958
<div
@@ -73,13 +72,16 @@
7372
class="pt-4 flex flex-col items-center pb-5"
7473
>
7574
<div
76-
class="mb-4 flex justify-center items-center text-center md:text-left sm:text-lg text-disco-blue dark:text-disco-light-cyan"
75+
class="flex justify-center items-center text-center md:text-left sm:text-lg text-disco-blue dark:text-white"
7776
>
78-
<span v-if="multiple"
79-
>Number of selected files:
80-
<span class="pl-1 text-xl">{{ files.size }}</span></span
81-
>
82-
<span v-else class="pl-1">{{ files.first()?.name ?? "none" }}</span>
77+
<i v-if="noUpload" class="fas fa-folder-open mr-2" />
78+
<span v-if="multiple">
79+
Number of selected files:
80+
<span class="text-xl">{{ files.size }}</span>
81+
</span>
82+
</div>
83+
<div class="flex flex-col py-4">
84+
<span v-for="(name, i) in fileNamesDisplay" :key="i">{{ name }}</span>
8385
</div>
8486
<div>
8587
<CustomButton @click="clearFiles">
@@ -101,11 +103,11 @@ const props = withDefaults(
101103
defineProps<{
102104
type: "image" | "json" | "tabular" | "text";
103105
multiple?: boolean; // accept one or multiple files
104-
lockIcon?: boolean;
106+
noUpload?: boolean;
105107
}>(),
106108
{
107109
multiple: false,
108-
lockIcon: false,
110+
noUpload: false,
109111
},
110112
);
111113
@@ -139,6 +141,7 @@ const fileType = computed(() => {
139141
140142
return `${name}${props.multiple ? "s" : ""}`;
141143
});
144+
142145
const acceptFilter = computed(() => {
143146
switch (props.type) {
144147
case "image":
@@ -156,6 +159,16 @@ const acceptFilter = computed(() => {
156159
throw new TypeError("invalid value");
157160
});
158161
162+
const fileNamesDisplay = computed(() => {
163+
if (!files.value) return "";
164+
const arr = files.value.map((f) => f.name);
165+
if (arr.size < 5) {
166+
return arr;
167+
} else {
168+
return [...arr.slice(0, 3), "...", arr.last()];
169+
}
170+
});
171+
159172
// we use an event counter to test whether the user is dragging a file over the field
160173
// because events are triggered multiple times when hovering of children elements (such as button or text)
161174
const dragEventCount = ref(0);

webapp/src/components/dataset_input/ImageDatasetInput.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<DatasetInput>
3-
<FileSelection v-model="files" type="image" lock-icon multiple />
3+
<FileSelection v-model="files" type="image" no-upload multiple />
44
</DatasetInput>
55
</template>
66

webapp/src/components/dataset_input/LabeledImageDatasetInput/ByCSV.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Connect the CSV file containing a mapping between images and labels
77
</template>
88

9-
<FileSelection v-model="csvFiles" type="tabular" private>
9+
<FileSelection v-model="csvFiles" type="tabular" no-upload>
1010
<b>
1111
The CSV file must contain a header with only two columns (filename,
1212
label) </b
@@ -39,11 +39,10 @@
3939
>
4040
<template #title> Connect the images </template>
4141

42-
<FileSelection v-model="images" type="image" multiple private>
42+
<FileSelection v-model="images" type="image" multiple no-upload>
4343
Drag and drop or browse for the images referenced in the connected CSV
4444
file.
4545
<br />
46-
{{ browsingTip }}
4746
</FileSelection>
4847
</IconCard>
4948
</div>
@@ -64,7 +63,6 @@ import { useToaster } from "@/composables/toaster";
6463
import FileSelection from "../FileSelection.vue";
6564
6665
import type { NamedLabeledImageDataset } from "../types.js";
67-
import { browsingTip } from "./strings.js";
6866
6967
const toaster = useToaster();
7068

webapp/src/components/dataset_input/LabeledImageDatasetInput/ByGroup.vue

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
<div class="grid grid-cols-1 lg:grid-cols-2 cards-gap">
33
<div class="contents">
44
<IconCard v-for="[label, files] in labelsAndFiles" :key="label">
5-
<template #title> Group label:&nbsp;&nbsp;{{ label }} </template>
5+
<template #title> Group label:&nbsp;{{ label }} </template>
66

7-
<FileSelection v-model="files.value" type="image" multiple private>
8-
{{ browsingTip }}
9-
</FileSelection>
7+
<FileSelection v-model="files.value" type="image" multiple no-upload />
108
</IconCard>
119
</div>
1210
</div>
@@ -25,7 +23,6 @@ import IconCard from "@/components/containers/IconCard.vue";
2523
import FileSelection from "../FileSelection.vue";
2624
2725
import type { NamedLabeledImageDataset } from "../types.js";
28-
import { browsingTip } from "./strings.js";
2926
3027
const props = defineProps<{
3128
labels: Set<string>;

0 commit comments

Comments
 (0)