diff --git a/documentation/components/component_documentation_generator.vue b/documentation/components/component_documentation_generator.vue index 443fb39a9ddf03020d4591b5cc8672cfc0e356ff..9c9069944536ed6a24ecba17bed78c8c51967a60 100644 --- a/documentation/components/component_documentation_generator.vue +++ b/documentation/components/component_documentation_generator.vue @@ -232,7 +232,7 @@ export default { {{ field.value }} v-model @@ -269,10 +269,7 @@ export default { diff --git a/documentation/components_documentation.js b/documentation/components_documentation.js index 475b2fb6af7c8fd27fdaf4aee4afe35178b06fed..8a1f80e5645be5fe733bb3ba989ee6a01cdc9d1c 100644 --- a/documentation/components_documentation.js +++ b/documentation/components_documentation.js @@ -26,6 +26,7 @@ export { default as GlColumnChartDocumentation } from '../src/components/charts/ export { default as GlDiscreteScatterChartDocumentation } from '../src/components/charts/discrete_scatter/discrete_scatter.documentation'; export { default as GlSkeletonLoadingDocumentation } from '../src/components/base/skeleton_loading/skeleton_loading.documentation'; export { default as GlBadgeDocumentation } from '../src/components/base/badge/badge.documentation'; +export { default as GlDeprecatedBadgeDocumentation } from '../src/components/base/deprecated_badge/badge.documentation'; export { default as GlDeprecatedButtonDocumentation } from '../src/components/base/deprecated_button/deprecated_button.documentation'; export { default as GlButtonDocumentation } from '../src/components/base/button/button.documentation'; export { default as GlLinkDocumentation } from '../src/components/base/link/link.documentation'; diff --git a/index.js b/index.js index 0cdb10454a091e2de57304df5208d2011bc06656..5305f446bc9d4dba57693b843ce7e1f720dbccad 100644 --- a/index.js +++ b/index.js @@ -28,6 +28,7 @@ export { default as GlProgressBar } from './src/components/base/progress_bar/pro export { default as GlToken } from './src/components/base/token/token.vue'; export { default as GlSkeletonLoading } from './src/components/base/skeleton_loading/skeleton_loading.vue'; export { default as GlBadge } from './src/components/base/badge/badge.vue'; +export { default as GlDeprecatedBadge } from './src/components/base/deprecated_badge/badge.vue'; export { default as GlBanner } from './src/components/base/banner/banner.vue'; export { default as GlDeprecatedButton } from './src/components/base/deprecated_button/deprecated_button.vue'; export { default as GlButton } from './src/components/base/button/button.vue'; diff --git a/src/components/base/badge/badge.documentation.js b/src/components/base/badge/badge.documentation.js index 5c263cd7e0427a10a2bb77284f25cde39c1d9662..7e2afade91f16d448d49406a8bc5be7b4bb23008 100644 --- a/src/components/base/badge/badge.documentation.js +++ b/src/components/base/badge/badge.documentation.js @@ -4,8 +4,19 @@ export default { examples, bootstrapComponent: 'b-badge', bootstrapPropsInfo: { + href: { + additionalInfo: + 'Denotes the target URL of the link for standard a links. Providing this makes the badge actionable (clickable).', + }, + }, + propsInfo: { variant: { - enum: 'variantOptions', + additionalInfo: 'The variant of the badge.', + enum: 'badgeVariantOptions', + }, + size: { + additionalInfo: 'The size of the badge.', + enum: 'badgeSizeOptions', }, }, }; diff --git a/src/components/base/badge/badge.md b/src/components/base/badge/badge.md new file mode 100644 index 0000000000000000000000000000000000000000..cab3374e7a7574e17709a9faf9271803f9a582e0 --- /dev/null +++ b/src/components/base/badge/badge.md @@ -0,0 +1,28 @@ +# GlBadge + + + +Badges highlight metadata of objects, the kind of information that always needs +some context and isn’t useful on its own. For example, they can be used to +indicate an issue’s status, a member’s role, or if a branch is protected. + +## Usage + +```html +Hello, world! +``` + +> Note: Native support for icons in badges will be added in a future version. + +## Edge cases + +While this component is based on +[`BBadge`](https://bootstrap-vue.js.org/docs/components/badge) from +`bootstrap-vue`, it is not a drop-in replacement. Specifically, this component: + + - Has a different set of valid `variant`s. See the examples or props + documentation for those values. + - Always sets the `pill` prop of the underlying `BBadge` to `true`. Any passed + in `pill` prop value is ignored. + - Does _not_ scale itself to match the size of its immediate parent, like + `BBadge` does. diff --git a/src/components/base/badge/badge.scss b/src/components/base/badge/badge.scss index a8df0960cc542a662a42e8399df4243163426a71..f8d78be9efb8e96696ee2bbb403db66ad7602bd1 100644 --- a/src/components/base/badge/badge.scss +++ b/src/components/base/badge/badge.scss @@ -1,51 +1,125 @@ -@import './badge-neutral'; -@import './badge-info'; -@import './badge-success'; -@import './badge-warning'; -@import './badge-danger'; +@mixin gl-badge-variant( + $variant, + $color, + $bg, + $hover-color, + $border-color, + $active-color, + $active-bg +) { + .gl-badge.badge-#{$variant} { + background-color: $bg; + color: $color; + } + + a.gl-badge.badge-#{$variant} { + &:hover { + color: $hover-color; + // Needed to override bootstrap's badge variant background + background-color: $bg; + box-shadow: inset 0 0 0 $gl-border-size-1 $border-color; + } + + &:focus { + color: $hover-color; + // Needed to override bootstrap's badge variant background + background-color: $bg; + @include gl-focus($gl-border-size-1, $white-transparent); + } + + &.active, + &:active { + color: $active-color; + background-color: $active-bg; + @include gl-focus($gl-border-size-1, $white-transparent); + } + } +} + +/* Basic badge styles */ .gl-badge { @include gl-display-inline-flex; @include gl-align-items-center; - @include gl-h-6; - @include gl-font-weight-bold; @include gl-font-sm; + @include gl-font-weight-normal; @include gl-line-height-normal; - @include gl-px-b6; + @include gl-py-2; + @include gl-px-3; @include gl-outline-none; - @include gl-border-solid; - @include gl-border-2; - // Sizes &.sm { - @include gl-h-5; + @include gl-py-0; } - &.lg { - @include gl-h-7; - @include gl-font-base; + &.md { + @include gl-py-2; } - &.normal { - @include gl-font-weight-normal; + &.lg { + @include gl-py-3; + @include gl-font-base; } } -a, -button { - &.gl-badge { - &:focus { - @include gl-focus-glow; - @include gl-outline-none; - } +/* Variants */ - &:hover { - @include gl-drop-shadow; - } +@include gl-badge-variant( + $variant: muted, + $color: $gray-500, + $bg: $gray-50, + $hover-color: $gray-600, + $border-color: $gray-200, + $active-color: $gray-800, + $active-bg: $gray-100 +); - &.active, - &:active { - @include gl-inset-shadow; - } - } -} +@include gl-badge-variant( + $variant: neutral, + $color: $gray-700, + $bg: $gray-100, + $hover-color: $gray-800, + $border-color: $gray-200, + $active-color: $gray-900, + $active-bg: $gray-200 +); + +@include gl-badge-variant( + $variant: info, + $color: $blue-700, + $bg: $blue-100, + $hover-color: $blue-800, + $border-color: $blue-200, + $active-color: $blue-900, + $active-bg: $blue-200 +); + +@include gl-badge-variant( + $variant: success, + $color: $green-700, + $bg: $green-100, + $hover-color: $green-800, + $border-color: $green-200, + $active-color: $green-900, + $active-bg: $green-200 +); + +@include gl-badge-variant( + $variant: warning, + $color: $orange-700, + $bg: $orange-100, + $hover-color: $orange-800, + $border-color: $orange-200, + $active-color: $orange-900, + $active-bg: $orange-200 +); + +@include gl-badge-variant( + $variant: danger, + $color: $red-700, + $bg: $red-100, + $hover-color: $red-800, + $border-color: $red-200, + $active-color: $red-900, + $active-bg: $red-200 +); diff --git a/src/components/base/badge/badge.stories.js b/src/components/base/badge/badge.stories.js index bb5b7bf16d2f6433fcacba3badb35f9719b6f960..8ddadac7055d30a10e471f9e2ef4418ee1760493 100644 --- a/src/components/base/badge/badge.stories.js +++ b/src/components/base/badge/badge.stories.js @@ -1,14 +1,59 @@ -import { withKnobs } from '@storybook/addon-knobs'; +import { withKnobs, text, select } from '@storybook/addon-knobs'; import { documentedStoriesOf } from '../../../../documentation/documented_stories'; +import readme from './badge.md'; +import GlBadge from './badge.vue'; +import { badgeSizeOptions, badgeVariantOptions } from '../../../utils/constants'; + +const components = { + GlBadge, +}; const template = ` -
- Testbadge -
+ {{ content }} `; -documentedStoriesOf('base|badge', '') +const generateProps = ({ + variant = GlBadge.props.variant.default, + size = GlBadge.props.size.default, + href = '', + content = 'TestBadge', +} = {}) => ({ + variant: { + type: String, + default: select('variant', Object.values(badgeVariantOptions), variant), + }, + size: { + type: String, + default: select('size', Object.values(badgeSizeOptions), size), + }, + href: { + type: String, + default: text('href', href), + }, + content: { + type: String, + default: text('content', content), + }, +}); + +documentedStoriesOf('base|badge', readme) .addDecorator(withKnobs) .add('default', () => ({ + components, + props: generateProps(), + template, + })) + .add('actionable warning', () => ({ + components, + props: generateProps({ href: '#foo', variant: badgeVariantOptions.warning }), + template, + })) + .add('large danger', () => ({ + components, + props: generateProps({ size: badgeSizeOptions.lg, variant: badgeVariantOptions.danger }), template, })); diff --git a/src/components/base/badge/badge.vue b/src/components/base/badge/badge.vue index dc52202d48d3913c311b6f74d70de9dcfccbf448..69d1b5debc0499ae1b8fc504c18351fc2ce69db6 100644 --- a/src/components/base/badge/badge.vue +++ b/src/components/base/badge/badge.vue @@ -1,16 +1,35 @@ diff --git a/src/components/base/badge/examples/badge.action.example.vue b/src/components/base/badge/examples/badge.action.example.vue index 505ae95bd10c65ec8229d331b649b2500716631d..e6350168dd8d0109b7aa2da49aa4ec1028282ec7 100644 --- a/src/components/base/badge/examples/badge.action.example.vue +++ b/src/components/base/badge/examples/badge.action.example.vue @@ -1,12 +1,10 @@ diff --git a/src/components/base/badge/examples/badge.basic.example.vue b/src/components/base/badge/examples/badge.basic.example.vue index e45da4c80a5f2b9d51741a447bfee865fdefa9b6..51e26ff06b4f23009679fc96f446f35f3f7d0e08 100644 --- a/src/components/base/badge/examples/badge.basic.example.vue +++ b/src/components/base/badge/examples/badge.basic.example.vue @@ -1,3 +1,3 @@ diff --git a/src/components/base/badge/examples/badge.button.example.vue b/src/components/base/badge/examples/badge.button.example.vue index f56fb6ba32f74349b1e8e80928b469a3d1311462..9d0db29f43388ca9a563be55395d93fbc991cbc8 100644 --- a/src/components/base/badge/examples/badge.button.example.vue +++ b/src/components/base/badge/examples/badge.button.example.vue @@ -1,7 +1,8 @@ diff --git a/src/components/base/badge/examples/badge.scaling.example.vue b/src/components/base/badge/examples/badge.scaling.example.vue deleted file mode 100644 index 9cc9dedf812e62c6886e17e2830a8e5ff6d3ab42..0000000000000000000000000000000000000000 --- a/src/components/base/badge/examples/badge.scaling.example.vue +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/src/components/base/badge/examples/badge.sizes.example.vue b/src/components/base/badge/examples/badge.sizes.example.vue new file mode 100644 index 0000000000000000000000000000000000000000..c83b479ec78fe62a623a0842e9648c866d013479 --- /dev/null +++ b/src/components/base/badge/examples/badge.sizes.example.vue @@ -0,0 +1,7 @@ + diff --git a/src/components/base/badge/examples/badge.variants.example.vue b/src/components/base/badge/examples/badge.variants.example.vue index bf154923f2b6bc36c4b628189955fe1020d73328..2709fc8018724a1a386ac4290f4422a5f9bf9609 100644 --- a/src/components/base/badge/examples/badge.variants.example.vue +++ b/src/components/base/badge/examples/badge.variants.example.vue @@ -1,12 +1,10 @@ diff --git a/src/components/base/badge/examples/index.js b/src/components/base/badge/examples/index.js index 87baa0bc53a47c52775d37d5691c4e626830673d..08484a03119b245499b03da0f7c6d4fc385131a5 100644 --- a/src/components/base/badge/examples/index.js +++ b/src/components/base/badge/examples/index.js @@ -1,6 +1,6 @@ import BadgeBasicExample from './badge.basic.example.vue'; +import BadgeSizesExample from './badge.sizes.example.vue'; import BadgeVariantsExample from './badge.variants.example.vue'; -import BadgeScalingExample from './badge.scaling.example.vue'; import BadgeActionableExample from './badge.action.example.vue'; import BadgeButtonExample from './badge.button.example.vue'; @@ -21,10 +21,10 @@ export default [ component: BadgeVariantsExample, }, { - id: 'badge-scaling', - name: 'Scaling', - description: 'Automatic Scaling of Badges', - component: BadgeScalingExample, + id: 'badge-sizes', + name: 'Sizes', + description: 'Different Badge Sizes', + component: BadgeSizesExample, }, { id: 'badge-actions', diff --git a/src/components/base/deprecated_badge/badge.documentation.js b/src/components/base/deprecated_badge/badge.documentation.js new file mode 100644 index 0000000000000000000000000000000000000000..5c263cd7e0427a10a2bb77284f25cde39c1d9662 --- /dev/null +++ b/src/components/base/deprecated_badge/badge.documentation.js @@ -0,0 +1,11 @@ +import examples from './examples'; + +export default { + examples, + bootstrapComponent: 'b-badge', + bootstrapPropsInfo: { + variant: { + enum: 'variantOptions', + }, + }, +}; diff --git a/src/components/base/deprecated_badge/badge.md b/src/components/base/deprecated_badge/badge.md new file mode 100644 index 0000000000000000000000000000000000000000..5530e841d7e962a09dc712b0fb400971f2f97218 --- /dev/null +++ b/src/components/base/deprecated_badge/badge.md @@ -0,0 +1,5 @@ +# GlDeprecatedBadge + + + +> Note: `GlDeprecatedBadge` is _deprecated_ and should not be used. It exists only to ease migration to `GlBadge`, which should be used instead. diff --git a/src/components/base/deprecated_badge/badge.stories.js b/src/components/base/deprecated_badge/badge.stories.js new file mode 100644 index 0000000000000000000000000000000000000000..6e75929d360ec2e9c4fa1a00096b3a90b3749fe0 --- /dev/null +++ b/src/components/base/deprecated_badge/badge.stories.js @@ -0,0 +1,15 @@ +import { withKnobs } from '@storybook/addon-knobs'; +import { documentedStoriesOf } from '../../../../documentation/documented_stories'; +import readme from './badge.md'; + +const template = ` +
+ Testbadge +
+ `; + +documentedStoriesOf('base|deprecated-badge', readme) + .addDecorator(withKnobs) + .add('default', () => ({ + template, + })); diff --git a/src/components/base/deprecated_badge/badge.vue b/src/components/base/deprecated_badge/badge.vue new file mode 100644 index 0000000000000000000000000000000000000000..dc52202d48d3913c311b6f74d70de9dcfccbf448 --- /dev/null +++ b/src/components/base/deprecated_badge/badge.vue @@ -0,0 +1,16 @@ + + + diff --git a/src/components/base/deprecated_badge/examples/badge.action.example.vue b/src/components/base/deprecated_badge/examples/badge.action.example.vue new file mode 100644 index 0000000000000000000000000000000000000000..7249e663f85d771bc4cfaec09a00281e358b62cc --- /dev/null +++ b/src/components/base/deprecated_badge/examples/badge.action.example.vue @@ -0,0 +1,12 @@ + diff --git a/src/components/base/deprecated_badge/examples/badge.basic.example.vue b/src/components/base/deprecated_badge/examples/badge.basic.example.vue new file mode 100644 index 0000000000000000000000000000000000000000..4102233053135f4227617a3af28717722efb9904 --- /dev/null +++ b/src/components/base/deprecated_badge/examples/badge.basic.example.vue @@ -0,0 +1,3 @@ + diff --git a/src/components/base/deprecated_badge/examples/badge.button.example.vue b/src/components/base/deprecated_badge/examples/badge.button.example.vue new file mode 100644 index 0000000000000000000000000000000000000000..8d1552637cf3eca09545532273547ab92af72341 --- /dev/null +++ b/src/components/base/deprecated_badge/examples/badge.button.example.vue @@ -0,0 +1,7 @@ + diff --git a/src/components/base/deprecated_badge/examples/badge.scaling.example.vue b/src/components/base/deprecated_badge/examples/badge.scaling.example.vue new file mode 100644 index 0000000000000000000000000000000000000000..09fe27dada5b508e8db7ff05ce18850c88c80748 --- /dev/null +++ b/src/components/base/deprecated_badge/examples/badge.scaling.example.vue @@ -0,0 +1,9 @@ + diff --git a/src/components/base/deprecated_badge/examples/badge.variants.example.vue b/src/components/base/deprecated_badge/examples/badge.variants.example.vue new file mode 100644 index 0000000000000000000000000000000000000000..b6c0fe08bc332522cf202dcdd9198e50b70c4d56 --- /dev/null +++ b/src/components/base/deprecated_badge/examples/badge.variants.example.vue @@ -0,0 +1,12 @@ + diff --git a/src/components/base/deprecated_badge/examples/index.js b/src/components/base/deprecated_badge/examples/index.js new file mode 100644 index 0000000000000000000000000000000000000000..ec17e6c4915cf49d298fed63209cd1cd0724e719 --- /dev/null +++ b/src/components/base/deprecated_badge/examples/index.js @@ -0,0 +1,43 @@ +import BadgeBasicExample from './badge.basic.example.vue'; +import BadgeVariantsExample from './badge.variants.example.vue'; +import BadgeScalingExample from './badge.scaling.example.vue'; +import BadgeActionableExample from './badge.action.example.vue'; +import BadgeButtonExample from './badge.button.example.vue'; + +export default [ + { + name: 'Basic', + items: [ + { + id: 'deprecated-badge-basic', + name: 'Basic', + description: 'Basic Badge', + component: BadgeBasicExample, + }, + { + id: 'deprecated-badge-variants', + name: 'Variants', + description: 'Different Badge Variants', + component: BadgeVariantsExample, + }, + { + id: 'deprecated-badge-scaling', + name: 'Scaling', + description: 'Automatic Scaling of Badges', + component: BadgeScalingExample, + }, + { + id: 'deprecated-badge-actions', + name: 'Actionable', + description: 'Badges with Actions', + component: BadgeActionableExample, + }, + { + id: 'deprecated-badge-button', + name: 'Inside Button', + description: 'Show Badge inside Button', + component: BadgeButtonExample, + }, + ], + }, +]; diff --git a/src/scss/components.scss b/src/scss/components.scss index c2d08b54c266bdfbc41a7f0a32703a111c86a1f1..c85b6f3acd4323523fbb4bd2d0b04acc0a2352af 100644 --- a/src/scss/components.scss +++ b/src/scss/components.scss @@ -9,6 +9,7 @@ @import '../components/base/avatar_labeled/avatar_labeled'; @import '../components/base/avatar_link/avatar_link'; @import '../components/base/avatars_inline/avatars_inline'; +@import '../components/base/badge/badge'; @import '../components/base/breadcrumb/breadcrumb'; @import '../components/base/datepicker/datepicker'; @import '../components/base/banner/banner'; diff --git a/src/utils/constants.js b/src/utils/constants.js index c5b1309916d5137292fd1daff2e09d3da52eba03..8155ba1ee553897eea9b92fbec7b7513b563643b 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -20,6 +20,21 @@ export const variantOptions = { dark: 'dark', }; +export const badgeSizeOptions = { + sm: 'sm', + md: 'md', + lg: 'lg', +}; + +export const badgeVariantOptions = { + muted: 'muted', + neutral: 'neutral', + info: 'info', + success: 'success', + warning: 'warning', + danger: 'danger', +}; + export const variantCssColorMap = { info: 'gl-text-blue-500', success: 'gl-text-green-500', diff --git a/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-actionable-warning-1-snap.png b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-actionable-warning-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..179a4aba0ca48a4b5e44eed76d58e4d234ea687f Binary files /dev/null and b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-actionable-warning-1-snap.png differ diff --git a/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-default-1-snap.png b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-default-1-snap.png index dbc81925bc03a2c08c535b7b74e37be3c737fa27..adaf63fd1bd1ba9746b8cd3e196737d12c7bca9e 100644 Binary files a/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-default-1-snap.png and b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-default-1-snap.png differ diff --git a/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-large-danger-1-snap.png b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-large-danger-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..fd8d5f001c46e0377045f893d440587e4242c1f6 Binary files /dev/null and b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-badge-large-danger-1-snap.png differ diff --git a/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-deprecated-badge-default-1-snap.png b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-deprecated-badge-default-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..dbc81925bc03a2c08c535b7b74e37be3c737fa27 Binary files /dev/null and b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-deprecated-badge-default-1-snap.png differ