@finos/legend-application 0.0.13 → 0.2.1

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/lib/components/ActionAlert.d.ts.map +1 -1
  3. package/lib/components/ActionAlert.js +5 -8
  4. package/lib/components/ActionAlert.js.map +1 -1
  5. package/lib/components/AppHeader.d.ts +20 -0
  6. package/lib/components/AppHeader.d.ts.map +1 -0
  7. package/lib/components/AppHeader.js +26 -0
  8. package/lib/components/AppHeader.js.map +1 -0
  9. package/lib/components/ApplicationStoreProvider.js +1 -1
  10. package/lib/components/ApplicationStoreProvider.js.map +1 -1
  11. package/lib/components/ApplicationStoreProviderTestUtils.js +1 -1
  12. package/lib/components/ApplicationStoreProviderTestUtils.js.map +1 -1
  13. package/lib/components/BlockingAlert.js +2 -2
  14. package/lib/components/BlockingAlert.js.map +1 -1
  15. package/lib/components/LambdaEditor.d.ts.map +1 -1
  16. package/lib/components/LambdaEditor.js +18 -19
  17. package/lib/components/LambdaEditor.js.map +1 -1
  18. package/lib/components/NotificationSnackbar.d.ts.map +1 -1
  19. package/lib/components/NotificationSnackbar.js +22 -12
  20. package/lib/components/NotificationSnackbar.js.map +1 -1
  21. package/lib/components/TextInputEditor.d.ts.map +1 -1
  22. package/lib/components/TextInputEditor.js +16 -7
  23. package/lib/components/TextInputEditor.js.map +1 -1
  24. package/lib/components/WebApplicationNavigatorProvider.d.ts.map +1 -1
  25. package/lib/components/WebApplicationNavigatorProvider.js +1 -1
  26. package/lib/components/WebApplicationNavigatorProvider.js.map +1 -1
  27. package/lib/index.css +2 -2
  28. package/lib/index.css.map +1 -1
  29. package/lib/index.d.ts +1 -0
  30. package/lib/index.d.ts.map +1 -1
  31. package/lib/index.js +1 -0
  32. package/lib/index.js.map +1 -1
  33. package/lib/stores/ApplicationStore.d.ts +1 -1
  34. package/lib/stores/ApplicationStore.d.ts.map +1 -1
  35. package/lib/stores/ApplicationStore.js +9 -12
  36. package/lib/stores/ApplicationStore.js.map +1 -1
  37. package/lib/stores/WebApplicationNavigator.d.ts +18 -1
  38. package/lib/stores/WebApplicationNavigator.d.ts.map +1 -1
  39. package/lib/stores/WebApplicationNavigator.js +5 -2
  40. package/lib/stores/WebApplicationNavigator.js.map +1 -1
  41. package/package.json +20 -20
  42. package/src/components/ActionAlert.tsx +1 -4
  43. package/src/components/AppHeader.tsx +47 -0
  44. package/src/components/LambdaEditor.tsx +11 -10
  45. package/src/components/NotificationSnackbar.tsx +28 -3
  46. package/src/components/TextInputEditor.tsx +17 -5
  47. package/src/components/WebApplicationNavigatorProvider.tsx +2 -1
  48. package/src/index.ts +1 -0
  49. package/src/stores/ApplicationStore.ts +8 -20
  50. package/src/stores/WebApplicationNavigator.ts +22 -2
  51. package/tsconfig.json +2 -5
  52. package/tsconfig.package.json +0 -40
@@ -31,14 +31,16 @@ import {
31
31
  FaBug,
32
32
  } from 'react-icons/fa';
33
33
  import { useApplicationStore } from './ApplicationStoreProvider';
34
+ import { ChevronDownIcon, ChevronUpIcon, clsx } from '@finos/legend-art';
35
+ import { useState } from 'react';
34
36
 
35
37
  export const NotificationSnackbar = observer(() => {
36
38
  const applicationStore = useApplicationStore();
37
39
  const notification = applicationStore.notification;
38
40
  const isOpen = Boolean(notification);
39
- // TODO: have a better way to truncate message in snackbar
40
41
  const message = notification?.message ?? '';
41
42
  const severity = notification?.severity ?? NOTIFCATION_SEVERITY.INFO;
43
+ const [isExpanded, setIsExpanded] = useState(false);
42
44
  let notificationIcon = (
43
45
  <div className="notification__message__content__icon notification__message__content__icon--info">
44
46
  <FaInfoCircle />
@@ -76,7 +78,14 @@ export const NotificationSnackbar = observer(() => {
76
78
  default:
77
79
  break;
78
80
  }
79
- const handleClose = (): void => applicationStore.setNotification(undefined);
81
+ const handleClose = (): void => {
82
+ applicationStore.setNotification(undefined);
83
+ setIsExpanded(false);
84
+ };
85
+ const handleCopy = (): Promise<void> =>
86
+ applicationStore.copyTextToClipboard(message);
87
+ const toggleExpansion = (): void => setIsExpanded(!isExpanded);
88
+
80
89
  const onSnackbarAutoHideOrClickAway = (
81
90
  event: React.SyntheticEvent<unknown>,
82
91
  reason: SnackbarCloseReason,
@@ -125,12 +134,28 @@ export const NotificationSnackbar = observer(() => {
125
134
  message={
126
135
  <div className="notification__message__content">
127
136
  {notificationIcon}
128
- <div className="notification__message__content__text">
137
+ <div
138
+ className={clsx('notification__message__content__text', {
139
+ 'notification__message__content__text--expanded': isExpanded,
140
+ })}
141
+ onClick={handleCopy}
142
+ title="Click to Copy"
143
+ >
129
144
  {message}
130
145
  </div>
131
146
  </div>
132
147
  }
133
148
  action={[
149
+ <button
150
+ className="notification__action"
151
+ id="expand_button"
152
+ key="expand"
153
+ onClick={toggleExpansion}
154
+ tabIndex={-1}
155
+ title={isExpanded ? 'Collapse' : 'Expand'}
156
+ >
157
+ {isExpanded ? <ChevronDownIcon /> : <ChevronUpIcon />}
158
+ </button>,
134
159
  <button
135
160
  className="notification__action"
136
161
  key="close"
@@ -23,6 +23,8 @@ import {
23
23
  disableEditorHotKeys,
24
24
  baseTextEditorSettings,
25
25
  resetLineNumberGutterWidth,
26
+ getEditorValue,
27
+ normalizeLineEnding,
26
28
  } from '@finos/legend-art';
27
29
  import type { EDITOR_LANGUAGE } from '../const';
28
30
  import { EDITOR_THEME, TAB_SIZE } from '../const';
@@ -63,6 +65,16 @@ export const TextInputEditor: React.FC<{
63
65
  const onDidChangeModelContentEventDisposer = useRef<IDisposable | undefined>(
64
66
  undefined,
65
67
  );
68
+
69
+ /**
70
+ * NOTE: we want to normalize line ending here since if the original
71
+ * input value includes CR '\r' character, it will get normalized, calling
72
+ * the updateInput method and cause a rerender. With the way we setup
73
+ * `onChange` method, React will warn about `setState` being called in
74
+ * `render` method.
75
+ * See https://github.com/finos/legend-studio/issues/608
76
+ */
77
+ const value = normalizeLineEnding(inputValue);
66
78
  const textInputRef = useRef<HTMLDivElement>(null);
67
79
 
68
80
  const { ref, width, height } = useResizeDetector<HTMLDivElement>();
@@ -103,8 +115,8 @@ export const TextInputEditor: React.FC<{
103
115
  onDidChangeModelContentEventDisposer.current?.dispose();
104
116
  onDidChangeModelContentEventDisposer.current =
105
117
  editor.onDidChangeModelContent(() => {
106
- const currentVal = editor.getValue();
107
- if (currentVal !== inputValue) {
118
+ const currentVal = getEditorValue(editor);
119
+ if (currentVal !== value) {
108
120
  updateInput?.(currentVal);
109
121
  }
110
122
  });
@@ -123,9 +135,9 @@ export const TextInputEditor: React.FC<{
123
135
  });
124
136
 
125
137
  // Set the text value and editor options
126
- const currentValue = editor.getValue();
127
- if (currentValue !== inputValue) {
128
- editor.setValue(inputValue);
138
+ const currentValue = getEditorValue(editor);
139
+ if (currentValue !== value) {
140
+ editor.setValue(value);
129
141
  }
130
142
  editor.updateOptions({
131
143
  readOnly: Boolean(isReadOnly),
@@ -18,6 +18,7 @@ import { guaranteeNonNullable } from '@finos/legend-shared';
18
18
  import { useLocalObservable } from 'mobx-react-lite';
19
19
  import { createContext, useContext } from 'react';
20
20
  import { useHistory } from 'react-router';
21
+ import type { History } from 'history';
21
22
  import { WebApplicationNavigator } from '../stores/WebApplicationNavigator';
22
23
 
23
24
  const WebApplicationNavigatorContext = createContext<
@@ -29,7 +30,7 @@ export const WebApplicationNavigatorProvider = ({
29
30
  }: {
30
31
  children: React.ReactNode;
31
32
  }): React.ReactElement => {
32
- const history = useHistory();
33
+ const history = useHistory() as History;
33
34
  const navigator = useLocalObservable(
34
35
  () => new WebApplicationNavigator(history),
35
36
  );
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ export * from './components/WebApplicationNavigatorProvider';
23
23
 
24
24
  export * from './components/ApplicationStoreProviderTestUtils';
25
25
  export * from './components/WebApplicationNavigatorProviderTestUtils';
26
+ export { AppHeader } from './components/AppHeader';
26
27
  export { BlockingAlert } from './components/BlockingAlert';
27
28
  export { ActionAlert } from './components/ActionAlert';
28
29
  export { NotificationSnackbar } from './components/NotificationSnackbar';
@@ -16,6 +16,7 @@
16
16
 
17
17
  import type { Log, SuperGenericFunction } from '@finos/legend-shared';
18
18
  import {
19
+ assertTrue,
19
20
  LogEvent,
20
21
  assertErrorThrown,
21
22
  isString,
@@ -204,26 +205,15 @@ export class ApplicationStore<T extends LegendApplicationConfig> {
204
205
  );
205
206
  }
206
207
 
207
- notifyError(
208
- content: unknown,
209
- actions?: NotificationAction[],
210
- autoHideDuration?: number | null,
211
- ): void {
208
+ notifyError(content: Error | string, actions?: NotificationAction[]): void {
212
209
  let message: string | undefined;
213
- if (content instanceof Error || content instanceof ApplicationError) {
210
+ if (content instanceof ApplicationError) {
211
+ message = content.getFullErrorMessage();
212
+ } else if (content instanceof Error) {
214
213
  message = content.message;
215
- } else if (isString(content)) {
216
- message = content;
217
214
  } else {
218
- message = undefined;
219
- this.log.error(
220
- LogEvent.create(
221
- APPLICATION_LOG_EVENT.ILLEGAL_APPLICATION_STATE_OCCURRED,
222
- ),
223
- 'Unable to display error in notification',
224
- message,
225
- );
226
- this.notifyIllegalState('Unable to display error');
215
+ assertTrue(isString(content), `Can't display error`);
216
+ message = content;
227
217
  }
228
218
  if (message) {
229
219
  this.setNotification(
@@ -231,9 +221,7 @@ export class ApplicationStore<T extends LegendApplicationConfig> {
231
221
  NOTIFCATION_SEVERITY.ERROR,
232
222
  message,
233
223
  actions ?? [],
234
- autoHideDuration === null
235
- ? undefined
236
- : autoHideDuration ?? DEFAULT_NOTIFICATION_HIDE_TIME,
224
+ undefined,
237
225
  ),
238
226
  );
239
227
  }
@@ -20,6 +20,20 @@ import { guaranteeNonNullable } from '@finos/legend-shared';
20
20
  /**
21
21
  * This is an initial attempt to try to generalize the application
22
22
  * to other platforms. But regardless, this is more convenient for testing.
23
+ *
24
+ * FIXME: this is not the right way to do this. Our intention here is to make
25
+ * app navigator something generic enough so we are somewhat platform-agnostic
26
+ * i.e. browser, electron, PC, UNIX, etc.
27
+ *
28
+ * Parameterize on the type of the location might not be the best thing to do.
29
+ * Because typing wise, this forces us to also parameterize consumers of this,
30
+ * which is `ApplicationStore`. It means that we must dictate in the source
31
+ * code the platform the app depends on, clearly for web browser, `string` is the
32
+ * easy option, but if we do so, it defeats the purpose of this abstraction in the
33
+ * first place.
34
+ *
35
+ * As such, instead, we should design a more generic concept `Location` to pass around.
36
+ * We would need to flesh out the details, but this is the rough idea.
23
37
  */
24
38
  interface ApplicationNavigator<T> {
25
39
  reload(): void;
@@ -27,6 +41,8 @@ interface ApplicationNavigator<T> {
27
41
  jumpTo(location: T): void;
28
42
  openNewWindow(location: T): void;
29
43
  getCurrentLocation(): T;
44
+ getCurrentLocationPath(): T;
45
+ generateLocation(locationPath: T): T;
30
46
  }
31
47
 
32
48
  export class WebApplicationNavigator implements ApplicationNavigator<string> {
@@ -63,10 +79,14 @@ export class WebApplicationNavigator implements ApplicationNavigator<string> {
63
79
  return this.window.location.href;
64
80
  }
65
81
 
66
- generateLocation(location: string): string {
82
+ getCurrentLocationPath(): string {
83
+ return this.historyAPI.location.pathname;
84
+ }
85
+
86
+ generateLocation(locationPath: string): string {
67
87
  return (
68
88
  window.location.origin +
69
- this.historyAPI.createHref({ pathname: location })
89
+ this.historyAPI.createHref({ pathname: locationPath })
70
90
  );
71
91
  }
72
92
  }
package/tsconfig.json CHANGED
@@ -22,6 +22,7 @@
22
22
  "allowSyntheticDefaultImports": true,
23
23
  "strict": true,
24
24
  "noImplicitOverride": true,
25
+ "noUncheckedIndexedAccess": true,
25
26
  "exactOptionalPropertyTypes": true,
26
27
  "forceConsistentCasingInFileNames": true,
27
28
  "outDir": "./lib",
@@ -29,11 +30,6 @@
29
30
  "rootDir": "./src",
30
31
  "jsx": "react-jsx"
31
32
  },
32
- "references": [
33
- {
34
- "path": "./tsconfig.package.json"
35
- }
36
- ],
37
33
  "files": [
38
34
  "./src/const.ts",
39
35
  "./src/index.ts",
@@ -49,6 +45,7 @@
49
45
  "./src/stores/WebApplicationNavigator.ts",
50
46
  "./src/application/LegendApplication.tsx",
51
47
  "./src/components/ActionAlert.tsx",
48
+ "./src/components/AppHeader.tsx",
52
49
  "./src/components/ApplicationBackdrop.tsx",
53
50
  "./src/components/ApplicationStoreProvider.tsx",
54
51
  "./src/components/ApplicationStoreProviderTestUtils.tsx",
@@ -1,40 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "lib": [
4
- "dom",
5
- "dom.iterable",
6
- "esnext",
7
- "webworker",
8
- "scripthost"
9
- ],
10
- "composite": true,
11
- "declaration": true,
12
- "declarationMap": true,
13
- "sourceMap": true,
14
- "target": "esnext",
15
- "module": "esnext",
16
- "skipLibCheck": true,
17
- "moduleResolution": "node",
18
- "resolveJsonModule": true,
19
- "isolatedModules": true,
20
- "importsNotUsedAsValues": "error",
21
- "esModuleInterop": true,
22
- "allowSyntheticDefaultImports": true,
23
- "strict": true,
24
- "noImplicitOverride": true,
25
- "exactOptionalPropertyTypes": true,
26
- "forceConsistentCasingInFileNames": true,
27
- "outDir": "./lib",
28
- "tsBuildInfoFile": "./build/package.tsbuildinfo",
29
- "rootDir": "./"
30
- },
31
- "files": [
32
- "./package.json"
33
- ],
34
- "include": [
35
- "package.json"
36
- ],
37
- "exclude": [
38
- "lib"
39
- ]
40
- }