Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/src/components/input/input.ios.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,8 @@
:host(.input-label-placement-stacked) .input-wrapper {
min-height: 56px;
}

:host(.input-label-placement-stacked) .label-text-wrapper,
:host(.input-label-placement-floating) .label-text-wrapper {
max-width: 100%;
}
73 changes: 60 additions & 13 deletions core/src/components/input/input.md.outline.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import "./input.vars";

// Input Fill: Outline
// ----------------------------------------------------------------

Expand All @@ -9,7 +11,7 @@
}

:host(.input-fill-outline.input-shape-round) {
--border-radius: 9999px;
--border-radius: 28px;
--padding-start: 32px;
--padding-end: 32px;
}
Expand Down Expand Up @@ -62,7 +64,15 @@

:host(.input-fill-outline.input-label-placement-stacked) .label-text-wrapper,
:host(.input-fill-outline.input-label-placement-floating) .label-text-wrapper {
@include transform-origin(center, top);
@include transform-origin(start, top);

position: absolute;

/**
* Label text should not extend
* beyond the bounds of the input.
*/
max-width: calc(100% - var(--padding-start) - var(--padding-end));
}

/**
Expand All @@ -83,8 +93,14 @@
: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(.input-fill-outline.input-label-placement-stacked) .label-text-wrapper {
@include transform(translateY(-32%), scale(.75));
@include transform(translateY(-32%), scale(#{$input-floating-label-scale}));
@include margin(0);

/**
* Label text should not extend
* beyond the bounds of the input.
*/
max-width: calc((100% - var(--padding-start) - var(--padding-end) - #{$input-md-floating-label-padding * 2}) / #{$input-floating-label-scale});
}

/**
Expand Down Expand Up @@ -128,20 +144,51 @@
border-bottom: var(--border-width) var(--border-style) var(--border-color);
}

:host(.input-fill-outline) .input-outline-start {
@include border-radius(var(--border-radius), 0px, 0px, var(--border-radius));
@include border(null, null, null, var(--border-width) var(--border-style) var(--border-color));

width: 12px;
/**
* Ensures long labels do not cause the notch to flow
* out of bounds.
*/
:host(.input-fill-outline) .input-outline-notch {
max-width: calc(100% - var(--padding-start) - var(--padding-end));
}

/**
* When shape="round", the starting outline fragment
* should appear with a pill shape.
* This element ensures that the notch used
* the size of the scaled text so that the
* border cut out is the correct width.
* The text in this element should not
* be interactive.
*/
:host(.input-fill-outline.input-shape-round) .input-outline-start {
@include border-radius(28px, 0px, 0px, 28px);
width: 28px;
:host(.input-fill-outline) .notch-spacer {
/**
* We need $input-md-floating-label-padding of padding on the right.
* However, we also subtracted $input-md-floating-label-padding from
* the width of .input-outline-start
* to create space, so we need to take
* that into consideration here.
*/
@include padding(null, #{$input-md-floating-label-padding * 2}, null, null);

font-size: calc(1em * #{$input-floating-label-scale});

opacity: 0;
pointer-events: none;
}


:host(.input-fill-outline) .input-outline-start {
@include border-radius(var(--border-radius), 0px, 0px, var(--border-radius));
@include border(null, null, null, var(--border-width) var(--border-style) var(--border-color));

/**
* There should be spacing between the translated text
* and .input-outline-start. However, we can't add this
* spacing onto the notch because it would cause the
* label to look like it is not aligned with the
* text input. Instead, we subtract a few pixels from
* this element.
*/
width: calc(var(--padding-start) - #{$input-md-floating-label-padding});
}

:host(.input-fill-outline) .input-outline-end {
Expand Down
21 changes: 21 additions & 0 deletions core/src/components/input/input.md.solid.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import "./input.vars";

// Input Fill: Solid
// ----------------------------------------------------------------

Expand Down Expand Up @@ -59,3 +61,22 @@
*/
@include border-radius(var(--border-radius), var(--border-radius), 0px, 0px);
}

// Input Label
// ----------------------------------------------------------------

:host(.input-fill-solid.input-label-placement-stacked) .label-text-wrapper,
:host(.input-fill-solid.input-label-placement-floating) .label-text-wrapper {
max-width: 100%;
}

:host(.has-focus.input-fill-solid.input-label-placement-stacked) .label-text-wrapper,
:host(.has-value.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 {
/**
* Label text should not extend
* beyond the bounds of the input.
*/
max-width: calc(100% / #{$input-floating-label-scale});
}
3 changes: 3 additions & 0 deletions core/src/components/input/input.md.vars.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,6 @@ $input-md-inset-margin-bottom: ($item-md-padding-bottom * 0.5) !defau

/// @prop - Margin start of the inset input
$input-md-inset-margin-start: $item-md-padding-start !default;

/// @prop - The amount of whitespace to display on either side of the floating label
$input-md-floating-label-padding: 4px !default;
41 changes: 34 additions & 7 deletions core/src/components/input/input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,6 @@
// ----------------------------------------------------------------

.label-text-wrapper {
/**
* The margin between the label and
* the input should be on the end
* when the label sits at the start.
*/
@include margin(0, 8px, 0, 0);

/**
* This causes the label to take up
* the entire height of its container
Expand All @@ -384,6 +377,16 @@

align-items: center;

/**
* Label text should not extend
* beyond the bounds of the input.
* However, we do not set the max
* width to 100% because then
* only the label would show and users
* would not be able to see what they are typing.
*/
max-width: 200px;

transition: color 150ms cubic-bezier(.4, 0, .2, 1), transform 150ms cubic-bezier(.4, 0, .2, 1);

/**
Expand Down Expand Up @@ -435,6 +438,15 @@
flex-direction: row;
}

:host(.input-label-placement-start) .label-text-wrapper {
/**
* The margin between the label and
* the input should be on the end
* when the label sits at the start.
*/
@include margin(0, 8px, 0, 0);
}

// Input Label Placement - End
// ----------------------------------------------------------------

Expand All @@ -458,6 +470,15 @@
// Input Label Placement - Fixed
// ----------------------------------------------------------------

:host(.input-label-placement-fixed) .label-text-wrapper {
/**
* The margin between the label and
* the input should be on the end
* when the label sits at the start.
*/
@include margin(0, 8px, 0, 0);
}

/**
* Label is on the left of the input in LTR and
* on the right in RTL. Label also has a fixed width.
Expand Down Expand Up @@ -534,4 +555,10 @@
:host(.has-focus.input-label-placement-floating) .label-text-wrapper,
:host(.has-value.input-label-placement-floating) .label-text-wrapper {
@include transform(translateY(50%), scale(.75));

/**
* Label text should not extend
* beyond the bounds of the input.
*/
max-width: calc(100% / #{$input-floating-label-scale});
}
20 changes: 10 additions & 10 deletions core/src/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -572,28 +572,28 @@ export class Input implements ComponentInterface {
* when fill="outline".
*/
private renderLabelContainer() {
const { labelPlacement } = this;
const hasOutlineFill = this.fill === 'outline';
const needsNotch = labelPlacement === 'floating' || labelPlacement === 'stacked';
const mode = getIonMode(this);
const hasOutlineFill = mode === 'md' && this.fill === 'outline';

if (hasOutlineFill) {
/**
* The outline fill has a special outline
* that appears around the input and the label.
* Certain label placements cause the
* Certain stacked and floating label placements cause the
* label to translate up and create a "cut out"
* inside of that border. When this happens, we need
* to render the label inside of the input-outline-notch
* element. Otherwise, we can render it as a sibling
* of the outline container.
* inside of that border by using the notch-spacer element.
*/
return [
<div class="input-outline-container">
<div class="input-outline-start"></div>
<div class="input-outline-notch">{needsNotch && this.renderLabel()}</div>
<div class="input-outline-notch">
<div class="notch-spacer" aria-hidden="true">
{this.label}
</div>
</div>
<div class="input-outline-end"></div>
</div>,
!needsNotch && this.renderLabel(),
this.renderLabel(),
];
}

Expand Down
3 changes: 3 additions & 0 deletions core/src/components/input/input.vars.scss
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
@import "../../themes/ionic.globals";

/// @prop - How much to scale the floating label by
$input-floating-label-scale: 0.75 !default;
90 changes: 90 additions & 0 deletions core/src/components/input/test/fill/input.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ test.describe('input: fill', () => {
const input = page.locator('ion-input');
expect(await input.screenshot()).toMatchSnapshot(`input-fill-solid-${page.getSnapshotSettings()}.png`);
});
test('should render correctly with floating label', async ({ page }) => {
await page.setContent(`
<ion-input
fill="solid"
label="Email"
label-placement="floating"
value="hi@ionic.io"
helper-text="Enter your email"
maxlength="20"
counter="true"
></ion-input>
`);

const input = page.locator('ion-input');
expect(await input.screenshot()).toMatchSnapshot(
`input-fill-solid-label-floating-${page.getSnapshotSettings()}.png`
);
});
test('should not have visual regressions with shaped solid', async ({ page }) => {
await page.setContent(`
<ion-input
Expand All @@ -38,6 +56,33 @@ test.describe('input: fill', () => {
const input = page.locator('ion-input');
expect(await input.screenshot()).toMatchSnapshot(`input-fill-shaped-solid-${page.getSnapshotSettings()}.png`);
});
test('padding and border radius should be customizable', async ({ page }) => {
await page.setContent(`
<style>
ion-input {
--border-radius: 10px !important;
--padding-start: 50px !important;
--padding-end: 50px !important;
}
</style>

<ion-input
shape="round"
fill="solid"
label="Email"
label-placement="floating"
value="hi@ionic.io"
helper-text="Enter your email"
maxlength="20"
counter="true"
></ion-input>
`);

const input = page.locator('ion-input');
expect(await input.screenshot()).toMatchSnapshot(
`input-fill-shaped-solid-custom-${page.getSnapshotSettings()}.png`
);
});
});
test.describe('input: fill outline', () => {
test('should not have visual regressions', async ({ page }) => {
Expand All @@ -55,6 +100,24 @@ test.describe('input: fill', () => {
const input = page.locator('ion-input');
expect(await input.screenshot()).toMatchSnapshot(`input-fill-outline-${page.getSnapshotSettings()}.png`);
});
test('should render correctly with floating label', async ({ page }) => {
await page.setContent(`
<ion-input
fill="outline"
label="Email"
label-placement="floating"
value="hi@ionic.io"
helper-text="Enter your email"
maxlength="20"
counter="true"
></ion-input>
`);

const input = page.locator('ion-input');
expect(await input.screenshot()).toMatchSnapshot(
`input-fill-outline-label-floating-${page.getSnapshotSettings()}.png`
);
});
test('should not have visual regressions with shaped outline', async ({ page }) => {
await page.setContent(`
<ion-input
Expand All @@ -71,5 +134,32 @@ test.describe('input: fill', () => {
const input = page.locator('ion-input');
expect(await input.screenshot()).toMatchSnapshot(`input-fill-shaped-outline-${page.getSnapshotSettings()}.png`);
});
test('padding and border radius should be customizable', async ({ page }) => {
await page.setContent(`
<style>
ion-input {
--border-radius: 10px !important;
--padding-start: 50px !important;
--padding-end: 50px !important;
}
</style>

<ion-input
shape="round"
fill="outline"
label="Email"
label-placement="floating"
value="hi@ionic.io"
helper-text="Enter your email"
maxlength="20"
counter="true"
></ion-input>
`);

const input = page.locator('ion-input');
expect(await input.screenshot()).toMatchSnapshot(
`input-fill-shaped-outline-custom-${page.getSnapshotSettings()}.png`
);
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading