datajunction-ui 0.0.1-rc.24 → 0.0.1-rc.25

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 (36) hide show
  1. package/.env +1 -0
  2. package/package.json +3 -2
  3. package/src/app/components/Tab.jsx +0 -1
  4. package/src/app/constants.js +2 -2
  5. package/src/app/icons/LoadingIcon.jsx +14 -0
  6. package/src/app/index.tsx +11 -1
  7. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +28 -2
  8. package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +44 -9
  9. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +1 -0
  10. package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +0 -50
  11. package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +2 -0
  12. package/src/app/pages/AddEditNodePage/index.jsx +60 -6
  13. package/src/app/pages/AddEditTagPage/Loadable.jsx +16 -0
  14. package/src/app/pages/AddEditTagPage/__tests__/AddEditTagPage.test.jsx +107 -0
  15. package/src/app/pages/AddEditTagPage/index.jsx +132 -0
  16. package/src/app/pages/LoginPage/LoginForm.jsx +116 -0
  17. package/src/app/pages/LoginPage/SignupForm.jsx +144 -0
  18. package/src/app/pages/LoginPage/__tests__/index.test.jsx +34 -2
  19. package/src/app/pages/LoginPage/index.jsx +9 -82
  20. package/src/app/pages/NamespacePage/index.jsx +5 -0
  21. package/src/app/pages/NodePage/EditColumnPopover.jsx +1 -1
  22. package/src/app/pages/NodePage/NodeColumnTab.jsx +11 -0
  23. package/src/app/pages/NodePage/NodeInfoTab.jsx +5 -1
  24. package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +6 -3
  25. package/src/app/pages/RegisterTablePage/__tests__/__snapshots__/RegisterTablePage.test.jsx.snap +1 -0
  26. package/src/app/pages/Root/index.tsx +1 -1
  27. package/src/app/pages/TagPage/Loadable.jsx +16 -0
  28. package/src/app/pages/TagPage/__tests__/TagPage.test.jsx +70 -0
  29. package/src/app/pages/TagPage/index.jsx +79 -0
  30. package/src/app/services/DJService.js +79 -1
  31. package/src/app/services/__tests__/DJService.test.jsx +84 -1
  32. package/src/mocks/mockNodes.jsx +88 -44
  33. package/src/styles/index.css +19 -0
  34. package/src/styles/loading.css +35 -0
  35. package/src/styles/login.css +17 -3
  36. package/src/utils/form.jsx +2 -2
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Add or edit tags
3
+ */
4
+ import { ErrorMessage, Field, Form, Formik } from 'formik';
5
+
6
+ import NamespaceHeader from '../../components/NamespaceHeader';
7
+ import React, { useContext } from 'react';
8
+ import DJClientContext from '../../providers/djclient';
9
+ import 'styles/node-creation.scss';
10
+ import { displayMessageAfterSubmit } from '../../../utils/form';
11
+
12
+ export function AddEditTagPage() {
13
+ const djClient = useContext(DJClientContext).DataJunctionAPI;
14
+ const initialValues = {
15
+ name: '',
16
+ };
17
+
18
+ const validator = values => {
19
+ const errors = {};
20
+ if (!values.name) {
21
+ errors.name = 'Required';
22
+ }
23
+ return errors;
24
+ };
25
+
26
+ const handleSubmit = async (values, { setSubmitting, setStatus }) => {
27
+ const { status, json } = await djClient.addTag(
28
+ values.name,
29
+ values.display_name,
30
+ values.tag_type,
31
+ values.description,
32
+ );
33
+ if (status === 200 || status === 201) {
34
+ setStatus({
35
+ success: (
36
+ <>
37
+ Successfully added tag{' '}
38
+ <a href={`/tags/${json.name}`}>{json.display_name}</a>.
39
+ </>
40
+ ),
41
+ });
42
+ } else {
43
+ setStatus({
44
+ failure: `${json.message}`,
45
+ });
46
+ }
47
+ setSubmitting(false);
48
+ window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
49
+ };
50
+
51
+ return (
52
+ <div className="mid">
53
+ <NamespaceHeader namespace="" />
54
+ <div className="card">
55
+ <div className="card-header">
56
+ <h2>
57
+ Add{' '}
58
+ <span className={`node_type__source node_type_creation_heading`}>
59
+ Tag
60
+ </span>
61
+ </h2>
62
+ <center>
63
+ <Formik
64
+ initialValues={initialValues}
65
+ validate={validator}
66
+ onSubmit={handleSubmit}
67
+ >
68
+ {function Render({ isSubmitting, status }) {
69
+ return (
70
+ <Form>
71
+ {displayMessageAfterSubmit(status)}
72
+ {
73
+ <>
74
+ <div className="NodeCreationInput">
75
+ <ErrorMessage name="name" component="span" />
76
+ <label htmlFor="name">Name</label>
77
+ <Field
78
+ type="text"
79
+ name="name"
80
+ id="name"
81
+ placeholder="Tag Name"
82
+ />
83
+ </div>
84
+ <br />
85
+ <div className="FullNameInput NodeCreationInput">
86
+ <ErrorMessage name="display_name" component="span" />
87
+ <label htmlFor="display_name">Display Name</label>
88
+ <Field
89
+ type="text"
90
+ name="display_name"
91
+ id="display_name"
92
+ placeholder="Display Name"
93
+ class="FullNameField"
94
+ />
95
+ </div>
96
+ <br />
97
+ <div className="NodeCreationInput">
98
+ <ErrorMessage name="tag_type" component="span" />
99
+ <label htmlFor="tag_type">Tag Type</label>
100
+ <Field
101
+ type="text"
102
+ name="tag_type"
103
+ id="tag_type"
104
+ placeholder="Tag Type"
105
+ />
106
+ </div>
107
+ <div className="DescriptionInput NodeCreationInput">
108
+ <ErrorMessage name="description" component="span" />
109
+ <label htmlFor="description">Description</label>
110
+ <Field
111
+ type="textarea"
112
+ as="textarea"
113
+ name="description"
114
+ id="Description"
115
+ placeholder="Describe the tag"
116
+ />
117
+ </div>
118
+ <button type="submit" disabled={isSubmitting}>
119
+ Add Tag
120
+ </button>
121
+ </>
122
+ }
123
+ </Form>
124
+ );
125
+ }}
126
+ </Formik>
127
+ </center>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ );
132
+ }
@@ -0,0 +1,116 @@
1
+ import { useState } from 'react';
2
+ import { Formik, Form, Field, ErrorMessage } from 'formik';
3
+ import '../../../styles/login.css';
4
+ import LoadingIcon from '../../icons/LoadingIcon';
5
+ import logo from '../Root/assets/dj-logo.png';
6
+ import GitHubLoginButton from './assets/sign-in-with-github.png';
7
+ import GoogleLoginButton from './assets/sign-in-with-google.png';
8
+ import * as Yup from 'yup';
9
+
10
+ const githubLoginURL = new URL('/github/login/', process.env.REACT_APP_DJ_URL);
11
+ const googleLoginURL = new URL('/google/login/', process.env.REACT_APP_DJ_URL);
12
+
13
+ const LoginSchema = Yup.object().shape({
14
+ username: Yup.string()
15
+ .min(2, 'Must be at least 2 characters')
16
+ .required('Username is required'),
17
+ password: Yup.string().required('Password is required'),
18
+ });
19
+
20
+ export default function LoginForm({ setShowSignup }) {
21
+ const [, setError] = useState('');
22
+
23
+ // Add the path that the user was trying to access in order to properly redirect after auth
24
+ githubLoginURL.searchParams.append('target', window.location.pathname);
25
+ googleLoginURL.searchParams.append('target', window.location.pathname);
26
+
27
+ const handleBasicLogin = async ({ username, password }) => {
28
+ const data = new FormData();
29
+ data.append('username', username);
30
+ data.append('password', password);
31
+ await fetch(`${process.env.REACT_APP_DJ_URL}/basic/login/`, {
32
+ method: 'POST',
33
+ body: data,
34
+ credentials: 'include',
35
+ }).catch(error => {
36
+ setError(error ? JSON.stringify(error) : '');
37
+ });
38
+ window.location.reload();
39
+ };
40
+
41
+ return (
42
+ <Formik
43
+ initialValues={{
44
+ username: '',
45
+ password: '',
46
+ target: window.location.pathname,
47
+ }}
48
+ validationSchema={LoginSchema}
49
+ onSubmit={(values, { setSubmitting }) => {
50
+ setTimeout(() => {
51
+ handleBasicLogin(values);
52
+ setSubmitting(false);
53
+ }, 400);
54
+ }}
55
+ >
56
+ {({ isSubmitting }) => (
57
+ <Form>
58
+ <div className="logo-title">
59
+ <img src={logo} alt="DJ Logo" width="75px" height="75px" />
60
+ <h2>DataJunction</h2>
61
+ </div>
62
+ <div>
63
+ <Field type="text" name="username" placeholder="Username" />
64
+ </div>
65
+ <div>
66
+ <ErrorMessage className="form-error" name="username" component="span" />
67
+ </div>
68
+ <div>
69
+ <Field type="password" name="password" placeholder="Password" />
70
+ </div>
71
+ <div>
72
+ <ErrorMessage className="form-error" name="password" component="span" />
73
+ </div>
74
+ <div>
75
+ <p>
76
+ Don't have an account yet?{' '}
77
+ <a onClick={() => setShowSignup(true)}>Sign Up</a>
78
+ </p>
79
+ </div>
80
+ <button type="submit" disabled={isSubmitting}>
81
+ {isSubmitting ? <LoadingIcon /> : 'Login'}
82
+ </button>
83
+ <div>
84
+ <p>Or</p>
85
+ </div>
86
+ {process.env.REACT_ENABLE_GITHUB_OAUTH === 'true' ? (
87
+ <div>
88
+ <a href={githubLoginURL.href}>
89
+ <img
90
+ src={GitHubLoginButton}
91
+ alt="Sign in with GitHub"
92
+ width="200px"
93
+ />
94
+ </a>
95
+ </div>
96
+ ) : (
97
+ ''
98
+ )}
99
+ {process.env.REACT_ENABLE_GOOGLE_OAUTH === 'true' ? (
100
+ <div>
101
+ <a href={googleLoginURL.href}>
102
+ <img
103
+ src={GoogleLoginButton}
104
+ alt="Sign in with Google"
105
+ width="200px"
106
+ />
107
+ </a>
108
+ </div>
109
+ ) : (
110
+ ''
111
+ )}
112
+ </Form>
113
+ )}
114
+ </Formik>
115
+ );
116
+ }
@@ -0,0 +1,144 @@
1
+ import { useState } from 'react';
2
+ import { Formik, Form, Field, ErrorMessage } from 'formik';
3
+ import '../../../styles/login.css';
4
+ import logo from '../Root/assets/dj-logo.png';
5
+ import LoadingIcon from '../../icons/LoadingIcon';
6
+ import GitHubLoginButton from './assets/sign-in-with-github.png';
7
+ import GoogleLoginButton from './assets/sign-in-with-google.png';
8
+ import * as Yup from 'yup';
9
+
10
+ const githubLoginURL = new URL('/github/login/', process.env.REACT_APP_DJ_URL);
11
+ const googleLoginURL = new URL('/google/login/', process.env.REACT_APP_DJ_URL);
12
+
13
+ const SignupSchema = Yup.object().shape({
14
+ email: Yup.string().email('Invalid email').required('Email is required'),
15
+ signupUsername: Yup.string()
16
+ .min(3, 'Must be at least 2 characters')
17
+ .max(20, 'Must be less than 20 characters')
18
+ .required('Username is required'),
19
+ signupPassword: Yup.string().required('Password is required'),
20
+ });
21
+
22
+ export default function SignupForm({ setShowSignup }) {
23
+ const [, setError] = useState('');
24
+
25
+ // Add the path that the user was trying to access in order to properly redirect after auth
26
+ githubLoginURL.searchParams.append('target', window.location.pathname);
27
+ googleLoginURL.searchParams.append('target', window.location.pathname);
28
+
29
+ const handleBasicSignup = async ({
30
+ email,
31
+ signupUsername,
32
+ signupPassword,
33
+ }) => {
34
+ const data = new FormData();
35
+ data.append('email', email);
36
+ data.append('username', signupUsername);
37
+ data.append('password', signupPassword);
38
+ await fetch(`${process.env.REACT_APP_DJ_URL}/basic/user/`, {
39
+ method: 'POST',
40
+ body: data,
41
+ credentials: 'include',
42
+ }).catch(error => {
43
+ setError(error ? JSON.stringify(error) : '');
44
+ });
45
+ const loginData = new FormData();
46
+ loginData.append('username', signupUsername);
47
+ loginData.append('password', signupPassword);
48
+ await fetch(`${process.env.REACT_APP_DJ_URL}/basic/login/`, {
49
+ method: 'POST',
50
+ body: data,
51
+ credentials: 'include',
52
+ }).catch(error => {
53
+ setError(error ? JSON.stringify(error) : '');
54
+ });
55
+ window.location.reload();
56
+ };
57
+
58
+ return (
59
+ <Formik
60
+ initialValues={{
61
+ email: '',
62
+ signupUsername: '',
63
+ signupPassword: '',
64
+ target: window.location.pathname,
65
+ }}
66
+ validationSchema={SignupSchema}
67
+ onSubmit={(values, { setSubmitting }) => {
68
+ setTimeout(() => {
69
+ handleBasicSignup(values);
70
+ setSubmitting(false);
71
+ }, 400);
72
+ }}
73
+ >
74
+ {({ isSubmitting }) => (
75
+ <Form>
76
+ <div className="logo-title">
77
+ <img src={logo} alt="DJ Logo" width="75px" height="75px" />
78
+ <h2>DataJunction</h2>
79
+ </div>
80
+ <div>
81
+ <Field type="text" name="email" placeholder="Email" />
82
+ </div>
83
+ <div>
84
+ <ErrorMessage className="form-error" name="email" component="span" />
85
+ </div>
86
+ <div>
87
+ <Field type="text" name="signupUsername" placeholder="Username" />
88
+ </div>
89
+ <div>
90
+ <ErrorMessage className="form-error" name="signupUsername" component="span" />
91
+ </div>
92
+ <div>
93
+ <Field
94
+ type="password"
95
+ name="signupPassword"
96
+ placeholder="Password"
97
+ />
98
+ </div>
99
+ <div>
100
+ <ErrorMessage className="form-error" name="signupPassword" component="span" />
101
+ </div>
102
+ <div>
103
+ <p>
104
+ Have an account already?{' '}
105
+ <a onClick={() => setShowSignup(false)}>Login</a>
106
+ </p>
107
+ </div>
108
+ <button type="submit" disabled={isSubmitting}>
109
+ {isSubmitting ? <LoadingIcon /> : 'Sign Up'}
110
+ </button>
111
+ <div>
112
+ <p>Or</p>
113
+ </div>
114
+ {process.env.REACT_ENABLE_GITHUB_OAUTH === 'true' ? (
115
+ <div>
116
+ <a href={githubLoginURL.href}>
117
+ <img
118
+ src={GitHubLoginButton}
119
+ alt="Sign in with GitHub"
120
+ width="200px"
121
+ />
122
+ </a>
123
+ </div>
124
+ ) : (
125
+ ''
126
+ )}
127
+ {process.env.REACT_ENABLE_GOOGLE_OAUTH === 'true' ? (
128
+ <div>
129
+ <a href={googleLoginURL.href}>
130
+ <img
131
+ src={GoogleLoginButton}
132
+ alt="Sign in with Google"
133
+ width="200px"
134
+ />
135
+ </a>
136
+ </div>
137
+ ) : (
138
+ ''
139
+ )}
140
+ </Form>
141
+ )}
142
+ </Formik>
143
+ );
144
+ }
@@ -33,11 +33,12 @@ describe('LoginPage', () => {
33
33
 
34
34
  await waitFor(() => {
35
35
  expect(getByText('DataJunction')).toBeInTheDocument();
36
- expect(queryAllByText('Required').length).toEqual(2);
36
+ expect(getByText('Username is required')).toBeInTheDocument();
37
+ expect(getByText('Password is required')).toBeInTheDocument();
37
38
  });
38
39
  });
39
40
 
40
- it('calls fetch with correct data on submit', async () => {
41
+ it('calls fetch with correct data on login', async () => {
41
42
  const username = 'testUser';
42
43
  const password = 'testPassword';
43
44
 
@@ -62,4 +63,35 @@ describe('LoginPage', () => {
62
63
  expect(window.location.reload).toHaveBeenCalled();
63
64
  });
64
65
  });
66
+
67
+ it('calls fetch with correct data on signup', async () => {
68
+ const email = 'testEmail@testEmail.com';
69
+ const username = 'testUser';
70
+ const password = 'testPassword';
71
+
72
+ const { getByText, getByPlaceholderText } = render(<LoginPage />);
73
+ fireEvent.click(getByText('Sign Up'));
74
+ fireEvent.change(getByPlaceholderText('Email'), {
75
+ target: { value: email },
76
+ });
77
+ fireEvent.change(getByPlaceholderText('Username'), {
78
+ target: { value: username },
79
+ });
80
+ fireEvent.change(getByPlaceholderText('Password'), {
81
+ target: { value: password },
82
+ });
83
+ fireEvent.click(getByText('Sign Up'));
84
+
85
+ await waitFor(() => {
86
+ expect(fetch).toHaveBeenCalledWith(
87
+ `${process.env.REACT_APP_DJ_URL}/basic/user/`,
88
+ expect.objectContaining({
89
+ method: 'POST',
90
+ body: expect.any(FormData),
91
+ credentials: 'include',
92
+ }),
93
+ );
94
+ expect(window.location.reload).toHaveBeenCalled();
95
+ });
96
+ });
65
97
  });
@@ -1,90 +1,17 @@
1
1
  import { useState } from 'react';
2
- import { Formik, Form, Field, ErrorMessage } from 'formik';
3
2
  import '../../../styles/login.css';
4
- import logo from '../Root/assets/dj-logo.png';
5
- import GitHubLoginButton from './assets/sign-in-with-github.png';
3
+ import SignupForm from './SignupForm';
4
+ import LoginForm from './LoginForm';
6
5
 
7
6
  export function LoginPage() {
8
- const [, setError] = useState('');
9
- const githubLoginURL = new URL('/github/login/', process.env.REACT_APP_DJ_URL)
10
- .href;
11
-
12
- const handleBasicLogin = async ({ username, password }) => {
13
- const data = new FormData();
14
- data.append('username', username);
15
- data.append('password', password);
16
- await fetch(`${process.env.REACT_APP_DJ_URL}/basic/login/`, {
17
- method: 'POST',
18
- body: data,
19
- credentials: 'include',
20
- }).catch(error => {
21
- setError(error ? JSON.stringify(error) : '');
22
- });
23
- window.location.reload();
24
- };
25
-
7
+ const [showSignup, setShowSignup] = useState(false);
26
8
  return (
27
- <div className="container">
28
- <div className="login">
29
- <center>
30
- <Formik
31
- initialValues={{ username: '', password: '' }}
32
- validate={values => {
33
- const errors = {};
34
- if (!values.username) {
35
- errors.username = 'Required';
36
- }
37
- if (!values.password) {
38
- errors.password = 'Required';
39
- }
40
- return errors;
41
- }}
42
- onSubmit={(values, { setSubmitting }) => {
43
- setTimeout(() => {
44
- handleBasicLogin(values);
45
- setSubmitting(false);
46
- }, 400);
47
- }}
48
- >
49
- {({ isSubmitting }) => (
50
- <Form>
51
- <div className="logo-title">
52
- <img src={logo} alt="DJ Logo" width="75px" height="75px" />
53
- <h2>DataJunction</h2>
54
- </div>
55
- <div className="inputContainer">
56
- <ErrorMessage name="username" component="span" />
57
- <Field type="text" name="username" placeholder="Username" />
58
- </div>
59
- <div>
60
- <ErrorMessage name="password" component="span" />
61
- <Field
62
- type="password"
63
- name="password"
64
- placeholder="Password"
65
- />
66
- </div>
67
- <button type="submit" disabled={isSubmitting}>
68
- Login
69
- </button>
70
- {process.env.REACT_ENABLE_GITHUB_OAUTH === 'true' ? (
71
- <div>
72
- <a href={githubLoginURL}>
73
- <img
74
- src={GitHubLoginButton}
75
- alt="Sign in with GitHub"
76
- width="200px"
77
- />
78
- </a>
79
- </div>
80
- ) : (
81
- ''
82
- )}
83
- </Form>
84
- )}
85
- </Formik>
86
- </center>
87
- </div>
9
+ <div className="container login">
10
+ {showSignup ? (
11
+ <SignupForm setShowSignup={setShowSignup} />
12
+ ) : (
13
+ <LoginForm setShowSignup={setShowSignup} />
14
+ )}
88
15
  </div>
89
16
  );
90
17
  }
@@ -145,6 +145,11 @@ export function NamespacePage() {
145
145
  Dimension
146
146
  </div>
147
147
  </a>
148
+ <a href={`/create/tag`}>
149
+ <div className="entity__tag node_type_creation_heading">
150
+ Tag
151
+ </div>
152
+ </a>
148
153
  </div>
149
154
  </div>
150
155
  </span>
@@ -1,4 +1,4 @@
1
- import {useContext, useEffect, useRef, useState} from 'react';
1
+ import { useContext, useEffect, useRef, useState } from 'react';
2
2
  import * as React from 'react';
3
3
  import DJClientContext from '../../providers/djclient';
4
4
  import { Form, Formik } from 'formik';
@@ -60,6 +60,16 @@ export default function NodeColumnTab({ node, djClient }) {
60
60
  >
61
61
  {col.name}
62
62
  </td>
63
+ <td>
64
+ <span
65
+ className=""
66
+ role="columnheader"
67
+ aria-label="ColumnDisplayName"
68
+ aria-hidden="false"
69
+ >
70
+ {col.display_name}
71
+ </span>
72
+ </td>
63
73
  <td>
64
74
  <span
65
75
  className="node_type__transform badge node_type"
@@ -111,6 +121,7 @@ export default function NodeColumnTab({ node, djClient }) {
111
121
  <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
112
122
  <tr>
113
123
  <th className="text-start">Column</th>
124
+ <th>Display Name</th>
114
125
  <th>Type</th>
115
126
  <th>Linked Dimension</th>
116
127
  <th>Attributes</th>
@@ -13,7 +13,11 @@ foundation.hljs['padding'] = '2rem';
13
13
  export default function NodeInfoTab({ node }) {
14
14
  const [compiledSQL, setCompiledSQL] = useState('');
15
15
  const [checked, setChecked] = useState(false);
16
- const nodeTags = node?.tags.map(tag => <div>{tag}</div>);
16
+ const nodeTags = node?.tags.map(tag => (
17
+ <div className={'badge tag_value'}>
18
+ <a href={`/tags/${tag.name}`}>{tag.display_name}</a>
19
+ </div>
20
+ ));
17
21
  const djClient = useContext(DJClientContext).DataJunctionAPI;
18
22
  useEffect(() => {
19
23
  const fetchData = async () => {
@@ -70,6 +70,7 @@ describe('<NodePage />', () => {
70
70
  {
71
71
  name: 'default_DOT_avg_repair_price',
72
72
  type: 'double',
73
+ display_name: 'Default DOT avg repair price',
73
74
  attributes: [],
74
75
  dimension: null,
75
76
  },
@@ -82,7 +83,7 @@ describe('<NodePage />', () => {
82
83
  },
83
84
  ],
84
85
  created_at: '2023-08-21T16:48:56.932162+00:00',
85
- tags: [],
86
+ tags: [{ name: 'purpose', display_name: 'Purpose' }],
86
87
  primary_key: [],
87
88
  createNodeClientCode:
88
89
  'dj = DJBuilder(DJ_URL)\n\navg_repair_price = dj.create_metric(\n description="Average repair price",\n display_name="Default: Avg Repair Price",\n name="default.avg_repair_price",\n primary_key=[],\n query="""SELECT avg(price) default_DOT_avg_repair_price \n FROM default.repair_order_details\n\n"""\n)',
@@ -303,13 +304,12 @@ describe('<NodePage />', () => {
303
304
  'v1.0',
304
305
  );
305
306
 
306
- // expect(screen.getByRole('dialog', { name: 'Table' })).not.toBeInTheDocument();
307
307
  expect(
308
308
  screen.getByRole('dialog', { name: 'NodeStatus' }),
309
309
  ).toBeInTheDocument();
310
310
 
311
311
  expect(screen.getByRole('dialog', { name: 'Tags' })).toHaveTextContent(
312
- '',
312
+ 'Purpose',
313
313
  );
314
314
 
315
315
  expect(
@@ -416,6 +416,9 @@ describe('<NodePage />', () => {
416
416
  expect(
417
417
  screen.getByRole('columnheader', { name: 'ColumnName' }),
418
418
  ).toHaveTextContent('default_DOT_avg_repair_price');
419
+ expect(
420
+ screen.getByRole('columnheader', { name: 'ColumnDisplayName' }),
421
+ ).toHaveTextContent('Default DOT avg repair price');
419
422
  expect(
420
423
  screen.getByRole('columnheader', { name: 'ColumnType' }),
421
424
  ).toHaveTextContent('double');
@@ -4,6 +4,7 @@ exports[`<RegisterTablePage /> registers a table correctly 1`] = `
4
4
  HTMLCollection [
5
5
  <div
6
6
  class="message success"
7
+ data-testid="success"
7
8
  >
8
9
  <svg
9
10
  class="bi bi-check-circle-fill"
@@ -59,7 +59,7 @@ export function Root() {
59
59
  ) : (
60
60
  <span className="menu-link">
61
61
  <span className="menu-title">
62
- <button onClick={handleLogout}>Logout</button>
62
+ <a onClick={handleLogout}>Logout</a>
63
63
  </span>
64
64
  </span>
65
65
  )}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Asynchronously loads the component for the Node page
3
+ */
4
+
5
+ import * as React from 'react';
6
+ import { lazyLoad } from '../../../utils/loadable';
7
+
8
+ export const TagPage = () => {
9
+ return lazyLoad(
10
+ () => import('./index'),
11
+ module => module.TagPage,
12
+ {
13
+ fallback: <div></div>,
14
+ },
15
+ )();
16
+ };