Skip to content
This repository has been archived by the owner on Feb 15, 2022. It is now read-only.

Commit

Permalink
feat(VukButton): add button component with primary & secondary varian…
Browse files Browse the repository at this point in the history
…ts (#11)

* refactor(vukheading): changed template for render function

* feat(vukbutton): add button component with primary & secondary variants

* build: export button component

* Update tailwind.config.js
  • Loading branch information
AxonC authored Sep 22, 2020
1 parent 6201f4b commit 9f84073
Show file tree
Hide file tree
Showing 11 changed files with 494 additions and 42 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"node-sass": "^4.13.0",
"postcss": "^7.0.21",
"sass-loader": "^8.0.0",
"tailwindcss": "^1.1.3",
"tailwindcss": "^1.8.10",
"vue": "^2.6.10",
"vue-class-component": "^7.2.3",
"vue-property-decorator": "^8.4.2"
Expand Down
80 changes: 80 additions & 0 deletions src/components/VukButton/VukButton.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# VukButton

Consistent button component for usage across VATSIM UK Services.

## Usage

Example usage

```html
<vuk-button type="blue" variant="primary">Content Goes here</vuk-button>
```

### Colors

Per the design guidelines, the following types are defined:

- blue
- yellow
- purple
- success (green)
- error (red)

### Variants

Within these types, two variants are supported

- primary
- secondary

### Icons

An icon can also be used within a button but a directive must be applied to the SVG.
Adding the `v-button-icon` directive will apply the necessary logic to allow the icon to co-exist with text within the button.

```html
<vuk-button color="purple" variant="secondary">
<svg
v-button-icon
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<!-- path content here -->
</svg>
Testing some long text with an icon
</vuk-button>
```

### Disabling

If you need to disable a button, for example within a form, simply use the built in HTML disabled attribute.

```html
<vuk-button color="blue" variant="primary" disabled
>Content Goes here</vuk-button
>
```

This will render the disabled button of the button and disable its usage. This can also be used via a bound property as normal.

### Events

To register a click event on the button, you must use the native modifier

```html
<vuk-button color="blue" variant="primary" @click.native="myFunction"
>Content Goes here</vuk-button
>
```

### Native Attributes

Any of the native HTML button attributes can be applied directly to the component e.g. type in a form.

```html
<vuk-button color="blue" variant="primary" type="submit"
>Content Goes here</vuk-button
>
```
101 changes: 101 additions & 0 deletions src/components/VukButton/VukButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script>
import { Variants, Colors } from './constants'
export default {
name: 'VukButton',
props: {
color: {
type: String,
required: true,
validator: value => {
return Object.values(Colors).includes(value.toLowerCase())
}
},
variant: {
type: String,
required: true,
validator: value => {
return Object.values(Variants).includes(value.toLowerCase())
}
}
},
computed: {
background() {
return this.variant === Variants.SECONDARY
? 'bg-transparent'
: this.resolveBackgroundColorClass()
},
borderColor() {
return {
blue: 'border-primary',
yellow: 'border-secondary',
purple: 'border-tertiary',
success: 'border-green',
error: 'border-red'
}[this.color]
},
textColor() {
const textColors = {
blue: 'text-primary',
yellow: 'text-secondary',
purple: 'text-tertiary',
success: 'text-green',
error: 'text-red'
}
return {
primary: 'text-white',
secondary: textColors[this.color]
}[this.variant]
},
hoverStyles() {
return {
primary: 'hover:bg-opacity-25',
secondary: `hover:${this.resolveBackgroundColorClass()} hover:text-white` // still remains purgable because parent style is defined in background
}[this.variant]
}
},
methods: {
resolveBackgroundColorClass(color = this.color) {
return {
blue: 'bg-primary',
yellow: 'bg-secondary',
purple: 'bg-tertiary',
success: 'bg-green',
error: 'bg-red'
}[color]
}
},
render(h) {
return h(
'button',
{
class: [
'flex',
'py-4',
'px-8',
'rounded',
'font-display',
'border-2',
'items-center',
'disabled:bg-grey-300',
'disabled:cursor-not-allowed',
'disabled:text-grey-low',
'disabled:border-2',
'disabled:border-grey-300',
'transition',
'duration-300',
'ease-in-out',
this.hoverStyles,
this.textColor,
this.background,
this.borderColor
]
},
this.$slots.default
)
}
}
</script>
14 changes: 14 additions & 0 deletions src/components/VukButton/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const Variants = {
PRIMARY: 'primary',
SECONDARY: 'secondary'
}

export const Colors = {
BLUE: 'blue',
YELLOW: 'yellow',
PURPLE: 'purple',
SUCCESS: 'success',
ERROR: 'error'
}

export default { Variants, Colors }
4 changes: 4 additions & 0 deletions src/components/VukButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import VukButton from './VukButton.vue'

export { VukButton }
export default VukButton
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './VukHeading'
export * from './VukLogo'
export * from './VukButton'
6 changes: 6 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ const install = function (Vue: VueConstructor, options: Object = {}) {
Object.values(components).forEach(component => {
Vue.component(component.name, component)
})

Vue.directive('button-icon', {
inserted(el) {
el.classList.add('h-4', 'mr-2', 'stroke-current')
}
})
}

// @ts-ignore
Expand Down
31 changes: 22 additions & 9 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,26 @@ module.exports = {
light: '#25ADE3'
},
grey: {
base: '#1F2122',
secondary: '#F2F2F1',
100: '#222425',
200: '#27292A',
300: '#313334',
400: '#3B3D3E',
500: '#585A5A'
base: '#121212',
100: 'rgba(255,255,255,0.05)',
200: 'rgba(255,255,255,0.07)',
300: 'rgba(255,255,255,0.08)',
400: 'rgba(255,255,255,0.09)',
500: 'rgba(255,255,255,0.11)',
600: 'rgba(255,255,255,0.12)',
700: 'rgba(255,255,255,0.14)',
800: 'rgba(255,255,255,0.15)',
900: 'rgba(255,255,255,0.16)',
low: 'rgba(255, 255, 255, 0.38)',
medium: 'rgba(255, 255, 255, 0.60)',
high: 'rgba(255, 255, 255, 0.87)'
},
green: '#3BC35A',
red: '#E3253B',
violet: '#C33B77',
yellow: '#E3B725',
white: '#ffffff'
white: 'rgba(255, 255, 255, 0.87)',
transparent: 'transparent'
},
fontFamily: {
display: 'Roboto'
Expand All @@ -42,6 +49,12 @@ module.exports = {
'4xl': '6rem'
}
},
variants: {},
variants: {
backgroundColor: ({ after }) => after(['disabled']),
cursor: ({ after }) => after(['disabled']),
textColor: ({ after }) => after(['disabled']),
borderWidth: ({ after }) => after(['disabled']),
borderColor: ({ after }) => after(['disabled'])
},
plugins: []
}
75 changes: 75 additions & 0 deletions tests/unit/VukButton.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import VukButton from '@/components/VukButton/VukButton.vue'
import { Colors } from '@/components/VukButton/constants'
import { shallowMount } from '@vue/test-utils'

describe('VukButton', () => {
let cmp: any

const propsData = {
color: 'blue',
variant: 'primary'
}
beforeEach(() => {
cmp = shallowMount(VukButton, {
propsData
})
})

describe('snapshot', () => {
it('should match the snapshot', () => {
expect(cmp.vm.$el).toMatchSnapshot()
})
})
describe('props', () => {
describe('type', () => {
it('should pass the validator when it is a valid type', () => {
expect(cmp.vm.$options.props.color.validator(Colors.BLUE)).toEqual(true)
})
it('should not pass the validator when it is not a valid type', () => {
expect(cmp.vm.$options.props.color.validator('not-valid')).toEqual(
false
)
})
})
})

describe('computed', () => {
describe('background', () => {
it('should resolve a value based upon a valid type', () => {
expect(cmp.vm.$options.computed.background.call(cmp.vm)).toEqual(
'bg-primary'
)
})
it('should resolve a transparent background value when the variant is secondary', () => {
cmp.setProps({ variant: 'secondary' })
expect(cmp.vm.$options.computed.background.call(cmp.vm)).toEqual(
'bg-transparent'
)
})
})
describe('borderColor', () => {
it('should resolve a value based upon a valid type', () => {
cmp.setProps({ type: 'blue' })
expect(cmp.vm.$options.computed.borderColor.call(cmp.vm)).toEqual(
'border-primary'
)
})
})
describe('textColor', () => {
it('should resolve a text value based upon a valid type', () => {
cmp.setProps({ type: 'blue' })
expect(cmp.vm.$options.computed.textColor.call(cmp.vm)).toEqual(
'text-white'
)
})
})
describe('hoverStyles', () => {
it('should resolve a hover value based upon a valid type', () => {
cmp.setProps({ variant: 'primary' })
expect(cmp.vm.$options.computed.hoverStyles.call(cmp.vm)).toEqual(
'hover:bg-opacity-25'
)
})
})
})
})
7 changes: 7 additions & 0 deletions tests/unit/__snapshots__/VukButton.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`VukButton snapshot should match the snapshot 1`] = `
<button
class="flex py-4 px-8 rounded font-display border-2 items-center disabled:bg-grey-300 disabled:cursor-not-allowed disabled:text-grey-low disabled:border-2 disabled:border-grey-300 transition duration-300 ease-in-out hover:bg-opacity-25 text-white bg-primary border-primary"
/>
`;
Loading

0 comments on commit 9f84073

Please sign in to comment.