diff --git a/CHANGELOG.md b/CHANGELOG.md index 5459ad6110..5e6762da2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Fix `autofill` text color in dark themes ([#871](/~https://github.com/opensearch-project/oui/pull/871)) - [Header] Update background color in next theme ([#936](/~https://github.com/opensearch-project/oui/pull/936)) - Set link to use semi bold font weight ([#961](/~https://github.com/opensearch-project/oui/pull/961)) +- Adds `SchemaItem` as an experimental component ([#974](/~https://github.com/opensearch-project/oui/pull/974)) - Make `CollapsibleNavGroup` background colors theme-able ([#968](/~https://github.com/opensearch-project/oui/pull/968)) ### 🐛 Bug Fixes diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index 6740a76f63..1ed4231629 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -187,6 +187,8 @@ import { ResizableContainerExample } from './views/resizable_container/resizable import { ResponsiveExample } from './views/responsive/responsive_example'; +import { SchemaExample } from './views/schema/schema_example'; + import { SearchBarExample } from './views/search_bar/search_bar_example'; import { SelectableExample } from './views/selectable/selectable_example'; @@ -399,6 +401,7 @@ const navigation = [ LoadingExample, NotificationEventExample, ProgressExample, + SchemaExample, StatExample, TextExample, TitleExample, diff --git a/src-docs/src/views/schema/schema.tsx b/src-docs/src/views/schema/schema.tsx new file mode 100644 index 0000000000..de9bf10d3f --- /dev/null +++ b/src-docs/src/views/schema/schema.tsx @@ -0,0 +1,142 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React from 'react'; + +import { OuiSchemaItem } from '../../../../src/components/schema'; +import { OuiSpacer } from '../../../../src/components/spacer'; + +export default () => ( + <> + + + {}, + color: 'danger', + }, + { + iconType: 'pencil', + 'aria-label': 'Edit', + onClick: () => {}, + tooltip: { + content: 'Edit', + position: 'top', + delay: 'long', + }, + }, + ]} + /> + {}, + color: 'danger', + }, + { + iconType: 'pencil', + 'aria-label': 'Edit', + onClick: () => {}, + tooltip: { + content: 'Edit', + position: 'top', + delay: 'long', + }, + }, + ]} + compressed + /> + + {}, + color: 'danger', + }, + { + iconType: 'pencil', + 'aria-label': 'Edit', + onClick: () => {}, + tooltip: { + content: 'Edit', + position: 'top', + delay: 'long', + }, + }, + ]} + withPanel + /> + + {}, + color: 'danger', + }, + { + iconType: 'pencil', + 'aria-label': 'Edit', + onClick: () => {}, + tooltip: { + content: 'Edit', + position: 'top', + delay: 'long', + }, + }, + ]} + withPanel + compressed + /> + +
+ {}, + color: 'danger', + }, + { + iconType: 'pencil', + 'aria-label': 'Edit', + onClick: () => {}, + tooltip: { + content: 'Edit', + position: 'top', + delay: 'long', + }, + }, + ]} + withPanel + /> +
+ +); diff --git a/src-docs/src/views/schema/schema_example.js b/src-docs/src/views/schema/schema_example.js new file mode 100644 index 0000000000..28e6862678 --- /dev/null +++ b/src-docs/src/views/schema/schema_example.js @@ -0,0 +1,73 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React from 'react'; + +import { renderToHtml } from '../../services'; + +import { GuideSectionTypes } from '../../components'; + +import { OuiSchemaItem } from '../../../../src/components'; + +import Schema from './schema'; +const schemaSource = require('!!raw-loader!./schema'); +const schemaHtml = renderToHtml(Schema); + +import SchemaGroup from './schema_group'; +const schemaGroupSource = require('!!raw-loader!./schema'); +const schemaGroupHtml = renderToHtml(Schema); + +export const SchemaExample = { + title: 'Schema', + isExperimental: true, + sections: [ + { + source: [ + { + type: GuideSectionTypes.JS, + code: schemaSource, + }, + { + type: GuideSectionTypes.HTML, + code: schemaHtml.render(), + }, + ], + text: ( +

+ This is the basic OuiSchemaItem component. +

+ ), + props: { OuiSchemaItem }, + demo: , + }, + { + title: 'SchemaItem as a list', + source: [ + { + type: GuideSectionTypes.JS, + code: schemaGroupSource, + }, + { + type: GuideSectionTypes.HTML, + code: schemaGroupHtml, + }, + ], + text: ( +

+ Grouping OuiSchemaItem with{' '} + OuiFlexGroup to make a list. +

+ ), + props: { OuiSchemaItem }, + demo: , + }, + ], +}; diff --git a/src-docs/src/views/schema/schema_group.tsx b/src-docs/src/views/schema/schema_group.tsx new file mode 100644 index 0000000000..b53b9497bb --- /dev/null +++ b/src-docs/src/views/schema/schema_group.tsx @@ -0,0 +1,81 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React from 'react'; + +import { OuiSchemaItem } from '../../../../src/components/schema'; +import { OuiFlexGroup, OuiFlexItem } from '../../../../src/components/flex'; +import { OuiSpacer } from '../../../../src/components/spacer'; + +export default () => { + const actions = [ + { + iconType: 'trash', + 'aria-label': 'Delete', + onClick: () => {}, + color: 'danger' as const, + }, + { + iconType: 'pencil', + 'aria-label': 'Edit', + onClick: () => {}, + tooltip: { + content: 'Edit', + position: 'top' as const, + delay: 'long' as const, + }, + }, + ]; + + const items = Array.from({ length: 5 }, (_, i) => ({ + key: `item${i}`, + iconType: 'tokenShape', + label: `Item no ${i + 1}`, + actions, + })); + + items.push({ + key: 'last', + iconType: 'tokenShape', + label: 'Item with a really long label that will wrap', + actions, + }); + + return ( + <> +

As a list

+ + + {items.map((item) => ( + + + + ))} + + +

With panels

+ + + {items.map((item) => ( + + + + ))} + + + ); +}; diff --git a/src/components/index.js b/src/components/index.js index ff50592530..c1112b5701 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -297,6 +297,8 @@ export { useResizeObserver, } from './observer/resize_observer'; +export { OuiSchemaItem } from './schema'; + export { OuiSearchBar, Query, Ast } from './search_bar'; export { diff --git a/src/components/index.scss b/src/components/index.scss index 2b3ad5cd34..f008f44fdd 100644 --- a/src/components/index.scss +++ b/src/components/index.scss @@ -65,6 +65,7 @@ @import 'progress/index'; @import 'tree_view/index'; @import 'resizable_container/index'; +@import 'schema/index'; @import 'side_nav/index'; @import 'spacer/index'; @import 'search_bar/index'; diff --git a/src/components/schema/__snapshots__/schema_item.test.tsx.snap b/src/components/schema/__snapshots__/schema_item.test.tsx.snap new file mode 100644 index 0000000000..624ce787f1 --- /dev/null +++ b/src/components/schema/__snapshots__/schema_item.test.tsx.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OuiSchema is rendered 1`] = ` +
+
+ Test +
+
+
+`; + +exports[`OuiSchema it renders compressed correctly 1`] = ` +
+
+ Test +
+
+
+`; diff --git a/src/components/schema/_index.scss b/src/components/schema/_index.scss new file mode 100644 index 0000000000..59390ef9b2 --- /dev/null +++ b/src/components/schema/_index.scss @@ -0,0 +1,12 @@ +/*! + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +@import 'schema_item'; diff --git a/src/components/schema/_schema_item.scss b/src/components/schema/_schema_item.scss new file mode 100644 index 0000000000..17fb07e134 --- /dev/null +++ b/src/components/schema/_schema_item.scss @@ -0,0 +1,79 @@ +/*! + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +.ouiSchemaItem { + display: flex; + // sass-lint:disable-block no-misspelled-properties + gap: $ouiSizeS; + align-items: center; + padding: $ouiSizeS; + border-radius: $ouiBorderRadius; + + &:hover { + @include ouiFocusBackground($ouiTextColor); + + .ouiSchemaItem__actions .ouiButtonIcon { + opacity: 1; + visibility: visible; + } + } +} + +.ouiSchemaItem__icon { + align-self: flex-start; + margin: calc($ouiSize / 2) 0; // To align with buttonIcon +} + +.ouiSchemaItem__label { + flex: 1; + margin: calc($ouiSize / 2) 0; // To align with buttonIcon +} + +.ouiSchemaItem__actions { + // sass-lint:disable-block no-misspelled-properties + gap: $ouiSizeXS; + display: flex; + + .ouiButtonIcon { + opacity: 0; + transition: opacity $ouiAnimSpeedNormal $ouiAnimSlightResistance; + + &:focus { + opacity: 1; + } + } +} + +.ouiSchemaItem--withPanel { + @include ouiBottomShadowSmall; + border: $ouiBorderThin; + + &:hover { + background-color: tintOrShade($ouiColorLightestShade, 50%, 20%); + } +} + +.ouiSchemaItem--compressed { + padding: $ouiSizeXS; + // sass-lint:disable-block no-misspelled-properties + gap: $ouiSizeXS; + font-size: $ouiFontSizeXS; + + .ouiSchemaItem__icon { + margin: calc($ouiSizeM / 2) 0; + margin-left: $ouiSizeXS; + } + + .ouiSchemaItem__label { + flex: 1; + margin: calc($ouiSizeM / 2) 0; + } +} diff --git a/src/components/schema/index.ts b/src/components/schema/index.ts new file mode 100644 index 0000000000..9b4fe1bf84 --- /dev/null +++ b/src/components/schema/index.ts @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +export { OuiSchemaItem } from './schema_item'; diff --git a/src/components/schema/schema_item.test.tsx b/src/components/schema/schema_item.test.tsx new file mode 100644 index 0000000000..9ba3944d6a --- /dev/null +++ b/src/components/schema/schema_item.test.tsx @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../test/required_props'; + +import { OuiSchemaItem } from './schema_item'; + +describe('OuiSchema', () => { + test('is rendered', () => { + const component = render(); + + expect(component).toMatchSnapshot(); + }); + + test('it only renders at most 2 actions', () => { + const actions = Array(3) + .fill(null) + .map(() => ({ + iconType: 'trash', + onClick: () => {}, + })); + const component = render( + + ); + expect(component.find('.ouiSchemaItem__actions').children().length).toBe(2); + }); + + test('it renders compressed correctly', () => { + const component = render( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/components/schema/schema_item.tsx b/src/components/schema/schema_item.tsx new file mode 100644 index 0000000000..8e89857681 --- /dev/null +++ b/src/components/schema/schema_item.tsx @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React, { FunctionComponent } from 'react'; +import { CommonProps } from '../common'; +import classNames from 'classnames'; +import { OuiButtonIcon, OuiButtonIconPropsForButton } from '../button'; +import { OuiToolTip, OuiToolTipProps } from '../tool_tip'; +import { IconType } from '../icon'; +import { OuiToken } from '../token'; + +export interface Action extends OuiButtonIconPropsForButton { + tooltip?: Omit; +} + +/** + * Props for the OuiSchemaItem component. + * @public + */ +export type OuiSchemaItemProps = CommonProps & { + /** The icon type to display. */ + iconType?: IconType; + /** The label to display. */ + label: string; + /** An array of actions to display. Limit 2 */ + actions?: Action[]; + /** Whether the item is compressed. */ + compressed?: boolean; + /** Whether the item is displayed with a panel. */ + withPanel?: boolean; +}; + +/** + * A component that displays a schema item. + * @public + */ +export const OuiSchemaItem: FunctionComponent = ({ + className, + iconType, + label, + actions = [], + compressed, + withPanel, + ...rest +}) => { + const classes = classNames( + 'ouiSchemaItem', + { + 'ouiSchemaItem--compressed': compressed, + 'ouiSchemaItem--withPanel': withPanel, + }, + className + ); + + const filteredActions = actions.slice(0, 2); + + return ( +
+ {iconType && ( + + )} +
{label}
+
+ {filteredActions.map(({ tooltip, ...action }, index) => + tooltip ? ( + + + + ) : ( + + ) + )} +
+
+ ); +};