Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Commit

Permalink
selectable button
Browse files Browse the repository at this point in the history
  • Loading branch information
Sugan G authored and Sugan G committed Oct 16, 2023
1 parent 5fb3c90 commit 0270db2
Show file tree
Hide file tree
Showing 23 changed files with 265 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ exports[`ActionHeader correctly applies the theme context className 1`] = `
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.close"
type="button"
Expand Down Expand Up @@ -75,6 +76,7 @@ exports[`ActionHeader correctly applies the theme context className 1`] = `
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.maximize"
type="button"
Expand Down Expand Up @@ -115,6 +117,7 @@ exports[`ActionHeader should render an action header with back and close buttons
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.close"
type="button"
Expand All @@ -141,6 +144,7 @@ exports[`ActionHeader should render an action header with back and close buttons
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.back"
type="button"
Expand Down Expand Up @@ -174,6 +178,7 @@ exports[`ActionHeader should render an action header with back button and title
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.back"
type="button"
Expand Down Expand Up @@ -205,6 +210,7 @@ exports[`ActionHeader should render an action header with close button and title
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.close"
type="button"
Expand Down Expand Up @@ -233,6 +239,7 @@ exports[`ActionHeader should render an action header with custom button and titl
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Custom Button"
type="button"
Expand Down Expand Up @@ -274,6 +281,7 @@ exports[`ActionHeader should render an action header with maximize button and ti
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.maximize"
type="button"
Expand Down Expand Up @@ -308,6 +316,7 @@ exports[`ActionHeader should render an action header with minimize button and ti
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.minimize"
type="button"
Expand All @@ -334,6 +343,7 @@ exports[`ActionHeader should render an action header with multiple custom button
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Custom Button"
type="button"
Expand All @@ -345,6 +355,7 @@ exports[`ActionHeader should render an action header with multiple custom button
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Custom Button"
type="button"
Expand Down Expand Up @@ -379,6 +390,7 @@ exports[`ActionHeader should render an action header with next and previous butt
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.previous"
type="button"
Expand All @@ -397,6 +409,7 @@ exports[`ActionHeader should render an action header with next and previous butt
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.next"
type="button"
Expand Down Expand Up @@ -444,6 +457,7 @@ exports[`ActionHeader should render an action header with title, enabled next bu
isDisabled={true}
isIconOnly={true}
isReversed={false}
isSelectable={false}
text="Terra.actionHeader.previous"
type="button"
variant="utility"
Expand All @@ -461,6 +475,7 @@ exports[`ActionHeader should render an action header with title, enabled next bu
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.next"
type="button"
Expand Down Expand Up @@ -498,6 +513,7 @@ exports[`ActionHeader should render an action header with title, enabled previou
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onClick={[Function]}
text="Terra.actionHeader.previous"
type="button"
Expand All @@ -516,6 +532,7 @@ exports[`ActionHeader should render an action header with title, enabled previou
isDisabled={true}
isIconOnly={true}
isReversed={false}
isSelectable={false}
text="Terra.actionHeader.next"
type="button"
variant="utility"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ exports[`Alert of type success with an action button text content should render
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onClick={[MockFunction]}
text="Action"
type="button"
Expand Down Expand Up @@ -967,6 +968,7 @@ exports[`Dismissible Alert of type custom with action button, custom title and t
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onClick={[MockFunction]}
text="Action"
type="button"
Expand All @@ -979,6 +981,7 @@ exports[`Dismissible Alert of type custom with action button, custom title and t
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onClick={[MockFunction]}
text="Terra.alert.dismiss"
type="button"
Expand Down Expand Up @@ -1040,6 +1043,7 @@ exports[`Dismissible Alert that includes actions section should render an alert
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onClick={[MockFunction]}
text="Terra.alert.dismiss"
type="button"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ exports[`correctly applies the theme context className 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
Expand Down Expand Up @@ -88,6 +89,7 @@ exports[`correctly applies the theme context className 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ exports[`correctly applies the theme context className 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
Expand Down Expand Up @@ -65,6 +66,7 @@ exports[`should render a default component 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
Expand All @@ -84,6 +86,7 @@ exports[`should render as disabled 1`] = `
isDisabled={true}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
Expand All @@ -108,6 +111,7 @@ exports[`should render with icon only 1`] = `
isDisabled={false}
isIconOnly={true}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
Expand All @@ -127,6 +131,7 @@ exports[`should render with onBlur 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
Expand All @@ -146,6 +151,7 @@ exports[`should render with onClick 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
Expand All @@ -166,6 +172,7 @@ exports[`should render with onFocus 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
Expand All @@ -185,6 +192,7 @@ exports[`should render with onKeyDown 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
Expand All @@ -204,6 +212,7 @@ exports[`should render with onKeyUp 1`] = `
isDisabled={false}
isIconOnly={false}
isReversed={false}
isSelectable={false}
onBlur={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
Expand Down
31 changes: 29 additions & 2 deletions packages/terra-button/src/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ const propTypes = {
* Sets the button variant. One of `neutral`, `emphasis`, `ghost`, `de-emphasis`, `action` or `utility`.
*/
variant: PropTypes.oneOf([ButtonVariants.NEUTRAL, ButtonVariants.EMPHASIS, ButtonVariants.GHOST, ButtonVariants['DE-EMPHASIS'], ButtonVariants.ACTION, ButtonVariants.UTILITY]),
/**
* @private
* Whether or not the button can be selected (toggleable button).
*/
isSelectable: PropTypes.bool,
/**
* @private
* Callback function when the state changes. Parameters are (event, toggleState).
*/
onChange: PropTypes.func,
};

const defaultProps = {
Expand All @@ -113,18 +123,20 @@ const defaultProps = {
title: undefined,
type: ButtonTypes.BUTTON,
variant: ButtonVariants.NEUTRAL,
isSelectable: false,
};

class Button extends React.Component {
constructor(props) {
super(props);
this.state = { active: false, focused: false };
this.state = { active: false, focused: false, isSelected: false };
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
this.handleOnBlur = this.handleOnBlur.bind(this);
this.handleFocus = this.handleFocus.bind(this);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);

this.shouldShowFocus = true;
}
Expand All @@ -144,6 +156,12 @@ class Button extends React.Component {
}
}

handleOnChange(event) {
if (this.props.onChange && this.props.isSelectable) {
this.props.onChange(event, this.state.isSelected);
}
}

handleClick(event) {
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Button#Clicking_and_focus
// Button on Firefox, Safari and IE running on OS X does not receive focus when clicked.
Expand All @@ -154,6 +172,8 @@ class Button extends React.Component {
this.shouldShowFocus = true;
}

this.handleOnChange(event);

if (this.props.onClick) {
this.props.onClick(event);
}
Expand All @@ -174,7 +194,7 @@ class Button extends React.Component {

// Add focus styles for keyboard navigation
if (event.nativeEvent.keyCode === KeyCode.KEY_SPACE || event.nativeEvent.keyCode === KeyCode.KEY_RETURN) {
this.setState({ focused: true });
this.setState(prevState => ({ focused: true, isSelected: !prevState.isSelected }));
}

if (this.props.onKeyDown) {
Expand Down Expand Up @@ -212,6 +232,7 @@ class Button extends React.Component {
if (this.props.onMouseDown) {
this.props.onMouseDown(event);
}
this.setState(prevState => ({ isSelected: !prevState.isSelected }));

// See https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus#Notes
// If you call HTMLElement.focus() from a mousedown event handler, you must call event.preventDefault() to keep the focus from leaving the HTMLElement.
Expand Down Expand Up @@ -239,6 +260,8 @@ class Button extends React.Component {
onKeyUp,
refCallback,
title,
isSelectable,
onChange,
...customProps
} = this.props;

Expand All @@ -255,6 +278,7 @@ class Button extends React.Component {
{ compact: isCompact },
{ 'is-active': this.state.active && !isDisabled },
{ 'is-focused': this.state.focused && !isDisabled },
{ 'is-selected': isSelectable && this.state.isSelected && !isDisabled },
theme.className,
]),
customProps.className,
Expand Down Expand Up @@ -306,6 +330,8 @@ class Button extends React.Component {
if (href) {
ComponentType = 'a';
customProps.role = 'button';
} else if (isSelectable) {
customProps.role = 'checkbox';
}

return (
Expand All @@ -326,6 +352,7 @@ class Button extends React.Component {
onFocus={this.handleFocus}
href={href}
ref={refCallback}
aria-checked={isSelectable ? this.state.isSelected : undefined}
>
{buttonLabel}
</ComponentType>
Expand Down
Loading

0 comments on commit 0270db2

Please sign in to comment.