diff --git a/CHANGELOG.md b/CHANGELOG.md index da049ea49e..238bc9551e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,25 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) **For example** - DP-1234: The short description text on a [service detail](http://mayflower.digital.mass.gov/?p=pages-detail-for-service-howto-location) page banner ([@organisms/by-template/page-banner](http://mayflower.digital.mass.gov/?p=organisms-page-banner)) should now render ([PR #493](https://github.com/massgov/mayflower/pull/493)) +## 9.2.0 (05/05/2019) + +### Added +- (Patternlab) [Header] DP-4562: Set focus state for search on mobile menu in mobileNav module. #473 +- (React) [HelpTip] DP-12875: Add `disabled` prop to disable HelpTip trigger text. #494 + +### Fixed +- (React) [InputSlider] DP-12732: Allows slider callback and updates form context on handler drag (Added onUpdate prop) #495 +- (React) [InputSlider] DP-12732: Allows keyboard actions and slider track click to update value (Changed handleChange from using onSlideEnd to onChange) #495 +- (React) [InputCurrency] DP-12807: Prevent InputCurrency returning NaN when default value is set to null #484 +- (React) [ErrorMessage] DP-12806: Fix error message inline styling #484 +- (React) [InputSlider] DP-12875: Disable handle button when InputSlider is disabled. #494 +- (React) [InputCurrency] DP-12890: Fix `NaN` value when defaultValue is null using up/down buttons. #498 + +### Changed +- (Patternlab) [SectionLinks] DP-9249: Topic card more links #472 +- (React) [InputCurrency] DP-12890: Pass event type to callback #498 + + ## 9.1.1 (02/19/2019) ### Fixed diff --git a/assets/scss/03-organisms/_help-tip.scss b/assets/scss/03-organisms/_help-tip.scss index 768b56a271..4a86611091 100644 --- a/assets/scss/03-organisms/_help-tip.scss +++ b/assets/scss/03-organisms/_help-tip.scss @@ -54,12 +54,20 @@ $border-width: 1px; border-bottom: 2px dotted $c-primary; background-color: $c-bay-blue-lightest; + svg { + margin-left: 5px; + width: 16px; + height: 16px; + margin-bottom: -.1em; + fill: $c-font-link; + } + &:focus { box-shadow: 0 0 3px 3px $c-focus; outline: 0; } - &.active, &:hover { + &--active, &:hover { background-color: $c-primary; color: $c-white; @@ -68,12 +76,8 @@ $border-width: 1px; } } - svg { - margin-left: 5px; - width: 16px; - height: 16px; - margin-bottom: -.1em; - fill: $c-font-link; + &--disabled { + pointer-events: none; } } diff --git a/changelogs/template.txt b/changelogs/template.md similarity index 60% rename from changelogs/template.txt rename to changelogs/template.md index 48891a63dd..d42d5b0e47 100644 --- a/changelogs/template.txt +++ b/changelogs/template.md @@ -1,7 +1,13 @@ +___EXAMPLE___ +Minor +Added +- (Patternlab) [Header] DP-4562: Set focus state for search on mobile menu in mobileNav module #473 +- (React) [HelpTip] DP-12875: Add `disabled` prop to disable HelpTip trigger text. #494 + ___DESCRIPTION___ -Change_type (see below) Change_impact (see below) -- (Project_prefix) DP-1234: Adds apples to apple trees for admin apple pickers #PR# +Change_type (see below) +- (Project_prefix) [ComponentName] DP-1234: Adds apples to apple trees for admin apple pickers #PR ___POST DEPLOY STEPS___ 1. Do this @@ -28,3 +34,8 @@ ___PROJECTS PREFIX___ - Patternlab - React - Docs + + +___COMPONENT NAME___ +Component name in `PascalCase` +e.g. Header, Form, InputSlider, InputTextTypeAhead diff --git a/docs/for-developers/changelog-instructions.md b/docs/for-developers/changelog-instructions.md index 3ee6ceee4c..2a6135f985 100644 --- a/docs/for-developers/changelog-instructions.md +++ b/docs/for-developers/changelog-instructions.md @@ -4,18 +4,20 @@ This documentation outlines a simple series of steps to keep `CHANGELOG.md` up-t ## Dev: Before You Submit a PR for a Feature Branch or Hot Fix -1. Make a copy of `changelogs/template.txt` with the ticket number as the name \(example: `DP-1234.txt`\). If there is no ticket number for the contribution, just use the branch name and your initials \(`awesome- feature-branch.txt`\). -2. Write a plain language description of the feature you're contributing. It needs to include the name, what changed, and who it impacts. -3. Write down any post deploy steps that need to be performed \(Examples: `This change will effect the local build. Make sure all developers know what steps to take after this gets in to dev`\). -4. Commit the file and open your PR. +1. Make a copy of `changelogs/template.md` with the ticket number as the name, e.g. `DP-1234.md.` If there is no ticket number for the contribution, just use the branch name and your initials, e.g. `awesome- feature-branch.md`. +2. Write a plain language description of the feature you're contributing. It needs to include the project prefix, component name, what changed, and who it impacts. +3. Write down any post deploy steps that need to be performed, e.g. +> This change will effect the local build. Make sure all developers know what steps to take after this gets in to dev. + +4. For any breaking changes, add a comment in the PR describing the necessary changes on the consumer side and link that comment in the changelog. +5. Commit the file and open your PR. ## Release Master: When you are Releasing Do this after your open your release branch: 1. In `CHANGELOG.md`, create a new section for the release with "Added", "Changed", and "Removed" sub-sections. -2. Go through each `changelogs/*.txt` file and copy the description into the appropriate sub-section of `CHANGELOG.md`. +2. Go through each `changelogs/*.md` file and copy the description into the appropriate sub-section of `CHANGELOG.md`. 3. Keep a list of post deployment steps handy for yourself. -4. After the last description is copied and you have all the post deployment steps, delete all files in `changelogs/` except for `changelogs/template.txt.` +4. After the last description is copied and you have all the post deployment steps, delete all files in `changelogs/` except for `changelogs/template.md`. 5. Commit changes to the release branch. - diff --git a/patternlab/styleguide/source/_patterns/02-molecules/section-links.twig b/patternlab/styleguide/source/_patterns/02-molecules/section-links.twig index b2e2bd7308..2fce87419c 100644 --- a/patternlab/styleguide/source/_patterns/02-molecules/section-links.twig +++ b/patternlab/styleguide/source/_patterns/02-molecules/section-links.twig @@ -27,6 +27,7 @@ "type": "", "href": sectionLinks.title.href, "text": "Learn More", + "labelContext": "about " ~ sectionLinks.title.text, "info": "learn more about " ~ sectionLinks.title.text } %} {% include "@atoms/decorative-link.twig" %} @@ -51,8 +52,15 @@ {% if sectionLinks.seeAll %} {% endif %} diff --git a/patternlab/styleguide/source/assets/js/modules/mobileNav.js b/patternlab/styleguide/source/assets/js/modules/mobileNav.js index 93e76712b1..5d1e38e793 100644 --- a/patternlab/styleguide/source/assets/js/modules/mobileNav.js +++ b/patternlab/styleguide/source/assets/js/modules/mobileNav.js @@ -2,8 +2,8 @@ let menuButton = document.querySelector(".js-header-menu-button"); let feedbackButton = document.querySelector('.ma__fixed-feedback-button'); -if(null !== menuButton){ - menuButton.addEventListener("click", function(event) { +if (null !== menuButton) { + menuButton.addEventListener("click", function (event) { event.preventDefault(); document.querySelector("body").classList.toggle("show-menu"); @@ -15,13 +15,14 @@ if(null !== menuButton){ // ****** Main Header Search button on mobile should open the mobile menu ****** let searchForm = document.querySelector(".js-header-search-menu .js-header-search-form"); -if(null !== searchForm){ - searchForm.addEventListener("submit", function(event) { - if(window.innerWidth > 620) { +if (null !== searchForm) { + searchForm.addEventListener("submit", function (event) { + if (window.innerWidth > 620) { return; } event.preventDefault(); document.querySelector("body").classList.toggle("show-menu"); + document.querySelector('.ma__header__nav-search .ma__header-search__input').focus(); feedbackButton.classList.toggle("hide-button"); }); } diff --git a/react/backstop/data/bitmaps_reference/vrt_atoms_forms_Form_0_document_0_small_atom.png b/react/backstop/data/bitmaps_reference/vrt_atoms_forms_Form_0_document_0_small_atom.png index 3b117dda34..9ccbcaff30 100644 Binary files a/react/backstop/data/bitmaps_reference/vrt_atoms_forms_Form_0_document_0_small_atom.png and b/react/backstop/data/bitmaps_reference/vrt_atoms_forms_Form_0_document_0_small_atom.png differ diff --git a/react/src/components/atoms/forms/CompoundSlider/index.js b/react/src/components/atoms/forms/CompoundSlider/index.js index 04265e52d4..22bad0c6d5 100644 --- a/react/src/components/atoms/forms/CompoundSlider/index.js +++ b/react/src/components/atoms/forms/CompoundSlider/index.js @@ -9,7 +9,7 @@ import './style.css'; const Handle = (props) => { const { - handle: { id, value, percent }, getHandleProps, axis, min, max, step, displayValueFormat + handle: { id, value, percent }, getHandleProps, axis, min, max, step, displayValueFormat, disabled } = props; const decimalPlaces = countDecimals(step); const roundedValue = (Number.isInteger(step)) ? value : Number(Number.parseFloat(value).toFixed(decimalPlaces)); @@ -17,6 +17,7 @@ const Handle = (props) => { 'aria-valuemin': min, 'aria-valuemax': max, 'aria-valuenow': roundedValue, + disabled, role: 'slider', onClick: (e) => { e.preventDefault(); @@ -106,14 +107,22 @@ class CompoundSlider extends Component { { (context) => { const { - min, max, step, disabled, domain + min, max, step, disabled, domain, onChange, onUpdate } = this.props; const decimalPlaces = countDecimals(step); - const handleDragEnd = (values) => { + const handleChange = (values) => { const value = (Number.isInteger(step)) ? values[0] : Number(Number.parseFloat(values[0]).toFixed(decimalPlaces)); context.updateState({ value }, () => { - if (typeof this.props.onChange === 'function') { - this.props.onChange(value, this.props.id); + if (typeof onChange === 'function') { + onChange(value, this.props.id); + } + }); + }; + const handleUpdate = (values) => { + const value = (Number.isInteger(step)) ? values[0] : Number(Number.parseFloat(values[0]).toFixed(decimalPlaces)); + context.updateState({ value }, () => { + if (typeof onUpdate === 'function') { + onUpdate(value, this.props.id); } }); }; @@ -157,11 +166,14 @@ class CompoundSlider extends Component { domain, step, vertical: !(this.props.axis === 'x'), - onSlideEnd: handleDragEnd, + onChange: handleChange, values: [defaultValue], mode: handleMode, disabled }; + if (onUpdate) { + sliderProps.onUpdate = handleUpdate; + } const wrapperClasses = classNames({ 'ma__input-slider': true, 'ma__input-slider--disabled': disabled, @@ -190,6 +202,7 @@ class CompoundSlider extends Component { max={max} step={step} displayValueFormat={this.props.displayValueFormat} + disabled={disabled} /> ))} @@ -251,7 +264,9 @@ class CompoundSlider extends Component { CompoundSlider.propTypes = { /** The unique ID for the input field */ id: PropTypes.string.isRequired, - /** Custom change function */ + /** Custom update function, triggered with the values on drag (caution: high-volume updates when dragging). Only if a function is passed to onUpdate will form context get updated on drag. */ + onUpdate: PropTypes.func, + /** Custom on change function, triggered when the value of the slider has changed. This will recieve changes at the end of a slide as well as changes from clicks on rails and tracks. */ onChange: PropTypes.func, /** Default input text value */ defaultValue: PropTypes.string, diff --git a/react/src/components/atoms/forms/CompoundSlider/style.scss b/react/src/components/atoms/forms/CompoundSlider/style.scss index 6a33afc780..fc1cc6a203 100644 --- a/react/src/components/atoms/forms/CompoundSlider/style.scss +++ b/react/src/components/atoms/forms/CompoundSlider/style.scss @@ -65,9 +65,14 @@ $margin: 20px; &-handle { background-color: $c-gray-dark; cursor: not-allowed; + opacity: 1; &:hover { transform: none; } + + &-value { + color: $c-black; + } } } } diff --git a/react/src/components/atoms/forms/Form/Form.stories.js b/react/src/components/atoms/forms/Form/Form.stories.js index d8a727959f..4a01322ded 100644 --- a/react/src/components/atoms/forms/Form/Form.stories.js +++ b/react/src/components/atoms/forms/Form/Form.stories.js @@ -33,7 +33,7 @@ storiesOf('atoms/forms', module) inputSliderOptionsWithKnobs.domain = array('InputSlider.domain', [0, 1]).map((num) => Number(num)); inputSliderOptionsWithKnobs.max = number('InputSlider.max', 1); inputSliderOptionsWithKnobs.step = number('InputSlider.step', 0.01, { min: 0, max: 1, step: 0.01 }); - inputSliderOptionsWithKnobs.labelText = text('InputSlider.labelText', 'Slider (Linked to Input 3)'); + inputSliderOptionsWithKnobs.labelText = text('InputSlider.labelText', 'Slider (Linked to Input 0 and Input 1)'); const formTicks = object('InputSlider.ticks', { 0: '0%', 0.6: 'Minimum requirement', 1: '100%' }); const ticks = []; Object.keys(formTicks).forEach((tick) => ticks.push([tick, formTicks[tick]])); @@ -42,7 +42,7 @@ storiesOf('atoms/forms', module) delete InputCurrencyOptions.labelText; const inputCurrencyOptionsWithKnobs = Object.assign(...Object.entries(InputCurrencyOptions).map(([k, v]) => ( { [k]: v() }))); - inputCurrencyOptionsWithKnobs.labelText = text('InputCurrency.labelText', 'Currency Input (Set to 999 when Slider is 60)'); + inputCurrencyOptionsWithKnobs.labelText = text('InputCurrency.labelText', 'Currency Input (Set to 999 when Slider is greater than 60)'); const languages = new Map(); languages.set('Chinese', 'zh-CN'); languages.set('English', 'en-US'); @@ -55,28 +55,27 @@ storiesOf('atoms/forms', module) { (formContext) => { inputTextOptionsWithKnobs.onChange = (e, newVal, id) => { - if (formContext.hasId('test1')) { - if (formContext.getValue('test1') === 30) { - formContext.setValue({ id: 'test2', value: 25 }); - } - } // Keep test0 and test1 in sync. - if (formContext.hasId('test1') && formContext.hasId('test0')) { + if (formContext.hasId('test0') && formContext.hasId('test1') && (formContext.hasId('slider'))) { if (id === 'test0') { - formContext.setValue({ id: 'test1', value: formContext.getValue('test0') }); + const test0 = formContext.getValue('test0'); + formContext.setValue({ id: 'test1', value: 100 - test0 }); + formContext.setValue({ id: 'slider', value: test0 / 100 }); + if (test0 > 60) { + formContext.setValue({ id: 'currency-input', value: '$999.00' }); + } else { + formContext.setValue({ id: 'currency-input', value: '$0.00' }); + } } if (id === 'test1') { - formContext.setValue({ id: 'test0', value: formContext.getValue('test1') }); - } - } - if (formContext.hasId('test3')) { - if (id === 'test3') { - formContext.setValue({ id: 'slider', value: Number(formContext.getValue('test3') / 100).toFixed(2) }, () => { - // Use afterUpdate function so that slider is updated before this check. - if (formContext.getValue('slider') === 0.6) { - formContext.setValue({ id: 'currency-input', value: '$999.00' }); - } - }); + const test1 = formContext.getValue('test1'); + formContext.setValue({ id: 'test0', value: 100 - test1 }); + formContext.setValue({ id: 'slider', value: (100 - test1) / 100 }); + if ((100 - test1) > 60) { + formContext.setValue({ id: 'currency-input', value: '$999.00' }); + } else { + formContext.setValue({ id: 'currency-input', value: '$0.00' }); + } } } }; @@ -87,8 +86,9 @@ storiesOf('atoms/forms', module) ...inputTextOptionsWithKnobs, key: 'Form.InputNumber.test0', defaultValue: 0, - labelText: 'Input 0 (Linked to Input 1)', - id: 'test0' + labelText: 'Input 0 (Linked to Input 1 and Slider)', + id: 'test0', + unit: '%' } ], [ @@ -96,29 +96,10 @@ storiesOf('atoms/forms', module) { ...inputTextOptionsWithKnobs, key: 'Form.InputNumber.test1', - defaultValue: 1, - labelText: 'Input 1 (Linked to Input 0)', - id: 'test1' - } - ], - [ - 'test2', - { - ...inputTextOptionsWithKnobs, - key: 'Form.InputNumber.test2', - defaultValue: 2, - labelText: 'Input 2 (Set to 25 when Input 1 is 30)', - id: 'test2' - } - ], - [ - 'test3', - { - ...inputTextOptionsWithKnobs, - key: 'Form.InputNumber.test3', - defaultValue: 0, - labelText: 'Input 3 (Linked to Slider)', - id: 'test3' + defaultValue: 100, + labelText: 'Input 1 (Linked to Input 0 and Slider)', + id: 'test1', + unit: '%' } ] ]; @@ -126,21 +107,24 @@ storiesOf('atoms/forms', module) ids.forEach((numberProps) => { inputs.push(); }); - inputSliderOptionsWithKnobs.onChange = (newVal, id) => { + inputSliderOptionsWithKnobs.onUpdate = (newVal, id) => { if (formContext.hasId(id)) { - formContext.setValue({ id: 'test3', value: Number(formContext.getValue(id) * 100).toFixed(2) }); + formContext.setValue({ id: 'test0', value: Number(formContext.getValue(id) * 100).toFixed() }); + formContext.setValue({ id: 'test1', value: Number((1 - formContext.getValue(id)) * 100).toFixed() }); if (formContext.hasId('currency-input')) { - if (newVal === 0.6) { + if (newVal > 0.6) { // Sets currency to 999 when slider is 60%. formContext.setValue({ id: 'currency-input', value: '$999.00' }); + } else { + formContext.setValue({ id: 'currency-input', value: '$0.00' }); } } } }; return( - {inputs} + {inputs} ); diff --git a/react/src/components/atoms/forms/Input/style.scss b/react/src/components/atoms/forms/Input/style.scss index 7d59a0d485..2f63862295 100644 --- a/react/src/components/atoms/forms/Input/style.scss +++ b/react/src/components/atoms/forms/Input/style.scss @@ -14,7 +14,6 @@ #{$group}-right { display: flex; flex-direction: column; - align-items: flex-end; @media ($bp-small-max) { align-items: flex-start; } diff --git a/react/src/components/atoms/forms/InputCurrency/index.js b/react/src/components/atoms/forms/InputCurrency/index.js index 3765cb2286..81d5efc8f1 100644 --- a/react/src/components/atoms/forms/InputCurrency/index.js +++ b/react/src/components/atoms/forms/InputCurrency/index.js @@ -43,6 +43,7 @@ const Currency = (props) => ( return number; }; const handleChange = (e) => { + const { type } = e; let stringValue; if (typeof e.target.value !== 'string') { stringValue = String(e.target.value); @@ -70,11 +71,12 @@ const Currency = (props) => ( } context.updateState(update, () => { if (typeof props.onChange === 'function') { - props.onChange(numberValue, props.id); + props.onChange(numberValue, props.id, type); } }); }; const handleAdjust = (e, direction) => { + const { type } = e; let stringValue; if (typeof context.value !== 'string') { stringValue = String(context.value); @@ -82,17 +84,19 @@ const Currency = (props) => ( stringValue = context.value; } const numberValue = numbro.unformat(stringValue); + // default to 0 if defaultValue is NaN + const baseValue = numberValue || 0; if (!Number.isNaN(numberValue)) { let newValue; if (direction === 'up') { - newValue = Number(Number.parseFloat(numberValue + props.step).toFixed(2)); + newValue = Number(Number.parseFloat(baseValue + props.step).toFixed(2)); } else if (direction === 'down') { - newValue = Number(Number.parseFloat(numberValue - props.step).toFixed(2)); + newValue = Number(Number.parseFloat(baseValue - props.step).toFixed(2)); } const { showError, errorMsg } = validNumber(newValue, props.min, props.max); context.updateState({ showError, errorMsg, value: toCurrency(newValue, 2) }, () => { if (typeof props.onChange === 'function') { - props.onChange(newValue, props.id); + props.onChange(newValue, props.id, type, direction); } }); } @@ -142,6 +146,7 @@ const Currency = (props) => ( } }, onKeyDown: (e) => { + const { type, key } = e; let stringValue; if (typeof context.value !== 'string') { stringValue = String(context.value); @@ -149,22 +154,24 @@ const Currency = (props) => ( stringValue = context.value; } const numberValue = numbro.unformat(stringValue); + // default to 0 if defaultValue is NaN + const baseValue = numberValue || 0; if (!Number.isNaN(numberValue) && stringValue.length > 0) { let newValue; - if (e.key === 'ArrowDown') { - newValue = Number(Number.parseFloat(numberValue - props.step).toFixed(2)); + if (key === 'ArrowDown') { + newValue = Number(Number.parseFloat(baseValue - props.step).toFixed(2)); const { showError, errorMsg } = validNumber(newValue, props.min, props.max); context.updateState({ showError, errorMsg, value: toCurrency(newValue, 2) }, () => { if (typeof props.onChange === 'function') { - props.onChange(newValue, props.id); + props.onChange(newValue, props.id, type, key); } }); - } else if (e.key === 'ArrowUp') { - newValue = Number(Number.parseFloat(numberValue + props.step).toFixed(2)); + } else if (key === 'ArrowUp') { + newValue = Number(Number.parseFloat(baseValue + props.step).toFixed(2)); const { showError, errorMsg } = validNumber(newValue, props.min, props.max); context.updateState({ showError, errorMsg, value: toCurrency(newValue, 2) }, () => { if (typeof props.onChange === 'function') { - props.onChange(newValue, props.id); + props.onChange(newValue, props.id, type, key); } }); } diff --git a/react/src/components/atoms/forms/InputSlider/InputSlider.knobs.options.js b/react/src/components/atoms/forms/InputSlider/InputSlider.knobs.options.js index 23f18f14a4..ddc96327b3 100644 --- a/react/src/components/atoms/forms/InputSlider/InputSlider.knobs.options.js +++ b/react/src/components/atoms/forms/InputSlider/InputSlider.knobs.options.js @@ -15,6 +15,7 @@ export default { // Array knob converts numbers to strings - put it back to number. domain: () => array('InputSlider.domain', [0, 1]).map((num) => Number(num)), onChange: () => action('inputSlide.onChange'), + onUpdate: () => action('inputSlide.onUpdate'), skipped: () => boolean('InputSlider.skipped', false), displayValueFormat: () => select('InputSlider.displayValueFormat', ['percentage', 'value', null], 'percentage') }; diff --git a/react/src/components/atoms/forms/InputSlider/index.js b/react/src/components/atoms/forms/InputSlider/index.js index 81634338db..aa67d8d6dd 100644 --- a/react/src/components/atoms/forms/InputSlider/index.js +++ b/react/src/components/atoms/forms/InputSlider/index.js @@ -5,10 +5,10 @@ import CompoundSlider from '../CompoundSlider'; const InputSlider = (props) => { const { - axis, max, min, step, ticks, onChange, domain, skipped, displayValueFormat, ...inputProps + axis, max, min, step, ticks, onChange, onUpdate, domain, skipped, displayValueFormat, ...inputProps } = props; const sliderProps = { - axis, max, min, step, defaultValue: props.defaultValue, onChange, domain, skipped, displayValueFormat + axis, max, min, step, defaultValue: props.defaultValue, onChange, onUpdate, domain, skipped, displayValueFormat }; const { id, disabled } = inputProps; sliderProps.id = id; @@ -30,6 +30,8 @@ InputSlider.propTypes = { id: PropTypes.string.isRequired, /** Custom change function */ onChange: PropTypes.func, + /** Custom change function */ + onUpdate: PropTypes.func, /** Default input text value */ defaultValue: PropTypes.string, /** Max value for the field. */ diff --git a/react/src/components/organisms/HelpTip/HelpTip.stories.js b/react/src/components/organisms/HelpTip/HelpTip.stories.js index 3576cb412e..60935e53eb 100644 --- a/react/src/components/organisms/HelpTip/HelpTip.stories.js +++ b/react/src/components/organisms/HelpTip/HelpTip.stories.js @@ -27,7 +27,8 @@ storiesOf('organisms/HelpTip', module) id: text('helpText.id', 'helptext-id-123'), bypassMobileStyle: boolean('helpTip.bypassMobileStyle', false), hasMarkup: boolean('helpTip.hasMarkup', true), - theme: select('helpTip.theme', themeOptions, 'c-primary') + theme: select('helpTip.theme', themeOptions, 'c-primary'), + disabled: boolean('helpTip.disabled', false) }; return(); @@ -39,7 +40,8 @@ storiesOf('organisms/HelpTip', module) id: text('helpText.id', 'helptext-id-123'), bypassMobileStyle: boolean('helpTip.bypassMobileStyle', false), hasMarkup: boolean('helpTip.hasMarkup', true), - theme: select('helpTip.theme', themeOptions, 'c-primary') + theme: select('helpTip.theme', themeOptions, 'c-primary'), + disabled: boolean('helpTip.disabled', false) }; return( diff --git a/react/src/components/organisms/HelpTip/index.js b/react/src/components/organisms/HelpTip/index.js index 789a7c1040..26be4e01a4 100644 --- a/react/src/components/organisms/HelpTip/index.js +++ b/react/src/components/organisms/HelpTip/index.js @@ -52,7 +52,7 @@ class HelpTip extends Component { render() { const { - hasMarkup, triggerText, textAfter, helpText, children, id, theme, text + hasMarkup, triggerText, textAfter, helpText, children, id, theme, text, disabled } = this.props; const baseClass = classNames({ @@ -78,31 +78,38 @@ class HelpTip extends Component { return( - {triggerText.map((trigger, index) => ( - - {index === 0 && this.buildDangerouslyIfHasMarkup(splitText[index], hasMarkup)} - - this.toggleOpen(index)} - onKeyUp={(e) => this.toggleOpenForKeyUp(e, index)} - tabIndex="0" - role="button" - aria-describedby={`context-a11y-${id}-${index}`} - aria-expanded={this.state.isOpen[index]} - aria-controls={`help-tip-content-${id}-${index}`} - > - {this.buildDangerouslyIfHasMarkup(trigger, hasMarkup)} - + {triggerText.map((trigger, index) => { + const triggerTextClasses = classNames({ + 'ma__help-tip__trigger': true, + 'ma__help-tip__trigger--active': this.state.isOpen[index], + 'ma__help-tip__trigger--disabled': disabled + }); + return( + + {index === 0 && this.buildDangerouslyIfHasMarkup(splitText[index], hasMarkup)} + + this.toggleOpen(index)} + onKeyUp={(e) => this.toggleOpenForKeyUp(e, index)} + tabIndex={disabled ? -1 : 0} + role="button" + aria-describedby={`context-a11y-${id}-${index}`} + aria-expanded={this.state.isOpen[index]} + aria-controls={`help-tip-content-${id}-${index}`} + > + {this.buildDangerouslyIfHasMarkup(trigger, hasMarkup)} + + + {this.buildDangerouslyIfHasMarkup(splitText[index + 1], hasMarkup)} - {this.buildDangerouslyIfHasMarkup(splitText[index + 1], hasMarkup)} - - ))} - {triggerText.map((trigger, index) => ( + ); + })} + {triggerText.map((trigger, index) => !disabled && (