diff --git a/packages/design-system-dashboard-cli/README.md b/packages/design-system-dashboard-cli/README.md index 54da8efb8..9e1399be7 100644 --- a/packages/design-system-dashboard-cli/README.md +++ b/packages/design-system-dashboard-cli/README.md @@ -75,6 +75,8 @@ If you supply search terms, it'll filter the result. ![filtered result](./img/filtered-result.png) +If you supply the string `uswds` as a search term, it'll return all web component and react binding usages where the uswds prop is being used. + **Note:** Search terms are case sensitive! Search terms may be regex. diff --git a/packages/design-system-dashboard-cli/coverage/lcov-report/write-react-owners-to-csv.js.html b/packages/design-system-dashboard-cli/coverage/lcov-report/write-react-owners-to-csv.js.html index 59ad019fd..7bf63a620 100644 --- a/packages/design-system-dashboard-cli/coverage/lcov-report/write-react-owners-to-csv.js.html +++ b/packages/design-system-dashboard-cli/coverage/lcov-report/write-react-owners-to-csv.js.html @@ -331,7 +331,6 @@

All files write-react-owners-to-csv.js

  const componentsToKeep = [ 'AlertBox', - 'Checkbox', 'ExpandingGroup', 'IconSearch', 'LoadingIndicator', diff --git a/packages/design-system-dashboard-cli/src/find-ds-components.js b/packages/design-system-dashboard-cli/src/find-ds-components.js index de2328d2e..45378d932 100644 --- a/packages/design-system-dashboard-cli/src/find-ds-components.js +++ b/packages/design-system-dashboard-cli/src/find-ds-components.js @@ -143,6 +143,9 @@ function findUsedReactComponents(vwModules, regexPattern) { * Search vets-website and the content build for design system components. * * @param searchStrings {string[]} - The components to display usage for + * + * Passing in the string 'uswds' will output a list of all web components and + * web component react bindings where the uswds prop is used */ function findComponents(searchStrings) { const vwModules = readAllModules(`${repos['vets-website']}/src`); @@ -163,18 +166,30 @@ function findComponents(searchStrings) { ); const wcTagRegex = /<(va-[^\s>]+)/gms; - const vwWebComponents = search(vwModules, wcTagRegex); - const contentBuildWC = search(contentTemplates, wcTagRegex); + const wcUswds3Regex = /<(Va[^\s]+|va-[^\s]+)(\s|\n)[^>]*?uswds/gms; - const vwComponents = [ - ...usedReactComponents, - ...vwWebComponents, - ...usedReactBindings, - ]; + let vwWebComponents; + let contentBuildWC; + let vwComponents; - const data = tallyResults(vwComponents, contentBuildWC); + if (searchStrings?.includes('uswds')) { + vwWebComponents = search(vwModules, wcUswds3Regex); + contentBuildWC = search(contentTemplates, wcUswds3Regex); + vwComponents = [...vwWebComponents]; + } else { + vwWebComponents = search(vwModules, wcTagRegex); + contentBuildWC = search(contentTemplates, wcTagRegex); + vwComponents = [ + ...usedReactComponents, + ...vwWebComponents, + ...usedReactBindings, + ]; + } - return filterSearchedComponents(data, searchStrings); + const data = tallyResults(vwComponents, contentBuildWC); + return searchStrings?.includes('uswds') + ? data + : filterSearchedComponents(data, searchStrings); } if (require.main === module) { diff --git a/packages/design-system-dashboard-cli/src/search-files.js b/packages/design-system-dashboard-cli/src/search-files.js index 8fb6c6f19..3c5bba33b 100644 --- a/packages/design-system-dashboard-cli/src/search-files.js +++ b/packages/design-system-dashboard-cli/src/search-files.js @@ -26,6 +26,8 @@ function readAllModules(rootDir) { `${rootDir}/**/_mock-form/**`, `${rootDir}/**/*.unit.@(js|jsx)`, `${rootDir}/**/*.spec.@(js|jsx)`, + `${rootDir}/**/ds-playground/**`, + `${rootDir}/**/ds-v3-playground/**`, ], }); diff --git a/packages/design-system-dashboard-cli/src/write-react-owners-to-csv.js b/packages/design-system-dashboard-cli/src/write-react-owners-to-csv.js index 406d99145..bf2bae4f4 100644 --- a/packages/design-system-dashboard-cli/src/write-react-owners-to-csv.js +++ b/packages/design-system-dashboard-cli/src/write-react-owners-to-csv.js @@ -7,7 +7,6 @@ const today = require('./today'); const componentsToKeep = [ 'AlertBox', 'Breadcrumbs', - 'Checkbox', 'ExpandingGroup', 'IconSearch', 'Modal', diff --git a/packages/react-components/index.js b/packages/react-components/index.js index 641a0cf56..dd731a0c9 100644 --- a/packages/react-components/index.js +++ b/packages/react-components/index.js @@ -1,6 +1,5 @@ import AlertBox, { ALERT_TYPE } from './AlertBox'; import Breadcrumbs from './Breadcrumbs'; -import Checkbox from './Checkbox'; import ExpandingGroup from './ExpandingGroup'; import IconBase from './IconBase'; import IconSearch from './IconSearch'; @@ -14,7 +13,6 @@ export { AlertBox, ALERT_TYPE, Breadcrumbs, - Checkbox, ExpandingGroup, IconBase, IconSearch, diff --git a/packages/react-components/src/components/Checkbox/Checkbox.jsx b/packages/react-components/src/components/Checkbox/Checkbox.jsx deleted file mode 100644 index ba5835816..000000000 --- a/packages/react-components/src/components/Checkbox/Checkbox.jsx +++ /dev/null @@ -1,170 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { uniqueId } from '../../helpers/utilities'; - -import dispatchAnalyticsEvent from '../../helpers/analytics'; - -class Checkbox extends React.Component { - constructor() { - super(); - this.handleChange = this.handleChange.bind(this); - } - - UNSAFE_componentWillMount() { - this.inputId = uniqueId('errorable-checkbox-'); - } - - handleChange(domEvent) { - const isChecked = domEvent.target.checked; - - if (isChecked && this.props.enableAnalytics) { - dispatchAnalyticsEvent({ - componentName: 'Checkbox', - action: 'change', - details: { - label: this.props.label, - labelAboveCheckbox: this.props.labelAboveCheckbox, - required: this.props.required, - }, - }); - } - - this.props.onValueChange(isChecked); - } - - render() { - // TODO: extract error logic into a utility function - // Calculate error state. - let errorSpan = ''; - let errorSpanId = undefined; - if (this.props.errorMessage) { - errorSpanId = `${this.inputId}-error-message`; - errorSpan = ( - - Error {this.props.errorMessage} - - ); - } - - // Calculate required. - let requiredSpan = undefined; - if (this.props.required) { - requiredSpan = (*Required); - } - - let className = `form-checkbox${ - this.props.errorMessage ? ' usa-input-error' : '' - }`; - if (this.props.className !== undefined) { - className = `${className} ${this.props.className}`; - } - - return ( -
- {this.props.labelAboveCheckbox && ( - - {this.props.labelAboveCheckbox} - - )} - {errorSpan} - - -
- ); - } -} - -Checkbox.propTypes = { - /** - * If the checkbox is checked or not - */ - checked: PropTypes.bool, - /** - * Optionally adds one or more CSS classes to the NAV element - */ - className: PropTypes.string, - /** - * Error message for the modal - */ - errorMessage: PropTypes.string, - /** - * Name for the modal - */ - name: PropTypes.string, - /** - * Label [string or object] for the checkbox. Either this or ariaLabelledBy is required. - */ - /* eslint-disable consistent-return */ - label: (props, propName, componentName) => { - const validTypes = ['string', 'object']; - - if (!props.label && !props.ariaLabelledBy) { - return new Error( - `Either ${propName} or ariaLabelledBy property is required in ${componentName}, but both are missing.`, - ); - } - - if (props.label && !validTypes.includes(typeof props.label)) { - return new Error( - `${componentName}’s label property type is invalid -- should be one of - these types: ${validTypes.join(', ')}.`, - ); - } - }, - /* eslint-enable consistent-return */ - /** - * Descriptive text to sit above the checkbox and label - */ - labelAboveCheckbox: PropTypes.string, - /** - * aria-labelledby attribute [string] (external-heading ID). Either this or label is required. - */ - /* eslint-disable consistent-return */ - ariaLabelledBy: (props, propName, componentName) => { - if (!props.label && !props.ariaLabelledBy) { - return new Error( - `Either ${propName} or label property is required in ${componentName}, but both are missing.`, - ); - } - - if (props.ariaLabelledBy && typeof props.ariaLabelledBy !== 'string') { - return new Error( - `${componentName}’s ariaLabelledBy property type is invalid -- should be - string.`, - ); - } - }, - /* eslint-enable consistent-return */ - /** - * Handler for when the checkbox is changed - */ - onValueChange: PropTypes.func.isRequired, - /** - * If the checkbox is required or not - */ - required: PropTypes.bool, - /** - * Analytics tracking function(s) will be called. Form components - * are disabled by default due to PII/PHI concerns. - */ - enableAnalytics: PropTypes.bool, -}; - -export default Checkbox; diff --git a/packages/react-components/src/components/Checkbox/Checkbox.mdx b/packages/react-components/src/components/Checkbox/Checkbox.mdx deleted file mode 100644 index 88aebda23..000000000 --- a/packages/react-components/src/components/Checkbox/Checkbox.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Checkbox -name: Checkbox -tags: checkbox, component ---- - -import Checkbox from './Checkbox' - -### Code: -```javascript -import Checkbox from '@department-of-veterans-affairs/component-library/Checkbox' - -
- value} - id='default' - errorMessage='' - required={true} - title='Checkbox' - /> - value} - id='default' - errorMessage='Error message' - required={true} - title='Checkbox' - /> -

External heading [label-substitute]

- value} - id='default' - errorMessage='' - required={true} - title='Checkbox' - /> -
-``` - -### Rendered Component -
- value} - id='default' - errorMessage='' - required={true} - title='Checkbox' - /> - value} - id='default' - errorMessage='Error message' - required={true} - title='Checkbox' - /> -

External heading [label-substitute]

- value} - id='default' - errorMessage='' - required={true} - title='Checkbox' - /> -
diff --git a/packages/react-components/src/components/Checkbox/Checkbox.unit.spec.jsx b/packages/react-components/src/components/Checkbox/Checkbox.unit.spec.jsx deleted file mode 100644 index 44911ea46..000000000 --- a/packages/react-components/src/components/Checkbox/Checkbox.unit.spec.jsx +++ /dev/null @@ -1,204 +0,0 @@ -import React from 'react'; -import { expect } from 'chai'; -import { mount, shallow } from 'enzyme'; -import { axeCheck } from '../../helpers/test-helpers'; -import Checkbox from './Checkbox.jsx'; -import sinon from 'sinon'; -import { testAnalytics } from '../../helpers/test-helpers'; - -describe('', () => { - it('should render without the labelAboveCheckbox', () => { - const tree = shallow( {}} />); - expect(tree.text()).to.contain('test'); - tree.unmount(); - }); - - it('should render with the labelAboveCheckbox', () => { - const tree = shallow( - {}} - />, - ); - expect(tree.text()).to.contain('test'); - expect(tree.text()).to.contain('this is a checkbox'); - tree.unmount(); - }); - - it('should pass aXe check', () => - axeCheck( {}} />)); - it('ensure checked changes propagate', () => { - const handleChangeSpy = sinon.spy(Checkbox.prototype, 'handleChange'); - const tree = shallow( {}} />); - const event = { target: { checked: true } }; - - const checkBox = () => tree.find('[type="checkbox"]'); - checkBox().simulate('change', event); - expect(handleChangeSpy.calledOnce).to.be.true; - tree.unmount(); - }); - it('no error styles when errorMessage undefined', () => { - const tree = shallow( - {}} />, - ); - - // No error classes. - expect(tree.children('.usa-input-error')).to.have.lengthOf(0); - expect(tree.children('.usa-input-error-label')).to.have.lengthOf(0); - expect(tree.children('.usa-input-error-message')).to.have.lengthOf(0); - - // Ensure no unnecessary class names on label w/o error.. - const labels = tree.children('label'); - expect(labels).to.have.lengthOf(1); - expect(labels.prop('className')).to.be.equal( - undefined, - 'Unnecessary class names on label without error', - ); - - // No error means no aria-describedby to not confuse screen readers. - const inputs = tree.find('input'); - expect(inputs).to.have.lengthOf(1); - expect(inputs.prop('aria-describedby')).to.be.equal( - undefined, - 'Unnecessary aria-describedby', - ); - tree.unmount(); - }); - - it('has error styles when errorMessage is set', () => { - const tree = shallow( - {}} - />, - ); - - // Ensure all error classes set. - expect(tree.find('.usa-input-error')).to.have.lengthOf(1); - - const labels = tree.find('.usa-input-error-label'); - expect(labels).to.have.lengthOf(1); - - const errorMessages = tree.find('.usa-input-error-message'); - expect(errorMessages).to.have.lengthOf(1); - expect(errorMessages.text()).to.equal('Error error message'); - - // No error means no aria-describedby to not confuse screen readers. - const inputs = tree.find('input'); - expect(inputs).to.have.lengthOf(1); - expect(inputs.prop('aria-describedby')).to.not.be.equal(undefined); - expect(inputs.prop('aria-describedby')).to.equal(errorMessages.prop('id')); - tree.unmount(); - }); - - it('required=false does not have required asterisk', () => { - const tree = shallow( - {}} />, - ); - - expect(tree.find('label').text()).to.equal('my label'); - tree.unmount(); - }); - - it('required=true has required asterisk', () => { - const tree = shallow( - {}} />, - ); - - const label = tree.find('label'); - expect(label.text()).to.equal('my label(*Required)'); - tree.unmount(); - }); - - it('label attribute propagates', () => { - const tree = shallow( - {}} />, - ); - - // Ensure label text is correct. - const labels = tree.find('label'); - expect(labels).to.have.lengthOf(1); - expect(labels.text()).to.equal('my label'); - - // Ensure label htmlFor is attached to input id. - const inputs = tree.find('input'); - expect(inputs).to.have.lengthOf(1); - expect(inputs.prop('id')).to.not.be.equal(undefined); - expect(inputs.prop('id')).to.equal(labels.prop('htmlFor')); - tree.unmount(); - }); - - it('adds aria-labelledby attribute', () => { - const tree = shallow( - {}} />, - ); - - // Ensure label text is empty string. - const labels = tree.find('label'); - expect(labels).to.have.lengthOf(1); - expect(labels.text()).to.equal(''); - - // Ensure label aria-labelledby is attached to input id. - const inputs = tree.find('input'); - expect(inputs).to.have.lengthOf(1); - expect(inputs.prop('aria-labelledby')).to.equal('headingId'); - tree.unmount(); - }); - - describe('analytics event', function () { - it('should NOT be triggered when enableAnalytics is not true', () => { - const wrapper = shallow( - {}} - />, - ); - - const spy = testAnalytics(wrapper, () => { - const event = { target: { checked: true } }; - wrapper.find('[type="checkbox"]').simulate('change', event); - }); - - expect(spy.called).to.be.false; - - wrapper.unmount(); - }); - - it('should be triggered when Checkbox is checked', () => { - const wrapper = mount( - {}} - enableAnalytics - required={false} - />, - ); - - const spy = testAnalytics(wrapper, wrapper => { - const event = { target: { checked: true } }; - wrapper.find('[type="checkbox"]').simulate('change', event); - }); - - expect( - spy.calledWith( - sinon.match.has('detail', { - componentName: 'Checkbox', - action: 'change', - details: { - label: 'test', - labelAboveCheckbox: 'this is a checkbox', - required: false, - }, - version: sinon.match.string, - }), - ), - ).to.be.true; - - wrapper.unmount(); - }); - }); -}); diff --git a/packages/react-components/src/index.js b/packages/react-components/src/index.js index 1f0de2182..25850a8b6 100644 --- a/packages/react-components/src/index.js +++ b/packages/react-components/src/index.js @@ -1,6 +1,5 @@ import AlertBox, { ALERT_TYPE } from './components/AlertBox/AlertBox'; import Breadcrumbs from './components/Breadcrumbs/Breadcrumbs'; -import Checkbox from './components/Checkbox/Checkbox'; import ExpandingGroup from './components/ExpandingGroup/ExpandingGroup'; import IconBase from './components/IconBase/IconBase'; import IconSearch from './components/IconSearch/IconSearch'; @@ -12,7 +11,6 @@ export { AlertBox, ALERT_TYPE, Breadcrumbs, - Checkbox, ExpandingGroup, IconBase, IconSearch,