From 83bd61c08f2ed3dc7207ac4822ac150ab7cc7ecf Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Thu, 10 Oct 2019 14:44:02 -0500 Subject: [PATCH] feat(ToggleListInput): add required and labelAppearance - Added required prop to set if the listLabel should display a required asterisk. This is so that the input can be used in the same way as other form inputs - Add labelAppearance to control the style of the listLabel in a similar way that we style other input labels. --- .../components/ToggleInputList.tsx | 45 +- .../stories/ToggleInputList.stories.tsx | 48 +- .../tests/ToggleInputList.test.tsx | 26 + .../ToggleInputList.test.tsx.snap | 821 +++++++++++++++++- 4 files changed, 896 insertions(+), 44 deletions(-) diff --git a/packages/toggleInputList/components/ToggleInputList.tsx b/packages/toggleInputList/components/ToggleInputList.tsx index 56ef8495e..df1640c1a 100644 --- a/packages/toggleInputList/components/ToggleInputList.tsx +++ b/packages/toggleInputList/components/ToggleInputList.tsx @@ -7,10 +7,11 @@ import { fieldsetReset, legendReset, padding, - textWeight, visuallyHidden } from "../../shared/styles/styleUtils"; import { InputAppearance } from "../../shared/types/inputAppearance"; +import { getLabelStyle } from "../../shared/styles/formStyles"; +import { DangerText } from "../../index"; export interface ToggleInputProperties { appearance?: InputAppearance; @@ -57,23 +58,34 @@ export interface ToggleInputListProps { * How the text content of each option vertically aligns with it's related input */ vertAlign?: "center" | "top"; + /** + * If this entire input list is a required field + */ + required?: boolean; + /** + * Sets the current appearance of the label component. This defaults to InputAppearance.Standard, but supports `InputAppearance.Error` & `InputAppearance.Success` appearances as well. + */ + labelAppearance: InputAppearance; } class ToggleInputList extends React.PureComponent { public static defaultProps: Partial = { showListLabel: true, vertAlign: "center", - selectedItems: [] + selectedItems: [], + labelAppearance: InputAppearance.Standard }; constructor(props) { super(props); this.getSelectedItems = this.getSelectedItems.bind(this); + this.listLegendContent = this.listLegendContent.bind(this); + this.inputListItems = this.inputListItems.bind(this); } public render() { - const { listLabel, showListLabel, errors, hintContent, id } = this.props; + const { errors, hintContent, id, required } = this.props; return ( @@ -82,14 +94,10 @@ class ToggleInputList extends React.PureComponent { className={fieldsetReset} aria-invalid={!isValid} aria-describedby={describedByIds} + aria-required={required} + role="listbox" > - - {listLabel} - + {this.listLegendContent()}
    {this.inputListItems()}
{getHintContent} {getValidationErrors} @@ -99,6 +107,23 @@ class ToggleInputList extends React.PureComponent { ); } + private listLegendContent() { + const { labelAppearance, showListLabel, listLabel, required } = this.props; + const requiredContent = required ? ( + * + ) : null; + const hasError = labelAppearance === InputAppearance.Error; + const legendClassName = showListLabel + ? getLabelStyle(hasError) + : cx(visuallyHidden); + return ( + + {listLabel} + {requiredContent} + + ); + } + private inputListItems() { const { items } = this.props; diff --git a/packages/toggleInputList/stories/ToggleInputList.stories.tsx b/packages/toggleInputList/stories/ToggleInputList.stories.tsx index 8b0ef84c0..559eb9f44 100644 --- a/packages/toggleInputList/stories/ToggleInputList.stories.tsx +++ b/packages/toggleInputList/stories/ToggleInputList.stories.tsx @@ -122,4 +122,50 @@ storiesOf("Forms/ToggleInputList", module) /> )} - )); + )) + .add( + "required", + () => ( + + {({ changeHandler, selectedItems }) => ( + + )} + + ), + { + info: { + propTables: [ToggleInputList] + } + } + ) + .add( + "error with message", + () => ( + + {({ changeHandler, selectedItems }) => ( + + )} + + ), + { + info: { + propTables: [ToggleInputList] + } + } + ); diff --git a/packages/toggleInputList/tests/ToggleInputList.test.tsx b/packages/toggleInputList/tests/ToggleInputList.test.tsx index e7800501a..3727f25e0 100644 --- a/packages/toggleInputList/tests/ToggleInputList.test.tsx +++ b/packages/toggleInputList/tests/ToggleInputList.test.tsx @@ -4,6 +4,7 @@ import * as emotion from "emotion"; import { createSerializer } from "jest-emotion"; import toJson from "enzyme-to-json"; import { ToggleInputList } from "../"; +import { InputAppearance } from "../../shared/types/inputAppearance"; const options = [ { inputLabel: "Sample label", id: "id.1", value: "value.1" }, @@ -61,6 +62,31 @@ describe("ToggleInputList", () => { expect(toJson(component)).toMatchSnapshot(); }); + it("renders with required", () => { + const component = mount( + + ); + expect(toJson(component)).toMatchSnapshot(); + }); + + it("renders with error label appearance", () => { + const component = mount( + + ); + expect(toJson(component)).toMatchSnapshot(); + }); + it("calls onChange prop with the selected values", () => { const onChangeFn = jest.fn(); const component = mount( diff --git a/packages/toggleInputList/tests/__snapshots__/ToggleInputList.test.tsx.snap b/packages/toggleInputList/tests/__snapshots__/ToggleInputList.test.tsx.snap index 026bf24d5..3a98cb277 100644 --- a/packages/toggleInputList/tests/__snapshots__/ToggleInputList.test.tsx.snap +++ b/packages/toggleInputList/tests/__snapshots__/ToggleInputList.test.tsx.snap @@ -27,15 +27,19 @@ exports[`ToggleInputList renders default 1`] = ` height: 1px; } +.emotion-6 { + padding-top: 8px; +} + .emotion-0 { padding: 0; + display: block; + padding-top: 0; + margin-top: 0; + margin-bottom: 4px; font-weight: 500; } -.emotion-6 { - padding-top: 8px; -} - .emotion-2 { border-color: var(--themeBorder,#DADDE2); background-color: var(--themeBgPrimary,#FFFFFF); @@ -120,6 +124,7 @@ exports[`ToggleInputList renders default 1`] = ` }, ] } + labelAppearance="standard" listLabel="Sample legend" selectedItems={Array []} showListLabel={true} @@ -132,6 +137,7 @@ exports[`ToggleInputList renders default 1`] = ` aria-describedby="" aria-invalid={false} className="emotion-20" + role="listbox" > `; -exports[`ToggleInputList renders with errors 1`] = ` +exports[`ToggleInputList renders with error label appearance 1`] = ` .emotion-19 { list-style: none; margin-left: 0; @@ -348,11 +354,6 @@ exports[`ToggleInputList renders with errors 1`] = ` height: 1px; } -.emotion-0 { - padding: 0; - font-weight: 500; -} - .emotion-6 { padding-top: 8px; } @@ -434,6 +435,17 @@ exports[`ToggleInputList renders with errors 1`] = ` padding-top: 4px; } +.emotion-0 { + padding: 0; + display: block; + padding-top: 0; + margin-top: 0; + margin-bottom: 4px; + font-weight: 500; + color: var(--themeError,#EB293A); + fill: var(--themeError,#EB293A); +} + `; -exports[`ToggleInputList renders with hidden label 1`] = ` +exports[`ToggleInputList renders with errors 1`] = ` .emotion-19 { list-style: none; margin-left: 0; @@ -695,7 +709,7 @@ exports[`ToggleInputList renders with hidden label 1`] = ` padding-left: 0; } -.emotion-20 { +.emotion-23 { border: 0; margin: 0; padding: 0; @@ -717,6 +731,15 @@ exports[`ToggleInputList renders with hidden label 1`] = ` padding-top: 8px; } +.emotion-0 { + padding: 0; + display: block; + padding-top: 0; + margin-top: 0; + margin-bottom: 4px; + font-weight: 500; +} + .emotion-2 { border-color: var(--themeBorder,#DADDE2); background-color: var(--themeBgPrimary,#FFFFFF); @@ -780,21 +803,27 @@ exports[`ToggleInputList renders with hidden label 1`] = ` padding-left: 12px; } -.emotion-0 { - padding: 0; - font-weight: 500; - border: 0; - -webkit-clip: rect(0 0 0 0); - clip: rect(0 0 0 0); - position: absolute; - overflow: hidden; - margin: -1px; +.emotion-22 { padding: 0; - width: 1px; - height: 1px; + margin: 0; + font-size: 12px; + color: var(--themeError,#EB293A); + fill: var(--themeError,#EB293A); +} + +.emotion-20 { + text-indent: 0; + list-style-type: none; + padding-top: 4px; } `; -exports[`ToggleInputList renders with selected items 1`] = ` -.emotion-23 { +exports[`ToggleInputList renders with hidden label 1`] = ` +.emotion-19 { list-style: none; margin-left: 0; margin-top: 0; @@ -1025,7 +1080,7 @@ exports[`ToggleInputList renders with selected items 1`] = ` padding-left: 0; } -.emotion-24 { +.emotion-20 { border: 0; margin: 0; padding: 0; @@ -1043,13 +1098,711 @@ exports[`ToggleInputList renders with selected items 1`] = ` height: 1px; } +.emotion-6 { + padding-top: 8px; +} + +.emotion-2 { + border-color: var(--themeBorder,#DADDE2); + background-color: var(--themeBgPrimary,#FFFFFF); + border-style: solid; + border-width: 1px; + height: 15px; + width: 15px; + border-radius: 4px; + position: relative; +} + +.emotion-5 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + height: auto; + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + min-height: 0; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.emotion-5 > div { + width: auto; +} + +.emotion-3 { + box-sizing: border-box; + -webkit-flex-basis: auto; + -ms-flex-preferred-size: auto; + flex-basis: auto; + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + width: initial; + display: inherit; +} + +.emotion-4 { + box-sizing: border-box; + -webkit-flex-basis: 0; + -ms-flex-preferred-size: 0; + flex-basis: 0; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + min-width: 0; + width: auto; + padding-left: 12px; +} + .emotion-0 { padding: 0; - font-weight: 500; + border: 0; + -webkit-clip: rect(0 0 0 0); + clip: rect(0 0 0 0); + position: absolute; + overflow: hidden; + margin: -1px; + padding: 0; + width: 1px; + height: 1px; } -.emotion-8 { - padding-top: 8px; + + +
+ + Sample legend + +
    +
  • + + +
    + +
    +
    +
    +
  • +
  • + + +
    + +
    +
    +
    +
  • +
  • + + +
    + +
    +
    +
    +
  • +
+
+
+
+`; + +exports[`ToggleInputList renders with required 1`] = ` +.emotion-20 { + list-style: none; + margin-left: 0; + margin-top: 0; + margin-bottom: 0; + padding-left: 0; +} + +.emotion-21 { + border: 0; + margin: 0; + padding: 0; +} + +.emotion-2 { + border: 0; + -webkit-clip: rect(0 0 0 0); + clip: rect(0 0 0 0); + position: absolute; + overflow: hidden; + margin: -1px; + padding: 0; + width: 1px; + height: 1px; +} + +.emotion-7 { + padding-top: 8px; +} + +.emotion-1 { + padding: 0; + display: block; + padding-top: 0; + margin-top: 0; + margin-bottom: 4px; + font-weight: 500; +} + +.emotion-3 { + border-color: var(--themeBorder,#DADDE2); + background-color: var(--themeBgPrimary,#FFFFFF); + border-style: solid; + border-width: 1px; + height: 15px; + width: 15px; + border-radius: 4px; + position: relative; +} + +.emotion-6 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + height: auto; + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + min-height: 0; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.emotion-6 > div { + width: auto; +} + +.emotion-4 { + box-sizing: border-box; + -webkit-flex-basis: auto; + -ms-flex-preferred-size: auto; + flex-basis: auto; + -webkit-box-flex: 0; + -webkit-flex-grow: 0; + -ms-flex-positive: 0; + flex-grow: 0; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + width: initial; + display: inherit; +} + +.emotion-5 { + box-sizing: border-box; + -webkit-flex-basis: 0; + -ms-flex-preferred-size: 0; + flex-basis: 0; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + min-width: 0; + width: auto; + padding-left: 12px; +} + +.emotion-0 { + line-height: 1.4; + font-weight: 400; + font-size: 14px; + color: var(--themeError,#EB293A); + fill: var(--themeError,#EB293A); + margin: 0; + text-align: inherit; +} + + + +
+ + Sample legend + + + + * + + + + +
    +
  • + + +
    + +
    +
    +
    +
  • +
  • + + +
    + +
    +
    +
    +
  • +
  • + + +
    + +
    +
    +
    +
  • +
+
+
+
+`; + +exports[`ToggleInputList renders with selected items 1`] = ` +.emotion-23 { + list-style: none; + margin-left: 0; + margin-top: 0; + margin-bottom: 0; + padding-left: 0; +} + +.emotion-24 { + border: 0; + margin: 0; + padding: 0; +} + +.emotion-1 { + border: 0; + -webkit-clip: rect(0 0 0 0); + clip: rect(0 0 0 0); + position: absolute; + overflow: hidden; + margin: -1px; + padding: 0; + width: 1px; + height: 1px; +} + +.emotion-8 { + padding-top: 8px; +} + +.emotion-0 { + padding: 0; + display: block; + padding-top: 0; + margin-top: 0; + margin-bottom: 4px; + font-weight: 500; } .emotion-18 { @@ -1175,6 +1928,7 @@ exports[`ToggleInputList renders with selected items 1`] = ` }, ] } + labelAppearance="standard" listLabel="Sample legend" selectedItems={ Array [ @@ -1192,6 +1946,7 @@ exports[`ToggleInputList renders with selected items 1`] = ` aria-describedby="" aria-invalid={false} className="emotion-24" + role="listbox" >