datajunction-ui 0.0.1-rc.19 → 0.0.1-rc.20
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 +6 -5
- package/src/__tests__/reportWebVitals.test.jsx +44 -0
- package/src/app/components/DeleteNode.jsx +79 -0
- package/src/app/components/ListGroupItem.jsx +8 -1
- package/src/app/components/QueryInfo.jsx +4 -4
- package/src/app/components/Tab.jsx +9 -1
- package/src/app/components/ToggleSwitch.jsx +3 -0
- package/src/app/components/__tests__/QueryInfo.test.jsx +55 -0
- package/src/app/components/__tests__/Tab.test.jsx +27 -0
- package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
- package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +3 -0
- package/src/app/components/djgraph/DJNodeColumns.jsx +4 -1
- package/src/app/components/djgraph/DJNodeDimensions.jsx +9 -2
- package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
- package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
- package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
- package/src/app/index.tsx +6 -0
- package/src/app/pages/AddEditNodePage/FormikSelect.jsx +15 -2
- package/src/app/pages/AddEditNodePage/FullNameField.jsx +2 -1
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +77 -0
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +93 -0
- package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +34 -3
- package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +4 -2
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +53 -0
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +53 -0
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +0 -81
- package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +82 -257
- package/src/app/pages/AddEditNodePage/index.jsx +13 -5
- package/src/app/pages/LoginPage/__tests__/index.test.jsx +70 -0
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +95 -0
- package/src/app/pages/NamespacePage/index.jsx +8 -5
- package/src/app/pages/NodePage/ClientCodePopover.jsx +3 -1
- package/src/app/pages/NodePage/EditColumnPopover.jsx +102 -0
- package/src/app/pages/NodePage/LinkDimensionPopover.jsx +135 -0
- package/src/app/pages/NodePage/NodeColumnTab.jsx +80 -17
- package/src/app/pages/NodePage/NodeHistory.jsx +63 -30
- package/src/app/pages/NodePage/NodeInfoTab.jsx +52 -7
- package/src/app/pages/NodePage/NodeMaterializationTab.jsx +50 -27
- package/src/app/pages/NodePage/NodeSQLTab.jsx +0 -10
- package/src/app/pages/NodePage/NodesWithDimension.jsx +4 -2
- package/src/app/pages/NodePage/__tests__/ClientCodePopover.test.jsx +49 -0
- package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +148 -0
- package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +165 -0
- package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +591 -0
- package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +57 -0
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +725 -0
- package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
- package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +402 -0
- package/src/app/pages/NodePage/index.jsx +22 -6
- package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
- package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
- package/src/app/pages/RegisterTablePage/index.jsx +163 -0
- package/src/app/pages/Root/__tests__/index.test.jsx +77 -0
- package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
- package/src/app/pages/SQLBuilderPage/index.jsx +61 -43
- package/src/app/services/DJService.js +125 -54
- package/src/app/services/__tests__/DJService.test.jsx +609 -0
- package/src/mocks/mockNodes.jsx +1397 -0
- package/src/setupTests.ts +30 -0
- package/src/styles/index.css +43 -0
- package/src/styles/node-creation.scss +7 -0
- package/src/utils/form.jsx +23 -0
- package/.github/pull_request_template.md +0 -11
- package/.github/workflows/ci.yml +0 -33
- package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -118
- package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node add + edit page for transforms, metrics, and dimensions. The creation and edit flow for these
|
|
3
|
+
* node types is largely the same, with minor differences handled server-side. For the `query`
|
|
4
|
+
* field, this page will render a CodeMirror SQL editor with autocompletion and syntax highlighting.
|
|
5
|
+
*/
|
|
6
|
+
import { ErrorMessage, Field, Form, Formik } from 'formik';
|
|
7
|
+
|
|
8
|
+
import NamespaceHeader from '../../components/NamespaceHeader';
|
|
9
|
+
import React, { useContext, useEffect, useState } from 'react';
|
|
10
|
+
import DJClientContext from '../../providers/djclient';
|
|
11
|
+
import 'styles/node-creation.scss';
|
|
12
|
+
import AlertIcon from '../../icons/AlertIcon';
|
|
13
|
+
import ValidIcon from '../../icons/ValidIcon';
|
|
14
|
+
import { FormikSelect } from '../AddEditNodePage/FormikSelect';
|
|
15
|
+
|
|
16
|
+
export function RegisterTablePage() {
|
|
17
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
18
|
+
const [catalogs, setCatalogs] = useState([]);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const fetchData = async () => {
|
|
22
|
+
const catalogs = await djClient.catalogs();
|
|
23
|
+
setCatalogs(
|
|
24
|
+
catalogs.map(catalog => {
|
|
25
|
+
return { value: catalog.name, label: catalog.name };
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
fetchData().catch(console.error);
|
|
30
|
+
}, [djClient, djClient.namespaces]);
|
|
31
|
+
|
|
32
|
+
const initialValues = {
|
|
33
|
+
catalog: '',
|
|
34
|
+
schema: '',
|
|
35
|
+
table: '',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const validator = values => {
|
|
39
|
+
const errors = {};
|
|
40
|
+
if (!values.table) {
|
|
41
|
+
errors.table = 'Required';
|
|
42
|
+
}
|
|
43
|
+
if (!values.schema) {
|
|
44
|
+
errors.schema = 'Required';
|
|
45
|
+
}
|
|
46
|
+
return errors;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const handleSubmit = async (values, { setSubmitting, setStatus }) => {
|
|
50
|
+
const { status, json } = await djClient.registerTable(
|
|
51
|
+
values.catalog,
|
|
52
|
+
values.schema,
|
|
53
|
+
values.table,
|
|
54
|
+
);
|
|
55
|
+
if (status === 200 || status === 201) {
|
|
56
|
+
setStatus({
|
|
57
|
+
success: (
|
|
58
|
+
<>
|
|
59
|
+
Successfully registered source node{' '}
|
|
60
|
+
<a href={`/nodes/${json.name}`}>{json.name}</a>, which references
|
|
61
|
+
table {values.catalog}.{values.schema}.{values.table}.
|
|
62
|
+
</>
|
|
63
|
+
),
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
setStatus({
|
|
67
|
+
failure: `${json.message}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
setSubmitting(false);
|
|
71
|
+
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const displayMessageAfterSubmit = status => {
|
|
75
|
+
return status?.success !== undefined ? (
|
|
76
|
+
<div className="message success">
|
|
77
|
+
<ValidIcon />
|
|
78
|
+
{status?.success}
|
|
79
|
+
</div>
|
|
80
|
+
) : status?.failure !== undefined ? (
|
|
81
|
+
alertMessage(status?.failure)
|
|
82
|
+
) : (
|
|
83
|
+
''
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const alertMessage = message => {
|
|
88
|
+
return (
|
|
89
|
+
<div className="message alert">
|
|
90
|
+
<AlertIcon />
|
|
91
|
+
{message}
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="mid">
|
|
98
|
+
<NamespaceHeader namespace="" />
|
|
99
|
+
<div className="card">
|
|
100
|
+
<div className="card-header">
|
|
101
|
+
<h2>
|
|
102
|
+
Register{' '}
|
|
103
|
+
<span className={`node_type__source node_type_creation_heading`}>
|
|
104
|
+
Source
|
|
105
|
+
</span>
|
|
106
|
+
</h2>
|
|
107
|
+
<center>
|
|
108
|
+
<Formik
|
|
109
|
+
initialValues={initialValues}
|
|
110
|
+
validate={validator}
|
|
111
|
+
onSubmit={handleSubmit}
|
|
112
|
+
>
|
|
113
|
+
{function Render({ isSubmitting, status, setFieldValue }) {
|
|
114
|
+
return (
|
|
115
|
+
<Form>
|
|
116
|
+
{displayMessageAfterSubmit(status)}
|
|
117
|
+
{
|
|
118
|
+
<>
|
|
119
|
+
<div className="SourceCreationInput">
|
|
120
|
+
<ErrorMessage name="catalog" component="span" />
|
|
121
|
+
<label htmlFor="catalog">Catalog</label>
|
|
122
|
+
<FormikSelect
|
|
123
|
+
selectOptions={catalogs}
|
|
124
|
+
formikFieldName="catalog"
|
|
125
|
+
placeholder="Choose Catalog"
|
|
126
|
+
defaultValue={catalogs[0]}
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
<div className="SourceCreationInput">
|
|
130
|
+
<ErrorMessage name="schema" component="span" />
|
|
131
|
+
<label htmlFor="schema">Schema</label>
|
|
132
|
+
<Field
|
|
133
|
+
type="text"
|
|
134
|
+
name="schema"
|
|
135
|
+
id="schema"
|
|
136
|
+
placeholder="Schema"
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
<div className="SourceCreationInput NodeCreationInput">
|
|
140
|
+
<ErrorMessage name="table" component="span" />
|
|
141
|
+
<label htmlFor="table">Table</label>
|
|
142
|
+
<Field
|
|
143
|
+
type="text"
|
|
144
|
+
name="table"
|
|
145
|
+
id="table"
|
|
146
|
+
placeholder="Table"
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
<button type="submit" disabled={isSubmitting}>
|
|
150
|
+
Register
|
|
151
|
+
</button>
|
|
152
|
+
</>
|
|
153
|
+
}
|
|
154
|
+
</Form>
|
|
155
|
+
);
|
|
156
|
+
}}
|
|
157
|
+
</Formik>
|
|
158
|
+
</center>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import { Root } from '../index';
|
|
4
|
+
import DJClientContext from '../../../providers/djclient';
|
|
5
|
+
import { HelmetProvider } from 'react-helmet-async';
|
|
6
|
+
|
|
7
|
+
describe('<Root />', () => {
|
|
8
|
+
const mockDjClient = {
|
|
9
|
+
logout: jest.fn(),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
it('renders with the correct title and navigation', async () => {
|
|
13
|
+
render(
|
|
14
|
+
<HelmetProvider>
|
|
15
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
16
|
+
<Root />
|
|
17
|
+
</DJClientContext.Provider>
|
|
18
|
+
</HelmetProvider>,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
waitFor(() => {
|
|
22
|
+
expect(document.title).toEqual('DataJunction');
|
|
23
|
+
const metaDescription = document.querySelector(
|
|
24
|
+
"meta[name='description']",
|
|
25
|
+
);
|
|
26
|
+
expect(metaDescription).toBeInTheDocument();
|
|
27
|
+
expect(metaDescription.content).toBe(
|
|
28
|
+
'DataJunction Metrics Platform Webapp',
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expect(screen.getByText(/^DataJunction$/)).toBeInTheDocument();
|
|
32
|
+
expect(screen.getByText('Explore').closest('a')).toHaveAttribute(
|
|
33
|
+
'href',
|
|
34
|
+
'/',
|
|
35
|
+
);
|
|
36
|
+
expect(screen.getByText('SQL').closest('a')).toHaveAttribute(
|
|
37
|
+
'href',
|
|
38
|
+
'/sql',
|
|
39
|
+
);
|
|
40
|
+
expect(screen.getByText('Docs').closest('a')).toHaveAttribute(
|
|
41
|
+
'href',
|
|
42
|
+
'https://www.datajunction.io',
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('renders Logout button unless REACT_DISABLE_AUTH is true', () => {
|
|
48
|
+
process.env.REACT_DISABLE_AUTH = 'false';
|
|
49
|
+
render(
|
|
50
|
+
<HelmetProvider>
|
|
51
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
52
|
+
<Root />
|
|
53
|
+
</DJClientContext.Provider>
|
|
54
|
+
</HelmetProvider>,
|
|
55
|
+
);
|
|
56
|
+
expect(screen.getByText('Logout')).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('calls logout and reloads window on logout button click', () => {
|
|
60
|
+
process.env.REACT_DISABLE_AUTH = 'false';
|
|
61
|
+
const originalLocation = window.location;
|
|
62
|
+
delete window.location;
|
|
63
|
+
window.location = { ...originalLocation, reload: jest.fn() };
|
|
64
|
+
|
|
65
|
+
render(
|
|
66
|
+
<HelmetProvider>
|
|
67
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
68
|
+
<Root />
|
|
69
|
+
</DJClientContext.Provider>
|
|
70
|
+
</HelmetProvider>,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
screen.getByText('Logout').click();
|
|
74
|
+
expect(mockDjClient.logout).toHaveBeenCalled();
|
|
75
|
+
window.location = originalLocation;
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
render,
|
|
3
|
+
screen,
|
|
4
|
+
fireEvent,
|
|
5
|
+
waitFor,
|
|
6
|
+
waitForElement,
|
|
7
|
+
act,
|
|
8
|
+
} from '@testing-library/react';
|
|
9
|
+
import DJClientContext from '../../../providers/djclient';
|
|
10
|
+
import { SQLBuilderPage } from '../index';
|
|
11
|
+
|
|
12
|
+
const mockDjClient = {
|
|
13
|
+
metrics: jest.fn(),
|
|
14
|
+
commonDimensions: jest.fn(),
|
|
15
|
+
sqls: jest.fn(),
|
|
16
|
+
data: jest.fn(),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const mockMetrics = [
|
|
20
|
+
'default.num_repair_orders',
|
|
21
|
+
'default.avg_repair_price',
|
|
22
|
+
'default.total_repair_cost',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const mockCommonDimensions = [
|
|
26
|
+
{
|
|
27
|
+
name: 'default.date_dim.dateint',
|
|
28
|
+
type: 'timestamp',
|
|
29
|
+
path: [
|
|
30
|
+
'default.repair_order_details.repair_order_id',
|
|
31
|
+
'default.repair_order.hard_hat_id',
|
|
32
|
+
'default.hard_hat.birth_date',
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'default.date_dim.dateint',
|
|
37
|
+
type: 'timestamp',
|
|
38
|
+
path: [
|
|
39
|
+
'default.repair_order_details.repair_order_id',
|
|
40
|
+
'default.repair_order.hard_hat_id',
|
|
41
|
+
'default.hard_hat.hire_date',
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'default.date_dim.day',
|
|
46
|
+
type: 'int',
|
|
47
|
+
path: [
|
|
48
|
+
'default.repair_order_details.repair_order_id',
|
|
49
|
+
'default.repair_order.hard_hat_id',
|
|
50
|
+
'default.hard_hat.birth_date',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'default.date_dim.day',
|
|
55
|
+
type: 'int',
|
|
56
|
+
path: [
|
|
57
|
+
'default.repair_order_details.repair_order_id',
|
|
58
|
+
'default.repair_order.hard_hat_id',
|
|
59
|
+
'default.hard_hat.hire_date',
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'default.date_dim.month',
|
|
64
|
+
type: 'int',
|
|
65
|
+
path: [
|
|
66
|
+
'default.repair_order_details.repair_order_id',
|
|
67
|
+
'default.repair_order.hard_hat_id',
|
|
68
|
+
'default.hard_hat.birth_date',
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'default.date_dim.month',
|
|
73
|
+
type: 'int',
|
|
74
|
+
path: [
|
|
75
|
+
'default.repair_order_details.repair_order_id',
|
|
76
|
+
'default.repair_order.hard_hat_id',
|
|
77
|
+
'default.hard_hat.hire_date',
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'default.date_dim.year',
|
|
82
|
+
type: 'int',
|
|
83
|
+
path: [
|
|
84
|
+
'default.repair_order_details.repair_order_id',
|
|
85
|
+
'default.repair_order.hard_hat_id',
|
|
86
|
+
'default.hard_hat.birth_date',
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'default.date_dim.year',
|
|
91
|
+
type: 'int',
|
|
92
|
+
path: [
|
|
93
|
+
'default.repair_order_details.repair_order_id',
|
|
94
|
+
'default.repair_order.hard_hat_id',
|
|
95
|
+
'default.hard_hat.hire_date',
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
describe('SQLBuilderPage', () => {
|
|
101
|
+
beforeEach(() => {
|
|
102
|
+
mockDjClient.metrics.mockResolvedValue(mockMetrics);
|
|
103
|
+
mockDjClient.commonDimensions.mockResolvedValue(mockCommonDimensions);
|
|
104
|
+
mockDjClient.sqls.mockResolvedValue({ sql: 'SELECT ...' });
|
|
105
|
+
mockDjClient.data.mockResolvedValue({});
|
|
106
|
+
|
|
107
|
+
render(
|
|
108
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
109
|
+
<SQLBuilderPage />
|
|
110
|
+
</DJClientContext.Provider>,
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
afterEach(() => {
|
|
115
|
+
jest.clearAllMocks();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('renders without crashing', () => {
|
|
119
|
+
expect(screen.getByText('Using the SQL Builder')).toBeInTheDocument();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('renders the Metrics section', () => {
|
|
123
|
+
expect(screen.getByText('Metrics')).toBeInTheDocument();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('renders the Group By section', () => {
|
|
127
|
+
expect(screen.getByText('Group By')).toBeInTheDocument();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('renders the Filter By section', () => {
|
|
131
|
+
expect(screen.getByText('Filter By')).toBeInTheDocument();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('fetches metrics on mount', async () => {
|
|
135
|
+
render(
|
|
136
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
137
|
+
<SQLBuilderPage />
|
|
138
|
+
</DJClientContext.Provider>,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(mockDjClient.metrics).toHaveBeenCalled();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const selectMetrics = screen.getAllByTestId('select-metrics')[0];
|
|
146
|
+
expect(selectMetrics).toBeDefined();
|
|
147
|
+
expect(selectMetrics).not.toBeNull();
|
|
148
|
+
expect(screen.getAllByText('3 Available Metrics')[0]).toBeInTheDocument();
|
|
149
|
+
|
|
150
|
+
fireEvent.keyDown(selectMetrics.firstChild, { key: 'ArrowDown' });
|
|
151
|
+
for (const metric of mockMetrics) {
|
|
152
|
+
await waitFor(() => {
|
|
153
|
+
expect(screen.getByText(metric)).toBeInTheDocument();
|
|
154
|
+
fireEvent.click(screen.getByText(metric));
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
fireEvent.click(screen.getAllByText('Group By')[0]);
|
|
158
|
+
|
|
159
|
+
expect(mockDjClient.commonDimensions).toHaveBeenCalled();
|
|
160
|
+
|
|
161
|
+
const selectDimensions = screen.getAllByTestId('select-dimensions')[0];
|
|
162
|
+
expect(selectDimensions).toBeDefined();
|
|
163
|
+
expect(selectDimensions).not.toBeNull();
|
|
164
|
+
expect(screen.getAllByText('8 Shared Dimensions')[0]).toBeInTheDocument();
|
|
165
|
+
fireEvent.keyDown(selectDimensions.firstChild, { key: 'ArrowDown' });
|
|
166
|
+
|
|
167
|
+
for (const dim of mockCommonDimensions) {
|
|
168
|
+
expect(screen.getAllByText(dim.name)[0]).toBeInTheDocument();
|
|
169
|
+
fireEvent.click(screen.getAllByText(dim.name)[0]);
|
|
170
|
+
}
|
|
171
|
+
expect(mockDjClient.sqls).toHaveBeenCalled();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -204,48 +204,54 @@ export function SQLBuilderPage() {
|
|
|
204
204
|
<div className="card">
|
|
205
205
|
<div className="card-header">
|
|
206
206
|
<h4>Metrics</h4>
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
207
|
+
<span data-testid="select-metrics">
|
|
208
|
+
<Select
|
|
209
|
+
name="metrics"
|
|
210
|
+
options={metrics}
|
|
211
|
+
isDisabled={
|
|
212
|
+
!!(selectedMetrics.length && selectedDimensions.length)
|
|
213
|
+
}
|
|
214
|
+
noOptionsMessage={() => 'No metrics found.'}
|
|
215
|
+
placeholder={`${metrics.length} Available Metrics`}
|
|
216
|
+
isMulti
|
|
217
|
+
isClearable
|
|
218
|
+
closeMenuOnSelect={false}
|
|
219
|
+
onChange={e => {
|
|
220
|
+
setSelectedDimensions([]);
|
|
221
|
+
resetView();
|
|
222
|
+
setStagedMetrics(e.map(m => m.value));
|
|
223
|
+
setSelectedMetrics(stagedMetrics);
|
|
224
|
+
}}
|
|
225
|
+
onMenuClose={() => {
|
|
226
|
+
resetView();
|
|
227
|
+
setSelectedDimensions([]);
|
|
228
|
+
setSelectedMetrics(stagedMetrics);
|
|
229
|
+
}}
|
|
230
|
+
/>
|
|
231
|
+
</span>
|
|
229
232
|
<h4>Group By</h4>
|
|
230
|
-
<
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
233
|
+
<span data-testid="select-dimensions">
|
|
234
|
+
<Select
|
|
235
|
+
name="dimensions"
|
|
236
|
+
formatOptionLabel={formatOptionLabel}
|
|
237
|
+
options={commonDimensionsList}
|
|
238
|
+
noOptionsMessage={() =>
|
|
239
|
+
'No shared dimensions found. Try selecting different metrics.'
|
|
240
|
+
}
|
|
241
|
+
placeholder={`${commonDimensionsList.length} Shared Dimensions`}
|
|
242
|
+
isMulti
|
|
243
|
+
isClearable
|
|
244
|
+
closeMenuOnSelect={false}
|
|
245
|
+
onChange={e => {
|
|
246
|
+
resetView();
|
|
247
|
+
setStagedDimensions(e.map(d => d.value));
|
|
248
|
+
setSelectedDimensions(stagedDimensions);
|
|
249
|
+
}}
|
|
250
|
+
onMenuClose={() => {
|
|
251
|
+
setSelectedDimensions(stagedDimensions);
|
|
252
|
+
}}
|
|
253
|
+
/>
|
|
254
|
+
</span>
|
|
249
255
|
<h4>Filter By</h4>
|
|
250
256
|
<QueryBuilder
|
|
251
257
|
fields={fields}
|
|
@@ -293,11 +299,23 @@ export function SQLBuilderPage() {
|
|
|
293
299
|
{query ? (
|
|
294
300
|
<>
|
|
295
301
|
{loadingData ? (
|
|
296
|
-
<span
|
|
302
|
+
<span
|
|
303
|
+
className="button-3 executing-button"
|
|
304
|
+
onClick={getData}
|
|
305
|
+
role="button"
|
|
306
|
+
aria-label="RunQuery"
|
|
307
|
+
aria-hidden="false"
|
|
308
|
+
>
|
|
297
309
|
{'Running Query'}
|
|
298
310
|
</span>
|
|
299
311
|
) : (
|
|
300
|
-
<span
|
|
312
|
+
<span
|
|
313
|
+
className="button-3 execute-button"
|
|
314
|
+
onClick={getData}
|
|
315
|
+
role="button"
|
|
316
|
+
aria-label="RunQuery"
|
|
317
|
+
aria-hidden="false"
|
|
318
|
+
>
|
|
301
319
|
{'Run Query'}
|
|
302
320
|
</span>
|
|
303
321
|
)}
|