Skip to content

EncryptedUpload

EncryptedUpload is a composite component that groups an encryption toggle button with a file input and an upload action button.

It composes InputGroup, Button, FileInputField, and ButtonAction into a cohesive unit with optional Popover support for enforced encryption scenarios.

Basic Usage

To implement the EncryptedUpload component, import it first:

import {
  ButtonAction,
  EncryptedUpload,
} from 'rades_react';

Then use it with a controlled encrypted state and an onFileChange handler:

React.createElement(() => {
  const [encrypted, setEncrypted] = React.useState(false);

  return (
    <EncryptedUpload
      encrypted={encrypted}
      label="Attachment"
      onEncryptedChange={setEncrypted}
      onFileChange={(files) => console.log('Selected files:', files)}
    >
      <ButtonAction
        color="secondary"
        label="Upload"
        onClick={() => Promise.resolve(true)}
      />
    </EncryptedUpload>
  );
})

Locked Encryption

Set locked to true to prevent the user from changing the encryption state. When locked is set, clicking the toggle suppresses onEncryptedChange. Provide lockedDescription to show a Popover with an explanation when the toggle is clicked.

Locked: Encrypted

Set encrypted to true and locked to true. Provide a lockedDescription to explain why encryption cannot be disabled.

React.createElement(() => {
  // DO NOT COPY the wrapping <div> into your code. It's only needed for this
  // preview to provide space for the popover with the description.
  return (
    <div style={{ display: 'grid', placeContent: 'start center', height: '12rem' }}>
      <EncryptedUpload
        encrypted
        label="Attachment"
        locked
        lockedDescription="Encryption is enforced by your organization's security policy and cannot be disabled."
        onFileChange={(files) => console.log('Selected files:', files)}
      >
        <ButtonAction
          color="secondary"
          label="Upload"
          onClick={() => Promise.resolve(true)}
        />
      </EncryptedUpload>
    </div>
  );
})

Locked: Unencrypted

Set encrypted to false and locked to true. Provide a lockedDescription to explain why encryption is not available.

React.createElement(() => {
  // DO NOT COPY the wrapping <div> into your code. It's only needed for this
  // preview to provide space for the popover with the description.
  return (
    <div style={{ display: 'grid', placeContent: 'start center', height: '12rem' }}>
      <EncryptedUpload
        encrypted={false}
        label="Attachment"
        locked
        lockedDescription="Encryption is not available on this device."
        onFileChange={(files) => console.log('Selected files:', files)}
      >
        <ButtonAction
          color="secondary"
          label="Upload"
          onClick={() => Promise.resolve(true)}
        />
      </EncryptedUpload>
    </div>
  );
})

Without Action Button

The action button can be omitted when the upload is triggered externally, for example by a form submit button.

React.createElement(() => {
  const [encrypted, setEncrypted] = React.useState(false);

  return (
    <EncryptedUpload
      encrypted={encrypted}
      label="Attachment"
      onEncryptedChange={setEncrypted}
      onFileChange={() => {}}
    />
  );
})

Sizes

Three sizes are available: small, medium, and large.

React.createElement(() => {
  const [encryptedSmall, setEncryptedSmall] = React.useState(false);
  const [encryptedMedium, setEncryptedMedium] = React.useState(false);
  const [encryptedLarge, setEncryptedLarge] = React.useState(false);

  return (
    <>
      <EncryptedUpload
        encrypted={encryptedSmall}
        label="Attachment"
        onEncryptedChange={setEncryptedSmall}
        onFileChange={() => {}}
        size="small"
      >
        <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
      </EncryptedUpload>
      <EncryptedUpload
        encrypted={encryptedMedium}
        label="Attachment"
        onEncryptedChange={setEncryptedMedium}
        onFileChange={() => {}}
      >
        <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
      </EncryptedUpload>
      <EncryptedUpload
        encrypted={encryptedLarge}
        label="Attachment"
        onEncryptedChange={setEncryptedLarge}
        onFileChange={() => {}}
        size="large"
      >
        <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
      </EncryptedUpload>
    </>
  );
})

Invisible Label

In some cases, it may be convenient to visually hide the group label. The label remains accessible to assistive technologies.

While it may be acceptable for simple forms with just a few fields, it's dangerous to hide labels from users in most cases. Keep in mind you should provide another visual clue so users know what to fill into the input.

React.createElement(() => {
  const [encrypted, setEncrypted] = React.useState(false);

  return (
    <EncryptedUpload
      encrypted={encrypted}
      isLabelVisible={false}
      label="Attachment"
      onEncryptedChange={setEncrypted}
      onFileChange={() => {}}
    >
      <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
    </EncryptedUpload>
  );
})

Horizontal Layout

The default vertical layout is straightforward to use and work with. However, there are situations where horizontal layout suits better — and that's why React UI supports this kind of layout as well.

React.createElement(() => {
  const [encrypted, setEncrypted] = React.useState(false);

  return (
    <EncryptedUpload
      encrypted={encrypted}
      label="Attachment"
      layout="horizontal"
      onEncryptedChange={setEncrypted}
      onFileChange={() => {}}
    >
      <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
    </EncryptedUpload>
  );
})

Help Text

You may provide one or more additional help texts to clarify the upload field.

React.createElement(() => {
  const [encrypted, setEncrypted] = React.useState(false);

  return (
    <EncryptedUpload
      encrypted={encrypted}
      helpTexts={[
        'Select a file to upload. Maximum file size: 10 MB.',
      ]}
      label="Attachment"
      onEncryptedChange={setEncrypted}
      onFileChange={() => {}}
    >
      <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
    </EncryptedUpload>
  );
})

States

Disabled State

<EncryptedUpload
  disabled
  encrypted={false}
  label="Attachment"
  onFileChange={() => {}}
>
  <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
</EncryptedUpload>

Validation States

Validation states visually present the result of validation of the upload field. You should always provide a validation message for states other than valid so users know what happened and what action they should take or what options they have.

React.createElement(() => {
  const [encryptedValid, setEncryptedValid] = React.useState(false);
  const [encryptedInvalid, setEncryptedInvalid] = React.useState(false);
  const [encryptedWarning, setEncryptedWarning] = React.useState(false);

  return (
    <>
      <EncryptedUpload
        encrypted={encryptedValid}
        label="Attachment"
        onEncryptedChange={setEncryptedValid}
        onFileChange={() => {}}
        validationState="valid"
        validationTexts={['File is ready to upload.']}
      >
        <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
      </EncryptedUpload>
      <EncryptedUpload
        encrypted={encryptedInvalid}
        label="Attachment"
        onEncryptedChange={setEncryptedInvalid}
        onFileChange={() => {}}
        validationState="invalid"
        validationTexts={['File is too large. Please select a file smaller than 10 MB.']}
      >
        <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
      </EncryptedUpload>
      <EncryptedUpload
        encrypted={encryptedWarning}
        label="Attachment"
        onEncryptedChange={setEncryptedWarning}
        onFileChange={() => {}}
        validationState="warning"
        validationTexts={['This file type may not be supported by all recipients.']}
      >
        <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
      </EncryptedUpload>
    </>
  );
})

Ref Forwarding

A ref passed to EncryptedUpload is forwarded to the inner FileInputField, giving access to its instance methods such as resetState.

React.createElement(() => {
  const ref = React.useRef();
  const [encrypted, setEncrypted] = React.useState(false);

  return (
    <>
      <EncryptedUpload
        encrypted={encrypted}
        label="Attachment"
        onEncryptedChange={setEncrypted}
        onFileChange={() => {}}
        ref={ref}
      >
        <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
      </EncryptedUpload>
      <div>
        <Button
          color="danger"
          label="Reset file input state"
          onClick={() => {
            if (!ref.current) {
              return;
            }
            ref.current.resetState();
          }}
        />
      </div>
    </>
  );
})

To disable the upload button until a file is selected, track file selection with onFileChange and derive a hasFile state from it. Pass disabled={!hasFile} to ButtonAction so it stays inactive until the user picks a file. After a successful upload, call ref.current.resetState() to clear the file input and reset hasFile to false.

React.createElement(() => {
  const ref = React.useRef();
  const [encrypted, setEncrypted] = React.useState(false);
  const [hasFile, setHasFile] = React.useState(false);

  return (
    <>
      <EncryptedUpload
        encrypted={encrypted}
        label="Attachment"
        onEncryptedChange={setEncrypted}
        onFileChange={(files) => setHasFile(files.length > 0)}
        ref={ref}
      >
        <ButtonAction
          color="secondary"
          disabled={!hasFile}
          label="Upload"
          onClick={() => {
            return Promise.resolve(true).then((success) => {
              if (success) {
                ref.current?.resetState();
                setHasFile(false);
              }
              return success;
            });
          }}
        />
      </EncryptedUpload>
    </>
  );
})

Forwarding HTML Attributes

In addition to the options below in the component's API section, you can specify any HTML attribute you like. All attributes that don't interfere with the API are forwarded to the <input type="file"> HTML element. This enables customizing the file input, for example restricting accepted file types or allowing multiple files.

React.createElement(() => {
  const [encrypted, setEncrypted] = React.useState(false);

  return (
    <EncryptedUpload
      accept="image/*"
      encrypted={encrypted}
      label="Profile picture"
      multiple
      onEncryptedChange={setEncrypted}
      onFileChange={() => {}}
    >
      <ButtonAction color="secondary" label="Upload" onClick={() => Promise.resolve(true)} />
    </EncryptedUpload>
  );
})

👉 Refer to the MDN reference for the full list of supported attributes of the input element.

API