From a3b1cd7ce206529aa4cdd51a16464495f92810aa Mon Sep 17 00:00:00 2001 From: janis Date: Wed, 5 Feb 2025 14:01:13 +0100 Subject: [PATCH 1/2] feat(ProgressBadge): Implement ProgressBadge component This commit introduces a new ProgressBadge component to display a condensed view of progress as a badge --- .../base/progress_badge/progress_badge.md | 44 ++++++++ .../base/progress_badge/progress_badge.scss | 76 +++++++++++++ .../progress_badge/progress_badge.spec.js | 104 +++++++++++++++++ .../progress_badge/progress_badge.stories.js | 55 +++++++++ .../base/progress_badge/progress_badge.vue | 105 ++++++++++++++++++ src/scss/components.scss | 1 + 6 files changed, 385 insertions(+) create mode 100644 src/components/base/progress_badge/progress_badge.md create mode 100644 src/components/base/progress_badge/progress_badge.scss create mode 100644 src/components/base/progress_badge/progress_badge.spec.js create mode 100644 src/components/base/progress_badge/progress_badge.stories.js create mode 100644 src/components/base/progress_badge/progress_badge.vue diff --git a/src/components/base/progress_badge/progress_badge.md b/src/components/base/progress_badge/progress_badge.md new file mode 100644 index 0000000000..1cab06d5ad --- /dev/null +++ b/src/components/base/progress_badge/progress_badge.md @@ -0,0 +1,44 @@ +## Usage + +Signify the progress of something in a condensed way using a badge that +displays it as a fraction of a circle. + +```html + +``` + +You can optionally provide a `warning-at-percent` prop. If you set it to 60, +the badge will appear in a warning state once the count exceeds 60% of the +total. You can also provide it as a fraction (`0.6`). + +```html + +``` + +Similarly, you can provide a `danger-at-percent` prop that will make the badge +appear in a danger state once the count exceeds that amount of the total. + +```html + +``` + +If you don't provide a value for `warning-at-percent` and `danger-at-percent`, +the badge will appear in its default state even when `count >= total`. + +You can provide both warning states and danger states. If both are set to the +same value, the danger state takes precedence. + +Since you can provide both fractions and integers, a value of `1` will be +treated as 100%. diff --git a/src/components/base/progress_badge/progress_badge.scss b/src/components/base/progress_badge/progress_badge.scss new file mode 100644 index 0000000000..2c68d9481a --- /dev/null +++ b/src/components/base/progress_badge/progress_badge.scss @@ -0,0 +1,76 @@ +.gl-progress-badge { + @apply gl-inline-flex gl-items-center gl-p-1 gl-decoration-0; + + border-radius: 9999px; + + .gl-progress-badge-chart { + @apply gl-size-5; + + .gl-progress-badge-chart-filled, .gl-progress-badge-chart-remaining { + fill: none; + stroke-width: 7; + } + } + + .gl-progress-badge-content { + @apply gl-px-1 gl-text-sm; + } +} + +@mixin gl-progress-badge-variant( + $variant, + $background-color, + $empty-chart-line-color, + $filled-chart-line-color, + $filled-chart-line-color-darkmode, + $text-color +) { + .gl-progress-badge.gl-progress-badge-#{$variant} { + background-color: $background-color; + + .gl-progress-badge-content{ + color: $text-color; + } + + .gl-progress-badge-chart { + .gl-progress-badge-chart-filled { + stroke: $filled-chart-line-color; + } + + .gl-progress-badge-chart-remaining { + stroke: $empty-chart-line-color + } + } + + .gl-dark .gl-progress-badge-chart .gl-progress-badge-chart-filled { + stroke: $filled-chart-line-color-darkmode; + } + } +} + +@include gl-progress-badge-variant( + $variant: default, + $background-color: var(--gl-badge-muted-background-color-default), + $empty-chart-line-color: var(--gl-color-alpha-dark-24), + $filled-chart-line-color: var(--gl-color-blue-500), + $filled-chart-line-color-darkmode: var(--gl-color-blue-800), + $text-color: var(--gl-badge-muted-text-color-default), +); + +@include gl-progress-badge-variant( + $variant: warning, + $background-color: var(--gl-badge-warning-background-color-default), + $empty-chart-line-color: var(--gl-color-alpha-dark-16), + $filled-chart-line-color: var(--gl-badge-warning-icon-color-default), + $filled-chart-line-color-darkmode: var(--gl-badge-warning-icon-color-default), + $text-color: var(--gl-badge-warning-text-color-default), +); + +@include gl-progress-badge-variant( + $variant: danger, + $background-color: var(--gl-badge-danger-background-color-default), + $empty-chart-line-color: var(--gl-color-alpha-dark-16), + $filled-chart-line-color: var(--gl-badge-danger-icon-color-default), + $filled-chart-line-color-darkmode: var(--gl-badge-danger-icon-color-default), + $text-color: var(--gl-badge-danger-text-color-default), +); diff --git a/src/components/base/progress_badge/progress_badge.spec.js b/src/components/base/progress_badge/progress_badge.spec.js new file mode 100644 index 0000000000..7b1230e64e --- /dev/null +++ b/src/components/base/progress_badge/progress_badge.spec.js @@ -0,0 +1,104 @@ +import { mount } from '@vue/test-utils'; +import ProgressBadge from './progress_badge.vue'; + +describe('ProgressBadge', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = mount(ProgressBadge, { + propsData: props, + }); + }; + + const getSvg = () => wrapper.get('svg'); + const getChartFilledLine = () => getSvg().get('.gl-progress-badge-chart-filled'); + const getChartEmptyLine = () => getSvg().get('.gl-progress-badge-chart-remaining'); + const getContent = () => wrapper.get('.gl-progress-badge-content'); + + describe('svg structure', () => { + beforeEach(() => { + createComponent({ + count: 10, + total: 99, + }); + }); + + it('uses the correct path configuration that allows using percentage values in stroke-dasharray', () => { + const expectedPath = + 'M19.5 3.6 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831'; + expect(getChartFilledLine().attributes('d')).toEqual(expectedPath); + expect(getChartEmptyLine().attributes('d')).toEqual(expectedPath); + }); + }); + + describe.each` + scenario | count | total | warnAtPercent | dangerAtPercent | expectedVariantClass | expectedContent | expectedFilledStroke | expectedEmptyStroke + ${'default'} | ${20} | ${100} | ${undefined} | ${undefined} | ${'gl-progress-badge-default'} | ${'20/100'} | ${'20, 100'} | ${'0, 20, 100'} + ${'default almost filled, no warning'} | ${599} | ${600} | ${undefined} | ${undefined} | ${'gl-progress-badge-default'} | ${'599/600'} | ${'100, 100'} | ${'0, 100, 100'} + ${'warning'} | ${7} | ${12} | ${50} | ${undefined} | ${'gl-progress-badge-warning'} | ${'7/12'} | ${'59, 100'} | ${'0, 59, 100'} + ${'warning level as fraction'} | ${126} | ${222} | ${0.5} | ${undefined} | ${'gl-progress-badge-warning'} | ${'126/222'} | ${'57, 100'} | ${'0, 57, 100'} + ${'danger'} | ${901} | ${1001} | ${undefined} | ${90} | ${'gl-progress-badge-danger'} | ${'901/1001'} | ${'91, 100'} | ${'0, 91, 100'} + ${'danger level as fraction'} | ${19} | ${20} | ${undefined} | ${0.94} | ${'gl-progress-badge-danger'} | ${'19/20'} | ${'95, 100'} | ${'0, 95, 100'} + ${'negative count'} | ${-20} | ${100} | ${undefined} | ${undefined} | ${'gl-progress-badge-default'} | ${'-20/100'} | ${'0, 100'} | ${'0, 0, 100'} + ${'count > total'} | ${84} | ${67} | ${undefined} | ${undefined} | ${'gl-progress-badge-default'} | ${'84/67'} | ${'100, 100'} | ${'0, 100, 100'} + ${'total is 0'} | ${9262} | ${0} | ${undefined} | ${undefined} | ${'gl-progress-badge-default'} | ${'9262/0'} | ${'100, 100'} | ${'0, 100, 100'} + ${'warning level is 0'} | ${0} | ${10} | ${0} | ${undefined} | ${'gl-progress-badge-warning'} | ${'0/10'} | ${'0, 100'} | ${'0, 0, 100'} + ${'danger level is 0'} | ${0} | ${10} | ${undefined} | ${0} | ${'gl-progress-badge-danger'} | ${'0/10'} | ${'0, 100'} | ${'0, 0, 100'} + ${'warning level == danger level'} | ${60} | ${100} | ${60} | ${60} | ${'gl-progress-badge-danger'} | ${'60/100'} | ${'60, 100'} | ${'0, 60, 100'} + ${'warning level = 1 will be treated as 100%'} | ${10} | ${100} | ${1} | ${undefined} | ${'gl-progress-badge-default'} | ${'10/100'} | ${'10, 100'} | ${'0, 10, 100'} + ${'danger level = 1 will be treated as 100%'} | ${10} | ${100} | ${5} | ${1} | ${'gl-progress-badge-warning'} | ${'10/100'} | ${'10, 100'} | ${'0, 10, 100'} + `( + '$scenario', + ({ + count, + total, + warnAtPercent, + dangerAtPercent, + expectedVariantClass, + expectedContent, + expectedFilledStroke, + expectedEmptyStroke, + }) => { + beforeEach(() => { + createComponent({ count, total, warnAtPercent, dangerAtPercent }); + }); + + it('renders the correct wrapper classes', () => { + expect(wrapper.classes()).toContain('gl-progress-badge'); + expect(wrapper.classes()).toContain(expectedVariantClass); + }); + + it('renders the percentage as a filled dasharray', () => { + expect(getChartFilledLine().attributes('stroke-dasharray')).toEqual(expectedFilledStroke); + }); + + it('renders the remaining percentage as dasharray', () => { + expect(getChartEmptyLine().attributes('stroke-dasharray')).toEqual(expectedEmptyStroke); + }); + + it('renders the expected content', () => { + expect(getContent().text()).toBe(expectedContent); + }); + } + ); + + describe('validators', () => { + it.each([-Infinity, -1, 100.000001, 101, Infinity, 'invalid'])( + 'logs a warning when warnAtPercent is %d', + (value) => { + createComponent({ count: 1, total: 10, warnAtPercent: value }); + + expect(wrapper).toHaveLoggedVueWarnings(); + } + ); + + it.each([-Infinity, -1, 100.000001, 101, Infinity, 'invalid'])( + 'logs a warning when dangerAtPercent is %d', + (value) => { + createComponent({ count: 1, total: 10, dangerAtPercent: value }); + + expect(wrapper).toHaveLoggedVueWarnings(); + } + ); + }); +}); diff --git a/src/components/base/progress_badge/progress_badge.stories.js b/src/components/base/progress_badge/progress_badge.stories.js new file mode 100644 index 0000000000..340d249b5a --- /dev/null +++ b/src/components/base/progress_badge/progress_badge.stories.js @@ -0,0 +1,55 @@ +import GlProgressBadge from './progress_badge.vue'; +import readme from './progress_badge.md'; + +const generateProps = ({ + count = 33, + total = 100, + warnAtPercent = 60, + dangerAtPercent = 90, +} = {}) => ({ + count, + total, + warnAtPercent, + dangerAtPercent, +}); + +const Template = (args) => ({ + components: { GlProgressBadge }, + props: Object.keys(args), + template: ``, +}); + +export const Default = Template.bind({}); +Default.args = generateProps(); + +export const Warning = () => ({ + components: { GlProgressBadge }, + data: () => ({ + total: 100, + count: 65, + }), + template: ``, +}); +Warning.parameters = { controls: { disable: true } }; + +export const Danger = () => ({ + components: { GlProgressBadge }, + data: () => ({ + total: 100, + count: 90, + }), + template: ``, +}); +Danger.parameters = { controls: { disable: true } }; + +export default { + title: 'base/progress-badge', + component: GlProgressBadge, + parameters: { + docs: { + description: { + component: readme, + }, + }, + }, +}; diff --git a/src/components/base/progress_badge/progress_badge.vue b/src/components/base/progress_badge/progress_badge.vue new file mode 100644 index 0000000000..c9fdfee852 --- /dev/null +++ b/src/components/base/progress_badge/progress_badge.vue @@ -0,0 +1,105 @@ + + + diff --git a/src/scss/components.scss b/src/scss/components.scss index 5e9bc5c29a..e5ab1db22a 100644 --- a/src/scss/components.scss +++ b/src/scss/components.scss @@ -55,6 +55,7 @@ @import '../components/base/pagination/pagination'; @import '../components/base/path/path'; @import '../components/base/popover/popover'; +@import '../components/base/progress_badge/progress_badge'; @import '../components/base/progress_bar/progress_bar'; @import '../components/base/search_box_by_type/search_box_by_type'; @import '../components/base/search_box_by_click/search_box_by_click'; -- GitLab From 0e2006f35504673944e19a523b57f72c25bc840a Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 7 Feb 2025 12:17:09 +0000 Subject: [PATCH 2/2] chore: update snapshots --- ...yshots-base-progress-badge-danger-1-snap.png | Bin 0 -> 12260 bytes ...shots-base-progress-badge-default-1-snap.png | Bin 0 -> 12857 bytes ...shots-base-progress-badge-warning-1-snap.png | Bin 0 -> 12990 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-danger-1-snap.png create mode 100644 tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-default-1-snap.png create mode 100644 tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-warning-1-snap.png diff --git a/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-danger-1-snap.png b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-danger-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..25987382041691ae65cc5e88cc0ed69d3fac4e6c GIT binary patch literal 12260 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 cA@Cz!zopr0BMyOw*UYD literal 0 HcmV?d00001 diff --git a/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-default-1-snap.png b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-default-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..6f1b8be35f5f0327c1070661ea165dee670001c3 GIT binary patch literal 12857 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 literal 0 HcmV?d00001 diff --git a/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-warning-1-snap.png b/tests/__image_snapshots__/storyshots-spec-js-image-storyshots-base-progress-badge-warning-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..047d78aa88f9aa6f74e5e9d0c18f4624f43f5e06 GIT binary patch literal 12990 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 CI>IXe literal 0 HcmV?d00001 -- GitLab