@scm-manager/ui-components 2.29.1 → 2.29.2-20220017-180458

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scm-manager/ui-components",
3
- "version": "2.29.1",
3
+ "version": "2.29.2-20220017-180458",
4
4
  "description": "UI Components for SCM-Manager and its plugins",
5
5
  "main": "src/index.ts",
6
6
  "files": [
@@ -25,7 +25,7 @@
25
25
  "@scm-manager/jest-preset": "^2.13.0",
26
26
  "@scm-manager/prettier-config": "^2.10.1",
27
27
  "@scm-manager/tsconfig": "^2.12.0",
28
- "@scm-manager/ui-tests": "^2.29.1",
28
+ "@scm-manager/ui-tests": "^2.29.2-20220017-180458",
29
29
  "@storybook/addon-actions": "^6.3.12",
30
30
  "@storybook/addon-storyshots": "^6.3.12",
31
31
  "@storybook/builder-webpack5": "^6.3.12",
@@ -65,9 +65,9 @@
65
65
  "worker-plugin": "^3.2.0"
66
66
  },
67
67
  "dependencies": {
68
- "@scm-manager/ui-api": "^2.29.1",
69
- "@scm-manager/ui-extensions": "^2.29.1",
70
- "@scm-manager/ui-types": "^2.29.1",
68
+ "@scm-manager/ui-api": "^2.29.2-20220017-180458",
69
+ "@scm-manager/ui-extensions": "^2.29.2-20220017-180458",
70
+ "@scm-manager/ui-types": "^2.29.2-20220017-180458",
71
71
  "classnames": "^2.2.6",
72
72
  "date-fns": "^2.4.1",
73
73
  "deepmerge": "^4.2.2",
@@ -37,8 +37,11 @@ type Props = {
37
37
  placeholder: string;
38
38
  loadingMessage: string;
39
39
  noOptionsMessage: string;
40
+ errorMessage?: string;
41
+ informationMessage?: string;
40
42
  creatable?: boolean;
41
43
  className?: string;
44
+ disabled?: boolean;
42
45
  };
43
46
 
44
47
  type State = {};
@@ -78,52 +81,51 @@ class Autocomplete extends React.Component<Props, State> {
78
81
  loadingMessage,
79
82
  noOptionsMessage,
80
83
  loadSuggestions,
84
+ errorMessage,
85
+ informationMessage,
81
86
  creatable,
82
- className
87
+ className,
88
+ disabled
83
89
  } = this.props;
84
90
 
91
+ const asyncProps = {
92
+ className: "autocomplete-entry",
93
+ classNamePrefix: "autocomplete-entry",
94
+ cacheOptions: true,
95
+ loadOptions: loadSuggestions,
96
+ onChange: this.handleInputChange,
97
+ value,
98
+ placeholder,
99
+ loadingMessage: () => loadingMessage,
100
+ noOptionsMessage: () => noOptionsMessage,
101
+ isDisabled: disabled,
102
+ "aria-label": helpText || label
103
+ };
104
+
85
105
  return (
86
106
  <div className={classNames("field", className)}>
87
107
  <LabelWithHelpIcon label={label} helpText={helpText} />
88
108
  <div className="control">
89
109
  {creatable ? (
90
110
  <AsyncCreatable
91
- className="autocomplete-entry"
92
- classNamePrefix="autocomplete-entry"
93
- cacheOptions
94
- loadOptions={loadSuggestions}
95
- onChange={this.handleInputChange}
96
- value={value}
97
- placeholder={placeholder}
98
- loadingMessage={() => loadingMessage}
99
- noOptionsMessage={() => noOptionsMessage}
111
+ {...asyncProps}
100
112
  isValidNewOption={this.isValidNewOption}
101
- onCreateOption={value => {
113
+ onCreateOption={newValue => {
102
114
  this.selectValue({
103
- label: value,
115
+ label: newValue,
104
116
  value: {
105
- id: value,
106
- displayName: value
117
+ id: newValue,
118
+ displayName: newValue
107
119
  }
108
120
  });
109
121
  }}
110
- aria-label={helpText || label}
111
122
  />
112
123
  ) : (
113
- <Async
114
- className="autocomplete-entry"
115
- classNamePrefix="autocomplete-entry"
116
- cacheOptions
117
- loadOptions={loadSuggestions}
118
- onChange={this.handleInputChange}
119
- value={value}
120
- placeholder={placeholder}
121
- loadingMessage={() => loadingMessage}
122
- noOptionsMessage={() => noOptionsMessage}
123
- aria-label={helpText || label}
124
- />
124
+ <Async {...asyncProps} />
125
125
  )}
126
126
  </div>
127
+ {errorMessage ? <p className="help is-danger">{errorMessage}</p> : null}
128
+ {informationMessage ? <p className="help is-info">{informationMessage}</p> : null}
127
129
  </div>
128
130
  );
129
131
  }
@@ -19219,6 +19219,10 @@ exports[`Storyshots Modal/Modal With form elements 1`] = `null`;
19219
19219
 
19220
19220
  exports[`Storyshots Modal/Modal With long tooltips 1`] = `null`;
19221
19221
 
19222
+ exports[`Storyshots Modal/Modal With overflow 1`] = `null`;
19223
+
19224
+ exports[`Storyshots Modal/Modal With overflow and footer 1`] = `null`;
19225
+
19222
19226
  exports[`Storyshots Notification Closeable 1`] = `
19223
19227
  <div
19224
19228
  className="Notificationstories__Wrapper-sc-8fx7tr-0 gJPbVB"
@@ -32,6 +32,8 @@ import ExternalLink from "../navigation/ExternalLink";
32
32
  import { Radio, Textarea, InputField } from "../forms";
33
33
  import { ButtonGroup, Button } from "../buttons";
34
34
  import Notification from "../Notification";
35
+ import { Autocomplete } from "../index";
36
+ import { SelectValue } from "@scm-manager/ui-types";
35
37
 
36
38
  const TopAndBottomMargin = styled.div`
37
39
  margin: 0.75rem 0; // only for aesthetic reasons
@@ -51,7 +53,9 @@ const text = `Mind-paralyzing change needed improbability vortex machine sorts s
51
53
  ordinary mob.`;
52
54
 
53
55
  // eslint-disable-next-line @typescript-eslint/no-empty-function
54
- const doNothing = () => {};
56
+ const doNothing = () => {
57
+ // nothing to do
58
+ };
55
59
  const withFormElementsBody = (
56
60
  <>
57
61
  <RadioList>
@@ -71,8 +75,21 @@ const withFormElementsFooter = (
71
75
  </ButtonGroup>
72
76
  );
73
77
 
78
+ const loadSuggestions: (p: string) => Promise<SelectValue[]> = () =>
79
+ new Promise(resolve => {
80
+ setTimeout(() => {
81
+ resolve([
82
+ { value: { id: "trillian", displayName: "Tricia McMillan" }, label: "Tricia McMillan" },
83
+ { value: { id: "zaphod", displayName: "Zaphod Beeblebrox" }, label: "Zaphod Beeblebrox" },
84
+ { value: { id: "ford", displayName: "Ford Prefect" }, label: "Ford Prefect" },
85
+ { value: { id: "dent", displayName: "Arthur Dent" }, label: "Arthur Dent" },
86
+ { value: { id: "marvin", displayName: "Marvin" }, label: "Marvin the Paranoid Android " }
87
+ ]);
88
+ });
89
+ });
90
+
74
91
  storiesOf("Modal/Modal", module)
75
- .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
92
+ .addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
76
93
  .add("Default", () => (
77
94
  <NonCloseableModal>
78
95
  <p>{text}</p>
@@ -104,7 +121,7 @@ storiesOf("Modal/Modal", module)
104
121
  This story exists because we had a problem, that long tooltips causes a horizontal scrollbar on the modal.
105
122
  </Notification>
106
123
  <hr />
107
- <p>The following elements will have a verly long help text, which has triggered the scrollbar in the past.</p>
124
+ <p>The following elements will have a very long help text, which has triggered the scrollbar in the past.</p>
108
125
  <hr />
109
126
  <TopAndBottomMargin>
110
127
  <Checkbox label="Checkbox" checked={true} helpText={text} />
@@ -211,10 +228,47 @@ storiesOf("Modal/Modal", module)
211
228
  </p>
212
229
  </div>
213
230
  </NonCloseableModal>
214
- ));
231
+ ))
232
+ .add("With overflow", () => {
233
+ return (
234
+ <NonCloseableModal overflowVisible={true}>
235
+ <h1 className="title">Please Select</h1>
236
+ <Autocomplete
237
+ valueSelected={() => {
238
+ // nothing to do
239
+ }}
240
+ loadSuggestions={loadSuggestions}
241
+ />
242
+ </NonCloseableModal>
243
+ );
244
+ })
245
+ .add("With overflow and footer", () => {
246
+ return (
247
+ <NonCloseableModal overflowVisible={true} footer={withFormElementsFooter}>
248
+ <h1 className="title">Please Select</h1>
249
+ <Autocomplete
250
+ valueSelected={() => {
251
+ // nothing to do
252
+ }}
253
+ loadSuggestions={loadSuggestions}
254
+ />
255
+ </NonCloseableModal>
256
+ );
257
+ });
258
+
259
+ type NonCloseableModalProps = { overflowVisible?: boolean; footer?: any };
215
260
 
216
- const NonCloseableModal: FC = ({ children }) => {
217
- return <Modal body={children} closeFunction={doNothing} active={true} title={"Hitchhiker Modal"} />;
261
+ const NonCloseableModal: FC<NonCloseableModalProps> = ({ overflowVisible, footer, children }) => {
262
+ return (
263
+ <Modal
264
+ body={children}
265
+ closeFunction={doNothing}
266
+ active={true}
267
+ title={"Hitchhiker Modal"}
268
+ overflowVisible={overflowVisible}
269
+ footer={footer}
270
+ />
271
+ );
218
272
  };
219
273
 
220
274
  const CloseableModal: FC = ({ children }) => {
@@ -42,10 +42,24 @@ type Props = {
42
42
  headColor?: string;
43
43
  headTextColor?: string;
44
44
  size?: ModalSize;
45
+ overflowVisible?: boolean;
45
46
  };
46
47
 
47
- const SizedModal = styled.div<{ size?: ModalSize }>`
48
+ const SizedModal = styled.div<{ size?: ModalSize; overflow: string }>`
48
49
  width: ${props => (props.size ? `${modalSizes[props.size]}%` : "640px")};
50
+ overflow: ${props => props.overflow};
51
+ `;
52
+
53
+ const DivWithOptionalOverflow = styled.div<{ overflow: string; borderBottomRadius: string }>`
54
+ overflow: ${props => props.overflow};
55
+ border-bottom-left-radius: ${props => props.borderBottomRadius};
56
+ border-bottom-right-radius: ${props => props.borderBottomRadius};
57
+ `;
58
+
59
+ const SectionWithOptionalOverflow = styled.section<{ overflow: string; borderBottomRadius: string }>`
60
+ overflow: ${props => props.overflow};
61
+ border-bottom-left-radius: ${props => props.borderBottomRadius};
62
+ border-bottom-right-radius: ${props => props.borderBottomRadius};
49
63
  `;
50
64
 
51
65
  export const Modal: FC<Props> = ({
@@ -57,7 +71,8 @@ export const Modal: FC<Props> = ({
57
71
  className,
58
72
  headColor = "secondary-less",
59
73
  headTextColor = "secondary-most",
60
- size
74
+ size,
75
+ overflowVisible
61
76
  }) => {
62
77
  const portalRootElement = usePortalRootElement("modalsRoot");
63
78
  const initialFocusRef = useRef(null);
@@ -85,18 +100,29 @@ export const Modal: FC<Props> = ({
85
100
  }
86
101
  };
87
102
 
103
+ const overflowAttribute = overflowVisible ? "visible" : "auto";
104
+ const borderBottomRadiusAttribute = overflowVisible && !footer ? "inherit" : "unset";
105
+
88
106
  const modalElement = (
89
- <div className={classNames("modal", className, isActive)} ref={trapRef} onKeyDown={onKeyDown}>
107
+ <DivWithOptionalOverflow
108
+ className={classNames("modal", className, isActive)}
109
+ ref={trapRef}
110
+ onKeyDown={onKeyDown}
111
+ overflow={overflowAttribute}
112
+ borderBottomRadius={borderBottomRadiusAttribute}
113
+ >
90
114
  <div className="modal-background" onClick={closeFunction} />
91
- <SizedModal className="modal-card" size={size}>
115
+ <SizedModal className="modal-card" size={size} overflow={overflowAttribute}>
92
116
  <header className={classNames("modal-card-head", `has-background-${headColor}`)}>
93
117
  <h2 className={`modal-card-title m-0 has-text-${headTextColor}`}>{title}</h2>
94
118
  <button className="delete" aria-label="close" onClick={closeFunction} ref={initialFocusRef} autoFocus />
95
119
  </header>
96
- <section className="modal-card-body">{body}</section>
120
+ <SectionWithOptionalOverflow className="modal-card-body" overflow={overflowAttribute} borderBottomRadius={borderBottomRadiusAttribute}>
121
+ {body}
122
+ </SectionWithOptionalOverflow>
97
123
  {showFooter}
98
124
  </SizedModal>
99
- </div>
125
+ </DivWithOptionalOverflow>
100
126
  );
101
127
 
102
128
  return ReactDOM.createPortal(modalElement, portalRootElement);