Skip to content

Commit

Permalink
fix(form): last renamings and refactorings after review
Browse files Browse the repository at this point in the history
- use Array.from
- clean up code for file upload
- rename last files
- fix test for cf-field-value to compair correct prop
  • Loading branch information
derrabauke committed Aug 4, 2022
1 parent ea73e28 commit 19970d1
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 50 deletions.
6 changes: 3 additions & 3 deletions packages/form/addon/components/cf-field/input/files.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
{{#each this.files as |file|}}
<li class="uk-text-justify uk-text-middle">
<UkButton
data-test-download-link={{file.name}}
data-test-download-link={{file.id}}
@color="link"
@onClick={{fn this.download file.name}}
@onClick={{fn this.download file.id}}
>
{{file.name}}
</UkButton>
<UkIcon
class="uk-icon-button uk-margin-small-left"
role="button"
@icon="trash"
{{on "click" (fn this.delete file.name)}}
{{on "click" (fn this.delete file.id)}}
/>
</li>
{{/each}}
Expand Down
74 changes: 40 additions & 34 deletions packages/form/addon/components/cf-field/input/files.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { macroCondition, isTesting } from "@embroider/macros";
import Component from "@glimmer/component";
import { queryManager } from "ember-apollo-client";
import fetch from "fetch";
Expand All @@ -16,8 +17,8 @@ export default class CfFieldInputFilesComponent extends Component {
}

@action
async download(fileName) {
if (!fileName) {
async download(fileId) {
if (!fileId) {
return;
}
const answers = await this.apollo.query(
Expand All @@ -28,8 +29,14 @@ export default class CfFieldInputFilesComponent extends Component {
},
"node.value"
);

const { downloadUrl } = answers.find((file) => file.name === fileName);
const { downloadUrl } =
answers.find((file) =>
// the testing graph-ql setup does a base64 encoding of `__typename: fileID`
macroCondition(isTesting())
? file.id === fileId ||
atob(file.id).substring(file.__typename.length + 1) === fileId
: file.id === fileId
) ?? {};
if (downloadUrl) {
window.open(downloadUrl, "_blank");
}
Expand All @@ -38,51 +45,50 @@ export default class CfFieldInputFilesComponent extends Component {
@action
async save({ target }) {
// store the old list of files
const fileList = [...(this.files || [])];
let fileList = [...(this.files || [])];

// unwrap files from FileList construct
const newFiles = [];
for (let i = 0; i < target.files.length; i++) {
newFiles.push({ name: target.files[i].name });
if (
!fileList.find((existing) => existing.name === target.files[i].name)
) {
fileList.push({ name: target.files[i].name });
}
}
if (!newFiles) {
let newFiles = Array.from(target.files).map((file) => ({
name: file.name,
value: file,
}));
// newFiles.forEach((file) => fileList.push({ name: file.name }));

fileList = [...fileList, ...newFiles];

if (newFiles.length === 0) {
return;
}

// trigger save action for deduped file list of old and new files reduces properties
const { filesValue } = await this.args.onSave(
// trigger save action for file list of old and new files with
// reduces properties to match gql format
const { filesValue: savedAnswerValue } = await this.args.onSave(
fileList.map(({ name, id }) => ({ name, id }))
);

try {
// iterate over list of new files and map to graphql answer values
// this way we only upload new files selected by the input
const _newFiles = newFiles.map((file) =>
filesValue.find((value) => file.name === value.name)
);
for await (const file of _newFiles) {
// iterate over list of new files and enrich with graphql answer values
newFiles = newFiles.map((file) => ({
...savedAnswerValue.find(
(value) =>
file.name === value.name &&
!fileList.find((file) => file.id === value.id)
),
value: file.value,
}));

// upload the actual file to data storage
for await (const file of newFiles) {
const response = await fetch(file.uploadUrl, {
method: "PUT",
body: file,
body: file.value,
});

if (!response.ok) {
throw new Error();
}

// replace fileList value with filesValue value
fileList.splice(
fileList.findIndex((f) => f.name === file.name),
1,
file
);
}
this.args.field.answer.value = fileList;
this.args.field.answer.value = savedAnswerValue;
} catch (error) {
await this.args.onSave([]);
this.args.field._errors = [{ type: "uploadFailed" }];
Expand All @@ -93,9 +99,9 @@ export default class CfFieldInputFilesComponent extends Component {
}

@action
async delete(fileName) {
async delete(fileId) {
const remainingFiles = this.files
.filter((file) => file.name !== fileName)
.filter((file) => file.id !== fileId)
.map(({ name, id }) => ({ name, id }));

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default } from "@projectcaluma/ember-form/components/cf-field/input/file";
export { default } from "@projectcaluma/ember-form/components/cf-field/input/files";
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,6 @@ module("Integration | Component | cf-field-value", function (hooks) {

await render(hbs`<CfFieldValue @field={{this.field}} />`);

assert.dom(this.element).hasText(`model:file(${file.id})`);
assert.dom(this.element).hasText(file.name);
});
});
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import { render, triggerEvent, click } from "@ember/test-helpers";
import { faker } from "@faker-js/faker";
import { tracked } from "@glimmer/tracking";
import { hbs } from "ember-cli-htmlbars";
import { setupMirage } from "ember-cli-mirage/test-support";
import { setupIntl } from "ember-intl/test-support";
import { setupRenderingTest } from "ember-qunit";
import { module, test } from "qunit";

module("Integration | Component | cf-field/input/file", function (hooks) {
module("Integration | Component | cf-field/input/files", function (hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);
setupIntl(hooks);
setupIntl(hooks, ["en"]);

test("it computes the proper element id", async function (assert) {
await render(hbs`{{cf-field/input/file field=(hash pk="test-id")}}`);
await render(hbs`<CfField::Input::Files @field={{(hash pk="test-id")}} />`);

assert.dom("#test-id").exists();
});

test("it allows to upload files", async function (assert) {
assert.expect(9);
assert.expect(10);

this.field = new (class {
answer = {
Expand All @@ -33,6 +34,7 @@ module("Integration | Component | cf-field/input/file", function (hooks) {
this.onSave = (files) => ({
filesValue: files?.map((f) => ({
name: f.name,
id: faker.datatype.uuid(),
uploadUrl: `/minio/upload/${f.name}`,
})),
});
Expand All @@ -46,7 +48,7 @@ module("Integration | Component | cf-field/input/file", function (hooks) {
const payload_fail = new File(["test"], "fail.txt", { type: "text/plain" });

await render(
hbs`<CfField::Input::File @field={{this.field}} @onSave={{this.onSave}} />`
hbs`<CfField::Input::Files @field={{this.field}} @onSave={{this.onSave}} />`
);

await triggerEvent("input[type=file]", "change", { files: [] });
Expand All @@ -63,14 +65,16 @@ module("Integration | Component | cf-field/input/file", function (hooks) {
await triggerEvent("input[type=file]", "change", {
files: [payload_good_1],
});
assert.strictEqual(this.field.answer.value[0].name, "good-1.txt");
assert.strictEqual(this.field.answer.value?.[0]?.name, "good-1.txt");
assert.deepEqual(this.field._errors, []);

await triggerEvent("input[type=file]", "change", {
files: [payload_good_1, payload_good_2],
});
assert.strictEqual(this.field.answer.value[0].name, "good-1.txt");
assert.strictEqual(this.field.answer.value[1].name, "good-2.txt");

assert.strictEqual(this.field.answer.value?.[0]?.name, "good-1.txt");
assert.strictEqual(this.field.answer.value?.[1]?.name, "good-1.txt");
assert.strictEqual(this.field.answer.value?.[2]?.name, "good-2.txt");
assert.deepEqual(this.field._errors, []);
});

Expand All @@ -95,10 +99,10 @@ module("Integration | Component | cf-field/input/file", function (hooks) {
assert.strictEqual(target, "_blank", "Target for new window is _blank");
};

await render(hbs`<CfField::Input::File @field={{this.field}} />`);
await render(hbs`<CfField::Input::Files @field={{this.field}} />`);

assert.dom("[data-test-download-link]").exists();
assert.dom(`[data-test-download-link]`).hasText(file.name);
assert.dom(`[data-test-download-link="${file.id}"]`).exists();
assert.dom(`[data-test-download-link="${file.id}"]`).hasText(file.name);

await click("[data-test-download-link]");

Expand Down

0 comments on commit 19970d1

Please sign in to comment.