Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions src/lib/components/ui/FileInputField/FileInputField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import PropTypes from 'prop-types';
import React from 'react';
import getRootValidationStateClassName from '../../../helpers/getRootValidationStateClassName';
import { withProviderContext } from '../../../provider';
import transferProps from '../../../utils/transferProps';
import withForwardedRef from '../withForwardedRef';
import styles from './FileInputField.scss';

export const FileInputField = ({
changeHandler,
disabled,
forwardedRef,
fullWidth,
helpText,
id,
inFormLayout,
isLabelVisible,
label,
layout,
required,
validationState,
validationText,
...restProps
}) => (
<label
className={[
styles.root,
fullWidth ? styles.isRootFullWidth : '',
inFormLayout ? styles.isRootInFormLayout : '',
layout === 'horizontal' ? styles.rootLayoutHorizontal : styles.rootLayoutVertical,
disabled ? styles.isRootDisabled : '',
required ? styles.isRootRequired : '',
getRootValidationStateClassName(validationState, styles),
].join(' ')}
htmlFor={id}
id={`${id}__label`}
>
<div
className={[
styles.label,
isLabelVisible ? '' : styles.isLabelHidden,
].join(' ')}
id={`${id}__labelText`}
>
{label}
</div>
<div className={styles.field}>
<div className={styles.inputContainer}>
<input
{...transferProps(restProps)}
className={styles.input}
disabled={disabled}
id={id}
onChange={changeHandler}
ref={forwardedRef}
required={required}
type="file"
/>
</div>
{helpText && (
<div
className={styles.helpText}
id={`${id}__helpText`}
>
{helpText}
</div>
)}
{validationText && (
<div
className={styles.validationText}
id={`${id}__validationText`}
>
{validationText}
</div>
)}
</div>
</label>
);

FileInputField.defaultProps = {
changeHandler: null,
disabled: false,
forwardedRef: undefined,
fullWidth: false,
helpText: null,
inFormLayout: false,
isLabelVisible: true,
layout: 'vertical',
required: false,
validationState: null,
validationText: null,
};

FileInputField.propTypes = {
/**
* Function to call when the input has changed.
*/
changeHandler: PropTypes.func,
/**
* If `true`, the input will be disabled.
*/
disabled: PropTypes.bool,
/**
* Reference forwarded to the `input` element.
*/
forwardedRef: PropTypes.oneOfType([
PropTypes.func,
// eslint-disable-next-line react/forbid-prop-types
PropTypes.shape({ current: PropTypes.any }),
]),
/**
* If `true`, the field will span the full width of its parent.
*/
fullWidth: PropTypes.bool,
/**
* Optional help text.
*/
helpText: PropTypes.node,
/**
* ID of the input HTML element. It also serves as a prefix for important inner elements:
* `<ID>__label`, `<ID>__labelText`, `<ID>__helpText`, and `<ID>__validationText`.
*/
id: PropTypes.string.isRequired,
/**
* Treat the field differently when it's inside a FormLayout. Do not set manually!
*/
inFormLayout: PropTypes.bool,
/**
* If `false`, the label will be visually hidden (but remains accessible by assistive
* technologies).
*/
isLabelVisible: PropTypes.bool,
/**
* Text field label.
*/
label: PropTypes.string.isRequired,
/**
* Layout of the field.
*/
layout: PropTypes.oneOf(['horizontal', 'vertical']),
/**
* If `true`, the input will be required.
*/
required: PropTypes.bool,
/**
* Alter the field to provide feedback based on validation result.
*/
validationState: PropTypes.oneOf(['invalid', 'valid', 'warning']),
/**
* Validation message to be displayed.
*/
validationText: PropTypes.node,
};

export const FileInputFieldWithContext = withForwardedRef(withProviderContext(FileInputField, 'FileInputField'));

export default FileInputFieldWithContext;
70 changes: 70 additions & 0 deletions src/lib/components/ui/FileInputField/FileInputField.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
@use '../../../styles/tools/form-fields/box-field-elements';
@use '../../../styles/tools/form-fields/box-field-layout';
@use '../../../styles/tools/form-fields/foundation';
@use '../../../styles/tools/form-fields/relationships';
@use '../../../styles/tools/form-fields/variants';
@use '../../../styles/tools/accessibility';

// Foundation
.root {
@include foundation.root();
@include relationships.horizontal-neighbor();
}

.label {
@include foundation.label();
}

.inputContainer {
@include box-field-elements.input-container();
}

.input {
@include accessibility.focus-ring();
}

.helpText,
.validationText {
@include foundation.help-text();
}

.isRootRequired .label {
@include foundation.label-required();
}

// States
.isRootStateInvalid {
@include variants.validation(invalid);
}

.isRootStateValid {
@include variants.validation(valid);
}

.isRootStateWarning {
@include variants.validation(warning);
}

// Invisible label
.isLabelHidden {
@include accessibility.hide-text();
}

// Layouts
.rootLayoutVertical,
.rootLayoutHorizontal {
@include box-field-layout.vertical();
}

.rootLayoutHorizontal {
@include box-field-layout.horizontal();
}

.isRootFullWidth {
@include box-field-layout.full-width();
@include relationships.vertical-neighbor();
}

.isRootInFormLayout {
@include box-field-layout.in-form-layout();
}
Loading