Skip to content

feat(datetime-button): add support for custom, disabled, and accessible buttons #25622

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 28 commits into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 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
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
58899c2
chore(): add updated snapshots
Ionitron Jul 15, 2022
2174cd3
test(datetime-button): move skip to non-screenshot test
liamdebeasi Jul 19, 2022
d6aaef4
test(datetime-button): add default value
liamdebeasi Jul 19, 2022
8b5206d
chore(): add missing quote
liamdebeasi Jul 19, 2022
afee026
chore(): add updated snapshots
Ionitron Jul 19, 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
1 change: 1 addition & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ ion-datetime-button,prop,color,"danger" | "dark" | "light" | "medium" | "primary
ion-datetime-button,prop,datetime,string | undefined,undefined,false,false
ion-datetime-button,prop,disabled,boolean,false,false,true
ion-datetime-button,prop,mode,"ios" | "md",undefined,false,false
ion-datetime-button,part,native

ion-fab,shadow
ion-fab,prop,activated,boolean,false,false,false
Expand Down
12 changes: 12 additions & 0 deletions core/src/components/datetime-button/datetime-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
@include padding(6px, 12px, 6px, 12px);
@include margin(0px, 2px, 0px, 2px);

position: relative;

transition: 150ms color ease-in-out;

border: none;
Expand All @@ -30,9 +32,19 @@
cursor: pointer;

appearance: none;

overflow: hidden;
}

:host(.time-active) #time-button,
:host(.date-active) #date-button {
color: current-color(base);
}

:host(.datetime-button-disabled) {
pointer-events: none;
}

:host(.datetime-button-disabled) button {
opacity: 0.4;
}
46 changes: 26 additions & 20 deletions core/src/components/datetime-button/datetime-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { parseDate } from '../datetime/utils/parse';
*
* @slot date-target - Content displayed inside of the date button.
* @slot time-target - Content displayed inside of the time button.
*
* @part native - The native HTML button that wraps the slotted text.
*/
@Component({
tag: 'ion-datetime-button',
Expand Down Expand Up @@ -254,7 +256,7 @@ export class DatetimeButton implements ComponentInterface {
};

render() {
const { color, dateText, timeText, selectedButton, datetimeActive } = this;
const { color, dateText, timeText, selectedButton, datetimeActive, disabled } = this;

const mode = getIonMode(this);

Expand All @@ -263,31 +265,35 @@ export class DatetimeButton implements ComponentInterface {
class={createColorClasses(color, {
[mode]: true,
[`${selectedButton}-active`]: datetimeActive,
['datetime-button-disabled']: disabled,
})}
>
{dateText && (
<div class="date-target-container" onClick={() => this.handleDateClick()}>
<slot name="date-target">
{/*
The button is added inside of the <slot> so that
devs do not create nested interactives if they
decide to add in a custom ion-button.
*/}
<button id="date-button" aria-expanded={datetimeActive ? 'true' : 'false'}>
{dateText}
</button>
</slot>
</div>
<button
class="ion-activatable"
id="date-button"
aria-expanded={datetimeActive ? 'true' : 'false'}
onClick={() => this.handleDateClick()}
disabled={disabled}
part="native"
>
<slot name="date-target">{dateText}</slot>
{mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
</button>
)}

{timeText && (
<div class="time-target-container" onClick={() => this.handleTimeClick()}>
<slot name="time-target">
<button id="time-button" aria-expanded={datetimeActive ? 'true' : 'false'}>
{timeText}
</button>
</slot>
</div>
<button
class="ion-activatable"
id="time-button"
aria-expanded={datetimeActive ? 'true' : 'false'}
onClick={() => this.handleTimeClick()}
disabled={disabled}
part="native"
>
<slot name="time-target">{timeText}</slot>
{mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
</button>
)}
</Host>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ test.describe('datetime-button: switching to correct view', () => {
const datetime = page.locator('ion-datetime');
expect(datetime).toHaveJSProperty('presentation', 'date-time');

await page.locator('.date-target-container').click();
await page.locator('#date-button').click();

expect(datetime).toHaveJSProperty('presentation', 'date');
});
test('should switch to a time-only view when the time button is clicked', async ({ page }) => {
const datetime = page.locator('ion-datetime');
expect(datetime).toHaveJSProperty('presentation', 'date-time');

await page.locator('.time-target-container').click();
await page.locator('#time-button').click();

expect(datetime).toHaveJSProperty('presentation', 'time');
});
Expand All @@ -43,8 +43,8 @@ test.describe('datetime-button: labels', () => {
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('.date-target-container')).toContainText('Jan 1, 2022');
await expect(page.locator('.time-target-container')).toContainText('6:30 AM');
await expect(page.locator('#date-button')).toContainText('Jan 1, 2022');
await expect(page.locator('#time-button')).toContainText('6:30 AM');
});
test('should set only month and year', async ({ page }) => {
await page.setContent(`
Expand All @@ -53,8 +53,8 @@ test.describe('datetime-button: labels', () => {
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('.date-target-container')).toContainText('January 2022');
await expect(page.locator('.time-target-container')).toBeHidden();
await expect(page.locator('#date-button')).toContainText('January 2022');
await expect(page.locator('#time-button')).toBeHidden();
});
test('should set only year', async ({ page }) => {
await page.setContent(`
Expand All @@ -63,8 +63,8 @@ test.describe('datetime-button: labels', () => {
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('.date-target-container')).toContainText('2022');
await expect(page.locator('.time-target-container')).toBeHidden();
await expect(page.locator('#date-button')).toContainText('2022');
await expect(page.locator('#time-button')).toBeHidden();
});
test('should set only month', async ({ page }) => {
await page.setContent(`
Expand All @@ -73,8 +73,8 @@ test.describe('datetime-button: labels', () => {
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('.date-target-container')).toContainText('January');
await expect(page.locator('.time-target-container')).toBeHidden();
await expect(page.locator('#date-button')).toContainText('January');
await expect(page.locator('#time-button')).toBeHidden();
});
test('should set only time', async ({ page }) => {
await page.setContent(`
Expand All @@ -83,8 +83,8 @@ test.describe('datetime-button: labels', () => {
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('.date-target-container')).toBeHidden();
await expect(page.locator('.time-target-container')).toContainText('6:30 AM');
await expect(page.locator('#time-button')).toContainText('6:30 AM');
await expect(page.locator('#date-button')).toBeHidden();
});
test('should update the label when the value of the datetime changes', async ({ page }) => {
await page.setContent(`
Expand All @@ -94,7 +94,7 @@ test.describe('datetime-button: labels', () => {
await page.waitForSelector('.datetime-ready');

const datetime = page.locator('ion-datetime');
const dateTarget = page.locator('.date-target-container');
const dateTarget = page.locator('#date-button');

await expect(dateTarget).toContainText('Jan 1, 2022');

Expand Down Expand Up @@ -123,8 +123,8 @@ test.describe('datetime-button: locale', () => {
* a period after "ene". Just checking ene allows us to verify the
* behavior while avoiding these cross browser differences.
*/
await expect(page.locator('.date-target-container')).toContainText(/ene/);
await expect(page.locator('.time-target-container')).toContainText('6:30');
await expect(page.locator('#date-button')).toContainText(/ene/);
await expect(page.locator('#time-button')).toContainText('6:30');
});
test('should respect hour cycle even if different from locale default', async ({ page }) => {
await page.setContent(`
Expand All @@ -133,7 +133,7 @@ test.describe('datetime-button: locale', () => {
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('.time-target-container')).toContainText('16:30');
await expect(page.locator('#time-button')).toContainText('16:30');
});
test('should ignore the timezone when selecting a date', async ({ page }) => {
await page.setContent(`
Expand All @@ -142,7 +142,7 @@ test.describe('datetime-button: locale', () => {
`);
await page.waitForSelector('.datetime-ready');

const timeTarget = page.locator('.time-target-container');
const timeTarget = page.locator('#time-button');
await expect(timeTarget).toContainText('6:30');

const firstOfMonth = page.locator('ion-datetime .calendar-day[data-month="1"][data-day="1"]');
Expand All @@ -168,8 +168,8 @@ test.describe('datetime-button: wheel', () => {
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('.date-target-container')).toContainText('Jan 1, 2022 6:30 AM');
await expect(page.locator('.time-target-container')).not.toBeVisible();
await expect(page.locator('#date-button')).toContainText('Jan 1, 2022 6:30 AM');
await expect(page.locator('#time-button')).not.toBeVisible();
});
test('should only show a single date button when presentation="time-date" and prefer-wheel="true"', async ({
page,
Expand All @@ -180,7 +180,7 @@ test.describe('datetime-button: wheel', () => {
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('.date-target-container')).toContainText('Jan 1, 2022 6:30 AM');
await expect(page.locator('.time-target-container')).not.toBeVisible();
await expect(page.locator('#date-button')).toContainText('Jan 1, 2022 6:30 AM');
await expect(page.locator('#time-button')).not.toBeVisible();
});
});
8 changes: 2 additions & 6 deletions core/src/components/datetime-button/test/buttons/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,8 @@
<ion-item>
<ion-input value="March 15, 2022 at 12:43 AM"></ion-input>
<ion-datetime-button slot="end" id="default-button" datetime="default-datetime">
<ion-button id="custom-date-button" slot="date-target" fill="clear">
<ion-icon slot="icon-only" name="calendar"></ion-icon>
</ion-button>
<ion-button id="custom-time-button" slot="time-target" fill="clear">
<ion-icon name="time"></ion-icon>
</ion-button>
<ion-icon color="primary" id="custom-date-button" slot="date-target" name="calendar"></ion-icon>
<ion-icon color="primary" id="custom-time-button" slot="time-target" name="time"></ion-icon>
</ion-datetime-button>
</ion-item>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';

test.describe('datetime-button: disabled buttons', () => {
test('buttons should not be enabled when component is disabled', async ({ page }, testInfo) => {
test.skip(testInfo.project.metadata.rtl === 'rtl', 'No layout tests');
test.skip(testInfo.project.metadata.mode === 'ios', 'No mode-specific logic');

await page.setContent(`
<ion-datetime-button datetime="datetime" disabled="true"></ion-datetime-button>
<ion-datetime id="datetime" presentation="date-time"></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');

await expect(page.locator('#date-button')).toBeDisabled();
await expect(page.locator('#time-button')).toBeDisabled();
});
test('buttons should visually be disabled', async ({ page }) => {
await page.setContent(`
<ion-datetime-button datetime="datetime" disabled="true"></ion-datetime-button>
<ion-datetime id="datetime" presentation="date-time" value="2022-01-01T16:30:00"></ion-datetime>
`);
await page.waitForSelector('.datetime-ready');

const datetimeButton = page.locator('ion-datetime-button');
expect(await datetimeButton.screenshot()).toMatchSnapshot(
`datetime-button-disabled-${page.getSnapshotSettings()}.png`
);
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 5 additions & 9 deletions core/src/components/datetime-button/test/disabled/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,14 @@ <h2>Custom Button Disabled</h2>

<ion-item>
<ion-label>Start Date</ion-label>
<ion-datetime-button disabled="true" slot="end" id="year-button" datetime="year-datetime">
<ion-button disabled="true" id="custom-date-button" slot="date-target" fill="clear">
<ion-icon slot="icon-only" name="calendar"></ion-icon>
</ion-button>
<ion-button disabled="true" id="custom-time-button" slot="time-target" fill="clear">
<ion-icon name="time"></ion-icon>
</ion-button>
<ion-datetime-button disabled="true" slot="end" id="custom-button" datetime="custom-datetime">
<ion-icon color="primary" id="custom-date-button" slot="date-target" name="calendar"></ion-icon>
<ion-icon color="primary" id="custom-time-button" slot="time-target" name="time"></ion-icon>
</ion-datetime-button>
</ion-item>

<ion-popover arrow="false" trigger="year-button">
<ion-datetime locale="en-US" id="year-datetime" value="2022-03-15T00:43:00"></ion-datetime>
<ion-popover arrow="false" trigger="custom-button">
<ion-datetime locale="en-US" id="custom-datetime" value="2022-03-15T00:43:00"></ion-datetime>
</ion-popover>
</div>
</div>
Expand Down