Skip to content

Commit

Permalink
Add capacity indicator component (#19)
Browse files Browse the repository at this point in the history
+ Don't automatically open browser for Storybook
+ Update icons package
  • Loading branch information
laurensgroeneveld authored Mar 6, 2023
1 parent ad992eb commit 772fcb1
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 13 deletions.
5 changes: 4 additions & 1 deletion docs/contributing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Figma designs are generally based on [Flowbite](https://flowbite.com/docs/gettin
### Follow Figma naming for props
Props should be (if possible) be named like they are named in Figma. Usually designers will create a component in Figma that has properties as well. By keeping these as close as possible it's easier to compare different variants between design and code.

### Don't abbreviate props
Props should have a clear name that indicates what it is for, without the need to look up documentation. Abbreviations make this harder, especially for non-native speakers. Instead of trying to save on keystrokes (and modern editors will probably autocomplete props anyways), we'll save on potential user frustration.

### Don't include all attributes as props
When building a wrapper around a native HTML element such as `input`, don't include all the attributes of this element as props on the component. Vue will automatically [pass them through](https://vuejs.org/guide/components/attrs.html). If the element is not the top level element in your component (for example because it is wrapped in a `span`), bind the Vue `$attrs` property to the appropriate element.

Expand All @@ -47,4 +50,4 @@ From the [Vue documentation](https://vuejs.org/guide/components/attrs.html#disab
### Don't use `<style>` in an SFC
Commonly, a Vue Single File Component (SFC) includes a `script`, `template`, and `style` tag. The use of a `style` tag should be avoided, for two reasons:
1. Adding classes this way will generate a CSS file in the output. We don't want consumers of this Vue component library having to include a separate CSS file
2. Since we're using Tailwind, it's not needed to write any CSS. By ruling out the possiblity of using custom classes, even when they're `scoped`, it's clear where CSS styling comes from: Tailwind
2. Since we're using Tailwind, it's not needed to write any CSS. By ruling out the possibility of using custom classes, even when they're `scoped`, it's clear where CSS styling comes from: Tailwind
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"build-types": "vue-tsc --declaration --emitDeclarationOnly -p tsconfig.compile.json",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"storybook": "start-storybook -p 6006",
"storybook": "start-storybook -p 6006 --no-open",
"build-storybook": "build-storybook",
"create-component": "./scripts/create-component.sh"
},
Expand All @@ -35,8 +35,8 @@
},
"devDependencies": {
"@babel/core": "^7.20.12",
"@bcc-code/design-system-tokens": "dev",
"@bcc-code/icons-vue": "^0.1.1",
"@bcc-code/design-system-tokens": "^0.0.0-dev.5ffe680",
"@bcc-code/icons-vue": "^0.2.0",
"@rushstack/eslint-patch": "^1.1.4",
"@storybook/addon-actions": "^6.5.16",
"@storybook/addon-essentials": "^6.5.16",
Expand Down
2 changes: 1 addition & 1 deletion scripts/templates/create-vue-file.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
echo "<script setup lang="ts">
echo "<script setup lang=\"ts\">
type Props = {
};
Expand Down
17 changes: 17 additions & 0 deletions src/components/CapacityIndicator/CapacityIndicator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { describe, it, expect } from "vitest";
import { AllInclusiveIcon } from "@bcc-code/icons-vue";

import { mount } from "@vue/test-utils";
import CapacityIndicator from "./CapacityIndicator.vue";

describe("CapacityIndicator", () => {
it("shows the remaining capacity", () => {
const wrapper = mount(CapacityIndicator, { props: { capacity: 20, used: 14 } });
expect(wrapper.text()).toBe("6");
});

it("shows an icon if the capacity is infinite", () => {
const wrapper = mount(CapacityIndicator, { props: { capacity: Infinity, used: 14 } });
expect(wrapper.findComponent(AllInclusiveIcon).isVisible()).toBe(true);
});
});
41 changes: 41 additions & 0 deletions src/components/CapacityIndicator/CapacityIndicator.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import CapacityIndicator from "./CapacityIndicator.vue";

import type { Meta, StoryFn } from "@storybook/vue3";

export default {
title: "Components/CapacityIndicator",
component: CapacityIndicator,
argTypes: {},
} as Meta<typeof CapacityIndicator>;

const Template: StoryFn<typeof CapacityIndicator> = (args) => ({
components: { CapacityIndicator },
setup() {
return { args };
},
template: `
<CapacityIndicator v-bind="args" />
`,
});

export const Example = Template.bind({});
Example.parameters = {
viewMode: "docs",
};
Example.args = {
capacity: 20,
used: 14,
};

export const State: StoryFn<typeof CapacityIndicator> = () => ({
components: { CapacityIndicator },
template: `
<div class="flex items-center space-x-4">
<CapacityIndicator :capacity="200" :used="1" />
<CapacityIndicator :capacity="20" :used="6" />
<CapacityIndicator :capacity="20" :used="18" />
<CapacityIndicator :capacity="20" :used="20" />
<CapacityIndicator :capacity="Infinity" />
</div>
`,
});
74 changes: 74 additions & 0 deletions src/components/CapacityIndicator/CapacityIndicator.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<script setup lang="ts">
import { AllInclusiveIcon } from "@bcc-code/icons-vue";
import { ref } from "vue";
type Props = {
capacity?: number;
used?: number;
};
const props = withDefaults(defineProps<Props>(), {
capacity: Infinity,
used: 0,
});
const size = 40;
const progress = ref(props.capacity === Infinity ? 100 : (props.used / props.capacity) * 100);
const trackWidth = 2;
const center = size / 2;
const radius = center - trackWidth;
const dashArray = 2 * Math.PI * radius;
const dashOffset = dashArray * ((100 - progress.value) / 100);
</script>

<template>
<div class="relative inline-block">
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<span v-if="capacity === Infinity">
<AllInclusiveIcon class="h-4 w-4 text-neutral-900" />
</span>
<span
v-else
class="text-sm font-bold"
:class="{
'text-neutral-900': progress < 50,
'text-muddy-waters-600': progress >= 50 && progress < 100,
'text-red-900': progress >= 100,
}"
>
{{ capacity - used }}
</span>
</div>

<svg class="-rotate-90" :style="{ width: size, height: size }">
<circle
:cx="center"
:cy="center"
:r="radius"
:stroke-width="trackWidth"
fill="transparent"
:class="{
'stroke-neutral-200': capacity === Infinity,
'stroke-neutral-300': capacity !== Infinity && progress < 50,
'stroke-muddy-waters-100': capacity !== Infinity && progress >= 50,
}"
/>
<circle
v-if="capacity !== Infinity"
:cx="center"
:cy="center"
:r="radius"
:stroke-width="trackWidth"
:stroke-dasharray="dashArray"
:stroke-dashoffset="dashOffset"
fill="transparent"
:class="{
'stroke-neutral-500': progress < 50,
'stroke-muddy-waters-500': progress >= 50 && progress < 100,
'stroke-red-900': progress >= 100,
}"
/>
</svg>
</div>
</template>
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as BccButton } from "./components/BccButton/BccButton.vue";
export { default as BccInput } from "./components/BccInput/BccInput.vue";
export { default as Badge } from "./components/Badge/Badge.vue";
export { default as CapacityIndicator } from "./components/CapacityIndicator/CapacityIndicator.vue";

0 comments on commit 772fcb1

Please sign in to comment.