mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CodeEditor: Improved styles when the code editor is loading (#88102)
* fix: monaco loading state * chore: betterer * test: leverage e2e selectors * refactor: prefer HOC to wrap with testid * refactor: add clarity * docs: add clarity * refactor: loading messaging * test: rename vars to improve clarity * test: rename vars to improve clarity
This commit is contained in:
parent
5de7d4d06d
commit
7334f71e09
@ -5054,8 +5054,6 @@ exports[`no gf-form usage`] = {
|
|||||||
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"]
|
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"]
|
||||||
],
|
],
|
||||||
"packages/grafana-prometheus/src/components/PromQueryField.tsx:5381": [
|
"packages/grafana-prometheus/src/components/PromQueryField.tsx:5381": [
|
||||||
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"],
|
|
||||||
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"],
|
|
||||||
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"],
|
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"],
|
||||||
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"],
|
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"],
|
||||||
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"],
|
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "5381"],
|
||||||
|
@ -526,6 +526,9 @@ export const Components = {
|
|||||||
CodeEditor: {
|
CodeEditor: {
|
||||||
container: 'data-testid Code editor container',
|
container: 'data-testid Code editor container',
|
||||||
},
|
},
|
||||||
|
ReactMonacoEditor: {
|
||||||
|
container: 'data-testid ReactMonacoEditor container',
|
||||||
|
},
|
||||||
DashboardImportPage: {
|
DashboardImportPage: {
|
||||||
textarea: 'data-testid-import-dashboard-textarea',
|
textarea: 'data-testid-import-dashboard-textarea',
|
||||||
submit: 'data-testid-load-dashboard',
|
submit: 'data-testid-load-dashboard',
|
||||||
|
@ -238,7 +238,7 @@ class PromQueryFieldClass extends React.PureComponent<PromQueryFieldProps, PromQ
|
|||||||
<Icon name={labelBrowserVisible ? 'angle-down' : 'angle-right'} />
|
<Icon name={labelBrowserVisible ? 'angle-down' : 'angle-right'} />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
|
<div className="flex-grow-1 min-width-15">
|
||||||
<MonacoQueryFieldWrapper
|
<MonacoQueryFieldWrapper
|
||||||
languageProvider={languageProvider}
|
languageProvider={languageProvider}
|
||||||
history={history}
|
history={history}
|
||||||
|
@ -83,6 +83,11 @@ const getStyles = (theme: GrafanaTheme2, placeholder: string) => {
|
|||||||
container: css({
|
container: css({
|
||||||
borderRadius: theme.shape.radius.default,
|
borderRadius: theme.shape.radius.default,
|
||||||
border: `1px solid ${theme.components.input.borderColor}`,
|
border: `1px solid ${theme.components.input.borderColor}`,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'start',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '100%',
|
||||||
}),
|
}),
|
||||||
placeholder: css({
|
placeholder: css({
|
||||||
'::after': {
|
'::after': {
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
|
import { useStyles2 } from '../../themes';
|
||||||
import { useAsyncDependency } from '../../utils/useAsyncDependency';
|
import { useAsyncDependency } from '../../utils/useAsyncDependency';
|
||||||
import { ErrorWithStack } from '../ErrorBoundary/ErrorWithStack';
|
import { ErrorWithStack } from '../ErrorBoundary/ErrorWithStack';
|
||||||
import { LoadingPlaceholder } from '../LoadingPlaceholder/LoadingPlaceholder';
|
import { LoadingPlaceholder } from '../LoadingPlaceholder/LoadingPlaceholder';
|
||||||
@ -11,13 +16,14 @@ import type { ReactMonacoEditorProps } from './types';
|
|||||||
* @internal
|
* @internal
|
||||||
* Experimental export
|
* Experimental export
|
||||||
**/
|
**/
|
||||||
export const ReactMonacoEditorLazy = (props: ReactMonacoEditorProps) => {
|
const MonacoEditorLazy = (props: ReactMonacoEditorProps) => {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
const { loading, error, dependency } = useAsyncDependency(
|
const { loading, error, dependency } = useAsyncDependency(
|
||||||
import(/* webpackChunkName: "react-monaco-editor" */ './ReactMonacoEditor')
|
import(/* webpackChunkName: "react-monaco-editor" */ './ReactMonacoEditor')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <LoadingPlaceholder text={''} />;
|
return <LoadingPlaceholder text={'Loading editor'} className={styles.container} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -31,5 +37,29 @@ export const ReactMonacoEditorLazy = (props: ReactMonacoEditorProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ReactMonacoEditor = dependency.ReactMonacoEditor;
|
const ReactMonacoEditor = dependency.ReactMonacoEditor;
|
||||||
return <ReactMonacoEditor {...props} />;
|
return <ReactMonacoEditor {...props} loading={props.loading ?? null} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
|
return {
|
||||||
|
container: css({
|
||||||
|
marginBottom: 'unset',
|
||||||
|
marginLeft: theme.spacing(1),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const withContainer = <P extends object>(Component: React.ComponentType<P>): React.ComponentType<P> => {
|
||||||
|
const WithContainer = (props: P) => (
|
||||||
|
// allow tests to easily determine if the code editor has rendered in any of its three states (loading, error, or ready)
|
||||||
|
<div data-testid={selectors.components.ReactMonacoEditor.container}>
|
||||||
|
<Component {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
WithContainer.displayName = Component.displayName;
|
||||||
|
|
||||||
|
return WithContainer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ReactMonacoEditorLazy = withContainer(MonacoEditorLazy);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { render, screen, waitFor } from '@testing-library/react';
|
import { render, screen, waitFor } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
import { createLokiDatasource } from '../../__mocks__/datasource';
|
import { createLokiDatasource } from '../../__mocks__/datasource';
|
||||||
|
|
||||||
import { MonacoQueryFieldWrapper, Props } from './MonacoQueryFieldWrapper';
|
import { MonacoQueryFieldWrapper, Props } from './MonacoQueryFieldWrapper';
|
||||||
@ -25,7 +27,8 @@ describe('MonacoFieldWrapper', () => {
|
|||||||
renderComponent();
|
renderComponent();
|
||||||
|
|
||||||
await waitFor(async () => {
|
await waitFor(async () => {
|
||||||
expect(await screen.findByText('Loading...')).toBeInTheDocument();
|
const monacoEditor = await screen.findByTestId(selectors.components.ReactMonacoEditor.container);
|
||||||
|
expect(monacoEditor).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
import { createLokiDatasource } from '../../__mocks__/datasource';
|
import { createLokiDatasource } from '../../__mocks__/datasource';
|
||||||
|
|
||||||
import MonacoQueryField from './MonacoQueryField';
|
import MonacoQueryField from './MonacoQueryField';
|
||||||
@ -31,6 +33,7 @@ describe('MonacoQueryField', () => {
|
|||||||
test('Renders with no errors', async () => {
|
test('Renders with no errors', async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
|
|
||||||
expect(await screen.findByText('Loading...')).toBeInTheDocument();
|
const monacoEditor = await screen.findByTestId(selectors.components.ReactMonacoEditor.container);
|
||||||
|
expect(monacoEditor).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
import { createLokiDatasource } from '../../__mocks__/datasource';
|
import { createLokiDatasource } from '../../__mocks__/datasource';
|
||||||
import { LokiQuery } from '../../types';
|
import { LokiQuery } from '../../types';
|
||||||
|
|
||||||
@ -32,7 +34,8 @@ describe('LokiQueryCodeEditor', () => {
|
|||||||
props.showExplain = true;
|
props.showExplain = true;
|
||||||
props.datasource.metadataRequest = jest.fn().mockResolvedValue([]);
|
props.datasource.metadataRequest = jest.fn().mockResolvedValue([]);
|
||||||
render(<LokiQueryCodeEditor {...props} query={defaultQuery} />);
|
render(<LokiQueryCodeEditor {...props} query={defaultQuery} />);
|
||||||
expect(await screen.findByText('Loading...')).toBeInTheDocument();
|
const monacoEditor = await screen.findByTestId(selectors.components.ReactMonacoEditor.container);
|
||||||
|
expect(monacoEditor).toBeInTheDocument();
|
||||||
expect(screen.getByText(EXPLAIN_LABEL_FILTER_CONTENT)).toBeInTheDocument();
|
expect(screen.getByText(EXPLAIN_LABEL_FILTER_CONTENT)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -40,7 +43,8 @@ describe('LokiQueryCodeEditor', () => {
|
|||||||
const props = createDefaultProps();
|
const props = createDefaultProps();
|
||||||
props.datasource.metadataRequest = jest.fn().mockResolvedValue([]);
|
props.datasource.metadataRequest = jest.fn().mockResolvedValue([]);
|
||||||
render(<LokiQueryCodeEditor {...props} query={defaultQuery} />);
|
render(<LokiQueryCodeEditor {...props} query={defaultQuery} />);
|
||||||
expect(await screen.findByText('Loading...')).toBeInTheDocument();
|
const monacoEditor = await screen.findByTestId(selectors.components.ReactMonacoEditor.container);
|
||||||
|
expect(monacoEditor).toBeInTheDocument();
|
||||||
expect(screen.queryByText(EXPLAIN_LABEL_FILTER_CONTENT)).not.toBeInTheDocument();
|
expect(screen.queryByText(EXPLAIN_LABEL_FILTER_CONTENT)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user