diff --git a/packages/kbn-shared-ux-components/src/index.ts b/packages/kbn-shared-ux-components/src/index.ts index 557ac980a14c6..a022d2d0c755d 100644 --- a/packages/kbn-shared-ux-components/src/index.ts +++ b/packages/kbn-shared-ux-components/src/index.ts @@ -10,7 +10,7 @@ import React from 'react'; import { withSuspense } from '@kbn/shared-ux-utility'; /** - * The Lazily-loaded `ExitFullScreenButton` component. Consumers should use `React.Suspennse` or the + * The Lazily-loaded `ExitFullScreenButton` component. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ export const LazyExitFullScreenButton = React.lazy(() => @@ -42,12 +42,12 @@ export const ExitFullScreenButton = withSuspense(LazyExitFullScreenButton); export const ToolbarButton = withSuspense(LazyToolbarButton); /** - * An example of the solution toolbar button + * An example of the toolbar button and popover */ -export { AddFromLibraryButton } from './toolbar'; +export { AddFromLibraryButton, ToolbarPopover } from './toolbar'; /** - * The Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspennse` or the + * The Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ export const LazyNoDataViews = React.lazy(() => @@ -64,7 +64,7 @@ export const LazyNoDataViews = React.lazy(() => export const NoDataViews = withSuspense(LazyNoDataViews); /** - * A pure `NoDataViews` component, with no services hooks. Consumers should use `React.Suspennse` or the + * A pure `NoDataViews` component, with no services hooks. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ export const LazyNoDataViewsComponent = React.lazy(() => @@ -81,7 +81,7 @@ export const LazyNoDataViewsComponent = React.lazy(() => export const NoDataViewsComponent = withSuspense(LazyNoDataViewsComponent); /** - * The Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspennse` or the + * The Lazily-loaded `IconButtonGroup` component. Consumers should use `React.Suspense` or the * `withSuspense` HOC to load this component. */ export const LazyIconButtonGroup = React.lazy(() => diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap index 4cdc858c7e50c..520184f0f96dc 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/add_from_library/__snapshots__/add_from_library.test.tsx.snap @@ -9,6 +9,7 @@ exports[` is rendered 1`] = ` @@ -18,6 +19,7 @@ exports[` is rendered 1`] = ` disabled={false} element="button" fill={true} + iconSide="left" iconType="folderOpen" isDisabled={false} size="m" diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap index 8e447f7a0ee5c..7f50690aba8a6 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/__snapshots__/primary.test.tsx.snap @@ -43,6 +43,7 @@ exports[` is rendered 1`] = ` is rendered 1`] = ` disabled={false} element="button" fill={true} + iconSide="left" isDisabled={false} size="m" type="button" diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx index c1fa431f39bdc..5b72eb92360be 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.mdx @@ -1,12 +1,12 @@ --- id: sharedUX/Components/ToolbarButton slug: /shared-ux/components/toolbar/buttons/primary -title: Solution Toolbar Button +title: Toolbar Button summary: An opinionated implementation of the toolbar extracted to just the button. tags: ['shared-ux', 'component'] -date: 2022-02-17 +date: 2022-03-30 --- > This documentation is in-progress. -This button is a part of the solution toolbar component. This button has primary styling and requires a label. OnClick handlers and icon types are supported as an extension of EuiButtonProps. Icons are always on the left of any labels within the button. +This button is a part of the solution toolbar component. This button has primary styling and requires a label. OnClick handlers, icon side, and icon types are supported as an extension of EuiButtonProps. Icons by default are left of any labels within the button but can also be set to right. diff --git a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx index 48677d965fa6c..f935a08fe8434 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx +++ b/packages/kbn-shared-ux-components/src/toolbar/buttons/primary/primary.tsx @@ -10,13 +10,13 @@ import React from 'react'; import { EuiButton } from '@elastic/eui'; import { EuiButtonPropsForButton } from '@elastic/eui/src/components/button/button'; -export interface Props extends Pick { +export interface Props extends Pick { label: string; } -export const ToolbarButton = ({ label, ...rest }: Props) => { +export const ToolbarButton = ({ label, iconSide = 'left', ...rest }: Props) => { return ( - + {label} ); diff --git a/packages/kbn-shared-ux-components/src/toolbar/index.ts b/packages/kbn-shared-ux-components/src/toolbar/index.ts index 513f81c1ddfc7..f3e74ace3e5ff 100644 --- a/packages/kbn-shared-ux-components/src/toolbar/index.ts +++ b/packages/kbn-shared-ux-components/src/toolbar/index.ts @@ -8,3 +8,4 @@ export { ToolbarButton } from './buttons/primary/primary'; export { IconButtonGroup } from './buttons/icon_button_group/icon_button_group'; export { AddFromLibraryButton } from './buttons/add_from_library/add_from_library'; +export { ToolbarPopover } from './popovers/popover'; diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/__snapshots__/popover.test.tsx.snap b/packages/kbn-shared-ux-components/src/toolbar/popovers/__snapshots__/popover.test.tsx.snap new file mode 100644 index 0000000000000..c38d0b8e4cd7c --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/__snapshots__/popover.test.tsx.snap @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` is rendered 1`] = ` + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+
+ + + + + + + +
+
+
+
+`; diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.mdx b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.mdx new file mode 100644 index 0000000000000..d1ade51485ae4 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.mdx @@ -0,0 +1,11 @@ +--- +id: sharedUX/Components/Popover +slug: /shared-ux/components/toolbar/popovers/popover +title: Toolbar Popover +summary: A popover component that can be placed within a toolbar button. +tags: ['shared-ux', 'component'] +date: 2022-03-28 +--- + +This component is a thing wrapper around `EuiPopover` that handles open and close state. Its open and close state is controlled by a `ToolbarButton` button component. +This popover requires a label and children. \ No newline at end of file diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.stories.tsx b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.stories.tsx new file mode 100644 index 0000000000000..f429cebd7071b --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.stories.tsx @@ -0,0 +1,70 @@ +/* + * 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 { EuiContextMenu } from '@elastic/eui'; +import { ButtonContentIconSide } from '@elastic/eui/src/components/button/button_content'; +import { Story } from '@storybook/react'; +import React from 'react'; +import { ToolbarPopover } from './popover'; +import mdx from './popover.mdx'; + +export default { + title: 'Toolbar/Popover', + description: 'A popover that is a part of a toolbar.', + parameters: { + docs: { + page: mdx, + }, + }, + argTypes: { + iconSide: { + control: { + type: 'radio', + options: ['left', 'right', 'undefined'], + }, + }, + }, +}; + +export const Component: Story<{ + iconSide: ButtonContentIconSide | undefined; +}> = ({ iconSide }) => { + return ( + + {() => ( + + )} + + ); +}; + +Component.args = { + iconSide: 'left', +}; diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.test.tsx b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.test.tsx new file mode 100644 index 0000000000000..fcbf3d3542a16 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.test.tsx @@ -0,0 +1,30 @@ +/* + * 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 { mount as enzymeMount } from 'enzyme'; +import React from 'react'; +import { ToolbarPopover } from './popover'; + +describe('', () => { + test('is rendered', () => { + const isOpen = true; + const component = enzymeMount( !isOpen} />); + + expect(component).toMatchSnapshot(); + }); + + test('accepts an onClick handler', () => { + const isOpen = true; + const mockHandler = jest.fn(); + const component = enzymeMount( + !isOpen} onClick={mockHandler} /> + ); + component.simulate('click'); + expect(mockHandler).toHaveBeenCalled(); + }); +}); diff --git a/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx new file mode 100644 index 0000000000000..ceae588b61941 --- /dev/null +++ b/packages/kbn-shared-ux-components/src/toolbar/popovers/popover.tsx @@ -0,0 +1,41 @@ +/* + * 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, { useState } from 'react'; +import { EuiPopover } from '@elastic/eui'; +import { Props as EuiPopoverProps } from '@elastic/eui/src/components/popover/popover'; + +import { ToolbarButton, Props as ButtonProps } from '../buttons/primary/primary'; + +type AllowedButtonProps = Omit; +type AllowedPopoverProps = Omit< + EuiPopoverProps, + 'button' | 'isOpen' | 'closePopover' | 'anchorPosition' +>; + +export type Props = AllowedButtonProps & + AllowedPopoverProps & { + children: (arg: { closePopover: () => void }) => React.ReactNode; + }; + +export const ToolbarPopover = ({ label, iconType, children, iconSide, ...popover }: Props) => { + const [isOpen, setIsOpen] = useState(false); + + const onButtonClick = () => setIsOpen((status) => !status); + const closePopover = () => setIsOpen(false); + + const button = ; + + return ( + // the following ts-ignore is needed until typings/* directory is exposed for consumption to packages + // @ts-ignore Types of property css are incompatible Type 'InterpolationWithTheme' is not assignable to type 'Interpolation'. + + {children({ closePopover })} + + ); +};