Skip to content

feat(datetime-button): add support for opening datetimes in overlays with button #25649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 46 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f15a41d
feat(datetime-button): add base component
liamdebeasi Jul 12, 2022
be5ef5a
chore(): lint
liamdebeasi Jul 12, 2022
c2a3669
chore(): prettier
liamdebeasi Jul 12, 2022
e931d28
feat(datetime-button): add base styles and examples
liamdebeasi Jul 12, 2022
059f118
feat(datetime-button): add base functionality
liamdebeasi Jul 12, 2022
98879f0
feat(datetime-button): add support for active states
liamdebeasi Jul 12, 2022
c6e594b
feat(datetime-button): ensure overlays are sized correctly
liamdebeasi Jul 12, 2022
4aa1bec
chore(): remove stubs
liamdebeasi Jul 12, 2022
a802aa5
Revert "feat(datetime-button): ensure overlays are sized correctly"
liamdebeasi Jul 12, 2022
a4ac7a2
test(datetime-button): add basic rendering tests
liamdebeasi Jul 12, 2022
bf1faeb
chore(): lint
liamdebeasi Jul 12, 2022
e4e9958
chore(): lint
liamdebeasi Jul 12, 2022
ae63db5
test(): fix test
liamdebeasi Jul 12, 2022
cc0a1d4
feat(datetime-button): improve button support
liamdebeasi Jul 12, 2022
e817a2d
docs(): add slot documentation
liamdebeasi Jul 12, 2022
2adf417
Merge remote-tracking branch 'origin/FW-546' into 546-base
liamdebeasi Jul 12, 2022
cd7642b
Merge remote-tracking branch 'origin/546-base' into 546-styles
liamdebeasi Jul 12, 2022
31289c8
Merge remote-tracking branch 'origin/546-styles' into 546-text
liamdebeasi Jul 12, 2022
c1766ca
chore(): sync
liamdebeasi Jul 12, 2022
a251ba0
chore(): typo
liamdebeasi Jul 12, 2022
34b122b
chore(); update comment
liamdebeasi Jul 12, 2022
d340b4b
chore(): remove default case
liamdebeasi Jul 12, 2022
63702db
feat(datetime-button): add correct overlay sizing
liamdebeasi Jul 13, 2022
988a165
fix(datetime-button): improve auto height sizing
liamdebeasi Jul 13, 2022
73b998f
chore(): remove extra class
liamdebeasi Jul 13, 2022
1c94505
fix(datetime-button): remove datetime cover sizing
liamdebeasi Jul 13, 2022
f5c4b31
fix(datetime-button): let browser determine width
liamdebeasi Jul 13, 2022
be18fa0
fix(datetime-button): ensure overlays are sized and positioned correctly
liamdebeasi Jul 13, 2022
6bcd860
test(datetime-button): add tests
liamdebeasi Jul 13, 2022
0a932ea
chore(): sync with FW-546
liamdebeasi Jul 14, 2022
34f9f68
chore(): sync with FW-546
liamdebeasi Jul 14, 2022
8272747
test(): fix tests from bad merge
liamdebeasi Jul 14, 2022
463421c
test(datetime-button): add disabled visual test
liamdebeasi Jul 15, 2022
36a49e5
chore(): sync with disabled 546
liamdebeasi Jul 15, 2022
6ecfb9d
chore(): add missing refs
liamdebeasi Jul 15, 2022
d761bb1
test(datetime-button): update class references
liamdebeasi Jul 15, 2022
4bd2d9b
chore(): sync with FW-546
liamdebeasi Jul 19, 2022
6a7777e
chore(): remove async
liamdebeasi Jul 19, 2022
9dd1058
chore(): use raf
liamdebeasi Jul 19, 2022
1fc0119
refactor(datetime-button): extract styles to class
liamdebeasi Jul 19, 2022
249ca18
chore(); add datetime-grid class
liamdebeasi Jul 19, 2022
c7d3496
fix(datetime-button): improve sizing in overlays
liamdebeasi Jul 20, 2022
7f4a7d4
Update core/src/components/datetime-button/datetime-button.tsx
liamdebeasi Jul 19, 2022
96f7250
chore(): remove old accordion demo
liamdebeasi Jul 20, 2022
8863b8e
chore(): correct usage in overlays sample
liamdebeasi Jul 20, 2022
8b4979f
chore(): remove trigger
liamdebeasi Jul 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4753,6 +4753,10 @@ declare namespace LocalJSX {
* Emitted when the datetime has focus.
*/
"onIonFocus"?: (event: IonDatetimeCustomEvent<void>) => void;
/**
* Emitted when componentDidRender is fired.
*/
"onIonRender"?: (event: IonDatetimeCustomEvent<void>) => void;
/**
* Emitted when the styles change.
*/
Expand Down
115 changes: 107 additions & 8 deletions core/src/components/datetime-button/datetime-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import { parseDate } from '../datetime/utils/parse';
})
export class DatetimeButton implements ComponentInterface {
private datetimeEl: HTMLIonDatetimeElement | null = null;
private overlayEl: HTMLIonModalElement | HTMLIonPopoverElement | null = null;
private dateTargetEl: HTMLElement | undefined;
private timeTargetEl: HTMLElement | undefined;

@Element() el!: HTMLIonDatetimeButtonElement;

Expand Down Expand Up @@ -86,6 +89,26 @@ export class DatetimeButton implements ComponentInterface {

io.observe(datetimeEl);

/**
* Get a reference to any modal/popover
* the datetime is being used in so we can
* correctly size it when it is presented.
*/
const overlayEl = (this.overlayEl = datetimeEl.closest('ion-modal, ion-popover'));

/**
* The .ion-datetime-button-overlay class contains
* styles that allow any modal/popover to be
* sized according to the dimensions of the datetime.
* If developers want a smaller/larger overlay all they need
* to do is change the width/height of the datetime.
* Additionally, this lets us avoid having to set
* explicit widths on each variant of datetime.
*/
if (overlayEl) {
overlayEl.classList.add('ion-datetime-button-overlay');
}

componentOnReady(datetimeEl, () => {
const datetimePresentation = (this.datetimePresentation = datetimeEl.presentation || 'date-time');

Expand Down Expand Up @@ -183,13 +206,31 @@ export class DatetimeButton implements ComponentInterface {
}
};

private handleDateClick = () => {
/**
* Waits for the ion-datetime to re-render.
* This is needed in order to correctly position
* a popover relative to the trigger element.
*/
private waitForDatetimeChanges = async () => {
const { datetimeEl } = this;
if (!datetimeEl) {
return Promise.resolve();
}

return new Promise((resolve) => {
datetimeEl.addEventListener('ionRender', resolve, { once: true });
});
};

private handleDateClick = async (ev: Event) => {
const { datetimeEl, datetimePresentation } = this;

if (!datetimeEl) {
return;
}

let needsPresentationChange = false;

/**
* When clicking the date button,
* we need to make sure that only a date
Expand All @@ -200,18 +241,18 @@ export class DatetimeButton implements ComponentInterface {
switch (datetimePresentation) {
case 'date-time':
case 'time-date':
const needsChange = datetimeEl.presentation !== 'date';
/**
* The date+time wheel picker
* shows date and time together,
* so do not adjust the presentation
* in that case.
*/
if (!datetimeEl.preferWheel) {
if (!datetimeEl.preferWheel && needsChange) {
datetimeEl.presentation = 'date';
needsPresentationChange = true;
}
break;
default:
break;
}

/**
Expand All @@ -222,15 +263,19 @@ export class DatetimeButton implements ComponentInterface {
* the datetime is opened.
*/
this.selectedButton = 'date';

this.presentOverlay(ev, needsPresentationChange, this.dateTargetEl);
};

private handleTimeClick = () => {
private handleTimeClick = (ev: Event) => {
const { datetimeEl, datetimePresentation } = this;

if (!datetimeEl) {
return;
}

let needsPresentationChange = false;

/**
* When clicking the time button,
* we need to make sure that only a time
Expand All @@ -241,7 +286,11 @@ export class DatetimeButton implements ComponentInterface {
switch (datetimePresentation) {
case 'date-time':
case 'time-date':
datetimeEl.presentation = 'time';
const needsChange = datetimeEl.presentation !== 'time';
if (needsChange) {
datetimeEl.presentation = 'time';
needsPresentationChange = true;
}
break;
}

Expand All @@ -253,6 +302,54 @@ export class DatetimeButton implements ComponentInterface {
* the datetime is opened.
*/
this.selectedButton = 'time';

this.presentOverlay(ev, needsPresentationChange, this.timeTargetEl);
};

/**
* If the datetime is presented in an
* overlay, the datetime and overlay
* should be appropriately sized.
* These classes provide default sizing values
* that developers can customize.
* The goal is to provide an overlay that is
* reasonably sized with a datetime that
* fills the entire container.
*/
private presentOverlay = async (ev: Event, needsPresentationChange: boolean, triggerEl?: HTMLElement) => {
const { overlayEl } = this;

if (!overlayEl) {
return;
}

if (overlayEl.tagName === 'ION-POPOVER') {
/**
* When the presentation on datetime changes,
* we need to wait for the component to re-render
* otherwise the computed width/height of the
* popover content will be wrong, causing
* the popover to not align with the trigger element.
*/

if (needsPresentationChange) {
await this.waitForDatetimeChanges();
}

/**
* We pass the trigger button element
* so that the popover aligns with the individual
* button that was clicked, not the component container.
*/
(overlayEl as HTMLIonPopoverElement).present({
...ev,
detail: {
ionShadowTarget: triggerEl,
},
} as CustomEvent);
} else {
overlayEl.present();
}
};

render() {
Expand All @@ -273,9 +370,10 @@ export class DatetimeButton implements ComponentInterface {
class="ion-activatable"
id="date-button"
aria-expanded={datetimeActive ? 'true' : 'false'}
onClick={() => this.handleDateClick()}
onClick={this.handleDateClick}
disabled={disabled}
part="native"
ref={(el) => (this.dateTargetEl = el)}
>
<slot name="date-target">{dateText}</slot>
{mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
Expand All @@ -287,9 +385,10 @@ export class DatetimeButton implements ComponentInterface {
class="ion-activatable"
id="time-button"
aria-expanded={datetimeActive ? 'true' : 'false'}
onClick={() => this.handleTimeClick()}
onClick={this.handleTimeClick}
disabled={disabled}
part="native"
ref={(el) => (this.timeTargetEl = el)}
>
<slot name="time-target">{timeText}</slot>
{mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
Expand Down
72 changes: 0 additions & 72 deletions core/src/components/datetime-button/test/accordion/index.html

This file was deleted.

Loading