[APM] Storybook theme fixes (#69730)

* [APM] Storybook theme fixes

The changes adding theme support in #69362 broke some of the Storybook stories.

Add decorators to wrap some of the stories in the theme context.

This should be done in a global decorator, but our current storybook setup doesn't support this. It also would be nice to be able to switch between light/dark mode, but that's something we can add in the future.

* Remove unused import

* Adds missing decorator to cytoscape examples + adds a new real-world example

Co-authored-by: Oliver Gupte <olivergupte@gmail.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nathan L Smith 2020-06-24 06:54:01 -05:00 committed by GitHub
parent 78ebb6250a
commit 2e078dbab9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 2426 additions and 259 deletions

View file

@ -10,60 +10,63 @@ import cytoscape from 'cytoscape';
import React from 'react';
import { Cytoscape } from '../Cytoscape';
import { iconForNode } from '../icons';
import { EuiThemeProvider } from '../../../../../../observability/public';
storiesOf('app/ServiceMap/Cytoscape', module).add(
'example',
() => {
const elements: cytoscape.ElementDefinition[] = [
{
data: {
id: 'opbeans-python',
'service.name': 'opbeans-python',
'agent.name': 'python',
storiesOf('app/ServiceMap/Cytoscape', module)
.addDecorator((storyFn) => <EuiThemeProvider>{storyFn()}</EuiThemeProvider>)
.add(
'example',
() => {
const elements: cytoscape.ElementDefinition[] = [
{
data: {
id: 'opbeans-python',
'service.name': 'opbeans-python',
'agent.name': 'python',
},
},
},
{
data: {
id: 'opbeans-node',
'service.name': 'opbeans-node',
'agent.name': 'nodejs',
{
data: {
id: 'opbeans-node',
'service.name': 'opbeans-node',
'agent.name': 'nodejs',
},
},
},
{
data: {
id: 'opbeans-ruby',
'service.name': 'opbeans-ruby',
'agent.name': 'ruby',
{
data: {
id: 'opbeans-ruby',
'service.name': 'opbeans-ruby',
'agent.name': 'ruby',
},
},
},
{ data: { source: 'opbeans-python', target: 'opbeans-node' } },
{
data: {
bidirectional: true,
source: 'opbeans-python',
target: 'opbeans-ruby',
{ data: { source: 'opbeans-python', target: 'opbeans-node' } },
{
data: {
bidirectional: true,
source: 'opbeans-python',
target: 'opbeans-ruby',
},
},
},
];
const height = 300;
const width = 1340;
const serviceName = 'opbeans-python';
return (
<Cytoscape
elements={elements}
height={height}
width={width}
serviceName={serviceName}
/>
);
},
{
info: {
propTables: false,
source: false,
];
const height = 300;
const width = 1340;
const serviceName = 'opbeans-python';
return (
<Cytoscape
elements={elements}
height={height}
width={width}
serviceName={serviceName}
/>
);
},
}
);
{
info: {
propTables: false,
source: false,
},
}
);
storiesOf('app/ServiceMap/Cytoscape', module).add(
'node icons',

View file

@ -6,23 +6,25 @@
/* eslint-disable no-console */
import {
EuiButton,
EuiCodeEditor,
EuiFieldNumber,
EuiFilePicker,
EuiFlexGroup,
EuiFlexItem,
EuiButton,
EuiForm,
EuiFieldNumber,
EuiToolTip,
EuiCodeEditor,
EuiSpacer,
EuiFilePicker,
EuiToolTip,
} from '@elastic/eui';
import { storiesOf } from '@storybook/react';
import React, { useState, useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { EuiThemeProvider } from '../../../../../../observability/public';
import { Cytoscape } from '../Cytoscape';
import { generateServiceMapElements } from './generate_service_map_elements';
import exampleResponseOpbeansBeats from './example_response_opbeans_beats.json';
import exampleResponseHipsterStore from './example_response_hipster_store.json';
import exampleResponseOpbeansBeats from './example_response_opbeans_beats.json';
import exampleResponseTodo from './example_response_todo.json';
import exampleResponseOneDomainManyIPs from './example_response_one_domain_many_ips.json';
import { generateServiceMapElements } from './generate_service_map_elements';
const STORYBOOK_PATH = 'app/ServiceMap/Cytoscape/Example data';
@ -34,151 +36,155 @@ function setSessionJson(json: string) {
window.sessionStorage.setItem(SESSION_STORAGE_KEY, json);
}
storiesOf(STORYBOOK_PATH, module).add(
'Generate map',
() => {
const [size, setSize] = useState<number>(10);
const [json, setJson] = useState<string>('');
const [elements, setElements] = useState<any[]>(
generateServiceMapElements(size)
);
storiesOf(STORYBOOK_PATH, module)
.addDecorator((storyFn) => <EuiThemeProvider>{storyFn()}</EuiThemeProvider>)
.add(
'Generate map',
() => {
const [size, setSize] = useState<number>(10);
const [json, setJson] = useState<string>('');
const [elements, setElements] = useState<any[]>(
generateServiceMapElements(size)
);
return (
<div>
<EuiFlexGroup>
<EuiFlexItem>
<EuiButton
onClick={() => {
setElements(generateServiceMapElements(size));
setJson('');
}}
>
Generate service map
</EuiButton>
</EuiFlexItem>
<EuiFlexItem>
<EuiToolTip position="right" content="Number of services">
<EuiFieldNumber
placeholder="Size"
value={size}
onChange={(e) => setSize(e.target.valueAsNumber)}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton
onClick={() => {
setJson(JSON.stringify({ elements }, null, 2));
}}
>
Get JSON
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
<Cytoscape elements={elements} height={600} width={1340} />
{json && (
<EuiCodeEditor
mode="json"
theme="github"
width="100%"
value={json}
setOptions={{ fontSize: '12px' }}
isReadOnly
/>
)}
</div>
);
},
{
info: { propTables: false, source: false },
}
);
storiesOf(STORYBOOK_PATH, module).add(
'Map from JSON',
() => {
const [json, setJson] = useState<string>(
getSessionJson() || JSON.stringify(exampleResponseTodo, null, 2)
);
const [error, setError] = useState<string | undefined>();
const [elements, setElements] = useState<any[]>([]);
useEffect(() => {
try {
setElements(JSON.parse(json).elements);
} catch (e) {
setError(e.message);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div>
<Cytoscape elements={elements} height={600} width={1340} />
<EuiForm isInvalid={error !== undefined} error={error}>
return (
<div>
<EuiFlexGroup>
<EuiFlexItem>
<EuiCodeEditor
mode="json"
theme="github"
width="100%"
value={json}
setOptions={{ fontSize: '12px' }}
onChange={(value) => {
setJson(value);
<EuiButton
onClick={() => {
setElements(generateServiceMapElements(size));
setJson('');
}}
/>
>
Generate service map
</EuiButton>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup direction="column">
<EuiFilePicker
display={'large'}
fullWidth={true}
style={{ height: '100%' }}
initialPromptText="Upload a JSON file"
onChange={(event) => {
const item = event?.item(0);
if (item) {
const f = new FileReader();
f.onload = (onloadEvent) => {
const result = onloadEvent?.target?.result;
if (typeof result === 'string') {
setJson(result);
}
};
f.readAsText(item);
}
}}
<EuiToolTip position="right" content="Number of services">
<EuiFieldNumber
placeholder="Size"
value={size}
onChange={(e) => setSize(e.target.valueAsNumber)}
/>
<EuiSpacer />
<EuiButton
onClick={() => {
try {
setElements(JSON.parse(json).elements);
setSessionJson(json);
setError(undefined);
} catch (e) {
setError(e.message);
}
}}
>
Render JSON
</EuiButton>
</EuiFlexGroup>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem>
<EuiButton
onClick={() => {
setJson(JSON.stringify({ elements }, null, 2));
}}
>
Get JSON
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiForm>
</div>
);
},
{
info: {
propTables: false,
source: false,
text: `
<Cytoscape elements={elements} height={600} width={1340} />
{json && (
<EuiCodeEditor
mode="json"
theme="github"
width="100%"
value={json}
setOptions={{ fontSize: '12px' }}
isReadOnly
/>
)}
</div>
);
},
{
info: { propTables: false, source: false },
}
);
storiesOf(STORYBOOK_PATH, module)
.addDecorator((storyFn) => <EuiThemeProvider>{storyFn()}</EuiThemeProvider>)
.add(
'Map from JSON',
() => {
const [json, setJson] = useState<string>(
getSessionJson() || JSON.stringify(exampleResponseTodo, null, 2)
);
const [error, setError] = useState<string | undefined>();
const [elements, setElements] = useState<any[]>([]);
useEffect(() => {
try {
setElements(JSON.parse(json).elements);
} catch (e) {
setError(e.message);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div>
<Cytoscape elements={elements} height={600} width={1340} />
<EuiForm isInvalid={error !== undefined} error={error}>
<EuiFlexGroup>
<EuiFlexItem>
<EuiCodeEditor
mode="json"
theme="github"
width="100%"
value={json}
setOptions={{ fontSize: '12px' }}
onChange={(value) => {
setJson(value);
}}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup direction="column">
<EuiFilePicker
display={'large'}
fullWidth={true}
style={{ height: '100%' }}
initialPromptText="Upload a JSON file"
onChange={(event) => {
const item = event?.item(0);
if (item) {
const f = new FileReader();
f.onload = (onloadEvent) => {
const result = onloadEvent?.target?.result;
if (typeof result === 'string') {
setJson(result);
}
};
f.readAsText(item);
}
}}
/>
<EuiSpacer />
<EuiButton
onClick={() => {
try {
setElements(JSON.parse(json).elements);
setSessionJson(json);
setError(undefined);
} catch (e) {
setError(e.message);
}
}}
>
Render JSON
</EuiButton>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiForm>
</div>
);
},
{
info: {
propTables: false,
source: false,
text: `
Enter JSON map data into the text box or upload a file and click "Render JSON" to see the results. You can enable a download button on the service map by putting
\`\`\`
@ -186,60 +192,86 @@ storiesOf(STORYBOOK_PATH, module).add(
\`\`\`
into the JavaScript console and reloading the page.`,
},
}
);
storiesOf(STORYBOOK_PATH, module)
.addDecorator((storyFn) => <EuiThemeProvider>{storyFn()}</EuiThemeProvider>)
.add(
'Todo app',
() => {
return (
<div>
<Cytoscape
elements={exampleResponseTodo.elements}
height={600}
width={1340}
/>
</div>
);
},
}
);
{
info: { propTables: false, source: false },
}
);
storiesOf(STORYBOOK_PATH, module).add(
'Todo app',
() => {
return (
<div>
<Cytoscape
elements={exampleResponseTodo.elements}
height={600}
width={1340}
/>
</div>
);
},
{
info: { propTables: false, source: false },
}
);
storiesOf(STORYBOOK_PATH, module)
.addDecorator((storyFn) => <EuiThemeProvider>{storyFn()}</EuiThemeProvider>)
.add(
'Opbeans + beats',
() => {
return (
<div>
<Cytoscape
elements={exampleResponseOpbeansBeats.elements}
height={600}
width={1340}
/>
</div>
);
},
{
info: { propTables: false, source: false },
}
);
storiesOf(STORYBOOK_PATH, module).add(
'Opbeans + beats',
() => {
return (
<div>
<Cytoscape
elements={exampleResponseOpbeansBeats.elements}
height={600}
width={1340}
/>
</div>
);
},
{
info: { propTables: false, source: false },
}
);
storiesOf(STORYBOOK_PATH, module)
.addDecorator((storyFn) => <EuiThemeProvider>{storyFn()}</EuiThemeProvider>)
.add(
'Hipster store',
() => {
return (
<div>
<Cytoscape
elements={exampleResponseHipsterStore.elements}
height={600}
width={1340}
/>
</div>
);
},
{
info: { propTables: false, source: false },
}
);
storiesOf(STORYBOOK_PATH, module).add(
'Hipster store',
() => {
return (
<div>
<Cytoscape
elements={exampleResponseHipsterStore.elements}
height={600}
width={1340}
/>
</div>
);
},
{
info: { propTables: false, source: false },
}
);
storiesOf(STORYBOOK_PATH, module)
.addDecorator((storyFn) => <EuiThemeProvider>{storyFn()}</EuiThemeProvider>)
.add(
'Node resolves one domain name to many IPs',
() => {
return (
<div>
<Cytoscape
elements={exampleResponseOneDomainManyIPs.elements}
height={600}
width={1340}
/>
</div>
);
},
{
info: { propTables: false, source: false },
}
);

View file

@ -21,13 +21,13 @@ import {
ApmPluginContext,
ApmPluginContextValue,
} from '../../../../../context/ApmPluginContext';
import { EuiThemeProvider } from '../../../../../../../observability/public';
storiesOf(
'app/Settings/AgentConfigurations/AgentConfigurationCreateEdit',
module
).add(
'with config',
() => {
)
.addDecorator((storyFn) => {
const httpMock = {};
// mock
@ -40,10 +40,21 @@ storiesOf(
},
},
};
return (
<ApmPluginContext.Provider
value={(contextMock as unknown) as ApmPluginContextValue}
>
<EuiThemeProvider>
<ApmPluginContext.Provider
value={(contextMock as unknown) as ApmPluginContextValue}
>
{storyFn()}
</ApmPluginContext.Provider>
</EuiThemeProvider>
);
})
.add(
'with config',
() => {
return (
<AgentConfigurationCreateEdit
pageStep="choose-settings-step"
existingConfigResult={{
@ -54,12 +65,11 @@ storiesOf(
} as AgentConfiguration,
}}
/>
</ApmPluginContext.Provider>
);
},
{
info: {
source: false,
);
},
}
);
{
info: {
source: false,
},
}
);