From 0acf44f101dfd115a1faab3aa26ff78a5ad70939 Mon Sep 17 00:00:00 2001
From: mbucknel <mbucknell@usgs.gov>
Date: Wed, 1 Jun 2022 16:24:41 -0500
Subject: [PATCH] Added a USWDS checkbox component

---
 .../vue-components/graph-controls.vue         | 69 ++++++---------
 .../scripts/uswds-components/checkbox.test.js | 86 +++++++++++++++++++
 .../src/scripts/uswds-components/checkbox.vue | 71 +++++++++++++++
 3 files changed, 185 insertions(+), 41 deletions(-)
 create mode 100644 assets/src/scripts/uswds-components/checkbox.test.js
 create mode 100644 assets/src/scripts/uswds-components/checkbox.vue

diff --git a/assets/src/scripts/monitoring-location/components/hydrograph/vue-components/graph-controls.vue b/assets/src/scripts/monitoring-location/components/hydrograph/vue-components/graph-controls.vue
index 5c4c3dbd7..76cc102ed 100644
--- a/assets/src/scripts/monitoring-location/components/hydrograph/vue-components/graph-controls.vue
+++ b/assets/src/scripts/monitoring-location/components/hydrograph/vue-components/graph-controls.vue
@@ -1,40 +1,22 @@
 <template>
   <div>
-    <div class="usa-checkbox">
-      <input
-        id="iv-compare-timeseries-checkbox"
-        class="usa-checkbox__input"
-        type="checkbox"
-        name="graphControls"
-        value="compare"
-        :checked="compareChecked"
-        :disabled="!compareEnabled"
-        @change="selectCompare"
-      >
-      <label
-        class="usa-checkbox__label"
-        for="iv-compare-timeseries-checkbox"
-      >
-        Compare to last year
-      </label>
-    </div>
-    <div class="usa-checkbox">
-      <input
-        id="iv-median-timeseries-checkbox"
-        class="usa-checkbox__input"
-        type="checkbox"
-        name="graphControls"
-        value="median"
-        :checked="medianChecked"
-        @change="selectMedian"
-      >
-      <label
-        class="usa-checkbox__label"
-        for="iv-median-timeseries-checkbox"
-      >
-        Display median
-      </label>
-    </div>
+    <USWDSCheckbox
+      label="Compare to last year"
+      name="graphControls"
+      value="compare"
+      id="iv-compare-timeseries-checkbox"
+      :is-checked="compareChecked"
+      :is-checkbox-disabled="!compareEnabled"
+      @toggleCheckbox="selectCompare"
+    />
+    <USWDSCheckbox
+      label="Display median"
+      name="graphControls"
+      value="median"
+      id="iv-median-timeseries-checkbox"
+      :is-checked="medianChecked"
+      @toggleCheckbox="selectMedian"
+    />
   </div>
 </template>
 
@@ -44,6 +26,7 @@ import {createSelector} from 'reselect';
 import {inject} from 'vue';
 
 import config from 'ui/config';
+import USWDSCheckbox from 'ui/uswds-components/checkbox.vue';
 
 import {getSelectedParameterCode, getSelectedTimeSpan} from 'ml/selectors/hydrograph-state-selector';
 import {getTimeRange} from 'ml/selectors/hydrograph-data-selector';
@@ -55,6 +38,10 @@ import {showDataIndicators} from '../data-indicator';
 
 export default {
   name: 'GraphControls',
+  components: {
+    USWDSCheckbox
+  },
+
   setup() {
     const hasIVData = function(parameterCode) {
       return config.ivPeriodOfRecord && parameterCode in config.ivPeriodOfRecord;
@@ -86,11 +73,11 @@ export default {
     const reduxStore = inject('store');
     const siteno = inject('siteno');
 
-    function selectCompare(ev) {
+    function selectCompare(checked) {
       const reduxState = reduxStore.getState();
       const currentTimeRange = getTimeRange('current')(reduxState);
-      actions.setCompareDataVisibility(ev.target.checked);
-      if (ev.target.checked) {
+      actions.setCompareDataVisibility(checked);
+      if (checked) {
         showDataIndicators(true, reduxStore);
         actions.retrievePriorYearIVData(siteno, {
             parameterCode: getSelectedParameterCode(reduxState),
@@ -104,10 +91,10 @@ export default {
       }
     }
 
-    function selectMedian(ev) {
+    function selectMedian(checked) {
       const reduxState = reduxStore.getState();
-      actions.setMedianDataVisibility(ev.target.checked);
-      if (ev.target.checked) {
+      actions.setMedianDataVisibility(checked);
+      if (checked) {
         showDataIndicators(true, reduxStore);
         actions.retrieveMedianStatistics(siteno, getSelectedParameterCode(reduxState))
           .then(() => {
diff --git a/assets/src/scripts/uswds-components/checkbox.test.js b/assets/src/scripts/uswds-components/checkbox.test.js
new file mode 100644
index 000000000..d04cddad2
--- /dev/null
+++ b/assets/src/scripts/uswds-components/checkbox.test.js
@@ -0,0 +1,86 @@
+import {mount} from '@vue/test-utils';
+
+import USWDSCheckbox from './checkbox.vue';
+
+describe('components/uswds/checkbox', () => {
+    it('Expects a properly rendered unchecked USWDS checkbox', () => {
+        const wrapper = mount(USWDSCheckbox, {
+            props: {
+                label: 'This checkbox',
+                name: 'checkbox-name',
+                value: 'yes',
+                id: 'my-unique-id'
+            }
+        });
+
+        const checkbox = wrapper.find('.usa-checkbox__input');
+        const checkboxLabel = wrapper.find('.usa-checkbox__label');
+        expect(checkbox.attributes('id')).toBe('my-unique-id');
+        expect(checkbox.attributes('name')).toBe('checkbox-name');
+        expect(checkbox.attributes('value')).toBe('yes');
+        expect(checkbox.element.checked).toBe(false);
+        expect(checkboxLabel.attributes('for')).toBe('my-unique-id');
+        expect(checkboxLabel.text()).toBe('This checkbox');
+    });
+
+    it('Expects a checkbox that is initial checked', () => {
+        const wrapper = mount(USWDSCheckbox, {
+            props: {
+                label: 'This checkbox',
+                name: 'checkbox-name',
+                value: 'yes',
+                id: 'my-unique-id',
+                isChecked: true
+            }
+        });
+
+        const checkbox = wrapper.find('.usa-checkbox__input');
+        expect(checkbox.element.checked).toBe(true);
+    });
+
+    it('Expect to emit a toggleCheckbox event if the checkbox is clicked', async() => {
+        const wrapper = mount(USWDSCheckbox, {
+            props: {
+                label: 'This checkbox',
+                name: 'checkbox-name',
+                value: 'yes',
+                id: 'my-unique-id'
+            }
+        });
+        const checkbox = wrapper.find('.usa-checkbox__input');
+        checkbox.setChecked(true);
+
+        expect(wrapper.emitted('toggleCheckbox')[0][0]).toBe(true);
+
+        checkbox.setChecked(false);
+
+        expect(wrapper.emitted('toggleCheckbox')[1][0]).toBe(false);
+    });
+
+    it('Expect checkbox to be disabled when prop true', () => {
+        const wrapper = mount(USWDSCheckbox, {
+            props: {
+                label: 'This checkbox',
+                name: 'checkbox-name',
+                value: 'yes',
+                id: 'my-unique-id',
+                isCheckboxDisabled: true
+            }
+        });
+        const checkboxInput = wrapper.find('input');
+        expect(checkboxInput.attributes('disabled')).toBeDefined();
+    });
+
+    it('Expect checkbox to be enabled by default', () => {
+        const wrapper = mount(USWDSCheckbox, {
+            props: {
+                label: 'This checkbox',
+                name: 'checkbox-name',
+                value: 'yes',
+                id: 'my-unique-id'
+            }
+        });
+        const checkboxInput = wrapper.find('input');
+        expect(checkboxInput.attributes('disabled')).not.toBeDefined();
+    });
+});
diff --git a/assets/src/scripts/uswds-components/checkbox.vue b/assets/src/scripts/uswds-components/checkbox.vue
new file mode 100644
index 000000000..1dbf39c41
--- /dev/null
+++ b/assets/src/scripts/uswds-components/checkbox.vue
@@ -0,0 +1,71 @@
+<template>
+  <div class="usa-checkbox">
+    <input
+      :id="id"
+      class="usa-checkbox__input"
+      type="checkbox"
+      :name="name"
+      :value="value"
+      :checked="isChecked"
+      :disabled="isCheckboxDisabled"
+      @change="toggleCheckbox"
+    >
+    <label
+      class="usa-checkbox__label"
+      :for="id"
+    >
+      {{ label }}
+    </label>
+  </div>
+</template>
+
+<script>
+/*
+ * @vue-prop {String} label
+ * @vue-prop {String} name
+ * @vue-prop {String} value
+ * @vue-prop {String} id - should be unique with the DOM
+ * @vue-prop {Boolean} isChecked - defaults to false
+ * @vue-prop {Boolean} isCheckboxDisabled - checkbox disabled when set to true, default is false
+ * @vue-event {DOM Event} toggleCheckbox
+ */
+export default {
+  name: 'USWDSCheckbox',
+  props: {
+    label: {
+      type: String,
+      required: true
+    },
+    name: {
+      type: String,
+      required: true
+    },
+    value: {
+      type: String,
+      required: true
+    },
+    id: {
+      type: String,
+      required: true
+    },
+    isChecked: {
+      type: Boolean,
+      default: false
+    },
+    isCheckboxDisabled: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['toggleCheckbox'],
+  setup(props, {emit}) {
+    function toggleCheckbox(ev) {
+      emit('toggleCheckbox', ev.currentTarget.checked);
+    }
+
+    return {
+      toggleCheckbox
+    };
+  }
+};
+</script>
-- 
GitLab