Compare commits

..

35 Commits

Author SHA1 Message Date
ionitron
90ce7f00fa v7.6.0 2023-12-06 15:33:45 +00:00
Liam DeBeasi
77e2147733 chore: sync with main
chore: sync with main
2023-12-06 10:26:44 -05:00
Liam DeBeasi
ede9c6e3c8 Merge remote-tracking branch 'origin/main' into sync-76-final 2023-12-06 10:16:26 -05:00
Liam DeBeasi
e20acb98ac merge release-7.5.8
Release 7.5.8
2023-12-06 10:15:48 -05:00
ionitron
5573fde6a2 chore(): update package lock files 2023-12-06 15:04:35 +00:00
Liam DeBeasi
e71e7a0690 fix(select): do not collapse to width: 0 when placed in flex container (#28631)
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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->

We currently apply a workaround to `ion-select` so it can wrap correctly
inside of `ion-item`:
357b8b2beb/core/src/components/select/select.scss (L99-L103)

However, this causes issues when a parent element has `display: flex`
because the `ion-select` width becomes 0.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- In order to get the desired behavior, we need the `ion-select` (and
other elements in the default slot) to either truncate or wrap within
its own container and then have the entire container (i.e. the entire
`ion-select`) wrap to the next line once the container is too small.

To achieve this, I needed to set a min-width on `.item-inner` to define
the point at which the element should wrap to the next line. I also
changed the flex basis from `auto` to `0` which means the initial main
size of the flex item will be 0px. In reality, this will be
`--inner-min-width` since we also set `min-width:
var(--inner-min-width)`. I used `0` for simplicity but I can change this
to use the CSS variable if that's more clear. Since we also set
`flex-grow: 1` we indicate that the element can grow from that basis
(but it cannot shrink).

I chose `--inner-min-width: 4rem` to minimize the number of diffs. We
can certainly change this, but it may cause some diffs as certain
elements will start wrapping sooner. I also chose to use `rem` because
having a fixed min-width means that fewer characters are going to fit in
the same space as text scales.

I made this a CSS variable but left it undocumented. If developers need
a way of changing this `min-width` they can request it and we can easily
expose this variable. However, I think `4rem` is small enough that this
should be sufficient.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

The visual diffs here are correct. The table below shows the screenshot
group and an explanation for why the changes are correct.


| Path | Example | Details |
| - | - | - |
| `disabled` |
[Link](https://github.com/ionic-team/ionic-framework/pull/28631/files#diff-d529716f95f7a7aa82c88588104220775b728af67077f48cd47a8afa04423143)
| The searchbar is able to shrink slightly to fit on the same line as
the checkbox at the bottom. |
| `highlight` |
[Link](https://github.com/ionic-team/ionic-framework/pull/28631/files#diff-0b64f24c91393923701d1ced4e330a1c6b926d72ee461b8ab1e135e708be3457)
| We're changing how small the main content can get, so the input is
only wrapping once it gets to `--inner-min-width`. |
| `legacy/fill` |
[Link](https://github.com/ionic-team/ionic-framework/pull/28631/files#diff-2ef8dbfa5e69e2b96c3e1ed29ab962f08cf5ba2aaf2af773e40bd143e38a4bef)
| We're changing how small the main content can get, so the input is
only wrapping once it gets to `--inner-min-width`. |
| `slotted-inputs` |
[Link](https://github.com/ionic-team/ionic-framework/pull/28631/files#diff-2f173c7303969d6a6c58f30a618cebc3caf918d3761fc83df5642fd48dfabd7b)
| We're changing how small the main content can get, so the range is
only wrapping once it gets to `--inner-min-width`. |

`slotted-inputs` note: I'd argue many of these examples are not best
practices. For example, adding a range in the start slot and the end
slot is a bit unusual. I'm not aware of any native apps that implement
this pattern.

popover note: I [removed the `ion-item` from the `popover/test/async`
test](331fcb859c).
There was a diff because the min-width increased, but IMO that component
should not be used in the popover test since we want to test the
popover, not the item.

--------

Demo:

| `feature-7.6` | `branch` |
| - | - |
| <video
src="https://github.com/ionic-team/ionic-framework/assets/2721089/693d4947-fa33-460d-bc7f-7b96b6338032"></video>
| <video
src="https://github.com/ionic-team/ionic-framework/assets/2721089/df35ca73-87aa-4e76-9bb7-99f0f2810640"></video>
|

(In this demo I updated the `ion-select` to wrap within its own
container first instead of truncate. We may want to consider doing this
by default, but I think this is out of scope for this task)

---------

Co-authored-by: ionitron <hi@ionicframework.com>
Co-authored-by: Brandy Carney <brandy@ionic.io>
2023-12-05 18:33:02 -05:00
Liam DeBeasi
8c235fd30c fix(infinite-scroll): remaining in threshold after ionInfinite can trigger event again on scroll (#28569)
Issue number: resolves #18071

---------

<!-- 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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->

When adding elements to the DOM in the `ionInfinite` callback, scrolling
again would sometimes not cause `ionInfinite` to trigger again. We [set
the didFire flag to
`true`](388d19e04f/core/src/components/infinite-scroll/infinite-scroll.tsx (L126))
before calling `ionInfinite`. This flag ensures that `ionInfinite` is
not called multiple times if users continue to scroll after
`ionInfinite` is fired but before the `complete` method is called.

The [didFire flag is
reset](388d19e04f/core/src/components/infinite-scroll/infinite-scroll.tsx (L131))
once the user scrolls outside of the threshold. Normally this is fine:
If an application adds several new items to a list the current scroll
position will be outside of the threshold. However, if the scroll
position remains in the threshold (such as if an application append a
small number of new items to a list) then the `didFire` flag will not
get reset.

Additionally, there are some instances where the scroll position
restoration when `position="top"` may not work which can cause this bug
to trigger as well. For example, if users quickly scroll to the top, the
scroll position will not be restored correctly and the scroll position
will still be at the top of the screen. That is another instance where
this bug can trigger even if a large number of items were added to the
DOM.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- The `didFire` flag is reset when the `complete` method is called. This
ensures that even if the scroll position is still in the threshold
`ionInfinite` can fire again.


Note that developers may notice `ionInfinite` firing more times as a
result of this change. This can happen when appending a small number of
items to the DOM such that the scroll position remains in the threshold.
Previously `ionInfinite` would not fire again, but now it does since
users are scrolling in the threshold. I decided to target this change
for a minor release to minimize any surprises for developers.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `7.5.4-dev.11700602203.1e7155a1`

---------

Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
2023-12-05 12:20:49 -05:00
Liam DeBeasi
65106ce21a chore: sync feature-7.6 with main
chore: sync feature-7.6 with main
2023-12-05 09:36:11 -05:00
Liam DeBeasi
e51deed21c Merge remote-tracking branch 'origin/main' into sync-76-125 2023-12-05 09:11:57 -05:00
Amanda Johnston
357b8b2beb feat(input, textarea, select): add start and end slots (#28583)
Issue number: Resolves #26297

---------

<!-- 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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->

With the modern form control syntax, it is not possible to add icon
buttons or other decorators to the sides of `ion-input`, `ion-textarea`,
or `ion-select`, as you can with `ion-item`.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

`start` and `end` slots added to each component.

This PR is a combination of several others that were already approved.
If needed, it might be easiest to review the PRs individually by looking
at the commit history here.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Docs PR: https://github.com/ionic-team/ionic-docs/pull/3271

Dev build: `7.5.4-dev.11701112913.1ea61220`

---------

Co-authored-by: ionitron <hi@ionicframework.com>
Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
2023-12-01 14:54:10 -06:00
Shawn Taylor
b757970d23 feat(radio): add shadow part for label (#28607)
Issue number: Part of #28300 

---------

<!-- 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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->
Developers are unable to adjust margin, width, etc. of the radio label

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Radio label has a shadow part.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
As part of this work, I investigated moving `pointer-events: none` up
the DOM tree so developers wouldn't be able to override it with the
shadow part. In my testing, I was unable to see any difference in
behavior with vs without `pointer-events: none`. Therefore, I removed it
entirely.
2023-12-01 11:38:29 -05:00
Shawn Taylor
f9f5654ab0 feat(checkbox): add shadow part for label (#28604)
Issue number: Part of #28300 

---------

<!-- 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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->
Developers are unable to adjust margin, width, etc. of the checkbox
label

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Checkbox label has a shadow part.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
As part of this work, I investigated moving `pointer-events: none` up
the DOM tree so developers wouldn't be able to override it with the
shadow part. In my testing, I was unable to see any difference in
behavior with vs without `pointer-events: none`. Therefore, I removed it
entirely.
2023-12-01 11:26:05 -05:00
Liam DeBeasi
a34188f7db feat(toggle): expose label wrapper as shadow part (#28585) 2023-11-30 14:03:51 -05:00
Liam DeBeasi
52ed2bf637 feat(range): expose label wrapper as shadow part (#28601) 2023-11-30 10:36:21 -05:00
Brandy Carney
1c1b567279 fix(item): allow item to grow when it is used in a flex container (#28594)
When an item containing a select is used inside of a flex container, the item collapses to `0px` width. Example code:

```html
<div style="display: flex">
  <ion-item>
    <ion-select aria-label="fruit" placeholder="Select fruit">
      <ion-select-option value="apples">Apples</ion-select-option>
      <ion-select-option value="oranges">Oranges</ion-select-option>
      <ion-select-option value="bananas">Bananas</ion-select-option>
    </ion-select>
  </ion-item>
</div>
```

This change sets the flex property to `1` on `ion-item` so that it will grow inside of a flex container, resulting in the select being displayed. The `flex` property is ignored when item is inside of a block container.
2023-11-29 10:57:58 -05:00
Liam DeBeasi
ddf630abfe chore: sync with main
chore: sync with main
2023-11-29 09:34:47 -05:00
Liam DeBeasi
fd36b953d6 chore: sync 2023-11-28 16:31:47 -05:00
Liam DeBeasi
4ad6df67f0 fix(toast): add swipeGesture to ToastOptions (#28518) 2023-11-22 11:46:38 -05:00
Liam DeBeasi
30c21aab3e feat(toast): add swipe to dismiss functionality (#28442)
Issue number: resolves #21769

---------

<!-- 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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->

Toast does not support swipe gestures to dismiss.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Added a `swipeGesture` property that allows users to swipe toasts
closed.
Note: This is a combination of previous PRs
https://github.com/ionic-team/ionic-framework/pull/28380 and
https://github.com/ionic-team/ionic-framework/pull/28402

⚠️ There is a visual glitch on iOS where dragging and having the toast
animate back to its opened position causes a flicker. This is an iOS 17
regression and is being tracked in
https://github.com/ionic-team/ionic-framework/issues/28467. This bug has
been reported to and confirmed by Apple.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

⚠️ Give co-author credit to author in
https://github.com/ionic-team/ionic-framework/pull/23124

---------

Co-authored-by: evgeniy-skakun <evgeniy-skakun@users.noreply.github.com>
2023-11-13 12:14:29 -05:00
Shawn Taylor
0ae327f0e0 feat(radio-group): add compareWith property (#28452) 2023-11-09 10:21:55 -05:00
Shawn Taylor
27c4d194c5 Merge pull request #28458 from ionic-team/sp/sync-feature-7-6-with-main
chore: sync with main
2023-11-02 17:52:03 -04:00
ionitron
fcffa8f01b chore(): add updated snapshots 2023-11-02 20:19:50 +00:00
Shawn Taylor
b5a4d37970 Update test to work with new item wrapping 2023-11-02 15:26:07 -04:00
Shawn Taylor
50e99b2180 Build 2023-11-02 13:57:12 -04:00
Sean Perkins
11fd074972 Merge remote-tracking branch 'origin/main' into sp/sync-feature-7-6-with-main 2023-11-02 13:14:57 -04:00
Amanda Johnston
0854a11a25 fix(angular,vue): range form value updates while dragging knob (#28422)
Issue number: Resolves #28256

---------

<!-- 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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->

In the form integrations for Angular and Vue, the value of a range does
not update while the knob is actively being dragged, only when the knob
is released.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

The form integrations now update the range's value when the `ionInput`
event fires, rather than `ionChange`.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

I wasn't sure how to add reliable automated tests for this behavior. The
difference only applies when actively dragging the knob, and we've had
issues with such gestures being flaky in the past. I did add value
displays to the test apps so the behavior can be manually tested.
2023-10-30 10:18:53 -05:00
Liam DeBeasi
60630ccb42 refactor: improve hardware back button types (#28335)
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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->

As part of FW-2832, the team would like to swap out usages of the `any`
type for stronger types.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Added `ionBackButton` event types to the browser utilities
- Updated menuController to use the `doc` utility instead of `document`
so we can get proper types
- Moved the definitions for back button types out of `interface.d.ts`
and into `hardware-back-button`. `interface.d.ts` still exports these
back button interfaces.
- Updated all `BackButtonEvent` imports inside of `@ionic/core` to
import from the utility file instead of the public interface file.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Note: This PR was separated from other type updates associated with the
FW-3832 work because I had to modify the implementation of a feature in
Ionic. While I don't expect there to be any functional differences, I
have opted to pull this work out into a separate branch and target a
feature branch to a) reduce the impact of any unintended bugs and b)
make it easier to do a `git bisect` if a bug is introduced.
2023-10-25 10:44:58 -04:00
Liam DeBeasi
6e79e1d179 refactor: improve types for input shims (#28333) 2023-10-24 12:00:14 -04:00
Liam DeBeasi
eae8162d0d fix(animation): progressEnd coercion is reset before onFinish (#28394)
Issue number: resolves #28393

---------

<!-- 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 current behavior?
<!-- Please describe the current behavior that you are modifying. -->

Our animation library's value coercion is not reset before developer
`onFinish` callbacks fire. This can lead to developers getting incorrect
state when querying for `getDuration` or `getDirection`

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Internal value coercion is reset before developer `onFinish` callbacks
fire.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

I'm putting this in a minor release to minimize risk. This is not a
breaking change, but there may be developers relying on this broken
behavior to implement a workaround.

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Note: This change is needed for the toast swipe to dismiss feature
(FW-2004)
2023-10-23 12:28:03 -04:00
Liam DeBeasi
0e2797bd33 refactor(toast): md animation uses transform (#28392) 2023-10-23 10:44:32 -04:00
Liam DeBeasi
4a088d5d61 fix(animation): add stronger types to Animation interface (#28334) 2023-10-19 11:55:25 -04:00
Liam DeBeasi
51c8dc82e9 chore: run build for vue output 2023-10-18 18:16:49 -04:00
Liam DeBeasi
8cd05d332e chore: sync with main
chore: sync with main
2023-10-18 18:10:19 -04:00
Liam DeBeasi
6b9d1a0c63 Merge remote-tracking branch 'origin/main' into feature-7.6 2023-10-18 17:52:12 -04:00
Brandy Carney
6438e3e919 fix(item): wrap elements and label contents when the font size increases or the elements do not fit (#28146)
1) Wraps the label text and other content in an item when there is not enough room for everything to fit, instead of truncating the label with an ellipsis. Does not apply to items containing legacy inputs.
2) Passes the legacy property up to item from checkbox, input, radio, range, select, textarea and toggle. Item adds classes for all of these and does not wrap its contents if that class exists. If a developer is using a legacy input without the legacy property on it then they will need to add the legacy property to prevent the wrapping.
3) If a developer does not want the text to wrap for labels in modern items, the `ion-text-nowrap` class can be added to the label.
2023-10-17 14:08:35 -04:00
356 changed files with 3163 additions and 781 deletions

View File

@@ -3,6 +3,35 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [7.6.0](https://github.com/ionic-team/ionic-framework/compare/v7.5.8...v7.6.0) (2023-12-06)
### Bug Fixes
* **angular,vue:** range form value updates while dragging knob ([#28422](https://github.com/ionic-team/ionic-framework/issues/28422)) ([0854a11](https://github.com/ionic-team/ionic-framework/commit/0854a11a25759d0201eae66c96a62fe138d486f8)), closes [#28256](https://github.com/ionic-team/ionic-framework/issues/28256)
* **animation:** add stronger types to Animation interface ([#28334](https://github.com/ionic-team/ionic-framework/issues/28334)) ([4a088d5](https://github.com/ionic-team/ionic-framework/commit/4a088d5d612ab0387064d388b37d46cdf15cf1ff))
* **animation:** progressEnd coercion is reset before onFinish ([#28394](https://github.com/ionic-team/ionic-framework/issues/28394)) ([eae8162](https://github.com/ionic-team/ionic-framework/commit/eae8162d0dc2e0bd7a9d56a3662a8e5f5d142b72)), closes [#28393](https://github.com/ionic-team/ionic-framework/issues/28393)
* **infinite-scroll:** remaining in threshold after ionInfinite can trigger event again on scroll ([#28569](https://github.com/ionic-team/ionic-framework/issues/28569)) ([8c235fd](https://github.com/ionic-team/ionic-framework/commit/8c235fd30c50f317de1f37f69068507aa0979068)), closes [#18071](https://github.com/ionic-team/ionic-framework/issues/18071)
* **item:** allow item to grow when it is used in a flex container ([#28594](https://github.com/ionic-team/ionic-framework/issues/28594)) ([1c1b567](https://github.com/ionic-team/ionic-framework/commit/1c1b567279dee44da70bb9b90c129946c9043987))
* **item:** wrap elements and label contents when the font size increases or the elements do not fit ([#28146](https://github.com/ionic-team/ionic-framework/issues/28146)) ([6438e3e](https://github.com/ionic-team/ionic-framework/commit/6438e3e919c665569b731a2d74fe1547b4f3c1cc))
* **select:** do not collapse to width: 0 when placed in flex container ([#28631](https://github.com/ionic-team/ionic-framework/issues/28631)) ([e71e7a0](https://github.com/ionic-team/ionic-framework/commit/e71e7a069000db8738abc304758de64286817442))
* **toast:** add swipeGesture to ToastOptions ([#28518](https://github.com/ionic-team/ionic-framework/issues/28518)) ([4ad6df6](https://github.com/ionic-team/ionic-framework/commit/4ad6df67f01cebce30d4da46c7541c4b14c5d4a4))
### Features
* **checkbox:** add shadow part for label ([#28604](https://github.com/ionic-team/ionic-framework/issues/28604)) ([f9f5654](https://github.com/ionic-team/ionic-framework/commit/f9f5654ab0e920bf97089fbabfb9eedbcf6fe8ae)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
* **input, textarea, select:** add start and end slots ([#28583](https://github.com/ionic-team/ionic-framework/issues/28583)) ([357b8b2](https://github.com/ionic-team/ionic-framework/commit/357b8b2beb29b95d53ef043af349067be1d32658)), closes [#26297](https://github.com/ionic-team/ionic-framework/issues/26297)
* **radio-group:** add compareWith property ([#28452](https://github.com/ionic-team/ionic-framework/issues/28452)) ([0ae327f](https://github.com/ionic-team/ionic-framework/commit/0ae327f0e09cd97d705f2d3051c215034381e226))
* **radio:** add shadow part for label ([#28607](https://github.com/ionic-team/ionic-framework/issues/28607)) ([b757970](https://github.com/ionic-team/ionic-framework/commit/b757970d23e87c59aa883ecb1bfa9b66bcae8de2)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
* **range:** expose label wrapper as shadow part ([#28601](https://github.com/ionic-team/ionic-framework/issues/28601)) ([52ed2bf](https://github.com/ionic-team/ionic-framework/commit/52ed2bf63777c764f57bb4c3a5d4a127bff46c50))
* **toast:** add swipe to dismiss functionality ([#28442](https://github.com/ionic-team/ionic-framework/issues/28442)) ([30c21aa](https://github.com/ionic-team/ionic-framework/commit/30c21aab3ed40d73c28e7d60d0952d8891b0a9d3)), closes [#21769](https://github.com/ionic-team/ionic-framework/issues/21769)
* **toggle:** expose label wrapper as shadow part ([#28585](https://github.com/ionic-team/ionic-framework/issues/28585)) ([a34188f](https://github.com/ionic-team/ionic-framework/commit/a34188f7dbec4a16e4f2043ed3dc096e337725a7))
## [7.5.8](https://github.com/ionic-team/ionic-framework/compare/v7.5.7...v7.5.8) (2023-12-06)

View File

@@ -3,6 +3,35 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [7.6.0](https://github.com/ionic-team/ionic-framework/compare/v7.5.8...v7.6.0) (2023-12-06)
### Bug Fixes
* **angular,vue:** range form value updates while dragging knob ([#28422](https://github.com/ionic-team/ionic-framework/issues/28422)) ([0854a11](https://github.com/ionic-team/ionic-framework/commit/0854a11a25759d0201eae66c96a62fe138d486f8)), closes [#28256](https://github.com/ionic-team/ionic-framework/issues/28256)
* **animation:** add stronger types to Animation interface ([#28334](https://github.com/ionic-team/ionic-framework/issues/28334)) ([4a088d5](https://github.com/ionic-team/ionic-framework/commit/4a088d5d612ab0387064d388b37d46cdf15cf1ff))
* **animation:** progressEnd coercion is reset before onFinish ([#28394](https://github.com/ionic-team/ionic-framework/issues/28394)) ([eae8162](https://github.com/ionic-team/ionic-framework/commit/eae8162d0dc2e0bd7a9d56a3662a8e5f5d142b72)), closes [#28393](https://github.com/ionic-team/ionic-framework/issues/28393)
* **infinite-scroll:** remaining in threshold after ionInfinite can trigger event again on scroll ([#28569](https://github.com/ionic-team/ionic-framework/issues/28569)) ([8c235fd](https://github.com/ionic-team/ionic-framework/commit/8c235fd30c50f317de1f37f69068507aa0979068)), closes [#18071](https://github.com/ionic-team/ionic-framework/issues/18071)
* **item:** allow item to grow when it is used in a flex container ([#28594](https://github.com/ionic-team/ionic-framework/issues/28594)) ([1c1b567](https://github.com/ionic-team/ionic-framework/commit/1c1b567279dee44da70bb9b90c129946c9043987))
* **item:** wrap elements and label contents when the font size increases or the elements do not fit ([#28146](https://github.com/ionic-team/ionic-framework/issues/28146)) ([6438e3e](https://github.com/ionic-team/ionic-framework/commit/6438e3e919c665569b731a2d74fe1547b4f3c1cc))
* **select:** do not collapse to width: 0 when placed in flex container ([#28631](https://github.com/ionic-team/ionic-framework/issues/28631)) ([e71e7a0](https://github.com/ionic-team/ionic-framework/commit/e71e7a069000db8738abc304758de64286817442))
* **toast:** add swipeGesture to ToastOptions ([#28518](https://github.com/ionic-team/ionic-framework/issues/28518)) ([4ad6df6](https://github.com/ionic-team/ionic-framework/commit/4ad6df67f01cebce30d4da46c7541c4b14c5d4a4))
### Features
* **checkbox:** add shadow part for label ([#28604](https://github.com/ionic-team/ionic-framework/issues/28604)) ([f9f5654](https://github.com/ionic-team/ionic-framework/commit/f9f5654ab0e920bf97089fbabfb9eedbcf6fe8ae)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
* **input, textarea, select:** add start and end slots ([#28583](https://github.com/ionic-team/ionic-framework/issues/28583)) ([357b8b2](https://github.com/ionic-team/ionic-framework/commit/357b8b2beb29b95d53ef043af349067be1d32658)), closes [#26297](https://github.com/ionic-team/ionic-framework/issues/26297)
* **radio-group:** add compareWith property ([#28452](https://github.com/ionic-team/ionic-framework/issues/28452)) ([0ae327f](https://github.com/ionic-team/ionic-framework/commit/0ae327f0e09cd97d705f2d3051c215034381e226))
* **radio:** add shadow part for label ([#28607](https://github.com/ionic-team/ionic-framework/issues/28607)) ([b757970](https://github.com/ionic-team/ionic-framework/commit/b757970d23e87c59aa883ecb1bfa9b66bcae8de2)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
* **range:** expose label wrapper as shadow part ([#28601](https://github.com/ionic-team/ionic-framework/issues/28601)) ([52ed2bf](https://github.com/ionic-team/ionic-framework/commit/52ed2bf63777c764f57bb4c3a5d4a127bff46c50))
* **toast:** add swipe to dismiss functionality ([#28442](https://github.com/ionic-team/ionic-framework/issues/28442)) ([30c21aa](https://github.com/ionic-team/ionic-framework/commit/30c21aab3ed40d73c28e7d60d0952d8891b0a9d3)), closes [#21769](https://github.com/ionic-team/ionic-framework/issues/21769)
* **toggle:** expose label wrapper as shadow part ([#28585](https://github.com/ionic-team/ionic-framework/issues/28585)) ([a34188f](https://github.com/ionic-team/ionic-framework/commit/a34188f7dbec4a16e4f2043ed3dc096e337725a7))
## [7.5.8](https://github.com/ionic-team/ionic-framework/compare/v7.5.7...v7.5.8) (2023-12-06)

View File

@@ -315,6 +315,7 @@ ion-checkbox,css-prop,--checkmark-width
ion-checkbox,css-prop,--size
ion-checkbox,css-prop,--transition
ion-checkbox,part,container
ion-checkbox,part,label
ion-checkbox,part,mark
ion-chip,shadow
@@ -1029,10 +1030,12 @@ ion-radio,css-prop,--color
ion-radio,css-prop,--color-checked
ion-radio,css-prop,--inner-border-radius
ion-radio,part,container
ion-radio,part,label
ion-radio,part,mark
ion-radio-group,none
ion-radio-group,prop,allowEmptySelection,boolean,false,false,false
ion-radio-group,prop,compareWith,((currentValue: any, compareValue: any) => boolean) | null | string | undefined,undefined,false,false
ion-radio-group,prop,name,string,this.inputId,false,false
ion-radio-group,prop,value,any,undefined,false,false
ion-radio-group,event,ionChange,RadioGroupChangeEventDetail<any>,true
@@ -1076,6 +1079,7 @@ ion-range,css-prop,--pin-color
ion-range,part,bar
ion-range,part,bar-active
ion-range,part,knob
ion-range,part,label
ion-range,part,pin
ion-range,part,tick
ion-range,part,tick-active
@@ -1446,6 +1450,7 @@ ion-toast,prop,message,IonicSafeString | string | undefined,undefined,false,fals
ion-toast,prop,mode,"ios" | "md",undefined,false,false
ion-toast,prop,position,"bottom" | "middle" | "top",'bottom',false,false
ion-toast,prop,positionAnchor,HTMLElement | string | undefined,undefined,false,false
ion-toast,prop,swipeGesture,"vertical" | undefined,undefined,false,false
ion-toast,prop,translucent,boolean,false,false,false
ion-toast,prop,trigger,string | undefined,undefined,false,false
ion-toast,method,dismiss,dismiss(data?: any, role?: string) => Promise<boolean>
@@ -1512,6 +1517,7 @@ ion-toggle,css-prop,--handle-width
ion-toggle,css-prop,--track-background
ion-toggle,css-prop,--track-background-checked
ion-toggle,part,handle
ion-toggle,part,label
ion-toggle,part,track
ion-toolbar,shadow

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "7.5.8",
"version": "7.6.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "7.5.8",
"version": "7.6.0",
"license": "MIT",
"dependencies": {
"@stencil/core": "^4.8.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "7.5.8",
"version": "7.6.0",
"description": "Base components for Ionic",
"keywords": [
"ionic",

21
core/setupJest.js Normal file
View File

@@ -0,0 +1,21 @@
expect.extend({
toHaveShadowPart(received, part) {
if (typeof part !== 'string') {
throw new Error('expected toHaveShadowPart to be called with a string of the CSS shadow part name');
}
if (received.shadowRoot === null) {
throw new Error('expected toHaveShadowPart to be called on an element with a shadow root');
}
const shadowPart = received.shadowRoot.querySelector(`[part="${part}"]`);
const pass = shadowPart !== null;
const message = `expected ${received.tagName.toLowerCase()} to have shadow part "${part}"`;
return {
pass,
message: () => message,
};
},
});

View File

@@ -27,7 +27,7 @@ import { PickerButton, PickerColumn } from "./components/picker/picker-interface
import { PickerColumnItem } from "./components/picker-column-internal/picker-column-internal-interfaces";
import { PickerInternalChangeEventDetail } from "./components/picker-internal/picker-internal-interfaces";
import { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface";
import { RadioGroupChangeEventDetail } from "./components/radio-group/radio-group-interface";
import { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface";
import { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface";
import { RefresherEventDetail } from "./components/refresher/refresher-interface";
import { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface";
@@ -39,7 +39,7 @@ import { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
import { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
import { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
import { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
import { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
export { AccordionGroupChangeEventDetail } from "./components/accordion-group/accordion-group-interface";
export { AnimationBuilder, AutocompleteTypes, Color, ComponentProps, ComponentRef, FrameworkDelegate, StyleEventDetail, TextFieldTypes } from "./interface";
@@ -63,7 +63,7 @@ export { PickerButton, PickerColumn } from "./components/picker/picker-interface
export { PickerColumnItem } from "./components/picker-column-internal/picker-column-internal-interfaces";
export { PickerInternalChangeEventDetail } from "./components/picker-internal/picker-internal-interfaces";
export { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface";
export { RadioGroupChangeEventDetail } from "./components/radio-group/radio-group-interface";
export { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface";
export { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface";
export { RefresherEventDetail } from "./components/refresher/refresher-interface";
export { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface";
@@ -75,7 +75,7 @@ export { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
export { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
export { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
export { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
export { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
export namespace Components {
interface IonAccordion {
@@ -2269,6 +2269,10 @@ export namespace Components {
* If `true`, the radios can be deselected.
*/
"allowEmptySelection": boolean;
/**
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-radio-group. When not specified, the default behavior will use strict equality (===) for comparison.
*/
"compareWith"?: string | RadioGroupCompareFn | null;
/**
* The name of the control, which is submitted with the form data.
*/
@@ -2688,7 +2692,7 @@ export namespace Components {
*/
"color"?: Color;
/**
* A property name or function used to compare object values
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-select. When not specified, the default behavior will use strict equality (===) for comparison.
*/
"compareWith"?: string | SelectCompareFn | null;
/**
@@ -3172,6 +3176,10 @@ export namespace Components {
* Present the toast overlay after it has been created.
*/
"present": () => Promise<void>;
/**
* If set to 'vertical', the Toast can be dismissed with a swipe gesture. The swipe direction is determined by the value of the `position` property: `top`: The Toast can be swiped up to dismiss. `bottom`: The Toast can be swiped down to dismiss. `middle`: The Toast can be swiped up or down to dismiss.
*/
"swipeGesture"?: ToastSwipeGestureDirection;
/**
* If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
*/
@@ -3390,6 +3398,10 @@ export interface IonSelectCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLIonSelectElement;
}
export interface IonSkeletonTextCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLIonSkeletonTextElement;
}
export interface IonSplitPaneCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLIonSplitPaneElement;
@@ -4404,7 +4416,18 @@ declare global {
prototype: HTMLIonSelectPopoverElement;
new (): HTMLIonSelectPopoverElement;
};
interface HTMLIonSkeletonTextElementEventMap {
"ionStyle": StyleEventDetail;
}
interface HTMLIonSkeletonTextElement extends Components.IonSkeletonText, HTMLStencilElement {
addEventListener<K extends keyof HTMLIonSkeletonTextElementEventMap>(type: K, listener: (this: HTMLIonSkeletonTextElement, ev: IonSkeletonTextCustomEvent<HTMLIonSkeletonTextElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof HTMLIonSkeletonTextElementEventMap>(type: K, listener: (this: HTMLIonSkeletonTextElement, ev: IonSkeletonTextCustomEvent<HTMLIonSkeletonTextElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
var HTMLIonSkeletonTextElement: {
prototype: HTMLIonSkeletonTextElement;
@@ -6934,6 +6957,10 @@ declare namespace LocalJSX {
* If `true`, the radios can be deselected.
*/
"allowEmptySelection"?: boolean;
/**
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-radio-group. When not specified, the default behavior will use strict equality (===) for comparison.
*/
"compareWith"?: string | RadioGroupCompareFn | null;
/**
* The name of the control, which is submitted with the form data.
*/
@@ -7416,7 +7443,7 @@ declare namespace LocalJSX {
*/
"color"?: Color;
/**
* A property name or function used to compare object values
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-select. When not specified, the default behavior will use strict equality (===) for comparison.
*/
"compareWith"?: string | SelectCompareFn | null;
/**
@@ -7553,6 +7580,10 @@ declare namespace LocalJSX {
* If `true`, the skeleton text will animate.
*/
"animated"?: boolean;
/**
* Emitted when the styles change.
*/
"onIonStyle"?: (event: IonSkeletonTextCustomEvent<StyleEventDetail>) => void;
}
interface IonSpinner {
/**
@@ -7951,6 +7982,10 @@ declare namespace LocalJSX {
* The element to anchor the toast's position to. Can be set as a direct reference or the ID of the element. With `position="bottom"`, the toast will sit above the chosen element. With `position="top"`, the toast will sit below the chosen element. With `position="middle"`, the value of `positionAnchor` is ignored.
*/
"positionAnchor"?: HTMLElement | string;
/**
* If set to 'vertical', the Toast can be dismissed with a swipe gesture. The swipe direction is determined by the value of the `position` property: `top`: The Toast can be swiped up to dismiss. `bottom`: The Toast can be swiped down to dismiss. `middle`: The Toast can be swiped up or down to dismiss.
*/
"swipeGesture"?: ToastSwipeGestureDirection;
/**
* If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
*/

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -90,13 +90,6 @@
}
.label-text-wrapper {
/**
* This ensures that double tapping this text
* clicks the <label> and focuses the checkbox
* when a screen reader is enabled.
*/
pointer-events: none;
text-overflow: ellipsis;
white-space: nowrap;
@@ -173,7 +166,6 @@ input {
opacity: 0;
}
// Justify Content
// ---------------------------------------------
@@ -200,7 +192,6 @@ input {
align-items: center;
}
// Label Placement - Start
// ----------------------------------------------------------------
@@ -221,7 +212,6 @@ input {
@include margin(null, $form-control-label-margin, null, 0);
}
// Label Placement - End
// ----------------------------------------------------------------
@@ -242,7 +232,6 @@ input {
@include margin(null, 0, null, $form-control-label-margin);
}
// Label Placement - Fixed
// ----------------------------------------------------------------
@@ -317,7 +306,6 @@ input {
opacity: 1;
}
// Disabled Checkbox
// ---------------------------------------------

View File

@@ -18,6 +18,7 @@ import type { CheckboxChangeEventDetail } from './checkbox-interface';
* @slot - The label text to associate with the checkbox. Use the "labelPlacement" property to control where the label is placed relative to the checkbox.
*
* @part container - The container for the checkbox mark.
* @part label - The label text describing the checkbox.
* @part mark - The checkmark used to indicate the checked state.
*/
@Component({
@@ -164,6 +165,8 @@ export class Checkbox implements ComponentInterface {
private emitStyle() {
const style: StyleEventDetail = {
'interactive-disabled': this.disabled,
// TODO(FW-3100): remove this
legacy: !!this.legacy,
};
// TODO(FW-3100): remove this
@@ -282,6 +285,7 @@ export class Checkbox implements ComponentInterface {
'label-text-wrapper': true,
'label-text-wrapper-hidden': el.textContent === '',
}}
part="label"
>
<slot></slot>
</div>

View File

@@ -2,6 +2,23 @@ import { newSpecPage } from '@stencil/core/testing';
import { Checkbox } from '../checkbox';
describe('ion-checkbox: shadow parts', () => {
it('should render the checkbox with shadow parts', async () => {
const page = await newSpecPage({
components: [Checkbox],
html: `
<ion-checkbox>Checkbox</ion-checkbox>
`,
});
const checkbox = page.body.querySelector('ion-checkbox')!;
expect(checkbox).toHaveShadowPart('container');
expect(checkbox).toHaveShadowPart('label');
expect(checkbox).toHaveShadowPart('mark');
});
});
describe('ion-checkbox: disabled', () => {
it('clicking disabled checkbox should not toggle checked state', async () => {
const page = await newSpecPage({

View File

@@ -267,6 +267,9 @@ ion-picker-column-internal {
justify-content: space-between;
}
// TODO(FW-3547): the styles targeting ion-item
// can be removed if we refactor datetime to
// not use an ion-item
:host .calendar-action-buttons ion-item,
:host .calendar-action-buttons ion-button {
--background: translucent;
@@ -276,6 +279,11 @@ ion-picker-column-internal {
display: flex;
align-items: center;
// Width is set to auto because it is set
// to min-content elsewhere and we want to
// prevent wrapping the label in datetime
width: auto;
}
:host .calendar-action-buttons ion-item ion-icon {

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -12,6 +12,14 @@ export class InfiniteScroll implements ComponentInterface {
private thrPx = 0;
private thrPc = 0;
private scrollEl?: HTMLElement;
/**
* didFire exists so that ionInfinite
* does not fire multiple times if
* users continue to scroll after
* scrolling into the infinite
* scroll threshold.
*/
private didFire = false;
private isBusy = false;
@@ -127,8 +135,6 @@ export class InfiniteScroll implements ComponentInterface {
this.ionInfinite.emit();
return 3;
}
} else {
this.didFire = false;
}
return 4;
@@ -190,10 +196,13 @@ export class InfiniteScroll implements ComponentInterface {
writeTask(() => {
scrollEl.scrollTop = newScrollTop;
this.isBusy = false;
this.didFire = false;
});
});
});
});
} else {
this.didFire = false;
}
}

View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Infinite Scroll - Small DOM Update</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
#list .item {
width: 100%;
border-bottom: 1px solid gray;
padding: 10px;
}
</style>
</head>
<body>
<ion-app>
<ion-content class="ion-padding" id="content">
<div id="list"></div>
<ion-infinite-scroll threshold="100px" id="infinite-scroll">
<ion-infinite-scroll-content loading-spinner="crescent" loading-text="Loading more data...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-content>
</ion-app>
<script>
const list = document.getElementById('list');
const infiniteScroll = document.getElementById('infinite-scroll');
infiniteScroll.addEventListener('ionInfinite', () => {
setTimeout(() => {
appendItems();
infiniteScroll.complete();
// Custom event consumed in the e2e tests
window.dispatchEvent(new CustomEvent('ionInfiniteComplete'));
}, 500);
});
function appendItems(count = 3) {
for (var i = 0; i < count; i++) {
const el = document.createElement('div');
el.classList.add('item');
el.textContent = `${1 + i}`;
list.appendChild(el);
}
}
appendItems(30);
</script>
</body>
</html>

View File

@@ -0,0 +1,34 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('infinite-scroll: appending small amounts to dom'), () => {
test('should load more after remaining in threshold', async ({ page }) => {
await page.goto('/src/components/infinite-scroll/test/small-dom-update', config);
const ionInfiniteComplete = await page.spyOnEvent('ionInfiniteComplete');
const content = page.locator('ion-content');
const items = page.locator('#list .item');
expect(await items.count()).toBe(30);
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
await ionInfiniteComplete.next();
/**
* Even after appending we'll still be within
* the infinite scroll's threshold
*/
expect(await items.count()).toBe(33);
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
await ionInfiniteComplete.next();
/**
* Scrolling down again without leaving
* the threshold should still trigger
* infinite scroll again.
*/
expect(await items.count()).toBe(36);
});
});
});

View File

@@ -89,9 +89,7 @@
/**
* This makes the label sit above the input.
*/
:host(.has-focus.input-fill-outline.input-label-placement-floating) .label-text-wrapper,
:host(.has-value.input-fill-outline.input-label-placement-floating) .label-text-wrapper,
:host(.input-fill-outline.input-label-placement-stacked) .label-text-wrapper {
:host(.label-floating.input-fill-outline) .label-text-wrapper {
@include transform(translateY(-32%), scale(#{$form-control-label-stacked-scale}));
@include margin(0);
@@ -216,8 +214,6 @@
* the floating/stacked label. We simulate this "cut out"
* by removing the top border from the notch fragment.
*/
:host(.has-focus.input-fill-outline.input-label-placement-floating) .input-outline-notch,
:host(.has-value.input-fill-outline.input-label-placement-floating) .input-outline-notch,
:host(.input-fill-outline.input-label-placement-stacked) .input-outline-notch {
:host(.label-floating.input-fill-outline) .input-outline-notch {
border-top: none;
}

View File

@@ -67,9 +67,7 @@
// Input Label
// ----------------------------------------------------------------
:host(.input-fill-solid.input-label-placement-stacked) .label-text-wrapper,
:host(.has-focus.input-fill-solid.input-label-placement-floating) .label-text-wrapper,
:host(.has-value.input-fill-solid.input-label-placement-floating) .label-text-wrapper {
:host(.label-floating.input-fill-solid.input-label-placement-floating) .label-text-wrapper {
/**
* Label text should not extend
* beyond the bounds of the input.

View File

@@ -322,6 +322,9 @@
flex-grow: 1;
// ensure start/end slot content is vertically centered
align-items: center;
width: 100%;
}
@@ -641,9 +644,7 @@
/**
* This makes the label sit above the input.
*/
:host(.input-label-placement-stacked) .label-text-wrapper,
:host(.has-focus.input-label-placement-floating) .label-text-wrapper,
:host(.has-value.input-label-placement-floating) .label-text-wrapper {
:host(.label-floating) .label-text-wrapper {
@include transform(translateY(50%), scale(#{$form-control-label-stacked-scale}));
/**
@@ -652,3 +653,14 @@
*/
max-width: calc(100% / #{$form-control-label-stacked-scale});
}
// Start/End Slots
// ----------------------------------------------------------------
::slotted([slot="start"]) {
margin-inline-end: $form-control-label-margin;
}
::slotted([slot="end"]) {
margin-inline-start: $form-control-label-margin;
}

View File

@@ -26,6 +26,8 @@ import { getCounterText } from './input.utils';
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
*
* @slot label - The label text to associate with the input. Use the `labelPlacement` property to control where the label is placed relative to the input. Use this if you need to render a label with custom HTML. (EXPERIMENTAL)
* @slot start - Content to display at the leading edge of the input. (EXPERIMENTAL)
* @slot end - Content to display at the trailing edge of the input. (EXPERIMENTAL)
*/
@Component({
tag: 'ion-input',
@@ -371,7 +373,7 @@ export class Input implements ComponentInterface {
const { el } = this;
this.legacyFormController = createLegacyFormController(el);
this.slotMutationController = createSlotMutationController(el, 'label', () => forceUpdate(this));
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
this.notchController = createNotchController(
el,
() => this.notchSpacerEl,
@@ -496,6 +498,8 @@ export class Input implements ComponentInterface {
'has-value': this.hasValue(),
'has-focus': this.hasFocus,
'interactive-disabled': this.disabled,
// TODO(FW-2764): remove this
legacy: !!this.legacy,
});
}
}
@@ -699,18 +703,42 @@ export class Input implements ComponentInterface {
}
private renderInput() {
const { disabled, fill, readonly, shape, inputId, labelPlacement } = this;
const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus } = this;
const mode = getIonMode(this);
const value = this.getValue();
const inItem = hostContext('ion-item', this.el);
const shouldRenderHighlight = mode === 'md' && fill !== 'outline' && !inItem;
const hasValue = this.hasValue();
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
/**
* If the label is stacked, it should always sit above the input.
* For floating labels, the label should move above the input if
* the input has a value, is focused, or has anything in either
* the start or end slot.
*
* If there is content in the start slot, the label would overlap
* it if not forced to float. This is also applied to the end slot
* because with the default or solid fills, the input is not
* vertically centered in the container, but the label is. This
* causes the slots and label to appear vertically offset from each
* other when the label isn't floating above the input. This doesn't
* apply to the outline fill, but this was not accounted for to keep
* things consistent.
*
* TODO(FW-5592): Remove hasStartEndSlots condition
*/
const labelShouldFloat =
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
return (
<Host
class={createColorClasses(this.color, {
[mode]: true,
'has-value': this.hasValue(),
'has-focus': this.hasFocus,
'has-value': hasValue,
'has-focus': hasFocus,
'label-floating': labelShouldFloat,
[`input-fill-${fill}`]: fill !== undefined,
[`input-shape-${shape}`]: shape !== undefined,
[`input-label-placement-${labelPlacement}`]: true,
@@ -719,9 +747,16 @@ export class Input implements ComponentInterface {
'input-disabled': disabled,
})}
>
<label class="input-wrapper">
{/**
* htmlFor is needed so that clicking the label always focuses
* the input. Otherwise, if the start slot has something
* interactable, clicking the label would focus that instead
* since it comes before the input in the DOM.
*/}
<label class="input-wrapper" htmlFor={inputId}>
{this.renderLabelContainer()}
<div class="native-wrapper">
<slot name="start"></slot>
<input
class="native-input"
ref={(input) => (this.nativeInput = input)}
@@ -776,6 +811,7 @@ export class Input implements ComponentInterface {
<ion-icon aria-hidden="true" icon={mode === 'ios' ? closeCircle : closeSharp}></ion-icon>
</button>
)}
<slot name="end"></slot>
</div>
{shouldRenderHighlight && <div class="input-highlight"></div>}
</label>

View File

@@ -21,7 +21,13 @@
<ion-input label="Email" label-placement="stacked" value="hi@ionic.io"></ion-input><br />
<ion-input label="Email" label-placement="floating"></ion-input> <br />
<ion-input label="Email" label-placement="floating" fill="outline" value="hi@ionic.io"></ion-input> <br />
<ion-input label="Email" label-placement="floating" fill="solid" value="hi@ionic.io"></ion-input>
<ion-input label="Email" label-placement="floating" fill="solid" value="hi@ionic.io"></ion-input><br />
<ion-input label="Email" fill="solid" value="hi@ionic.io">
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
<ion-button slot="end" aria-label="button">
<ion-icon slot="icon-only" name="lock-closed" aria-hidden="true"></ion-icon>
</ion-button>
</ion-input>
</main>
</body>
</html>

View File

@@ -29,7 +29,7 @@
<h2>Inactive</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
@@ -37,7 +37,7 @@
<h2>Focused</h2>
<ion-item class="item-has-focus">
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
@@ -45,7 +45,7 @@
<h2>Activated</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
<ion-input legacy="true" placeholder="Placeholder Text" value="Input Text"></ion-input>
</ion-item>
</div>
@@ -53,7 +53,7 @@
<h2>Hover</h2>
<ion-item class="item-hovered">
<ion-label position="floating">Label</ion-label>
<ion-input></ion-input>
<ion-input legacy="true"></ion-input>
</ion-item>
</div>
@@ -61,7 +61,7 @@
<h2>Disabled</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input disabled></ion-input>
<ion-input legacy="true" disabled></ion-input>
</ion-item>
</div>
@@ -69,7 +69,7 @@
<h2>Toggle Placeholder</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input id="floatingToggle" type="password"></ion-input>
<ion-input legacy="true" id="floatingToggle" type="password"></ion-input>
<ion-button
fill="clear"
slot="end"
@@ -89,7 +89,7 @@
<h2>Inactive</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input></ion-input>
<ion-input legacy="true"></ion-input>
</ion-item>
</div>
@@ -97,7 +97,7 @@
<h2>Focused</h2>
<ion-item class="item-has-focus">
<ion-label position="stacked">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
@@ -105,7 +105,7 @@
<h2>Activated</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
<ion-input legacy="true" placeholder="Placeholder Text" value="Input Text"></ion-input>
</ion-item>
</div>
@@ -113,7 +113,7 @@
<h2>Hover</h2>
<ion-item class="item-hovered">
<ion-label position="stacked">Label</ion-label>
<ion-input></ion-input>
<ion-input legacy="true"></ion-input>
</ion-item>
</div>
@@ -121,7 +121,7 @@
<h2>Disabled</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input disabled></ion-input>
<ion-input legacy="true" disabled></ion-input>
</ion-item>
</div>
@@ -129,7 +129,7 @@
<h2>Toggle Placeholder</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input id="stackedToggle" type="password"></ion-input>
<ion-input legacy="true" id="stackedToggle" type="password"></ion-input>
<ion-button
fill="clear"
slot="end"

View File

@@ -49,53 +49,125 @@
</ion-header>
<ion-content id="content" class="ion-padding">
<h1>Label Slot</h1>
<div class="grid">
<div class="grid-item">
<h2>No Fill / Start</h2>
<h2>No Fill / Start Label</h2>
<ion-input label-placement="start" value="hi@ionic.io">
<div slot="label">Email <span class="required">*</span></div>
</ion-input>
</div>
<div class="grid-item">
<h2>Solid / Start</h2>
<h2>Solid / Start Label</h2>
<ion-input label-placement="start" fill="solid" value="hi@ionic.io">
<div slot="label">Email <span class="required">*</span></div>
</ion-input>
</div>
<div class="grid-item">
<h2>Outline / Start</h2>
<h2>Outline / Start Label</h2>
<ion-input label-placement="start" fill="outline" value="hi@ionic.io">
<div slot="label">Email <span class="required">*</span></div>
</ion-input>
</div>
<div class="grid-item">
<h2>No Fill / Floating</h2>
<h2>No Fill / Floating Label</h2>
<ion-input label-placement="floating" value="hi@ionic.io">
<div slot="label">Email <span class="required">*</span></div>
</ion-input>
</div>
<div class="grid-item">
<h2>Solid / Floating</h2>
<h2>Solid / Floating Label</h2>
<ion-input label-placement="floating" fill="solid" value="hi@ionic.io">
<div slot="label">Email <span class="required">*</span></div>
</ion-input>
</div>
<div class="grid-item">
<h2>Outline / Floating</h2>
<h2>Outline / Floating Label</h2>
<ion-input label-placement="floating" fill="outline" value="hi@ionic.io">
<div slot="label">Email <span class="required">*</span></div>
</ion-input>
</div>
</div>
<h1>Start/End Slots</h1>
<div class="grid">
<div class="grid-item">
<h2>No Fill / Start Label</h2>
<ion-input label-placement="start" value="hi@ionic.io" label="Email">
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
</ion-button>
</ion-input>
</div>
<div class="grid-item">
<h2>Outline / Floating / Async</h2>
<h2>Solid / Start Label</h2>
<ion-input label-placement="start" fill="solid" value="hi@ionic.io" label="Email">
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
</ion-button>
</ion-input>
</div>
<div class="grid-item">
<h2>Outline / Start Label</h2>
<ion-input label-placement="start" fill="outline" value="hi@ionic.io" label="Email">
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
</ion-button>
</ion-input>
</div>
<div class="grid-item">
<h2>No Fill / Floating Label</h2>
<ion-input label-placement="floating" value="hi@ionic.io" label="Email">
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
</ion-button>
</ion-input>
</div>
<div class="grid-item">
<h2>Solid / Floating Label</h2>
<ion-input label-placement="floating" fill="solid" value="hi@ionic.io" label="Email">
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
</ion-button>
</ion-input>
</div>
<div class="grid-item">
<h2>Outline / Floating Label</h2>
<ion-input label-placement="floating" fill="outline" value="hi@ionic.io" label="Email">
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
</ion-button>
</ion-input>
</div>
</div>
<h1>Async Slot Content</h1>
<div class="grid">
<div class="grid-item">
<h2>Outline / Async Label</h2>
<ion-input id="solid-async" label-placement="floating" fill="outline" value="hi@ionic.io"></ion-input>
</div>
<div class="grid-item">
<h2>Outline / Async Decorations</h2>
<ion-input id="async-decorations" label-placement="floating" fill="outline" label="Email"></ion-input>
</div>
</div>
<ion-button onclick="addSlot()">Add Slotted Content</ion-button>
@@ -106,29 +178,65 @@
<script>
const solidAsync = document.querySelector('#solid-async');
const asyncDecos = document.querySelector('#async-decorations');
const getSlottedContent = () => {
const getSlottedLabel = () => {
return solidAsync.querySelector('[slot="label"]');
};
const getSlottedStartDeco = () => {
return asyncDecos.querySelector('[slot="start"]');
};
const getSlottedEndDeco = () => {
return asyncDecos.querySelector('[slot="end"]');
};
const addSlot = () => {
if (getSlottedContent() === null) {
if (getSlottedLabel() === null) {
const labelEl = document.createElement('div');
labelEl.slot = 'label';
labelEl.innerHTML = 'Email <span class="required">*</span>';
solidAsync.appendChild(labelEl);
}
if (getSlottedStartDeco() === null) {
const startEl = document.createElement('div');
startEl.slot = 'start';
startEl.innerHTML = 'Start';
asyncDecos.insertAdjacentElement('afterbegin', startEl);
}
if (getSlottedEndDeco() === null) {
const endEl = document.createElement('div');
endEl.slot = 'end';
endEl.innerHTML = 'End';
asyncDecos.insertAdjacentElement('beforeend', endEl);
}
};
const removeSlot = () => {
if (getSlottedContent() !== null) {
solidAsync.querySelector('[slot="label"]').remove();
const slottedLabel = getSlottedLabel();
if (slottedLabel !== null) {
slottedLabel.remove();
}
const slottedStartDeco = getSlottedStartDeco();
if (slottedStartDeco !== null) {
slottedStartDeco.remove();
}
const slottedEndDeco = getSlottedEndDeco();
if (slottedEndDeco !== null) {
slottedEndDeco.remove();
}
};
const updateSlot = () => {
const slottedContent = getSlottedContent();
const slottedContent = getSlottedLabel();
if (slottedContent !== null) {
slottedContent.textContent = 'This is my really really really long text';

View File

@@ -0,0 +1,68 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
configs().forEach(({ title, screenshot, config }) => {
test.describe(title('input: start and end slots (visual checks)'), () => {
test('should not have visual regressions with a start-positioned label', async ({ page }) => {
await page.setContent(
`
<ion-input label-placement="start" fill="solid" value="100" label="Weight" clear-input="true">
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
<ion-label slot="end">lbs</ion-label>
</ion-input>
`,
config
);
const input = page.locator('ion-input');
await expect(input).toHaveScreenshot(screenshot(`input-slots-label-start`));
});
test('should not have visual regressions with a floating label', async ({ page }) => {
await page.setContent(
`
<ion-input label-placement="floating" fill="solid" value="100" label="Weight" clear-input="true">
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
<ion-label slot="end">lbs</ion-label>
</ion-input>
`,
config
);
const input = page.locator('ion-input');
await expect(input).toHaveScreenshot(screenshot(`input-slots-label-floating`));
});
});
});
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('input: start and end slots (functionality checks)'), () => {
test('should raise floating label when there is content in the start slot', async ({ page }) => {
await page.setContent(
`
<ion-input label-placement="floating" fill="solid" label="Weight">
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
</ion-input>
`,
config
);
const input = page.locator('ion-input');
await expect(input).toHaveClass(/label-floating/);
});
test('should raise floating label when there is content in the end slot', async ({ page }) => {
await page.setContent(
`
<ion-input label-placement="floating" fill="solid" label="Weight">
<ion-icon slot="end" name="barbell" aria-hidden="true"></ion-icon>
</ion-input>
`,
config
);
const input = page.locator('ion-input');
await expect(input).toHaveClass(/label-floating/);
});
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -51,6 +51,15 @@
* @prop --highlight-color-valid: The color of the highlight on the item when valid. Only applies to inputs and textareas using the legacy form syntax. DEPRECATED: Highlights can be styled on `ion-input` or `ion-textarea` when using the modern form syntax.
* @prop --highlight-color-invalid: The color of the highlight on the item when invalid. Only applies to inputs and textareas using the legacy form syntax. DEPRECATED: Highlights can be styled on `ion-input` or `ion-textarea` when using the modern form syntax.
*/
/**
* We change the minimum width as the
* font size changes. Using a fixed minimum
* width means that fewer and fewer characters
* can be displayed in the same space as the
* text grows.
*/
--inner-min-width: 4rem;
--border-radius: 0px;
--border-width: 0px;
--border-style: solid;
@@ -221,6 +230,11 @@
display: flex;
position: relative;
// Flex wrap is required here in order to wrap
// the start slot + .item-inner content that
// doesn't fit on the same line
flex-wrap: wrap;
align-items: inherit;
justify-content: inherit;
@@ -238,9 +252,15 @@
background: var(--background);
overflow: inherit;
box-sizing: border-box;
z-index: 1;
box-sizing: border-box;
}
// TODO(FW-5289): remove
:host(.item-legacy) .item-native {
flex-wrap: unset;
}
.item-native::-moz-focus-inner {
@@ -287,11 +307,34 @@ button, a {
// This is required to work with an inset highlight
position: relative;
flex: 1;
// This flex property is required in order to make
// the elements wrap when there is a slotted start
// element and a label
flex: 1 0 0;
flex-direction: inherit;
// Flex wrap is required here in order to wrap
// .input-wrapper content + the end slot that
// doesn't fit on the same line
flex-wrap: wrap;
align-items: inherit;
align-self: stretch;
/**
* The min-width defines when the
* content in the default slot should
* stop wrapping/truncating within its own
* container. At this point the entire
* container will wrap to the next line.
*/
min-width: var(--inner-min-width);
// Max width must be set to 100%, otherwise the
// elements will overflow this container instead
// of wrapping
max-width: 100%;
min-height: inherit;
border-width: var(--inner-border-width);
@@ -303,6 +346,14 @@ button, a {
box-sizing: border-box;
}
// TODO(FW-5289): remove
:host(.item-legacy) .item-inner {
flex: 1;
flex-wrap: unset;
max-width: unset;
}
// Item Bottom
// --------------------------------------------------
@@ -370,6 +421,11 @@ button, a {
::slotted(ion-label:not([slot="end"])) {
flex: 1;
// Setting width to min-content allows the label to
// shrink and wrap its text instead of moving to its
// own row if there are slotted elements next to it
width: min-content;
}
// Item Input
@@ -382,18 +438,41 @@ button, a {
.input-wrapper {
display: flex;
flex: 1;
// This flex property is required in order to keep
// the label from shrinking when there are wide
// elements next to it
flex: 1 0 auto;
flex-direction: inherit;
// Flex wrap is required here in order to wrap
// content in the default slot (such as a label
// and a button) that doesn't fit on the same line
flex-wrap: wrap;
align-items: inherit;
align-self: stretch;
// Max width must be set to 100%, otherwise the
// elements will overflow this container instead
// of wrapping
max-width: 100%;
text-overflow: ellipsis;
overflow: inherit;
box-sizing: border-box;
}
// TODO(FW-5289): remove
:host(.item-legacy) .input-wrapper {
flex: 1;
flex-wrap: unset;
max-width: unset;
}
:host(.item-label-stacked),
:host(.item-label-floating) {
align-items: start;

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -43,11 +43,13 @@
</ion-item>
<ion-item>
<ion-label> Single line text that should have ellipses when it doesn't all fit in the item</ion-label>
<ion-label class="ion-text-nowrap">
Single line text that should have ellipses when it doesn't all fit in the item
</ion-label>
</ion-item>
<ion-item lines="none">
<ion-label> Single line item with no lines</ion-label>
<ion-label>Single line item with no lines</ion-label>
</ion-item>
<ion-item>
@@ -57,21 +59,21 @@
</ion-item>
<ion-item color="secondary">
<ion-label class="ion-text-wrap">
<ion-label>
<h1>H1 Title Text</h1>
<p>Paragraph line 1</p>
</ion-label>
</ion-item>
<ion-item>
<ion-label class="ion-text-wrap">
<ion-label>
<h2>H2 Title Text</h2>
<p>Paragraph line 1</p>
</ion-label>
</ion-item>
<ion-item>
<ion-label class="ion-text-wrap">
<ion-label>
<ion-text color="primary">
<h3>H3 Title Text</h3>
</ion-text>
@@ -83,7 +85,7 @@
</ion-item>
<ion-item>
<ion-label class="ion-text-wrap">
<ion-label>
<h4>H4 Title Text</h4>
<p>Paragraph line 1</p>
<p>Paragraph line 2</p>
@@ -92,7 +94,7 @@
</ion-item>
<ion-item>
<ion-label> Item using inner ion-label </ion-label>
<ion-label>Item using inner ion-label </ion-label>
</ion-item>
<ion-item class="overflow-visible">

View File

@@ -31,7 +31,7 @@ configs().forEach(({ title, screenshot, config }) => {
</ion-item>
<ion-item>
<ion-label> Single line text that should have ellipses when it doesn't all fit in the item</ion-label>
<ion-label class="ion-text-nowrap"> Single line text that should have ellipses when it doesn't all fit in the item</ion-label>
</ion-item>
</ion-list>
`,

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -144,8 +144,7 @@
<ion-item>
<ion-checkbox aria-label="Checkbox" slot="start"></ion-checkbox>
<ion-label>Checkbox + Toggle</ion-label>
<ion-toggle aria-label="Checkbox" disabled value="45"></ion-toggle>
<ion-toggle disabled value="45">Checkbox + Toggle</ion-toggle>
</ion-item>
<ion-item>

View File

@@ -11,7 +11,6 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
await page.setIonViewport();
// TODO: FW-4037 - Fix label color inconsistency between disabled controls
await expect(page).toHaveScreenshot(screenshot(`item-disabled-diff`));
});
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 87 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Some files were not shown because too many files have changed in this diff Show More