From 37e2b60c7465ad7eb5e3661817e1f5370d2dfeb4 Mon Sep 17 00:00:00 2001
From: Weronika Olejniczak
Date: Fri, 11 Oct 2024 13:59:01 +0200
Subject: [PATCH] chore(code-connect): integrate the most used components
---
.../eui/src/components/badge/badge.figma.tsx | 66 +++++
.../src/components/badge/badge.stories.tsx | 76 ++++--
.../src/components/button/button.figma.tsx | 148 +++++++++++
.../src/components/button/button.stories.tsx | 81 +++++-
.../button_empty/button_empty.stories.tsx | 78 +++++-
.../button_group/button_group.stories.tsx | 135 +++++++---
.../components/call_out/call_out.figma.tsx | 62 +++++
.../components/call_out/call_out.stories.tsx | 70 ++++-
.../components/combo_box/combo_box.figma.tsx | 71 ++++++
.../combo_box/combo_box.stories.tsx | 239 +++++++++++-------
.../form/field_text/field_text.figma.tsx | 64 +++++
.../form/field_text/field_text.stories.tsx | 129 +++++++---
.../components/form/select/select.figma.tsx | 69 +++++
.../components/form/select/select.stories.tsx | 103 +++++++-
.../eui/src/components/icon/icon.figma.tsx | 38 +++
.../eui/src/components/icon/icon.stories.tsx | 29 ++-
.../eui/src/components/panel/panel.figma.tsx | 55 ++++
.../src/components/panel/panel.stories.tsx | 52 +++-
.../eui/src/components/text/text.figma.tsx | 24 ++
.../eui/src/components/text/text.stories.tsx | 35 ++-
.../components/tool_tip/tool_tip.figma.tsx | 37 +++
.../components/tool_tip/tool_tip.stories.tsx | 74 ++++--
22 files changed, 1486 insertions(+), 249 deletions(-)
create mode 100644 packages/eui/src/components/badge/badge.figma.tsx
create mode 100644 packages/eui/src/components/button/button.figma.tsx
create mode 100644 packages/eui/src/components/call_out/call_out.figma.tsx
create mode 100644 packages/eui/src/components/combo_box/combo_box.figma.tsx
create mode 100644 packages/eui/src/components/form/field_text/field_text.figma.tsx
create mode 100644 packages/eui/src/components/form/select/select.figma.tsx
create mode 100644 packages/eui/src/components/icon/icon.figma.tsx
create mode 100644 packages/eui/src/components/panel/panel.figma.tsx
create mode 100644 packages/eui/src/components/text/text.figma.tsx
create mode 100644 packages/eui/src/components/tool_tip/tool_tip.figma.tsx
diff --git a/packages/eui/src/components/badge/badge.figma.tsx b/packages/eui/src/components/badge/badge.figma.tsx
new file mode 100644
index 000000000000..995532768d60
--- /dev/null
+++ b/packages/eui/src/components/badge/badge.figma.tsx
@@ -0,0 +1,66 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+// eslint-disable-next-line
+// @ts-nocheck
+
+/**
+ * `iconType` expects an enum member, a string or ComponentType
+ * `iconSide` expects "left" or "right" or undefined
+ *
+ * I cannot use `as const` to narrow down the type because the value is serialized, so the code snippet would be
+ * `iconSide={'left' as const}`
+ *
+ * figma.instance returns a a JSX.Element, not ComponentType or string. The code is not executed so it doesn't matter.
+ *
+ * `props` must be an object literal so we cannot use assertion there either.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiBadge } from './badge';
+
+figma.connect(EuiBadge, 'node-id=31918-390303', {
+ props: {
+ children: figma.boolean('Icon only', {
+ true: undefined,
+ false: figma.string('Text'),
+ }),
+ color: figma.enum('Color', {
+ Default: undefined,
+ Hollow: 'hollow',
+ Primary: 'primary',
+ Accent: 'accent',
+ Success: 'success',
+ Danger: 'danger',
+ Warning: 'warning',
+ }),
+ isDisabled: figma.boolean('Disabled'),
+ iconType: figma.boolean('Icon only', {
+ true: figma.instance('⮑ Icon'),
+ false: figma.boolean('Icon left', {
+ true: figma.instance('⮑ Icon left'),
+ false: figma.boolean('Icon right', {
+ true: figma.instance('⮑ Icon right'),
+ false: undefined,
+ }),
+ }),
+ }),
+ iconSide: figma.boolean('Icon left', {
+ true: 'left',
+ false: figma.boolean('Icon right', {
+ true: 'right',
+ false: undefined,
+ }),
+ }),
+ },
+ example: ({ children, ...props }) => (
+ {children}
+ ),
+});
diff --git a/packages/eui/src/components/badge/badge.stories.tsx b/packages/eui/src/components/badge/badge.stories.tsx
index 608c1f5f0fd9..c246757d0564 100644
--- a/packages/eui/src/components/badge/badge.stories.tsx
+++ b/packages/eui/src/components/badge/badge.stories.tsx
@@ -6,25 +6,12 @@
* Side Public License, v 1.
*/
+import React from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
import { EuiBadge, EuiBadgeProps, COLORS } from './badge';
-const meta: Meta = {
- title: 'Display/EuiBadge/EuiBadge',
- component: EuiBadge,
- argTypes: {
- iconType: { control: 'text' },
- },
- args: {
- // Component defaults
- iconSide: 'left',
- isDisabled: false,
- color: 'default',
- },
-};
-
-export default meta;
type Story = StoryObj;
export const Playground: Story = {
@@ -37,6 +24,9 @@ export const Playground: Story = {
options: COLORS,
},
},
+ render: ({ children, ...args }: EuiBadgeProps) => (
+ {children}
+ ),
};
export const CustomColors: Story = {
@@ -50,3 +40,59 @@ export const CustomColors: Story = {
color: '#0000FF',
},
};
+
+const meta: Meta = {
+ title: 'Display/EuiBadge/EuiBadge',
+ component: EuiBadge,
+ argTypes: {
+ iconType: { control: 'text' },
+ },
+ args: {
+ // Component defaults
+ iconSide: 'left',
+ isDisabled: false,
+ color: 'default',
+ },
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=31918-390303&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ children: figma.boolean('Icon only', {
+ true: undefined,
+ false: figma.string('Text'),
+ }),
+ color: figma.enum('Color', {
+ Default: undefined,
+ Hollow: 'hollow',
+ Primary: 'primary',
+ Accent: 'accent',
+ Success: 'success',
+ Danger: 'danger',
+ Warning: 'warning',
+ }),
+ isDisabled: figma.boolean('Disabled'),
+ iconType: figma.boolean('Icon only', {
+ true: figma.instance('⮑ Icon'),
+ false: figma.boolean('Icon left', {
+ true: figma.instance('⮑ Icon left'),
+ false: figma.boolean('Icon right', {
+ true: figma.instance('⮑ Icon right'),
+ false: undefined,
+ }),
+ }),
+ }),
+ iconSide: figma.boolean('Icon left', {
+ true: 'left',
+ false: figma.boolean('Icon right', {
+ true: 'right',
+ false: undefined,
+ }),
+ }),
+ },
+ },
+ },
+};
+
+export default meta;
diff --git a/packages/eui/src/components/button/button.figma.tsx b/packages/eui/src/components/button/button.figma.tsx
new file mode 100644
index 000000000000..5fa3ed790de9
--- /dev/null
+++ b/packages/eui/src/components/button/button.figma.tsx
@@ -0,0 +1,148 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+// eslint-disable-next-line
+// @ts-nocheck
+
+/**
+ * `iconType` expects an enum member, a string or ComponentType
+ * `iconSide` expects "left" or "right" or undefined
+ *
+ * I cannot use `as const` to narrow down the type below because the value is serialized,
+ * so the code snippet would be `iconSide={'left' as const}`,
+ * and figma.instance is a JSX.Element, not ComponentType or string but it's a conflict between
+ * Code Connect API and the icon implementation (it's not render props pattern). I don't think there's an alternative,
+ * at the same time it doesn't matter much because Figma files are not executed, they're parsed as string.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiButton } from './button';
+import { EuiButtonEmpty } from './button_empty';
+import { EuiButtonGroup } from './button_group';
+
+/* Example: reusing the same props across multiple component connections */
+const sharedProps = {
+ children: figma.boolean('Icon only', {
+ true: undefined,
+ false: figma.textContent('Text'),
+ }),
+ color: figma.enum('Color', {
+ 'Primary*': undefined,
+ Neutral: 'text',
+ Success: 'success',
+ Warning: 'warning',
+ Danger: 'danger',
+ Accent: 'accent',
+ }),
+ isDisabled: figma.boolean('Disabled'),
+ isLoading: figma.boolean('Loading'),
+ iconType: figma.boolean('Icon only', {
+ true: figma.instance('⮑ Icon'),
+ false: figma.boolean('Icon left', {
+ true: figma.instance('⮑ Icon left'),
+ false: figma.boolean('Icon right', {
+ true: figma.instance('⮑ Icon right'),
+ false: undefined,
+ }),
+ }),
+ }),
+ iconSide: figma.boolean('Icon left', {
+ true: 'left',
+ false: figma.boolean('Icon right', {
+ true: 'right',
+ false: figma.boolean('Loading', {
+ true: figma.boolean('Left spinner', {
+ true: 'left',
+ false: figma.boolean('Right spinner', {
+ true: 'right',
+ false: undefined,
+ }),
+ }),
+ false: undefined,
+ }),
+ }),
+ }),
+ size: figma.enum('Size', {
+ 'Medium*': 'm',
+ Small: 's',
+ // Discrepancy between Figma and EUI
+ // 'Extra Small': 'extra-small',
+ }),
+};
+
+/* Basic example */
+figma.connect(EuiButton, 'node-id=31735-391399', {
+ props: {
+ ...sharedProps,
+ fill: figma.enum('Style', {
+ 'Default*': undefined,
+ Filled: true,
+ }),
+ },
+ example: ({ children, ...props }) => (
+ {}} {...props}>
+ {children}
+
+ ),
+});
+
+/* Example: Self-closing tags example (doesn't work, error: The Figma Variant "Icon only" does not have an option for true) */
+/* figma.connect(EuiButton, 'node-id=31735-391399', {
+ variant: { 'Icon only': true },
+ props: sharedProps,
+ example: (props) => (
+ {}} {...props} />
+ ),
+}); */
+
+/* Example: Figma variant being a separate component in code */
+figma.connect(EuiButtonEmpty, 'node-id=31735-391399', {
+ variant: { Style: 'Empty' },
+ props: sharedProps,
+ example: (props) => (
+ {}} {...props}>
+ {children}
+
+ ),
+});
+
+/* Example: static, non-controllable props to showcase the usage */
+figma.connect(EuiButtonGroup, 'node-id=31735-392753', {
+ props: {
+ buttonSize: figma.enum('Size', {
+ 'Small*': 's',
+ Medium: 'm',
+ Compressed: 'compressed',
+ }),
+ color: figma.enum('Color', {
+ 'Neutral*': 'text',
+ Primary: 'primary',
+ // Discrepancy between Figma and EUI
+ // Lack of "accent", "success", "warning", "danger" in Figma
+ }),
+ isDisabled: figma.boolean('Disabled'),
+ isFullWidth: figma.boolean('Full width'),
+ isIconOnly: figma.boolean('Icon only'),
+ },
+ example: (props) => (
+ {}}
+ options={[
+ { id: '0', label: 'Button' },
+ { id: '1', label: 'Button' },
+ { id: '2', label: 'Button' },
+ ]}
+ {...props}
+ />
+ ),
+});
diff --git a/packages/eui/src/components/button/button.stories.tsx b/packages/eui/src/components/button/button.stories.tsx
index f6135409d974..d9bc9a2062b5 100644
--- a/packages/eui/src/components/button/button.stories.tsx
+++ b/packages/eui/src/components/button/button.stories.tsx
@@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
+import React from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
import {
@@ -15,6 +17,18 @@ import {
import { EuiButton, Props as EuiButtonProps } from './button';
+type Story = StoryObj;
+
+export const Playground: Story = {
+ args: {
+ children: 'Button',
+ },
+ render: ({ children, ...args }: EuiButtonProps) => (
+ {children}
+ ),
+};
+disableStorybookControls(Playground, ['buttonRef']);
+
const meta: Meta = {
title: 'Navigation/EuiButton',
component: EuiButton,
@@ -37,15 +51,66 @@ const meta: Meta = {
isLoading: false,
isSelected: false,
},
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=31735-391399&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ children: figma.boolean('Icon only', {
+ true: undefined,
+ false: figma.textContent('Text'),
+ }),
+ color: figma.enum('Color', {
+ 'Primary*': undefined,
+ Neutral: 'text',
+ Success: 'success',
+ Warning: 'warning',
+ Danger: 'danger',
+ Accent: 'accent',
+ }),
+ fill: figma.enum('Style', {
+ 'Default*': undefined,
+ Filled: true,
+ }),
+ isDisabled: figma.boolean('Disabled'),
+ isLoading: figma.boolean('Loading'),
+ iconType: figma.boolean('Icon only', {
+ true: figma.instance('⮑ Icon'),
+ false: figma.boolean('Icon left', {
+ true: figma.instance('⮑ Icon left'),
+ false: figma.boolean('Icon right', {
+ true: figma.instance('⮑ Icon right'),
+ false: undefined,
+ }),
+ }),
+ }),
+ iconSide: figma.boolean('Icon left', {
+ true: 'left',
+ false: figma.boolean('Icon right', {
+ true: 'right',
+ false: figma.boolean('Loading', {
+ true: figma.boolean('Left spinner', {
+ true: 'left',
+ false: figma.boolean('Right spinner', {
+ true: 'right',
+ false: undefined,
+ }),
+ }),
+ false: undefined,
+ }),
+ }),
+ }),
+ size: figma.enum('Size', {
+ 'Medium*': 'm',
+ Small: 's',
+ // Discrepancy between Figma and EUI
+ // 'Extra Small': 'extra-small',
+ }),
+ },
+ },
+ },
};
enableFunctionToggleControls(meta, ['onClick']);
export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {
- args: {
- children: 'Button',
- },
-};
-disableStorybookControls(Playground, ['buttonRef']);
diff --git a/packages/eui/src/components/button/button_empty/button_empty.stories.tsx b/packages/eui/src/components/button/button_empty/button_empty.stories.tsx
index 66602e0bcc46..7808e6eec687 100644
--- a/packages/eui/src/components/button/button_empty/button_empty.stories.tsx
+++ b/packages/eui/src/components/button/button_empty/button_empty.stories.tsx
@@ -6,11 +6,26 @@
* Side Public License, v 1.
*/
+import React from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
+
import { disableStorybookControls } from '../../../../.storybook/utils';
import { EuiButtonEmpty, EuiButtonEmptyProps } from './button_empty';
+type Story = StoryObj;
+
+export const Playground: Story = {
+ args: {
+ children: 'Tertiary action',
+ },
+ render: ({ children, ...args }: EuiButtonEmptyProps) => (
+ {children}
+ ),
+};
+disableStorybookControls(Playground, ['buttonRef']);
+
const meta: Meta = {
title: 'Navigation/EuiButtonEmpty',
component: EuiButtonEmpty,
@@ -32,14 +47,61 @@ const meta: Meta = {
isLoading: false,
isSelected: false,
},
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=31735-391399&node-type=frame&m=dev',
+ examples: [{ example: Playground, variant: { Style: 'Empty' } }],
+ props: {
+ children: figma.boolean('Icon only', {
+ true: undefined,
+ false: figma.textContent('Text'),
+ }),
+ color: figma.enum('Color', {
+ 'Primary*': 'primary',
+ Neutral: 'text',
+ Success: 'success',
+ Warning: 'warning',
+ Danger: 'danger',
+ Accent: 'accent',
+ }),
+ iconType: figma.boolean('Icon only', {
+ true: figma.instance('⮑ Icon'),
+ false: figma.boolean('Icon left', {
+ true: figma.instance('⮑ Icon left'),
+ false: figma.boolean('Icon right', {
+ true: figma.instance('⮑ Icon right'),
+ false: undefined,
+ }),
+ }),
+ }),
+ iconSide: figma.boolean('Icon left', {
+ true: 'left',
+ false: figma.boolean('Icon right', {
+ true: 'right',
+ false: figma.boolean('Loading', {
+ true: figma.boolean('Left spinner', {
+ true: 'left',
+ false: figma.boolean('Right spinner', {
+ true: 'right',
+ false: undefined,
+ }),
+ }),
+ false: undefined,
+ }),
+ }),
+ }),
+ isDisabled: figma.boolean('Disabled'),
+ isLoading: figma.boolean('Loading'),
+ size: figma.enum('Size', {
+ 'Medium*': 'm',
+ Small: 's',
+ // TODO: document discrepancy between Figma and EUI
+ // 'Extra Small': 'extra-small',
+ }),
+ },
+ },
+ },
};
export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {
- args: {
- children: 'Tertiary action',
- },
-};
-disableStorybookControls(Playground, ['buttonRef']);
diff --git a/packages/eui/src/components/button/button_group/button_group.stories.tsx b/packages/eui/src/components/button/button_group/button_group.stories.tsx
index 9f9a3fe1bd45..3bf02311b4dc 100644
--- a/packages/eui/src/components/button/button_group/button_group.stories.tsx
+++ b/packages/eui/src/components/button/button_group/button_group.stories.tsx
@@ -7,7 +7,10 @@
*/
import React, { useState } from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
+import { useArgs } from '@storybook/preview-api';
+
import { disableStorybookControls } from '../../../../.storybook/utils';
import {
@@ -16,40 +19,6 @@ import {
EuiButtonGroupOptionProps,
} from './button_group';
-const meta: Meta = {
- title: 'Navigation/EuiButtonGroup',
- // @ts-ignore This still works for Storybook controls, even though Typescript complains
- component: EuiButtonGroup,
- argTypes: {
- type: {
- options: ['single', 'multi'],
- control: { type: 'radio' },
- },
- idSelected: {
- control: 'text',
- if: { arg: 'type', eq: 'single' },
- },
- idToSelectedMap: {
- control: 'object',
- if: { arg: 'type', eq: 'multi' },
- },
- options: {
- control: 'array',
- },
- },
- args: {
- // Component defaults
- type: 'single',
- buttonSize: 's',
- color: 'text',
- isDisabled: false,
- isFullWidth: false,
- isIconOnly: false,
- },
-};
-disableStorybookControls(meta, ['type']);
-
-export default meta;
type Story = StoryObj;
const options: EuiButtonGroupOptionProps[] = [
@@ -67,20 +36,44 @@ const options: EuiButtonGroupOptionProps[] = [
},
];
-const StatefulEuiButtonGroupSingle = (props: any) => {
- const [idSelected, setIdSelected] = useState(props.idSelected);
+/* Notice how within the same file we have 2 component wrappers,
+ - one for single selection
+ - and one for multi selection
+
+ Using these wrappers as-is in render function will result in an incorrect code snippet:
+
+ ```tsx
+
+ ```
+
+ Component name should be EuiButtonGroup and there are important props missing
+ like `type`, `options`, `onChange`, `idSelected` / `idToSelectedMap`.
+
+ Instead, 1) we could leverage args and useArgs utility hook from Storybook.
+ to synchronize the sandbox component state with Storybook controls panel.
+ (Source: https://storybook.js.org/docs/writing-stories/args#setting-args-from-within-a-story)
+*/
+
+const StatefulEuiButtonGroupSingle = (props: EuiButtonGroupProps) => {
+ const [{ idSelected }, updateArgs] = useArgs();
+
+ const onChange = (idSelected: EuiButtonGroupProps['idSelected']) => {
+ updateArgs({ idSelected });
+ };
return (
- setIdSelected(id)}
- idSelected={idSelected}
- />
+
);
};
export const SingleSelection: Story = {
- render: ({ ...args }) => ,
+ render: (args: EuiButtonGroupProps) => (
+
+ ),
args: {
legend: 'EuiButtonGroup - single selection',
options,
@@ -157,3 +150,61 @@ export const WithTooltips: Story = {
idToSelectedMap: { button1: true },
},
};
+
+const meta: Meta = {
+ title: 'Navigation/EuiButtonGroup',
+ // @ts-ignore This still works for Storybook controls, even though Typescript complains
+ component: EuiButtonGroup,
+ argTypes: {
+ type: {
+ options: ['single', 'multi'],
+ control: { type: 'radio' },
+ },
+ idSelected: {
+ control: 'text',
+ if: { arg: 'type', eq: 'single' },
+ },
+ idToSelectedMap: {
+ control: 'object',
+ if: { arg: 'type', eq: 'multi' },
+ },
+ options: {
+ control: 'array',
+ },
+ },
+ args: {
+ // Component defaults
+ type: 'single',
+ buttonSize: 's',
+ color: 'text',
+ isDisabled: false,
+ isFullWidth: false,
+ isIconOnly: false,
+ },
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=31735-392753&node-type=frame&m=dev',
+ examples: [SingleSelection],
+ props: {
+ buttonSize: figma.enum('Size', {
+ 'Small*': 's',
+ Medium: 'm',
+ Compressed: 'compressed',
+ }),
+ color: figma.enum('Color', {
+ 'Neutral*': 'text',
+ Primary: 'primary',
+ // TODO: document discrepancy between Figma and EUI
+ // accent, success, warning, danger
+ }),
+ isDisabled: figma.boolean('Disabled'),
+ isFullWidth: figma.boolean('Full width'),
+ isIconOnly: figma.boolean('Icon only'),
+ },
+ },
+ },
+};
+disableStorybookControls(meta, ['type']);
+
+export default meta;
diff --git a/packages/eui/src/components/call_out/call_out.figma.tsx b/packages/eui/src/components/call_out/call_out.figma.tsx
new file mode 100644
index 000000000000..775fb9ce2cb0
--- /dev/null
+++ b/packages/eui/src/components/call_out/call_out.figma.tsx
@@ -0,0 +1,62 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+// eslint-disable-next-line
+// @ts-nocheck
+
+/**
+ * `iconType` expects an enum member, a string or ComponentType
+ *
+ * figma.instance is a JSX.Element, not ComponentType or string but it's a conflict between
+ * Code Connect API and the icon implementation (it's not render props pattern). I don't think there's an alternative,
+ * at the same time it doesn't matter much because Figma files are not executed, they're parsed as string.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiCallOut } from './call_out';
+
+figma.connect(EuiCallOut, 'node-id=32350-392160', {
+ props: {
+ children: figma.nestedProps('Children', {
+ text: figma.children('Callout text'),
+ }),
+ color: figma.enum('Color', {
+ Success: 'success',
+ Danger: 'danger',
+ Warning: 'warning',
+ Primary: 'primary',
+ }),
+ iconType: figma.boolean('⮑ Icon', {
+ true: figma.instance('⮑ Icon glyph'),
+ false: undefined,
+ }),
+ onDismiss: figma.boolean('Dismiss', {
+ true: () => {},
+ false: undefined,
+ }),
+ size: figma.enum('Size', {
+ Medium: 'm',
+ Small: 's',
+ }),
+ title: figma.boolean('Title', {
+ true: figma.nestedProps('Callout title', {
+ text: figma.string('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ },
+ example: ({ children, title, ...props }) => (
+
+ {children.text}
+
+ ),
+});
diff --git a/packages/eui/src/components/call_out/call_out.stories.tsx b/packages/eui/src/components/call_out/call_out.stories.tsx
index 7f40c86a6550..748af0ce3c4f 100644
--- a/packages/eui/src/components/call_out/call_out.stories.tsx
+++ b/packages/eui/src/components/call_out/call_out.stories.tsx
@@ -6,10 +6,40 @@
* Side Public License, v 1.
*/
+import React from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
import { EuiCallOut, EuiCallOutProps } from './call_out';
+type Story = StoryObj;
+
+// It's hard to align component props with Figma property mapping
+/* type Story = StoryObj<
+ EuiCallOutProps & {
+ title: { text: string };
+ children: { text: string };
+ }
+>; */
+
+export const Playground: Story = {
+ args: {
+ title: 'Callout title',
+ children: 'Callout text',
+ },
+ render: ({ children, title, ...props }) => (
+
+ {children}
+
+ ),
+ // This would have to be the render function with nested properties:
+ /* render: ({ children, title, ...props }) => (
+
+ {children.text}
+
+ ), */
+};
+
const meta: Meta = {
title: 'Display/EuiCallOut',
component: EuiCallOut,
@@ -22,14 +52,38 @@ const meta: Meta = {
heading: 'p',
size: 'm',
},
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=32350-392160&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ children: figma.instance('Children'),
+ color: figma.enum('Color', {
+ Success: 'success',
+ Danger: 'danger',
+ Warning: 'warning',
+ Primary: 'primary',
+ }),
+ iconType: figma.boolean('⮑ Icon', {
+ true: figma.instance('⮑ Icon glyph'),
+ false: undefined,
+ }),
+ onDismiss: figma.boolean('Dismiss', {
+ true: () => {},
+ false: undefined,
+ }),
+ size: figma.enum('Size', {
+ Medium: 'm',
+ Small: 's',
+ }),
+ // This has to differ from the standalone Figma config to align with Storybook
+ title: figma.boolean('Title', {
+ true: 'Title',
+ }),
+ },
+ },
+ },
};
export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {
- args: {
- title: 'Callout title',
- children: 'Callout text',
- },
-};
diff --git a/packages/eui/src/components/combo_box/combo_box.figma.tsx b/packages/eui/src/components/combo_box/combo_box.figma.tsx
new file mode 100644
index 000000000000..d47eeabc0431
--- /dev/null
+++ b/packages/eui/src/components/combo_box/combo_box.figma.tsx
@@ -0,0 +1,71 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiComboBox } from './combo_box';
+import { EuiFormRow } from '../form';
+
+figma.connect(EuiComboBox, 'node-id=15883-161301', {
+ props: {
+ ariaLabel: figma.boolean('Label', {
+ true: undefined,
+ false: 'Meaningful label',
+ }),
+ compressed: figma.boolean('Compressed'),
+ error: figma.nestedProps('📦Form Row / Error text', {
+ text: figma.textContent('Text'),
+ }),
+ helpText: figma.boolean('Help text', {
+ true: figma.nestedProps('📦 Form Row / Help text', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ isDisabled: figma.enum('State', {
+ Disabled: true,
+ }),
+ isInvalid: figma.enum('State', {
+ Invalid: true,
+ }),
+ label: figma.boolean('Label', {
+ true: figma.nestedProps('📦 Form Row / Label', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ },
+ example: ({ ariaLabel, error, helpText, isInvalid, label, ...props }) => (
+
+ {}}
+ isInvalid={isInvalid}
+ {...props}
+ />
+
+ ),
+});
diff --git a/packages/eui/src/components/combo_box/combo_box.stories.tsx b/packages/eui/src/components/combo_box/combo_box.stories.tsx
index 880c9d93496e..4e3c385fc964 100644
--- a/packages/eui/src/components/combo_box/combo_box.stories.tsx
+++ b/packages/eui/src/components/combo_box/combo_box.stories.tsx
@@ -7,6 +7,7 @@
*/
import React, { useCallback, useState } from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
import { userEvent, waitFor, within, expect } from '@storybook/test';
@@ -19,7 +20,8 @@ import { EuiCode } from '../code';
import { EuiFlexItem } from '../flex';
import { EuiComboBoxOptionMatcher } from './types';
-import { EuiComboBox, EuiComboBoxProps } from './combo_box';
+import { EuiComboBox as ComboBox, EuiComboBoxProps } from './combo_box';
+import { EuiFormRow } from '../form/form_row';
const options = [
{ label: 'Item 1' },
@@ -29,50 +31,75 @@ const options = [
{ label: 'Item 5' },
];
-const meta: Meta> = {
- title: 'Forms/EuiComboBox',
- // @ts-ignore typescript shenanigans
- component: EuiComboBox,
- argTypes: {
- singleSelection: {
- control: 'radio',
- options: [false, true, 'asPlainText'],
- },
- append: { control: 'text' },
- prepend: { control: 'text' },
- // Storybook is skipping the Pick<> props from EuiComboBoxList for some annoying reason
- onCreateOption: { control: 'boolean' }, // Set to a true/false for ease of testing
- customOptionText: { control: 'text' },
- renderOption: { control: 'function' },
- },
- args: {
- // Pass options in by default for ease of testing
- options: options,
- selectedOptions: [options[0]],
- // Component defaults
- delimiter: ',',
- sortMatchesBy: 'none',
- singleSelection: false,
- noSuggestions: false,
- async: false,
- isCaseSensitive: false,
- isClearable: true,
- isDisabled: false,
- isInvalid: false,
- isLoading: false,
- autoFocus: false,
- compressed: false,
- fullWidth: false,
- onCreateOption: undefined, // Override Storybook's default callback
- },
+const EuiComboBox = ({
+ singleSelection,
+ onCreateOption,
+ onChange,
+ ...args
+}: EuiComboBoxProps<{}>) => {
+ const [selectedOptions, setSelectedOptions] = useState(args.selectedOptions);
+ const handleOnChange: EuiComboBoxProps<{}>['onChange'] = (
+ options,
+ ...args
+ ) => {
+ setSelectedOptions(options);
+ onChange?.(options, ...args);
+ };
+ const _onCreateOption: EuiComboBoxProps<{}>['onCreateOption'] = (
+ searchValue,
+ ...args
+ ) => {
+ const createdOption = { label: searchValue };
+ setSelectedOptions((prevState) =>
+ !prevState || singleSelection
+ ? [createdOption]
+ : [...prevState, createdOption]
+ );
+ onCreateOption?.(searchValue, ...args);
+ };
+ return (
+
+ );
};
-enableFunctionToggleControls(meta, ['onChange', 'onCreateOption']);
-export default meta;
-type Story = StoryObj>;
+type Story = StoryObj<
+ EuiComboBoxProps<{}> & {
+ ariaLabel: string;
+ error: { text?: string };
+ helpText: { text?: string };
+ label: { text?: string };
+ }
+>;
export const Playground: Story = {
- render: (args) => ,
+ render: ({ ariaLabel, error, helpText, isInvalid, label, ...props }) => (
+
+ {}}
+ isInvalid={isInvalid}
+ {...props}
+ />
+
+ ),
};
export const WithTooltip: Story = {
@@ -94,7 +121,7 @@ export const WithTooltip: Story = {
value: idx,
})),
},
- render: (args) => ,
+ render: (args) => ,
play: lokiPlayDecorator(async (context) => {
const { bodyElement, step } = context;
@@ -158,7 +185,7 @@ export const Groups: Story = {
],
autoFocus: true,
},
- render: (args) => ,
+ render: (args) => ,
};
export const NestedOptionsGroups: Story = {
@@ -189,49 +216,7 @@ export const NestedOptionsGroups: Story = {
],
autoFocus: true,
},
- render: (args) => ,
-};
-
-const StatefulComboBox = ({
- singleSelection,
- onCreateOption,
- onChange,
- ...args
-}: EuiComboBoxProps<{}>) => {
- const [selectedOptions, setSelectedOptions] = useState(args.selectedOptions);
- const handleOnChange: EuiComboBoxProps<{}>['onChange'] = (
- options,
- ...args
- ) => {
- setSelectedOptions(options);
- onChange?.(options, ...args);
- };
- const _onCreateOption: EuiComboBoxProps<{}>['onCreateOption'] = (
- searchValue,
- ...args
- ) => {
- const createdOption = { label: searchValue };
- setSelectedOptions((prevState) =>
- !prevState || singleSelection
- ? [createdOption]
- : [...prevState, createdOption]
- );
- onCreateOption?.(searchValue, ...args);
- };
- return (
-
- );
+ render: (args) => ,
};
const StoryCustomMatcher = ({
@@ -263,7 +248,7 @@ const StoryCustomMatcher = ({
matched.
-
);
};
+
+const meta: Meta> = {
+ title: 'Forms/EuiComboBox',
+ // @ts-ignore typescript shenanigans
+ component: ComboBox,
+ argTypes: {
+ singleSelection: {
+ control: 'radio',
+ options: [false, true, 'asPlainText'],
+ },
+ append: { control: 'text' },
+ prepend: { control: 'text' },
+ // Storybook is skipping the Pick<> props from EuiComboBoxList for some annoying reason
+ onCreateOption: { control: 'boolean' }, // Set to a true/false for ease of testing
+ customOptionText: { control: 'text' },
+ renderOption: { control: 'function' },
+ },
+ args: {
+ // Pass options in by default for ease of testing
+ options: options,
+ selectedOptions: [options[0]],
+ // Component defaults
+ delimiter: ',',
+ sortMatchesBy: 'none',
+ singleSelection: false,
+ noSuggestions: false,
+ async: false,
+ isCaseSensitive: false,
+ isClearable: true,
+ isDisabled: false,
+ isInvalid: false,
+ isLoading: false,
+ autoFocus: false,
+ compressed: false,
+ fullWidth: false,
+ onCreateOption: undefined, // Override Storybook's default callback
+ },
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=15883-161301&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ ariaLabel: figma.boolean('Label', {
+ true: undefined,
+ false: 'Meaningful label',
+ }),
+ compressed: figma.boolean('Compressed'),
+ error: figma.nestedProps('📦Form Row / Error text', {
+ text: figma.textContent('Text'),
+ }),
+ helpText: figma.boolean('Help text', {
+ true: figma.nestedProps('📦 Form Row / Help text', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ isDisabled: figma.enum('State', {
+ Disabled: true,
+ }),
+ isInvalid: figma.enum('State', {
+ Invalid: true,
+ }),
+ label: figma.boolean('Label', {
+ true: figma.nestedProps('📦 Form Row / Label', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ },
+ },
+ },
+};
+enableFunctionToggleControls(meta, ['onChange', 'onCreateOption']);
+
+export default meta;
diff --git a/packages/eui/src/components/form/field_text/field_text.figma.tsx b/packages/eui/src/components/form/field_text/field_text.figma.tsx
new file mode 100644
index 000000000000..0f81cd8b349c
--- /dev/null
+++ b/packages/eui/src/components/form/field_text/field_text.figma.tsx
@@ -0,0 +1,64 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiFieldText } from './field_text';
+import { EuiFormRow } from '../form_row';
+
+figma.connect(EuiFieldText, 'node-id=13676-796', {
+ props: {
+ ariaLabel: figma.boolean('Label', {
+ true: undefined,
+ false: 'Meaningful label',
+ }),
+ compressed: figma.boolean('Compressed'),
+ error: figma.nestedProps('📦Form Row / Error text', {
+ text: figma.textContent('Text'),
+ }),
+ helpText: figma.boolean('Help text', {
+ true: figma.nestedProps('📦 Form Row / Help text', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ isDisabled: figma.enum('State', {
+ Disabled: true,
+ }),
+ isInvalid: figma.enum('State', {
+ Invalid: true,
+ }),
+ label: figma.boolean('Label', {
+ true: figma.nestedProps('📦 Form Row / Label', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ },
+ example: ({ ariaLabel, error, helpText, isInvalid, label, ...props }) => (
+
+ {}}
+ isInvalid={isInvalid}
+ {...props}
+ />
+
+ ),
+});
diff --git a/packages/eui/src/components/form/field_text/field_text.stories.tsx b/packages/eui/src/components/form/field_text/field_text.stories.tsx
index 70ead83c42d4..9c23df495de8 100644
--- a/packages/eui/src/components/form/field_text/field_text.stories.tsx
+++ b/packages/eui/src/components/form/field_text/field_text.stories.tsx
@@ -7,45 +7,46 @@
*/
import React from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
+
import {
disableStorybookControls,
moveStorybookControlsToCategory,
} from '../../../../.storybook/utils';
+import { EuiFormRow } from '../form_row';
import { EuiFieldText, EuiFieldTextProps } from './field_text';
-const meta: Meta = {
- title: 'Forms/EuiFieldText',
- component: EuiFieldText,
- argTypes: {
- // For quicker/easier QA
- icon: { control: 'text' },
- prepend: { control: 'text' },
- append: { control: 'text' },
- value: { control: 'text' },
- },
- args: {
- // Component defaults
- compressed: false,
- fullWidth: false,
- isInvalid: false,
- isLoading: false,
- disabled: false,
- readOnly: false,
- controlOnly: false,
- // Added for easier testing
- placeholder: 'EuiFieldText',
- id: '',
- name: '',
- },
-};
+// We need to add some args to make the snippet fully accurate
+// Here, args do not map 1:1 to props
+type Story = StoryObj<
+ EuiFieldTextProps & {
+ ariaLabel: string;
+ error: { text?: string };
+ helpText: { text?: string };
+ label: { text?: string };
+ }
+>;
-export default meta;
-type Story = StoryObj;
-disableStorybookControls(meta, ['inputRef']);
-
-export const Playground: Story = {};
+export const Playground: Story = {
+ render: ({ ariaLabel, error, helpText, isInvalid, label, ...props }) => (
+
+ {}}
+ isInvalid={isInvalid}
+ {...props}
+ />
+
+ ),
+};
export const IconShape: Story = {
parameters: {
@@ -98,3 +99,71 @@ export const AutoFill: Story = {
name: 'autofill-test',
},
};
+
+const meta: Meta = {
+ title: 'Forms/EuiFieldText',
+ component: EuiFieldText,
+ argTypes: {
+ // For quicker/easier QA
+ icon: { control: 'text' },
+ prepend: { control: 'text' },
+ append: { control: 'text' },
+ value: { control: 'text' },
+ },
+ args: {
+ // Component defaults
+ compressed: false,
+ fullWidth: false,
+ isInvalid: false,
+ isLoading: false,
+ disabled: false,
+ readOnly: false,
+ controlOnly: false,
+ // Added for easier testing
+ placeholder: 'EuiFieldText',
+ id: '',
+ name: '',
+ },
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=13676-796&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ ariaLabel: figma.boolean('Label', {
+ true: undefined,
+ false: 'Meaningful label',
+ }),
+ compressed: figma.boolean('Compressed'),
+ error: figma.nestedProps('📦Form Row / Error text', {
+ text: figma.textContent('Text'),
+ }),
+ helpText: figma.boolean('Help text', {
+ true: figma.nestedProps('📦 Form Row / Help text', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ isDisabled: figma.enum('State', {
+ Disabled: true,
+ }),
+ isInvalid: figma.enum('State', {
+ Invalid: true,
+ }),
+ label: figma.boolean('Label', {
+ true: figma.nestedProps('📦 Form Row / Label', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ },
+ },
+ },
+};
+
+export default meta;
+disableStorybookControls(meta, ['inputRef']);
diff --git a/packages/eui/src/components/form/select/select.figma.tsx b/packages/eui/src/components/form/select/select.figma.tsx
new file mode 100644
index 000000000000..2c7147976e60
--- /dev/null
+++ b/packages/eui/src/components/form/select/select.figma.tsx
@@ -0,0 +1,69 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiSelect } from './select';
+import { EuiFormRow } from '../form_row';
+
+figma.connect(EuiSelect, 'node-id=15883-129716', {
+ props: {
+ ariaLabel: figma.boolean('Label', {
+ true: undefined,
+ false: 'Meaningful label',
+ }),
+ compressed: figma.boolean('Compressed'),
+ error: figma.nestedProps('📦Form Row / Error text', {
+ text: figma.textContent('Text'),
+ }),
+ helpText: figma.boolean('Help text', {
+ true: figma.nestedProps('📦 Form Row / Help text', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ isDisabled: figma.enum('State', {
+ Disabled: true,
+ }),
+ isInvalid: figma.enum('State', {
+ Invalid: true,
+ }),
+ label: figma.boolean('Label', {
+ true: figma.nestedProps('📦 Form Row / Label', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ },
+ example: ({ ariaLabel, error, helpText, isInvalid, label, ...props }) => (
+
+ {}}
+ isInvalid={isInvalid}
+ {...props}
+ />
+
+ ),
+});
diff --git a/packages/eui/src/components/form/select/select.stories.tsx b/packages/eui/src/components/form/select/select.stories.tsx
index deb4e0401f2c..18fc78f4ecb0 100644
--- a/packages/eui/src/components/form/select/select.stories.tsx
+++ b/packages/eui/src/components/form/select/select.stories.tsx
@@ -8,15 +8,69 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
+import figma from '@figma/code-connect';
import {
disableStorybookControls,
enableFunctionToggleControls,
} from '../../../../.storybook/utils';
import { EuiIcon } from '../../icon';
+import { EuiFormRow } from '../form_row';
import { EuiSelect, EuiSelectProps } from './select';
+// We need to add story-specific arguments (that do not map directly to component props)
+type Story = StoryObj<
+ EuiSelectProps & {
+ ariaLabel: string;
+ error: { text?: string };
+ helpText: { text?: string };
+ label: { text?: string };
+ }
+>;
+
+export const Playground: Story = {
+ args: {
+ defaultValue: 'option-2',
+ options: [
+ { value: 'option-1', text: 'Option 1' },
+ { value: 'option-2', text: 'Option 2' },
+ { value: 'option-3', text: 'Option 3' },
+ ],
+ },
+ // Each prop has to be explicitly defined to be present in the Code Connect code snippet
+ // but if it's not available in the Figma mapping, the parsing will fail if we add it as an explicit story arg
+ render: ({
+ ariaLabel,
+ error,
+ helpText,
+ isInvalid,
+ label,
+ /* options - this will fail parsing */
+ ...props
+ }) => (
+
+ {}}
+ isInvalid={isInvalid}
+ {...props}
+ />
+
+ ),
+};
+
const meta: Meta = {
title: 'Forms/EuiSelect',
component: EuiSelect,
@@ -25,6 +79,43 @@ const meta: Meta = {
// Excude onMouseUp from controls, as it's not a terribly useful prop to document
exclude: ['onMouseUp'],
},
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=15883-129716&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ ariaLabel: figma.boolean('Label', {
+ true: undefined,
+ false: 'Meaningful label',
+ }),
+ compressed: figma.boolean('Compressed'),
+ error: figma.nestedProps('📦Form Row / Error text', {
+ text: figma.textContent('Text'),
+ }),
+ helpText: figma.boolean('Help text', {
+ true: figma.nestedProps('📦 Form Row / Help text', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ isDisabled: figma.enum('State', {
+ Disabled: true,
+ }),
+ isInvalid: figma.enum('State', {
+ Invalid: true,
+ }),
+ label: figma.boolean('Label', {
+ true: figma.nestedProps('📦 Form Row / Label', {
+ text: figma.textContent('Text'),
+ }),
+ false: {
+ text: undefined,
+ },
+ }),
+ },
+ },
},
argTypes: {
append: {
@@ -63,15 +154,3 @@ enableFunctionToggleControls(meta, ['onChange']);
disableStorybookControls(meta, ['inputRef']);
export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {
- args: {
- defaultValue: 'option-2',
- options: [
- { value: 'option-1', text: 'Option 1' },
- { value: 'option-2', text: 'Option 2' },
- { value: 'option-3', text: 'Option 3' },
- ],
- },
-};
diff --git a/packages/eui/src/components/icon/icon.figma.tsx b/packages/eui/src/components/icon/icon.figma.tsx
new file mode 100644
index 000000000000..f71998b8af0a
--- /dev/null
+++ b/packages/eui/src/components/icon/icon.figma.tsx
@@ -0,0 +1,38 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+// eslint-disable-next-line
+// @ts-nocheck
+
+/**
+ * `type` expects an enum member, a string or ComponentType
+ *
+ * figma.instance is a JSX.Element, not ComponentType or string but it's a conflict between
+ * Code Connect API and the icon implementation (it's not render props pattern). I don't think there's an alternative,
+ * at the same time it doesn't matter much because Figma files are not executed, they're parsed as string.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiIcon } from './icon';
+
+figma.connect(EuiIcon, 'node-id=31572-393323', {
+ props: {
+ type: figma.instance('Type'),
+ size: figma.enum('Size', {
+ 'Small - 12px': 's',
+ 'Medium* - 16px': 'm',
+ 'Large - 24px': 'l',
+ 'X-Large - 32px': 'xl',
+ 'XX-Large - 40px': 'xxl',
+ Size6: 'original',
+ }),
+ },
+ example: (props) => ,
+});
diff --git a/packages/eui/src/components/icon/icon.stories.tsx b/packages/eui/src/components/icon/icon.stories.tsx
index c226953ff576..95f60c48bc37 100644
--- a/packages/eui/src/components/icon/icon.stories.tsx
+++ b/packages/eui/src/components/icon/icon.stories.tsx
@@ -6,10 +6,18 @@
* Side Public License, v 1.
*/
+import React from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
import { EuiIcon, EuiIconProps } from './icon';
+type Story = StoryObj;
+
+export const Playground: Story = {
+ render: (props) => ,
+};
+
const meta: Meta = {
title: 'Display/EuiIcon',
component: EuiIcon,
@@ -21,9 +29,24 @@ const meta: Meta = {
type: 'accessibility',
size: 'm',
},
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=31572-393323&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ type: figma.instance('Type'),
+ size: figma.enum('Size', {
+ 'Small - 12px': 's',
+ 'Medium* - 16px': 'm',
+ 'Large - 24px': 'l',
+ 'X-Large - 32px': 'xl',
+ 'XX-Large - 40px': 'xxl',
+ Size6: 'original',
+ }),
+ },
+ },
+ },
};
export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {};
diff --git a/packages/eui/src/components/panel/panel.figma.tsx b/packages/eui/src/components/panel/panel.figma.tsx
new file mode 100644
index 000000000000..bbcf1336a2fe
--- /dev/null
+++ b/packages/eui/src/components/panel/panel.figma.tsx
@@ -0,0 +1,55 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiPanel } from './panel';
+
+const sharedProps = {
+ borderRadius: figma.boolean('Border radius', {
+ true: 'm',
+ false: 'none',
+ }),
+ color: figma.enum('Color', {
+ 'Plain*': undefined,
+ Subdued: 'subdued',
+ Primary: 'primary',
+ Success: 'success',
+ Warning: 'warning',
+ Danger: 'danger',
+ Accent: 'accent',
+ Transparent: 'transparent',
+ }),
+ children: figma.instance('Content'),
+ hasBorder: figma.boolean('Border'),
+ paddingSize: figma.enum('Padding size', {
+ None: 'none',
+ Small: 's',
+ 'Medium*': undefined,
+ Large: 'l',
+ }),
+};
+
+figma.connect(EuiPanel, 'node-id=32642-391756', {
+ variant: { Shadow: true },
+ props: sharedProps,
+ example: ({ children, ...props }) => (
+ {children}
+ ),
+});
+
+figma.connect(EuiPanel, 'node-id=32642-391756', {
+ variant: { Shadow: false },
+ props: sharedProps,
+ example: ({ children, ...props }) => (
+
+ {children}
+
+ ),
+});
diff --git a/packages/eui/src/components/panel/panel.stories.tsx b/packages/eui/src/components/panel/panel.stories.tsx
index 03e3296a6377..620eafc13aac 100644
--- a/packages/eui/src/components/panel/panel.stories.tsx
+++ b/packages/eui/src/components/panel/panel.stories.tsx
@@ -6,14 +6,28 @@
* Side Public License, v 1.
*/
+import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
+import figma from '@figma/code-connect';
import {
disableStorybookControls,
enableFunctionToggleControls,
} from '../../../.storybook/utils';
+
import { EuiPanel, EuiPanelProps } from './panel';
+type Story = StoryObj;
+
+export const Playground: Story = {
+ args: {
+ children: 'Panel content',
+ },
+ render: ({ children, ...props }: EuiPanelProps) => (
+ {children}
+ ),
+};
+
const meta: Meta = {
title: 'Layout/EuiPanel',
component: EuiPanel,
@@ -33,15 +47,39 @@ const meta: Meta = {
hasBorder: false,
grow: true,
},
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=32642-391756&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ borderRadius: figma.boolean('Border radius', {
+ true: 'm',
+ false: 'none',
+ }),
+ color: figma.enum('Color', {
+ 'Plain*': undefined,
+ Subdued: 'subdued',
+ Primary: 'primary',
+ Success: 'success',
+ Warning: 'warning',
+ Danger: 'danger',
+ Accent: 'accent',
+ Transparent: 'transparent',
+ }),
+ children: figma.instance('Content'),
+ hasBorder: figma.boolean('Border'),
+ paddingSize: figma.enum('Padding size', {
+ None: 'none',
+ Small: 's',
+ 'Medium*': undefined,
+ Large: 'l',
+ }),
+ },
+ },
+ },
};
enableFunctionToggleControls(meta, ['onClick']);
disableStorybookControls(meta, ['panelRef']);
export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {
- args: {
- children: 'Panel content',
- },
-};
diff --git a/packages/eui/src/components/text/text.figma.tsx b/packages/eui/src/components/text/text.figma.tsx
new file mode 100644
index 000000000000..e27463e7cf65
--- /dev/null
+++ b/packages/eui/src/components/text/text.figma.tsx
@@ -0,0 +1,24 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiText } from './text';
+
+figma.connect(EuiText, 'node-id=32296-391647', {
+ props: {
+ children: figma.string('Text'),
+ size: figma.enum('Size', {
+ Medium: 'm',
+ Small: 's',
+ 'X-Small': 'xs',
+ }),
+ },
+ example: ({ children, ...props }) => {children},
+});
diff --git a/packages/eui/src/components/text/text.stories.tsx b/packages/eui/src/components/text/text.stories.tsx
index e8a613c1ea54..4f20fe667447 100644
--- a/packages/eui/src/components/text/text.stories.tsx
+++ b/packages/eui/src/components/text/text.stories.tsx
@@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
+import React from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
import { faker } from '@faker-js/faker';
@@ -14,6 +16,17 @@ faker.seed(42);
import { moveStorybookControlsToCategory } from '../../../.storybook/utils';
import { EuiText, EuiTextProps } from './text';
+type Story = StoryObj;
+
+export const Playground: Story = {
+ args: {
+ children: faker.lorem.sentences(3),
+ },
+ render: ({ children, ...args }: EuiTextProps) => (
+ {children}
+ ),
+};
+
const meta: Meta = {
title: 'Display/EuiText/EuiText',
component: EuiText,
@@ -27,15 +40,23 @@ const meta: Meta = {
textAlign: 'left',
component: 'div',
},
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=32296-391647&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ children: figma.string('Text'),
+ size: figma.enum('Size', {
+ Medium: 'm',
+ Small: 's',
+ 'X-Small': 'xs',
+ }),
+ },
+ },
+ },
};
moveStorybookControlsToCategory(meta, ['color'], 'EuiTextColor props');
moveStorybookControlsToCategory(meta, ['textAlign'], 'EuiTextAlign props');
export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {
- args: {
- children: faker.lorem.sentences(3),
- },
-};
diff --git a/packages/eui/src/components/tool_tip/tool_tip.figma.tsx b/packages/eui/src/components/tool_tip/tool_tip.figma.tsx
new file mode 100644
index 000000000000..76d7c166fae5
--- /dev/null
+++ b/packages/eui/src/components/tool_tip/tool_tip.figma.tsx
@@ -0,0 +1,37 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import figma from '@figma/code-connect';
+
+import { EuiToolTip } from './tool_tip';
+
+figma.connect(EuiToolTip, 'node-id=32039-390707', {
+ props: {
+ content: figma.string('Description'),
+ position: figma.enum('Direction', {
+ '12:00 ↑': 'bottom',
+ '11:00': 'bottom',
+ '10:00': 'right',
+ '8:00': 'right',
+ '7:00': 'top',
+ '6:00 ↓': 'top',
+ '5:00': 'top',
+ '4:00': 'left',
+ '3:00 →': 'left',
+ '2:00': 'left',
+ '1::00': 'bottom',
+ '9:00 ←': 'right',
+ }),
+ title: figma.boolean('Title', {
+ true: figma.string('⮑ Title'),
+ false: undefined,
+ }),
+ },
+ example: (props) => ,
+});
diff --git a/packages/eui/src/components/tool_tip/tool_tip.stories.tsx b/packages/eui/src/components/tool_tip/tool_tip.stories.tsx
index 8c4deb062634..0e522ebaf647 100644
--- a/packages/eui/src/components/tool_tip/tool_tip.stories.tsx
+++ b/packages/eui/src/components/tool_tip/tool_tip.stories.tsx
@@ -7,14 +7,40 @@
*/
import React from 'react';
+import figma from '@figma/code-connect';
import type { Meta, StoryObj } from '@storybook/react';
import { enableFunctionToggleControls } from '../../../.storybook/utils';
import { LOKI_SELECTORS } from '../../../.storybook/loki';
import { EuiFlexGroup } from '../flex';
import { EuiButton } from '../button';
+
import { EuiToolTip, EuiToolTipProps } from './tool_tip';
+type Story = StoryObj;
+
+export const Playground: Story = {
+ args: {
+ // using autoFocus here as small trick to ensure showing the tooltip on load (e.g. for VRT)
+ // TODO: uncomment loki play() interactions and remove autoFocus once #7747 is merged
+ children: Tooltip trigger,
+ content: 'tooltip content',
+ },
+ render: (props) => ,
+ // play: lokiPlayDecorator(async (context) => {
+ // const { bodyElement, step } = context;
+
+ // const canvas = within(bodyElement);
+
+ // await step('show tooltip on click', async () => {
+ // await userEvent.click(canvas.getByRole('button'));
+ // await waitFor(() => {
+ // expect(canvas.getByRole('tooltip')).toBeVisible();
+ // });
+ // });
+ // }),
+};
+
const meta: Meta = {
title: 'Display/EuiToolTip',
component: EuiToolTip,
@@ -23,6 +49,32 @@ const meta: Meta = {
loki: {
chromeSelector: LOKI_SELECTORS.portal,
},
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/RzfYLj2xmH9K7gQtbSKygn/Elastic-UI?node-id=32039-390707&node-type=frame&m=dev',
+ examples: [Playground],
+ props: {
+ content: figma.string('Description'),
+ position: figma.enum('Direction', {
+ '12:00 ↑': 'bottom',
+ '11:00': 'bottom',
+ '10:00': 'right',
+ '8:00': 'right',
+ '7:00': 'top',
+ '6:00 ↓': 'top',
+ '5:00': 'top',
+ '4:00': 'left',
+ '3:00 →': 'left',
+ '2:00': 'left',
+ '1::00': 'bottom',
+ '9:00 ←': 'right',
+ }),
+ title: figma.boolean('Title', {
+ true: figma.string('⮑ Title'),
+ false: undefined,
+ }),
+ },
+ },
},
decorators: [
(Story, { args }) => (
@@ -50,25 +102,3 @@ const meta: Meta = {
enableFunctionToggleControls(meta, ['onMouseOut']);
export default meta;
-type Story = StoryObj;
-
-export const Playground: Story = {
- args: {
- // using autoFocus here as small trick to ensure showing the tooltip on load (e.g. for VRT)
- // TODO: uncomment loki play() interactions and remove autoFocus once #7747 is merged
- children: Tooltip trigger,
- content: 'tooltip content',
- },
- // play: lokiPlayDecorator(async (context) => {
- // const { bodyElement, step } = context;
-
- // const canvas = within(bodyElement);
-
- // await step('show tooltip on click', async () => {
- // await userEvent.click(canvas.getByRole('button'));
- // await waitFor(() => {
- // expect(canvas.getByRole('tooltip')).toBeVisible();
- // });
- // });
- // }),
-};