diff --git a/res/css/views/elements/_StyledCheckbox.scss b/res/css/views/elements/_StyledCheckbox.scss index ee91c69846..df0b8c6d94 100644 --- a/res/css/views/elements/_StyledCheckbox.scss +++ b/res/css/views/elements/_StyledCheckbox.scss @@ -48,6 +48,8 @@ limitations under the License. border-radius: $border-radius; img { + display: none; + height: 100%; width: 100%; filter: invert(100%); @@ -57,6 +59,10 @@ limitations under the License. &:checked + label > .mx_Checkbox_background { background: $accent-color; border-color: $accent-color; + + img { + display: block; + } } & + label > *:not(.mx_Checkbox_background) { diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.tsx similarity index 67% rename from src/components/views/elements/AccessibleButton.js rename to src/components/views/elements/AccessibleButton.tsx index d708a44ab2..3a542387c5 100644 --- a/src/components/views/elements/AccessibleButton.js +++ b/src/components/views/elements/AccessibleButton.tsx @@ -15,9 +15,34 @@ */ import React from 'react'; -import PropTypes from 'prop-types'; import {Key} from '../../../Keyboard'; +import classnames from 'classnames'; + +/** + * children: React's magic prop. Represents all children given to the element. + * element: (optional) The base element type. "div" by default. + * onClick: (required) Event handler for button activation. Should be + * implemented exactly like a normal onClick handler. + */ +interface IProps extends React.InputHTMLAttributes { + inputRef?: React.Ref; + element?: string; + // The kind of button, similar to how Bootstrap works. + // See available classes for AccessibleButton for options. + kind?: string; + // The ARIA role + role?: string; + // The tabIndex + tabIndex?: number; + disabled?: boolean; + className?: string; + onClick?(e?: React.MouseEvent | React.KeyboardEvent): void; +}; + +interface IAccessibleButtonProps extends React.InputHTMLAttributes { + ref?: React.Ref; +} /** * AccessibleButton is a generic wrapper for any element that should be treated @@ -27,11 +52,20 @@ import {Key} from '../../../Keyboard'; * @param {Object} props react element properties * @returns {Object} rendered react */ -export default function AccessibleButton(props) { - const {element, onClick, children, kind, disabled, ...restProps} = props; +export default function AccessibleButton({ + element, + onClick, + children, + kind, + disabled, + inputRef, + className, + ...restProps +}: IProps) { + const newProps: IAccessibleButtonProps = restProps; if (!disabled) { - restProps.onClick = onClick; + newProps.onClick = onClick; // We need to consume enter onKeyDown and space onKeyUp // otherwise we are risking also activating other keyboard focusable elements // that might receive focus as a result of the AccessibleButtonClick action @@ -39,7 +73,7 @@ export default function AccessibleButton(props) { // And divs which we report as role button to assistive technologies. // Browsers handle space and enter keypresses differently and we are only adjusting to the // inconsistencies here - restProps.onKeyDown = function(e) { + newProps.onKeyDown = (e) => { if (e.key === Key.ENTER) { e.stopPropagation(); e.preventDefault(); @@ -50,7 +84,7 @@ export default function AccessibleButton(props) { e.preventDefault(); } }; - restProps.onKeyUp = function(e) { + newProps.onKeyUp = (e) => { if (e.key === Key.SPACE) { e.stopPropagation(); e.preventDefault(); @@ -64,53 +98,22 @@ export default function AccessibleButton(props) { } // Pass through the ref - used for keyboard shortcut access to some buttons - restProps.ref = restProps.inputRef; - delete restProps.inputRef; + newProps.ref = inputRef; - restProps.className = (restProps.className ? restProps.className + " " : "") + "mx_AccessibleButton"; - - if (kind) { - // We apply a hasKind class to maintain backwards compatibility with - // buttons which might not know about kind and break - restProps.className += " mx_AccessibleButton_hasKind mx_AccessibleButton_kind_" + kind; - } - - if (disabled) { - restProps.className += " mx_AccessibleButton_disabled"; - restProps["aria-disabled"] = true; - } + newProps.className = classnames( + "mx_AccessibleButton", + className, + { + "mx_AccessibleButton_hasKind": kind, + [`mx_AccessibleButton_kind_${kind}`]: kind, + "mx_AccessibleButton_disabled": disabled, + }, + ); + // React.createElement expects InputHTMLAttributes return React.createElement(element, restProps, children); } -/** - * children: React's magic prop. Represents all children given to the element. - * element: (optional) The base element type. "div" by default. - * onClick: (required) Event handler for button activation. Should be - * implemented exactly like a normal onClick handler. - */ -AccessibleButton.propTypes = { - children: PropTypes.node, - inputRef: PropTypes.oneOfType([ - // Either a function - PropTypes.func, - // Or the instance of a DOM native element - PropTypes.shape({ current: PropTypes.instanceOf(Element) }), - ]), - element: PropTypes.string, - onClick: PropTypes.func.isRequired, - - // The kind of button, similar to how Bootstrap works. - // See available classes for AccessibleButton for options. - kind: PropTypes.string, - // The ARIA role - role: PropTypes.string, - // The tabIndex - tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - - disabled: PropTypes.bool, -}; - AccessibleButton.defaultProps = { element: 'div', role: 'button', diff --git a/src/components/views/elements/SettingsFlag.js b/src/components/views/elements/SettingsFlag.tsx similarity index 53% rename from src/components/views/elements/SettingsFlag.js rename to src/components/views/elements/SettingsFlag.tsx index 15f17805a8..bda15ebaab 100644 --- a/src/components/views/elements/SettingsFlag.js +++ b/src/components/views/elements/SettingsFlag.tsx @@ -16,63 +16,78 @@ limitations under the License. */ import React from "react"; -import PropTypes from 'prop-types'; -import createReactClass from 'create-react-class'; import SettingsStore from "../../../settings/SettingsStore"; import { _t } from '../../../languageHandler'; import ToggleSwitch from "./ToggleSwitch"; +import StyledCheckbox from "./StyledCheckbox"; -export default createReactClass({ - displayName: 'SettingsFlag', - propTypes: { - name: PropTypes.string.isRequired, - level: PropTypes.string.isRequired, - roomId: PropTypes.string, // for per-room settings - label: PropTypes.string, // untranslated - onChange: PropTypes.func, - isExplicit: PropTypes.bool, - }, +interface IProps { + // The setting must be a boolean + name: string; + level: string; + roomId?: string; // for per-room settings + label?: string; // untranslated + isExplicit?: boolean; + // XXX: once design replaces all toggles make this the default + useCheckbox?: boolean; + onChange?(checked: boolean): void; +} - getInitialState: function() { - return { +interface IState { + value: boolean; +} + +export default class SettingsFlag extends React.Component { + constructor(props: IProps) { + super(props); + + this.state = { value: SettingsStore.getValueAt( this.props.level, this.props.name, this.props.roomId, this.props.isExplicit, ), - }; - }, - - onChange: function(checked) { - if (this.props.group && !checked) return; + } + } + private onChange = (checked: boolean): void => { this.save(checked); this.setState({ value: checked }); if (this.props.onChange) this.props.onChange(checked); - }, + } - save: function(val = undefined) { + private checkBoxOnChange = (e: React.ChangeEvent) => { + this.onChange(e.target.checked); + } + + private save = (val?: boolean): void => { return SettingsStore.setValue( this.props.name, this.props.roomId, this.props.level, val !== undefined ? val : this.state.value, ); - }, + } - render: function() { + public render() { const canChange = SettingsStore.canSetValue(this.props.name, this.props.roomId, this.props.level); let label = this.props.label; if (!label) label = SettingsStore.getDisplayName(this.props.name, this.props.level); else label = _t(label); - return ( -
- {label} - -
- ); - }, -}); + if (this.props.useCheckbox) { + return + {label} + ; + } else { + return ( +
+ {label} + +
+ ); + } + } +} diff --git a/src/components/views/elements/ToggleSwitch.js b/src/components/views/elements/ToggleSwitch.tsx similarity index 83% rename from src/components/views/elements/ToggleSwitch.js rename to src/components/views/elements/ToggleSwitch.tsx index bea1a85555..7b690899ac 100644 --- a/src/components/views/elements/ToggleSwitch.js +++ b/src/components/views/elements/ToggleSwitch.tsx @@ -16,13 +16,23 @@ limitations under the License. */ import React from "react"; -import PropTypes from "prop-types"; import classNames from "classnames"; import * as sdk from "../../../index"; +interface IProps { + // Whether or not this toggle is in the 'on' position. + checked: boolean; + + // Whether or not the user can interact with the switch + disabled: boolean; + + // Called when the checked state changes. First argument will be the new state. + onChange(checked: boolean): void; +}; + // Controlled Toggle Switch element, written with Accessibility in mind -const ToggleSwitch = ({checked, disabled=false, onChange, ...props}) => { - const _onClick = (e) => { +export default ({checked, disabled = false, onChange, ...props}: IProps) => { + const _onClick = () => { if (disabled) return; onChange(!checked); }; @@ -46,16 +56,3 @@ const ToggleSwitch = ({checked, disabled=false, onChange, ...props}) => { ); }; - -ToggleSwitch.propTypes = { - // Whether or not this toggle is in the 'on' position. - checked: PropTypes.bool.isRequired, - - // Whether or not the user can interact with the switch - disabled: PropTypes.bool, - - // Called when the checked state changes. First argument will be the new state. - onChange: PropTypes.func.isRequired, -}; - -export default ToggleSwitch; diff --git a/src/components/views/rooms/RoomSublist2.tsx b/src/components/views/rooms/RoomSublist2.tsx index bc90f739cb..0df9c2ad73 100644 --- a/src/components/views/rooms/RoomSublist2.tsx +++ b/src/components/views/rooms/RoomSublist2.tsx @@ -233,7 +233,7 @@ export default class RoomSublist2 extends React.Component { tabIndex={tabIndex} className={"mx_RoomSublist2_headerText"} role="treeitem" - aria-level="1" + aria-level={1} > {this.props.label}