Skip to content

Commit

Permalink
feat(textfield): add prefix and suffix
Browse files Browse the repository at this point in the history
Fixes #1892

PiperOrigin-RevId: 305502022
  • Loading branch information
asyncLiz authored and copybara-github committed Apr 8, 2020
1 parent 82fa986 commit 717058b
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 9 deletions.
25 changes: 25 additions & 0 deletions packages/mdc-textfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,24 @@ inside of text field component.
Helper text and Character counter are optional subcomponents of text field that can co-exist independently.
It is recommended that `.mdc-text-field` and `.mdc-text-field-helper-line` elements have same width for correct layout.

### Text field with prefix and suffix text

Prefix and suffix text can add context to a text field, such as a currency symbol prefix or a unit of mass suffix.
A prefix, suffix, or both can be added within the default or outlined variants of text fields.

```html
<label class="mdc-text-field">
<span class="mdc-text-field__ripple"></span>
<span class="mdc-text-field__affix mdc-text-field__affix--prefix">$</span>
<input class="mdc-text-field__input" type="text" aria-labelledby="my-label-id">
<span class="mdc-text-field__affix mdc-text-field__affix--suffix">.00</span>
<span class="mdc-floating-label" id="my-label-id">Currency Value</span>
<span class="mdc-line-ripple"></span>
</label>
```

**Note: Do not use `mdc-text-field--affix` within `mdc-text-field--textarea`.**

### Text field with leading and trailing icons

Leading and trailing icons can be added within the default or outlined variant of MDC Text Field as visual indicators as
Expand Down Expand Up @@ -319,6 +337,7 @@ CSS Class | Description
`mdc-text-field--focused` | Styles the text field as a text field in focus.
`mdc-text-field--no-label` | Styles the text field that has no label.
`mdc-text-field--end-aligned` | Styles the text field with an end-aligned input.
`mdc-text-field--label-floating` | Styles the text field with a floating label and pre-filled or focused value.
`mdc-text-field-helper-line` | Styles the container of helper text and character counter elements.

### Sass mixins
Expand All @@ -341,6 +360,10 @@ Mixin | Description
`label-color($color)` | Customizes the text color of the label in an enabled text field.
`disabled-label-color($color)` | Customizes the text color of the label in a disabled text field.
`caret-color($color)` | Customizes the color of the cursor caret of the text field.
`prefix-color($color)` | Customizes the color of the prefix text of an enabled text field.
`disabled-prefix-color($color)` | Customizes the color of the prefix text of a disabled text field.
`suffix-color($color)` | Customizes the color of the suffix text of an enabled text field.
`disabled-suffix-color($color)` | Customizes the color of the suffix text of a disabled text field.

#### Mixins for filled text field and textarea

Expand Down Expand Up @@ -400,6 +423,8 @@ Property | Value Type | Description
`trailingIconAriaLabel` | `string` (write-only) | Proxies to the foundation's `setTrailingIconAriaLabel` method.
`leadingIconContent` | `string` (write-only) | Proxies to the foundation's `setLeadingIconContent` method.
`trailingIconContent` | `string` (write-only) | Proxies to the foundation's `setTrailingIconContent` method.
`prefixText` | `string` | Gets or sets the text content of the prefix, if it exists.
`suffixText` | `string` | Gets or sets the text content of the suffix, if it exists.

In addition to the above, the following properties proxy to the `input` element's properties of the same name:

Expand Down
166 changes: 158 additions & 8 deletions packages/mdc-textfield/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
@include icon-mixins.leading-icon-color(variables.$icon-color, $query: $query);
@include icon-mixins.trailing-icon-color(variables.$icon-color, $query: $query);
@include fill-color(variables.$background, $query: $query);
@include prefix-color(variables.$affix-color, $query: $query);
@include suffix-color(variables.$affix-color, $query: $query);

// Floating Label
@include floating-label_($query);
Expand All @@ -101,13 +103,10 @@
}

.mdc-text-field__input {
// Exclude setting line-height to keep caret (text cursor) same height as the input text in iOS browser.
@include typography-mixins.typography(subtitle1, $exclude-props: (line-height), $query: $query);

@include _text($query: $query);
@include feature-targeting-mixins.targets($feat-structure) {
width: 100%;
min-width: 0; // Fixes flex issues on Firefox
height: variables.$input-height;
border: none;
border-radius: 0;
background: none;
Expand All @@ -120,10 +119,6 @@
}
}

@include feature-targeting-mixins.targets($feat-animation) {
transition: functions.transition(opacity);
}

@include placeholder-selector_ {
@include feature-targeting-mixins.targets($feat-animation) {
transition: functions.transition(opacity, $duration: 67ms);
Expand Down Expand Up @@ -172,6 +167,31 @@
}
}

.mdc-text-field__affix {
@include _affix($query: $query);

.mdc-text-field--label-floating &,
.mdc-text-field--no-label & {
@include _affix-visible($query: $query);
}
}

.mdc-text-field__affix--prefix {
@include _prefix($query: $query);

.mdc-text-field--end-aligned & {
@include _prefix-end-aligned($query: $query);
}
}

.mdc-text-field__affix--suffix {
@include _suffix($query: $query);

.mdc-text-field--end-aligned & {
@include _suffix-end-aligned($query: $query);
}
}

// stylelint-disable-next-line plugin/selector-bem-pattern
// Move label when text-field gets auto-filled in Chrome.
.mdc-text-field__input:-webkit-autofill + .mdc-floating-label {
Expand Down Expand Up @@ -698,6 +718,46 @@
}
}

///
/// Customizes the color of the prefix text for an enabled text field.
/// @param {Color} $color - The desired prefix text color.
///
@mixin prefix-color($color, $query: feature-targeting-functions.all()) {
@include if-enabled_ {
@include _prefix-color($color, $query: $query);
}
}

///
/// Customizes the color of the prefix text for a disabled text field.
/// @param {Color} $color - The desired prefix text color.
///
@mixin disabled-prefix-color($color, $query: feature-targeting-functions.all()) {
@include if-disabled_ {
@include _prefix-color($color, $query: $query);
}
}

///
/// Customizes the color of the suffix text for an enabled text field.
/// @param {Color} $color - The desired suffix text color.
///
@mixin suffix-color($color, $query: feature-targeting-functions.all()) {
@include if-enabled_ {
@include _suffix-color($color, $query: $query);
}
}

///
/// Customizes the color of the suffix text for a disabled text field.
/// @param {Color} $color - The desired suffix text color.
///
@mixin disabled-suffix-color($color, $query: feature-targeting-functions.all()) {
@include if-disabled_ {
@include _suffix-color($color, $query: $query);
}
}

// Private mixins

// Baseline
Expand Down Expand Up @@ -804,6 +864,8 @@
@include icon-mixins.leading-icon-color_(variables.$disabled-icon, $query: $query);
@include icon-mixins.trailing-icon-color_(variables.$disabled-icon, $query: $query);
@include fill-color_(variables.$disabled-background, $query: $query);
@include _prefix-color(variables.$disabled-affix-color, $query: $query);
@include _suffix-color(variables.$disabled-affix-color, $query: $query);

@media screen and (-ms-high-contrast: active) {
@include bottom-line-color_(GrayText, $query: $query);
Expand Down Expand Up @@ -1194,6 +1256,74 @@
}
}

// Text, Prefix and Suffix

// Common styles for the text of the text field, including the prefix, suffix,
// and input.
@mixin _text($query: feature-targeting-functions.all()) {
$feat-animation: feature-targeting-functions.create-target($query, animation);
$feat-structure: feature-targeting-functions.create-target($query, structure);

// Exclude setting line-height to keep caret (text cursor) same height as the input text in iOS browser.
@include typography-mixins.typography(subtitle1, $exclude-props: (line-height), $query: $query);
@include feature-targeting-mixins.targets($feat-structure) {
height: variables.$input-height;
}

@include feature-targeting-mixins.targets($feat-animation) {
transition: functions.transition(opacity);
}
}

@mixin _affix($query: feature-targeting-functions.all()) {
$feat-structure: feature-targeting-functions.create-target($query, structure);

@include _text($query: $query);
@include feature-targeting-mixins.targets($feat-structure) {
opacity: 0;
}
}

@mixin _affix-visible($query: feature-targeting-functions.all()) {
$feat-structure: feature-targeting-functions.create-target($query, structure);

@include feature-targeting-mixins.targets($feat-structure) {
opacity: 1;
}
}

@mixin _prefix($query: feature-targeting-functions.all()) {
$feat-structure: feature-targeting-functions.create-target($query, structure);

@include feature-targeting-mixins.targets($feat-structure) {
@include rtl-mixins.reflexive-box(padding, right, variables.$affix-aligned-padding);
}
}

@mixin _prefix-end-aligned($query: feature-targeting-functions.all()) {
$feat-structure: feature-targeting-functions.create-target($query, structure);

@include feature-targeting-mixins.targets($feat-structure) {
@include rtl-mixins.reflexive-box(padding, right, variables.$affix-padding);
}
}

@mixin _suffix($query: feature-targeting-functions.all()) {
$feat-structure: feature-targeting-functions.create-target($query, structure);

@include feature-targeting-mixins.targets($feat-structure) {
@include rtl-mixins.reflexive-box(padding, left, variables.$affix-padding);
}
}

@mixin _suffix-end-aligned($query: feature-targeting-functions.all()) {
$feat-structure: feature-targeting-functions.create-target($query, structure);

@include feature-targeting-mixins.targets($feat-structure) {
@include rtl-mixins.reflexive-box(padding, left, variables.$affix-aligned-padding);
}
}

// End aligned
@mixin end-aligned_($query: feature-targeting-functions.all()) {
$feat-structure: feature-targeting-functions.create-target($query, structure);
Expand Down Expand Up @@ -1275,6 +1405,26 @@
}
}

@mixin _prefix-color($color, $query: feature-targeting-functions.all()) {
$feat-color: feature-targeting-functions.create-target($query, color);

@include feature-targeting-mixins.targets($feat-color) {
.mdc-text-field__affix--prefix {
@include theme-mixins.prop(color, $color);
}
}
}

@mixin _suffix-color($color, $query: feature-targeting-functions.all()) {
$feat-color: feature-targeting-functions.create-target($query, color);

@include feature-targeting-mixins.targets($feat-color) {
.mdc-text-field__affix--suffix {
@include theme-mixins.prop(color, $color);
}
}
}

// Selectors

@mixin placeholder-selector_ {
Expand Down
5 changes: 5 additions & 0 deletions packages/mdc-textfield/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ $helper-text-color: rgba(theme-variables.prop-value(on-surface), .6) !default;
$icon-color: rgba(theme-variables.prop-value(on-surface), .54) !default;
$focused-label-color: rgba(theme-variables.prop-value(primary), .87) !default;
$placeholder-ink-color: rgba(theme-variables.prop-value(on-surface), .54) !default;
$affix-color: rgba(theme-variables.prop-value(on-surface), .6) !default;

$disabled-label-color: rgba(theme-variables.prop-value(on-surface), .38) !default;
$disabled-ink-color: rgba(theme-variables.prop-value(on-surface), .38) !default;
$disabled-placeholder-ink-color: rgba(theme-variables.prop-value(on-surface), .38) !default;
$disabled-helper-text-color: rgba(theme-variables.prop-value(on-surface), .38) !default;
$disabled-affix-color: rgba(theme-variables.prop-value(on-surface), .38) !default;

$background: color.mix(theme-variables.prop-value(on-surface), theme-variables.prop-value(surface), 4%) !default;
$disabled-background: color.mix(theme-variables.prop-value(on-surface), theme-variables.prop-value(surface), 2%) !default;
Expand Down Expand Up @@ -98,3 +100,6 @@ $textarea-input-margin-top: 8px !default;
$textarea-input-handle-margin: 1px !default;
$textarea-input-padding-bottom: 16px !default;
// Note that the scale factor is an eyeballed approximation of what's shown in the mocks.

$affix-padding: 12px !default;
$affix-aligned-padding: 2px !default;
38 changes: 38 additions & 0 deletions packages/mdc-textfield/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export class MDCTextField extends MDCComponent<MDCTextFieldFoundation> implement
private lineRipple_!: MDCLineRipple | null; // assigned in initialize()
private outline_!: MDCNotchedOutline | null; // assigned in initialize()
private trailingIcon_!: MDCTextFieldIcon | null; // assigned in initialize()
private prefix_!: Element | null; // assigned in initialize()
private suffix_!: Element | null; // assigned in initialize()

initialize(
rippleFactory: MDCRippleFactory = (el, foundation) => new MDCRipple(el, foundation),
Expand Down Expand Up @@ -111,6 +113,10 @@ export class MDCTextField extends MDCComponent<MDCTextFieldFoundation> implement
this.root_.querySelector(strings.TRAILING_ICON_SELECTOR);
this.trailingIcon_ = trailingIconEl ? iconFactory(trailingIconEl) : null;

// Prefix and Suffix
this.prefix_ = this.root_.querySelector(strings.PREFIX_SELECTOR);
this.suffix_ = this.root_.querySelector(strings.SUFFIX_SELECTOR);

this.ripple = this.createRipple_(rippleFactory);
}

Expand Down Expand Up @@ -308,6 +314,38 @@ export class MDCTextField extends MDCComponent<MDCTextFieldFoundation> implement
this.foundation_.setUseNativeValidation(useNativeValidation);
}

/**
* Gets the text content of the prefix, or null if it does not exist.
*/
get prefixText(): string | null {
return this.prefix_ ? this.prefix_.textContent : null;
}

/**
* Sets the text content of the prefix, if it exists.
*/
set prefixText(prefixText: string | null) {
if (this.prefix_) {
this.prefix_.textContent = prefixText;
}
}

/**
* Gets the text content of the suffix, or null if it does not exist.
*/
get suffixText(): string | null {
return this.suffix_ ? this.suffix_.textContent : null;
}

/**
* Sets the text content of the suffix, if it exists.
*/
set suffixText(suffixText: string | null) {
if (this.suffix_) {
this.suffix_.textContent = suffixText;
}
}

/**
* Focuses the input element.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/mdc-textfield/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const strings = {
LEADING_ICON_SELECTOR: '.mdc-text-field__icon--leading',
LINE_RIPPLE_SELECTOR: '.mdc-line-ripple',
OUTLINE_SELECTOR: '.mdc-notched-outline',
PREFIX_SELECTOR: '.mdc-text-field__affix--prefix',
SUFFIX_SELECTOR: '.mdc-text-field__affix--suffix',
TRAILING_ICON_SELECTOR: '.mdc-text-field__icon--trailing'
};

Expand Down
Loading

0 comments on commit 717058b

Please sign in to comment.