running-tools

A collection of tools for runners and their coaches
git clone https://git.ashermorgan.net/running-tools/
Log | Files | Refs | README

commit 147f47f096469669d4925a172f91ff35feba94f2
parent bfd85b24ed19608c23f7a2d4e9f5c2e7fd516ed6
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Sat, 28 Jun 2025 14:52:15 -0700

Use withDefaults macro for most prop definitions

Also removed unnecessary default prop values.

Diffstat:
Msrc/components/DoubleOutputTable.vue | 30++++++++----------------------
Msrc/components/PaceInput.vue | 24+++++++-----------------
Msrc/components/RaceOptions.vue | 16+++++-----------
Msrc/components/SingleOutputTable.vue | 24++++++++----------------
Msrc/components/SplitOutputTable.vue | 10+++-------
Msrc/components/TargetEditor.vue | 15+++++----------
Msrc/components/TargetSetSelector.vue | 14++++++--------
Mtests/unit/components/PaceInput.spec.js | 10+++++++++-
Mtests/unit/components/RaceOptions.spec.js | 9++++++++-
Mtests/unit/components/TargetEditor.spec.js | 40+++++++++++++++++++++++++++++-----------
Mtests/unit/components/TargetSetSelector.spec.js | 6+++++-
11 files changed, 93 insertions(+), 105 deletions(-)

diff --git a/src/components/DoubleOutputTable.vue b/src/components/DoubleOutputTable.vue @@ -28,7 +28,6 @@ <script setup lang="ts"> import { computed } from 'vue'; -import type { PropType } from 'vue'; import { ResultType } from '@/utils/calculators'; import type { TargetResult } from '@/utils/calculators'; @@ -37,42 +36,29 @@ import type { Target } from '@/utils/targets'; import { DistanceUnitData } from '@/utils/units'; import type { Distance, DistanceTime } from '@/utils/units'; -const props = defineProps({ +interface Props { /** * The method that generates the target table rows */ - calculateResult: { - type: Function as PropType<(x: DistanceTime, y: Target) => TargetResult>, - required: true, - }, + calculateResult: (x: DistanceTime, y: Target) => TargetResult, /** * The target set */ - targets: { - type: Array<Target>, - default: () => [], - }, + targets: Array<Target>, /** * The set of input times */ - inputTimes: { - type: Array<number>, - default: () => [], - }, + inputTimes: Array<number>, /** * The input distance */ - inputDistance: { - type: Object as PropType<Distance>, - default: () => ({ - distanceValue: 5, - distanceUnit: 'kilometers', - }), - } -}); + inputDistance: Distance, +} + +const props = defineProps<Props>(); /** * The target table results diff --git a/src/components/PaceInput.vue b/src/components/PaceInput.vue @@ -18,8 +18,6 @@ </template> <script setup lang="ts"> -import type { PropType } from 'vue'; - import { DistanceUnits, DistanceUnitData } from '@/utils/units'; import type { DistanceTime } from '@/utils/units'; @@ -27,27 +25,19 @@ import DecimalInput from '@/components/DecimalInput.vue'; import TimeInput from '@/components/TimeInput.vue'; import useObjectModel from '@/composables/useObjectModel'; -const props = defineProps({ +interface Props { /** - * The prefix for each field's aria-label + * The prefix for each field's aria-label (defaults to 'Input') */ - label: { - type: String, - default: 'Input', - }, + label?: string, /** * The component value */ - modelValue: { - type: Object as PropType<DistanceTime>, - default: () => ({ - distanceValue: 5, - distanceUnit: 'kilometers', - time: 1200, - }), - }, -}); + modelValue: DistanceTime, +}; + +const props = withDefaults(defineProps<Props>(), { label: 'Input' }); // Generate internal ref tied to modelValue prop const emit = defineEmits(['update:modelValue']); diff --git a/src/components/RaceOptions.vue b/src/components/RaceOptions.vue @@ -18,25 +18,19 @@ </template> <script setup lang="ts"> -import type { PropType } from 'vue'; - import type { RaceOptions } from '@/utils/calculators'; import DecimalInput from '@/components/DecimalInput.vue'; import useObjectModel from '@/composables/useObjectModel'; -const props = defineProps({ +interface Props { /** * The component value */ - modelValue: { - type: Object as PropType<RaceOptions>, - default: () => ({ - model: 'AverageModel', - riegelExponent: 1.06, - }), - }, -}); + modelValue: RaceOptions, +} + +const props = defineProps<Props>(); // Generate internal ref tied to modelValue prop const emit = defineEmits(['update:modelValue']); diff --git a/src/components/SingleOutputTable.vue b/src/components/SingleOutputTable.vue @@ -38,36 +38,28 @@ <script setup lang="ts"> import { computed } from 'vue'; -import type { PropType } from 'vue'; import type { TargetResult } from '@/utils/calculators'; import type { Target } from '@/utils/targets'; -const props = defineProps({ +interface Props { /** * The method that generates the target table rows */ - calculateResult: { - type: Function as PropType<(x: Target) => TargetResult>, - required: true, - }, + calculateResult: (x: Target) => TargetResult, /** * The target set */ - targets: { - type: Array<Target>, - default: () => [], - }, + targets: Array<Target>, /** - * Whether to show result paces + * Whether to show result paces (defaults to false) */ - showPace: { - type: Boolean, - default: false, - }, -}); + showPace?: boolean, +}; + +const props = withDefaults(defineProps<Props>(), { showPace: false }); /** * The target table results diff --git a/src/components/SplitOutputTable.vue b/src/components/SplitOutputTable.vue @@ -48,7 +48,6 @@ <script setup lang="ts"> import { computed } from 'vue'; -import type { PropType } from 'vue'; import { formatDuration, formatNumber } from '@/utils/format'; import type { SplitTarget } from '@/utils/targets'; @@ -69,20 +68,17 @@ interface SplitTargetResult { interface Props { /** - * The unit system to use when showing result paces + * The unit system to use when showing result paces (defaults to metric) */ defaultUnitSystem?: UnitSystems, /** * The split targets */ - modelValue?: Array<SplitTarget>, + modelValue: Array<SplitTarget>, }; -const props = withDefaults(defineProps<Props>(), { - defaultUnitSystem: UnitSystems.Metric, - modelValue: [], -}); +const props = withDefaults(defineProps<Props>(), { defaultUnitSystem: UnitSystems.Metric }); // Generate internal ref tied to modelValue prop const emit = defineEmits(['update:modelValue']); diff --git a/src/components/TargetEditor.vue b/src/components/TargetEditor.vue @@ -88,7 +88,6 @@ <script setup lang="ts"> import VueFeather from 'vue-feather'; -import type { PropType } from 'vue'; import { TargetType, TargetSetType, workoutTargetToString } from '@/utils/targets'; import type { StandardTargetSet, TargetSet, WorkoutTarget, WorkoutTargetSet } from '@/utils/targets'; @@ -100,26 +99,26 @@ import useObjectModel from '@/composables/useObjectModel'; interface Props { /** - * Whether to allow custom names for workout targets + * Whether to allow custom names for workout targets (defaults to false) */ customWorkoutNames?: boolean, /** - * The unit system to use when creating distance targets + * The unit system to use when creating distance targets (defaults to metric) */ defaultUnitSystem?: UnitSystems, /** - * Whether the target set is a custom or default set + * Whether the target set is a custom or default set (defaults to false) */ isCustomSet?: boolean, /** * The component value */ - modelValue?: TargetSet, + modelValue: TargetSet, /** - * The target set type ('standard', 'split', or 'workout') + * The target set type (Standard, Split, or Workout, defaults to Standard) */ setType?: TargetSetType, } @@ -128,10 +127,6 @@ const props = withDefaults(defineProps<Props>(), { customWorkoutNames: false, defaultUnitSystem: UnitSystems.Metric, isCustomSet: false, - modelValue: { - name: 'New target set', - targets: [], - }, setType: TargetSetType.Standard, }); diff --git a/src/components/TargetSetSelector.vue b/src/components/TargetSetSelector.vue @@ -22,7 +22,6 @@ <script setup lang="ts"> import { computed, nextTick, ref } from 'vue'; -import type { PropType } from 'vue'; import VueFeather from 'vue-feather'; @@ -39,29 +38,29 @@ import useObjectModel from '@/composables/useObjectModel'; */ const model = defineModel('selectedTargetSet', { type: String, - default: '_new', + required: true, }); interface Props { /** - * Whether to allow custom names for workout targets + * Whether to allow custom names for workout targets (defaults to false) */ - customWorkoutNames?: Boolean, + customWorkoutNames?: boolean, /** - * The unit system to use when creating distance targets + * The unit system to use when creating distance targets (defaults to metric) */ defaultUnitSystem?: UnitSystems, /** - * The target set type ('standard', 'split', or 'workout') + * The target set type (Standard, Split, or Workout, defaults to Standard) */ setType?: TargetSetType, /** * The target sets */ - targetSets?: TargetSets, + targetSets: TargetSets, }; @@ -69,7 +68,6 @@ const props = withDefaults(defineProps<Props>(), { customWorkoutNames: false, defaultUnitSystem: UnitSystems.Metric, setType: TargetSetType.Standard, - targetSets: {} }); // Generate internal ref tied to modelValue prop diff --git a/tests/unit/components/PaceInput.spec.js b/tests/unit/components/PaceInput.spec.js @@ -22,7 +22,15 @@ test('should be initialized to modelValue', () => { test('should emit event when inputs are modified', async () => { // Initialize component - const wrapper = shallowMount(PaceInput); + const wrapper = shallowMount(PaceInput, { + propsData: { + modelValue: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, + }, + }); // Update distance value await wrapper.findComponent({ name: 'decimal-input' }).setValue(3); diff --git a/tests/unit/components/RaceOptions.spec.js b/tests/unit/components/RaceOptions.spec.js @@ -20,7 +20,14 @@ test('should be initialized to modelValue', () => { test('should emit event when inputs are modified', async () => { // Initialize component - const wrapper = shallowMount(RaceOptions); + const wrapper = shallowMount(RaceOptions, { + propsData: { + modelValue: { + model: 'AverageModel', + riegelExponent: 1.06, + }, + }, + }); // Update model await wrapper.find('select').setValue('CameronModel'); diff --git a/tests/unit/components/TargetEditor.spec.js b/tests/unit/components/TargetEditor.spec.js @@ -165,7 +165,14 @@ test('should correctly render workout target set with custom names', async () => test('revert button should emit revert event', async () => { // Initialize component - const wrapper = shallowMount(TargetEditor); + const wrapper = shallowMount(TargetEditor, { + propsData: { + modelValue: { + name: 'My target set', + targets: [], + }, + }, + }); // Click revert button await wrapper.find('button[title="Revert target set"]').trigger('click'); @@ -178,8 +185,12 @@ test('delete button should emit revert event', async () => { // Initialize component const wrapper = shallowMount(TargetEditor, { propsData: { + modelValue: { + name: 'My target set', + targets: [], + }, isCustomSet: true, - } + }, }); // Click delete button @@ -191,7 +202,14 @@ test('delete button should emit revert event', async () => { test('close button should emit close event', async () => { // Initialize component - const wrapper = shallowMount(TargetEditor); + const wrapper = shallowMount(TargetEditor, { + propsData: { + modelValue: { + name: 'My target set', + targets: [], + }, + }, + }); // Call close method await wrapper.find('button[title="Close"]').trigger('click'); @@ -212,7 +230,7 @@ test('add distance target button should correctly add standard imperial distance ], }, setType: 'standard', - defaultUnitSystem: 'imperial' + defaultUnitSystem: 'imperial', }, }); @@ -244,7 +262,7 @@ test('add distance target button should correctly add standard metric distance t ], }, setType: 'standard', - defaultUnitSystem: 'metric' + defaultUnitSystem: 'metric', }, }); @@ -275,7 +293,7 @@ test('add distance target button should correctly add split imperial distance ta ], }, setType: 'split', - defaultUnitSystem: 'imperial' + defaultUnitSystem: 'imperial', }, }); @@ -305,7 +323,7 @@ test('add distance target button should correctly add split metric distance targ ], }, setType: 'split', - defaultUnitSystem: 'metric' + defaultUnitSystem: 'metric', }, }); @@ -344,7 +362,7 @@ test('add distance target button should correctly add workout imperial distance ], }, setType: 'workout', - defaultUnitSystem: 'imperial' + defaultUnitSystem: 'imperial', }, }); @@ -396,7 +414,7 @@ test('add distance target button should correctly add workout metric distance ta ], }, setType: 'workout', - defaultUnitSystem: 'metric' + defaultUnitSystem: 'metric', }, }); @@ -497,7 +515,7 @@ test('add time target button should correctly add workout imperial time target', ], }, setType: 'workout', - defaultUnitSystem: 'imperial' + defaultUnitSystem: 'imperial', }, }); @@ -549,7 +567,7 @@ test('add time target button should correctly add workout metric time target', a ], }, setType: 'workout', - defaultUnitSystem: 'metric' + defaultUnitSystem: 'metric', }, }); diff --git a/tests/unit/components/TargetSetSelector.spec.js b/tests/unit/components/TargetSetSelector.spec.js @@ -387,8 +387,10 @@ test('should sort target set after target editor is closed', async () => { test('should correctly pass setType prop to TargetEditor', async () => { const wrapper = shallowMount(TargetSetSelector, { propsData: { + selectedTargetSet: '_new', + targetSets: {}, setType: 'foo' - } + }, }); // Assert target editor props are correct @@ -398,6 +400,8 @@ test('should correctly pass setType prop to TargetEditor', async () => { test('should correctly pass customWorkoutNames prop to TargetEditor', async () => { const wrapper = shallowMount(TargetSetSelector, { propsData: { + selectedTargetSet: '_new', + targetSets: {}, customWorkoutNames: false, } });