diff --git a/CHANGELOG.md b/CHANGELOG.md index 38336ba4ba5..f83a0f8adea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Fixed `EuiSuperDatePicker` to update `asyncInterval.isStopped` on a `isPaused` prop change. ([#2250](https://github.com/elastic/eui/pull/2250)) - Converted table, popover, buttons, pagination, outside click detector, focus trap, context menu, and panel to TypeScript ([#2212](https://github.com/elastic/eui/pull/2212)) +**Reverts** + +- Revert conversion of `EuiSwitch` to `button[role=switch]` and TypeScript ([#2255](https://github.com/elastic/eui/pull/2255)) + ## [`13.5.0`](https://github.com/elastic/eui/tree/v13.5.0) - Fixed `logoCloudEnterprise`, `logoLogging`, and `logoSecurity` SVGs in `EuiIcon` to be center aligned ([#2246](https://github.com/elastic/eui/pull/2246)) diff --git a/src-docs/src/views/form_controls/switch.js b/src-docs/src/views/form_controls/switch.js index f465980af56..2d8f061679c 100644 --- a/src-docs/src/views/form_controls/switch.js +++ b/src-docs/src/views/form_controls/switch.js @@ -11,9 +11,9 @@ export default class extends Component { }; } - onChange = () => { + onChange = e => { this.setState({ - checked: !this.state.checked, + checked: e.target.checked, }); }; diff --git a/src/components/form/_variables.scss b/src/components/form/_variables.scss index e8bc6ece33a..eae29dabd11 100644 --- a/src/components/form/_variables.scss +++ b/src/components/form/_variables.scss @@ -21,5 +21,4 @@ $euiFormBorderDisabledColor: transparentize($euiColorFullShade, .92) !default; $euiFormCustomControlDisabledIconColor: shadeOrTint($euiColorMediumShade, 38%, 48.5%) !default; // exact 508c foreground for $euiColorLightShade $euiFormControlDisabledColor: $euiColorMediumShade !default; $euiFormControlBoxShadow: 0 1px 1px -1px transparentize($euiShadowColor, .8), 0 3px 2px -2px transparentize($euiShadowColor, .8); -$euiFormInputGroupLabelBackground: shadeOrTint($euiFormBackgroundDisabledColor, 0, 3%); -$euiSwitchOffColor: lightOrDarkTheme(transparentize($euiColorMediumShade, .8), transparentize($euiColorMediumShade, .3)); +$euiFormInputGroupLabelBackground: shadeOrTint($euiFormBackgroundDisabledColor, 0, 3%); \ No newline at end of file diff --git a/src/components/form/index.d.ts b/src/components/form/index.d.ts index a31e554069b..366419d36f9 100644 --- a/src/components/form/index.d.ts +++ b/src/components/form/index.d.ts @@ -9,6 +9,7 @@ import { CommonProps } from '../common'; /// /// /// +/// /// import { FunctionComponent, FormHTMLAttributes, ReactNode } from 'react'; diff --git a/src/components/form/switch/__snapshots__/switch.test.js.snap b/src/components/form/switch/__snapshots__/switch.test.js.snap new file mode 100644 index 00000000000..7d5d582e468 --- /dev/null +++ b/src/components/form/switch/__snapshots__/switch.test.js.snap @@ -0,0 +1,81 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiSwitch assigns automatically generated ID to label 1`] = ` +
+ + + + + + + + +
+`; + +exports[`EuiSwitch is rendered 1`] = ` +
+ + + + + + + + +
+`; diff --git a/src/components/form/switch/__snapshots__/switch.test.tsx.snap b/src/components/form/switch/__snapshots__/switch.test.tsx.snap deleted file mode 100644 index b05767ccc4d..00000000000 --- a/src/components/form/switch/__snapshots__/switch.test.tsx.snap +++ /dev/null @@ -1,97 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EuiSwitch assigns automatically generated ID to label 1`] = ` -
- - -
-`; - -exports[`EuiSwitch is rendered 1`] = ` -
- - -
-`; diff --git a/src/components/form/switch/_switch.scss b/src/components/form/switch/_switch.scss index a143b66ffee..cea8270aa94 100644 --- a/src/components/form/switch/_switch.scss +++ b/src/components/form/switch/_switch.scss @@ -4,62 +4,29 @@ min-height: $euiSwitchHeight; .euiSwitch__label { - cursor: pointer; padding-left: $euiSizeS; line-height: $euiSwitchHeight; font-size: $euiFontSizeS; vertical-align: middle; } - .euiSwitch__button { - line-height: 0; // ensures button takes height of switch inside - - &:focus .euiSwitch__thumb { - @include euiCustomControlFocused; - } - - &:disabled { - &:hover, - ~ .euiSwitch__label:hover { - cursor: not-allowed; - } - - .euiSwitch__body { - background-color: $euiSwitchOffColor; - } - - .euiSwitch__thumb { - @include euiCustomControlDisabled; - background-color: $euiSwitchOffColor; - } - - .euiSwitch__icon { - fill: $euiFormCustomControlDisabledIconColor; - } - - + .euiSwitch__label { - color: $euiFormControlDisabledColor; - } - } - - &[aria-checked='false'] { - .euiSwitch__body { - background-color: $euiSwitchOffColor; - } - - // When input is not checked, we shift around the positioning of the thumb and the icon - .euiSwitch__thumb { // move the thumb left - left: 0; - } + /** + * 1. The input is "hidden" but still focusable. + * 2. Make sure it's still hidden when [disabled]. + */ + .euiSwitch__input, + .euiSwitch__input[disabled] /* 2 */ { + position: absolute; + opacity: 0; /* 1 */ + width: 100%; + height: 100%; + cursor: pointer; + } - .euiSwitch__icon { // move the icon right - right: -$euiSizeS; + .euiSwitch__input:focus + .euiSwitch__body { - &.euiSwitch__icon--checked { - right: auto; - left: -($euiSwitchWidth - ($euiSwitchThumbSize / 2)); - } - } + .euiSwitch__thumb { + @include euiCustomControlFocused; } } @@ -110,13 +77,64 @@ fill: $euiColorEmptyShade; } - &:hover .euiSwitch__button { - &:not(:disabled) .euiSwitch__thumb { - transform: scale(1.05); + /** + * The thumb is slightly scaled when in use, unless it's disabled. + */ + &:hover { + .euiSwitch__input:not(:disabled) ~ .euiSwitch__body { + .euiSwitch__thumb { + transform: scale(1.05); + } } + } - &:active .euiSwitch__thumb { + &:active { + .euiSwitch__thumb { transform: scale(.95); } } + + .euiSwitch__input:disabled:hover { + cursor: not-allowed; + } + + .euiSwitch__input:disabled ~ .euiSwitch__body, + .euiSwitch__input:checked:disabled ~ .euiSwitch__body { + background-color: lightOrDarkTheme(transparentize($euiColorMediumShade, .8), transparentize($euiColorMediumShade, .3)); + + .euiSwitch__thumb { + @include euiCustomControlDisabled; + background-color: lightOrDarkTheme(transparentize($euiColorMediumShade, .8), transparentize($euiColorMediumShade, .3)); + } + + .euiSwitch__icon { + fill: $euiFormCustomControlDisabledIconColor; + } + + + label { + color: $euiFormControlDisabledColor; + } + } + + .euiSwitch__input:not(:checked):not(:disabled) ~ .euiSwitch__body { + background-color: lightOrDarkTheme(transparentize($euiColorMediumShade, .8), transparentize($euiColorMediumShade, .3)); + } + + /** + * When input is not checked, we shift around the positioning of sibling/child selectors. + */ + .euiSwitch__input:not(:checked) ~ .euiSwitch__body { + .euiSwitch__thumb { + left: 0; + } + + .euiSwitch__icon { + right: -$euiSizeS; + + &.euiSwitch__icon--checked { + right: auto; + left: -($euiSwitchWidth - ($euiSwitchThumbSize / 2)); + } + } + } } diff --git a/src/components/form/switch/index.d.ts b/src/components/form/switch/index.d.ts new file mode 100644 index 00000000000..caaea6c1bdc --- /dev/null +++ b/src/components/form/switch/index.d.ts @@ -0,0 +1,15 @@ +import { CommonProps } from '../../common'; + +import { FunctionComponent, InputHTMLAttributes, ReactNode } from 'react'; + +declare module '@elastic/eui' { + /** + * @see './switch.js' + */ + export type EuiSwitchProps = CommonProps & + InputHTMLAttributes & { + label?: ReactNode; + }; + + export const EuiSwitch: FunctionComponent; +} diff --git a/src/components/form/switch/index.js b/src/components/form/switch/index.js new file mode 100644 index 00000000000..893cd7f7f6c --- /dev/null +++ b/src/components/form/switch/index.js @@ -0,0 +1 @@ +export { EuiSwitch } from './switch'; diff --git a/src/components/form/switch/index.ts b/src/components/form/switch/index.ts deleted file mode 100644 index 6c4ce9a8635..00000000000 --- a/src/components/form/switch/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { EuiSwitch, EuiSwitchEvent, EuiSwitchProps } from './switch'; diff --git a/src/components/form/switch/switch.js b/src/components/form/switch/switch.js new file mode 100644 index 00000000000..ab16f30bcaa --- /dev/null +++ b/src/components/form/switch/switch.js @@ -0,0 +1,85 @@ +import React, { Component } from 'react'; + +import PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import makeId from '../../form/form_row/make_id'; +import { EuiIcon } from '../../icon'; + +export class EuiSwitch extends Component { + constructor(props) { + super(props); + + this.state = { + switchId: props.id || makeId(), + }; + } + + render() { + const { + label, + id, + name, + checked, + disabled, + compressed, + onChange, + className, + ...rest + } = this.props; + + const { switchId } = this.state; + + const classes = classNames( + 'euiSwitch', + { + 'euiSwitch--compressed': compressed, + }, + className + ); + + return ( +
+ + + + + + + + + + + + {label && ( + + )} +
+ ); + } +} + +EuiSwitch.propTypes = { + name: PropTypes.string, + id: PropTypes.string, + label: PropTypes.node, + checked: PropTypes.bool, + onChange: PropTypes.func, + disabled: PropTypes.bool, + compressed: PropTypes.bool, +}; diff --git a/src/components/form/switch/switch.test.tsx b/src/components/form/switch/switch.test.js similarity index 65% rename from src/components/form/switch/switch.test.tsx rename to src/components/form/switch/switch.test.js index a564df98ca1..50927c7fa6e 100644 --- a/src/components/form/switch/switch.test.tsx +++ b/src/components/form/switch/switch.test.js @@ -4,25 +4,17 @@ import { requiredProps } from '../../../test/required_props'; import { EuiSwitch } from './switch'; -const props = { - checked: false, - label: 'Label', - onChange: () => {}, -}; - jest.mock('../form_row/make_id', () => () => 'generated-id'); describe('EuiSwitch', () => { test('is rendered', () => { - const component = render( - - ); + const component = render(); expect(component).toMatchSnapshot(); }); test('assigns automatically generated ID to label', () => { - const component = render(); + const component = render(); expect(component).toMatchSnapshot(); }); diff --git a/src/components/form/switch/switch.tsx b/src/components/form/switch/switch.tsx deleted file mode 100644 index 334b7394bb6..00000000000 --- a/src/components/form/switch/switch.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { - ButtonHTMLAttributes, - FunctionComponent, - ReactNode, - useState, -} from 'react'; -import classNames from 'classnames'; - -import { CommonProps, Omit } from '../../common'; -import makeId from '../../form/form_row/make_id'; -import { EuiIcon } from '../../icon'; - -export type EuiSwitchEvent = React.BaseSyntheticEvent< - React.MouseEvent, - HTMLButtonElement, - EventTarget & { - checked: boolean; - } ->; - -export type EuiSwitchProps = CommonProps & - Omit, 'onChange'> & { - label: ReactNode; - checked: boolean; - onChange: (event: EuiSwitchEvent) => void; - disabled?: boolean; - compressed?: boolean; - }; - -export const EuiSwitch: FunctionComponent = ({ - label, - id, - name, - checked, - disabled, - compressed, - onChange, - className, - ...rest -}) => { - const [switchId] = useState(id || makeId()); - - const onClick = (e: React.MouseEvent) => { - const event = (e as unknown) as EuiSwitchEvent; - event.target.checked = !checked; - onChange(event); - }; - - const classes = classNames( - 'euiSwitch', - { - 'euiSwitch--compressed': compressed, - }, - className - ); - - return ( -
- - - -
- ); -}; diff --git a/src/global_styling/reset/_reset.scss b/src/global_styling/reset/_reset.scss index 26d7b809ea5..0824f6a834e 100644 --- a/src/global_styling/reset/_reset.scss +++ b/src/global_styling/reset/_reset.scss @@ -71,11 +71,6 @@ body { *:focus { outline: none; - - // sass-lint:disable no-vendor-prefixes - &::-moz-focus-inner { - border: none; - } } a { @@ -141,4 +136,4 @@ hr { fieldset { min-inline-size: auto; -} +} \ No newline at end of file