diff --git a/.changeset/neat-sloths-march.md b/.changeset/neat-sloths-march.md new file mode 100644 index 0000000000..9d65b65396 --- /dev/null +++ b/.changeset/neat-sloths-march.md @@ -0,0 +1,7 @@ +--- +"@primer/css": patch +--- + +- Updates stories to reflect markup for Rails +- Clean up FormControl classes +- Add Radio and Checkbox custom styles diff --git a/docs/.storybook/main.js b/docs/.storybook/main.js index 0aca5a6fd1..bbb2b0925a 100644 --- a/docs/.storybook/main.js +++ b/docs/.storybook/main.js @@ -7,7 +7,8 @@ module.exports = { '@storybook/preset-scss', '@whitespace/storybook-addon-html', 'storybook-addon-designs', - 'storybook-color-picker' + 'storybook-color-picker', + 'storybook-addon-variants/preset.js' ], framework: '@storybook/react', core: { diff --git a/docs/package.json b/docs/package.json index 32607096b8..c09da46036 100644 --- a/docs/package.json +++ b/docs/package.json @@ -59,6 +59,7 @@ "@whitespace/storybook-addon-html": "^5.0.0", "babel-loader": "^8.2.5", "storybook-addon-designs": "6.2.1", + "storybook-addon-variants": "^0.0.5", "storybook-color-picker": "2.3.1" } } diff --git a/docs/src/stories/rails-form-framework/Checkbox.stories.jsx b/docs/src/stories/rails-form-framework/Checkbox.stories.jsx new file mode 100644 index 0000000000..8e5e2f4d3e --- /dev/null +++ b/docs/src/stories/rails-form-framework/Checkbox.stories.jsx @@ -0,0 +1,117 @@ +import React from 'react' +import clsx from 'clsx' + +export default { + title: 'Rails Forms/Checkbox', + parameters: { + layout: 'padded' + }, + decorators: [ + Story => ( +
+ + + ) + ], + excludeStories: ['InputTemplate'], + argTypes: { + disabled: { + description: 'disabled field', + control: {type: 'boolean'}, + table: { + category: 'CSS' + } + }, + visuallyHidden: { + description: 'visually hide label', + control: {type: 'boolean'}, + table: { + category: 'CSS' + } + }, + label: { + type: 'string', + name: 'label', + description: 'string', + table: { + category: 'HTML' + } + }, + caption: { + name: 'caption', + type: 'string', + description: 'caption', + table: { + category: 'HTML' + } + }, + focusElement: { + control: {type: 'boolean'}, + description: 'set focus on element', + table: { + category: 'Interactive' + } + }, + checked: { + control: {type: 'boolean'}, + description: 'checked', + table: { + category: 'Interactive' + } + }, + indeterminate: { + control: {type: 'boolean'}, + description: 'indeterminate', + table: { + category: 'Interactive' + } + } + } +} + +const focusMethod = function getFocus() { + // find the focusable element + var input = document.getElementsByTagName('input')[0] + // set focus on element + input.focus() +} + +export const InputTemplate = ({label, disabled, visuallyHidden, focusElement, caption, checked, indeterminate}) => ( + <> +
+ + + + {caption && ( +

+ {caption} +

+ )} +
+
+ + {focusElement && focusMethod()} + +) + +export const Playground = InputTemplate.bind({}) +Playground.args = { + label: 'Select an option', + disabled: false, + focusElement: false, + caption: 'Caption', + invalid: false, + visuallyHidden: false, + checked: false, + indeterminate: false +} diff --git a/docs/src/stories/rails-form-framework/Radio.stories.jsx b/docs/src/stories/rails-form-framework/Radio.stories.jsx new file mode 100644 index 0000000000..9d6142fe12 --- /dev/null +++ b/docs/src/stories/rails-form-framework/Radio.stories.jsx @@ -0,0 +1,127 @@ +import React from 'react' +import clsx from 'clsx' + +export default { + title: 'Rails Forms/Radio', + parameters: { + layout: 'padded' + }, + decorators: [ + Story => ( +
+ + + ) + ], + excludeStories: ['RadioTemplate'], + argTypes: { + disabled: { + description: 'disabled field', + control: {type: 'boolean'}, + table: { + category: 'CSS' + } + }, + visuallyHidden: { + description: 'visually hide label', + control: {type: 'boolean'}, + table: { + category: 'CSS' + } + }, + label: { + type: 'string', + name: 'label', + description: 'string', + table: { + category: 'HTML' + } + }, + caption: { + name: 'caption', + type: 'string', + description: 'caption', + table: { + category: 'HTML' + } + }, + id: { + name: 'id', + type: 'string', + description: 'id', + table: { + category: 'Radio' + } + }, + focusElement: { + control: {type: 'boolean'}, + description: 'set focus on element', + table: { + category: 'Interactive' + } + }, + checked: { + control: {type: 'boolean'}, + description: 'checked', + table: { + category: 'Interactive' + } + }, + indeterminate: { + control: {type: 'boolean'}, + description: 'indeterminate', + table: { + category: 'Interactive' + } + } + } +} + +const focusMethod = function getFocus() { + // find the focusable element + var input = document.getElementsByTagName('input')[0] + // set focus on element + input.focus() +} + +export const RadioTemplate = ({label, disabled, visuallyHidden, focusElement, caption, checked, indeterminate, id}) => ( + <> +
+ + + + {caption && ( +

+ {caption} +

+ )} +
+
+ + {focusElement && focusMethod()} + +) + +export const Playground = RadioTemplate.bind({}) +Playground.args = { + id: 'some-id', + label: 'Select an option', + disabled: false, + focusElement: false, + caption: 'Caption', + invalid: false, + visuallyHidden: false, + checked: false, + indeterminate: false +} diff --git a/docs/src/stories/rails-form-framework/RadioFeatures.stories.jsx b/docs/src/stories/rails-form-framework/RadioFeatures.stories.jsx new file mode 100644 index 0000000000..2dcef6821b --- /dev/null +++ b/docs/src/stories/rails-form-framework/RadioFeatures.stories.jsx @@ -0,0 +1,14 @@ +import React from 'react' +import {RadioTemplate} from './Radio.stories.jsx' + +export default { + title: 'Rails Forms/Radio/Features' +} + +export const MultiRadios = ({}) => ( +
+ + + + +) diff --git a/docs/src/stories/rails-form-framework/Select.stories.jsx b/docs/src/stories/rails-form-framework/Select.stories.jsx new file mode 100644 index 0000000000..35d8053d05 --- /dev/null +++ b/docs/src/stories/rails-form-framework/Select.stories.jsx @@ -0,0 +1,210 @@ +import React from 'react' +import clsx from 'clsx' + +export default { + title: 'Rails Forms/Select', + parameters: { + layout: 'padded' + }, + decorators: [ + Story => ( +
+ + + ) + ], + excludeStories: ['InputTemplate'], + argTypes: { + size: { + options: [0, 1, 2], // iterator + mapping: ['FormControl-small', 'FormControl-medium', 'FormControl-large'], // values + control: { + type: 'inline-radio', + labels: ['small', 'medium', 'large'] + }, + table: { + category: 'Input' + } + }, + validationStatus: { + options: [0, 1, 2, 3], // iterator + mapping: ['', 'FormControl-error', 'FormControl-success', 'FormControl-warning'], // values + control: { + type: 'inline-radio', + labels: ['undefined', 'error', 'success', 'warning'] + }, + table: { + category: 'Validation' + } + }, + fullWidth: { + description: 'formerly called Block', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + disabled: { + description: 'disabled field', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + required: { + description: 'required field', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + invalid: { + description: 'invalid field', + control: {type: 'boolean'}, + table: { + category: 'Validation' + } + }, + visuallyHidden: { + description: 'visually hide label', + control: {type: 'boolean'}, + table: { + category: 'Label' + } + }, + label: { + type: 'string', + name: 'label', + description: 'string', + table: { + category: 'Label' + } + }, + caption: { + name: 'caption', + type: 'string', + description: 'caption', + table: { + category: 'Caption' + } + }, + validation: { + type: 'string', + name: 'label', + description: 'string', + table: { + category: 'Validation' + } + }, + focusElement: { + control: {type: 'boolean'}, + description: 'set focus on element', + table: { + category: 'Interactive' + } + }, + monospace: { + description: 'monospace text', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + inset: { + description: 'formerly called Contrast', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + } + } +} + +const focusMethod = function getFocus() { + // find the focusable element + var input = document.getElementsByTagName('input')[0] + // set focus on element + input.focus() +} + +export const InputTemplate = ({ + label, + size, + fullWidth, + placeholder, + inset, + disabled, + visuallyHidden, + monospace, + focusElement, + invalid, + caption, + validationStatus +}) => ( + <> +
+ +
+ +
+ {invalid && ( + + +

{validation}

+
+ )} + {caption && ( +

+ {caption} +

+ )} +
+ {focusElement && focusMethod()} + +) + +export const Playground = InputTemplate.bind({}) +Playground.args = { + type: 'email', + placeholder: 'Email address', + label: 'Enter email address', + fullWidth: false, + monospace: false, + inset: false, + disabled: false, + focusElement: false, + size: 1, + caption: 'Caption', + invalid: false, + visuallyHidden: false, + validationStatus: 0 +} diff --git a/docs/src/stories/rails-form-framework/TextInput.stories.jsx b/docs/src/stories/rails-form-framework/TextInput.stories.jsx new file mode 100644 index 0000000000..4412823fd4 --- /dev/null +++ b/docs/src/stories/rails-form-framework/TextInput.stories.jsx @@ -0,0 +1,311 @@ +import React from 'react' +import clsx from 'clsx' + +export default { + title: 'Rails Forms/TextInput', + parameters: { + layout: 'padded' + }, + decorators: [ + Story => ( +
+ + + ) + ], + excludeStories: ['InputTemplate'], + argTypes: { + size: { + options: [0, 1, 2], // iterator + mapping: ['FormControl-small', 'FormControl-medium', 'FormControl-large'], // values + control: { + type: 'inline-radio', + labels: ['small', 'medium', 'large'] + }, + table: { + category: 'Input' + } + }, + validationStatus: { + options: [0, 1, 2, 3], // iterator + mapping: ['', 'FormControl-error', 'FormControl-success', 'FormControl-warning'], // values + control: { + type: 'inline-radio', + labels: ['undefined', 'error', 'success', 'warning'] + }, + table: { + category: 'Validation' + } + }, + fullWidth: { + description: 'formerly called Block', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + showClearButton: { + description: 'show clear button', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + trailingActionDivider: { + description: 'divider between input and trailing action', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + monospace: { + description: 'monospace text', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + inset: { + description: 'formerly called Contrast', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + disabled: { + description: 'disabled field', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + required: { + description: 'required field', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + invalid: { + description: 'invalid field', + control: {type: 'boolean'}, + table: { + category: 'Validation' + } + }, + visuallyHidden: { + description: 'visually hide label', + control: {type: 'boolean'}, + table: { + category: 'Label' + } + }, + placeholder: { + type: 'string', + name: 'placeholder', + description: 'string', + table: { + category: 'Input' + } + }, + label: { + type: 'string', + name: 'label', + description: 'string', + table: { + category: 'Label' + } + }, + caption: { + name: 'caption', + type: 'string', + description: 'caption', + table: { + category: 'Caption' + } + }, + validation: { + type: 'string', + name: 'label', + description: 'string', + table: { + category: 'Validation' + } + }, + focusElement: { + control: {type: 'boolean'}, + description: 'set focus on element', + table: { + category: 'Interactive' + } + }, + leadingVisual: { + name: 'leadingVisual', + type: 'boolean', + description: 'octicon', + table: { + category: 'Input' + } + } + } +} + +const focusMethod = function getFocus() { + // find the focusable element + var input = document.getElementsByTagName('input')[0] + // set focus on element + input.focus() +} + +export const InputTemplate = ({ + label, + size, + fullWidth, + placeholder, + inset, + disabled, + visuallyHidden, + monospace, + focusElement, + showClearButton, + leadingVisual, + invalid, + caption, + validation, + trailingActionDivider, + validationStatus +}) => ( + <> +
+ + {showClearButton || leadingVisual ? ( +
+ {leadingVisual && ( + + + + )} + + {showClearButton && ( + + )} +
+ ) : ( + + )} + {invalid && ( + + +

{validation}

+
+ )} + {caption && ( +

+ {caption} +

+ )} +
+ {focusElement && focusMethod()} + +) + +export const Playground = InputTemplate.bind({}) +Playground.args = { + placeholder: 'Email address', + label: 'Enter email address', + fullWidth: false, + monospace: false, + inset: false, + disabled: false, + focusElement: false, + leadingVisual: false, + size: 1, + caption: 'Caption', + showClearButton: false, + invalid: false, + visuallyHidden: false, + validation: '', + trailingActionDivider: false, + validationStatus: 0 +} diff --git a/docs/src/stories/rails-form-framework/Textarea.stories.jsx b/docs/src/stories/rails-form-framework/Textarea.stories.jsx new file mode 100644 index 0000000000..6a7a31bffc --- /dev/null +++ b/docs/src/stories/rails-form-framework/Textarea.stories.jsx @@ -0,0 +1,199 @@ +import React from 'react' +import clsx from 'clsx' + +export default { + title: 'Rails Forms/Textarea', + parameters: { + layout: 'padded' + }, + decorators: [ + Story => ( +
+ + + ) + ], + excludeStories: ['InputTemplate'], + argTypes: { + validationStatus: { + options: [0, 1, 2, 3], // iterator + mapping: ['', 'FormControl-error', 'FormControl-success', 'FormControl-warning'], // values + control: { + type: 'inline-radio', + labels: ['undefined', 'error', 'success', 'warning'] + }, + table: { + category: 'Validation' + } + }, + fullWidth: { + description: 'formerly called Block', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + monospace: { + description: 'monospace text', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + inset: { + description: 'formerly called Contrast', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + disabled: { + description: 'disabled field', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + required: { + description: 'required field', + control: {type: 'boolean'}, + table: { + category: 'Input' + } + }, + invalid: { + description: 'invalid field', + control: {type: 'boolean'}, + table: { + category: 'Validation' + } + }, + visuallyHidden: { + description: 'visually hide label', + control: {type: 'boolean'}, + table: { + category: 'Label' + } + }, + placeholder: { + type: 'string', + name: 'placeholder', + description: 'string', + table: { + category: 'Input' + } + }, + label: { + type: 'string', + name: 'label', + description: 'string', + table: { + category: 'Label' + } + }, + caption: { + name: 'caption', + type: 'string', + description: 'caption', + table: { + category: 'Caption' + } + }, + validation: { + type: 'string', + name: 'label', + description: 'string', + table: { + category: 'Validation' + } + }, + focusElement: { + control: {type: 'boolean'}, + description: 'set focus on element', + table: { + category: 'Interactive' + } + } + } +} + +const focusMethod = function getFocus() { + // find the focusable element + var input = document.getElementsByTagName('input')[0] + // set focus on element + input.focus() +} + +export const InputTemplate = ({ + label, + fullWidth, + placeholder, + inset, + disabled, + visuallyHidden, + monospace, + focusElement, + invalid, + caption, + validation, + validationStatus +}) => ( + <> +
+ +