-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #429 from dcos-labs/mp/feat/text-input-with-buttons
Add components to support text inputs with buttons inside
- Loading branch information
Showing
16 changed files
with
1,097 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import * as React from "react"; | ||
import { css, cx } from "emotion"; | ||
import { buttonReset } from "../../shared/styles/styleUtils"; | ||
|
||
const pointerCursor = css` | ||
cursor: pointer; | ||
`; | ||
|
||
const outlineNone = css` | ||
&:focus { | ||
outline: none; | ||
} | ||
`; | ||
|
||
// Replicates default browser focus ring styles. | ||
// | ||
// The media query targets Webkit browsers, which can | ||
// more accurately replicate the native focus ring style | ||
const keyboardFocus = css` | ||
&:focus > div { | ||
outline-color: Highlight; | ||
outline-width: thin; | ||
@media (-webkit-min-device-pixel-ratio: 0) { | ||
outline-color: -webkit-focus-ring-color; | ||
outline-style: auto; | ||
outline-width: unset; | ||
} | ||
} | ||
`; | ||
|
||
const ResetButton = (props: React.HTMLAttributes<HTMLButtonElement>) => { | ||
const { children, className, ...other } = props; | ||
const classNames = cx( | ||
buttonReset, | ||
className, | ||
outlineNone, | ||
keyboardFocus, | ||
pointerCursor | ||
); | ||
|
||
return ( | ||
<button className={classNames} {...other}> | ||
<div className={outlineNone} tabIndex={-1}> | ||
{children} | ||
</div> | ||
</button> | ||
); | ||
}; | ||
|
||
export default ResetButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import * as React from "react"; | ||
import { cx } from "emotion"; | ||
import TextInputWithIcon, { TextInputWithIconProps } from "./TextInputWithIcon"; | ||
import FormFieldWrapper from "../../shared/components/FormFieldWrapper"; | ||
import { flex, padding, flexItem } from "../../shared/styles/styleUtils"; | ||
import { | ||
inputContainer, | ||
getInputAppearanceStyle | ||
} from "../../shared/styles/formStyles"; | ||
import { TextInputButtonProps } from "../../textInputButton/components/TextInputButton"; | ||
|
||
export interface TextInputWithButtonsProps | ||
extends Omit<TextInputWithIconProps, "iconEnd"> { | ||
/** | ||
* An array of TextInputButton components to render at the end of the input | ||
*/ | ||
buttons: Array<React.ReactElement<TextInputButtonProps>>; | ||
} | ||
|
||
class TextInputWithButtons extends TextInputWithIcon< | ||
TextInputWithButtonsProps, | ||
{} | ||
> { | ||
protected getInputElementProps() { | ||
let baseProps = super.getInputElementProps(); | ||
const { buttons, ...inputProps } = baseProps as TextInputWithButtonsProps; | ||
|
||
return inputProps; | ||
} | ||
|
||
protected getInputContent() { | ||
const inputAppearance = this.getInputAppearance(); | ||
|
||
return ( | ||
<FormFieldWrapper | ||
id={this.props.id} | ||
errors={this.props.errors} | ||
hintContent={this.props.hintContent} | ||
> | ||
{({ getValidationErrors, getHintContent, isValid, describedByIds }) => ( | ||
<React.Fragment> | ||
<div | ||
className={cx( | ||
flex(), | ||
padding("horiz", "s"), | ||
inputContainer, | ||
getInputAppearanceStyle(inputAppearance) | ||
)} | ||
> | ||
{this.getIconStartContent()} | ||
{this.getInputElement( | ||
[flexItem("grow"), padding("all", "none")], | ||
isValid, | ||
describedByIds | ||
)} | ||
{this.getButtons()} | ||
</div> | ||
{getHintContent} | ||
{getValidationErrors} | ||
</React.Fragment> | ||
)} | ||
</FormFieldWrapper> | ||
); | ||
} | ||
|
||
private getButtons() { | ||
if (!this.props.buttons.filter(Boolean)) { | ||
return; | ||
} | ||
|
||
return this.props.buttons.map((button, i) => ( | ||
// TODO: consider making a component for this wrapper span | ||
<span | ||
className={cx( | ||
flex({ align: "center", justify: "center" }), | ||
flexItem("shrink"), | ||
{ [padding("left", "s")]: i !== 0 } | ||
)} | ||
key={(React.isValidElement(button) && button.key) || i} | ||
> | ||
{button} | ||
</span> | ||
)); | ||
} | ||
} | ||
|
||
export default TextInputWithButtons; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
packages/textInput/stories/TextInputWithButtons.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import * as React from "react"; | ||
import { storiesOf } from "@storybook/react"; | ||
import { withReadme } from "storybook-readme"; | ||
import { TextInputWithButtons } from "../index"; | ||
import { inputStoryWrapper } from "../../../decorators/inputStoryWrapper"; | ||
import { SystemIcons } from "../../icons/dist/system-icons-enum"; | ||
import { TextInputButton } from "../../textInputButton"; | ||
import { Icon } from "../../icon"; | ||
|
||
const readme = require("../README.md"); | ||
|
||
const btnClickFn = () => { | ||
alert("button one clicked"); | ||
}; | ||
|
||
storiesOf("Forms/TextInputWithButtons", module) | ||
.addDecorator(withReadme([readme])) | ||
.addDecorator(inputStoryWrapper) | ||
.addParameters({ | ||
info: { | ||
propTables: [TextInputWithButtons, TextInputButton] | ||
} | ||
}) | ||
.add("one button", () => ( | ||
<TextInputWithButtons | ||
id="oneBtn" | ||
inputLabel="One button" | ||
buttons={[ | ||
<TextInputButton | ||
key={0} | ||
shape={SystemIcons.Close} | ||
onClick={btnClickFn} | ||
aria-label="Clear input" | ||
/> | ||
]} | ||
/> | ||
)) | ||
.add("two buttons", () => ( | ||
<TextInputWithButtons | ||
id="twoBtn" | ||
inputLabel="Two buttons" | ||
buttons={[ | ||
<TextInputButton | ||
key={0} | ||
shape={SystemIcons.Close} | ||
onClick={btnClickFn} | ||
aria-label="Clear input" | ||
/>, | ||
<TextInputButton | ||
key={1} | ||
shape={SystemIcons.Funnel} | ||
onClick={btnClickFn} | ||
aria-label="Activate filter" | ||
/> | ||
]} | ||
/> | ||
)) | ||
.add("with an icon", () => ( | ||
<TextInputWithButtons | ||
id="withIcon" | ||
inputLabel="With icon" | ||
iconStart={<Icon shape={SystemIcons.Search} />} | ||
buttons={[ | ||
<TextInputButton | ||
key={0} | ||
shape={SystemIcons.Close} | ||
onClick={btnClickFn} | ||
aria-label="Clear input" | ||
/> | ||
]} | ||
/> | ||
)) | ||
.add("with a custom colored icon", () => ( | ||
<TextInputWithButtons | ||
id="withIcon.colored" | ||
inputLabel="With colored icon" | ||
iconStart={<Icon shape={SystemIcons.Search} />} | ||
buttons={[ | ||
<TextInputButton | ||
key={0} | ||
color="red" | ||
shape={SystemIcons.Close} | ||
onClick={btnClickFn} | ||
aria-label="Clear input" | ||
/> | ||
]} | ||
/> | ||
)); |
Oops, something went wrong.