Skip to content

Commit

Permalink
Merge pull request elastic#6 from CoenWarmer/storybook
Browse files Browse the repository at this point in the history
  • Loading branch information
dgieselaar authored Jul 19, 2023
2 parents 798b1c3 + 378cc90 commit 6d7bbcf
Show file tree
Hide file tree
Showing 10 changed files with 343 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/dev/storybook/aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const storybookAliases = {
lists: 'x-pack/plugins/lists/.storybook',
management: 'packages/kbn-management/storybook/config',
observability: 'x-pack/plugins/observability/.storybook',
observability_ai_assistant: 'x-pack/plugins/observability_ai_assistant/.storybook',
presentation: 'src/plugins/presentation_util/storybook',
random_sampling: 'x-pack/packages/kbn-random-sampling/.storybook',
text_based_editor: 'packages/kbn-text-based-editor/.storybook',
Expand Down
11 changes: 11 additions & 0 deletions x-pack/plugins/observability_ai_assistant/.storybook/jest_setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { setGlobalConfig } from '@storybook/testing-react';
import * as globalStorybookConfig from './preview';

setGlobalConfig(globalStorybookConfig);
8 changes: 8 additions & 0 deletions x-pack/plugins/observability_ai_assistant/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

module.exports = require('@kbn/storybook').defaultConfig;
10 changes: 10 additions & 0 deletions x-pack/plugins/observability_ai_assistant/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { EuiThemeProviderDecorator } from '@kbn/kibana-react-plugin/common';

export const decorators = [EuiThemeProviderDecorator];
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { ComponentStory } from '@storybook/react';
import { EuiButtonSize } from '@elastic/eui';

import { AskAssistantButton as Component, AskAssistantButtonProps } from './ask_assistant_button';

export default {
component: Component,
title: 'app/Atoms/AskAiAssistantButton',
argTypes: {
size: {
options: ['xs', 's', 'm'] as EuiButtonSize[],
control: { type: 'radio' },
},
fill: {
control: {
type: 'boolean',
},
},
flush: {
control: {
type: 'boolean',
if: { arg: 'variant', eq: 'empty' },
},
},
variant: {
options: ['basic', 'empty', 'iconOnly'],
control: { type: 'radio' },
},
},
};

const Template: ComponentStory<typeof Component> = (props: AskAssistantButtonProps) => (
<Component {...props} />
);

const defaultProps = {
fill: true,
iconOnly: false,
size: 'm' as EuiButtonSize,
variant: 'basic' as const,
};

export const AskAiAssistantButton = Template.bind({});
AskAiAssistantButton.args = defaultProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import {
EuiButton,
EuiButtonEmpty,
EuiButtonSize,
EuiButtonEmptySizes,
useEuiTheme,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';

export type AskAssistantButtonProps = (
| {
variant: 'basic' | 'iconOnly';
size: EuiButtonSize;
fill?: boolean;
flush?: false;
}
| {
variant: 'empty';
size: EuiButtonEmptySizes;
fill?: false;
flush?: 'both';
}
) & {
onClick: () => void;
};

// In order to leverage all the styling / display code that Eui buttons provide,
// we need to have the Sparkle icon part of EuiIcons. While we wait for that to land
// we have to redo some of that logic below. Todo: cleanup once Sparkle icon lands.

export function AskAssistantButton({
fill,
flush,
size,
variant,
onClick,
}: AskAssistantButtonProps) {
const contents = (
<>
<SparkleIcon color={variant !== 'empty' && fill ? 'white' : 'blue'} size={size} />
{variant === 'empty' ? ' ' : null}

{variant === 'iconOnly'
? null
: i18n.translate('xpack.obsAiAssistant.askAssistantButton.buttonLabel', {
defaultMessage: 'Ask Assistant',
})}
</>
);

switch (variant) {
case 'basic':
return (
<EuiButton fill={fill} size={size} onClick={onClick}>
{contents}
</EuiButton>
);

case 'empty':
return (
<EuiButtonEmpty size={size} flush={flush} onClick={onClick}>
{contents}
</EuiButtonEmpty>
);

case 'iconOnly':
return (
<EuiToolTip
position="top"
title={i18n.translate('xpack.obsAiAssistant.askAssistantButton.popoverTitle', {
defaultMessage: 'Elastic Assistant',
})}
content={i18n.translate('xpack.obsAiAssistant.askAssistantButton.popoverContent', {
defaultMessage: 'Get insights into your data with the Elastic Assistant',
})}
>
<EuiButton fill={fill} size={size} style={{ minWidth: 'auto' }} onClick={onClick}>
{contents}
</EuiButton>
</EuiToolTip>
);
}
}

// This icon is temporary and should be removed once it lands in Eui.
function SparkleIcon({ size, color }: { size: 'xs' | 's' | 'm'; color: 'white' | 'blue' }) {
const { euiTheme } = useEuiTheme();

return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size === 'xs' ? 9.75 : 13}
height={size === 'xs' ? 9.75 : 13}
fill="none"
>
<path
fill={color === 'white' ? '#fff' : euiTheme.colors.primaryText}
d="m4.016 2.382.035-.092a.492.492 0 0 1 .898 0l.035.092a5.9 5.9 0 0 0 3.38 3.536c.061.025.125.05.253.097l.093.036a.492.492 0 0 1 0 .897l-.093.036a5.9 5.9 0 0 0-3.633 3.633l-.035.092a.492.492 0 0 1-.898 0l-.035-.092A5.9 5.9 0 0 0 .636 7.08c-.06-.025-.125-.05-.253-.097a2.17 2.17 0 0 1-.093-.036.492.492 0 0 1 0-.897l.093-.036.253-.096a5.9 5.9 0 0 0 3.38-3.537ZM10.785.17A.973.973 0 0 1 10.8.129a.219.219 0 0 1 .398 0 2.622 2.622 0 0 0 1.518 1.613l.113.042.04.016a.219.219 0 0 1 0 .399l-.04.016-.113.043a2.622 2.622 0 0 0-1.502 1.571.972.972 0 0 1-.016.041.219.219 0 0 1-.398 0 2.622 2.622 0 0 0-1.63-1.656.94.94 0 0 1-.042-.015.219.219 0 0 1 0-.399.968.968 0 0 1 .041-.016l.113-.043A2.622 2.622 0 0 0 10.785.17ZM10.23 8.212l.02-.051a.273.273 0 0 1 .5 0l.02.051.053.14c.333.833.992 1.492 1.824 1.825l.14.053.052.02a.273.273 0 0 1 0 .499l-.052.02-.14.053a3.278 3.278 0 0 0-1.824 1.824l-.054.14c-.01.03-.016.044-.02.052a.273.273 0 0 1-.498 0 1.282 1.282 0 0 1-.02-.051c-.027-.071-.04-.107-.054-.14a3.278 3.278 0 0 0-1.824-1.825l-.14-.053-.052-.02a.273.273 0 0 1 0-.499 1.24 1.24 0 0 1 .052-.02l.14-.053a3.278 3.278 0 0 0 1.824-1.824l.054-.14Z"
/>
</svg>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { ComponentStory } from '@storybook/react';

import { AssistantAvatar as Component, AssistantAvatarProps } from './assistant_avatar';

export default {
component: Component,
title: 'app/Atoms/AssistantAvatar',
argTypes: {
size: {
options: ['xs', 's', 'm', 'l', 'xl'],
control: { type: 'radio' },
},
},
};

const Template: ComponentStory<typeof Component> = (props: AssistantAvatarProps) => (
<Component {...props} />
);

const defaultProps = {
size: 'm' as const,
};

export const AssistantAvatar = Template.bind({});
AssistantAvatar.args = defaultProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';

export interface AssistantAvatarProps {
size: keyof typeof sizeMap;
}

export const sizeMap = {
xl: 64,
l: 48,
m: 32,
s: 24,
xs: 16,
};

export function AssistantAvatar({ size }: AssistantAvatarProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={sizeMap[size]}
height={sizeMap[size]}
viewBox="0 0 64 64"
fill="none"
>
<path fill="#F04E98" d="M36 28h24v36H36V28Z" />
<path fill="#00BFB3" d="M4 46c0-9.941 8.059-18 18-18h6v36h-6c-9.941 0-18-8.059-18-18Z" />
<path
fill="#343741"
d="M60 12c0 6.627-5.373 12-12 12s-12-5.373-12-12S41.373 0 48 0s12 5.373 12 12Z"
/>
<path fill="#FA744E" d="M6 23C6 10.85 15.85 1 28 1v22H6Z" />
</svg>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { ComponentStory } from '@storybook/react';

import { InsightPanel as Component, InsightPanelProps } from './insight_panel';

export default {
component: Component,
title: 'app/Molecules/InsightPanel',
argTypes: {},
};

const Template: ComponentStory<typeof Component> = (props: InsightPanelProps) => (
<Component {...props} />
);

const defaultProps = {
title: 'Elastic Assistant',
};

export const InsightPanel = Template.bind({});
InsightPanel.args = defaultProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui';
import { AssistantAvatar } from './assistant_avatar';

export interface InsightPanelProps {
title: string;
}

export function InsightPanel({ title }: InsightPanelProps) {
return (
<EuiPanel hasBorder hasShadow={false}>
<EuiFlexGroup wrap responsive={false}>
{/* expand / contract */}
<EuiFlexItem grow={false}>
<EuiButtonIcon iconType="arrowRight" />
</EuiFlexItem>

{/* content */}
<EuiFlexItem>
<EuiFlexGroup wrap responsive={false}>
<EuiFlexItem grow={false}>
<AssistantAvatar size="xs" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<h5>{title}</h5>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>

{/* actions */}
<EuiFlexItem grow={false}>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonIcon iconType="boxesHorizontal" />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
}

0 comments on commit 6d7bbcf

Please sign in to comment.