mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 19:57:22 +08:00
feat(picker-column-internal): add ability to disable items (#25412)
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
export interface PickerColumnItem {
|
||||
text: string;
|
||||
value: string | number;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
@ -34,21 +34,38 @@
|
||||
}
|
||||
|
||||
:host .picker-item {
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
|
||||
height: 34px;
|
||||
|
||||
border: 0px;
|
||||
|
||||
outline: none;
|
||||
|
||||
background: transparent;
|
||||
|
||||
font-size: inherit;
|
||||
|
||||
line-height: 34px;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
scroll-snap-align: center;
|
||||
}
|
||||
|
||||
:host .picker-item-empty {
|
||||
:host .picker-item-empty,
|
||||
:host .picker-item.picker-item-disabled {
|
||||
scroll-snap-align: none;
|
||||
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
:host(.picker-column-active) .picker-item.picker-item-active {
|
||||
|
@ -146,7 +146,7 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
private setValue(value?: string | number) {
|
||||
const { items } = this;
|
||||
this.value = value;
|
||||
const findItem = items.find((item) => item.value === value);
|
||||
const findItem = items.find((item) => item.value === value && item.disabled !== true);
|
||||
if (findItem) {
|
||||
this.ionChange.emit(findItem);
|
||||
}
|
||||
@ -226,11 +226,15 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
const centerX = bbox.x + bbox.width / 2;
|
||||
const centerY = bbox.y + bbox.height / 2;
|
||||
|
||||
const activeElement = el.shadowRoot!.elementFromPoint(centerX, centerY) as HTMLElement;
|
||||
const activeElement = el.shadowRoot!.elementFromPoint(centerX, centerY) as HTMLButtonElement;
|
||||
if (activeEl !== null) {
|
||||
activeEl.classList.remove(PICKER_COL_ACTIVE);
|
||||
}
|
||||
|
||||
if (activeElement.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are selecting a new value,
|
||||
* we need to run haptics again.
|
||||
@ -280,7 +284,9 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
};
|
||||
|
||||
get activeItem() {
|
||||
return getElementRoot(this.el).querySelector(`.picker-item[data-value="${this.value}"]`) as HTMLElement | null;
|
||||
return getElementRoot(this.el).querySelector(
|
||||
`.picker-item[data-value="${this.value}"]:not([disabled])`
|
||||
) as HTMLElement | null;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -301,16 +307,20 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
<div class="picker-item picker-item-empty"> </div>
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<div
|
||||
class="picker-item"
|
||||
<button
|
||||
class={{
|
||||
'picker-item': true,
|
||||
'picker-item-disabled': item.disabled || false,
|
||||
}}
|
||||
data-value={item.value}
|
||||
data-index={index}
|
||||
onClick={(ev: Event) => {
|
||||
this.centerPickerItemInView(ev.target as HTMLElement);
|
||||
}}
|
||||
disabled={item.disabled}
|
||||
>
|
||||
{item.text}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
<div class="picker-item picker-item-empty"> </div>
|
||||
|
@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Picker Column Internal - Basic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(250px, 1fr));
|
||||
grid-row-gap: 20px;
|
||||
grid-column-gap: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #6f7378;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Picker Column Internal - Disabled</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Default</h2>
|
||||
<ion-picker-internal>
|
||||
<ion-picker-column-internal id="default"></ion-picker-column-internal>
|
||||
</ion-picker-internal>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
<script>
|
||||
const defaultPickerColumn = document.getElementById('default');
|
||||
|
||||
const items = Array(24)
|
||||
.fill()
|
||||
.map((_, i) => ({
|
||||
text: `${i}`,
|
||||
value: i,
|
||||
disabled: i % 2 === 0,
|
||||
}));
|
||||
|
||||
defaultPickerColumn.items = items;
|
||||
defaultPickerColumn.value = 12;
|
||||
</script>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,111 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('picker-column-internal: disabled', () => {
|
||||
test('all picker items should be enabled by default', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-picker-internal>
|
||||
<ion-picker-column-internal></ion-picker-column-internal>
|
||||
</ion-picker-internal>
|
||||
|
||||
<script>
|
||||
const column = document.querySelector('ion-picker-column-internal');
|
||||
column.items = [
|
||||
{ text: 'A', value: 'a' },
|
||||
{ text: 'B', value: 'b' },
|
||||
{ text: 'C', value: 'c' }
|
||||
]
|
||||
</script>
|
||||
`);
|
||||
|
||||
const pickerItems = page.locator(
|
||||
'ion-picker-column-internal .picker-item:not(.picker-item-empty, .picker-item-disabled)'
|
||||
);
|
||||
|
||||
expect(await pickerItems.count()).toBe(3);
|
||||
});
|
||||
test('disabled picker item should not be interactive', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-picker-internal>
|
||||
<ion-picker-column-internal></ion-picker-column-internal>
|
||||
</ion-picker-internal>
|
||||
|
||||
<script>
|
||||
const column = document.querySelector('ion-picker-column-internal');
|
||||
column.items = [
|
||||
{ text: 'A', value: 'a' },
|
||||
{ text: 'B', value: 'b', disabled: true },
|
||||
{ text: 'C', value: 'c' }
|
||||
]
|
||||
</script>
|
||||
`);
|
||||
|
||||
const disabledItem = page.locator('ion-picker-column-internal .picker-item.picker-item-disabled');
|
||||
expect(disabledItem).not.toBeEnabled();
|
||||
});
|
||||
test('disabled picker item should not be considered active', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-picker-internal>
|
||||
<ion-picker-column-internal value="b"></ion-picker-column-internal>
|
||||
</ion-picker-internal>
|
||||
|
||||
<script>
|
||||
const column = document.querySelector('ion-picker-column-internal');
|
||||
column.items = [
|
||||
{ text: 'A', value: 'a' },
|
||||
{ text: 'B', value: 'b', disabled: true },
|
||||
{ text: 'C', value: 'c' }
|
||||
]
|
||||
</script>
|
||||
`);
|
||||
|
||||
const disabledItem = page.locator('ion-picker-column-internal .picker-item[data-value="b"]');
|
||||
expect(disabledItem).not.toHaveClass(/picker-item-active/);
|
||||
});
|
||||
test('setting the value to a disabled item should not cause that item to be active', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-picker-internal>
|
||||
<ion-picker-column-internal></ion-picker-column-internal>
|
||||
</ion-picker-internal>
|
||||
|
||||
<script>
|
||||
const column = document.querySelector('ion-picker-column-internal');
|
||||
column.items = [
|
||||
{ text: 'A', value: 'a' },
|
||||
{ text: 'B', value: 'b', disabled: true },
|
||||
{ text: 'C', value: 'c' }
|
||||
]
|
||||
</script>
|
||||
`);
|
||||
|
||||
const pickerColumn = page.locator('ion-picker-column-internal');
|
||||
await pickerColumn.evaluate((el: HTMLIonPickerColumnInternalElement) => (el.value = 'b'));
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
const disabledItem = page.locator('ion-picker-column-internal .picker-item[data-value="b"]');
|
||||
expect(disabledItem).toHaveClass(/picker-item-disabled/);
|
||||
expect(disabledItem).not.toHaveClass(/picker-item-active/);
|
||||
});
|
||||
test('defaulting the value to a disabled item should not cause that item to be active', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<ion-picker-internal>
|
||||
<ion-picker-column-internal></ion-picker-column-internal>
|
||||
</ion-picker-internal>
|
||||
|
||||
<script>
|
||||
const column = document.querySelector('ion-picker-column-internal');
|
||||
column.items = [
|
||||
{ text: 'A', value: 'a' },
|
||||
{ text: 'B', value: 'b', disabled: true },
|
||||
{ text: 'C', value: 'c' }
|
||||
]
|
||||
column.value = 'b'
|
||||
</script>
|
||||
`);
|
||||
|
||||
const disabledItem = page.locator('ion-picker-column-internal .picker-item[data-value="b"]');
|
||||
expect(disabledItem).toHaveClass(/picker-item-disabled/);
|
||||
expect(disabledItem).not.toHaveClass(/picker-item-active/);
|
||||
});
|
||||
});
|
@ -303,7 +303,7 @@ export class PickerInternal implements ComponentInterface {
|
||||
return;
|
||||
}
|
||||
|
||||
const values = inputModeColumn.items;
|
||||
const values = inputModeColumn.items.filter((item) => item.disabled !== true);
|
||||
|
||||
/**
|
||||
* If users pause for a bit, the search
|
||||
@ -374,7 +374,7 @@ export class PickerInternal implements ComponentInterface {
|
||||
zeroBehavior: 'start' | 'end' = 'start'
|
||||
) => {
|
||||
const behavior = zeroBehavior === 'start' ? /^0+/ : /0$/;
|
||||
const item = colEl.items.find(({ text }) => text.replace(behavior, '') === value);
|
||||
const item = colEl.items.find(({ text, disabled }) => disabled !== true && text.replace(behavior, '') === value);
|
||||
|
||||
if (item) {
|
||||
colEl.value = item.value;
|
||||
|
Reference in New Issue
Block a user