feat(datetime): add animation to adjacent days selection (#30298)
Issue number: internal --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> Now when an adjacent day is selected the component will scroll to the pretended month, as it does when a month change via arrow buttons. ## Changes: - add styles for active adjacent day; - scroll animation when adjacentDay is selected; ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information - [md preview](https://ionic-framework-git-rou-11744-ionic1.vercel.app/src/components/datetime/test/show-adjacent-days?ionic:mode=md) - [ios preview](https://ionic-framework-git-rou-11744-ionic1.vercel.app/src/components/datetime/test/show-adjacent-days?ionic:mode=ios ) <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> --------- Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com> Co-authored-by: ionitron <hi@ionicframework.com>
@ -251,7 +251,8 @@
 | 
				
			|||||||
 * is selected should have ion-color for
 | 
					 * is selected should have ion-color for
 | 
				
			||||||
 * text color and be bolder.
 | 
					 * text color and be bolder.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
:host .calendar-day.calendar-day-active {
 | 
					:host .calendar-day.calendar-day-active,
 | 
				
			||||||
 | 
					:host .calendar-day.calendar-day-adjacent-day.calendar-day-active {
 | 
				
			||||||
  color: current-color(base);
 | 
					  color: current-color(base);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  font-weight: 600;
 | 
					  font-weight: 600;
 | 
				
			||||||
 | 
				
			|||||||
@ -117,7 +117,8 @@
 | 
				
			|||||||
 * is selected should have ion-color for
 | 
					 * is selected should have ion-color for
 | 
				
			||||||
 * text color and be bolder.
 | 
					 * text color and be bolder.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
:host .calendar-day.calendar-day-active {
 | 
					:host .calendar-day.calendar-day-active,
 | 
				
			||||||
 | 
					:host .calendar-day.calendar-day-adjacent-day.calendar-day-active {
 | 
				
			||||||
  color: current-color(contrast);
 | 
					  color: current-color(contrast);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2383,36 +2383,25 @@ export class Datetime implements ComponentInterface {
 | 
				
			|||||||
                    if (isAdjacentDay) {
 | 
					                    if (isAdjacentDay) {
 | 
				
			||||||
                      // The user selected a day outside the current month. Ignore this button, as the month will be re-rendered.
 | 
					                      // The user selected a day outside the current month. Ignore this button, as the month will be re-rendered.
 | 
				
			||||||
                      this.el.blur();
 | 
					                      this.el.blur();
 | 
				
			||||||
                    }
 | 
					                      this.activeParts = { ...activePart, ...referenceParts };
 | 
				
			||||||
 | 
					                      this.animateToDate(referenceParts);
 | 
				
			||||||
 | 
					                      this.confirm();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
                      this.setWorkingParts({
 | 
					                      this.setWorkingParts({
 | 
				
			||||||
                        ...this.workingParts,
 | 
					                        ...this.workingParts,
 | 
				
			||||||
                      month: _month,
 | 
					                        ...referenceParts,
 | 
				
			||||||
                      day,
 | 
					 | 
				
			||||||
                      year: _year,
 | 
					 | 
				
			||||||
                      isAdjacentDay,
 | 
					 | 
				
			||||||
                      });
 | 
					                      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // multiple only needs date info, so we can wipe out other fields like time
 | 
					                      // Multiple only needs date info so we can wipe out other fields like time.
 | 
				
			||||||
                      if (multiple) {
 | 
					                      if (multiple) {
 | 
				
			||||||
                      this.setActiveParts(
 | 
					                        this.setActiveParts(referenceParts, isActive);
 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                          month: _month,
 | 
					 | 
				
			||||||
                          day,
 | 
					 | 
				
			||||||
                          year: _year,
 | 
					 | 
				
			||||||
                          isAdjacentDay,
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        isActive
 | 
					 | 
				
			||||||
                      );
 | 
					 | 
				
			||||||
                      } else {
 | 
					                      } else {
 | 
				
			||||||
                        this.setActiveParts({
 | 
					                        this.setActiveParts({
 | 
				
			||||||
                          ...activePart,
 | 
					                          ...activePart,
 | 
				
			||||||
                        month: _month,
 | 
					                          ...referenceParts,
 | 
				
			||||||
                        day,
 | 
					 | 
				
			||||||
                        year: _year,
 | 
					 | 
				
			||||||
                        isAdjacentDay,
 | 
					 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                      }
 | 
					                      }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                  }}
 | 
					                  }}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                  {text}
 | 
					                  {text}
 | 
				
			||||||
 | 
				
			|||||||
@ -47,5 +47,75 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
 | 
				
			|||||||
      const datetime = page.locator('#display');
 | 
					      const datetime = page.locator('#display');
 | 
				
			||||||
      await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-display`));
 | 
					      await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-display`));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('should return the same date format on current month days and on adjacent days', async ({ page }) => {
 | 
				
			||||||
 | 
					      await page.setContent(
 | 
				
			||||||
 | 
					        `
 | 
				
			||||||
 | 
					        <ion-datetime show-adjacent-days="true" value="2022-10-14T16:22:00.000Z" presentation="date"></ion-datetime>
 | 
				
			||||||
 | 
					      `,
 | 
				
			||||||
 | 
					        config
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Wait for the datetime to be ready.
 | 
				
			||||||
 | 
					      await page.locator('.datetime-ready').waitFor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const ionChange = await page.spyOnEvent('ionChange');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /**
 | 
				
			||||||
 | 
					       * Make sure to exclude adjacent days from the query since
 | 
				
			||||||
 | 
					       * the previous/next month is rendered hidden. This causes
 | 
				
			||||||
 | 
					       * the query to possibly return different results: one for
 | 
				
			||||||
 | 
					       * the current month and one from the hidden previous/next
 | 
				
			||||||
 | 
					       * month.
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      const october20Button = page.locator(
 | 
				
			||||||
 | 
					        '[data-month="10"][data-year="2022"][data-day="20"]:not(.calendar-day-adjacent-day)'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await october20Button.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await ionChange.next();
 | 
				
			||||||
 | 
					      await expect(ionChange).toHaveReceivedEventDetail({
 | 
				
			||||||
 | 
					        value: '2022-10-20T16:22:00',
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const november1Button = page.locator(
 | 
				
			||||||
 | 
					        '.calendar-day-adjacent-day[data-month="11"][data-year="2022"][data-day="1"]'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await november1Button.click();
 | 
				
			||||||
 | 
					      // Wait for the datetime to change the month since an adjacent day
 | 
				
			||||||
 | 
					      // was clicked.
 | 
				
			||||||
 | 
					      await page.waitForChanges();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Wait for the title to update to the new month since it changes
 | 
				
			||||||
 | 
					      // after the month animation finishes.
 | 
				
			||||||
 | 
					      await expect(calendarMonthYear).toHaveText('November 2022');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await ionChange.next();
 | 
				
			||||||
 | 
					      await expect(ionChange).toHaveReceivedEventDetail({
 | 
				
			||||||
 | 
					        value: '2022-11-01T16:22:00',
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /**
 | 
				
			||||||
 | 
					       * Make sure to exclude adjacent days from the query since
 | 
				
			||||||
 | 
					       * the previous/next month is rendered hidden. This causes
 | 
				
			||||||
 | 
					       * the query to possibly return different results: one for
 | 
				
			||||||
 | 
					       * the current month and one from the hidden previous/next
 | 
				
			||||||
 | 
					       * month.
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      const november22Button = page.locator(
 | 
				
			||||||
 | 
					        '[data-month="11"][data-year="2022"][data-day="22"]:not(.calendar-day-adjacent-day)'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await november22Button.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await ionChange.next();
 | 
				
			||||||
 | 
					      await expect(ionChange).toHaveReceivedEventDetail({
 | 
				
			||||||
 | 
					        value: '2022-11-22T16:22:00',
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB  | 
| 
		 Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB  | 
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB  | 
| 
		 Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB  | 
@ -161,8 +161,20 @@
 | 
				
			|||||||
              <span>FirstDayOfWeek: <span id="start-of-week">1</span></span>
 | 
					              <span>FirstDayOfWeek: <span id="start-of-week">1</span></span>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <div class="grid-item">
 | 
				
			||||||
 | 
					            <h2>DateTime format with IonChange Event (console)</h2>
 | 
				
			||||||
 | 
					            <ion-datetime
 | 
				
			||||||
 | 
					              id="default2"
 | 
				
			||||||
 | 
					              show-adjacent-days="true"
 | 
				
			||||||
 | 
					              locale="en-US"
 | 
				
			||||||
 | 
					              value="2022-10-14T16:22:00.000Z"
 | 
				
			||||||
 | 
					              presentation="date"
 | 
				
			||||||
 | 
					            ></ion-datetime>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        <div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="grid">
 | 
				
			||||||
 | 
					          <div class="grid-item">
 | 
				
			||||||
            <label for="presentation">Presentation</label>
 | 
					            <label for="presentation">Presentation</label>
 | 
				
			||||||
            <select id="presentation" onchange="changePresentation(event)">
 | 
					            <select id="presentation" onchange="changePresentation(event)">
 | 
				
			||||||
              <option value="date-time" selected>date-time</option>
 | 
					              <option value="date-time" selected>date-time</option>
 | 
				
			||||||
@ -178,6 +190,7 @@
 | 
				
			|||||||
            <br /><br />
 | 
					            <br /><br />
 | 
				
			||||||
            <ion-datetime show-adjacent-days="true" id="display" value="2022-02-22T16:30:00"></ion-datetime>
 | 
					            <ion-datetime show-adjacent-days="true" id="display" value="2022-02-22T16:30:00"></ion-datetime>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      </ion-content>
 | 
					      </ion-content>
 | 
				
			||||||
    </ion-app>
 | 
					    </ion-app>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -305,6 +318,11 @@
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      initCalendarMonthChangeObserver();
 | 
					      initCalendarMonthChangeObserver();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const datetimeDefault = document.querySelector('#default2');
 | 
				
			||||||
 | 
					      datetimeDefault.addEventListener('ionChange', (ev) => {
 | 
				
			||||||
 | 
					        console.log(ev.target.value);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    </script>
 | 
					    </script>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 | 
				
			|||||||