From bd5036296e0970bb7a027fd0d7cb3ec2582aa231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Murat=20=C3=87orlu?= <127687+muratcorlu@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:28:53 +0100 Subject: [PATCH] fix(input): disabled style and small size fixes (#389) * fix(icon): safari overflow issue fixed * fix(input): various visual fixes for input * disabled style fixed * small size added * vertical alignments fixed * internal component structure refactored for creating extra flexibility for upcoming features (like trailing/leading text), better reliability and maintainability * fix(input): placeholder color fix * ci: extra padding has been added for chromatic * fix(input): label max width with icon * ci: clean forgotten style * fix(input): positioning issues are fixed * fix(input): variable name fix * docs(input): small size added for storybook canvas * docs(input): label fixed attribute in canvas fix --- .storybook/preview.js | 27 ++- src/components/icon/bl-icon.css | 13 +- src/components/input/bl-input.css | 212 +++++++++++++--------- src/components/input/bl-input.stories.mdx | 69 +++++-- src/components/input/bl-input.test.ts | 27 ++- src/components/input/bl-input.ts | 61 ++++--- src/themes/default.css | 2 +- 7 files changed, 270 insertions(+), 141 deletions(-) diff --git a/.storybook/preview.js b/.storybook/preview.js index 3704b646..753a7503 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -30,8 +30,33 @@ const withNoAnimationOnChromaticLayout = makeDecorator({ } }); + +const extraPaddingForChromatic = makeDecorator({ + name: 'extraPaddingForChromatic', + parameterName: 'extraPaddingForChromatic', + skipIfNoParametersOrOptions: true, + wrapper: (getStory, context) => { + const story = getStory(context); + const decoratedStory = html` + ${isChromatic() ? html`` : html``} + +
+ ${ story } +
+ `; + + // return the modified story + return decoratedStory; + } +}); + export const decorators = [ - withNoAnimationOnChromaticLayout + withNoAnimationOnChromaticLayout, + extraPaddingForChromatic ] export const parameters = { diff --git a/src/components/icon/bl-icon.css b/src/components/icon/bl-icon.css index ce7e7353..f504dfd5 100644 --- a/src/components/icon/bl-icon.css +++ b/src/components/icon/bl-icon.css @@ -1,14 +1,19 @@ :host { display: inline-block; - position: relative; +} + +:host div { + display: flex; + align-items: stretch; width: 1em; height: 1em; min-width: 1em; min-height: 1em; + overflow: hidden; + transform: translateZ(0); } -:host div, :host svg { - width: 100%; - height: 100%; + width: 1em; + height: 1em; } diff --git a/src/components/input/bl-input.css b/src/components/input/bl-input.css index 01261db3..cec0abc4 100644 --- a/src/components/input/bl-input.css +++ b/src/components/input/bl-input.css @@ -2,111 +2,162 @@ display: inline-block; width: 200px; position: relative; +} + +.wrapper { + --border-color: var(--bl-color-border); + --icon-color: var(--bl-color-content-tertiary); + --text-color: var(--bl-color-content-primary); + --height: var(--bl-size-2xl); + --input-font: var(--bl-font-body-text-2); + --line-height: var(--bl-font-body-text-2-line-height); + --icon-size: var(--line-height); + --padding-vertical: calc((var(--height) - var(--line-height)) / 2); + --padding-horizontal: var(--bl-size-xs); - --bl-input-padding-vertical: var(--bl-size-2xs); - --bl-input-padding-horizontal: var(--bl-size-xs); - --bl-input-border-color: var(--bl-color-border); - --bl-input-icon-color: var(--bl-color-content-tertiary); - --bl-input-text-color: var(--bl-color-content-primary); - --bl-input-height: var(--bl-size-2xl); + display: flex; + flex-direction: column; + position: relative; + gap: var(--bl-size-3xs); } -input { - outline: none; +.wrapper:focus-within { + --border-color: var(--bl-color-primary); + --icon-color: var(--bl-color-primary); +} + +.wrapper.dirty.invalid { + --border-color: var(--bl-color-danger); + --icon-color: var(--bl-color-danger); +} + +:host([size='large']) .wrapper { + --height: var(--bl-size-3xl); + --padding-vertical: var(--bl-size-xs); + --padding-horizontal: var(--bl-size-m); +} + +:host([size='small']) .wrapper { + --height: var(--bl-size-xl); + --input-font: var(--bl-font-body-text-3); + --padding-vertical: var(--bl-size-3xs); + --icon-size: var(--bl-font-body-text-3-line-height); +} + +label { + position: absolute; + top: var(--padding-vertical); + left: var(--padding-horizontal); + right: var(--padding-horizontal); + max-width: max-content; + transition: all ease-in 0.2s; + pointer-events: none; + font: var(--input-font); + color: var(--bl-color-content-tertiary); + padding: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.input-wrapper { + --border-size: 1px; + + display: flex; box-sizing: border-box; - height: var(--bl-input-height); - border: solid 1px var(--bl-input-border-color); - width: 100%; - font: var(--bl-font-title-3-regular); - padding: 0 var(--bl-input-padding-horizontal); - margin: 0; + gap: var(--padding-vertical); + height: var(--height); + border: solid var(--border-size) var(--border-color); + padding: 0 calc(var(--padding-horizontal) - var(--border-size)); border-radius: 4px; - color: var(--bl-input-text-color); } -bl-icon { - position: absolute; - top: var(--bl-input-padding-vertical); - right: var(--bl-input-padding-horizontal); - font-size: var(--bl-size-m); - z-index: 1; - color: var(--bl-input-icon-color); +input { + width: 100%; + align-self: stretch; + outline: 0; + border: 0; + padding: 0; + font: var(--input-font); + color: var(--text-color); + -webkit-text-fill-color: var(--text-color); + background-color: transparent; } -input:focus { - --bl-input-border-color: var(--bl-color-primary); +.icon { + flex-basis: var(--icon-size); + align-self: center; + height: var(--icon-size); } -:host([label-fixed]) bl-icon { - top: calc(var(--bl-input-padding-vertical) + var(--bl-size-m)); +.wrapper:not(.has-icon) .icon { + display: none; +} + +.hint { + display: none; + font: var(--bl-font-body-text-3); + padding: 0 var(--padding-horizontal); +} + +.hint p { + padding: 0; + margin: 0; } -input:focus ~ bl-icon { - --bl-input-icon-color: var(--bl-color-primary); +bl-icon { + font-size: var(--icon-size); + color: var(--icon-color); + height: var(--icon-size); } -:host ::placeholder { +::placeholder { color: var(--bl-color-content-tertiary); + -webkit-text-fill-color: var(--bl-color-content-tertiary); } :host([label]) ::placeholder { color: transparent; + -webkit-text-fill-color: transparent; transition: color ease-out 0.4s; } -:host input:focus::placeholder, -:host([label-fixed]) ::placeholder { +:host([label-fixed]) ::placeholder, +:host .wrapper:focus-within ::placeholder { color: var(--bl-color-content-tertiary); + -webkit-text-fill-color: var(--bl-color-content-tertiary); } -input:disabled { - background-color: var(--bl-color-primary-background); - - --bl-input-text-color: var(--bl-color-content-tertiary); -} +:host([disabled]) .input-wrapper { + cursor: not-allowed; + background-color: var(--bl-color-secondary-background); -input.dirty:invalid { - --bl-input-border-color: var(--bl-color-danger); + --text-color: var(--bl-color-content-passive); } -input.has-icon { - padding-right: calc(var(--bl-size-xs) * 2 + var(--bl-size-m)); -} - -.error-icon, -.invalid-text { - display: none; +input:disabled { + cursor: not-allowed; } -label { - position: absolute; - top: var(--bl-input-padding-vertical); - left: var(--bl-input-padding-horizontal); - transition: all ease-in 0.2s; - pointer-events: none; - font: var(--bl-font-title-3-regular); - color: var(--bl-color-content-tertiary); - padding: 0; -} +:where(.wrapper:focus-within, .wrapper.has-value) label { + --label-padding: var(--bl-size-3xs); -:where(input:focus, input.has-value) ~ label { top: 0; - left: var(--bl-size-2xs); + left: calc(var(--padding-horizontal) - var(--label-padding)); transform: translateY(-50%); font: var(--bl-font-caption); color: var(--bl-color-content-secondary); - padding: 0 var(--bl-size-3xs); + padding: 0 var(--label-padding); background-color: var(--bl-color-primary-background); pointer-events: initial; } -:host([label-fixed]) { - padding-top: var(--bl-size-m); +:where(.has-icon:not(:focus-within), .has-icon:not(.has-value)) label { + right: calc(var(--padding-horizontal) + var(--icon-size) + var(--padding-vertical)); } :host([label-fixed]) label { - top: 0; - left: 0; + position: static; transition: none; transform: none; pointer-events: initial; @@ -115,18 +166,14 @@ label { padding: 0; } -.dirty:invalid ~ label { - color: var(--bl-color-danger); +.error-icon, +.invalid-text { + display: none; } +.dirty.invalid label, .invalid-text, -.help-text { - font: var(--bl-font-title-4-regular); - padding: var(--bl-size-3xs) var(--bl-input-padding-horizontal); - margin: 0; -} - -.invalid-text { +.error-icon { color: var(--bl-color-danger); } @@ -134,32 +181,23 @@ label { color: var(--bl-color-content-secondary); } -.error-icon { - color: var(--bl-color-danger); +:host([help-text]) .hint, +.dirty.invalid .hint { + display: block; } -.dirty:invalid ~ .invalid-text { +.dirty.invalid .invalid-text { display: block; } -.dirty:invalid ~ .help-text { +.dirty.invalid .help-text { display: none; } -.dirty:invalid ~ .error-icon { +.dirty.invalid .error-icon { display: inline-block; } -.dirty:invalid ~ .custom-icon ~ .error-icon { +.dirty.invalid .custom-icon ~ .error-icon { display: none; } - -.dirty:invalid ~ .custom-icon { - --bl-input-icon-color: var(--bl-color-danger); -} - -:host([size='large']) { - --bl-input-height: var(--bl-size-3xl); - --bl-input-padding-vertical: var(--bl-size-xs); - --bl-input-padding-horizontal: var(--bl-size-m); -} diff --git a/src/components/input/bl-input.stories.mdx b/src/components/input/bl-input.stories.mdx index 5400be2d..ce1be704 100644 --- a/src/components/input/bl-input.stories.mdx +++ b/src/components/input/bl-input.stories.mdx @@ -10,6 +10,9 @@ import { html`` -export const LabelStylesTemplate = args => html` - ${SingleInputTemplate({ labelFixed: true, ...args })} - ${SingleInputTemplate({ labelFixed: false, ...args })} - ${BlButton()} +export const SizeVariantsTemplate = args => html` +${SingleInputTemplate({ size: 'large', ...args })} +${SingleInputTemplate({ size: 'medium', ...args })} +${SingleInputTemplate({ size: 'small', ...args })} ` # Input @@ -107,8 +110,6 @@ export const LabelStylesTemplate = args => html` Input component is the component for taking text input from user. -Inline styles in examples are only for **demo purposes**. Use regular CSS classes or tag selectors to set styles. - ## Basic Usage Currently, input component supports `text` and `number` types, which default is `text`. @@ -148,6 +149,20 @@ If you want to use always it on top of the input, then you can use `label-fixed` +Input component will cut-out long labels those doesn't width of input, with ellipsis char. + + + + {SingleInputTemplate.bind({})} + + + {SingleInputTemplate.bind({})} + + + {SingleInputTemplate.bind({})} + + + ## Input Help Text You can give extra information to user with `help-text` attribute. @@ -201,11 +216,43 @@ Validation error messages are used from default browser error messages by defaul ## Input Sizes -Inputs have 2 size options: `medium` and `large`. `medium` size is default and if you want to show a large input you can set `size` attribute to `large`. +Inputs have 3 size options: `large`, `medium` and `small`. `medium` size is default and if you want to show a large or small input you can set `size` attribute. - + {SizeVariantsTemplate.bind({})} + + + {SizeVariantsTemplate.bind({})} + + + +## Disabled Input + +Input can be set as disabled by adding `disabled` attribute. + + + + {SingleInputTemplate.bind({})} + + + {SingleInputTemplate.bind({})} + + + {SingleInputTemplate.bind({})} + + {SingleInputTemplate.bind({})} diff --git a/src/components/input/bl-input.test.ts b/src/components/input/bl-input.test.ts index 034cdc16..83b74a91 100644 --- a/src/components/input/bl-input.test.ts +++ b/src/components/input/bl-input.test.ts @@ -12,16 +12,23 @@ describe('bl-input', () => { assert.shadowDom.equal( el, ` - - - +
+
+ +
+ + +
+
+
+
` ); }); diff --git a/src/components/input/bl-input.ts b/src/components/input/bl-input.ts index 3f643ccd..db10859c 100644 --- a/src/components/input/bl-input.ts +++ b/src/components/input/bl-input.ts @@ -121,7 +121,7 @@ export default class BlInput extends FormControlMixin(LitElement) { /** * Adds help text */ - @property({ type: String, attribute: 'help-text' }) + @property({ type: String, attribute: 'help-text', reflect: true }) helpText?: string; /** @@ -204,41 +204,48 @@ export default class BlInput extends FormControlMixin(LitElement) { ? html`

${this.validationMessage}

` : ``; const helpMessage = this.helpText ? html`

${this.helpText}

` : ``; + const icon = this.icon - ? html` ` + ? html`` : ''; const label = this.label ? html`` : ''; const classes = { - 'dirty': this.dirty, + wrapper: true, + dirty: this.dirty, + invalid: !this.checkValidity(), 'has-icon': this.icon || (this.dirty && !this.checkValidity()), 'has-value': this.value !== null && this.value !== '', }; - return html` - - ${label} ${icon} - - ${invalidMessage} ${helpMessage} - `; + return html`
+ ${label} +
+ +
${icon}
+
+
+ ${invalidMessage} + ${helpMessage} +
+
`; } } diff --git a/src/themes/default.css b/src/themes/default.css index d7703173..2c9bd24b 100644 --- a/src/themes/default.css +++ b/src/themes/default.css @@ -173,7 +173,7 @@ --bl-font-body-text-3: var(--bl-font-weight-regular) var(--bl-font-body-text-3-size) var(--bl-font-family); /* Font Style: Caption */ - --bl-font-caption-font-size: var(--bl-font-size-s); + --bl-font-caption-font-size: var(--bl-font-size-xs); --bl-font-caption-line-height: calc(var(--bl-font-caption-font-size) + var(--bl-size-4xs)); --bl-font-caption-size: var(--bl-font-caption-font-size)/var(--bl-font-caption-line-height); --bl-font-caption: var(--bl-font-weight-medium) var(--bl-font-caption-size) var(--bl-font-family);