Skip to content

Commit

Permalink
feat(ktabs): update ktabs for vue 3 (#570)
Browse files Browse the repository at this point in the history
* feat(ktabs): update ktabs for vue 3

* docs(ktabs): update inline code formatting
  • Loading branch information
adamdehaven committed Apr 11, 2022
1 parent 9b55021 commit 900d71c
Show file tree
Hide file tree
Showing 6 changed files with 494 additions and 6 deletions.
16 changes: 10 additions & 6 deletions TEMP-MIGRATION-CHANGELOG.md
Expand Up @@ -47,13 +47,13 @@ export default defineConfig({

### KInput

- `v-model` is now mapped to `modelValue` prop, and emits `input` and `update:modelValue` events.
- `v-model` is now mapped to `modelValue` prop instead of `value`, and emits `input` and `update:modelValue` events.
- Default font size for all inputs and textareas is now `16px` for a11y and to prevent page zoom in Safari.
- `.k-input-medium + .has-error` font-size is now `11px`

### KInputSwitch

- `v-model` is now mapped to `modelValue` prop, and emits `input`, `change`, and `update:modelValue` events.
- `v-model` is now mapped to `modelValue` prop instead of `value`, and emits `input`, `change`, and `update:modelValue` events.
- Added the following CSS rule for label alignment
```scss
&.has-label-left {
Expand All @@ -64,20 +64,20 @@ export default defineConfig({

### KTextArea

- `v-model` is now mapped to `modelValue` prop, and emits `input`, `update:modelValue`, `char-limit-exceeded` events.
- `v-model` is now mapped to `modelValue` prop instead of `value`, and emits `input`, `update:modelValue`, `char-limit-exceeded` events.

### KCheckbox

- `v-model` is now mapped to `modelValue` prop, and emits `input`, `change`, and `update:modelValue` events.
- `v-model` is now mapped to `modelValue` prop instead of `value`, and emits `input`, `change`, and `update:modelValue` events.

### KRadio

- `v-model` is now mapped to `modelValue` prop, and emits `change`, and `update:modelValue` events.
- `v-model` is now mapped to `modelValue` prop instead of `value`, and emits `change`, and `update:modelValue` events.
- `value` prop has been renamed to `selectedValue`

### KSelect

- `v-model` is now mapped to `modelValue` prop, and emits `input`, `change`, and `update:modelValue` events.
- `v-model` is now mapped to `modelValue` prop instead of `value`, and emits `input`, `change`, and `update:modelValue` events.
- `positionFixed` now defaults to `true`
- TODO: Noticing a weird page scroll when clicking on the select item

Expand All @@ -104,3 +104,7 @@ export default defineConfig({
### KPrompt

- Added `autcomplete="off"` and `autocapitalize="off"` to the confirmation text input.

### KTabs

- `v-model` is now mapped to `modelValue` prop instead of `value`.
1 change: 1 addition & 0 deletions docs/.vuepress/config.ts
Expand Up @@ -114,6 +114,7 @@ export default defineUserConfig<DefaultThemeOptions, ViteBundlerOptions>({
'/components/select',
'/components/skeleton',
'/components/table',
'/components/tabs',
'/components/textarea',
'/components/toaster',
'/components/tooltip',
Expand Down
293 changes: 293 additions & 0 deletions docs/components/tabs.md
@@ -0,0 +1,293 @@
# Tabs

**KTabs** - A mindblowing tabs component

<KTabs :tabs="tabs">
<template v-slot:tab1>
<p>Tab 1 content</p>
</template>
<template v-slot:tab2>
<p>Tab 2 content</p>
</template>
</KTabs>

```vue
<KTabs :tabs="tabs">
<template v-slot:tab1>
<p>Tab 1 content</p>
</template>
<template v-slot:tab2>
<p>Tab 2 content</p>
</template>
</KTabs>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
setup () {
const tabs = [
{
hash: '#tab1',
title: 'Tab 1'
},
{
hash: '#tab2',
title: 'Tab 2'
}
]
return {
tabs,
}
}
})
</script>
```

## Props

### tabs

`KTabs` has one **required** prop, `tabs`, which is an array of tab objects with the following interface:

```ts
export interface Tab {
hash: string
title: string
}
```

```vue
<template>
<KTabs :tabs="tabs" />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
setup () {
const tabs = [
{ hash: '#pictures', title: 'Pictures' },
{ hash: '#movies', title: 'Movies' },
{ hash: '#books', title: 'Books' },
]
return {
tabs,
}
}
})
</script>
```

### v-model

By default the `KTabs` will set the first tab in the array as active. You can override this by passing in the hash of any other tab to be used with `v-model`.

<KTabs v-model="defaultTab" :tabs="tabs">
<template v-slot:tab1>
<p>Tab 1 content</p>
</template>
<template v-slot:tab2>
<p>Tab 2 content</p>
</template>
</KTabs>

```vue
<KTabs v-model="#tab2" :tabs="tabs">
<template v-slot:tab1>Tab 1 content</template>
<template v-slot:tab2>Tab 2 content</template>
</KTabs>
```

If you want to keep your `v-model` in sync so that you can programatically change the active tab after initialization, you also must respond to the `@changed` emitted event.

<KTabs v-model="defaultProgrammaticTab" :tabs="tabs" @changed="hash => defaultProgrammaticTab = hash">
<template v-slot:tab1>
<p>Tab 1 content</p>
</template>
<template v-slot:tab2>
<p>Tab 2 content</p>
</template>
</KTabs>

<hr />

<KButton @click="defaultProgrammaticTab = '#tab1'" class="mr-2">Activate Tab 1</KButton>
<KButton @click="defaultProgrammaticTab = '#tab2'">Activate Tab 2</KButton>

```vue
<KTabs v-model="defaultTab" :tabs="tabs" @changed="hash => defaultTab = hash">
<template v-slot:tab1>
<p>Tab 1 content</p>
</template>
<template v-slot:tab2>
<p>Tab 2 content</p>
</template>
</KTabs>
<KButton @click="defaultTab = '#tab1'">Activate Tab 1</KButton>
<KButton @click="defaultTab = '#tab2'">Activate Tab 2</KButton>
```

## Slots

In order to actually see your tabbed content you must slot it using the tab hash property without the hash mark.

<KTabs :tabs="slottedTabs">
<template v-slot:pictures>
<p>Wow look Pictures!</p>
</template>
<template v-slot:movies>
<p>Wow look Movies!</p>
</template>
<template v-slot:books>
<p>Wow look Books!</p>
</template>
</KTabs>

```vue
<template>
<KTabs :tabs="tabs">
<template v-slot:pictures>Wow look Pictures!</template>
<template v-slot:movies>Wow look Movies!</template>
<template v-slot:books>Wow look Books!</template>
</KTabs>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
setup () {
const tabs = [
{ hash: '#pictures', title: 'Pictures' },
{ hash: '#movies', title: 'Movies' },
{ hash: '#books', title: 'Books' },
]
return {
tabs,
}
}
})
</script>
```

## Usage

### Router Hash

`KTabs` emits a `changed` event with the new tab hash when clicked. You can use this to set the router or window hash and in turn use that with [`v-model`](#v-model).

```vue
<template>
<KTabs
:tabs="tabs"
v-model="$route.hash"
@changed="hash => $router.replace({hash})">
<template v-slot:pictures>Wow look Pictures!</template>
<template v-slot:movies>Wow look Movies!</template>
<template v-slot:books>Wow look Books!</template>
</KTabs>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
// Importing $route and $router in your app may vary and is excluded in this example.
export default defineComponent({
setup () {
const tabs = [
{ hash: '#pictures', title: 'Pictures' },
{ hash: '#movies', title: 'Movies' },
{ hash: '#books', title: 'Books' },
]
return {
tabs,
}
}
})
</script>
```

## Theming

| Variable | Purpose
|:-------- |:-------
| `--KTabsBottomBorderColor`| Border between the tabs and the tab content
| `--KTabBottomBorderColor`| Border on the bottom of each tab
| `--KTabsActiveColor`| Active color of tab and underline
| `--KTabsColor`| Default text color of the tab items

\
An Example of changing the primary KButton variant to green instead of blue might
look like.
> Note: We are scoping the overrides to a wrapper in this example
<div class="KTabs-wrapper">
<KTabs :tabs="tabs">
<template v-slot:tab1>
<p>Tab 1 content</p>
</template>
<template v-slot:tab2>
<p>Tab 2 content</p>
</template>
</KTabs>
</div>

```vue
<template>
<div class="KTabs-wrapper">
<KTabs :tabs="tabs">
<template v-slot:tab1>
<p>Tab 1 content</p>
</template>
<template v-slot:tab2>
<p>Tab 2 content</p>
</template>
</KTabs>
</div>
</template>
<style>
.KTabs-wrapper {
--KTabsActiveColor: green;
}
</style>
```

<script>
export default {
data() {
return {
defaultTab: '#tab2',
defaultProgrammaticTab: '#tab2',
tabs: [
{
hash: '#tab1',
title: 'Tab 1'
},
{
hash: '#tab2',
title: 'Tab 2'
}
],
slottedTabs: [
{ hash: '#pictures', title: 'Pictures' },
{ hash: '#movies', title: 'Movies' },
{ hash: '#books', title: 'Books' },
],
}
}
}
</script>

<style lang="scss">
.KTabs-wrapper {
--KTabsActiveColor: green;
}
</style>
47 changes: 47 additions & 0 deletions src/components/KTabs/KTabs.spec.ts
@@ -0,0 +1,47 @@
// Import types for custom commands
/// <reference types="../../cypress/support" />

import { mount } from '@cypress/vue'
import KTabs from '@/components/KTabs/KTabs.vue'

const TABS = [
{ hash: '#pictures', title: 'Pictures' },
{ hash: '#movies', title: 'Movies' },
{ hash: '#books', title: 'Books' },
]

describe('KTabs', () => {
it('first tab is set if hash not found', () => {
mount(KTabs, {
props: {
tabs: TABS,
},
})

cy.get('.tab-item').eq(0).should('have.class', 'active')
})

it('sets correct tab if default tab prop', () => {
mount(KTabs, {
props: {
tabs: TABS,
modelValue: '#books',
},
})

cy.get('.tab-item').eq(2).should('have.class', 'active')
})

it('emits change event on click', () => {
mount(KTabs, {
props: {
tabs: TABS,
},
})

cy.get('.tab-item').eq(1).click().then(() => {
cy.wrap(Cypress.vueWrapper.emitted()).should('have.property', 'changed')
cy.wrap(Cypress.vueWrapper.emitted('changed')[0][0]).should('eq', '#movies')
})
})
})

0 comments on commit 900d71c

Please sign in to comment.