From 5cebeb04f05c2142ff60d5ad2f3428b979de2696 Mon Sep 17 00:00:00 2001 From: Darren Cadwallader Date: Wed, 3 May 2023 12:59:15 +0200 Subject: [PATCH] - Updates stories to cover all states - Adds sass variables for the extra states on checkboxes & radios - All stories now include labels --- scss/bitstyles/base/forms/Label.js | 8 +- scss/bitstyles/base/forms/forms.stories.js | 258 ------------------ scss/bitstyles/base/forms/forms.stories.mdx | 44 +-- .../base/forms/input-checkbox.stories.js | 86 +++++- .../base/forms/input-radio.stories.js | 66 ++++- .../base/forms/input-text.stories.js | 168 +++++++----- scss/bitstyles/base/forms/label.stories.js | 3 - scss/bitstyles/base/forms/select.stories.js | 23 +- scss/bitstyles/base/forms/textarea.stories.js | 59 ++-- .../bitstyles/base/input-checkbox/_index.scss | 21 ++ .../base/input-checkbox/_settings.scss | 20 +- scss/bitstyles/base/input-radio/_index.scss | 37 ++- .../bitstyles/base/input-radio/_settings.scss | 21 ++ scss/bitstyles/settings/_focus.scss | 6 + 14 files changed, 423 insertions(+), 397 deletions(-) delete mode 100644 scss/bitstyles/base/forms/forms.stories.js create mode 100644 scss/bitstyles/settings/_focus.scss diff --git a/scss/bitstyles/base/forms/Label.js b/scss/bitstyles/base/forms/Label.js index 44aa97e46..8c87b5809 100644 --- a/scss/bitstyles/base/forms/Label.js +++ b/scss/bitstyles/base/forms/Label.js @@ -1,9 +1,15 @@ -export default ({ children, htmlFor = null, ariaInvalid = false }) => { +export default ({ + children, + htmlFor = null, + ariaInvalid = false, + classnames = [], +}) => { const label = document.createElement('label'); children.forEach((child) => { label.append(child); }); if (ariaInvalid) label.setAttribute('aria-invalid', ariaInvalid); if (htmlFor) label.setAttribute('for', htmlFor); + classnames.forEach((classname) => label.classList.add(classname)); return label; }; diff --git a/scss/bitstyles/base/forms/forms.stories.js b/scss/bitstyles/base/forms/forms.stories.js deleted file mode 100644 index 67ee9da8b..000000000 --- a/scss/bitstyles/base/forms/forms.stories.js +++ /dev/null @@ -1,258 +0,0 @@ -import Input from './Input'; -import Label from './Label'; -import Textarea from './Textarea'; -import Select from './Select'; -import { generateLabel } from '../../../../.storybook/helpers'; - -export default { - title: 'Base/Forms', - component: Input, - subcomponents: [Label, Select], - argTypes: {}, -}; - -const TextInputTemplate = (args) => - Label({ - htmlFor: args.id, - ariaInvalid: args.ariaInvalid, - children: [args.label, Input(args)], - }); - -const CheckboxTemplate = (args) => { - return Label({ - htmlFor: args.id, - ariaInvalid: args.ariaInvalid, - children: [Input(args), args.label], - }); -}; - -const SelectTemplate = (args) => { - const wrapper = document.createElement('div'); - wrapper.appendChild( - Label({ - htmlFor: args.id, - ariaInvalid: args.ariaInvalid, - children: [args.label], - }) - ); - wrapper.appendChild(Select(args)); - return wrapper; -}; - -const TextareaTemplate = (args) => { - const wrapper = document.createElement('div'); - wrapper.appendChild( - Label({ - htmlFor: args.id, - ariaInvalid: args.ariaInvalid, - children: [args.label], - }) - ); - wrapper.appendChild(Textarea(args)); - return wrapper; -}; - -// ***** Text inputs with labels ****************** // - -export const TextInput = TextInputTemplate.bind({}); -TextInput.args = { - id: 'input-text', - label: 'Delivery address', - value: generateLabel(['input', 'text']), - placeholder: generateLabel(['input', 'text', 'placeholder']), -}; -TextInput.parameters = { - zeplinLink: [ - { - name: 'base', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29350fae8b712a63460', - }, - { - name: 'hover', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29350fae8b712a63460', - }, - { - name: 'active', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29d53bff5b7626b7444', - }, - ], -}; - -export const TextInputInvalid = TextInputTemplate.bind({}); -TextInputInvalid.args = { - id: 'input-text', - label: 'Telephone number', - value: generateLabel(['input', 'text']), - placeholder: generateLabel(['input', 'text', 'placeholder']), - ariaInvalid: true, -}; -TextInputInvalid.parameters = { - zeplinLink: [ - { - name: 'base', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29350fae8b712a63460', - }, - { - name: 'hover', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29350fae8b712a63460', - }, - { - name: 'active', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29d53bff5b7626b7444', - }, - ], -}; - -export const TextInputInvalidWith = TextInputTemplate.bind({}); -TextInputInvalid.args = { - id: 'input-text', - label: 'Telephone number', - value: generateLabel(['input', 'text']), - placeholder: generateLabel(['input', 'text', 'placeholder']), - ariaInvalid: true, -}; -TextInputInvalid.parameters = { - zeplinLink: [ - { - name: 'base', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29350fae8b712a63460', - }, - { - name: 'hover', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29350fae8b712a63460', - }, - { - name: 'active', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29d53bff5b7626b7444', - }, - ], -}; - -export const TextInputDisabled = TextInputTemplate.bind({}); -TextInputDisabled.args = { - id: 'input-text', - label: 'Full name', - value: generateLabel(['input', 'text']), - placeholder: generateLabel(['input', 'text', 'placeholder']), - disabled: true, -}; -TextInputDisabled.parameters = { - zeplinLink: [ - { - name: 'base', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29350fae8b712a63460', - }, - { - name: 'hover', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29350fae8b712a63460', - }, - { - name: 'active', - link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29d53bff5b7626b7444', - }, - ], -}; - -// ***** Radio inputs with labels ****************** // - -export const RadioInput = CheckboxTemplate.bind({}); -RadioInput.args = { - id: 'input-radio', - type: 'radio', - label: 'X-Large', -}; - -export const RadioInputChecked = CheckboxTemplate.bind({}); -RadioInputChecked.args = { - id: 'input-radio', - type: 'radio', - label: 'Large', - checked: true, -}; - -export const RadioInputInvalid = CheckboxTemplate.bind({}); -RadioInputInvalid.args = { - id: 'input-radio', - type: 'radio', - label: 'Medium', - ariaInvalid: true, -}; - -export const RadioInputDisabled = CheckboxTemplate.bind({}); -RadioInputDisabled.args = { - id: 'input-radio', - type: 'radio', - label: 'Small', - disabled: true, -}; - -// ***** Radio inputs with labels ****************** // - -export const CheckboxInput = CheckboxTemplate.bind({}); -CheckboxInput.args = { - id: 'input-checkbox', - type: 'checkbox', - label: 'X-Large', -}; - -export const CheckboxInputChecked = CheckboxTemplate.bind({}); -CheckboxInputChecked.args = { - id: 'input-checkbox', - type: 'checkbox', - label: 'Large', - checked: true, -}; - -export const CheckboxInputInvalid = CheckboxTemplate.bind({}); -CheckboxInputInvalid.args = { - id: 'input-checkbox', - type: 'checkbox', - label: 'Medium', - ariaInvalid: true, -}; - -export const CheckboxInputDisabled = CheckboxTemplate.bind({}); -CheckboxInputDisabled.args = { - id: 'input-checkbox', - type: 'checkbox', - label: 'Small', - disabled: true, -}; - -// ***** Selects with labels ****************** // - -export const SelectBase = SelectTemplate.bind({}); -SelectBase.args = { - label: 'Delivery method', -}; - -export const SelectInvalid = SelectTemplate.bind({}); -SelectInvalid.args = { - label: 'Delivery method', - ariaInvalid: true, -}; - -export const SelectDisabled = SelectTemplate.bind({}); -SelectDisabled.args = { - label: 'Delivery method', - disabled: true, -}; - -// ***** Selects with labels ****************** // - -export const TextareaBase = TextareaTemplate.bind({}); -TextareaBase.args = { - label: 'Extra comments', -}; - -export const TextareaInvalid = TextareaTemplate.bind({}); -TextareaInvalid.args = { - label: 'Extra comments', - ariaInvalid: true, -}; - -export const TextareaDisabled = TextareaTemplate.bind({}); -TextareaDisabled.args = { - label: 'Extra comments', - disabled: true, -}; diff --git a/scss/bitstyles/base/forms/forms.stories.mdx b/scss/bitstyles/base/forms/forms.stories.mdx index 08895a7fb..21a9e58f8 100644 --- a/scss/bitstyles/base/forms/forms.stories.mdx +++ b/scss/bitstyles/base/forms/forms.stories.mdx @@ -15,39 +15,49 @@ The exception is for radios & checkboxes (see below), which should always be a c This includes all standard `type="text"` inputs, plus all the text-based inputs such as `email`, `password`, `tel` — take a look at the other stories in the forms section for examples of the other input types. - - - + + + + + + ## Textareas - - - + + + + + + ## Checkboxes and Radios - - - - + + + + + + - - - - + + + + + + ## Selects - - - + + + diff --git a/scss/bitstyles/base/forms/input-checkbox.stories.js b/scss/bitstyles/base/forms/input-checkbox.stories.js index f9b8acdcb..6278b128d 100644 --- a/scss/bitstyles/base/forms/input-checkbox.stories.js +++ b/scss/bitstyles/base/forms/input-checkbox.stories.js @@ -1,26 +1,100 @@ import Input from './Input'; +import Label from './Label'; export default { title: 'Base/Forms/Input checkbox', component: Input, + subcomponents: [Label], argTypes: {}, }; -const Template = (args) => Input(args); +const Template = (args) => { + return Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + children: [Input(args), args.label], + classname: 'u-items-baseline', + }); +}; // ***** Text inputs with values ****************** // export const Base = Template.bind({}); -Base.args = { type: 'checkbox' }; +Base.args = { type: 'checkbox', label: 'Unchecked' }; +Base.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc96111d7d524003d54ff', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc97298cca5236f0332ce', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc97028fa4823ffce07c8', + }, + ], +}; export const Invalid = Template.bind({}); -Invalid.args = { type: 'checkbox', ariaInvalid: true }; +Invalid.args = { + type: 'checkbox', + ariaInvalid: true, + label: 'Unchecked invalid', +}; export const Disabled = Template.bind({}); -Disabled.args = { type: 'checkbox', disabled: true }; +Disabled.args = { + type: 'checkbox', + disabled: true, + label: 'Unchecked disabled', +}; +Disabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc971830548239007431f', +}; export const Checked = Template.bind({}); -Checked.args = { type: 'checkbox', checked: true }; +Checked.args = { + type: 'checkbox', + checked: true, + label: 'Checked', +}; +Checked.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc96362f9d023b1e5d965', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc964d04e11239e10ece0', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc96842426023b99c98de', + }, + ], +}; + +export const CheckedInvalid = Template.bind({}); +CheckedInvalid.args = { + type: 'checkbox', + checked: true, + ariaInvalid: true, + label: 'Checked invalid', +}; export const CheckedDisabled = Template.bind({}); -CheckedDisabled.args = { type: 'checkbox', checked: true, disabled: true }; +CheckedDisabled.args = { + type: 'checkbox', + checked: true, + disabled: true, + label: 'Checked disabled', +}; +CheckedDisabled.parameters = { + zeplinLink: + 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc96c28fa4823ffce078c', +}; diff --git a/scss/bitstyles/base/forms/input-radio.stories.js b/scss/bitstyles/base/forms/input-radio.stories.js index 33c1ef4f3..f9d83f6a1 100644 --- a/scss/bitstyles/base/forms/input-radio.stories.js +++ b/scss/bitstyles/base/forms/input-radio.stories.js @@ -1,26 +1,80 @@ import Input from './Input'; +import Label from './Label'; export default { title: 'Base/Forms/Input radio', component: Input, + subcomponents: [Label], argTypes: {}, }; -const Template = (args) => Input(args); +const Template = (args) => { + return Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + children: [Input(args), args.label], + classname: 'u-items-baseline', + }); +}; // ***** Text inputs with values ****************** // export const Base = Template.bind({}); -Base.args = { type: 'radio' }; +Base.args = { type: 'radio', label: 'Unchecked' }; +Base.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2b5d50be32376c68fcd', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2b95a65082385f940c1', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2bedf599923e9f4e22d', + }, + ], +}; export const Invalid = Template.bind({}); -Invalid.args = { type: 'radio', ariaInvalid: true }; +Invalid.args = { type: 'radio', ariaInvalid: true, label: 'Unchecked invalid' }; export const Disabled = Template.bind({}); -Disabled.args = { type: 'radio', disabled: true }; +Disabled.args = { type: 'radio', disabled: true, label: 'Unchecked disabled' }; export const Checked = Template.bind({}); -Checked.args = { type: 'radio', checked: true }; +Checked.args = { type: 'radio', checked: true, label: 'Checked' }; +Checked.parameters = { + zeplinLink: [ + { + name: 'base', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2bd35ff5623d67134f9', + }, + { + name: 'hover', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2b76d8d3923c74e7dc1', + }, + { + name: 'active', + link: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=643fc2b628fa4823ffcd6e3f', + }, + ], +}; + +export const CheckedInvalid = Template.bind({}); +CheckedInvalid.args = { + type: 'radio', + checked: true, + ariaInvalid: true, + label: 'Checked invalid', +}; export const CheckedDisabled = Template.bind({}); -CheckedDisabled.args = { type: 'radio', checked: true, disabled: true }; +CheckedDisabled.args = { + type: 'radio', + checked: true, + disabled: true, + label: 'Checked disabled', +}; diff --git a/scss/bitstyles/base/forms/input-text.stories.js b/scss/bitstyles/base/forms/input-text.stories.js index 6b82551d1..cab379ed9 100644 --- a/scss/bitstyles/base/forms/input-text.stories.js +++ b/scss/bitstyles/base/forms/input-text.stories.js @@ -1,22 +1,35 @@ import Input from './Input'; -import { generateLabel } from '../../../../.storybook/helpers'; +import Label from './Label'; export default { title: 'Base/Forms/Input text', component: Input, + subcomponents: [Label], argTypes: {}, }; -const Template = (args) => Input(args); +const Template = (args) => { + const wrapper = new DocumentFragment(); + const label = Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + children: [args.label], + }); + const input = Input(args); + wrapper.append(label); + wrapper.append(input); + return wrapper; +}; // ***** Text inputs with values ****************** // -export const Text = Template.bind({}); -Text.args = { - value: generateLabel(['input', 'text']), - placeholder: generateLabel(['input', 'text', 'placeholder']), +export const TextWithValue = Template.bind({}); +TextWithValue.args = { + value: 'Input text with value', + placeholder: 'Input text placeholder', + label: 'Input text with value', }; -Text.parameters = { +TextWithValue.parameters = { zeplinLink: [ { name: 'base', @@ -33,24 +46,26 @@ Text.parameters = { ], }; -export const TextInvalid = Template.bind({}); -TextInvalid.args = { - value: generateLabel(['input', 'text', 'invalid']), - placeholder: generateLabel(['input', 'text', 'invalid', 'placeholder']), +export const TextWithValueInvalid = Template.bind({}); +TextWithValueInvalid.args = { + value: 'Input text with value invalid', + placeholder: 'Input text invalid placeholder', ariaInvalid: true, + label: 'Input text with value invalid', }; -TextInvalid.parameters = { +TextWithValueInvalid.parameters = { zeplinLink: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29861150a30c8ecffa3', }; -export const TextDisabled = Template.bind({}); -TextDisabled.args = { - value: generateLabel(['input', 'text', 'disabled']), - placeholder: generateLabel(['input', 'text', 'disabled', 'placeholder']), +export const TextWithValueDisabled = Template.bind({}); +TextWithValueDisabled.args = { + value: 'Input text with value disabled', + placeholder: 'Input text disabled placeholder', disabled: true, + label: 'Input text with value disabled', }; -TextDisabled.parameters = { +TextWithValueDisabled.parameters = { zeplinLink: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29861150a30c8ecffa3', }; @@ -60,7 +75,8 @@ TextDisabled.parameters = { export const TextEmpty = Template.bind({}); TextEmpty.args = { value: '', - placeholder: generateLabel(['input', 'text', 'empty', 'placeholder']), + placeholder: 'Input text empty', + label: 'Input text empty', }; TextEmpty.parameters = { zeplinLink: [ @@ -79,49 +95,40 @@ TextEmpty.parameters = { ], }; -export const TextInvalidEmpty = Template.bind({}); -TextInvalidEmpty.args = { +export const TextEmptyInvalid = Template.bind({}); +TextEmptyInvalid.args = { value: '', - placeholder: generateLabel([ - 'input', - 'text', - 'invalid', - 'empty', - 'placeholder', - ]), + placeholder: 'Input text empty invalid', + label: 'Input text empty invalid', ariaInvalid: true, }; -TextInvalidEmpty.parameters = { +TextEmptyInvalid.parameters = { zeplinLink: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29861150a30c8ecffa3', }; -export const TextDisabledEmpty = Template.bind({}); -TextDisabledEmpty.args = { - disabled: true, +export const TextEmptyDisabled = Template.bind({}); +TextEmptyDisabled.args = { value: '', - placeholder: generateLabel([ - 'input', - 'text', - 'disabled', - 'empty', - 'placeholder', - ]), -}; -TextDisabledEmpty.parameters = { + placeholder: 'Input text empty disabled', + label: 'Input text empty disabled', + disabled: true, +}; +TextEmptyDisabled.parameters = { zeplinLink: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29861150a30c8ecffa3', }; // ***** Email inputs with values ****************** // -export const Email = Template.bind({}); -Email.args = { +export const EmailWithValue = Template.bind({}); +EmailWithValue.args = { type: 'email', - value: generateLabel(['input', 'email']), - placeholder: generateLabel(['input', 'email', 'placeholder']), + value: 'Input@email.base', + placeholder: 'Input email placeholder', + label: 'Input email with value', }; -Email.parameters = { +EmailWithValue.parameters = { zeplinLink: [ { name: 'base', @@ -138,26 +145,28 @@ Email.parameters = { ], }; -export const EmailInvalid = Template.bind({}); -EmailInvalid.args = { +export const EmailWithValueInvalid = Template.bind({}); +EmailWithValueInvalid.args = { type: 'email', - value: generateLabel(['input', 'email', 'invalid']), - placeholder: generateLabel(['input', 'email', 'invalid', 'placeholder']), + value: 'Input@email.invalid', + placeholder: 'Input email with value invalid placeholder', + label: 'Input email with value invalid', ariaInvalid: true, }; -EmailInvalid.parameters = { +EmailWithValueInvalid.parameters = { zeplinLink: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29861150a30c8ecffa3', }; -export const EmailDisabled = Template.bind({}); -EmailDisabled.args = { +export const EmailWithValueDisabled = Template.bind({}); +EmailWithValueDisabled.args = { type: 'email', - value: generateLabel(['input', 'email', 'disabled']), - placeholder: generateLabel(['input', 'email', 'disabled', 'placeholder']), + value: 'Input@email.disabled', + placeholder: 'Input email with value disabled placeholder', + label: 'Input email with value disabled', disabled: true, }; -EmailDisabled.parameters = { +EmailWithValueDisabled.parameters = { zeplinLink: 'https://app.zeplin.io/styleguide/63079b90d0bf4a646c46c227/components?coid=6363a29861150a30c8ecffa3', }; @@ -168,7 +177,8 @@ export const EmailEmpty = Template.bind({}); EmailEmpty.args = { type: 'email', value: '', - placeholder: generateLabel(['input', 'email', 'placeholder']), + placeholder: 'Input email empty placeholder', + label: 'Input email empty', }; EmailEmpty.parameters = { zeplinLink: [ @@ -191,7 +201,8 @@ export const EmailInvalidEmpty = Template.bind({}); EmailInvalidEmpty.args = { type: 'email', value: '', - placeholder: generateLabel(['input', 'email', 'invalid', 'placeholder']), + placeholder: 'Input email empty invalid placeholder', + label: 'Input email empty invalid', ariaInvalid: true, }; EmailInvalidEmpty.parameters = { @@ -203,7 +214,8 @@ export const EmailDisabledEmpty = Template.bind({}); EmailDisabledEmpty.args = { type: 'email', value: '', - placeholder: generateLabel(['input', 'email', 'disabled', 'placeholder']), + placeholder: 'Input email empty disabled placeholder', + label: 'Input email empty disabled', disabled: true, }; EmailDisabledEmpty.parameters = { @@ -213,26 +225,29 @@ EmailDisabledEmpty.parameters = { // ***** Number inputs with values ****************** // -export const Number = Template.bind({}); -Number.args = { +export const NumberWithValue = Template.bind({}); +NumberWithValue.args = { type: 'number', value: '012345', - placeholder: generateLabel(['input', 'number', 'placeholder']), + placeholder: 'Input number placeholder', + label: 'Input number with value', }; -export const NumberInvalid = Template.bind({}); -NumberInvalid.args = { +export const NumberWithValueInvalid = Template.bind({}); +NumberWithValueInvalid.args = { type: 'number', - value: generateLabel(['input', 'email', 'invalid']), - placeholder: generateLabel(['input', 'number', 'invalid', 'placeholder']), + value: 'Input email invalid', + placeholder: 'Input number invalid placeholder', + label: 'Input number with value invalid', ariaInvalid: true, }; -export const NumberDisabled = Template.bind({}); -NumberDisabled.args = { +export const NumberWithValueDisabled = Template.bind({}); +NumberWithValueDisabled.args = { type: 'number', value: '012345', - placeholder: generateLabel(['input', 'number', 'disabled', 'placeholder']), + placeholder: 'Input number disabled placeholder', + label: 'Input number with value disabled', disabled: true, }; @@ -242,21 +257,24 @@ export const NumberEmpty = Template.bind({}); NumberEmpty.args = { type: 'number', value: '', - placeholder: generateLabel(['input', 'number', 'placeholder']), + placeholder: 'Input number placeholder', + label: 'Input number empty', }; -export const NumberInvalidEmpty = Template.bind({}); -NumberInvalidEmpty.args = { +export const NumberEmptyInvalid = Template.bind({}); +NumberEmptyInvalid.args = { type: 'number', value: '', - placeholder: generateLabel(['input', 'number', 'invalid', 'placeholder']), + placeholder: 'Input number invalid placeholder', + label: 'Input number empty invalid', ariaInvalid: true, }; -export const NumberDisabledEmpty = Template.bind({}); -NumberDisabledEmpty.args = { +export const NumberEmptyDisabled = Template.bind({}); +NumberEmptyDisabled.args = { type: 'number', value: '', - placeholder: generateLabel(['input', 'number', 'disabled', 'placeholder']), + placeholder: 'Input number disabled placeholder', + label: 'Input number empty disabled', disabled: true, }; diff --git a/scss/bitstyles/base/forms/label.stories.js b/scss/bitstyles/base/forms/label.stories.js index ea0257940..57199a32e 100644 --- a/scss/bitstyles/base/forms/label.stories.js +++ b/scss/bitstyles/base/forms/label.stories.js @@ -17,9 +17,6 @@ const Link = () => { // ***** Labels ****************** // -export const Base = Template.bind({}); -Base.args = { children: ['Simple text label'] }; - export const Invalid = Template.bind({}); Invalid.args = { children: ['Simple text label for an invalid input'], diff --git a/scss/bitstyles/base/forms/select.stories.js b/scss/bitstyles/base/forms/select.stories.js index 7a4b03455..e2b91cc32 100644 --- a/scss/bitstyles/base/forms/select.stories.js +++ b/scss/bitstyles/base/forms/select.stories.js @@ -1,3 +1,4 @@ +import Label from './Label'; import Select from './Select'; import { generateLabel } from '../../../../.storybook/helpers'; @@ -7,14 +8,24 @@ export default { argTypes: {}, }; -const Template = (args) => Select(args); +const Template = (args) => { + const wrapper = new DocumentFragment(); + wrapper.append( + Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + children: [args.label], + }) + ); + wrapper.append(Select(args)); + return wrapper; +}; // ***** Selects with values ****************** // export const Base = Template.bind({}); Base.args = { - value: generateLabel(['textarea']), - placeholder: generateLabel(['textarea', 'placeholder']), + label: 'Select', }; Base.parameters = { zeplinLink: [ @@ -39,14 +50,12 @@ Base.parameters = { export const Invalid = Template.bind({}); Invalid.args = { - value: generateLabel(['textarea']), + label: 'Select invalid', ariaInvalid: true, - placeholder: generateLabel(['textarea', 'invalid', 'placeholder']), }; export const Disabled = Template.bind({}); Disabled.args = { - value: generateLabel(['textarea']), + label: 'Select disabled', disabled: true, - placeholder: generateLabel(['textarea', 'invalid', 'placeholder']), }; diff --git a/scss/bitstyles/base/forms/textarea.stories.js b/scss/bitstyles/base/forms/textarea.stories.js index 9502bad72..397e551b1 100644 --- a/scss/bitstyles/base/forms/textarea.stories.js +++ b/scss/bitstyles/base/forms/textarea.stories.js @@ -1,5 +1,5 @@ +import Label from './Label'; import Textarea from './Textarea'; -import { generateLabel } from '../../../../.storybook/helpers'; export default { title: 'Base/Forms/Textarea', @@ -7,28 +7,42 @@ export default { argTypes: {}, }; -const Template = (args) => Textarea(args); +const Template = (args) => { + const wrapper = new DocumentFragment(); + wrapper.append( + Label({ + htmlFor: args.id, + ariaInvalid: args.ariaInvalid, + children: [args.label], + }) + ); + wrapper.append(Textarea(args)); + return wrapper; +}; // ***** Textareas with values ****************** // -export const Base = Template.bind({}); -Base.args = { - value: generateLabel(['textarea']), - placeholder: generateLabel(['textarea', 'placeholder']), +export const WithValue = Template.bind({}); +WithValue.args = { + value: 'Textarea with value', + placeholder: 'Textarea with value placeholder', + label: 'Textarea with value', }; -export const Invalid = Template.bind({}); -Invalid.args = { - value: generateLabel(['textarea']), +export const WithValueInvalid = Template.bind({}); +WithValueInvalid.args = { + value: 'Textarea', ariaInvalid: true, - placeholder: generateLabel(['textarea', 'invalid', 'placeholder']), + placeholder: 'Textarea with value invalid placeholder', + label: 'Textarea with value invalid', }; -export const Disabled = Template.bind({}); -Disabled.args = { - value: generateLabel(['textarea']), +export const WithValueDisabled = Template.bind({}); +WithValueDisabled.args = { + value: 'Textarea with value disabled', disabled: true, - placeholder: generateLabel(['textarea', 'invalid', 'placeholder']), + placeholder: 'Textarea with value disabled placeholder', + label: 'Textarea with value disabled', }; // ***** Textareas without values ****************** // @@ -36,19 +50,22 @@ Disabled.args = { export const Empty = Template.bind({}); Empty.args = { value: '', - placeholder: generateLabel(['textarea', 'empty', 'placeholder']), + placeholder: 'Textarea empty placeholder', + label: 'Textarea empty', }; -export const InvalidEmpty = Template.bind({}); -InvalidEmpty.args = { +export const EmptyInvalid = Template.bind({}); +EmptyInvalid.args = { value: '', ariaInvalid: true, - placeholder: generateLabel(['textarea', 'empty', 'placeholder']), + placeholder: 'Textarea empty invalid placeholder', + label: 'Textarea empty invalid', }; -export const DisabledEmpty = Template.bind({}); -DisabledEmpty.args = { +export const EmptyDisabled = Template.bind({}); +EmptyDisabled.args = { value: '', disabled: true, - placeholder: generateLabel(['textarea', 'empty', 'placeholder']), + placeholder: 'Textarea empty disabled placeholder', + label: 'Textarea empty disabled', }; diff --git a/scss/bitstyles/base/input-checkbox/_index.scss b/scss/bitstyles/base/input-checkbox/_index.scss index d686ce165..a0e97118d 100644 --- a/scss/bitstyles/base/input-checkbox/_index.scss +++ b/scss/bitstyles/base/input-checkbox/_index.scss @@ -1,6 +1,7 @@ @forward 'settings'; @use './settings'; @use '../../settings/animation'; +@use '../../settings/focus'; @use 'sass:math'; [type='checkbox'] { @@ -15,6 +16,11 @@ cursor: pointer; appearance: none; top: calc(settings.$gap / 2); + + &:focus-visible { + outline: focus.$outline-color solid focus.$outline-width; + outline-offset: focus.$outline-offset; + } } @supports (-webkit-appearance: none) or (-moz-appearance: none) or @@ -64,6 +70,21 @@ } } + &:checked:hover, + &:checked:focus { + border-color: settings.$border-color-checked-hover; + background-color: settings.$background-color-checked-hover; + box-shadow: settings.$box-shadow-checked-hover; + color: settings.$color-checked-hover; + } + + &:checked:active { + border-color: settings.$border-color-checked-active; + background-color: settings.$background-color-checked-active; + box-shadow: settings.$box-shadow-checked-active; + color: settings.$color-checked-active; + } + &:invalid, &[aria-invalid='true'] { border-color: settings.$border-color-invalid; diff --git a/scss/bitstyles/base/input-checkbox/_settings.scss b/scss/bitstyles/base/input-checkbox/_settings.scss index 39fe2df13..1a39eee84 100644 --- a/scss/bitstyles/base/input-checkbox/_settings.scss +++ b/scss/bitstyles/base/input-checkbox/_settings.scss @@ -15,7 +15,7 @@ $size: size.get('l1') !default; $color: palette.get('grayscale') !default; $background-color: palette.get('grayscale', 'white') !default; $border-color: palette.get('grayscale') !default; -$box-shadow: shadows.get('default') !default; +$box-shadow: none !default; // // Hover colors //////////////////////////////////////// @@ -30,7 +30,23 @@ $color-checked: palette.get('grayscale', 'white') !default; $background-color-checked: palette.get('brand-1') !default; $border-color-checked: palette.get('brand-1') !default; $background-image-checked: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='#{color.url-encode-color(#ffffff)}' d='m83.07 11.71-44.41 44.59-21.73-21.81-15.93 15.99 37.65 37.81 60.35-60.59z'/%3E%3C/svg%3E") !default; -$box-shadow-checked: none !default; +$box-shadow-checked: shadows.get('default') !default; + +// +// Checked hover colors //////////////////////////////////////// +$color-checked-hover: palette.get('grayscale', 'white') !default; +$background-color-checked-hover: palette.get('brand-1') !default; +$border-color-checked-hover: palette.get('brand-1') !default; +$background-image-checked-hover: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='#{color.url-encode-color(#ffffff)}' d='m83.07 11.71-44.41 44.59-21.73-21.81-15.93 15.99 37.65 37.81 60.35-60.59z'/%3E%3C/svg%3E") !default; +$box-shadow-checked-hover: none !default; + +// +// Checked active colors //////////////////////////////////////// +$color-checked-active: palette.get('grayscale', 'white') !default; +$background-color-checked-active: palette.get('brand-1') !default; +$border-color-checked-active: palette.get('brand-1') !default; +$background-image-checked-active: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='#{color.url-encode-color(#ffffff)}' d='m83.07 11.71-44.41 44.59-21.73-21.81-15.93 15.99 37.65 37.81 60.35-60.59z'/%3E%3C/svg%3E") !default; +$box-shadow-checked-active: none !default; // // Invalid colors //////////////////////////////////////// diff --git a/scss/bitstyles/base/input-radio/_index.scss b/scss/bitstyles/base/input-radio/_index.scss index b543e7004..3f2dbd4dd 100644 --- a/scss/bitstyles/base/input-radio/_index.scss +++ b/scss/bitstyles/base/input-radio/_index.scss @@ -1,6 +1,7 @@ @forward 'settings'; @use './settings'; @use '../../settings/animation'; +@use '../../settings/focus'; [type='radio'] { display: inline-block; @@ -14,6 +15,11 @@ cursor: pointer; appearance: none; top: calc(settings.$gap / 2); + + &:focus-visible { + outline: focus.$outline-color solid focus.$outline-width; + outline-offset: focus.$outline-offset; + } } @supports (-webkit-appearance: none) or (-moz-appearance: none) or @@ -42,12 +48,18 @@ &:hover, &:focus { border-color: settings.$border-color-hover; - outline: none; background-color: settings.$background-color-hover; box-shadow: settings.$box-shadow-hover; color: settings.$color-hover; } + &:active { + border-color: settings.$border-color-active; + background-color: settings.$background-color-active; + box-shadow: settings.$box-shadow-active; + color: settings.$color-active; + } + &:checked { border-color: settings.$border-color-checked; background-color: settings.$background-color-checked; @@ -60,6 +72,29 @@ } } + &:checked:hover, + &:checked:focus { + border-color: settings.$border-color-checked-hover; + background-color: settings.$background-color-checked-hover; + box-shadow: settings.$box-shadow-checked-hover; + color: settings.$color-checked-hover; + } + + &:checked:active { + border-color: settings.$border-color-checked-active; + background-color: settings.$background-color-checked-active; + box-shadow: settings.$box-shadow-checked-active; + color: settings.$color-checked-active; + } + + &:invalid, + &[aria-invalid='true'] { + border-color: settings.$border-color-invalid; + background-color: settings.$background-color-invalid; + box-shadow: settings.$box-shadow-invalid; + color: settings.$color-invalid; + } + &[disabled] { border-color: settings.$border-color-disabled; background-color: settings.$background-color-disabled; diff --git a/scss/bitstyles/base/input-radio/_settings.scss b/scss/bitstyles/base/input-radio/_settings.scss index 36cbf809e..f773c4c83 100644 --- a/scss/bitstyles/base/input-radio/_settings.scss +++ b/scss/bitstyles/base/input-radio/_settings.scss @@ -24,6 +24,13 @@ $background-color-hover: palette.get('grayscale', 'white') !default; $border-color-hover: palette.get('brand-1') !default; $box-shadow-hover: none !default; +// +// Hover colors //////////////////////////////////////// +$color-active: palette.get('brand-1') !default; +$background-color-active: palette.get('grayscale', 'white') !default; +$border-color-active: palette.get('brand-1') !default; +$box-shadow-active: none !default; + // // Checked colors //////////////////////////////////////// $color-checked: palette.get('brand-1') !default; @@ -31,6 +38,20 @@ $background-color-checked: palette.get('grayscale', 'white') !default; $border-color-checked: palette.get('brand-1') !default; $box-shadow-checked: none !default; +// +// Checked hover colors //////////////////////////////////////// +$color-checked-hover: palette.get('brand-1') !default; +$background-color-checked-hover: palette.get('grayscale', 'white') !default; +$border-color-checked-hover: palette.get('brand-1') !default; +$box-shadow-checked-hover: none !default; + +// +// Checked active colors //////////////////////////////////////// +$color-checked-active: palette.get('brand-1') !default; +$background-color-checked-active: palette.get('grayscale', 'white') !default; +$border-color-checked-active: palette.get('brand-1') !default; +$box-shadow-checked-active: none !default; + // // Invalid colors //////////////////////////////////////// diff --git a/scss/bitstyles/settings/_focus.scss b/scss/bitstyles/settings/_focus.scss new file mode 100644 index 000000000..50773ac65 --- /dev/null +++ b/scss/bitstyles/settings/_focus.scss @@ -0,0 +1,6 @@ +@use '../tools/palette'; +@use '../tools/size'; + +$outline-color: palette.get('brand-2') !default; +$outline-width: calc(size.get('s7') + size.get('s7') / 2) !default; +$outline-offset: size.get('s7') !default;