Skip to content
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

feat: integrate column-option into column #28622

Merged
merged 55 commits into from
Dec 6, 2023
Merged

Conversation

liamdebeasi
Copy link
Contributor

@liamdebeasi liamdebeasi commented Dec 1, 2023

Issue number: Internal


What is the current behavior?

We'd like to introduce a new API to the picker column to allow developers to manipulate the picker options right in the DOM. Currently developers need to pass an object which is quite cumbersome as you need to re-pass that object anytime you want to change a single option.

What is the new behavior?

  • ion-picker-column now uses ion-picker-column-option for displaying options in a column
  • Scrolling the column and clicking an enabled option should update the value of the column/emit ionChange
  • Loading the picker with a valid value already set

There are a few things missing from this PR. I plan to address this separately and have disabled their tests for now:

  1. Ensure column option is disabled if the parent column is disabled
  2. Keyboard entry for numbers
  3. Setting a value initially if nothing is set/the value is out of bounds -- There are several ways I can tackle this, but based on the feedback on this PR I may change my approach so figured it would be best to hold off for now.

Notes to reviewers:

  • There is likely duplicate/unneeded. I plan to focus on getting functionality implemented + tests passing before I go ripping other code out. I added TODO comments so I can keep track of this.
  • There should be no visual diffs as a result of this change
  • When updating the datetime tests I tried to rely on public APIs for our components as much as possible instead of diving into the Shadow DOM. My hope is that this reduces the need for test updates in the event we make more internal changes. Let me know if you have ideas on how I can improve the test changes in this PR!

Does this introduce a breaking change?

  • Yes
  • No

The shadow parts on the picker column options have been removed since they are in the light DOM now. The wheel-item and active parts remain when targeting the wheel inside of ion-datetime.

Other information

@github-actions github-actions bot added the package: core @ionic/core package label Dec 1, 2023
Comment on lines -355 to -371
const dataIndex = activeElement.getAttribute('data-index');

/**
* If no value it is
* possible we hit one of the
* empty padding columns.
*/
if (dataIndex === null) {
return;
}

const index = parseInt(dataIndex, 10);
const selectedItem = this.items[index];

if (selectedItem.value !== this.value) {
this.setValue(selectedItem.value);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, we were dealing with button elements so we had to encode the button index so we could then find the associated item value. We don't need to do that with the option component since we can just look at the value property directly.

@@ -325,8 +338,8 @@ export class PickerColumn implements ComponentInterface {
}
}

activeEl = activeElement;
this.setPickerItemActiveState(activeElement, true);
activeEl = newActiveElement;
Copy link
Contributor Author

@liamdebeasi liamdebeasi Dec 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name change isn't needed to land this PR, but I couldn't immediately tell the different between activeEl and activeElement so I felt it best to rename.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! This makes it easier to comprehend when skimming the code.

Comment on lines 306 to 314
/**
* This null check is important because activeEl
* can be undefined but newActiveElement can be
* null since we are using a querySelector.
* newActiveElement !== activeEl would return true
* below if newActiveElement was null but activeEl
* was undefined.
*/
if (newActiveElement === null || newActiveElement.disabled) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered making activeEl null instead of undefined, but I decided against it. Array.prototype.find returns undefined if an array option was not found, and querySelector returns null if the element was not found in the DOM. I decided it would be better to add this comment highlighting the importance of the newActiveElement === null check instead of having the other methods return a value they were not designed to return.

Comment on lines 172 to 175
const findItem = items.find((item) => item.value === value && item.disabled !== true);
if (findItem) {
this.ionChange.emit(findItem);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed anymore. We could have this filter through the existing ion-picker-column-option elements, but none of our other grouped components do this (accordion, radio group, etc)

XoL1507

This comment was marked as spam.

Comment on lines 74 to 79
componentDidLoad() {
const parentPickerColumn = this.el.closest('ion-picker-column');
if (parentPickerColumn !== null && this.value === parentPickerColumn.value) {
parentPickerColumn.scrollActiveItemIntoView();
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is new behavior. With the internal picker, the options were part of ion-picker-column-internal, so we knew exactly when the options were loaded. With the ion-picker-column-option approach, these options can be loaded at any time (such as in an *ngIf), so we need the options to tell the column when it is ready to be scrolled into view.

@github-actions github-actions bot added the package: angular @ionic/angular package label Dec 1, 2023
*/
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('picker-column: disabled rendering'), () => {
test('should not have visual regressions', async ({ page }) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checking that the column options are rendered correctly when disabled. I already did this in #28621, so this test is not needed.

/**
* This behavior does not vary across modes/directions.
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('picker-column: disabled items'), () => {
// TODO FW-5580 move this to a spec test in picker-column-option
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much easier to test using a spec test now, so I'd like to update this in a future PR.


expect(await pickerItems.count()).toBe(3);
});
// TODO FW-5580 move this to a spec test in picker-column-option
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much easier to test using a spec test now, so I'd like to update this in a future PR.

configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('picker-column: disabled column'), () => {
test.describe.skip(title('picker-column: disabled column'), () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be handled in a future PR

configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('picker: keyboard entry'), () => {
test.describe.skip(title('picker: keyboard entry'), () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be handled in a future PR

@liamdebeasi liamdebeasi changed the title Ld/5580 integration feat: integrate column-option into column Dec 1, 2023
@@ -53,31 +53,36 @@ <h2>Even items disabled</h2>
<div class="grid-item">
<h2>Column disabled</h2>
<ion-picker>
<ion-picker-column id="column-disabled" value="11" disabled></ion-picker-column>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value here was bound as a string, but the value on each option was bound as a number. This should have always been failing, but not sure why it wasn't in the past.

core/src/components/datetime/datetime.tsx Outdated Show resolved Hide resolved
core/src/components/datetime/datetime.tsx Outdated Show resolved Hide resolved
core/src/components/datetime/datetime.tsx Outdated Show resolved Hide resolved
core/src/components/datetime/datetime.tsx Outdated Show resolved Hide resolved
core/src/components/datetime/datetime.tsx Outdated Show resolved Hide resolved
core/src/components/picker-column/picker-column.tsx Outdated Show resolved Hide resolved
core/src/components/picker-column/picker-column.tsx Outdated Show resolved Hide resolved
core/src/components/datetime/datetime.tsx Outdated Show resolved Hide resolved
core/src/components/picker-column/picker-column.tsx Outdated Show resolved Hide resolved
core/src/components/picker-column/picker-column.tsx Outdated Show resolved Hide resolved
Base automatically changed from ld/5580-base to FW-5580 December 5, 2023 19:40
@liamdebeasi liamdebeasi requested a review from a team as a code owner December 5, 2023 19:57
@@ -325,8 +338,8 @@ export class PickerColumn implements ComponentInterface {
}
}

activeEl = activeElement;
this.setPickerItemActiveState(activeElement, true);
activeEl = newActiveElement;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! This makes it easier to comprehend when skimming the code.

core/src/components/picker/test/basic/index.html Outdated Show resolved Hide resolved
Copy link
Contributor

@thetaPC thetaPC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, smooth on click also works

@liamdebeasi
Copy link
Contributor Author

Spoke with Sean offline and he's good with merging.

@liamdebeasi liamdebeasi merged commit ebcc4be into FW-5580 Dec 6, 2023
44 checks passed
@liamdebeasi liamdebeasi deleted the ld/5580-integration branch December 6, 2023 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package: angular @ionic/angular package package: core @ionic/core package
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants