Skip to content

Commit

Permalink
feat(link): add external option (#293) (#294)
Browse files Browse the repository at this point in the history
close #293 

---------

Co-authored-by: Kia King Ishii <kia.king.08@gmail.com>
  • Loading branch information
NozomuIkuta and kiaking committed May 31, 2023
1 parent 864c9c5 commit 9179f1d
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 7 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ function sidebar(): DefaultTheme.SidebarItem[] {
{ text: 'SInputSwitch', link: '/components/input-switch' },
{ text: 'SInputTextarea', link: '/components/input-textarea' },
{ text: 'SInputYMD', link: '/components/input-ymd' },
{ text: 'SLink', link: '/components/link' },
{ text: 'SPill', link: '/components/pill' },
{ text: 'SState', link: '/components/state' },
{ text: 'STable', link: '/components/table' },
Expand Down
71 changes: 71 additions & 0 deletions docs/components/link.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# SLink

`<SLink>` is a styleless component that renders an anchor tag with custom utility options.

## Usage

You may use `<SLink>` as almost the same as `<a>` tag. Wrap text with it and it will render an anchor tag with the text inside.

```vue
<script setup lang="ts">
import SLink from '@globalbrain/sefirot/lib/components/SLink.vue'
</script>
<template>
<SLink href="https://example.com">
Link text
</SLink>
</template>
```

`<SLink>` will automatically add `target="_blank"` and `rel="noopener noreferrer"` to the anchor tag if the `href` prop is an external link. If the link is internal, it will use `<RouterLink>` component from Vue Router instead.

```vue-html
<SLink href="https://example.com">
<!-- Becomes -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
<SLink href="/about">
<!-- Becomes -->
<RouterLink to="/about">
```

You may pass `:external` prop to override this behavior. For example, if you would like to have an internal link open in another tab, set `:external` to `true`.

```vue-html
<SLink href="/about" external>
<!-- Becomes -->
<a href="/about" target="_blank" rel="noopener noreferrer">
```

## Props

Here are the list of props you may pass to the component.

### `:href`

The link to be used. If the link is external, it will automatically add `target="_blank"` and `rel="noopener noreferrer"` to the anchor tag.

```ts
interface Props {
href?: string
}
```

```vue-html
<SLink href="/about">...</SLink>
```

### `:external`

Override the automatic external link detection. If set to `true`, it will always add `target="_blank"` and `rel="noopener noreferrer"` to the anchor tag.

```ts
interface Props {
external?: boolean
}
```

```vue-html
<SLink href="/about" external>...</SLink>
```
19 changes: 16 additions & 3 deletions lib/components/SLink.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps({
href: { type: String, default: null }
const props = withDefaults(defineProps<{
href?: string | null
external?: boolean | null
}>(), {
external: null
})
const OUTBOUND_REGEX = /^[a-z]+:/i
const isExternal = computed(() => OUTBOUND_REGEX.test(props.href))
const isExternal = computed(() => {
if (!props.href) {
return false
}
if (props.external !== null) {
return props.external
}
return OUTBOUND_REGEX.test(props.href)
})
const component = computed(() => {
if (!props.href) {
Expand Down
53 changes: 53 additions & 0 deletions stories/components/SLink.01_Playground.story.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script setup lang="ts">
import SLink from 'sefirot/components/SLink.vue'
const title = 'Components / SLink / 01. Playground'
const docs = '/components/link'
function state() {
return {
href: 'https://example.com',
external: 'undefined',
text: 'This is link text'
} as const
}
function getExternal(value: 'undefined' | 'true' | 'false') {
return value === 'undefined' ? undefined : value === 'true'
}
</script>

<template>
<Story :title="title" :init-state="state" source="Not available" auto-props-disabled>
<template #controls="{ state }">
<HstText
title="href"
v-model="state.href"
/>
<HstText
title="text"
v-model="state.text"
/>
<HstSelect
title="external"
:options="{
undefined: 'undefined',
true: 'true',
false: 'false'
}"
v-model="state.external"
/>
</template>

<template #default="{ state }">
<Board :title="title" :docs="docs">
<SLink
:href="state.href"
:external="getExternal(state.external)"
>
{{ state.text }}
</SLink>
</Board>
</template>
</Story>
</template>
30 changes: 26 additions & 4 deletions tests/components/SLink.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { mount } from '@vue/test-utils'
import SLink from 'sefirot/components/SLink.vue'

describe('components/SLink', () => {
it('creates `span` tag when no link is passed', () => {
test('creates `span` tag when no link is passed', () => {
const wrapper = mount(SLink)

expect(wrapper.find('span').exists()).toBe(true)
})

it('creates `router-link` tag when link is internal', () => {
test('creates `router-link` tag when link is internal', () => {
const wrapper = mount(SLink, {
props: {
href: 'about'
Expand All @@ -18,10 +18,32 @@ describe('components/SLink', () => {
expect(wrapper.find('router-link').exists()).toBe(true)
})

it('creates `a` tag when link is external', () => {
test('creates `a` tag when link is external', () => {
const wrapper = mount(SLink, {
props: {
href: 'https://google.com'
href: 'https://example.com'
}
})

expect(wrapper.find('a').exists()).toBe(true)
})

test('creates `router-link` tag when `:external` is `false`', () => {
const wrapper = mount(SLink, {
props: {
href: 'https://example.com',
external: false
}
})

expect(wrapper.find('router-link').exists()).toBe(true)
})

test('creates `a` tag when `:external` is `true`', () => {
const wrapper = mount(SLink, {
props: {
href: 'about',
external: true
}
})

Expand Down

0 comments on commit 9179f1d

Please sign in to comment.