From 6a02d472368c1f6164ba41e32a1f2feef8ff7fc9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 19:46:52 +0000 Subject: [PATCH 1/7] fix(DateInput): preserve year/month selection during navigation Modified handleMonthSelection and handleYearSelection to update the internal currentDate state directly using Luxon's .set() method. This ensures that when a user selects a new year, the month is preserved, and when they select a new month, the year is preserved. Removed redundant side effects that were updating the internalValue (input selection) during picker navigation, avoiding confusing UI updates before a day is actually selected. Updated version to 3.154.2. Co-authored-by: lucasn4s <17988272+lucasn4s@users.noreply.github.com> --- package-lock.json | 4 ++-- package.json | 2 +- src/components/DateInput.vue | 14 ++------------ 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7ddd1e0f..68e500fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sysvale/cuida", - "version": "3.154.1", + "version": "3.154.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@sysvale/cuida", - "version": "3.154.1", + "version": "3.154.2", "dependencies": { "@popperjs/core": "^2.11.6", "@sysvale/cuida-icons": "^1.18.0", diff --git a/package.json b/package.json index 2fa9f185..2c2b607a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sysvale/cuida", - "version": "3.154.1", + "version": "3.154.2", "description": "A design system built by Sysvale, using storybook and Vue components", "repository": { "type": "git", diff --git a/src/components/DateInput.vue b/src/components/DateInput.vue index f8d84d73..744701d6 100644 --- a/src/components/DateInput.vue +++ b/src/components/DateInput.vue @@ -829,23 +829,13 @@ function toDateTime() { } function handleMonthSelection(selectedMonth) { - internalValue.value = internalValue.value ? internalValue.value : DateTime.now().setLocale('pt-BR').toFormat('dd/MM/yyyy'); showMonthPicker.value = !showMonthPicker.value; - let [day, month, year] = internalValue.value.split('/'); - month = selectedMonth.index; - let daysInMonth = DateTime.local(+year, +month).daysInMonth; - - day = daysInMonth < day ? daysInMonth : day; - - currentDate.value = DateTime.fromFormat(`${day}/${month}/${year}`, 'dd/MM/yyyy'); + currentDate.value = currentDate.value.set({ month: parseInt(selectedMonth.index) }); } function handleYearSelection(selectedYear) { - internalValue.value = internalValue.value ? internalValue.value : DateTime.now().setLocale('pt-BR').toFormat('dd/MM/yyyy'); showYearPicker.value = !showYearPicker.value; - let [day, month] = internalValue.value.split('/'); - - currentDate.value = DateTime.fromFormat(`${day}/${month}/${selectedYear}`, 'dd/MM/yyyy'); + currentDate.value = currentDate.value.set({ year: selectedYear }); } function toggleMonthPickerDisplay() { From 6a0892fdd8a67e4b0420e7bf1a2ed17e39327140 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:15:05 +0000 Subject: [PATCH 2/7] fix(DateInput): fix incorrect year/month highlighting and navigation reset - Fix reset of year when selecting month (and vice-versa) by using currentDate.set() instead of relying on internalValue. - Fix incorrect highlighting of January/CurrentYear in selection grids when no date is selected. - Updated Vitest snapshots for DateInput. - Updated project version to 3.154.2. Co-authored-by: lucasn4s <17988272+lucasn4s@users.noreply.github.com> --- docs/.docgen/components-metadata.json | 171 ++++++++++++++++-- .../InternalComponents/MonthSelectorGrid.vue | 4 +- .../InternalComponents/YearSelectorGrid.vue | 15 +- src/tests/SelectorGridHighlight.spec.js | 60 ++++++ .../__snapshots__/DateInput.spec.js.snap | 4 +- 5 files changed, 226 insertions(+), 28 deletions(-) create mode 100644 src/tests/SelectorGridHighlight.spec.js diff --git a/docs/.docgen/components-metadata.json b/docs/.docgen/components-metadata.json index 3209ee87..fea21679 100644 --- a/docs/.docgen/components-metadata.json +++ b/docs/.docgen/components-metadata.json @@ -811,7 +811,7 @@ }, "defaultValue": { "func": false, - "value": "1" + "value": "5" } }, { @@ -822,7 +822,7 @@ }, "defaultValue": { "func": false, - "value": "false" + "value": "true" } } ], @@ -1144,6 +1144,17 @@ "func": false, "value": "false" } + }, + { + "name": "ghost", + "description": "Especifica se o componente deve ser exibido na sua versão ghost.", + "type": { + "name": "boolean" + }, + "defaultValue": { + "func": false, + "value": "false" + } } ], "events": [ @@ -1410,6 +1421,17 @@ "func": false, "value": "false" } + }, + { + "name": "ghost", + "description": "Especifica se o componente deve ser exibido na sua versão ghost.", + "type": { + "name": "boolean" + }, + "defaultValue": { + "func": false, + "value": "false" + } } ], "slots": [ @@ -1637,8 +1659,9 @@ ] }, "CdsButton": { - "displayName": "CdsButton", + "name": "CdsButton", "exportName": "default", + "displayName": "Button", "description": "", "tags": {}, "props": [ @@ -1740,7 +1763,7 @@ }, { "name": "loading", - "description": "Especifica se a versão do Botão é a secundária.", + "description": "Especifica se o botão deve mostrar spinner de carregamento.\nCaso o botão seja do tipo ghost ou secondary a variante do spinner é a mesma passada na prop variant.", "type": { "name": "boolean" }, @@ -2876,6 +2899,35 @@ "func": false, "value": "[]" } + }, + { + "name": "state", + "description": "Especifica o estado do TextInput. As opções são 'default', 'valid', 'loading' e 'invalid'.", + "tags": {}, + "values": [ + "default", + "valid", + "loading", + "invalid" + ], + "type": { + "name": "string" + }, + "defaultValue": { + "func": false, + "value": "'default'" + } + }, + { + "name": "errorMessage", + "description": "Especifica a mensagem de erro, que será exibida caso o estado seja inválido", + "type": { + "name": "string" + }, + "defaultValue": { + "func": false, + "value": "'Valor inválido'" + } } ], "events": [ @@ -4323,8 +4375,9 @@ ] }, "CdsDropdownButton": { - "displayName": "CdsDropdownButton", + "name": "CdsDropdownButton", "exportName": "default", + "displayName": "DropdownButton", "description": "", "tags": {}, "props": [ @@ -4424,27 +4477,38 @@ "md", "lg" ] - } - ], - "events": [ + }, { - "name": "click", - "description": "Evento que indica que o DropdownButton foi clicado", + "name": "disabled", + "description": "Desabilita o input.", "type": { - "names": [ - "Event" - ] + "name": "boolean" + }, + "defaultValue": { + "func": false, + "value": "false" } }, { - "name": "action-click", + "name": "tooltipText", + "description": "Texto a ser exibido como tooltip com o hover do DropdownButton quando a prop disabled estiver ativa.", "type": { - "names": [ - "undefined" - ] + "name": "string" + }, + "defaultValue": { + "func": false, + "value": "null" } } ], + "events": [ + { + "name": "button-click" + }, + { + "name": "action-click" + } + ], "sourceFiles": [ "src/components/DropdownButton.vue" ] @@ -8390,7 +8454,7 @@ }, "defaultValue": { "func": false, - "value": "'https://cuida.framer.wiki/'" + "value": "null" } }, { @@ -10730,6 +10794,18 @@ "value": "false" } }, + { + "name": "deepSearch", + "description": "Indica se a busca deve levar em consideração argumentos compostos.\nSó tem efeito se a prop `searchable` for `true`.", + "type": { + "name": "boolean" + }, + "required": false, + "defaultValue": { + "func": false, + "value": "false" + } + }, { "name": "width", "tags": { @@ -10927,6 +11003,17 @@ "func": false, "value": "false" } + }, + { + "name": "ghost", + "description": "Especifica se o componente deve ser exibido na sua versão ghost.", + "type": { + "name": "boolean" + }, + "defaultValue": { + "func": false, + "value": "false" + } } ], "slots": [ @@ -12673,6 +12760,17 @@ "func": false, "value": "false" } + }, + { + "name": "loading", + "description": "Ativa o estado de carregamento do componente, exibindo um Skeleton para a tabela.", + "type": { + "name": "boolean" + }, + "defaultValue": { + "func": false, + "value": "false" + } } ], "events": [ @@ -13354,7 +13452,7 @@ }, "defaultValue": { "func": false, - "value": "'https://cuida.framer.wiki/'" + "value": "null" } }, { @@ -13442,6 +13540,11 @@ } } ], + "slots": [ + { + "name": "label" + } + ], "sourceFiles": [ "src/components/TextInput.vue" ] @@ -14643,6 +14746,16 @@ "func": false, "value": "''" } + }, + { + "name": "year", + "type": { + "name": "number" + }, + "defaultValue": { + "func": false, + "value": "new Date().getFullYear()" + } } ], "events": [ @@ -14681,6 +14794,26 @@ "func": false, "value": "''" } + }, + { + "name": "minDate", + "type": { + "name": "string" + }, + "defaultValue": { + "func": false, + "value": "''" + } + }, + { + "name": "maxDate", + "type": { + "name": "string" + }, + "defaultValue": { + "func": false, + "value": "''" + } } ], "events": [ diff --git a/src/components/InternalComponents/MonthSelectorGrid.vue b/src/components/InternalComponents/MonthSelectorGrid.vue index 7a88fe8a..49972af6 100644 --- a/src/components/InternalComponents/MonthSelectorGrid.vue +++ b/src/components/InternalComponents/MonthSelectorGrid.vue @@ -58,9 +58,9 @@ const internalMaxDate = ref(props.maxDate); /* COMPUTED */ const currentMonth = computed(() => { - if (!internalDate.value) return 1; + if (!internalDate.value) return null; const month = internalDate.value.split('-')[1]; - return month ? parseInt(month) : 1; + return month ? parseInt(month) : null; }); /* WATCHERS */ diff --git a/src/components/InternalComponents/YearSelectorGrid.vue b/src/components/InternalComponents/YearSelectorGrid.vue index 3685953c..5e9204cd 100644 --- a/src/components/InternalComponents/YearSelectorGrid.vue +++ b/src/components/InternalComponents/YearSelectorGrid.vue @@ -61,20 +61,25 @@ const props = defineProps({ const emits = defineEmits(['click']); /* REACTIVE DATA */ -const currentYear = ref(new Date().getFullYear()); +const todayYear = new Date().getFullYear(); +const selectedYear = computed(() => { + if (!props.selectedDate) return null; + return parseInt(props.selectedDate.split('-')[0]); +}); + const minYear = computed(() => { if (props.minDate) { return parseInt(props.minDate.split('-')[0]); } - return currentYear.value - 120; + return todayYear - 120; }); const maxYear = computed(() => { if (props.maxDate) { return parseInt(props.maxDate.split('-')[0]); } - return currentYear.value + 50; + return todayYear + 50; }); -const initialYear = ref(currentYear.value - 7); +const initialYear = ref((selectedYear.value || todayYear) - 7); const scrollThumbHeight = ref(33); const isDragging = ref(false); const startY = ref(0); @@ -113,7 +118,7 @@ function yearSelectorClasses(year) { let classes = { [`year-selector__year--${props.variant}`]: true, 'year-selector__year--disabled':( year < minYear.value) || (year > maxYear.value), - [`year-selector__year--selected--${props.variant}`]: year == currentYear.value, + [`year-selector__year--selected--${props.variant}`]: year == selectedYear.value, } return classes; diff --git a/src/tests/SelectorGridHighlight.spec.js b/src/tests/SelectorGridHighlight.spec.js new file mode 100644 index 00000000..c7969e54 --- /dev/null +++ b/src/tests/SelectorGridHighlight.spec.js @@ -0,0 +1,60 @@ +import { describe, test, expect, beforeEach, vi } from 'vitest'; +import { mount } from '@vue/test-utils'; +import MonthSelectorGrid from '../components/InternalComponents/MonthSelectorGrid.vue'; +import YearSelectorGrid from '../components/InternalComponents/YearSelectorGrid.vue'; + +describe('SelectorGrid Highlight Fix', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date(2026, 1, 10)); // Feb 10, 2026 + }); + + describe('MonthSelectorGrid', () => { + test('no month should be highlighted when selectedDate is empty', () => { + const wrapper = mount(MonthSelectorGrid, { + props: { + selectedDate: '', + }, + }); + const selectedMonths = wrapper.findAll('.month-selector__month--selected--green'); + expect(selectedMonths.length).toBe(0); + }); + + test('correct month should be highlighted when selectedDate is provided', () => { + const wrapper = mount(MonthSelectorGrid, { + props: { + selectedDate: '2025-05-15', + }, + }); + // May is index 4 (0-based) in MONTHS array, so it's the 5th element + const months = wrapper.findAll('.month-selector__month'); + expect(months[4].classes()).toContain('month-selector__month--selected--green'); + + const selectedMonths = wrapper.findAll('[class*="month-selector__month--selected--"]'); + expect(selectedMonths.length).toBe(1); + }); + }); + + describe('YearSelectorGrid', () => { + test('no year should be highlighted when selectedDate is empty', () => { + const wrapper = mount(YearSelectorGrid, { + props: { + selectedDate: '', + }, + }); + const selectedYears = wrapper.findAll('.year-selector__year--selected--green'); + expect(selectedYears.length).toBe(0); + }); + + test('correct year should be highlighted when selectedDate is provided', () => { + const wrapper = mount(YearSelectorGrid, { + props: { + selectedDate: '2025-05-15', + }, + }); + const selectedYears = wrapper.findAll('.year-selector__year--selected--green'); + expect(selectedYears.length).toBe(1); + expect(selectedYears[0].text()).toBe('2025'); + }); + }); +}); diff --git a/src/tests/__snapshots__/DateInput.spec.js.snap b/src/tests/__snapshots__/DateInput.spec.js.snap index ec44b8bf..53fccae0 100644 --- a/src/tests/__snapshots__/DateInput.spec.js.snap +++ b/src/tests/__snapshots__/DateInput.spec.js.snap @@ -95,7 +95,7 @@ exports[`DateInput > renders correctly 1`] = `