Skip to content
Snippets Groups Projects
Unverified Commit 5078594f authored by Ryan Laurie's avatar Ryan Laurie Committed by GitHub
Browse files

Appends follow-up UI fixes (#38837)

parent 08b8dfb1
No related branches found
No related tags found
No related merge requests found
......@@ -15,6 +15,14 @@ export type CollectionAuthorityLevel = "official" | null;
export type CollectionType = "instance-analytics" | null;
export type LastEditInfo = {
email: string;
first_name: string;
last_name: string;
id: UserId;
timestamp: string;
};
export type CollectionAuthorityLevelConfig = {
type: CollectionAuthorityLevel;
name: string;
......@@ -86,6 +94,7 @@ export interface CollectionItem {
moderated_status?: string;
type?: string;
can_write?: boolean;
"last-edit-info"?: LastEditInfo;
getIcon: () => { name: IconName };
getUrl: (opts?: Record<string, unknown>) => string;
setArchived?: (isArchived: boolean) => void;
......
......@@ -13,6 +13,8 @@ import {
import { useSearchListQuery } from "metabase/common/hooks";
import type { CollectionId, TableId, CardId } from "metabase-types/api";
import { findLastEditedCollectionItem } from "./utils";
enum UploadMode {
append = "append",
create = "create",
......@@ -47,6 +49,18 @@ export function ModelUploadModal({
[models.data],
);
useEffect(
function setDefaultTableId() {
if (!uploadableModels?.length) {
return;
}
const latestModel = findLastEditedCollectionItem(uploadableModels);
setTableId(Number(latestModel.based_on_upload));
},
[uploadableModels],
);
const handleUpload = () => {
if (uploadMode === "append" && tableId) {
const modelForTableId = uploadableModels.find(
......@@ -93,6 +107,7 @@ export function ModelUploadModal({
<Radio.Group
value={uploadMode}
onChange={(val: UploadMode) => setUploadMode(val)}
pl="1px"
>
<Radio label={t`Create a new model`} value={UploadMode.create} />
<Radio
......
import type { CollectionItem } from "metabase-types/api";
export const findLastEditedCollectionItem = (
collectionItems: CollectionItem[],
) => {
return collectionItems.reduce((latest, item) => {
if (!latest) {
return item;
}
const latestTimestamp = latest?.["last-edit-info"]?.timestamp;
const itemTimestamp = item?.["last-edit-info"]?.timestamp;
if (latestTimestamp && itemTimestamp) {
return latestTimestamp > itemTimestamp ? latest : item;
}
return latest;
});
};
......@@ -79,7 +79,10 @@ const FileUploadStatusContent = ({
{ id: collectionId, enabled: !isEmpty(collectionId) },
);
if (!isVisible || tableLoading || collectionLoading) {
const isLoading = !!(tableLoading || collectionLoading);
const hasData = !!(table || collection);
if (!isVisible || (isLoading && !hasData)) {
return null;
}
......
......@@ -37,6 +37,11 @@ const secondCollection = createMockCollection({
name: "Second Collection",
});
const thirdCollection = createMockCollection({
id: 3,
name: "Second Collection",
});
const uploadedModel = createMockCollectionItem({
id: 3,
name: "my uploaded model",
......@@ -45,6 +50,14 @@ const uploadedModel = createMockCollectionItem({
based_on_upload: 123,
});
const uploadedModel2 = createMockCollectionItem({
id: 4,
name: "my second uploaded model",
collection: secondCollection,
model: "dataset",
based_on_upload: 123,
});
async function setupCollectionContent(overrides = {}) {
setupDatabasesEndpoints([createMockDatabase({ can_upload: true })]);
setupSearchEndpoints([]);
......@@ -86,18 +99,22 @@ async function setupCollectionContent(overrides = {}) {
describe("FileUploadStatus", () => {
beforeEach(() => {
setupCollectionByIdEndpoint({
collections: [firstCollection, secondCollection],
collections: [firstCollection, secondCollection, thirdCollection],
});
setupCollectionsEndpoints({
collections: [firstCollection, secondCollection],
collections: [firstCollection, secondCollection, thirdCollection],
});
setupCollectionItemsEndpoint({
collection: firstCollection,
collectionItems: [],
});
setupCollectionItemsEndpoint({
collection: secondCollection,
collectionItems: [uploadedModel],
});
setupCollectionItemsEndpoint({
collection: firstCollection,
collectionItems: [],
collection: thirdCollection,
collectionItems: [uploadedModel, uploadedModel2],
});
fetchMock.get(
"path:/api/table/123",
......@@ -239,7 +256,7 @@ describe("FileUploadStatus", () => {
).toHaveAttribute("href", "/model/3");
});
it("Should allow appends from collections when an appendable model exists", async () => {
it("Should default to appending to a single selectable model", async () => {
jest.useFakeTimers({ advanceTimers: true });
fetchMock.post("path:/api/table/123/append-csv", "3", { delay: 1000 });
......@@ -256,7 +273,47 @@ describe("FileUploadStatus", () => {
userEvent.click(screen.getByText("Append to a model"));
const submitButton = await screen.findByRole("button", { name: "Append" });
expect(submitButton).toBeDisabled(); // should be disabled until a model is selected
// only appendable model should be pre-selected
await screen.findByText("my uploaded model");
await waitFor(() => expect(submitButton).toBeEnabled());
userEvent.click(submitButton);
act(() => {
jest.advanceTimersByTime(500);
});
expect(
await screen.findByText(/Uploading data to Fancy Table/i),
).toBeInTheDocument();
act(() => {
jest.advanceTimersByTime(1000);
});
expect(
await screen.findByRole("link", { name: "Start exploring" }),
).toHaveAttribute("href", "/model/3");
});
it("Should allow selecting from appendable models", async () => {
jest.useFakeTimers({ advanceTimers: true });
fetchMock.post("path:/api/table/123/append-csv", "3", { delay: 1000 });
await setupCollectionContent({ collectionId: thirdCollection.id });
userEvent.upload(
screen.getByTestId("upload-input"),
new File(["foo, bar"], "test.csv", { type: "text/csv" }),
);
expect(
await screen.findByText("Select upload destination"),
).toBeInTheDocument();
userEvent.click(screen.getByText("Append to a model"));
const submitButton = await screen.findByRole("button", { name: "Append" });
userEvent.click(await screen.findByPlaceholderText("Select a model"));
userEvent.click(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment