From 34e804f9bec6fa64a302b69d20b3c77b402303c3 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 20 Aug 2020 11:09:07 -0400 Subject: [PATCH 01/34] Moved changes over from old PR --- .../button/button_group/_button_group.scss | 207 +++++++++++------- .../button/button_group/_index.scss | 2 + .../button/button_group/button_group.test.tsx | 110 +++++----- .../button/button_group/button_group.tsx | 149 +++++-------- src/components/button/button_group/index.ts | 7 +- 5 files changed, 242 insertions(+), 233 deletions(-) diff --git a/src/components/button/button_group/_button_group.scss b/src/components/button/button_group/_button_group.scss index 6251cf8fec0..737c09d3d2a 100644 --- a/src/components/button/button_group/_button_group.scss +++ b/src/components/button/button_group/_button_group.scss @@ -1,77 +1,140 @@ .euiButtonGroup { max-width: 100%; display: flex; -} + border-radius: $euiBorderRadius; -.euiButtonGroup__fieldset { - display: inline-block; - max-width: 100%; + .euiButton { + border-radius: 0; + margin-left: -1px; - &--fullWidth { - display: block; + &:not(.euiButton--fill) { + border-color: $euiButtonToggleBorderColor; + } + + &, + &:hover, + &:focus, + &:active { + transition: none; + transform: none; + animation: none; + box-shadow: none; + } + + &.euiButton--disabled:hover { + text-decoration: none; + cursor: not-allowed; + } } -} -.euiButtonGroup--fullWidth { - .euiButtonGroup__toggle { - flex: 1; + .euiButton--fill { + z-index: 0; + } + + .euiButtonGroup__button--iconOnly, + .euiButtonGroup__toggle--iconOnly { + min-width: auto; + } + + .euiButton--fill + .euiButton { + border-left-color: transparent; + } + + &:not(.euiButtonGroup--compressed) { + .euiButton { + &[aria-pressed='true'] + [aria-pressed='true'] { + box-shadow: -1px 0 0 transparentize($euiColorEmptyShade, .9); + } + + &:first-child { + border-top-left-radius: $euiBorderRadius; + border-bottom-left-radius: $euiBorderRadius; + } + + &:last-child { + border-top-right-radius: $euiBorderRadius; + border-bottom-right-radius: $euiBorderRadius; + } + } } } -.euiButtonGroup__toggle { - margin-left: -1px; - z-index: 1; +@each $name, $color in $euiButtonTypes { + .euiButtonGroup--#{$name}:not(.euiButtonGroup--compressed) { + @if ($name == 'ghost') { + // Ghost is unique and ALWAYS sits against a dark background. + color: $color; + } @else if ($name == 'text') { + // The default color is lighter than the normal text color, make the it the text color + color: $euiTextColor; + } @else { + // Other colors need to check their contrast against the page background color. + color: makeHighContrastColor($color, $euiPageBackgroundColor); + } - // DO NOT Transform - // sass-lint:disable-block no-important - transition: none !important; - transform: none !important; - animation: none !important; + $shadowColor: $euiShadowColor; + @if ($name == 'ghost') { + $shadowColor: $euiColorInk; + } @else if (lightness($euiTextColor) < 50) { + // Only if this is the light theme do we use the button variant color to colorize the shadow + $shadowColor: desaturate($color, 60%); + } - &[class*='checked'] { - z-index: 2; // Raise it above the simply bordered versions for crisper lines + @include euiSlightShadow($shadowColor); - // add a slight divider if two selected items are next to each other - + [class*='checked'] { - box-shadow: -1px 0 0 transparentize($euiColorEmptyShade, .9); + .euiButtonGroup__input:focus + .euiButtonGroup__label { + background-color: darken($color, 5%); + border-color: darken($color, 5%); + text-decoration: underline; } } +} - .euiButtonGroup__button { - border-radius: 0; - width: 100%; +.euiButtonGroup__button { + text-overflow: ellipsis; + overflow: hidden; +} - // DO NOT Transform - // sass-lint:disable-block no-important - transition: none !important; - transform: none !important; - animation: none !important; +.euiButtonGroup__fieldset { + display: inline-block; + max-width: 100%; - // always the same border color unless it's selected - &:not([class*='fill']) { - border-color: $euiButtonToggleBorderColor; - } + &--fullWidth { + display: block; + } +} - // don't colorize the shadows - &:enabled { - @include euiSlightShadow; - } +.euiButtonGroup__input { + @include euiScreenReaderOnly; +} + +.euiButtonGroup--compressed, +.euiButtonGroup--fullWidth { + .euiButtonGroup__toggle { + flex: 1; } +} - &:first-child { - margin-left: 0; +.euiButtonGroup__label { + white-space: nowrap; + cursor: inherit; - .euiButtonGroup__button { - border-top-left-radius: $euiBorderRadius; - border-bottom-left-radius: $euiBorderRadius; - } + .euiButton--disabled & { + pointer-events: none; } +} - &:last-child .euiButtonGroup__button { - border-top-right-radius: $euiBorderRadius; - border-bottom-right-radius: $euiBorderRadius; +.euiButtonGroup__button--iconOnly { + .euiButton__content { + flex-direction: column; + padding: 0 $euiSizeS; + justify-content: center; } + .euiIcon { + color: inherit; + margin: 0; + } } @include euiBreakpoint('xs', 's') { @@ -79,13 +142,9 @@ display: block; } - .euiButtonGroup__toggle { + .euiButtonGroup__button { flex: 1; min-width: 0; - - .euiButtonGroup__button { - min-width: 0; - } } } @@ -94,45 +153,41 @@ border-radius: $euiFormControlCompressedBorderRadius; background-color: $euiFormBackgroundColor; + .euiButtonGroup__button:focus, + .euiButtonGroup__input:focus + .euiButtonGroup__label { + outline: 2px solid $euiFocusRingColor; + } + .euiButtonGroup__button { - height: $euiFormControlCompressedHeight - 2px; - // sass-lint:disable-block no-important - box-shadow: none !important; - font-size: $euiFontSizeS; - min-width: 0; border: none; + background: none; border-radius: $euiBorderRadius; + font-size: $euiFontSizeS; + height: $euiFormControlCompressedHeight - 2px; + box-shadow: none; + min-width: 0; // Offset the background color from the border by 2px // by clipping background to before the padding starts padding: 2px; background-clip: content-box; - &:not(.euiButtonGroup__button--selected):not(:disabled) { + &:not(.euiButtonGroup__button--selected):not(.euiButton--disabled) { color: $euiColorDarkShade; } - .euiButton__content { - padding-left: $euiSizeS; - padding-right: $euiSizeS; + &:hover, + &:focus { + box-shadow: none; } } - .euiButtonGroup__toggle { - flex: 1; - min-width: 0; - } - - .euiButtonToggle__input:enabled:hover + .euiButtonGroup__button, - .euiButtonToggle__input:enabled:focus + .euiButtonGroup__button { - background-color: transparentize($euiFormInputGroupLabelBackground, .5); - } - - .euiButtonToggle__input:enabled:focus + .euiButtonGroup__button { - outline: 2px solid $euiFocusRingColor; - } - .euiButtonGroup__button--selected { font-weight: $euiFontWeightSemiBold; background-color: $euiFormInputGroupLabelBackground; } + + .euiButton__content { + padding-left: $euiSizeS; + padding-right: $euiSizeS; + } } diff --git a/src/components/button/button_group/_index.scss b/src/components/button/button_group/_index.scss index 78cfad351a8..0b8adecfa38 100644 --- a/src/components/button/button_group/_index.scss +++ b/src/components/button/button_group/_index.scss @@ -1 +1,3 @@ @import 'button_group'; +@import 'button_group_single_button'; +@import 'button_group_multi_button'; diff --git a/src/components/button/button_group/button_group.test.tsx b/src/components/button/button_group/button_group.test.tsx index 46ffcba1a43..00ef893035f 100644 --- a/src/components/button/button_group/button_group.test.tsx +++ b/src/components/button/button_group/button_group.test.tsx @@ -19,12 +19,24 @@ import React from 'react'; import { render } from 'enzyme'; -import { requiredProps } from '../../../test'; +import { requiredProps as commonProps } from '../../../test'; -import { EuiButtonGroup, GroupButtonSize } from './button_group'; +import { EuiButtonGroup } from './button_group'; +import { EuiButtonGroupProps } from './types'; import { COLORS } from '../button'; -const SIZES: GroupButtonSize[] = ['s', 'm', 'compressed']; +const SIZES: Array = [ + 's', + 'm', + 'compressed', +]; + +const requiredProps = { + legend: 'test', + onChange: () => {}, + options: [], + ...commonProps, +}; const options = [ { @@ -44,7 +56,7 @@ const options = [ describe('EuiButtonGroup', () => { test('is rendered', () => { const component = render( - {}} {...requiredProps} /> + ); expect(component).toMatchSnapshot(); @@ -54,23 +66,12 @@ describe('EuiButtonGroup', () => { describe('options', () => { it('are rendered', () => { const component = render( - {}} options={options} /> - ); - - expect(component).toMatchSnapshot(); - }); - - it('can pass down data-test-subj', () => { - const options2 = [ - { - id: 'button00', - label: 'Option one', - 'data-test-subj': 'test', - }, - ]; - - const component = render( - {}} options={options2} /> + ); expect(component).toMatchSnapshot(); @@ -82,7 +83,9 @@ describe('EuiButtonGroup', () => { test(`${size} is rendered`, () => { const component = render( {}} + type="single" + idSelected="" + {...requiredProps} buttonSize={size} options={options} /> @@ -96,7 +99,13 @@ describe('EuiButtonGroup', () => { describe('isDisabled', () => { it('is rendered', () => { const component = render( - {}} isDisabled options={options} /> + ); expect(component).toMatchSnapshot(); @@ -106,7 +115,13 @@ describe('EuiButtonGroup', () => { describe('isFullWidth', () => { it('is rendered', () => { const component = render( - {}} isFullWidth options={options} /> + ); expect(component).toMatchSnapshot(); @@ -116,7 +131,13 @@ describe('EuiButtonGroup', () => { describe('isIconOnly', () => { it('is rendered', () => { const component = render( - {}} isIconOnly options={options} /> + ); expect(component).toMatchSnapshot(); @@ -128,7 +149,9 @@ describe('EuiButtonGroup', () => { test(`${color} is rendered`, () => { const component = render( {}} + type="single" + idSelected="" + {...requiredProps} color={color} options={options} /> @@ -139,36 +162,13 @@ describe('EuiButtonGroup', () => { }); }); - describe('legend', () => { - it('is rendered', () => { - const component = render( - {}} - legend="legend" - options={options} - /> - ); - - expect(component).toMatchSnapshot(); - }); - }); - - describe('name', () => { - it('is rendered', () => { - const component = render( - {}} name="name" options={options} /> - ); - - expect(component).toMatchSnapshot(); - }); - }); - describe('idSelected', () => { it('is rendered', () => { const component = render( {}} - idSelected="button00" + type="single" + idSelected={options[0].id} + {...requiredProps} options={options} /> ); @@ -180,7 +180,7 @@ describe('EuiButtonGroup', () => { describe('type of multi', () => { it('is rendered', () => { const component = render( - {}} type="multi" options={options} /> + ); expect(component).toMatchSnapshot(); @@ -189,9 +189,9 @@ describe('EuiButtonGroup', () => { it('idToSelectedMap is rendered', () => { const component = render( {}} + {...requiredProps} type="multi" - idToSelectedMap={{ button00: true, button01: true }} + idToSelectedMap={{ [options[0].id]: true, [options[1].id]: true }} options={options} /> ); diff --git a/src/components/button/button_group/button_group.tsx b/src/components/button/button_group/button_group.tsx index 63750ccf3ce..228be85ebe4 100644 --- a/src/components/button/button_group/button_group.tsx +++ b/src/components/button/button_group/button_group.tsx @@ -17,53 +17,12 @@ * under the License. */ -import React, { ReactNode, FunctionComponent, HTMLAttributes } from 'react'; import classNames from 'classnames'; - +import React, { FunctionComponent, HTMLAttributes } from 'react'; import { EuiScreenReaderOnly } from '../../accessibility'; -import { ToggleType } from '../../toggle'; - -import { EuiButtonToggle } from '../button_toggle'; -import { CommonProps } from '../../common'; - -import { ButtonColor } from '../button'; -import { ButtonContentIconSide } from '../button_content'; -import { IconType } from '../../icon'; - -export interface EuiButtonGroupIdToSelectedMap { - [id: string]: boolean; -} - -export type GroupButtonSize = 's' | 'm' | 'compressed'; - -export interface EuiButtonGroupOption extends CommonProps { - id: string; - label: ReactNode; - name?: string; - isDisabled?: boolean; - value?: any; - iconSide?: ButtonContentIconSide; - iconType?: IconType; -} - -export interface EuiButtonGroupProps extends CommonProps { - options?: EuiButtonGroupOption[]; - onChange: (id: string, value?: any) => void; - /** - * Typical sizing is `s`. Medium `m` size should be reserved for major features. - * `compressed` is meant to be used alongside and within compressed forms. - */ - buttonSize?: GroupButtonSize; - isDisabled?: boolean; - isFullWidth?: boolean; - isIconOnly?: boolean; - idSelected?: string; - legend?: string; - color?: ButtonColor; - name?: string; - type?: ToggleType; - idToSelectedMap?: EuiButtonGroupIdToSelectedMap; -} +import { EuiButtonGroupMultiButton } from './button_group_multi_button'; +import { EuiButtonGroupSingleButton } from './button_group_single_button'; +import { EuiButtonGroupProps } from './types'; type Props = Omit, 'onChange'> & EuiButtonGroupProps; @@ -72,24 +31,33 @@ export const EuiButtonGroup: FunctionComponent = ({ className, buttonSize = 's', color = 'text', - idSelected, + idSelected = '', idToSelectedMap = {}, - isDisabled, - isFullWidth, - isIconOnly, - name, + isDisabled = false, + isFullWidth = false, + isIconOnly = false, legend, onChange, options = [], type = 'single', - 'data-test-subj': dataTestSubj, ...rest }) => { + // Compressed style can't support `ghost` color because it's more like a form field than a button + const badColorCombo = buttonSize === 'compressed' && color === 'ghost'; + const resolvedColor = badColorCombo ? 'text' : color; + if (badColorCombo) { + console.warn( + 'EuiButtonGroup of compressed size does not support the ghost color. It will render as text instead.' + ); + } + const classes = classNames( 'euiButtonGroup', - [`euiButtonGroup--${buttonSize}`], + `euiButtonGroup--${resolvedColor}`, { 'euiButtonGroup--fullWidth': isFullWidth, + 'euiButtonGroup--compressed': buttonSize === 'compressed', + 'euiButtonGroup--disabled': isDisabled, }, className ); @@ -98,67 +66,50 @@ export const EuiButtonGroup: FunctionComponent = ({ 'euiButtonGroup__fieldset--fullWidth': isFullWidth, }); - let legendNode; - if (legend) { - legendNode = ( + return ( +
{legend} - ); - } - - return ( -
- {legendNode}
{options.map((option, index) => { - const { - id, - name: optionName, - value, - isDisabled: optionDisabled, - className, - ...rest - } = option; - - let isSelectedState; - if (type === 'multi') { - isSelectedState = idToSelectedMap[id] || false; - } else { - isSelectedState = id === idSelected; - } - - let fill; - if (buttonSize !== 'compressed') { - fill = isSelectedState; - } - const buttonClasses = classNames( + const optionClasses = classNames( 'euiButtonGroup__button', + 'euiButton--no-hover', { - 'euiButtonGroup__button--selected': isSelectedState, + 'euiButtonGroup__button--iconOnly': isIconOnly, }, - className + option.className ); + if (type === 'single') { + return ( + + ); + } + return ( - onChange(id, value)} - size={buttonSize === 'compressed' ? 's' : buttonSize} - type={type} - data-test-subj={dataTestSubj} - {...rest} + isGroupDisabled={isDisabled} + onChange={onChange} + className={optionClasses} /> ); })} diff --git a/src/components/button/button_group/index.ts b/src/components/button/button_group/index.ts index 5e8f193e409..2e0c747810b 100644 --- a/src/components/button/button_group/index.ts +++ b/src/components/button/button_group/index.ts @@ -17,8 +17,9 @@ * under the License. */ +export { EuiButtonGroup } from './button_group'; export { - EuiButtonGroup, - EuiButtonGroupOption, + EuiButtonSingleGroupOptionProps, + EuiButtonMultiGroupOptionProps, EuiButtonGroupProps, -} from './button_group'; +} from './types'; From e4bf21e1e0dd85ed1b91a461887adf6b77d8ba44 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 20 Aug 2020 11:10:28 -0400 Subject: [PATCH 02/34] Moved changes over from old PR --- .../_button_group_multi_button.scss | 0 .../_button_group_single_button.scss | 0 .../button_group_multi_button.tsx | 81 +++++++++++ .../button_group_single_button.tsx | 100 +++++++++++++ src/components/button/button_group/types.tsx | 136 ++++++++++++++++++ 5 files changed, 317 insertions(+) create mode 100644 src/components/button/button_group/_button_group_multi_button.scss create mode 100644 src/components/button/button_group/_button_group_single_button.scss create mode 100644 src/components/button/button_group/button_group_multi_button.tsx create mode 100644 src/components/button/button_group/button_group_single_button.tsx create mode 100644 src/components/button/button_group/types.tsx diff --git a/src/components/button/button_group/_button_group_multi_button.scss b/src/components/button/button_group/_button_group_multi_button.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/components/button/button_group/_button_group_single_button.scss b/src/components/button/button_group/_button_group_single_button.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/components/button/button_group/button_group_multi_button.tsx b/src/components/button/button_group/button_group_multi_button.tsx new file mode 100644 index 00000000000..88fc78fe385 --- /dev/null +++ b/src/components/button/button_group/button_group_multi_button.tsx @@ -0,0 +1,81 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import classNames from 'classnames'; +import React, { FunctionComponent } from 'react'; +import { EuiScreenReaderOnly } from '../../accessibility'; +import { EuiButton } from '../button'; +import { + ButtonGroupOptionProps, + EuiButtonMultiGroupOptionProps, +} from './types'; + +type Props = { + idToSelectedMap: { [id: string]: boolean }; +} & ButtonGroupOptionProps & + EuiButtonMultiGroupOptionProps; + +export const EuiButtonGroupMultiButton: FunctionComponent = ({ + id, + label, + isDisabled, + className, + iconType, + iconSide, + idToSelectedMap, + size, + color, + isIconOnly, + isGroupDisabled, + onChange, + ...rest +}) => { + const isSelectedState = idToSelectedMap[id] || false; + const badColorCombo = size === 'compressed' && color === 'ghost'; + const buttonClasses = classNames( + 'euiButtonGroup__toggle', + { + 'euiButtonGroup__button--selected': isSelectedState, + }, + className + ); + + return ( + onChange(id)} + iconSide={iconSide} + iconType={iconType} + {...rest}> + {isIconOnly ? ( + + {label} + + ) : ( + <>{label} + )} + + ); +}; diff --git a/src/components/button/button_group/button_group_single_button.tsx b/src/components/button/button_group/button_group_single_button.tsx new file mode 100644 index 00000000000..98a0cb8c6b9 --- /dev/null +++ b/src/components/button/button_group/button_group_single_button.tsx @@ -0,0 +1,100 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import classNames from 'classnames'; +import React, { FunctionComponent } from 'react'; +import { EuiScreenReaderOnly } from '../../accessibility'; +import { EuiIcon } from '../../icon'; +import { + ButtonGroupOptionProps, + EuiButtonSingleGroupOptionProps, +} from './types'; + +type Props = { + idSelected: string; +} & ButtonGroupOptionProps & + EuiButtonSingleGroupOptionProps; + +export const EuiButtonGroupSingleButton: FunctionComponent = ({ + id, + value, + label, + isDisabled, + className, + iconType, + iconSide = 'left', + idSelected, + isGroupDisabled, + isIconOnly, + size, + name, + color, + onChange, + ...rest +}) => { + const isSelectedState = id === idSelected; + const fill = size !== 'compressed' && isSelectedState; + const wrapperClasses = classNames( + 'euiButton', + 'euiButton--no-hover', + 'euiButtonGroup__toggle', + { + 'euiButtonGroup__button--selected': isSelectedState, + 'euiButton--disabled': isGroupDisabled || isDisabled, + 'euiButtonGroup__toggle--iconOnly': isIconOnly, + 'euiButton--fill': fill, + 'euiButton--small': size === 'compressed' || size === 's', + 'euiButton--ghost': size !== 'compressed' && color === 'ghost', + }, + className + ); + const icon = iconType && ; + + return ( +
+ onChange(id, value)} + checked={isSelectedState} + disabled={isGroupDisabled || isDisabled} + value={value} + type="radio" + {...rest} + /> + +
+ ); +}; diff --git a/src/components/button/button_group/types.tsx b/src/components/button/button_group/types.tsx new file mode 100644 index 00000000000..9f2a97c6e62 --- /dev/null +++ b/src/components/button/button_group/types.tsx @@ -0,0 +1,136 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ReactNode } from 'react'; +import { CommonProps } from '../../common'; +import { IconType } from '../../icon'; +import { ButtonColor } from '../button'; +import { ButtonContentIconSide } from '../button_content'; + +export type ButtonGroupOptionProps = { + color: EuiButtonGroupProps['color']; + size: EuiButtonGroupProps['buttonSize']; + isIconOnly: EuiButtonGroupProps['isIconOnly']; + isGroupDisabled: EuiButtonGroupProps['isDisabled']; + onChange: EuiButtonGroupProps['onChange']; +}; + +export type EuiButtonMultiGroupOptionProps = EuiButtonGroupOptionProps; +export type EuiButtonSingleGroupOptionProps = EuiButtonGroupOptionProps & { + /** + * The `name` attribute for radio inputs + */ + name?: string; + /** + * The value of the radio input. + */ + value?: any; +}; + +type EuiButtonGroupOptionProps = CommonProps & { + /** + * Each option must have a unique `id` for maintaining selection + */ + id: string; + /** + * Each option must have a `label` even for icons which will be applied as the `aria-label` + */ + label: ReactNode; + isDisabled?: boolean; + /** + * Any `type` accepted by EuiIcon + */ + iconType?: IconType; + iconSide?: ButtonContentIconSide; +}; + +export type EuiButtonGroupProps = CommonProps & { + /** + * Typical sizing is `s`. Medium `m` size should be reserved for major features. + * `compressed` is meant to be used alongside and within compressed forms. + */ + buttonSize?: 's' | 'm' | 'compressed'; + isDisabled?: boolean; + /** + * Expands the whole group to the full width of the container. + * Each button gets equal widths no matter the content + */ + isFullWidth?: boolean; + /** + * Hides the label to only show the `iconType` provided by the `option` + */ + isIconOnly?: boolean; + + /** + * A hidden group title (required for accessibility) + */ + legend: string; + /** + * Compressed styles don't support `ghost` color (Color will be changed to "text") + */ + color?: ButtonColor; + /** + * Actual type is `'single' | 'multi'`. + * Determines how the selection of the group should be handled. + * With `'single'` only one option can be selected at a time (similar to radio group). + * With `'multi'` multiple options selected (similar to checkbox group). + */ + type?: 'single' | 'multi'; + + /** + * An array of #EuiButtonSingleGroupOptionProps or #EuiButtonMultiGroupOptionProps + */ + options: EuiButtonSingleGroupOptionProps[] | EuiButtonMultiGroupOptionProps[]; +} & ( + | { + type: 'single'; + /** + * An array of #EuiButtonSingleGroupOptionProps + */ + options: EuiButtonSingleGroupOptionProps[]; + /** + * Styles the selected option to look selected (usually with `fill`) + * Only used in `type='single'`. + */ + idSelected: string; + /** + * Returns the `id` of the clicked option and the `value` + * (`value` only in `type='single'`) + */ + onChange: (id: string, value?: any) => void; + idToSelectedMap?: never; + } + | { + type: 'multi'; + /** + * An array of #EuiButtonMultiGroupOptionProps + */ + options: EuiButtonMultiGroupOptionProps[]; + /** + * A map of `id`s as keys with the selected boolean values. + * Only used in `type='multi'`. + */ + idToSelectedMap?: { [id: string]: boolean }; + /** + * Returns the `id` of the clicked option + */ + onChange: (id: string) => void; + idSelected?: never; + } + ); From fb5fd51ecb67d1d156c515c9f9bf7a4d44099b62 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 20 Aug 2020 11:47:33 -0400 Subject: [PATCH 03/34] Cleanup multi button --- .../button_group/button_group_multi_button.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/button/button_group/button_group_multi_button.tsx b/src/components/button/button_group/button_group_multi_button.tsx index 88fc78fe385..d7eb48707ef 100644 --- a/src/components/button/button_group/button_group_multi_button.tsx +++ b/src/components/button/button_group/button_group_multi_button.tsx @@ -32,22 +32,26 @@ type Props = { EuiButtonMultiGroupOptionProps; export const EuiButtonGroupMultiButton: FunctionComponent = ({ + className, id, label, isDisabled, - className, - iconType, - iconSide, + isGroupDisabled, idToSelectedMap, size, color, isIconOnly, - isGroupDisabled, onChange, ...rest }) => { const isSelectedState = idToSelectedMap[id] || false; const badColorCombo = size === 'compressed' && color === 'ghost'; + if (badColorCombo) { + console.warn( + 'EuiButtonGroup of compressed size does not support the ghost color. It will render as text instead.' + ); + } + const buttonClasses = classNames( 'euiButtonGroup__toggle', { @@ -66,8 +70,6 @@ export const EuiButtonGroupMultiButton: FunctionComponent = ({ aria-pressed={isSelectedState} size={size === 'compressed' ? 's' : size} onClick={() => onChange(id)} - iconSide={iconSide} - iconType={iconType} {...rest}> {isIconOnly ? ( From 24a96235c3070626324d008a32db59730c8db4f5 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 20 Aug 2020 12:18:27 -0400 Subject: [PATCH 04/34] Fix up single button --- .../button_group_single_button.tsx | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/src/components/button/button_group/button_group_single_button.tsx b/src/components/button/button_group/button_group_single_button.tsx index 98a0cb8c6b9..98af099ee32 100644 --- a/src/components/button/button_group/button_group_single_button.tsx +++ b/src/components/button/button_group/button_group_single_button.tsx @@ -20,11 +20,11 @@ import classNames from 'classnames'; import React, { FunctionComponent } from 'react'; import { EuiScreenReaderOnly } from '../../accessibility'; -import { EuiIcon } from '../../icon'; import { ButtonGroupOptionProps, EuiButtonSingleGroupOptionProps, } from './types'; +import { EuiButtonDisplay } from '../button'; type Props = { idSelected: string; @@ -37,8 +37,6 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ label, isDisabled, className, - iconType, - iconSide = 'left', idSelected, isGroupDisabled, isIconOnly, @@ -49,52 +47,49 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ ...rest }) => { const isSelectedState = id === idSelected; - const fill = size !== 'compressed' && isSelectedState; - const wrapperClasses = classNames( - 'euiButton', - 'euiButton--no-hover', + const badColorCombo = size === 'compressed' && color === 'ghost'; + if (badColorCombo) { + console.warn( + 'EuiButtonGroup of compressed size does not support the ghost color. It will render as text instead.' + ); + } + + const buttonClasses = classNames( 'euiButtonGroup__toggle', { 'euiButtonGroup__button--selected': isSelectedState, - 'euiButton--disabled': isGroupDisabled || isDisabled, - 'euiButtonGroup__toggle--iconOnly': isIconOnly, - 'euiButton--fill': fill, - 'euiButton--small': size === 'compressed' || size === 's', - 'euiButton--ghost': size !== 'compressed' && color === 'ghost', }, className ); - const icon = iconType && ; return ( -
+ onChange(id, value)} + {...rest}> onChange(id, value)} checked={isSelectedState} disabled={isGroupDisabled || isDisabled} value={value} type="radio" - {...rest} /> - -
+ {isIconOnly ? ( + + {label} + + ) : ( + <>{label} + )} + ); }; From 0c3f3970ec2549afe1c50f8f4d7cf5e2f1698834 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 20 Aug 2020 14:51:12 -0400 Subject: [PATCH 05/34] Clean slate with styles --- src-docs/src/views/button/button_group.js | 2 + src/components/button/_button.scss | 20 +- .../button/button_group/_button_group.scss | 346 ++++++++---------- .../_button_group_multi_button.scss | 0 .../_button_group_single_button.scss | 0 .../button/button_group/_index.scss | 3 +- .../button/button_group/button_group.tsx | 11 - .../button_group_multi_button.tsx | 21 +- .../button_group_single_button.tsx | 23 +- 9 files changed, 186 insertions(+), 240 deletions(-) delete mode 100644 src/components/button/button_group/_button_group_multi_button.scss delete mode 100644 src/components/button/button_group/_button_group_single_button.scss diff --git a/src-docs/src/views/button/button_group.js b/src-docs/src/views/button/button_group.js index f54514806b8..d1d208ac1b2 100644 --- a/src-docs/src/views/button/button_group.js +++ b/src-docs/src/views/button/button_group.js @@ -190,6 +190,7 @@ export default () => { onChange(id)} @@ -239,6 +240,7 @@ export default () => {    onChangeIconsMulti(id)} diff --git a/src/components/button/_button.scss b/src/components/button/_button.scss index ea7f3a05b91..00e57f45285 100644 --- a/src/components/button/_button.scss +++ b/src/components/button/_button.scss @@ -30,14 +30,14 @@ &:enabled { &:hover, - &:focus { + &:focus, + &:focus-within { background-color: transparentize($euiColorPrimary, .9); } } &:disabled { @include euiButtonContentDisabled; - color: $euiButtonColorDisabledText; border-color: $euiButtonColorDisabled; @@ -48,16 +48,17 @@ border-color: $euiButtonColorDisabled; &:hover, - &:focus { + &:focus, + &:focus-within { background-color: $euiButtonColorDisabled; border-color: $euiButtonColorDisabled; } } &:hover, - &:focus { + &:focus, + &:focus-within { @include euiSlightShadow; - text-decoration: none; } } @@ -90,7 +91,8 @@ &:enabled { &:hover, - &:focus { + &:focus, + &:focus-within { background-color: darken($color, 5%); border-color: darken($color, 5%); } @@ -109,7 +111,8 @@ @include euiSlightShadow($shadowColor); &:hover, - &:focus { + &:focus, + &:focus-within { @include euiSlightShadowHover($shadowColor); background-color: transparentize($color, .9); @@ -125,7 +128,8 @@ .euiButton:disabled.euiButton--ghost { &, &:hover, - &:focus { + &:focus, + &:focus-within { @include euiSlightShadow($euiColorInk); color: $euiButtonColorGhostDisabled; border-color: $euiButtonColorGhostDisabled; diff --git a/src/components/button/button_group/_button_group.scss b/src/components/button/button_group/_button_group.scss index 737c09d3d2a..b2f612bbc90 100644 --- a/src/components/button/button_group/_button_group.scss +++ b/src/components/button/button_group/_button_group.scss @@ -1,193 +1,163 @@ -.euiButtonGroup { - max-width: 100%; - display: flex; - border-radius: $euiBorderRadius; - - .euiButton { - border-radius: 0; - margin-left: -1px; - - &:not(.euiButton--fill) { - border-color: $euiButtonToggleBorderColor; - } - - &, - &:hover, - &:focus, - &:active { - transition: none; - transform: none; - animation: none; - box-shadow: none; - } - - &.euiButton--disabled:hover { - text-decoration: none; - cursor: not-allowed; - } - } - - .euiButton--fill { - z-index: 0; - } - - .euiButtonGroup__button--iconOnly, - .euiButtonGroup__toggle--iconOnly { - min-width: auto; - } - - .euiButton--fill + .euiButton { - border-left-color: transparent; - } - - &:not(.euiButtonGroup--compressed) { - .euiButton { - &[aria-pressed='true'] + [aria-pressed='true'] { - box-shadow: -1px 0 0 transparentize($euiColorEmptyShade, .9); - } - - &:first-child { - border-top-left-radius: $euiBorderRadius; - border-bottom-left-radius: $euiBorderRadius; - } - - &:last-child { - border-top-right-radius: $euiBorderRadius; - border-bottom-right-radius: $euiBorderRadius; - } - } - } -} - -@each $name, $color in $euiButtonTypes { - .euiButtonGroup--#{$name}:not(.euiButtonGroup--compressed) { - @if ($name == 'ghost') { - // Ghost is unique and ALWAYS sits against a dark background. - color: $color; - } @else if ($name == 'text') { - // The default color is lighter than the normal text color, make the it the text color - color: $euiTextColor; - } @else { - // Other colors need to check their contrast against the page background color. - color: makeHighContrastColor($color, $euiPageBackgroundColor); - } - - $shadowColor: $euiShadowColor; - @if ($name == 'ghost') { - $shadowColor: $euiColorInk; - } @else if (lightness($euiTextColor) < 50) { - // Only if this is the light theme do we use the button variant color to colorize the shadow - $shadowColor: desaturate($color, 60%); - } - - @include euiSlightShadow($shadowColor); - - .euiButtonGroup__input:focus + .euiButtonGroup__label { - background-color: darken($color, 5%); - border-color: darken($color, 5%); - text-decoration: underline; - } - } -} - -.euiButtonGroup__button { - text-overflow: ellipsis; - overflow: hidden; -} +// .euiButtonGroup { +// max-width: 100%; +// display: flex; +// border-radius: $euiBorderRadius; + +// .euiButtonGroup__button { +// border-radius: 0; +// margin-left: -1px; +// min-width: auto; + +// &:not([class*='fill']) { +// border-color: $euiButtonToggleBorderColor; +// } + +// &, +// &:hover, +// &:focus, +// &:active { +// transition: none; +// transform: none; +// animation: none; +// box-shadow: none; +// } + +// &[class*='disabled']:hover { +// text-decoration: none; +// cursor: not-allowed; +// } + +// &[class*='fill'] { +// z-index: 0; +// } +// } + +// &:not(.euiButtonGroup--compressed) { +// .euiButtonGroup__button { +// &:first-child { +// border-top-left-radius: $euiBorderRadius; +// border-bottom-left-radius: $euiBorderRadius; +// } + +// &:last-child { +// border-top-right-radius: $euiBorderRadius; +// border-bottom-right-radius: $euiBorderRadius; +// } +// } + +// .euiButtonGroup__button--selected + .euiButtonGroup__button--selected { +// box-shadow: -1px 0 0 transparentize($euiColorEmptyShade, .9); +// } +// } +// } + +// @each $name, $color in $euiButtonTypes { +// .euiButtonGroup--#{$name}:not(.euiButtonGroup--compressed) { +// @if ($name == 'ghost') { +// // Ghost is unique and ALWAYS sits against a dark background. +// color: $color; +// } @else if ($name == 'text') { +// // The default color is lighter than the normal text color, make the it the text color +// color: $euiTextColor; +// } @else { +// // Other colors need to check their contrast against the page background color. +// color: makeHighContrastColor($color, $euiPageBackgroundColor); +// } + +// $shadowColor: $euiShadowColor; +// @if ($name == 'ghost') { +// $shadowColor: $euiColorInk; +// } @else if (lightness($euiTextColor) < 50) { +// // Only if this is the light theme do we use the button variant color to colorize the shadow +// $shadowColor: desaturate($color, 60%); +// } + +// @include euiSlightShadow($shadowColor); + +// .euiButtonGroup__input:focus + .euiButtonGroup__label { +// background-color: darken($color, 5%); +// border-color: darken($color, 5%); +// text-decoration: underline; +// } +// } +// } + +// .euiButtonGroup__button { +// text-overflow: ellipsis; +// overflow: hidden; +// } + +// .euiButtonGroup__fieldset { +// display: inline-block; +// max-width: 100%; + +// &--fullWidth { +// display: block; +// } +// } + +// .euiButtonGroup__input { +// @include euiScreenReaderOnly; +// } + +// .euiButtonGroup--compressed, +// .euiButtonGroup--fullWidth { +// .euiButtonGroup__button { +// flex: 1; +// } +// } -.euiButtonGroup__fieldset { - display: inline-block; - max-width: 100%; - - &--fullWidth { - display: block; - } -} - -.euiButtonGroup__input { - @include euiScreenReaderOnly; -} - -.euiButtonGroup--compressed, -.euiButtonGroup--fullWidth { - .euiButtonGroup__toggle { - flex: 1; - } -} - -.euiButtonGroup__label { - white-space: nowrap; - cursor: inherit; - - .euiButton--disabled & { - pointer-events: none; - } -} - -.euiButtonGroup__button--iconOnly { - .euiButton__content { - flex-direction: column; - padding: 0 $euiSizeS; - justify-content: center; - } - - .euiIcon { - color: inherit; - margin: 0; - } -} @include euiBreakpoint('xs', 's') { - .euiButtonGroup__fieldset { - display: block; - } - - .euiButtonGroup__button { - flex: 1; - min-width: 0; - } + // .euiButtonGroup__fieldset { + // display: block; + // } + + // .euiButtonGroup__button { + // flex: 1; + // min-width: 0; + // } } -.euiButtonGroup--compressed { - border: 1px solid $euiFormBorderColor; - border-radius: $euiFormControlCompressedBorderRadius; - background-color: $euiFormBackgroundColor; - - .euiButtonGroup__button:focus, - .euiButtonGroup__input:focus + .euiButtonGroup__label { - outline: 2px solid $euiFocusRingColor; - } - - .euiButtonGroup__button { - border: none; - background: none; - border-radius: $euiBorderRadius; - font-size: $euiFontSizeS; - height: $euiFormControlCompressedHeight - 2px; - box-shadow: none; - min-width: 0; - // Offset the background color from the border by 2px - // by clipping background to before the padding starts - padding: 2px; - background-clip: content-box; - - &:not(.euiButtonGroup__button--selected):not(.euiButton--disabled) { - color: $euiColorDarkShade; - } - - &:hover, - &:focus { - box-shadow: none; - } - } - - .euiButtonGroup__button--selected { - font-weight: $euiFontWeightSemiBold; - background-color: $euiFormInputGroupLabelBackground; - } - - .euiButton__content { - padding-left: $euiSizeS; - padding-right: $euiSizeS; - } -} +// .euiButtonGroup--compressed { +// border: 1px solid $euiFormBorderColor; +// border-radius: $euiFormControlCompressedBorderRadius; +// background-color: $euiFormBackgroundColor; + +// .euiButtonGroup__button { +// border: none; +// background: none; +// border-radius: $euiBorderRadius; +// font-size: $euiFontSizeS; +// height: $euiFormControlCompressedHeight - 2px; +// box-shadow: none; +// min-width: 0; +// // Offset the background color from the border by 2px +// // by clipping background to before the padding starts +// padding: 2px; +// background-clip: content-box; + +// &:not(.euiButtonGroup__button--selected):not(.euiButton--disabled) { +// color: $euiColorDarkShade; +// } + +// &:hover, +// &:focus { +// box-shadow: none; +// } + +// &:focus { +// outline: 2px solid $euiFocusRingColor; +// } +// } + +// .euiButtonGroup__button--selected { +// font-weight: $euiFontWeightSemiBold; +// background-color: $euiFormInputGroupLabelBackground; +// } + +// // .euiButton__content { +// // padding-left: $euiSizeS; +// // padding-right: $euiSizeS; +// // } +// } diff --git a/src/components/button/button_group/_button_group_multi_button.scss b/src/components/button/button_group/_button_group_multi_button.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/components/button/button_group/_button_group_single_button.scss b/src/components/button/button_group/_button_group_single_button.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/components/button/button_group/_index.scss b/src/components/button/button_group/_index.scss index 0b8adecfa38..dd9e2fa377b 100644 --- a/src/components/button/button_group/_index.scss +++ b/src/components/button/button_group/_index.scss @@ -1,3 +1,2 @@ @import 'button_group'; -@import 'button_group_single_button'; -@import 'button_group_multi_button'; +@import 'button_group_button'; diff --git a/src/components/button/button_group/button_group.tsx b/src/components/button/button_group/button_group.tsx index 228be85ebe4..b5774fd20df 100644 --- a/src/components/button/button_group/button_group.tsx +++ b/src/components/button/button_group/button_group.tsx @@ -74,15 +74,6 @@ export const EuiButtonGroup: FunctionComponent = ({
{options.map((option, index) => { - const optionClasses = classNames( - 'euiButtonGroup__button', - 'euiButton--no-hover', - { - 'euiButtonGroup__button--iconOnly': isIconOnly, - }, - option.className - ); - if (type === 'single') { return ( = ({ isGroupDisabled={isDisabled} color={resolvedColor} onChange={onChange} - className={optionClasses} /> ); } @@ -109,7 +99,6 @@ export const EuiButtonGroup: FunctionComponent = ({ isIconOnly={isIconOnly} isGroupDisabled={isDisabled} onChange={onChange} - className={optionClasses} /> ); })} diff --git a/src/components/button/button_group/button_group_multi_button.tsx b/src/components/button/button_group/button_group_multi_button.tsx index d7eb48707ef..25b4c0f79a3 100644 --- a/src/components/button/button_group/button_group_multi_button.tsx +++ b/src/components/button/button_group/button_group_multi_button.tsx @@ -19,7 +19,6 @@ import classNames from 'classnames'; import React, { FunctionComponent } from 'react'; -import { EuiScreenReaderOnly } from '../../accessibility'; import { EuiButton } from '../button'; import { ButtonGroupOptionProps, @@ -46,16 +45,11 @@ export const EuiButtonGroupMultiButton: FunctionComponent = ({ }) => { const isSelectedState = idToSelectedMap[id] || false; const badColorCombo = size === 'compressed' && color === 'ghost'; - if (badColorCombo) { - console.warn( - 'EuiButtonGroup of compressed size does not support the ghost color. It will render as text instead.' - ); - } const buttonClasses = classNames( - 'euiButtonGroup__toggle', + 'euiButtonGroupButton', { - 'euiButtonGroup__button--selected': isSelectedState, + 'euiButtonGroupButton--selected': isSelectedState, }, className ); @@ -70,14 +64,11 @@ export const EuiButtonGroupMultiButton: FunctionComponent = ({ aria-pressed={isSelectedState} size={size === 'compressed' ? 's' : size} onClick={() => onChange(id)} + textProps={{ + className: isIconOnly ? 'euiScreenReaderOnly' : undefined, + }} {...rest}> - {isIconOnly ? ( - - {label} - - ) : ( - <>{label} - )} + {label} ); }; diff --git a/src/components/button/button_group/button_group_single_button.tsx b/src/components/button/button_group/button_group_single_button.tsx index 98af099ee32..a8b17175316 100644 --- a/src/components/button/button_group/button_group_single_button.tsx +++ b/src/components/button/button_group/button_group_single_button.tsx @@ -19,7 +19,6 @@ import classNames from 'classnames'; import React, { FunctionComponent } from 'react'; -import { EuiScreenReaderOnly } from '../../accessibility'; import { ButtonGroupOptionProps, EuiButtonSingleGroupOptionProps, @@ -48,16 +47,11 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ }) => { const isSelectedState = id === idSelected; const badColorCombo = size === 'compressed' && color === 'ghost'; - if (badColorCombo) { - console.warn( - 'EuiButtonGroup of compressed size does not support the ghost color. It will render as text instead.' - ); - } const buttonClasses = classNames( - 'euiButtonGroup__toggle', + 'euiButtonGroupButton', { - 'euiButtonGroup__button--selected': isSelectedState, + 'euiButtonGroupButton--selected': isSelectedState, }, className ); @@ -73,23 +67,20 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ aria-pressed={isSelectedState} size={size === 'compressed' ? 's' : size} onClick={() => onChange(id, value)} + textProps={{ + className: isIconOnly ? 'euiScreenReaderOnly' : undefined, + }} {...rest}> - {isIconOnly ? ( - - {label} - - ) : ( - <>{label} - )} + {label} ); }; From ed50d7a897a5f4a661c65ca56b6baca5d1ece97c Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 20 Aug 2020 14:51:24 -0400 Subject: [PATCH 06/34] Clean slate with styles --- src/components/button/button_group/_button_group_button.scss | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/components/button/button_group/_button_group_button.scss diff --git a/src/components/button/button_group/_button_group_button.scss b/src/components/button/button_group/_button_group_button.scss new file mode 100644 index 00000000000..e69de29bb2d From 7875ebaf91ea5b83633e06dda61177605050e033 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 20 Aug 2020 15:27:05 -0400 Subject: [PATCH 07/34] [EuiButton] add `minWidth` prop --- src-docs/src/views/button/playground.js | 6 +++++ .../button/__snapshots__/button.test.tsx.snap | 16 +++++++++++++ src/components/button/button.test.tsx | 8 +++++++ src/components/button/button.tsx | 23 ++++++++++++++++++- .../button_group_multi_button.tsx | 1 + .../button_group_single_button.tsx | 3 ++- 6 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src-docs/src/views/button/playground.js b/src-docs/src/views/button/playground.js index bb61586582e..91950b9b415 100644 --- a/src-docs/src/views/button/playground.js +++ b/src-docs/src/views/button/playground.js @@ -4,6 +4,7 @@ import { propUtilityForPlayground, iconValidator, } from '../../services/playground'; +import { number } from 'prop-types'; export default () => { const docgenInfo = Array.isArray(EuiButton.__docgenInfo) @@ -20,6 +21,11 @@ export default () => { hidden: true, }; + propsToUse.minWidth = { + ...propsToUse.minWidth, + type: PropTypes.Number, + }; + const setGhostBackground = { color: 'ghost', }; diff --git a/src/components/button/__snapshots__/button.test.tsx.snap b/src/components/button/__snapshots__/button.test.tsx.snap index fb283b97522..9793bcbde4d 100644 --- a/src/components/button/__snapshots__/button.test.tsx.snap +++ b/src/components/button/__snapshots__/button.test.tsx.snap @@ -303,6 +303,22 @@ exports[`EuiButton props isLoading is rendered 1`] = ` `; +exports[`EuiButton props minWidth is rendered 1`] = ` + +`; + exports[`EuiButton props size m is rendered 1`] = `
`; @@ -15,87 +21,73 @@ exports[`EuiButtonGroup props buttonSize compressed is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -104,87 +96,73 @@ exports[`EuiButtonGroup props buttonSize m is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -193,87 +171,73 @@ exports[`EuiButtonGroup props buttonSize s is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -282,87 +246,73 @@ exports[`EuiButtonGroup props color danger is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -371,87 +321,73 @@ exports[`EuiButtonGroup props color ghost is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -460,87 +396,73 @@ exports[`EuiButtonGroup props color primary is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -549,87 +471,73 @@ exports[`EuiButtonGroup props color secondary is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -638,87 +546,73 @@ exports[`EuiButtonGroup props color text is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -727,87 +621,73 @@ exports[`EuiButtonGroup props color warning is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -816,88 +696,74 @@ exports[`EuiButtonGroup props idSelected is rendered 1`] = `
+ + test +
-
- - -
-
+ +
-
+ +
+ +
`; @@ -906,93 +772,79 @@ exports[`EuiButtonGroup props isDisabled is rendered 1`] = `
+ + test +
-
- - -
-
+ + -
-
+ + -
+ +
`; @@ -1001,356 +853,148 @@ exports[`EuiButtonGroup props isFullWidth is rendered 1`] = `
-
-
- - -
-
- - -
-
- - -
-
-
-`; - -exports[`EuiButtonGroup props isIconOnly is rendered 1`] = ` -
+ test +
-
- - -
-
+ +
-
+ +
+ +
`; -exports[`EuiButtonGroup props legend is rendered 1`] = ` +exports[`EuiButtonGroup props isIconOnly is rendered 1`] = `
- legend + test
-
- - -
-
- - -
-
- - -
-
-
-`; - -exports[`EuiButtonGroup props name is rendered 1`] = ` -
-
-
- - -
-
+ +
-
+ +
+ +
`; @@ -1359,125 +1003,73 @@ exports[`EuiButtonGroup props options are rendered 1`] = `
+ + test +
-
- - -
-
- - -
-
+ +
-
-
-`; - -exports[`EuiButtonGroup props options can pass down data-test-subj 1`] = ` -
-
-
+ +
+ +
`; @@ -1486,89 +1078,64 @@ exports[`EuiButtonGroup props type of multi idToSelectedMap is rendered 1`] = `
+ + test +
-
- - -
-
+ + -
-
+ + -
+ +
`; @@ -1577,87 +1144,64 @@ exports[`EuiButtonGroup props type of multi is rendered 1`] = `
+ + test +
-
- - -
-
+ + -
-
+ + -
+ +
`; diff --git a/src/components/button/button_group/_button_group.scss b/src/components/button/button_group/_button_group.scss index 2da820e407a..ada5bd93128 100644 --- a/src/components/button/button_group/_button_group.scss +++ b/src/components/button/button_group/_button_group.scss @@ -7,136 +7,26 @@ } } -.euiButtonGroup { - max-width: 100%; - display: flex; - - // Non-compressed version only - &:not(.euiButtonGroup--compressed) { - .euiButtonGroup__button { - } - } -} - -// .euiButtonGroup { -// &:not(.euiButtonGroup--compressed) { -// .euiButtonGroup__button { -// &:first-child { -// border-top-left-radius: $euiBorderRadius; -// border-bottom-left-radius: $euiBorderRadius; -// } - -// &:last-child { -// border-top-right-radius: $euiBorderRadius; -// border-bottom-right-radius: $euiBorderRadius; -// } -// } - -// .euiButtonGroup__button--selected + .euiButtonGroup__button--selected { -// box-shadow: -1px 0 0 transparentize($euiColorEmptyShade, .9); -// } -// } -// } - -// @each $name, $color in $euiButtonTypes { -// .euiButtonGroup--#{$name}:not(.euiButtonGroup--compressed) { -// @if ($name == 'ghost') { -// // Ghost is unique and ALWAYS sits against a dark background. -// color: $color; -// } @else if ($name == 'text') { -// // The default color is lighter than the normal text color, make the it the text color -// color: $euiTextColor; -// } @else { -// // Other colors need to check their contrast against the page background color. -// color: makeHighContrastColor($color, $euiPageBackgroundColor); -// } - -// $shadowColor: $euiShadowColor; -// @if ($name == 'ghost') { -// $shadowColor: $euiColorInk; -// } @else if (lightness($euiTextColor) < 50) { -// // Only if this is the light theme do we use the button variant color to colorize the shadow -// $shadowColor: desaturate($color, 60%); -// } - -// @include euiSlightShadow($shadowColor); - -// .euiButtonGroup__input:focus + .euiButtonGroup__label { -// background-color: darken($color, 5%); -// border-color: darken($color, 5%); -// text-decoration: underline; -// } -// } -// } - -// .euiButtonGroup__button { -// text-overflow: ellipsis; -// overflow: hidden; -// } - - -// .euiButtonGroup__input { -// @include euiScreenReaderOnly; -// } - -// .euiButtonGroup--compressed, .euiButtonGroup--fullWidth { + width: 100%; + .euiButtonGroupButton { flex: 1; } } +.euiButtonGroup { + @include euiSlightShadow; + border-radius: $euiBorderRadius + 1px; // Simply for the box-shadow -// @include euiBreakpoint('xs', 's') { -// .euiButtonGroup__fieldset { -// display: block; -// } - -// .euiButtonGroup__button { -// flex: 1; -// min-width: 0; -// } -// } - -// .euiButtonGroup--compressed { -// border: 1px solid $euiFormBorderColor; -// border-radius: $euiFormControlCompressedBorderRadius; -// background-color: $euiFormBackgroundColor; - -// .euiButtonGroup__button { -// border: none; -// background: none; -// border-radius: $euiBorderRadius; -// font-size: $euiFontSizeS; -// height: $euiFormControlCompressedHeight - 2px; -// box-shadow: none; -// min-width: 0; -// // Offset the background color from the border by 2px -// // by clipping background to before the padding starts -// padding: 2px; -// background-clip: content-box; - -// &:not(.euiButtonGroup__button--selected):not(.euiButton--disabled) { -// color: $euiColorDarkShade; -// } - -// &:hover, -// &:focus { -// box-shadow: none; -// } - -// &:focus { -// outline: 2px solid $euiFocusRingColor; -// } -// } - -// .euiButtonGroup__button--selected { -// font-weight: $euiFontWeightSemiBold; -// background-color: $euiFormInputGroupLabelBackground; -// } + text-decoration: none; + max-width: 100%; + display: flex; + overflow: hidden; + transition: all $euiAnimSpeedNormal ease-in-out; -// // .euiButton__content { -// // padding-left: $euiSizeS; -// // padding-right: $euiSizeS; -// // } -// } + &:hover, + &:active { + @include euiSlightShadowHover; + } +} diff --git a/src/components/button/button_group/_button_group_button.scss b/src/components/button/button_group/_button_group_button.scss index 4ccbe9c2ba4..edce7d095e9 100644 --- a/src/components/button/button_group/_button_group_button.scss +++ b/src/components/button/button_group/_button_group_button.scss @@ -1,37 +1,135 @@ .euiButtonGroupButton { - // Remove transforms from all states - transform: none !important; - animation: none !important; - // Also remove all individual styles that will be be applied to the group instead - // box-shadow: none !important; - border-radius: 0; - margin-left: -1px; + @include euiButtonBase; + @include euiFont; + @include euiFontSize; - &:focus-within { - text-decoration: underline; + border: $euiBorderThin; + transition: all $euiAnimSpeedNormal ease-in-out; + + .euiButton__content { + padding: 0 ($euiSize - $euiSizeXS); } - // Only change the border color when enabled and selected - &:not(.euiButtonGroupButton--selected):not([class*='isDisabled']) { - border-color: $euiButtonToggleBorderColor; + .euiButton__text { + text-overflow: ellipsis; + overflow: hidden; } - &:not(.euiButtonGroupButton--selected) { - z-index: 0; + &:not(:first-child) { + margin-left: -1px; } &:first-child { - border-top-left-radius: $euiBorderRadius; - border-bottom-left-radius: $euiBorderRadius; - margin-left: 0; + border-radius: $euiBorderRadius 0 0 $euiBorderRadius; } &:last-child { - border-top-right-radius: $euiBorderRadius; - border-bottom-right-radius: $euiBorderRadius; + border-radius: 0 $euiBorderRadius $euiBorderRadius 0; + } + + &.euiButtonGroupButton--small { + height: $euiButtonHeightSmall; + line-height: $euiButtonHeightSmall; // prevents descenders from getting cut off + } + + &.euiButtonGroupButton--compressed { + height: $euiFormControlCompressedHeight; + line-height: $euiFormControlCompressedHeight; // prevents descenders from getting cut off + } + + &:not([class*='isDisabled']) { + &:hover, + &:focus, + &:focus-within { + background-color: transparentize($euiColorPrimary, .9); + text-decoration: underline; + } + } + + &.euiButtonGroupButton-isDisabled { + @include euiButtonContentDisabled; + color: $euiButtonColorDisabledText; + + &.euiButtonGroupButton-isSelected { + // Only increase the contrast of background color to text to 2.0 for disabled + color: makeHighContrastColor($euiButtonColorDisabled, $euiButtonColorDisabled, 2); + background-color: $euiButtonColorDisabled; + border-color: $euiButtonColorDisabled; + + &:hover, + &:focus, + &:focus-within { + background-color: $euiButtonColorDisabled; + border-color: $euiButtonColorDisabled; + } + } + } +} + +// Create button modifiers based upon the map. +// sass-lint:disable nesting-depth +@each $name, $color in $euiButtonTypes { + .euiButtonGroupButton--#{$name} { + @if ($name == 'ghost') { + // Ghost is unique and ALWAYS sits against a dark background. + color: $color; + } @else if ($name == 'text') { + // The default color is lighter than the normal text color, make the it the text color + color: $euiTextColor; + } @else { + // Other colors need to check their contrast against the page background color. + color: makeHighContrastColor($color, $euiPageBackgroundColor); + } + + &.euiButtonGroupButton-isSelected { + background-color: $color; + border-color: $color; + + // The function makes that hexes safe for theming + $fillTextColor: chooseLightOrDarkText($color, $euiColorGhost, $euiColorInk); + + color: $fillTextColor; + + &:not([class*='isDisabled']) { + &:hover, + &:focus, + &:focus-within { + background-color: darken($color, 5%); + border-color: darken($color, 5%); + } + } + } + + &:not([class*='isDisabled']) { + &:hover, + &:focus, + &:focus-within { + background-color: transparentize($color, .9); + } + } + } +} + +// Fix ghost/disabled look specifically +.euiButtonGroupButton.euiButtonGroupButton-isDisabled.euiButtonGroupButton--ghost { + &, + &:hover, + &:focus, + &:focus-within { + color: $euiButtonColorGhostDisabled; + border-color: $euiButtonColorGhostDisabled; } + + &.euiButtonGroupButton-isSelected { + background-color: $euiButtonColorGhostDisabled; + color: makeHighContrastColor($euiButtonColorGhostDisabled, $euiButtonColorGhostDisabled, 2); + } +} + +.euiButtonGroupButton-isSelected { + z-index: 0; } -.euiButtonGroup__button--selected + .euiButtonGroup__button--selected { +.euiButtonGroupButton-isSelected + .euiButtonGroupButton-isSelected { box-shadow: -1px 0 0 transparentize($euiColorEmptyShade, .9); } diff --git a/src/components/button/button_group/button_group.tsx b/src/components/button/button_group/button_group.tsx index cfc98ef7472..b4254607e14 100644 --- a/src/components/button/button_group/button_group.tsx +++ b/src/components/button/button_group/button_group.tsx @@ -23,10 +23,16 @@ import { EuiScreenReaderOnly } from '../../accessibility'; import { EuiButtonGroupMultiButton } from './button_group_multi_button'; import { EuiButtonGroupSingleButton } from './button_group_single_button'; import { EuiButtonGroupProps } from './types'; +import { colorToClassNameMap, sizeToClassNameMap } from '../button'; type Props = Omit, 'onChange'> & EuiButtonGroupProps; +const groupSizeToClassNameMap = { + ...sizeToClassNameMap, + compressed: '--compressed', +}; + export const EuiButtonGroup: FunctionComponent = ({ className, buttonSize = 's', @@ -53,11 +59,11 @@ export const EuiButtonGroup: FunctionComponent = ({ const classes = classNames( 'euiButtonGroup', - `euiButtonGroup--${resolvedColor}`, + `euiButtonGroup${groupSizeToClassNameMap[buttonSize]}`, + `euiButtonGroup${colorToClassNameMap[resolvedColor]}`, { 'euiButtonGroup--fullWidth': isFullWidth, - 'euiButtonGroup--compressed': buttonSize === 'compressed', - 'euiButtonGroup--disabled': isDisabled, + 'euiButtonGroup--isDisabled': isDisabled, }, className ); diff --git a/src/components/button/button_group/button_group_multi_button.tsx b/src/components/button/button_group/button_group_multi_button.tsx index e2704aad4a1..d9a0cac14de 100644 --- a/src/components/button/button_group/button_group_multi_button.tsx +++ b/src/components/button/button_group/button_group_multi_button.tsx @@ -19,7 +19,7 @@ import classNames from 'classnames'; import React, { FunctionComponent } from 'react'; -import { EuiButton } from '../button'; +import { EuiButtonDisplay } from '../button'; import { ButtonGroupOptionProps, EuiButtonMultiGroupOptionProps, @@ -39,23 +39,27 @@ export const EuiButtonGroupMultiButton: FunctionComponent = ({ color, isIconOnly, onChange, + isDisabled, ...rest }) => { const isSelectedState = idToSelectedMap[id] || false; const badColorCombo = size === 'compressed' && color === 'ghost'; const buttonClasses = classNames( - 'euiButtonGroupButton', { - 'euiButtonGroupButton--selected': isSelectedState, + 'euiButtonGroupButton-isSelected': isSelectedState, }, className ); return ( - = ({ }} {...rest}> {label} - + ); }; diff --git a/src/components/button/button_group/button_group_single_button.tsx b/src/components/button/button_group/button_group_single_button.tsx index f6883facfd1..a8ae7d48885 100644 --- a/src/components/button/button_group/button_group_single_button.tsx +++ b/src/components/button/button_group/button_group_single_button.tsx @@ -48,15 +48,15 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ const badColorCombo = size === 'compressed' && color === 'ghost'; const buttonClasses = classNames( - 'euiButtonGroupButton', { - 'euiButtonGroupButton--selected': isSelectedState, + 'euiButtonGroupButton-isSelected': isSelectedState, }, className ); return ( = ({ textProps={{ className: isIconOnly ? 'euiScreenReaderOnly' : undefined, }} - minWidth={0} {...rest}> Date: Fri, 21 Aug 2020 16:42:30 -0400 Subject: [PATCH 11/34] Fix flex and ghost --- src-docs/src/views/button/button_group.js | 28 +++++++++---------- .../button/button_group/_button_group.scss | 10 ++++--- .../button_group/_button_group_button.scss | 8 ++++++ .../button_group_multi_button.tsx | 1 - 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src-docs/src/views/button/button_group.js b/src-docs/src/views/button/button_group.js index 15faf32510f..d683f66e4ad 100644 --- a/src-docs/src/views/button/button_group.js +++ b/src-docs/src/views/button/button_group.js @@ -140,15 +140,15 @@ export default () => { `${idPrefix2}4` ); - const onChange = (optionId) => { + const onChange = optionId => { setToggleIdSelected(optionId); }; - const onChangeDisabled = (optionId) => { + const onChangeDisabled = optionId => { setToggleIdDisabled(optionId); }; - const onChangeMulti = (optionId) => { + const onChangeMulti = optionId => { const newToggleIdToSelectedMap = { ...toggleIdToSelectedMap, ...{ @@ -158,15 +158,15 @@ export default () => { setToggleIdToSelectedMap(newToggleIdToSelectedMap); }; - const onChangeIcons = (optionId) => { + const onChangeIcons = optionId => { setToggleIconIdSelected(optionId); }; - const onChangeCompressed = (optionId) => { + const onChangeCompressed = optionId => { setToggleCompressedIdSelected(optionId); }; - const onChangeIconsMulti = (optionId) => { + const onChangeIconsMulti = optionId => { const newToggleIconIdToSelectedMap = { ...toggleIconIdToSelectedMap, ...{ @@ -177,7 +177,7 @@ export default () => { setToggleIconIdToSelectedMap(newToggleIconIdToSelectedMap); }; - const onChangeIconsMultiIcons = (optionId) => { + const onChangeIconsMultiIcons = optionId => { const newToggleIconIdToSelectedMapIcon = { ...toggleIconIdToSelectedMapIcon, ...{ @@ -195,7 +195,7 @@ export default () => { name="basic" options={toggleButtons} idSelected={toggleIdSelected} - onChange={(id) => onChange(id)} + onChange={id => onChange(id)} /> @@ -207,7 +207,7 @@ export default () => { name="primary" options={toggleButtonsMulti} idToSelectedMap={toggleIdToSelectedMap} - onChange={(id) => onChangeMulti(id)} + onChange={id => onChangeMulti(id)} color="primary" type="multi" /> @@ -221,7 +221,7 @@ export default () => { name="disabledGroup" options={toggleButtonsDisabled} idSelected={toggleIdDisabled} - onChange={(id) => onChangeDisabled(id)} + onChange={id => onChangeDisabled(id)} buttonSize="m" isDisabled isFullWidth @@ -236,7 +236,7 @@ export default () => { name="textAlign" options={toggleButtonsIcons} idSelected={toggleIconIdSelected} - onChange={(id) => onChangeIcons(id)} + onChange={id => onChangeIcons(id)} isIconOnly />    @@ -245,7 +245,7 @@ export default () => { name="Text style" options={toggleButtonsIconsMulti} idToSelectedMap={toggleIconIdToSelectedMap} - onChange={(id) => onChangeIconsMulti(id)} + onChange={id => onChangeIconsMulti(id)} type="multi" isIconOnly /> @@ -263,7 +263,7 @@ export default () => { legend="This is a basic group" options={toggleButtonsCompressed} idSelected={toggleCompressedIdSelected} - onChange={(id) => onChangeCompressed(id)} + onChange={id => onChangeCompressed(id)} buttonSize="compressed" isFullWidth /> @@ -278,7 +278,7 @@ export default () => { className="eui-displayInlineBlock" options={toggleButtonsIconsMulti} idToSelectedMap={toggleIconIdToSelectedMapIcon} - onChange={(id) => onChangeIconsMultiIcons(id)} + onChange={id => onChangeIconsMultiIcons(id)} type="multi" buttonSize="compressed" isIconOnly diff --git a/src/components/button/button_group/_button_group.scss b/src/components/button/button_group/_button_group.scss index ada5bd93128..d38fe021a86 100644 --- a/src/components/button/button_group/_button_group.scss +++ b/src/components/button/button_group/_button_group.scss @@ -19,14 +19,16 @@ @include euiSlightShadow; border-radius: $euiBorderRadius + 1px; // Simply for the box-shadow - text-decoration: none; max-width: 100%; display: flex; overflow: hidden; transition: all $euiAnimSpeedNormal ease-in-out; - &:hover, - &:active { - @include euiSlightShadowHover; + &:not(.euiButtonGroup--isDisabled) { + &:hover, + &:active, + &:focus-within { + @include euiSlightShadowHover; + } } } diff --git a/src/components/button/button_group/_button_group_button.scss b/src/components/button/button_group/_button_group_button.scss index edce7d095e9..e0040e37409 100644 --- a/src/components/button/button_group/_button_group_button.scss +++ b/src/components/button/button_group/_button_group_button.scss @@ -6,6 +6,11 @@ border: $euiBorderThin; transition: all $euiAnimSpeedNormal ease-in-out; + // Allow button to shrink and truncate + min-width: 0; + flex-shrink: 1; + flex-grow: 0; + .euiButton__content { padding: 0 ($euiSize - $euiSizeXS); } @@ -117,6 +122,9 @@ &:focus, &:focus-within { color: $euiButtonColorGhostDisabled; + } + + .euiButtonGroup--isDisabled & { border-color: $euiButtonColorGhostDisabled; } diff --git a/src/components/button/button_group/button_group_multi_button.tsx b/src/components/button/button_group/button_group_multi_button.tsx index d9a0cac14de..5c396dac465 100644 --- a/src/components/button/button_group/button_group_multi_button.tsx +++ b/src/components/button/button_group/button_group_multi_button.tsx @@ -65,7 +65,6 @@ export const EuiButtonGroupMultiButton: FunctionComponent = ({ aria-pressed={isSelectedState} size={size === 'compressed' ? 's' : size} onClick={() => onChange(id)} - minWidth={0} textProps={{ className: isIconOnly ? 'euiScreenReaderOnly' : undefined, }} From 2305f81668b6028671a7930eab32934e37cf3a6f Mon Sep 17 00:00:00 2001 From: cchaos Date: Sat, 22 Aug 2020 09:12:37 -0400 Subject: [PATCH 12/34] Finish up styles for EUI theme --- .../button/button_group/_button_group.scss | 9 + .../button_group/_button_group_button.scss | 191 ++++++++++++------ .../button/button_group/button_group.tsx | 9 +- .../button_group_multi_button.tsx | 16 +- .../button_group_single_button.tsx | 16 +- 5 files changed, 155 insertions(+), 86 deletions(-) diff --git a/src/components/button/button_group/_button_group.scss b/src/components/button/button_group/_button_group.scss index d38fe021a86..0cbec257bca 100644 --- a/src/components/button/button_group/_button_group.scss +++ b/src/components/button/button_group/_button_group.scss @@ -31,4 +31,13 @@ @include euiSlightShadowHover; } } + + &--compressed { + box-shadow: none !important; // sass-lint:disable-line no-important + border-radius: $euiFormControlCompressedBorderRadius; + background-color: $euiFormBackgroundColor; + height: $euiFormControlCompressedHeight; + border: 1px solid $euiFormBorderColor; + overflow: visible; +} } diff --git a/src/components/button/button_group/_button_group_button.scss b/src/components/button/button_group/_button_group_button.scss index e0040e37409..7fb5ada3442 100644 --- a/src/components/button/button_group/_button_group_button.scss +++ b/src/components/button/button_group/_button_group_button.scss @@ -3,8 +3,9 @@ @include euiFont; @include euiFontSize; - border: $euiBorderThin; - transition: all $euiAnimSpeedNormal ease-in-out; + transition: background-color $euiAnimSpeedNormal ease-in-out, + border-color $euiAnimSpeedNormal ease-in-out, + color $euiAnimSpeedNormal ease-in-out; // Allow button to shrink and truncate min-width: 0; @@ -12,7 +13,11 @@ flex-grow: 0; .euiButton__content { - padding: 0 ($euiSize - $euiSizeXS); + padding: 0 $euiSizeM; + } + + &-isIconOnly .euiButton__content { + padding: 0 $euiSizeS; } .euiButton__text { @@ -20,28 +25,11 @@ overflow: hidden; } - &:not(:first-child) { - margin-left: -1px; - } - - &:first-child { - border-radius: $euiBorderRadius 0 0 $euiBorderRadius; - } - - &:last-child { - border-radius: 0 $euiBorderRadius $euiBorderRadius 0; - } - &.euiButtonGroupButton--small { height: $euiButtonHeightSmall; line-height: $euiButtonHeightSmall; // prevents descenders from getting cut off } - &.euiButtonGroupButton--compressed { - height: $euiFormControlCompressedHeight; - line-height: $euiFormControlCompressedHeight; // prevents descenders from getting cut off - } - &:not([class*='isDisabled']) { &:hover, &:focus, @@ -58,6 +46,35 @@ &.euiButtonGroupButton-isSelected { // Only increase the contrast of background color to text to 2.0 for disabled color: makeHighContrastColor($euiButtonColorDisabled, $euiButtonColorDisabled, 2); + } + } +} + +/** + * Medium and Small sizing (regular button style) + */ + +// sass-lint:disable nesting-depth +.euiButtonGroup--medium, +.euiButtonGroup--small { + .euiButtonGroupButton { + border: $euiBorderThin; + + &:not(:first-child) { + margin-left: -1px; + } + + &:first-child { + border-radius: $euiBorderRadius 0 0 $euiBorderRadius; + } + + &:last-child { + border-radius: 0 $euiBorderRadius $euiBorderRadius 0; + } + } + + .euiButtonGroupButton-isDisabled { + &.euiButtonGroupButton-isSelected { background-color: $euiButtonColorDisabled; border-color: $euiButtonColorDisabled; @@ -69,33 +86,29 @@ } } } -} -// Create button modifiers based upon the map. -// sass-lint:disable nesting-depth -@each $name, $color in $euiButtonTypes { - .euiButtonGroupButton--#{$name} { - @if ($name == 'ghost') { - // Ghost is unique and ALWAYS sits against a dark background. - color: $color; - } @else if ($name == 'text') { - // The default color is lighter than the normal text color, make the it the text color - color: $euiTextColor; - } @else { - // Other colors need to check their contrast against the page background color. - color: makeHighContrastColor($color, $euiPageBackgroundColor); - } + @each $name, $color in $euiButtonTypes { + .euiButtonGroupButton--#{$name}:not([class*='isDisabled']) { + @if ($name == 'ghost') { + // Ghost is unique and ALWAYS sits against a dark background. + color: $color; + } @else if ($name == 'text') { + // The default color is lighter than the normal text color, make the it the text color + color: $euiTextColor; + } @else { + // Other colors need to check their contrast against the page background color. + color: makeHighContrastColor($color, $euiPageBackgroundColor); + } - &.euiButtonGroupButton-isSelected { - background-color: $color; - border-color: $color; + &.euiButtonGroupButton-isSelected { + background-color: $color; + border-color: $color; - // The function makes that hexes safe for theming - $fillTextColor: chooseLightOrDarkText($color, $euiColorGhost, $euiColorInk); + // The function makes that hexes safe for theming + $fillTextColor: chooseLightOrDarkText($color, $euiColorGhost, $euiColorInk); - color: $fillTextColor; + color: $fillTextColor; - &:not([class*='isDisabled']) { &:hover, &:focus, &:focus-within { @@ -103,9 +116,7 @@ border-color: darken($color, 5%); } } - } - &:not([class*='isDisabled']) { &:hover, &:focus, &:focus-within { @@ -113,31 +124,87 @@ } } } -} -// Fix ghost/disabled look specifically -.euiButtonGroupButton.euiButtonGroupButton-isDisabled.euiButtonGroupButton--ghost { - &, - &:hover, - &:focus, - &:focus-within { - color: $euiButtonColorGhostDisabled; + // Fix ghost/disabled look specifically + .euiButtonGroupButton.euiButtonGroupButton-isDisabled.euiButtonGroupButton--ghost { + &, + &:hover, + &:focus, + &:focus-within { + color: $euiButtonColorGhostDisabled; + } + + .euiButtonGroup--isDisabled & { + border-color: $euiButtonColorGhostDisabled; + } + + &.euiButtonGroupButton-isSelected { + background-color: $euiButtonColorGhostDisabled; + color: makeHighContrastColor($euiButtonColorGhostDisabled, $euiButtonColorGhostDisabled, 2); + } } - .euiButtonGroup--isDisabled & { - border-color: $euiButtonColorGhostDisabled; + .euiButtonGroupButton-isSelected { + z-index: 0; } - &.euiButtonGroupButton-isSelected { - background-color: $euiButtonColorGhostDisabled; - color: makeHighContrastColor($euiButtonColorGhostDisabled, $euiButtonColorGhostDisabled, 2); + .euiButtonGroupButton-isSelected + .euiButtonGroupButton-isSelected { + box-shadow: -1px 0 0 transparentize($euiColorEmptyShade, .9); } } -.euiButtonGroupButton-isSelected { - z-index: 0; -} +/** + * Compressed (form style) + */ + +.euiButtonGroup--compressed { + .euiButtonGroupButton { + height: $euiFormControlCompressedHeight - 2px; + line-height: $euiFormControlCompressedHeight - 2px; // prevents descenders from getting cut off + font-size: $euiFontSizeS; + border-radius: $euiBorderRadius; + // Offset the background color from the border by 2px + // by clipping background to before the padding starts + padding: 2px; + background-clip: content-box; + + .euiButton__content { + padding-left: $euiSizeS; + padding-right: $euiSizeS; + } + + &.euiButtonGroupButton-isSelected { + font-weight: $euiFontWeightSemiBold; + background-color: $euiFormInputGroupLabelBackground; + } + + &:not([class*='isDisabled']) { + color: $euiColorDarkShade; + + &:hover, + &:focus, + &:focus-within { + background-color: transparentize($euiFormInputGroupLabelBackground, .5); + } + + &:focus, + &:focus-within { + outline: 2px solid $euiFocusRingColor; + } + } + } -.euiButtonGroupButton-isSelected + .euiButtonGroupButton-isSelected { - box-shadow: -1px 0 0 transparentize($euiColorEmptyShade, .9); + @each $name, $color in $euiButtonTypes { + .euiButtonGroupButton--#{$name}:not([class*='isDisabled']) { + &.euiButtonGroupButton-isSelected { + @if ($name == 'text') { + // The default color is lighter than the normal text color, make the it the text color + color: $euiTextColor; + } @else { + // Other colors need to check their contrast against the page background color. + color: makeHighContrastColor($color, $euiPageBackgroundColor); + } + } + } + } } diff --git a/src/components/button/button_group/button_group.tsx b/src/components/button/button_group/button_group.tsx index b4254607e14..b861033716a 100644 --- a/src/components/button/button_group/button_group.tsx +++ b/src/components/button/button_group/button_group.tsx @@ -23,13 +23,14 @@ import { EuiScreenReaderOnly } from '../../accessibility'; import { EuiButtonGroupMultiButton } from './button_group_multi_button'; import { EuiButtonGroupSingleButton } from './button_group_single_button'; import { EuiButtonGroupProps } from './types'; -import { colorToClassNameMap, sizeToClassNameMap } from '../button'; +import { colorToClassNameMap } from '../button'; type Props = Omit, 'onChange'> & EuiButtonGroupProps; const groupSizeToClassNameMap = { - ...sizeToClassNameMap, + s: '--small', + m: '--medium', compressed: '--compressed', }; @@ -85,7 +86,7 @@ export const EuiButtonGroup: FunctionComponent = ({ = ({ = ({ className, id, label, - idToSelectedMap, + isSelected, size, - color, isIconOnly, onChange, isDisabled, ...rest }) => { - const isSelectedState = idToSelectedMap[id] || false; - const badColorCombo = size === 'compressed' && color === 'ghost'; - const buttonClasses = classNames( { - 'euiButtonGroupButton-isSelected': isSelectedState, + 'euiButtonGroupButton-isSelected': isSelected, + 'euiButtonGroupButton-isIconOnly': isIconOnly, }, className ); @@ -60,9 +57,8 @@ export const EuiButtonGroupMultiButton: FunctionComponent = ({ className={buttonClasses} disabled={isDisabled} isDisabled={isDisabled} - color={badColorCombo ? 'text' : color} - fill={size !== 'compressed' && isSelectedState} - aria-pressed={isSelectedState} + fill={size !== 'compressed' && isSelected} + aria-pressed={isSelected} size={size === 'compressed' ? 's' : size} onClick={() => onChange(id)} textProps={{ diff --git a/src/components/button/button_group/button_group_single_button.tsx b/src/components/button/button_group/button_group_single_button.tsx index a8ae7d48885..7f25bc4e17f 100644 --- a/src/components/button/button_group/button_group_single_button.tsx +++ b/src/components/button/button_group/button_group_single_button.tsx @@ -26,7 +26,7 @@ import { import { EuiButtonDisplay } from '../button'; type Props = { - idSelected: string; + isSelected: boolean; } & ButtonGroupOptionProps & EuiButtonSingleGroupOptionProps; @@ -36,20 +36,17 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ label, isDisabled, className, - idSelected, + isSelected, isIconOnly, size, name, - color, onChange, ...rest }) => { - const isSelectedState = id === idSelected; - const badColorCombo = size === 'compressed' && color === 'ghost'; - const buttonClasses = classNames( { - 'euiButtonGroupButton-isSelected': isSelectedState, + 'euiButtonGroupButton-isSelected': isSelected, + 'euiButtonGroupButton-isIconOnly': isIconOnly, }, className ); @@ -61,8 +58,7 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ // TODO: Not sure if this is the best way to handle disabled labels from incurring clicks element={isDisabled ? 'button' : 'label'} className={buttonClasses} - color={badColorCombo ? 'text' : color} - fill={size !== 'compressed' && isSelectedState} + fill={size !== 'compressed' && isSelected} // TODO: Return to why both disabled types are needed isDisabled={isDisabled} disabled={isDisabled} @@ -76,7 +72,7 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ id={id} className="euiScreenReaderOnly" name={name} - checked={isSelectedState} + checked={isSelected} disabled={isDisabled} value={value} type="radio" From 38dcaee055704e0f9ac59c323b382928c2098288 Mon Sep 17 00:00:00 2001 From: cchaos Date: Sat, 22 Aug 2020 09:50:00 -0400 Subject: [PATCH 13/34] Fixed Amsterdam styles --- .../button/button_group/_button_group.scss | 2 +- .../button_group/_button_group_button.scss | 1 + .../overrides/_button_group.scss | 48 +++++++++++++++---- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/components/button/button_group/_button_group.scss b/src/components/button/button_group/_button_group.scss index 0cbec257bca..4e83e3295f8 100644 --- a/src/components/button/button_group/_button_group.scss +++ b/src/components/button/button_group/_button_group.scss @@ -39,5 +39,5 @@ height: $euiFormControlCompressedHeight; border: 1px solid $euiFormBorderColor; overflow: visible; -} + } } diff --git a/src/components/button/button_group/_button_group_button.scss b/src/components/button/button_group/_button_group_button.scss index 7fb5ada3442..8c3c45ca8fe 100644 --- a/src/components/button/button_group/_button_group_button.scss +++ b/src/components/button/button_group/_button_group_button.scss @@ -3,6 +3,7 @@ @include euiFont; @include euiFontSize; + // sass-lint:disable-block indentation transition: background-color $euiAnimSpeedNormal ease-in-out, border-color $euiAnimSpeedNormal ease-in-out, color $euiAnimSpeedNormal ease-in-out; diff --git a/src/themes/eui-amsterdam/overrides/_button_group.scss b/src/themes/eui-amsterdam/overrides/_button_group.scss index 7c1ae611abe..667c1494bed 100644 --- a/src/themes/eui-amsterdam/overrides/_button_group.scss +++ b/src/themes/eui-amsterdam/overrides/_button_group.scss @@ -1,14 +1,44 @@ -.euiButtonGroup--compressed { - .euiButtonGroup__button { - &:not(.euiButtonGroup__button--selected):not(:disabled) { - background-color: transparentize($euiColorLightShade, .6); +.euiButtonGroup { + box-shadow: none !important; // sass-lint:disable-line no-important +} + +.euiButtonGroup--medium, +.euiButtonGroup--small { + .euiButtonGroupButton { + border: none !important; // sass-lint:disable-line no-important + } + + .euiButtonGroupButton-isDisabled:not(.euiButtonGroupButton--ghost):not(.euiButtonGroupButton-isSelected) { + $backgroundColorSimulated: mix($euiPageBackgroundColor, $euiButtonColorDisabled, 70%); + background-color: transparentize($euiButtonColorDisabled, .7); + color: makeHighContrastColor($euiButtonColorDisabled, $backgroundColorSimulated, 2); + } + + // Change the hollow (bordered) buttons to have a transparent background + // and no border + @each $name, $color in $euiButtonTypes { + .euiButtonGroupButton--#{$name} { + @include euiButtonDefaultStyle($color); + + @if ($name == 'ghost') { + // Ghost is unique and ALWAYS sits against a dark background. + $backgroundColorSimulated: mix($euiColorInk, $color, 70%); + color: makeHighContrastColor($color, $backgroundColorSimulated); + } } } - .euiButtonToggle__input:enabled:hover + .euiButtonGroup__button, - .euiButtonToggle__input:enabled:focus + .euiButtonGroup__button, - .euiButtonGroup__button--selected { - background-color: $euiColorDarkShade; - color: $euiColorLightestShade; + .euiButtonGroupButton-isDisabled.euiButtonGroupButton--ghost:not(.euiButtonGroupButton-isSelected) { + &, + &:hover, + &:focus { + background-color: transparentize($euiButtonColorGhostDisabled, .7); + } } } + +.euiButtonGroup--small { + // Use a moderately smaller radius on small buttons + // so that they don't appear completely rounded + border-radius: $euiBorderRadius * .667; +} From 812e5da6b54a1131bad78d2222c9d3de153dad2c Mon Sep 17 00:00:00 2001 From: cchaos Date: Mon, 24 Aug 2020 17:06:48 -0400 Subject: [PATCH 14/34] Fixing disabled states --- src-docs/src/views/button/button_group.js | 3 --- src/components/button/button.tsx | 5 ++++- .../button/button_group/button_group.tsx | 12 +++++++++--- .../button_group/button_group_multi_button.tsx | 7 +++---- .../button_group/button_group_single_button.tsx | 17 +++++++++++------ src/components/button/button_group/types.tsx | 10 ++++++---- 6 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src-docs/src/views/button/button_group.js b/src-docs/src/views/button/button_group.js index d683f66e4ad..0f1337433d7 100644 --- a/src-docs/src/views/button/button_group.js +++ b/src-docs/src/views/button/button_group.js @@ -204,7 +204,6 @@ export default () => { onChangeMulti(id)} @@ -242,7 +241,6 @@ export default () => {    onChangeIconsMulti(id)} @@ -273,7 +271,6 @@ export default () => { ( }, ref ) => { + const buttonIsDisabled = isLoading || isDisabled; + const classes = classNames( baseClassName, color ? `${baseClassName}${colorToClassNameMap[color]}` : null, @@ -161,7 +163,7 @@ const EuiButtonDisplay = React.forwardRef( : null, fill && `${baseClassName}--fill`, fullWidth && `${baseClassName}--fullWidth`, - isDisabled && `${baseClassName}-isDisabled`, + buttonIsDisabled && `${baseClassName}-isDisabled`, className ); @@ -205,6 +207,7 @@ const EuiButtonDisplay = React.forwardRef( { className: classes, style: calculatedStyle, + disabled: element === 'button' && buttonIsDisabled, ref, ...rest, }, diff --git a/src/components/button/button_group/button_group.tsx b/src/components/button/button_group/button_group.tsx index b861033716a..6cafe1052ab 100644 --- a/src/components/button/button_group/button_group.tsx +++ b/src/components/button/button_group/button_group.tsx @@ -22,7 +22,11 @@ import React, { FunctionComponent, HTMLAttributes } from 'react'; import { EuiScreenReaderOnly } from '../../accessibility'; import { EuiButtonGroupMultiButton } from './button_group_multi_button'; import { EuiButtonGroupSingleButton } from './button_group_single_button'; -import { EuiButtonGroupProps } from './types'; +import { + EuiButtonGroupProps, + EuiButtonSingleGroupOptionProps, + EuiButtonMultiGroupOptionProps, +} from './types'; import { colorToClassNameMap } from '../button'; type Props = Omit, 'onChange'> & @@ -44,6 +48,7 @@ export const EuiButtonGroup: FunctionComponent = ({ isFullWidth = false, isIconOnly = false, legend, + name, onChange, options = [], type = 'single', @@ -85,13 +90,14 @@ export const EuiButtonGroup: FunctionComponent = ({ return ( ); } @@ -99,7 +105,7 @@ export const EuiButtonGroup: FunctionComponent = ({ return ( = ({ return ( = ({ onChange, ...rest }) => { + const element = isDisabled ? 'button' : 'label'; + + let elementProps = {}; + if (element === 'label') { + elementProps = { ...elementProps, htmlFor: id }; + } + const buttonClasses = classNames( { 'euiButtonGroupButton-isSelected': isSelected, @@ -53,20 +61,17 @@ export const EuiButtonGroupSingleButton: FunctionComponent = ({ return ( onChange(id, value)} textProps={{ className: isIconOnly ? 'euiScreenReaderOnly' : undefined, }} + {...elementProps} {...rest}> void; idSelected?: never; + name?: never; } ); From bfccd9163894bc75f66d144285f77d24f8f9ee4a Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 17 Sep 2020 16:49:06 -0400 Subject: [PATCH 15/34] Update existing tests for button group --- .../__snapshots__/button_group.test.tsx.snap | 1655 ++++++++++++++--- .../button/button_group/button_group.test.tsx | 175 +- 2 files changed, 1435 insertions(+), 395 deletions(-) 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 2139eb366ff..27091f9ec75 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 @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EuiButtonGroup is rendered 1`] = ` +exports[`EuiButtonGroup button props buttonSize compressed is rendered for multi 1`] = `
@@ -10,14 +10,68 @@ exports[`EuiButtonGroup is rendered 1`] = ` test
+ class="euiButtonGroup euiButtonGroup--compressed euiButtonGroup--text" + > + + + +
`; -exports[`EuiButtonGroup props buttonSize compressed is rendered 1`] = ` +exports[`EuiButtonGroup button props buttonSize compressed is rendered for single 1`] = `
@@ -27,9 +81,7 @@ exports[`EuiButtonGroup props buttonSize compressed is rendered 1`] = ` test
`; -exports[`EuiButtonGroup props buttonSize m is rendered 1`] = ` +exports[`EuiButtonGroup button props buttonSize m is rendered for multi 1`] = `
@@ -102,9 +170,78 @@ exports[`EuiButtonGroup props buttonSize m is rendered 1`] = ` test
+ + + +
+
+`; + +exports[`EuiButtonGroup button props buttonSize m is rendered for single 1`] = ` +
+ + test + +
`; -exports[`EuiButtonGroup props buttonSize s is rendered 1`] = ` +exports[`EuiButtonGroup button props buttonSize s is rendered for multi 1`] = `
@@ -177,72 +330,68 @@ exports[`EuiButtonGroup props buttonSize s is rendered 1`] = ` test
-
`; -exports[`EuiButtonGroup props color danger is rendered 1`] = ` +exports[`EuiButtonGroup button props buttonSize s is rendered for single 1`] = `
@@ -252,23 +401,26 @@ exports[`EuiButtonGroup props color danger is rendered 1`] = ` test
`; -exports[`EuiButtonGroup props color ghost is rendered 1`] = ` +exports[`EuiButtonGroup button props color danger is rendered for multi 1`] = `
@@ -327,72 +490,68 @@ exports[`EuiButtonGroup props color ghost is rendered 1`] = ` test
-
`; -exports[`EuiButtonGroup props color primary is rendered 1`] = ` +exports[`EuiButtonGroup button props color danger is rendered for single 1`] = `
@@ -402,23 +561,26 @@ exports[`EuiButtonGroup props color primary is rendered 1`] = ` test
`; -exports[`EuiButtonGroup props color secondary is rendered 1`] = ` +exports[`EuiButtonGroup button props color ghost is rendered for multi 1`] = `
@@ -477,72 +650,870 @@ exports[`EuiButtonGroup props color secondary is rendered 1`] = ` test
-
+`; + +exports[`EuiButtonGroup button props color ghost is rendered for single 1`] = ` +
+ + test + +
+
+`; + +exports[`EuiButtonGroup button props color primary is rendered for multi 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`EuiButtonGroup button props color primary is rendered for single 1`] = ` +
+ + test + +
+
+`; + +exports[`EuiButtonGroup button props color secondary is rendered for multi 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`EuiButtonGroup button props color secondary is rendered for single 1`] = ` +
+ + test + +
+
+`; + +exports[`EuiButtonGroup button props color text is rendered for multi 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`EuiButtonGroup button props color text is rendered for single 1`] = ` +
+ + test + +
+
+`; + +exports[`EuiButtonGroup button props color warning is rendered for multi 1`] = ` +
+ + test + +
+ + + +
+
+`; + +exports[`EuiButtonGroup button props color warning is rendered for single 1`] = ` +
+ + test + +
+
+`; + +exports[`EuiButtonGroup button props isDisabled is rendered for multi 1`] = ` +
+ + test + +
+ + +
`; -exports[`EuiButtonGroup props color text is rendered 1`] = ` +exports[`EuiButtonGroup button props isDisabled is rendered for single 1`] = `
@@ -552,74 +1523,90 @@ exports[`EuiButtonGroup props color text is rendered 1`] = ` test
-
`; -exports[`EuiButtonGroup props color warning is rendered 1`] = ` +exports[`EuiButtonGroup button props isFullWidth is rendered for multi 1`] = `
-
`; -exports[`EuiButtonGroup props idSelected is rendered 1`] = ` +exports[`EuiButtonGroup button props isFullWidth is rendered for single 1`] = `
`; -exports[`EuiButtonGroup props isDisabled is rendered 1`] = ` +exports[`EuiButtonGroup button props isIconOnly is rendered for multi 1`] = `
@@ -778,69 +1774,59 @@ exports[`EuiButtonGroup props isDisabled is rendered 1`] = ` test
`; -exports[`EuiButtonGroup props isFullWidth is rendered 1`] = ` +exports[`EuiButtonGroup button props isIconOnly is rendered for single 1`] = `
`; -exports[`EuiButtonGroup props isIconOnly is rendered 1`] = ` +exports[`EuiButtonGroup button props selection idSelected is rendered for single 1`] = `
@@ -934,23 +1934,27 @@ exports[`EuiButtonGroup props isIconOnly is rendered 1`] = ` test
`; -exports[`EuiButtonGroup props options are rendered 1`] = ` +exports[`EuiButtonGroup button props selection idToSelectedMap is rendered for multi 1`] = `
@@ -1009,72 +2024,70 @@ exports[`EuiButtonGroup props options are rendered 1`] = ` test
-
`; -exports[`EuiButtonGroup props type of multi idToSelectedMap is rendered 1`] = ` +exports[`EuiButtonGroup type multi is rendered 1`] = `
@@ -1089,14 +2102,16 @@ exports[`EuiButtonGroup props type of multi idToSelectedMap is rendered 1`] = ` data-test-subj="test subject string" >
`; -exports[`EuiButtonGroup props type of multi is rendered 1`] = ` +exports[`EuiButtonGroup type single is rendered 1`] = `
@@ -1154,50 +2174,75 @@ exports[`EuiButtonGroup props type of multi is rendered 1`] = ` class="euiButtonGroup euiButtonGroup--small euiButtonGroup--text testClass1 testClass2" data-test-subj="test subject string" > - - + +`; + exports[`EuiButtonToggle is rendered 1`] = ` -
+ + + Toggle + + + +`; + +exports[`EuiButtonToggle isSelected is rendered 1`] = ` + -
+ + `; diff --git a/src/components/button/button_toggle/button_toggle.test.tsx b/src/components/button/button_toggle/button_toggle.test.tsx index 1111772511f..3427888e6d4 100644 --- a/src/components/button/button_toggle/button_toggle.test.tsx +++ b/src/components/button/button_toggle/button_toggle.test.tsx @@ -26,7 +26,27 @@ import { EuiButtonToggle } from './button_toggle'; describe('EuiButtonToggle', () => { test('is rendered', () => { const component = render( - + Toggle + ); + + expect(component).toMatchSnapshot(); + }); + + test('isSelected is rendered', () => { + const component = render( + + Toggle + + ); + + expect(component).toMatchSnapshot(); + }); + + test('extends button props like color', () => { + const component = render( + + Toggle + ); expect(component).toMatchSnapshot(); diff --git a/src/components/button/button_toggle/button_toggle.tsx b/src/components/button/button_toggle/button_toggle.tsx index cf086c90730..4eab7483d0c 100644 --- a/src/components/button/button_toggle/button_toggle.tsx +++ b/src/components/button/button_toggle/button_toggle.tsx @@ -17,142 +17,31 @@ * under the License. */ -import React, { - AnchorHTMLAttributes, - ButtonHTMLAttributes, - ChangeEventHandler, - FunctionComponent, - MouseEventHandler, - ReactNode, -} from 'react'; -import classNames from 'classnames'; -import { CommonProps, ExclusiveUnion } from '../../common'; +import React, { FunctionComponent } from 'react'; -import { EuiToggle, ToggleType } from '../../toggle'; -import { EuiButton, EuiButtonProps } from '../button'; -import { useRenderToText } from '../../inner_text/render_to_text'; - -export interface EuiButtonToggleProps extends EuiButtonProps, CommonProps { - /** - * Simulates a `EuiButtonEmpty` - */ - isEmpty?: boolean; - - /** - * Hides the label from the button content and only displays the icon - */ - isIconOnly?: boolean; +import { EuiButton, EuiButtonPropsForButton } from '../button'; +/** + * Extend EuiButtonProps but only for `; +exports[`EuiButton props isSelected is rendered as false 1`] = ` + +`; + +exports[`EuiButton props isSelected is rendered as true 1`] = ` + +`; + exports[`EuiButton props minWidth is rendered 1`] = ` -`; - -exports[`EuiButtonToggle is rendered 1`] = ` - -`; - -exports[`EuiButtonToggle isSelected is rendered 1`] = ` - -`; diff --git a/src/components/button/button_toggle/button_toggle.test.tsx b/src/components/button/button_toggle/button_toggle.test.tsx deleted file mode 100644 index 3427888e6d4..00000000000 --- a/src/components/button/button_toggle/button_toggle.test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { render } from 'enzyme'; -import { requiredProps } from '../../../test'; - -import { EuiButtonToggle } from './button_toggle'; - -describe('EuiButtonToggle', () => { - test('is rendered', () => { - const component = render( - Toggle - ); - - expect(component).toMatchSnapshot(); - }); - - test('isSelected is rendered', () => { - const component = render( - - Toggle - - ); - - expect(component).toMatchSnapshot(); - }); - - test('extends button props like color', () => { - const component = render( - - Toggle - - ); - - expect(component).toMatchSnapshot(); - }); -}); diff --git a/src/components/button/button_toggle/button_toggle.tsx b/src/components/button/button_toggle/button_toggle.tsx deleted file mode 100644 index 4eab7483d0c..00000000000 --- a/src/components/button/button_toggle/button_toggle.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { FunctionComponent } from 'react'; - -import { EuiButton, EuiButtonPropsForButton } from '../button'; - -/** - * Extend EuiButtonProps but only for `; +exports[`EuiButtonEmpty props isSelected is rendered as false 1`] = ` + +`; + +exports[`EuiButtonEmpty props isSelected is rendered as true 1`] = ` + +`; + exports[`EuiButtonEmpty props size l is rendered 1`] = ` diff --git a/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap b/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap index 6cae9bb61a3..78e6dc06502 100644 --- a/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap +++ b/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap @@ -200,3 +200,33 @@ exports[`EuiButtonIcon props isDisabled renders a button even when href is defin /> `; + +exports[`EuiButtonIcon props isSelected is rendered as false 1`] = ` + +`; + +exports[`EuiButtonIcon props isSelected is rendered as true 1`] = ` + +`; diff --git a/src/components/button/button_icon/button_icon.test.tsx b/src/components/button/button_icon/button_icon.test.tsx index f8efe7d3ee2..b8d16e05f2c 100644 --- a/src/components/button/button_icon/button_icon.test.tsx +++ b/src/components/button/button_icon/button_icon.test.tsx @@ -78,6 +78,28 @@ describe('EuiButtonIcon', () => { }); }); + describe('isSelected', () => { + it('is rendered as true', () => { + const component = render( + + ); + + expect(component).toMatchSnapshot(); + }); + + it('is rendered as false', () => { + const component = render( + + ); + + expect(component).toMatchSnapshot(); + }); + }); + describe('href', () => { it('secures the rel attribute when the target is _blank', () => { const component = render( diff --git a/src/components/button/button_icon/button_icon.tsx b/src/components/button/button_icon/button_icon.tsx index f05cf593e7e..94a42c16c37 100644 --- a/src/components/button/button_icon/button_icon.tsx +++ b/src/components/button/button_icon/button_icon.tsx @@ -107,6 +107,7 @@ export const EuiButtonIcon: FunctionComponent = ({ target, rel, buttonRef, + isSelected, ...rest }) => { const ariaHidden = rest['aria-hidden']; @@ -163,6 +164,7 @@ export const EuiButtonIcon: FunctionComponent = ({ tabIndex={isAriaHidden ? -1 : undefined} disabled={isDisabled} className={classes} + aria-pressed={isSelected} type={type as typeof buttonType} ref={buttonRef as Ref} {...(rest as ButtonHTMLAttributes)}> diff --git a/src/components/common.ts b/src/components/common.ts index 639ac7fcbbd..0e19a3bfcfe 100644 --- a/src/components/common.ts +++ b/src/components/common.ts @@ -190,6 +190,11 @@ export type PropsForAnchor = T & { export type PropsForButton = T & { onClick?: MouseEventHandler; + /** + * Applies the boolean state as the `aria-pressed` property to create a toggle button. + * *Only use when the readable text does not change between states.* + */ + isSelected?: boolean; } & ButtonHTMLAttributes & P; From 79c740056febf742b24afaee1df0ba3cf80ba106 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 15 Oct 2020 09:55:06 -0400 Subject: [PATCH 30/34] Add `isSelected` individually not to `PropsForButton` --- src/components/button/button.tsx | 9 +++++---- src/components/button/button_empty/button_empty.tsx | 5 +++++ src/components/button/button_icon/button_icon.tsx | 5 +++++ src/components/common.ts | 5 ----- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx index 4c7ab6b62bc..7a07d67cf99 100644 --- a/src/components/button/button.tsx +++ b/src/components/button/button.tsx @@ -97,6 +97,11 @@ export interface EuiButtonProps extends EuiButtonContentProps, CommonProps { * `disabled` is also allowed */ isDisabled?: boolean; + /** + * Applies the boolean state as the `aria-pressed` property to create a toggle button. + * *Only use when the readable text does not change between states.* + */ + isSelected?: boolean; /** * Extends the button to 100% width */ @@ -126,10 +131,6 @@ export type EuiButtonDisplayProps = EuiButtonProps & * Provide the component's base class name to build the class list on */ baseClassName: string; - /** - * Inherited from PropsForButton - */ - isSelected?: boolean; }; /** diff --git a/src/components/button/button_empty/button_empty.tsx b/src/components/button/button_empty/button_empty.tsx index cd186f555e5..f94aefbc7ce 100644 --- a/src/components/button/button_empty/button_empty.tsx +++ b/src/components/button/button_empty/button_empty.tsx @@ -91,6 +91,11 @@ interface CommonEuiButtonEmptyProps extends EuiButtonContentProps, CommonProps { * Force disables the button and changes the icon to a loading spinner */ isLoading?: boolean; + /** + * Applies the boolean state as the `aria-pressed` property to create a toggle button. + * *Only use when the readable text does not change between states.* + */ + isSelected?: boolean; href?: string; target?: string; rel?: string; diff --git a/src/components/button/button_icon/button_icon.tsx b/src/components/button/button_icon/button_icon.tsx index 94a42c16c37..7a30fa9c57c 100644 --- a/src/components/button/button_icon/button_icon.tsx +++ b/src/components/button/button_icon/button_icon.tsx @@ -57,6 +57,11 @@ export interface EuiButtonIconProps extends CommonProps { isDisabled?: boolean; size?: ButtonSize; iconSize?: IconSize; + /** + * Applies the boolean state as the `aria-pressed` property to create a toggle button. + * *Only use when the readable text does not change between states.* + */ + isSelected?: boolean; } type EuiButtonIconPropsForAnchor = { diff --git a/src/components/common.ts b/src/components/common.ts index 0e19a3bfcfe..639ac7fcbbd 100644 --- a/src/components/common.ts +++ b/src/components/common.ts @@ -190,11 +190,6 @@ export type PropsForAnchor = T & { export type PropsForButton = T & { onClick?: MouseEventHandler; - /** - * Applies the boolean state as the `aria-pressed` property to create a toggle button. - * *Only use when the readable text does not change between states.* - */ - isSelected?: boolean; } & ButtonHTMLAttributes & P; From dca637781cf245e7df9c6951ce7db1a80cc48105 Mon Sep 17 00:00:00 2001 From: cchaos Date: Thu, 15 Oct 2020 14:29:34 -0400 Subject: [PATCH 31/34] Some docs stuff --- src-docs/src/views/button/button_example.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src-docs/src/views/button/button_example.js b/src-docs/src/views/button/button_example.js index bd9eec091bd..176050bbb2d 100644 --- a/src-docs/src/views/button/button_example.js +++ b/src-docs/src/views/button/button_example.js @@ -91,14 +91,14 @@ const buttonToggleSnippet = [ `, ` + > `, ` @@ -330,7 +330,7 @@ export const ButtonExample = { the visual differences for on and off. Though there are two exclusive situations to consider.

-
    +
    1. If your button changes its readable text, via children or aria-label, then there is no @@ -339,11 +339,10 @@ export const ButtonExample = {
    2. If your button only changes the visual{' '} appearance, you must add aria-pressed passing a - boolean for the on and off states. EuiButton{' '} - provides a helper prop for this called{' '} - isSelected. + boolean for the on and off states. All EUI button types provide a + helper prop for this called isSelected.
    3. -
+ Date: Thu, 15 Oct 2020 15:55:03 -0400 Subject: [PATCH 32/34] cl --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de2eb5ed089..ce69ef48da0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ## [`master`](https://github.com/elastic/eui/tree/master) +- Added `minWidth` prop to `EuiButton` ([4056](https://github.com/elastic/eui/pull/4056)) +- Added `isSelected` prop to easily turn `EuiButton`, `EuiButtonEmpty`, and `EuiButtonIcon` into toggle buttons ([4056](https://github.com/elastic/eui/pull/4056)) +- Updated `EuiButtonGroup` props and render for better accessibility ([4056](https://github.com/elastic/eui/pull/4056)) + +**Breaking changes** + +- Removed `EuiToggle` and `EuiButtonToggle` in favor of `aria-pressed` ([4056](https://github.com/elastic/eui/pull/4056)) +- Updated `legend` and `idSelected` props of `EuiButtonGroup` to be required ([4056](https://github.com/elastic/eui/pull/4056)) + **Theme: Amsterdam** - Tightened `line-height` for some `EuiTitle` sizes ([4133](https://github.com/elastic/eui/pull/4133)) From d8b6f1ec88ec7ac99581724cb30a91b8039defc7 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Thu, 15 Oct 2020 16:25:15 -0400 Subject: [PATCH 33/34] typo Co-authored-by: Michail Yasonik --- src-docs/src/views/button/button_example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-docs/src/views/button/button_example.js b/src-docs/src/views/button/button_example.js index 176050bbb2d..b8a754a86ba 100644 --- a/src-docs/src/views/button/button_example.js +++ b/src-docs/src/views/button/button_example.js @@ -327,7 +327,7 @@ export const ButtonExample = { You can create a toggle style button with any button type like the standard EuiButton, EuiButtonEmpty , or EuiButtonIcon. Use state management to handle - the visual differences for on and off. Though there are two + the visual differences for on and off. Though there are two{' '} exclusive situations to consider.

    From 49a78c74642d8e77c56b49c5c9115b1f1c99015a Mon Sep 17 00:00:00 2001 From: cchaos Date: Fri, 16 Oct 2020 11:09:12 -0400 Subject: [PATCH 34/34] Simplify `isSelected` on EuiButtonGroupButton --- src/components/button/button_group/button_group_button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/button/button_group/button_group_button.tsx b/src/components/button/button_group/button_group_button.tsx index 1d1594928d0..dea959ed661 100644 --- a/src/components/button/button_group/button_group_button.tsx +++ b/src/components/button/button_group/button_group_button.tsx @@ -99,7 +99,7 @@ export const EuiButtonGroupButton: FunctionComponent = ({ elementProps = { ...elementProps, id, - 'aria-pressed': isSelected, + isSelected, onClick: () => onChange(id), }; }