From d0ef965e21b7b8092ef0de953ede0273c144749f Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:26:44 -0800 Subject: [PATCH] feat: vertical oriented button group (#755) (#1461) * feat: vertical oriented button group This commit added a new prop(orientation) to the current OuiButtonGroup, the possible values are `horizontal` and `vertical`. The default value is `horizontal` which match the current UI behavior. With `vertical` prop, the button group will be oriented vertically. related issue: #659 Signed-off-by: Yulong Ruan * doc(changelog): add changelog for vertical button group Signed-off-by: Yulong Ruan * fix(lint): fix sass lint Signed-off-by: Yulong Ruan --------- Signed-off-by: Yulong Ruan (cherry picked from commit 3c191a507d3280744bc432390b8fe4be7b5c59a9) Signed-off-by: github-actions[bot] # Conflicts: # CHANGELOG.md Co-authored-by: github-actions[bot] Co-authored-by: Viraj Sanghvi --- src-docs/src/views/button/button_example.js | 42 ++ .../src/views/button/button_group_vertical.js | 179 ++++++ .../__snapshots__/button_group.test.tsx.snap | 540 ++++++++++++++++++ .../button/button_group/_button_group.scss | 7 + .../button_group/_button_group_button.scss | 35 +- .../button/button_group/button_group.test.tsx | 38 ++ .../button/button_group/button_group.tsx | 6 + 7 files changed, 840 insertions(+), 7 deletions(-) create mode 100644 src-docs/src/views/button/button_group_vertical.js diff --git a/src-docs/src/views/button/button_example.js b/src-docs/src/views/button/button_example.js index b864a9b9e5..f74918b59f 100644 --- a/src-docs/src/views/button/button_example.js +++ b/src-docs/src/views/button/button_example.js @@ -139,6 +139,11 @@ const buttonGroupIconsHtml = renderToHtml(ButtonGroupIcons); import ButtonGroupCompressed from './button_group_compressed'; const buttonGroupCompressedSource = require('!!raw-loader!./button_group_compressed'); const buttonGroupCompressedHtml = renderToHtml(ButtonGroupCompressed); + +import ButtonGroupVertical from './button_group_vertical'; +const ButtonGroupVerticalSource = require('!!raw-loader!./button_group_vertical'); +const ButtonGroupVerticalHtml = renderToHtml(ButtonGroupVertical); + const buttonGroupSnippet = [ ``, ]; +const ButtonGroupVerticalSnippet = [ + ` {}} +/>`, +]; + export const ButtonExample = { title: 'Button', sections: [ @@ -469,6 +491,26 @@ export const ButtonExample = { demo: , snippet: buttonGroupIconsSnippet, }, + { + source: [ + { + type: GuideSectionTypes.JS, + code: ButtonGroupVerticalSource, + }, + { + type: GuideSectionTypes.HTML, + code: ButtonGroupVerticalHtml, + }, + ], + wrapText: false, + text: ( + +

Vertical orientated

+
+ ), + demo: , + snippet: ButtonGroupVerticalSnippet, + }, { source: [ { diff --git a/src-docs/src/views/button/button_group_vertical.js b/src-docs/src/views/button/button_group_vertical.js new file mode 100644 index 0000000000..8590fb970a --- /dev/null +++ b/src-docs/src/views/button/button_group_vertical.js @@ -0,0 +1,179 @@ +/* + * 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, { useState, Fragment } from 'react'; + +import { + OuiButtonGroup, + OuiSpacer, + OuiTitle, +} from '../../../../src/components'; + +import { htmlIdGenerator } from '../../../../src/services'; + +const idPrefix = htmlIdGenerator()(); +const idPrefix3 = htmlIdGenerator()(); + +export default () => { + const [toggleIdSelected, setToggleIdSelected] = useState(`${idPrefix}1`); + + const onChange = (optionId) => { + setToggleIdSelected(optionId); + }; + + const toggleButtons = [ + { + id: `${idPrefix}0`, + label: 'Option one', + }, + { + id: `${idPrefix}1`, + label: 'Option two', + }, + { + id: `${idPrefix}2`, + label: 'Option three', + }, + ]; + + const toggleButtonsIcons = [ + { + id: `${idPrefix3}0`, + label: 'Align left', + iconType: 'editorAlignLeft', + }, + { + id: `${idPrefix3}1`, + label: 'Align center', + iconType: 'editorAlignCenter', + }, + { + id: `${idPrefix3}2`, + label: 'Align right', + iconType: 'editorAlignRight', + isDisabled: true, + }, + ]; + + const toggleButtonsIconsMulti = [ + { + id: `${idPrefix3}3`, + label: 'Bold', + name: 'bold', + iconType: 'editorBold', + }, + { + id: `${idPrefix3}4`, + label: 'Italic', + name: 'italic', + iconType: 'editorItalic', + isDisabled: true, + }, + { + id: `${idPrefix3}5`, + label: 'Underline', + name: 'underline', + iconType: 'editorUnderline', + }, + { + id: `${idPrefix3}6`, + label: 'Strikethrough', + name: 'strikethrough', + iconType: 'editorStrike', + }, + ]; + + const [toggleIconIdSelected, setToggleIconIdSelected] = useState( + `${idPrefix3}1` + ); + const [toggleIconIdToSelectedMap, setToggleIconIdToSelectedMap] = useState( + {} + ); + + const onChangeIcons = (optionId) => { + setToggleIconIdSelected(optionId); + }; + + const onChangeIconsMulti = (optionId) => { + const newToggleIconIdToSelectedMap = { + ...toggleIconIdToSelectedMap, + ...{ + [optionId]: !toggleIconIdToSelectedMap[optionId], + }, + }; + + setToggleIconIdToSelectedMap(newToggleIconIdToSelectedMap); + }; + + return ( + + onChange(id)} + /> +    + onChangeIcons(id)} + isIconOnly + /> +    + onChangeIconsMulti(id)} + type="multi" + isIconOnly + /> + + +

Compressed

+
+ onChange(id)} + /> +    + onChangeIcons(id)} + isIconOnly + /> +    + onChangeIconsMulti(id)} + type="multi" + isIconOnly + /> +
+ ); +}; diff --git a/src/components/button/button_group/__snapshots__/button_group.test.tsx.snap b/src/components/button/button_group/__snapshots__/button_group.test.tsx.snap index 54c1542442..45f3628bff 100644 --- a/src/components/button/button_group/__snapshots__/button_group.test.tsx.snap +++ b/src/components/button/button_group/__snapshots__/button_group.test.tsx.snap @@ -2475,6 +2475,546 @@ exports[`OuiButtonGroup button props isIconOnly is rendered for single 1`] = ` `; +exports[`OuiButtonGroup button props orientation horizontal is rendered for multi 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`OuiButtonGroup button props orientation horizontal is rendered for single 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`OuiButtonGroup button props orientation horizontal is rendered with compressed 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`OuiButtonGroup button props orientation vertical is rendered for multi 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`OuiButtonGroup button props orientation vertical is rendered for single 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`OuiButtonGroup button props orientation vertical is rendered with compressed 1`] = ` +
+ + test + +
+ + + +
+
+`; + exports[`OuiButtonGroup button props selection idSelected is rendered for single 1`] = `
= [ 'compressed', ]; +const ORIENTATIONS: Array = [ + 'horizontal', + 'vertical', +]; + const options: OuiButtonGroupOptionProps[] = [ { id: 'button00', @@ -102,6 +107,39 @@ describe('OuiButtonGroup', () => { }); describe('button props', () => { + describe('orientation', () => { + ORIENTATIONS.forEach((orientation) => { + test(`${orientation} is rendered for single`, () => { + const component = render( + + ); + + expect(component).toMatchSnapshot(); + }); + test(`${orientation} is rendered for multi`, () => { + const component = render( + + ); + + expect(component).toMatchSnapshot(); + }); + test(`${orientation} is rendered with compressed`, () => { + const component = render( + + ); + + expect(component).toMatchSnapshot(); + }); + }); + }); + describe('buttonSize', () => { SIZES.forEach((size) => { test(`${size} is rendered for single`, () => { diff --git a/src/components/button/button_group/button_group.tsx b/src/components/button/button_group/button_group.tsx index c0e5a00f3c..10ae34c6f6 100644 --- a/src/components/button/button_group/button_group.tsx +++ b/src/components/button/button_group/button_group.tsx @@ -90,6 +90,10 @@ export type OuiButtonGroupProps = CommonProps & { * With `'multi'` multiple options selected (similar to checkbox group). */ type?: 'single' | 'multi'; + /** + * Determines if the buttons should be horizontal oriented or vertical oriented + */ + orientation?: 'horizontal' | 'vertical'; /** * An array of #OuiButtonGroupOptionProps */ @@ -155,6 +159,7 @@ export const OuiButtonGroup: FunctionComponent = ({ onChange, options = [], type = 'single', + orientation = 'horizontal', ...rest }) => { // Compressed style can't support `ghost` color because it's more like a form field than a button @@ -173,6 +178,7 @@ export const OuiButtonGroup: FunctionComponent = ({ { 'ouiButtonGroup--fullWidth': isFullWidth, 'ouiButtonGroup--isDisabled': isDisabled, + 'ouiButtonGroup--vertical': orientation === 'vertical', }, className );