@dtdot/lego 0.19.5 → 0.19.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,14 @@
1
1
  /// <reference types="react" />
2
2
  export declare type ImageUploadMode = 'fill' | 'form';
3
3
  interface ImageUploadProps {
4
- name: string;
5
- value?: string;
6
- onChange?: (value: string) => void;
7
- onSearch?: () => void;
8
- uploadFn?: (file: File) => Promise<string>;
4
+ 'name': string;
5
+ 'value'?: string;
6
+ 'error'?: string;
7
+ 'uploading'?: boolean;
8
+ 'onChange'?: (value: string) => void;
9
+ 'onSearch'?: () => void;
10
+ 'uploadFn'?: (file: File) => Promise<string>;
11
+ 'data-cy'?: string;
9
12
  }
10
- declare const ImageUpload: ({ name, value, onChange, onSearch }: ImageUploadProps) => JSX.Element;
13
+ declare const ImageUpload: ({ name, value, "error": propsError, "uploading": uploadingProp, onChange, onSearch, "data-cy": dataCy, }: ImageUploadProps) => JSX.Element;
11
14
  export default ImageUpload;
@@ -1,10 +1,11 @@
1
- import React, { useContext, useRef } from 'react';
2
- import { faCloudUploadAlt, faSearch } from '@fortawesome/free-solid-svg-icons';
1
+ import React, { useContext, useRef, useState } from 'react';
2
+ import { faCloudUploadAlt, faExclamationCircle, faSearch } from '@fortawesome/free-solid-svg-icons';
3
3
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4
4
  import styled from 'styled-components';
5
5
  import { motion } from 'framer-motion';
6
6
  import FileContext from '../../contexts/File.context';
7
7
  import useFormNode from '../Form/useFormNode.hook';
8
+ import Loader from '../Loader/Loader.component';
8
9
  const UploadContainer = styled.div `
9
10
  position: relative;
10
11
  min-height: 144px;
@@ -24,6 +25,13 @@ const UploadInnerContainer = styled.div `
24
25
  display: flex;
25
26
  justify-content: space-evenly;
26
27
  `;
28
+ const LoaderContainer = styled.div `
29
+ width: 100%;
30
+ height: 100%;
31
+ display: flex;
32
+ justify-content: center;
33
+ align-items: center;
34
+ `;
27
35
  const IconContainer = styled(motion.div) `
28
36
  flex-grow: 1;
29
37
  margin: 8px;
@@ -55,10 +63,42 @@ const Image = styled.img `
55
63
  height: 100%;
56
64
  object-fit: cover;
57
65
  `;
58
- const ImageUpload = ({ name, value, onChange, onSearch }) => {
66
+ const ErrorMessage = styled.div `
67
+ padding-left: 6px;
68
+
69
+ font-family: ${(props) => props.theme.fonts.default.family};
70
+ font-size: ${(props) => props.theme.fonts.default.size};
71
+ color: ${(props) => props.theme.colours.statusDanger.main};
72
+ `;
73
+ const ErrorContainer = styled(motion.div) `
74
+ position: absolute;
75
+ left: 10px;
76
+ top: 10px;
77
+ width: 100%;
78
+ display: flex;
79
+ align-items: center;
80
+
81
+ font-size: ${(props) => props.theme.fonts.default.size};
82
+ color: ${(props) => props.theme.colours.statusDanger.main};
83
+ `;
84
+ const ErrorInner = styled.div `
85
+ width: 24px;
86
+ height: 24px;
87
+
88
+ display: flex;
89
+ justify-content: center;
90
+ align-items: center;
91
+ cursor: pointer;
92
+ `;
93
+ const errorVariants = {
94
+ show: { opacity: 1 },
95
+ };
96
+ const ImageUpload = ({ name, value, 'error': propsError, 'uploading': uploadingProp, onChange, onSearch, 'data-cy': dataCy, }) => {
59
97
  const { upload, getUrl } = useContext(FileContext);
60
- const { value: contextValue, onChange: contextOnChange } = useFormNode(name);
98
+ const [uploading, setUploading] = useState(false);
99
+ const { value: contextValue, error: contextError, onChange: contextOnChange } = useFormNode(name);
61
100
  const inputRef = useRef();
101
+ const error = contextError || propsError;
62
102
  const handleUploadClicked = () => {
63
103
  if (inputRef && inputRef.current) {
64
104
  inputRef.current.click();
@@ -67,18 +107,29 @@ const ImageUpload = ({ name, value, onChange, onSearch }) => {
67
107
  const handleUpload = async (event) => {
68
108
  if (event.target.files && upload) {
69
109
  const file = event.target.files[0];
70
- const url = await upload(file);
71
- if (onChange) {
72
- onChange(url);
110
+ setUploading(true);
111
+ try {
112
+ const url = await upload(file);
113
+ if (onChange) {
114
+ onChange(url);
115
+ }
116
+ if (contextOnChange) {
117
+ contextOnChange(url);
118
+ }
73
119
  }
74
- if (contextOnChange) {
75
- contextOnChange(url);
120
+ finally {
121
+ setUploading(false);
76
122
  }
77
123
  }
78
124
  };
79
125
  const internalValue = value ? value : contextValue;
126
+ if (uploading || uploadingProp) {
127
+ return (React.createElement(UploadContainer, { "data-cy": dataCy },
128
+ React.createElement(LoaderContainer, null,
129
+ React.createElement(Loader, null))));
130
+ }
80
131
  if (!internalValue) {
81
- return (React.createElement(UploadContainer, null,
132
+ return (React.createElement(UploadContainer, { "data-cy": dataCy },
82
133
  React.createElement(UploadInnerContainer, null,
83
134
  React.createElement(IconContainer, { whileHover: { scale: 1.05 }, onClick: handleUploadClicked, "data-cy": 'button-image-upload' },
84
135
  React.createElement(FontAwesomeIcon, { icon: faCloudUploadAlt })),
@@ -86,8 +137,13 @@ const ImageUpload = ({ name, value, onChange, onSearch }) => {
86
137
  React.createElement(UploadVerticalDivider, null),
87
138
  React.createElement(IconContainer, { whileHover: { scale: 1.05 }, onClick: onSearch, "data-cy": 'button-image-search' },
88
139
  React.createElement(FontAwesomeIcon, { icon: faSearch }))))),
89
- React.createElement(HiddenInput, { value: '', ref: inputRef, type: 'file', onChange: handleUpload, "data-cy": 'input-image-hidden' })));
140
+ React.createElement(HiddenInput, { value: '', ref: inputRef, type: 'file', onChange: handleUpload, "data-cy": 'input-image-hidden' }),
141
+ error && (React.createElement(ErrorContainer, { animate: error ? 'show' : undefined, style: { opacity: 0 }, variants: errorVariants, transition: { type: 'spring', duration: 0.3 }, "data-cy": 'error-indicator' },
142
+ React.createElement(ErrorInner, null,
143
+ React.createElement(FontAwesomeIcon, { icon: faExclamationCircle })),
144
+ React.createElement(ErrorMessage, { "data-cy": 'error-message' }, error)))));
90
145
  }
91
- return React.createElement(Image, { "data-cy": 'uploaded-image', src: getUrl(internalValue) });
146
+ return (React.createElement("div", { "data-cy": dataCy },
147
+ React.createElement(Image, { "data-cy": 'uploaded-image', src: getUrl(internalValue) })));
92
148
  };
93
149
  export default ImageUpload;
@@ -119,11 +119,11 @@ const Input = React.forwardRef(function ForwardRefInput(props, ref) {
119
119
  };
120
120
  return (React.createElement("div", null,
121
121
  label && React.createElement(InputLabel, { htmlFor: name }, label),
122
- React.createElement(InputContainer, { animate: error ? (isFocused ? 'errorFocus' : 'error') : undefined },
123
- React.createElement(StyledInput, { ref: ref, variants: inputVariants, transition: { type: 'spring', duration: 0.3 }, type: type, name: name, placeholder: placeholder, value: getValue(value, contextValue), onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, autoFocus: autoFocus, "data-cy": dataCy || 'input' }),
124
- React.createElement(ErrorContainer, { animate: error ? 'show' : undefined, style: { opacity: 0 }, variants: errorVariants, transition: { type: 'spring', duration: 0.3 } },
122
+ React.createElement(InputContainer, { animate: error ? (isFocused ? 'errorFocus' : 'error') : undefined, "data-cy": dataCy },
123
+ React.createElement(StyledInput, { ref: ref, variants: inputVariants, transition: { type: 'spring', duration: 0.3 }, type: type, name: name, placeholder: placeholder, value: getValue(value, contextValue), onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, autoFocus: autoFocus, "data-cy": 'input' }),
124
+ React.createElement(ErrorContainer, { animate: error ? 'show' : undefined, style: { opacity: 0 }, variants: errorVariants, transition: { type: 'spring', duration: 0.3 }, "data-cy": 'error-indicator' },
125
125
  React.createElement(ErrorInner, null,
126
126
  React.createElement(FontAwesomeIcon, { icon: faExclamationCircle }))),
127
- error && (React.createElement(ErrorMessage, { style: { opacity: 0, y: 0 }, variants: messageVariants, transition: { type: 'spring', duration: 0.3 } }, error)))));
127
+ error && (React.createElement(ErrorMessage, { style: { opacity: 0, y: 0 }, variants: messageVariants, transition: { type: 'spring', duration: 0.3 }, "data-cy": 'error-message' }, error)))));
128
128
  });
129
129
  export default Input;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dtdot/lego",
3
- "version": "0.19.5",
3
+ "version": "0.19.6",
4
4
  "description": "Some reusable components for building my applications",
5
5
  "main": "build/index.js",
6
6
  "scripts": {