Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
agHelper,
draggableWidgets,
entityExplorer,
propPane,
} from "../../../../../support/Objects/ObjectsCore";
import EditorNavigation, {
EntityType,
} from "../../../../../support/Pages/EditorNavigation";
const commonlocators = require("../../../../../locators/commonlocators.json");

describe(
"FilePicker Widget Functionality",
{ tags: ["@tag.Widget", "@tag.Filepicker"] },
function () {
before(() => {
entityExplorer.DragDropWidgetNVerify(draggableWidgets.FILEPICKER);
});

it("Should display an error message when clicked on filepicker when max no-of files are set to 0", () => {
EditorNavigation.SelectEntityByName("FilePicker1", EntityType.Widget);
propPane.UpdatePropertyFieldValue("Max no. of files", "1");
const fixturePath = "cypress/fixtures/dummy.pdf";
cy.get(commonlocators.filepickerv2).click();
cy.get(commonlocators.filePickerInput).first().selectFile(fixturePath, {
force: true,
});
cy.get(commonlocators.filePickerUploadButton).click();
cy.get(commonlocators.dashboardItemName).contains("dummy.pdf");
agHelper.GetNClick(commonlocators.filePickerRemoveButton, 0, true);
propPane.UpdatePropertyFieldValue("Max no. of files", "0");
cy.get(commonlocators.filepickerv2).click();
});
},
);
Binary file added app/client/cypress/fixtures/dummy.pdf
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";
import FilePickerComponent, { type FilePickerComponentProps } from "./index";
import { ThemeProvider } from "styled-components";
import { lightTheme } from "selectors/themeSelectors";

const mockProps: FilePickerComponentProps = {
label: "Select files",
openModal: jest.fn(),
isLoading: false,
files: [],
buttonColor: "blue",
borderRadius: "5px",
shouldFitContent: true,
widgetId: "",
maxNumFiles: 2,
errorMessage: "Cannot upload any files",
};

describe("FilePickerComponent", () => {
test("1.renders with default label", () => {
render(
<ThemeProvider theme={lightTheme}>
<FilePickerComponent {...mockProps} />
</ThemeProvider>,
);
const button = screen.getByRole("button", { name: /select files/i });
expect(button).toBeInTheDocument();
});

test("2.displays number of selected files", () => {
const propsWithFiles = {
...mockProps,
files: [{ name: "file1" }, { name: "file2" }],
};
render(
<ThemeProvider theme={lightTheme}>
<FilePickerComponent {...propsWithFiles} />
</ThemeProvider>,
);
const button = screen.getByRole("button", { name: /2 files selected/i });
expect(button).toBeInTheDocument();
});

test("3.opens modal on button click", () => {
render(
<ThemeProvider theme={lightTheme}>
<FilePickerComponent {...mockProps} />
</ThemeProvider>,
);
const button = screen.getByRole("button", { name: /select files/i });
fireEvent.click(button);
expect(mockProps.openModal).toHaveBeenCalled();
});

test("4.displays error message when provided", () => {
const propsWithError = {
...mockProps,
maxNumFiles: 0,
};
render(
<ThemeProvider theme={lightTheme}>
<FilePickerComponent {...propsWithError} />
</ThemeProvider>,
);
expect(screen.getByText(/Cannot upload any files/i)).toBeInTheDocument();
});
});
37 changes: 24 additions & 13 deletions app/client/src/widgets/FilePickerWidgetV2/component/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import React from "react";
import type { ComponentProps } from "widgets/BaseComponent";
import { BaseButton } from "widgets/ButtonWidget/component";
import { Colors } from "constants/Colors";
import { theme } from "constants/DefaultTheme";
import styled from "styled-components";

const ErrorMessage = styled.div`
color: ${theme.colors.error};
`;

function FilePickerComponent(props: FilePickerComponentProps) {
let computedLabel = props.label;
Expand All @@ -11,19 +17,22 @@ function FilePickerComponent(props: FilePickerComponentProps) {
}

return (
<BaseButton
borderRadius={props.borderRadius}
boxShadow={props.boxShadow}
buttonColor={props.buttonColor}
disabled={props.isDisabled}
loading={props.isLoading}
maxWidth={props.maxWidth}
minHeight={props.minHeight}
minWidth={props.minWidth}
onClick={props.openModal}
shouldFitContent={props.shouldFitContent}
text={computedLabel}
/>
<>
<BaseButton
borderRadius={props.borderRadius}
boxShadow={props.boxShadow}
buttonColor={props.buttonColor}
disabled={props.isDisabled}
loading={props.isLoading}
maxWidth={props.maxWidth}
minHeight={props.minHeight}
minWidth={props.minWidth}
onClick={props.openModal}
shouldFitContent={props.shouldFitContent}
text={computedLabel}
/>
{props.errorMessage && <ErrorMessage>{props.errorMessage}</ErrorMessage>}
</>
);
}

Expand All @@ -41,6 +50,8 @@ export interface FilePickerComponentProps extends ComponentProps {
maxWidth?: number;
minWidth?: number;
minHeight?: number;
maxNumFiles?: number;
errorMessage?: string | null;
}

FilePickerComponent.defaultProps = {
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/widgets/FilePickerWidgetV2/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FileDataTypes } from "WidgetProvider/constants";

export default FileDataTypes;

export const ERROR_MESSAGE = "Cannot upload any files";
22 changes: 17 additions & 5 deletions app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { importUppy, isUppyLoaded } from "utils/importUppy";
import type { WidgetProps, WidgetState } from "widgets/BaseWidget";
import BaseWidget from "widgets/BaseWidget";
import FilePickerComponent from "../component";
import FileDataTypes from "../constants";
import FileDataTypes, { ERROR_MESSAGE } from "../constants";
import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils";
import type {
AnvilConfig,
Expand Down Expand Up @@ -49,6 +49,7 @@ class FilePickerWidget extends BaseWidget<
areFilesLoading: false,
isWaitingForUppyToLoad: false,
isUppyModalOpen: false,
errorMessage: null,
};
}

Expand Down Expand Up @@ -318,7 +319,7 @@ class FilePickerWidget extends BaseWidget<
{
propertyName: "isVisible",
label: "Visible",
helpText: "Controls the visibility of the widget",
helpText: "Controls the visibility of the widget ",
controlType: "SWITCH",
isJSConvertible: true,
isBindProperty: true,
Expand Down Expand Up @@ -374,7 +375,7 @@ class FilePickerWidget extends BaseWidget<
children: [
{
propertyName: "buttonColor",
helpText: "Changes the color of the button",
helpText: "Changes color of the button",
label: "Button color",
controlType: "COLOR_PICKER",
isJSConvertible: true,
Expand All @@ -390,8 +391,7 @@ class FilePickerWidget extends BaseWidget<
{
propertyName: "borderRadius",
label: "Border radius",
helpText:
"Rounds the corners of the icon button's outer border edge",
helpText: "Rounds the corners of the icon button's outer border edge",
controlType: "BORDER_RADIUS_OPTIONS",

isJSConvertible: true,
Expand Down Expand Up @@ -725,6 +725,11 @@ class FilePickerWidget extends BaseWidget<
}

this.clearFilesFromMemory(prevProps.selectedFiles);
const prevMaxNumFiles = prevProps.maxNumFiles ?? 0;
const currentMaxNumFiles = this.props.maxNumFiles ?? 0;
if (prevMaxNumFiles === 0 && currentMaxNumFiles > 0) {
this.setState({ errorMessage: null });
}
}

// Reclaim the memory used by blobs.
Expand Down Expand Up @@ -819,6 +824,11 @@ class FilePickerWidget extends BaseWidget<
minHeight={this.props.minHeight}
minWidth={this.props.minWidth}
openModal={async () => {
if (this.props.maxNumFiles === 0) {
this.setState({ errorMessage: ERROR_MESSAGE });
return;
}
this.setState({ errorMessage: null });
// If Uppy is still loading, show a spinner to indicate that handling the click
// will take some time.
//
Expand All @@ -841,6 +851,7 @@ class FilePickerWidget extends BaseWidget<
}}
shouldFitContent={this.isAutoLayoutMode}
widgetId={this.props.widgetId}
errorMessage={this.state.errorMessage}
/>

{this.state.isUppyModalOpen && (
Expand All @@ -855,6 +866,7 @@ interface FilePickerWidgetState extends WidgetState {
areFilesLoading: boolean;
isWaitingForUppyToLoad: boolean;
isUppyModalOpen: boolean;
errorMessage: string | null;
}

interface FilePickerWidgetProps extends WidgetProps {
Expand Down