Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Vue.use(VueCompositionAPI);
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/side-effects-usebeforeunload--demo)
- [`useCookie`](./src/components/useCookie/stories/useCookie.md) — provides way to read, update and delete a cookie.
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/side-effects-usecookie--demo)
- [`useLocalStorage`](./src/components/useLocalStorage/stories/useLocalStorage.md) — provides way to read, update and delete a localStorage key.
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/side-effects-uselocalstorage--demo)
- UI
- [`useClickAway`](./src/components/useClickAway/stories/useClickAway.md) — triggers callback when user clicks outside target area.
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/ui-useclickaway--demo)
Expand Down
4 changes: 3 additions & 1 deletion src/components/useCookie/stories/UseCookieDemo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ export default Vue.extend({
cookie: jsonCookie,
setCookie: jsonSetCookie,
removeCookie: jsonRemoveCookie
} = useCookie('jsonCookie')
} = useCookie('jsonCookie', {
isParsing: true
})

let counter = 0
const handleSetCookie = () => {
Expand Down
49 changes: 30 additions & 19 deletions src/components/useCookie/stories/useCookie.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ Vue function that provides way to read, set and remove a cookie.

## Reference

```typescript
interface UseCookieOptions {
isParsing: boolean
serializer?: SerializerFunction
deserializer?: DeserializerFunction
}
```

```typescript
function useCookie(
cookieName: string,
enableParseJSON?: boolean,
options?: UseCookieOptions,
runOnMount?: boolean
): {
cookie: Ref<any>
Expand All @@ -20,7 +28,10 @@ function useCookie(
### Parameters

- `cookieName: string` the cookie name you wish to get/set/remove
- `enableParseJSON: boolean` whether to enable JSON parsing or not, `false` by default
- `options: UseCookieOptions`
- `isParsing: boolean` whether to enable parsing the cookie value or not, `false` by default
- `serializer: SerializerFunction` a custom serializer, `JSON.stringify` by default
- `deserializer: DeserializerFunction` a custom deserializer, `JSON.parse` by default
- `runOnMount: boolean` whether to get the cookie on mount or not, `true` by default

### Returns
Expand All @@ -38,31 +49,31 @@ function useCookie(
```html
<template>
<div>
Cookie: {{ cookie }}

<div>Cookie: {{ cookie }}</div>
<button @click="getCookie">Get cookie</button>
<button @click="setCookie('Value here')">Set cookie</button>
<button @click="removeCookie">Remove cookie</button>
</div>
</template>

<script lang="ts">
import Vue from 'vue'
import { useCookie } from 'vue-use-kit'
import Vue from 'vue'
import { useCookie } from 'vue-use-kit'

export default Vue.extend({
name: 'UseCookieDemo',
setup() {
const {
cookie, getCookie, setCookie, removeCookie
} = useCookie('i_love_cookies')
export default Vue.extend({
name: 'UseCookieDemo',
setup() {
const { cookie, getCookie, setCookie, removeCookie } = useCookie(
'i_love_cookies'
)

return {
cookie,
getCookie,
setCookie,
removeCookie,
return {
cookie,
getCookie,
setCookie,
removeCookie
}
}
}
})
})
</script>
```
92 changes: 88 additions & 4 deletions src/components/useCookie/useCookie.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ afterEach(() => {
const testComponent = (
cookieName = 'cookieName',
cookieValue: any = '',
parseJson = false,
opts = { isParsing: false } as any,
onMount = true
) => ({
template: `
Expand All @@ -26,7 +26,7 @@ const testComponent = (
setup() {
const { cookie, getCookie, setCookie, removeCookie } = useCookie(
cookieName,
parseJson,
opts,
onMount
)

Expand Down Expand Up @@ -72,10 +72,12 @@ describe('useCookie', () => {
expect(document.cookie).not.toContain(cookieValue)
})

it('should correctly get and set the parseToJson object', async () => {
it('should correctly get and set the isParsing object', async () => {
const cookieName = 'cookieName'
const cookieValue = { value1: 'testValue1', value2: 'testValue2' }
const wrapper = mount(testComponent(cookieName, cookieValue, true))
const wrapper = mount(
testComponent(cookieName, cookieValue, { isParsing: true })
)
wrapper.find('#setCookie').trigger('click')
await wrapper.vm.$nextTick()
expect(wrapper.find('#cookieJson').html()).toContain(cookieValue.value1)
Expand All @@ -87,4 +89,86 @@ describe('useCookie', () => {
expect(wrapper.find('#cookieJson').html()).toContain(cookieValue.value1)
expect(wrapper.find('#cookieJson').html()).toContain(cookieValue.value2)
})

it('should correctly get using the deserializer', async () => {
const cookieName = 'cookieName'
const cookieValue = { value1: 'testValue1', value2: 'testValue2' }
const deserializerVal = { value1: 'gatto', value2: 'topo' }
const wrapper = mount(
testComponent(cookieName, cookieValue, {
isParsing: true,
deserializer: () => deserializerVal
})
)
await wrapper.vm.$nextTick()
expect(wrapper.find('#cookieJson').html()).toContain(deserializerVal.value1)
expect(wrapper.find('#cookieJson').html()).toContain(deserializerVal.value2)
})

it('should ignore deserializer when isParsing is false', async () => {
const cookieName = 'cookieName'
const cookieValue = { value1: 'testValue1', value2: 'testValue2' }
const deserializerVal = { value1: 'gatto', value2: 'topo' }
const wrapper = mount(
testComponent(cookieName, cookieValue, {
isParsing: false,
deserializer: () => deserializerVal
})
)
await wrapper.vm.$nextTick()
expect(wrapper.find('#cookie').html()).toContain(cookieValue.value1)
expect(wrapper.find('#cookie').html()).toContain(cookieValue.value2)
expect(wrapper.find('#cookieJson').html()).not.toContain(
deserializerVal.value1
)
expect(wrapper.find('#cookieJson').html()).not.toContain(
deserializerVal.value2
)
})

it('should correctly set the object using the serializer', async () => {
const cookieName = 'cookieName'
const cookieValue = { value1: 'testValue1', value2: 'testValue2' }
const serializerVal = { value1: 'testValue1+1', value2: 'testValue2+1' }
const wrapper = mount(
testComponent(cookieName, cookieValue, {
isParsing: true,
serializer: (obj: any) =>
JSON.stringify({
value1: `${obj.value1}+1`,
value2: `${obj.value2}+1`
})
})
)
wrapper.find('#setCookie').trigger('click')
wrapper.find('#getCookie').trigger('click')
await wrapper.vm.$nextTick()
expect(wrapper.find('#cookieJson').html()).toContain(serializerVal.value1)
expect(wrapper.find('#cookieJson').html()).toContain(serializerVal.value2)
})

it('should ignore serializer when isParsing is false', async () => {
const cookieName = 'cookieName'
const cookieValue = { value1: 'testValue1', value2: 'testValue2' }
const serializerVal = { value1: 'testValue1+1', value2: 'testValue2+1' }
const wrapper = mount(
testComponent(cookieName, cookieValue, {
isParsing: false,
serializer: (obj: any) => ({
value1: `${obj.value1}+1`,
value2: `${obj.value2}+1`
})
})
)
wrapper.find('#setCookie').trigger('click')
wrapper.find('#getCookie').trigger('click')
await wrapper.vm.$nextTick()
expect(wrapper.find('#cookie').html()).toContain('[object Object]')
expect(wrapper.find('#cookieJson').html()).not.toContain(
serializerVal.value1
)
expect(wrapper.find('#cookieJson').html()).not.toContain(
serializerVal.value2
)
})
})
43 changes: 36 additions & 7 deletions src/components/useCookie/useCookie.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,57 @@
import Cookies from 'cookie-universal'
import { CookieSerializeOptions } from 'cookie'
import {
createSerializer,
createDeserializer,
SerializerFunction,
DeserializerFunction,
trySerialize,
tryDeserialize,
isNullOrUndefined
} from '@src/utils'
import { ref, onMounted, Ref } from '@src/api'

export interface UseCookieOptions {
isParsing: boolean
serializer?: SerializerFunction
deserializer?: DeserializerFunction
}

const defaultOptions = {
isParsing: false
}

export function useCookie(
cookieName: string,
enableParseJSON = false,
options?: UseCookieOptions,
runOnMount = true
) {
const cookieLib = Cookies(undefined, undefined, enableParseJSON)
const { isParsing, ...opts } = Object.assign({}, defaultOptions, options)
const serializer = createSerializer(opts.serializer)
const deserializer = createDeserializer(opts.deserializer)

const cookieLib = Cookies(undefined, undefined, false)
const cookie: Ref<any> = ref(null)

const getCookie = () => {
const cookieVal = cookieLib.get(cookieName)
if (typeof cookieVal !== 'undefined') cookie.value = cookieVal
const cookieVal = tryDeserialize(
cookieLib.get(cookieName),
deserializer,
isParsing
)
if (!isNullOrUndefined(cookieVal)) cookie.value = cookieVal
}

const setCookie = (
// The user may pass a 'string', a 'number', or a valid JSON object/array
// The user may pass a 'string', a 'number', a valid JSON object/array
// or even a custom object when serializer/deserializer are defined
// so it is better to set allowed cookie value as 'any'
newVal: any,
options?: CookieSerializeOptions
) => {
cookieLib.set(cookieName, newVal, options)
cookie.value = newVal
const newCookieVal = trySerialize(newVal, serializer, isParsing)
cookieLib.set(cookieName, newCookieVal, options)
cookie.value = tryDeserialize(newCookieVal, deserializer, isParsing)
}

const removeCookie = (options?: CookieSerializeOptions) => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/useGeolocation/useGeolocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function useGeolocation(
options: PositionOptions = {},
runOnMount = true
) {
options = Object.assign(defaultOpts, options)
options = Object.assign({}, defaultOpts, options)

// Note: surprisingly the watchId can be 0 (not positive) so
// we have to check if watchId !== null every time
Expand Down
1 change: 1 addition & 0 deletions src/components/useLocalStorage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useLocalStorage'
84 changes: 84 additions & 0 deletions src/components/useLocalStorage/stories/UseLocalStorageDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<table class="table is-fullwidth">
<thead>
<tr>
<th>Prop</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>item</td>
<td>{{ item }}</td>
</tr>
<tr>
<td colspan="2">
<button class="button is-primary" @click="handleSetItem">
Set / Update item
</button>
<button class="button is-danger" @click="removeItem()">
Remove item
</button>
</td>
</tr>
<tr>
<td>jsonItem</td>
<td>{{ jsonItem }}</td>
</tr>
<tr>
<td colspan="2">
<button class="button is-primary" @click="handleSetJsonItem">
Set / Update JSON item
</button>
<button class="button is-danger" @click="jsonRemoveItem()">
Remove JSON item
</button>
</td>
</tr>
</tbody>
</table>
</template>

<script lang="ts">
import Vue from 'vue'
import { useLocalStorage } from '@src/vue-use-kit'

export default Vue.extend({
name: 'useLocalStorageDemo',
setup() {
const { item, setItem, removeItem } = useLocalStorage('normalItem')

const {
item: jsonItem,
setItem: jsonSetItem,
removeItem: jsonRemoveItem
} = useLocalStorage('jsonItem', {
isParsing: true
})

let counter = 0
const handleSetItem = () => {
counter++
setItem(`count${counter}`)
}

const handleSetJsonItem = () => {
counter++
jsonSetItem({
counter: counter,
counterTest: `test${counter}`
})
}

return {
item,
handleSetItem,
removeItem,
jsonItem,
handleSetJsonItem,
jsonRemoveItem,
counter
}
}
})
</script>
Loading